From 331fadfb48d65a212c5b61e5e82e9ac223f45484 Mon Sep 17 00:00:00 2001 From: laoyang Date: Thu, 24 Sep 2026 22:13:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E4=B8=8E=E6=AD=A3=E6=96=87?= =?UTF-8?q?=E5=90=8C=E5=AE=BD=20/=20=E8=AF=A6=E6=83=85=E9=A1=B5=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E6=94=B9=E5=88=97=E8=A1=A8=E8=A1=8C=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=20/=20=E5=B0=81=E9=9D=A2=E6=8B=96=E6=8B=BD=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=EF=BC=88=E6=89=B9=2021=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ① 带地图的页面(首页旅行地图、详情页行程地图)原本铺满整个视口 (批 18 的负 margin 出血),用户反馈「上面正文固定宽、下面地图铺满屏」 看着奇怪 → 改回与正文版心同宽,bleed() 已删、CSS 去边框覆盖已删。 ② 活动详情页顶部整幅 16:9 大封面太大 → 改用活动列表同款「封面行」 (.act-row-static:小封面 + 参与人 + 统计),数据复用 collectStats 同一聚合口径;.activity-cover 样式删除。 ③ 创建/编辑活动封面支持直接把图片拖进上传区:drop→DataTransfer 塞给 原有 file input 并触发 change,类型/大小校验与裁剪流程原样复用; 拖入悬停给高亮反馈。 验证:本地全链(含 _map12/_map16 同宽与封面行断言、5 档视口)全绿; 部署哨兵同步改向(批 17/18 锚定旧实现的断言全部反向或改口径)。 --- assets/style.css | 51 ++++++------------ src/Controllers/ActivityController.php | 5 ++ templates/activities/form.php | 29 +++++++++- templates/activities/index.php | 21 ++------ templates/activities/show.php | 73 +++++++++++++++++++------- 5 files changed, 108 insertions(+), 71 deletions(-) diff --git a/assets/style.css b/assets/style.css index ce84360..ae892fd 100644 --- a/assets/style.css +++ b/assets/style.css @@ -352,19 +352,8 @@ hr { border: 0; border-top: 1px solid var(--border); margin: 20px 0; } .card-body h3 a { color: var(--text); } .card-body h3 a:hover { color: var(--accent); text-decoration: none; } -/* 封面是按 16:9 裁的(activities/form.php 里的 Cropper aspectRatio 就是 16/9), - 展示端必须同比例 —— 不然辛苦裁好的构图在这里又被切掉一块。 - ⚠️ 别再加 max-height:容器比例一旦被改,object-fit: cover 就重新裁一次图。 */ -.activity-cover { - position: relative; border-radius: var(--radius-lg); overflow: hidden; - margin-bottom: 20px; background: var(--bg-soft); - aspect-ratio: 16 / 9; -} -.activity-cover img { width: 100%; height: 100%; object-fit: cover; display: block; } -.activity-cover .cover-placeholder { - width: 100%; height: 100%; - display: flex; align-items: center; justify-content: center; -} +/* 批 21:详情页顶部不再放整幅 16:9 大封面(原大图容器样式已删), + 改用列表同款 .act-row(含 .act-row-cover,仍是 16:9)—— 见「活动列表」一节。 */ /* ── 8. 时间轴与节点卡片 ─────────────────────────────────────────────────── */ .timeline-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; } @@ -923,7 +912,7 @@ video.video-thumb.video-thumb-mini { ⚠️ width / height / object-fit 必须写在 **img** 上,不能写在外层 div 上 —— 写错地方图片就完全不受约束,会按原始尺寸铺出来:头像 400×400、封面 1920×1080,直接把下面的表单盖住(这就是「头像非常大、整页错位」的根)。 - 对照写对了的 .card-cover img / .activity-cover img。 */ + 对照写对了的 .card-cover img / .act-row-cover img。 */ .avatar-preview { position: relative; flex: none; width: 84px; height: 84px; border-radius: 50%; @@ -979,6 +968,8 @@ video.video-thumb.video-thumb-mini { .avatar-preview, .cover-preview { transition: border-color .15s, background .15s; } .avatar-upload:hover .avatar-preview, .cover-upload:hover .cover-preview { border-color: var(--accent); background: var(--accent-soft); } +/* 批 21:把图片拖进上传区时给个高亮反馈(松手即走原有的裁剪流程) */ +.cover-upload.dragover .cover-preview { border-color: var(--accent); background: var(--accent-soft); } .avatar-upload-actions, .cover-upload-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-top: 0; @@ -1393,31 +1384,23 @@ video.video-thumb.video-thumb-mini { .act-member-more { font-size: 12.5px; color: var(--text-mute); align-self: center; } .act-row-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; } +/* 批 21:详情页顶部复用列表行样式展示「小封面 + 参与人/统计」。 + 它不是链接(详情页就在这儿),压掉 hover 反馈、留出与标题区的间距。 */ +.act-row-static { margin-bottom: 20px; cursor: default; } +.act-row-static:hover { border-color: var(--border); box-shadow: none; } + @media (max-width: 560px) { .act-row { grid-template-columns: 104px 1fr; gap: 12px; padding: 10px; } } -/* ── 23. 地图铺满整屏(批 18)──────────────────────────────────────────── */ -/* 行程路线 / 旅行地图:**突破正文版心,占满整个视口宽度**; - 高度跟着视口高度走 —— 屏幕越大容器越大,地图显示的地理范围也越大 - (AMap 填满容器,fitView 按当前视野重算,等于跟着分辨率自动缩放)。 - ⚠️ 左右出血的 margin **由 JS 给**:版心留白是 `max(gutter, (100% - 1080px)/2)`, - 而这个 100% 在**使用它的元素**上解析 —— 直接写「负的版心留白」在地图元素上 - 算出来只有 -gutter(实测 156px vs 该有的 180px),铺不满。 - 所以改成读 container 实际渲染出来的 padding-left 再取负(见模板里的 bleed())。 - ⚠️ 更不能写 `width: 100vw`:它含滚动条宽度,会让整页横向溢出。 - ⚠️ 已经贴着视口边缘了,就不要左右边框和圆角了(看着才像一整屏)。 */ +/* ── 23. 地图尺寸(批 21 调整)──────────────────────────────────────────── */ +/* 行程路线 / 旅行地图:**与正文版心同宽**。 + 批 18 曾让它突破版心铺满整个视口(JS 负 margin 出血),用户反馈 + 「上面正文固定宽、下面地图铺满屏」看着奇怪,批 21 改回一致 —— + 宽度跟随 .container 的内容区,模板里的 bleed() 已删,这里也不再有 + border-radius/border 的去边框覆盖(恢复 .route-map 默认圆角与描边)。 + 高度仍跟着视口高度走 —— 屏幕越大容器越大,地图显示的地理范围也越大。 */ #home-panel-map .route-map, #route-panel-map .route-map { height: clamp(460px, calc(100vh - 190px), 1000px); - border-radius: 0; - border-left: 0; - border-right: 0; -} -/* 地图下面那句说明:跟着一起出血,只留一个 gutter - (不然会孤零零悬在离地图左边缘很远的地方) */ -#home-panel-map .route-map-hint, -#route-panel-map .route-map-hint { - padding-left: var(--gutter); - padding-right: var(--gutter); } diff --git a/src/Controllers/ActivityController.php b/src/Controllers/ActivityController.php index 1aa25d1..d2772f7 100644 --- a/src/Controllers/ActivityController.php +++ b/src/Controllers/ActivityController.php @@ -348,12 +348,17 @@ class ActivityController array_column($realityNodes, 'id') )); + // 批 21:详情页顶部改用列表同款「封面行」(小封面 + 参与人/统计), + // 复用与列表同一份聚合口径(collectStats),不另写一套查询 + $actStats = $this->collectStats([$id]); + view('activities/show', [ 'activity' => $activity, 'planNodes' => $planNodes, 'realityNodes' => $realityNodes, 'executedPlanIds' => $executedPlanIds, 'nodeMembers' => $nodeMembers, + 'actStats' => $actStats, // 路线地图:每个执行节点第一张图片附件 id(圆形标记用),一条 GROUP BY 防止 N+1 'nodeFirstImage' => Attachments::firstImageIdsForNodes(array_column($realityNodes, 'id')), // 批 19:路线地图标记图标按出行方式映射(回退节点类型图标) diff --git a/templates/activities/form.php b/templates/activities/form.php index b828a5a..fa31f7d 100644 --- a/templates/activities/form.php +++ b/templates/activities/form.php @@ -29,7 +29,7 @@ $coverPreview = ($isEdit && $cover !== '') ? media_cover((int)$activity['id']) :
-
+
@@ -43,7 +43,7 @@ $coverPreview = ($isEdit && $cover !== '') ? media_cover((int)$activity['id']) : - 支持 JPG/PNG/WebP,裁剪为 800×450 + 支持 JPG/PNG/WebP,可直接拖进来或点选,裁剪为 800×450
@@ -103,6 +103,31 @@ $coverPreview = ($isEdit && $cover !== '') ? media_cover((int)$activity['id']) : btnUpload.addEventListener('click', function () { fileInput.click(); }); + /* 批 21:直接把图片拖进上传区 —— 走 DataTransfer 塞给原有 file input, + 后面的「校验类型/大小 → 打开裁剪」流程原样复用,不另写一套。 + dragenter/dragover 必须 preventDefault,否则浏览器会直接打开图片文件。 */ + var dropBox = document.getElementById('cover-upload-box'); + ['dragenter', 'dragover'].forEach(function (t) { + dropBox.addEventListener(t, function (e) { + e.preventDefault(); + dropBox.classList.add('dragover'); + }); + }); + ['dragleave', 'drop'].forEach(function (t) { + dropBox.addEventListener(t, function (e) { + e.preventDefault(); + dropBox.classList.remove('dragover'); + }); + }); + dropBox.addEventListener('drop', function (e) { + var files = e.dataTransfer && e.dataTransfer.files; + if (!files || !files.length) return; + var dt = new DataTransfer(); + dt.items.add(files[0]); // 封面只有一张:一次拖多张也只取第一张 + fileInput.files = dt.files; + fileInput.dispatchEvent(new Event('change')); + }); + fileInput.addEventListener('change', function (e) { var file = e.target.files && e.target.files[0]; if (!file) return; diff --git a/templates/activities/index.php b/templates/activities/index.php index 1019b59..fd80f03 100644 --- a/templates/activities/index.php +++ b/templates/activities/index.php @@ -161,20 +161,8 @@ foreach ($activities as $a) { .replace(/>/g, '>').replace(/"/g, '"'); } - /* 地图铺满整个视口宽度:把正文版心的左右留白用负 margin 还回去。 - ⚠️ 不能用 CSS `calc(-1 * var(--shell-pad))`:自定义属性里的 `100%` 是在 - **使用它的元素**上解析的,写在地图(它在版心内部,宽 1080)上算出来 - 只有 -gutter,实测只能铺到 1128/1440。这里按 container 实际渲染出的 - padding 反推,才铺得满;也不用 100vw(含滚动条宽度会撑出横向滚动条)。 */ - function bleed() { - var host = el.closest('.container'); - if (!host) return; - var pad = parseFloat(window.getComputedStyle(host).paddingLeft) || 0; - if (pad <= 0) return; - el.style.marginLeft = (-pad) + 'px'; - el.style.marginRight = (-pad) + 'px'; - } - bleed(); + /* 批 21:地图不再铺满整个视口 —— 用户反馈「上面正文固定宽、下面地图铺满屏」 + 看着奇怪,改回与正文版心同宽(批 18 的负 margin 出血逻辑 bleed() 已删)。 */ map = new AMap.Map(el, { viewMode: '2D', zoom: 5 }); window.__homeMap = map; @@ -260,14 +248,13 @@ foreach ($activities as $a) { relayout(); map.on('zoomend', relayout); - /* 改窗口大小 / 旋转屏幕 / 换分辨率:地图容器跟着变(宽度是铺满视口的), - 视野和图标排布都得按新尺寸重算一次。防抖 200ms,别在拖动时反复重排。 */ + /* 改窗口大小 / 旋转屏幕 / 换分辨率:地图容器跟着版心变,视野和图标排布 + 都得按新尺寸重算一次。防抖 200ms,别在拖动时反复重排。 */ var resizeTimer = null; window.addEventListener('resize', function () { clearTimeout(resizeTimer); resizeTimer = setTimeout(function () { if (!map) return; - bleed(); // 版心留白变了,出血量跟着变 map.resize(); fitView(); relayout(); diff --git a/templates/activities/show.php b/templates/activities/show.php index 8bb7779..6897fba 100644 --- a/templates/activities/show.php +++ b/templates/activities/show.php @@ -16,9 +16,58 @@
- -
- + + +
+
+ + + +
📸
+ +
+ +
+ +
+ + + + + + + + + + + count($membersTop)): ?> + 等 人 + +
+ +
+ 0): ?> + 📍 个地方 + + 0): ?> + 🕒 天 + + 🧳 个节点 +
+
+
@@ -174,19 +223,8 @@ require dirname(__DIR__) . '/comments/_block.php'; } if (!route.length) return; - /* 地图铺满整个视口宽度:把正文版心的左右留白用负 margin 还回去。 - ⚠️ 不能写 CSS `calc(-1 * var(--shell-pad))`:自定义属性里的 `100%` 在使用它的 - 元素上解析,地图在版心内部(宽 1080),算出来只有 -gutter,铺不满。 - 这里读 container 实际渲染出的 padding 反推;也不用 100vw(含滚动条会溢出)。 */ - function bleed() { - var host = el.closest('.container'); - if (!host) return; - var pad = parseFloat(window.getComputedStyle(host).paddingLeft) || 0; - if (pad <= 0) return; - el.style.marginLeft = (-pad) + 'px'; - el.style.marginRight = (-pad) + 'px'; - } - bleed(); + /* 批 21:地图不再铺满整个视口 —— 用户反馈「上面正文固定宽、下面地图铺满屏」 + 看着奇怪,改回与正文版心同宽(批 18 的负 margin 出血逻辑 bleed() 已删)。 */ map = new AMap.Map(el, { viewMode: '2D', zoom: 5 }); /* 挂到 window 供自动化测试(真浏览器量标记位置)与线上排查用;页面卸载即释放 */ @@ -386,14 +424,13 @@ require dirname(__DIR__) . '/comments/_block.php'; 所以缩放结束后按新的像素位置重排一次(不重新适配视野)。 */ map.on('zoomend', function () { drawRoute(true); }); - /* 改窗口大小 / 旋转屏幕 / 换分辨率:地图宽度是铺满视口的,容器一变 + /* 改窗口大小 / 旋转屏幕 / 换分辨率:地图容器跟着版心变,容器一变 就要按新尺寸重新适配视野并重排标记。防抖 200ms。 */ var resizeTimer = null; window.addEventListener('resize', function () { clearTimeout(resizeTimer); resizeTimer = setTimeout(function () { if (!map) return; - bleed(); // 版心留白变了,出血量跟着变 map.resize(); drawRoute(); // 重新适配视野 + 按新像素重排标记 }, 200);