v24 第 19 批:拖动上传 / 返还按钮 / TripLog 补迁 / 参与人回填 / 预览翻页 / 行程按钮(用户 8 条)
用户 8 条原话要点:
①「为所有的图片视频附件上传处,提供拖动上传的功能」
②「旅程详情页面,增加返回按钮,点击后返回到旅程列表」
③「已有行程的出行方式,包括航班号,怎么没从 triplog 系统迁移过来?」
④「题图右侧有一句话显示,但点编辑进去却看不到这句话」
⑤「编辑行程,参与的人没有带进来,还是空的。同样的问题也出在观影编辑时」
⑥「预览的时候左右有箭头可以直接看下一张;md5 的文件名放在预览图下面没有意义,去掉;
如果是文件,则在图标上显示文件名」
⑦「计划行程的修改/完成/未成行横着排列放行程右上角,修改黄、完成绿、未成行红;
实际行程的编辑删除也放右上角,编辑黄、删除红」
⑧「选人弹框暂存区太小了看不见人;前面的复选框没用,去掉」
🔴 抓到的两个真根因(都不是"没写对",是两处看似无关的地方):
1. **⑤ 参与人不回填 = 两个独立原因叠加**
(a) `Trip::node()`(单条)只 `SELECT *`,**不带 members/attachs**;而
`Trip::nodes()`(列表)是带的。编辑页走前者 → `$ppSelected=[]` 永远空。
同一处还让编辑页的照片墙永远不显示。→ 单条也挂 members/attachs/花销。
(b) `person_picker.js` 的 `init()` 末尾**无条件** `refresh()` → `syncChips()`
第一句 `chips.innerHTML=''` 再从**懒建**的候选清单重建 —— 首屏清单还没建,
一个 input 都没有 → **服务端渲染的 chips 全被清掉、补不回来**。
症状极隐蔽:curl 看到的 HTML 里有 3 个 chip,浏览器里数出来是 0。
→ `syncChips` 加 `if (!built) return;` 守卫 + `ppSetSelected` 前先 `box.ppBuild()`。
2. **④ 那句话 = 详情页读了两个字段**:`display:summary` + `events.note`(老字段,
表单里早没有输入框)→ 老数据的 note 在详情页冒出来、却无从修改。
→ 详情页只渲 summary;老数据给一句"点这里补简介"的提示;
由 `_fb_b19_backfill.php` 一次性搬进 summary。
本批新增能力:
- **拖动上传**(第 1 条):app.js 自动扫全页 `input[type=file]` 挂 DnD,**不改任何模板**
→ 全站十几处上传点一次到位;`data-pick-input` 指向的**预览图**也当落点
(题图/头像的 input 是 sr-only,往图上拖才自然)。三个必须踩准的点:
`dragover` 必须 preventDefault;`dt.items.add` 真写回 `input.files`;
写回后**手动派发 `change`**(`input.files=` 不会自己触发,预览/裁剪器都听 change)。
MutationObserver 补挂动态出现的框。
- **预览左右翻页**(第 6 条):候选 = 同页全部 `[data-prev]`(文档顺序),
`‹ ›` 按钮 + 键盘 ←→(**只在图片上**接管方向键,视频/PDF 里方向键另有含义);
只有一个附件时箭头隐藏(点了没反应比没有更糟);右上角带"第几张/共几张"。
- **附件墙**(第 6 条):缩略图下**不再写**(哈希)文件名;文件的名字压到**图标里**;
`att-doc-name` 两行封顶 + 省略号(长名不撑变形)。
- **行程按钮**(第 7 条):`.node-ops` 由 `column` 改 `row` + 贴顶;新增
`.btn-warn/.btn-ok/.btn-del`(**实心**黄/绿/红)—— 与全站淡红描边的 `.btn-danger`
刻意分开(改它会连带改掉十来处「删除」的样子)。「编辑」保留(改标题/加花销的唯一入口)。
- **选人弹框**(第 8 条):暂存区 148px 死高度 → `min-height:132px; max-height:46vh`,
chip 头像 20→28px;多选加 `.pp-compact` 隐藏复选框(**只 `display:none`,
input 仍在 DOM** —— 提交语义全靠它,绝不能 remove)。
上线补迁(`_tooling/_fb_b19_backfill.php`,幂等、不覆盖已有值):
- ③ TripLog 的 `travel_modes.mode_type` + `trip_number/line_name/carrier`
→ FamilyBook 的专列。`drive_own/drive_rental` 都映射成 `drive`(FamilyBook 的
"自驾"是一个方式,"哪台车"由 vehicle_id 表达);车辆按**车牌**对上;
顺手删掉 detail 里那段重复的「班次:…」。线上实跑 18 条、跳过 2 条。
- ④ `events.note` → `trip_details.summary`(只搬 summary 为空、note 有内容的,2 条)。
验证(本地全套 + 线上):
- e2e **960/0**(+23 条本批断言,含"成员清空后编辑页也不显示"的反向)· 升级 149/0 ·
城市解析 32/32 · 桌面几何 428/0 · 真机 UI **27/0**(拖动/翻页/按钮几何与配色)·
真机选人 chip **2/0**。
- **反向验证 4/4**:①`Trip::node` 去掉 members ②`syncChips` 去掉守卫
③详情页把 note 渲回来 ④附件墙把文件名写回去 —— 四种坏法全部被抓到,
且还原后 e2e 复绿 960/0。
- 线上:部署成功(双版本 l 通过 + 19 条站点自检全过);补迁后 18 段行程有出行方式、
7 段有车次、10 段挂车、2 个旅程有简介;线上 `Trip::node()` 实调
members/attachs 都回来了(部署前是 0)。
This commit is contained in:
+109
-11
@@ -443,6 +443,17 @@ textarea.input { min-height: 76px; resize: vertical; }
|
||||
.btn-ghost:hover { background: #FAF7F1; }
|
||||
.btn-danger { background: #fff; color: var(--red); border-color: rgba(192,83,63,.35); }
|
||||
.btn-danger:hover { background: #FDF1EE; }
|
||||
/* v24 第 19 批 用户第 7 条:行程行的动作按钮要**看得出颜色**——
|
||||
「修改是黄色背景,完成绿色,未成行红色」。
|
||||
⚠️ 这三个是**实心**色块(不是 .btn-danger 那种淡红描边):用户说的是背景色,
|
||||
要的是"一眼分得清哪个是哪个"。所以单独定义,不去改 .btn-danger 的既有语义
|
||||
(全站十来处「删除」都在用它,改成实心会到处变样)。 */
|
||||
.btn-warn { background: #E8A33D; color: #fff; border-color: transparent; }
|
||||
.btn-warn:hover { background: #D79230; }
|
||||
.btn-ok { background: #5E9E4E; color: #fff; border-color: transparent; }
|
||||
.btn-ok:hover { background: #518B43; }
|
||||
.btn-del { background: #C0533F; color: #fff; border-color: transparent; }
|
||||
.btn-del:hover { background: #AC4835; }
|
||||
.btn-sm { padding: 5px 12px; font-size: 13px; }
|
||||
.btn-row { display: flex; gap: 10px; margin-top: 20px; }
|
||||
|
||||
@@ -1928,11 +1939,22 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
}
|
||||
.att-doc {
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
gap: 4px; width: 100%; height: 100%;
|
||||
gap: 4px; width: 100%; height: 100%; padding: 6px;
|
||||
font-size: 12px; font-weight: 700; letter-spacing: .5px; color: #fff;
|
||||
text-align: center; box-sizing: border-box;
|
||||
}
|
||||
.att-doc::before {
|
||||
content: ""; width: 34px; height: 34px; border-radius: 6px; background: rgba(255, 255, 255, .22);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
/* v24 第 19 批 用户第 6 条「如果是文件,则在图标上显示文件名」:
|
||||
文件名压着色块显示 —— 文件就是**读名字**的,名字才是最该看清的东西。
|
||||
⚠️ 两行封顶 + 省略号:文件名可以很长(「学位学历证书_博士学位证书_2022-07-08-17-05-53.jpeg」),
|
||||
不截就会把图标撑变形、把相邻的格子挤歪。完整名字仍能从悬停 title / 下载看到。 */
|
||||
.att-doc-name {
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
||||
overflow: hidden; word-break: break-all; line-height: 1.35;
|
||||
max-width: 100%; font-size: 11px; font-weight: 600; letter-spacing: 0;
|
||||
}
|
||||
.att-doc-pdf { background: #B0342C; }
|
||||
.att-doc-doc { background: #2C5AA0; }
|
||||
@@ -1944,11 +1966,30 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px;
|
||||
margin-top: 4px; font-size: 12px; color: var(--muted);
|
||||
}
|
||||
/* v24 第 19 批 用户第 6 条:缩略图下面**不再放文件名**(那串是上传时的原始名,
|
||||
图片/视频是"看"的,一串哈希既认不出内容也不比缩略图本身多信息)。
|
||||
`.att-fname` 的规则保留 —— 万一将来又要在某处显示一行名字,样式不用重写。 */
|
||||
.att-fname {
|
||||
flex: 1 1 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
color: var(--ink);
|
||||
}
|
||||
.att-fsize { flex: 0 0 auto; }
|
||||
/* ---- 拖动上传(v24 第 19 批 用户第 1 条)----
|
||||
用户原话:「为所有的图片视频附件上传处,提供拖动上传的功能,就是允许用户,
|
||||
通过把文件拖动到那里进行上传。」
|
||||
⚠️ 类名 `.dropzone` 由 app.js **自动给页面上每个 file 控件挂上**,
|
||||
并且给「`data-pick-input` 指向该控件的预览图」也挂一份 —— 题图/头像那类
|
||||
控件的 input 是 sr-only(看不见),往旁边那张图上拖才是最自然的动作。
|
||||
全站十几处上传点一次性都获得这个能力(不改任何模板)。
|
||||
⚠️ 落在哪儿由 `.is-over` 高亮体现;真正的落文件逻辑在 app.js 里
|
||||
(DataTransfer → input.files → 手动派发 change)。
|
||||
⚠️ 曾经想过再加一句常驻的「也可以把文件拖到这里」小字,**去掉了**:
|
||||
十几个上传点各多一行灰字,页面会明显变吵,而"拖动"本身不是必须被教导的能力。 */
|
||||
.dropzone { position: relative; }
|
||||
.dropzone.is-over {
|
||||
outline: 2px dashed var(--brand); outline-offset: 3px;
|
||||
background: #FDF3EC; border-radius: var(--radius-s);
|
||||
}
|
||||
.att-meta form { display: inline; margin: 0; }
|
||||
/* ---- 全局预览层 ---- */
|
||||
.pv {
|
||||
@@ -1990,6 +2031,28 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
.pv-pdf {
|
||||
width: 92vw; height: 100%; border: 0; border-radius: 8px; background: #fff;
|
||||
}
|
||||
/* ---- 左右翻页箭头(v24 第 19 批 用户第 6 条)----
|
||||
用户原话:「预览的时候,左右有箭头,可以直接查看下一张。」
|
||||
⚠️ 左右各一个圆形按钮,垂直居中、压在内容两侧。禁用态(第一张/最后一张)
|
||||
要**看得出来**(半透明 + 禁止光标),否则用户会以为"点了没反应"。 */
|
||||
.pv-nav {
|
||||
position: fixed; top: 50%; transform: translateY(-50%); z-index: 5;
|
||||
width: 46px; height: 46px; border-radius: 50%; border: 0; cursor: pointer;
|
||||
background: rgba(255, 255, 255, .2); color: #fff; font-size: 30px; line-height: 1;
|
||||
display: flex; align-items: center; justify-content: center; padding: 0 0 4px;
|
||||
}
|
||||
.pv-nav:hover:not(:disabled) { background: rgba(255, 255, 255, .34); }
|
||||
.pv-nav:disabled { opacity: .28; cursor: default; }
|
||||
/* ⚠️ 与 .pv 同一个老坑:这里自己声明了 display:flex,不补 [hidden] 就藏不住 ——
|
||||
只有一个可预览项时箭头必须消失,靠的就是这条。 */
|
||||
.pv-nav[hidden] { display: none; }
|
||||
.pv-prev { left: 14px; }
|
||||
.pv-next { right: 14px; }
|
||||
@media (max-width: 560px) {
|
||||
.pv-nav { width: 40px; height: 40px; font-size: 26px; }
|
||||
.pv-prev { left: 6px; }
|
||||
.pv-next { right: 6px; }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.att-cell, .att-thumb { width: 108px; }
|
||||
.att-thumb { height: 80px; }
|
||||
@@ -2041,6 +2104,14 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
.pp-item:last-child { border-bottom: 0; }
|
||||
.pp-item:hover { background: var(--line-2); }
|
||||
.pp-item input { accent-color: var(--brand); flex: 0 0 auto; }
|
||||
/* v24 第 19 批 用户第 8 条:「前面的复选框就没用了吧,去掉吧。」
|
||||
⚠️ 多选时复选框**确实没用**了 —— 第 18 批起"选谁"靠点头像/名字(点击整行),
|
||||
勾选与否只由暂存区的 chip 表达,一个小小的方框反而是干扰。
|
||||
JS 给多选组件加 `.pp-compact` 才隐藏;**单选不隐藏**(单选的圆点是他唯一
|
||||
能看见"现在选的是谁"的地方)。
|
||||
⚠️ 只是 `display:none`,input **仍在 DOM 里** —— 表单提交全靠它。
|
||||
千万别改成 remove()(那就变成"选了保存后没人")。 */
|
||||
.pp-compact .pp-item input { display: none; }
|
||||
.pp-item .pp-av {
|
||||
width: 30px; height: 30px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
|
||||
background: #F1E7D6;
|
||||
@@ -2342,6 +2413,14 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
.trip-title { font-size: 17px; font-weight: 600; margin: 0; }
|
||||
.trip-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 13px; color: var(--muted); }
|
||||
.trip-sum { margin: 0; font-size: 14px; color: var(--muted); line-height: 1.6; }
|
||||
/* 老数据(只有 events.note、还没搬成 summary)时的一句提示 ——
|
||||
见 templates/trips/show.php 里第 19 批 第 4 条的说明。 */
|
||||
.trip-sum-legacy {
|
||||
margin: 0; font-size: 13px; color: var(--muted);
|
||||
background: #FDF3EC; border: 1px dashed rgba(168, 74, 40, .3);
|
||||
border-radius: var(--radius-s); padding: 8px 10px;
|
||||
}
|
||||
.trip-sum-legacy a { font-weight: 600; }
|
||||
.trip-people { display: flex; flex-wrap: wrap; gap: 4px 8px; }
|
||||
|
||||
.trip-hero { display: flex; gap: 20px; flex-wrap: wrap; }
|
||||
@@ -2384,7 +2463,16 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
.node-row.status-done .node-badge { background: #E6F0DC; color: #4A7226; }
|
||||
.node-place, .node-time { font-size: 12px; color: var(--muted); }
|
||||
.node-detail { margin: 0; font-size: 14px; line-height: 1.65; }
|
||||
.node-ops { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
|
||||
/* v24 第 19 批 用户第 7 条:动作按钮**横着排列**、放在行程**右上角**。
|
||||
「计划行程的三个按钮,修改、完成、未成行,横着排列,放在行程右上角」
|
||||
「实际行程的编辑和删除按钮,也放在行程的右上角」
|
||||
⚠️ 原来是 `flex-direction: column`(竖着一列、贴右下),用户要的是横排靠上。
|
||||
改成 row + align-self:flex-start(贴着行的顶部对齐),窄屏再允许换行。 */
|
||||
.node-ops {
|
||||
flex: 0 0 auto; display: flex; flex-direction: row; flex-wrap: wrap;
|
||||
gap: 6px; align-items: flex-start; justify-content: flex-end;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.node-edit > summary { cursor: pointer; }
|
||||
.node-add { margin-top: 14px; border-top: 1px solid var(--line-2); padding-top: 12px; }
|
||||
.node-add > summary { cursor: pointer; font-size: 14px; }
|
||||
@@ -3388,18 +3476,28 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
/* ---- 暂存区:在搜索框**下面**、候选清单**上面** ----
|
||||
⚠️ 用户说的是"选择框的上半部分"。搜索框在最顶(要一直能打字搜下一个人),
|
||||
所以暂存区放在搜索框之下、清单之上 —— 视觉上仍然属于"上半部分",
|
||||
而且不会因为暂存区变高把搜索框挤走(搜索是要反复用的)。 */
|
||||
而且不会因为暂存区变高把搜索框挤走(搜索是要反复用的)。
|
||||
⚠️ v24 第 19 批 用户第 8 条:「暂存区太小了,高度太低,看不见人」——
|
||||
原来只有 148px 且一行能塞很多 chip,看着像一条空横条。
|
||||
现在:① 高度提到 min 132 / max 46vh(跟着弹框走,不再写死一个矮数);
|
||||
② chip 改成**大一点**(头像 28px、字号 13.5),并允许每行少放几个,
|
||||
让人**看得见是谁**;③ 有内容时才占位(没人暂存时不显示,见 JS)。 */
|
||||
.pp-stage {
|
||||
border: 1px solid var(--line); border-radius: 8px;
|
||||
background: #FDFBF7; padding: 8px 10px; margin-bottom: 8px;
|
||||
max-height: 148px; overflow-y: auto;
|
||||
background: #FDFBF7; padding: 10px 12px; margin-bottom: 8px;
|
||||
min-height: 132px; max-height: 46vh; overflow-y: auto;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
.pp-stage[hidden] { display: none; }
|
||||
.pp-stage-label {
|
||||
display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px;
|
||||
display: block; font-size: 12px; color: var(--muted); margin-bottom: 8px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.pp-stage-label b { color: var(--brand); font-variant-numeric: tabular-nums; }
|
||||
.pp-stage-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.pp-stage-chips {
|
||||
display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
/* ---- 底部按钮:取消在左、确定在右(主操作靠右,与全站一致)---- */
|
||||
.pp-actions {
|
||||
@@ -3408,11 +3506,11 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
}
|
||||
|
||||
/* ---- 暂存区里的 chip 比外面的紧凑一点(弹窗里空间有限)---- */
|
||||
.pp-stage-chips .pp-chip { font-size: 12.5px; padding: 2px 5px 2px 3px; }
|
||||
.pp-stage-chips .pp-chip img { width: 20px; height: 20px; }
|
||||
.pp-stage-chips .pp-chip { font-size: 13px; padding: 3px 8px 3px 4px; }
|
||||
.pp-stage-chips .pp-chip img { width: 28px; height: 28px; }
|
||||
|
||||
/* ---- 窄屏:弹窗几乎占满,暂存区别太高(把清单挤没了就没法选了)---- */
|
||||
@media (max-width: 480px) {
|
||||
.pp-modal-box { max-height: 86vh; }
|
||||
.pp-stage { max-height: 104px; }
|
||||
.pp-modal-box { max-height: 88vh; }
|
||||
.pp-stage { min-height: 108px; max-height: 38vh; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user