b6dc60452a97544c55d04aac1b6ebc1c093c52d5
13
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f9e30bd7d8 |
v40 第 40 批:观影九条 + 修掉一个「编辑一次就把观影时间弄成 NULL」的数据丢失 bug
用户原话(九条):
①「观影详情页面左上角的观影记录,应该和观影的列表页面里面保持一致」
②「观影详情页面的编辑,还有返回观影的按钮,应该和观影列表页面里面的记一场电影保持一致」
③「删除按钮就用红色背景颜色」
④「观影详情里面的编辑按钮应该也是弹出的,和新增是一样的,编辑完以后,页面动态更新」
⑤「影院管理部分,注意控制列的宽度…最右侧的操作、编辑和删除两个按钮,已经被挤成两行了,
尝试让编辑和删除排在一行,宽度给它留够」
⑥「编辑使用蓝色的背景,删除使用红色的背景」
⑦「高级搜索里面的日期范围,也使用我们最新的控件」
⑧「观影统计里面,如果这个人在名册里面,但是他一部电影的记录也没有,就不要在下面的这个
按观影人统计里面出现了,太长了」
⑨「观影统计里面,月份分布和星期分布,12 个月用 12 种不同的颜色,星期 7 天用 7 种不同的颜色」
🔴🔴 **本轮最重:修掉一个数据丢失级的真 bug**(v37 引入、**影响所有 `.dt-range` 页面**)
· 现象:任何带已存时间的编辑表单,只要 JS 跑起来,日期在用户**什么都没动**时
就被抹掉 ⇒ 提交后库里 `starts_at` 变 **NULL**。
· 根因:`assets/js/app.js` 的 `bindParts()` 首屏写成 `rebuildDays(false)` ——
那个参数是"保留已选日吗",传 false 会**清空"日"下拉**;紧接着的 `sync()`
看到 y/mo 有值而 d 空 ⇒ 算出 '' ⇒ **把隐藏的 `*_date` 写成空串**。
· 修:改成 `rebuildDays(true)`。
· ⚠️ 为什么一直没被发现:e2e 是**服务端**驱动的(直接 POST 字段、不经过 JS);
真机探针只验"控件可见可点"与"改值能写回",**没验"什么都不动时值还在不在"**。
⇒ 补了回归断言 + 反向验证(把 true 改回 false 必须红)。
· ⚠️ 影响面大于观影:**日程 / 任务 / 旅程**的起止同样走 `.dt-range`,
修在共用件等于顺手修了四个模块。
① 详情页头部与列表页统一:`<span class="sub">观影记录</span>` → **`<h1>观影记录</h1>` + sub=片名**。
②③ 详情页「编辑」「返回观影」用**裸 `.btn`**(= 列表页「记一场电影」的模块品牌实心);
「删除」用 `.btn-del`(**红色实心**,原来是淡红描边的 `.btn-danger`)。
⚠️ 用户那句"编辑用蓝色"是**影院管理**的(见 ⑥),详情页要的是"与列表页一致" ——
第一版理解错、把详情页编辑也刷成蓝色,**反向验证当场抓住**。
④ 详情页「编辑」改**弹框** + AJAX + 保存后**动态更新**:
· 内嵌**同一份** `form.php`;新增 `$mvModalForce`(`$isEdit` 也强制弹框壳)
与可显式传的 `$mvOpen`;新增 `View::renderPartial()` 供校验失败时把弹框整块重画回 JSON。
· 保存成功:`fetch` 跟 302 → **只换正文容器**(保留滚动位置),并重跑页内内联 JS。
· 🔴 实测踩到并解决的四件事:
1. 模板里裸类名 `Person::roster()` 会**致命**(全局命名空间)⇒ 页面从此中断输出、
且不报给用户 ⇒ 弹框整块消失。必须 `\App\Person::`。
2. `<button form="mvForm">`(表单外的提交按钮):点它真的提交,但挂在 `document` 上的
`submit` 监听**收不到事件**(实测 `XRW=(无)`、`resource_type=document`)⇒ 整页跳走、
红字看不到 ⇒ 主路改成 **click 委托**(preventDefault),`submit` 只兜回车。
3. `innerHTML` 换正文容器后要重跑内联 JS,但**绝不能重跑带 `src` 的外部脚本**
(app.js 等,会二次绑定);本段脚本自己要有**幂等闸门**。
4. flash 是**一次性**的(`take_flash()` 读完即清)⇒ 成功后**不要**再取一次页面,
直接用第一次的 `r.text()`(否则"保存成功却没有任何提示")。
⑤⑥ 影院管理:`.cn-table { table-layout: fixed }` + 操作列 **132px** +
`.cn-ops { flex-wrap: nowrap }` ⇒ 编辑/删除**并排一行**;
新增语义色 `--blue: #2F6FB0`,编辑=蓝实心、删除=红实心
(⚠️ 必须在**页头动作区那一层**也写一条:`.btn` 的特异性会盖过基类)。
⑦ 高级搜索的日期范围换 AntD:新增 `[data-antd-daterange]`(**纯日期**版,
不传 showTime),与观影"时刻"那对共用同一套按需加载 + 中文 locale 逻辑。
⑧ `MovieStats::byPerson` 加 `HAVING cnt > 0`(⚠️ 不能写 `WHERE cnt > 0`:
聚合结果在 WHERE 阶段还不存在)。
⑨ 新增 `MovieStats::MONTH_COLORS`(12 色)/ `WEEKDAY_COLORS`(7 色)+
`monthColor()` / `weekdayColor()`;**统计页"月度趋势"与年度报告"月份分布"同月同色**
(用户没要求,但不一致一眼就看得出)。
验证(全部真机 + 反向验证):
· _fb_b40_movie.py **41/0**(九条逐条 + 两条 🔴🔴 日期不丢);
· _fb_b40_falsify.py **10/0**(日期抹掉 / 编辑按钮配色 / AJAX 主路 三处各自证伪 + 复原回全绿);
· 全路由 e2e **1502/0**(顺带修掉一条**依赖夹具数据**的脆弱断言:
"统计页必须有 bar-chart" → 无数据时改判"要么有图表、要么有空态");
· 页头按钮探针 **60/0** + 反向 **3/3 全部抓到**(删除按钮换成 `.btn-del` 后,
判据与注入目标都已同步更新);v37/v38/v39 各套全绿;
日期控件真机 53/0 + 反向 7/0;观影照片 16/0 + 8/0;DAV 三套;部署静态断言空跑 —— 全绿。
· 线上 11 个关键文件 SHA256 与本地**逐字节一致**。
⚠️ 顺带:`show.php` 的 JS 注释里写了字面 `<script>` / `<main>`,绊倒了我自己"按标签
提取内联 JS 做语法检查"的脚本(**与 v37 那次 `<form>` 同类**)—— 已把注释里的
标签字面量全部改写。⇒ 通识:模板注释里不要出现字面标签名。
|
||
|
|
d83c94e127 |
v37 第 37 批:日期控件换成「年/月/日 + 时/分」五个原生下拉 + 五处界面重做
用户原话(6 条,一次给全):
①「观影详情页面。观影图片下面有一个块…要加照片请点上方「编辑」…这个块不要,
直接把这个块删除。」
②「编辑页面的时间…选日期的这个控件非常非常不好用。就是如果我把年和月那个地方
拉下来,我选完年和月之后,它并不会回到让我选日的那个界面,它还是不动的…
要么换一个控件,要么在那个地方加一个确定按钮…现在开始和结束时间分在了两行…
能放得下的话就应该放一排…不要让它默认显示在两行上…"散场时间可不填。"这句话去掉。」
③「首页上今天的日程…分了几列,列之间的空隙非常非常的大…日程里面的那两个色块
就是紧挨着的。所以…应该让它们紧挨着。」
④「现在怎么部署的时间这么长?…你修改了哪些页面,你就只测试哪些页面就可以了。」
⑤「日程模块的新建日程…"加上"和"取消"两个按钮在右上角,离得特别远,根本看不到…
每个输入框前面的标题都太小了…都应该加粗、变大。」
⑥「1.新建任务的截止时间,也和观影一致。2.新建任务时竟然没有添加附件…
3.新建任务也弹出一个框,新建完之后刷新看板页面。4.观影新增也弹框输入所有信息。」
② 日期控件:**真根因是"这个控件给不了"**
`<input type="date">` 的日历弹层由**浏览器自己画**,我们碰不到 DOM、也控制不了流程;
Chrome 在"点顶部 `2026年 10月` 切到年月视图"之后,要靠用户**再点一次日期或按 Esc**
才回得到日期视图,各家还不一致 ⇒ 用户遇到的"选完年月就卡住、选不了日"。
修:`_datetime.php` 重写成 **年 / 月 / 日 三个原生 <select> + 时 / 分**,
选完即生效、无需"确定";秒默认去掉(用户"只记录到时和分")。
⚠️ 提交契约**一字未改**:`*_date` 仍是真字段(改 `type=hidden`)+ `*_hour/_minute`。
⚠️ `required` 落在隐藏控件上会让 Chrome 报 "not focusable"(点保存什么都不发生)
⇒ 改用 `data-dt-required` 声明、JS 自查(12 个调用点全部受影响)。
起止两端:`.dt-range.dt-cols` + `flex-wrap` → **宽屏一行、窄屏自动换行**
(不用 JS 量宽度:字体/动画/缩放时量不准,flex-wrap 是浏览器布局的一部分)。
🔴 顺带挖出真 bug:旅程「修正」表单的 `.dt-range` 只有 **22px 宽**
—— 它长在 `.node-ops`(一排小按钮的容器)里,展开的内容只有"修正"两个字那么宽。
修:`.rev-panel{position:absolute}` 纯 CSS 浮出来(不动 DOM,避免牵动节点列表排版)。
③ 首页冲突色块"空隙特别大":与日历页逐字对比出来的
日历页 `left: calc(2px + L%)` / `width: calc(W% - 4px)` → 只留 4px、紧挨;
首页却写死 `calc(46px + L%)` / `calc(W% - 50px)` —— 每块左右各扣 23/25px,
两列时那 46px **没法均分**、全挤进了中间的缝(实测 ≈50px)。
修:`left: calc(46px + L% + 2px)` / `width: calc(W% - 4px)` → 缝恒为 4px。
⑤ 日程弹框"按钮看不到":**两个**根因叠加
① `calendar/index.php` 里**多写了一个 `</div>`** —— `.cal-modal-panel` 被提前闭合,
底栏 `<div class="cal-modal-foot">` 成了 `.cal-modal` 的直接子元素、被挤到面板外;
② `.cal-modal-panel` 是 flex 列,但**它下面的 `<form>` 不是** →
`.cal-modal-body{flex:1 1 auto}` 的上下文只到 form 为止,body 压不下去也不滚。
另有 `.cal-modal-panel` 的 max-height 没算上 `.cal-modal` 自己的 padding(差 4px)。
三处都修;标签只对**弹框里**的 `.field > label` 加粗放大(14px/600/深色)。
⑥ 任务新建:内联卡片 → 弹框;**新增附件**(`attach[]`,服务端零改动能收);
`back=tasks` → 新建完回**看板**刷新。观影新增:列表页内嵌同一份 form.php 弹框。
① 详情页删掉那句提示块。④ 流水线改**增量**:`run_suite_if <标签>` + 从 git 改动
推模块;**一碰共用件就退回全量**(app.js/app.css/layout/index.php/_datetime.php…),
认不出来也全量(保守:少跑一段发坏东西的代价远大于多跑十分钟)。
🔴🔴 本轮最值得记的教训:**DOM 嵌套错误比语法错误难查得多**
在 movies/form.php 与 calendar/index.php 各写坏一次嵌套(多/少 `</div>`、多 `</form>`),
两处**都通过 php -l、页面也打得开**,症状却是"按钮跑到面板外"与
"编辑保存静默失效"(保存键落到 form 外面)。⇒ 模板里 `<?php if ?>` 与 `</div>`
必须**按分支配对**,改完用真机渲染 dump 一次 div 深度;光靠 php -l 发现不了。
验证(全部真机 + 反向验证):
· _fb_b34_dtrange.py **61/0**(四个页面:年/月/日/时/分 都可见可点 +
选完三个下拉**立刻**写回 *_date + 改月重建"日"列 + 起止默认同一行 + 无 JS 报错);
· _fb_b34_dtrange_falsify **7/0**(三处注入各自必须变红);
· _fb_b37_ui.py **27/0**(详情页无提示块 / 首页冲突缝 ≤8px / 弹框按钮在面板内且在
视口内 / 标签加粗 / 任务弹框+附件+回看板 / 观影列表页弹框);
· _fb_b37_falsify **5/0**;
· _fb_b35_movie_media.py **16/0**、_fb_geom_interact **109/0**、e2e **1503/0**、
b30_ui2 16/0(含首页冲突分列)、_b30_import 26/0、b18/b21/b23/…全绿、
DAV 三套全绿、部署静态断言空跑通过;
· 线上 app.js / app.css / movies/form.php / calendar/index.php 的 SHA256 与本地一致。
· 顺带修掉 _b30_import 的一个**时序竞争**:它用外部上游(联想词服务)的二次探测
当对照,浏览器取词在前、探测在后,上游抖一下会误报成"回归"(已改成两次确认)。
|
||
|
|
2fda455a59 |
v36 第 36 批:时间控件改「日期 + 时/分下拉」(修掉 v35 真没生效的可见性 bug)+ 详情页去掉补照片
用户原话:
①「观影模块,开始时间和结束时间是空白的,无法选择,可以调整为,前面是日期,
后面是时和分,只记录到时和分就行了。时和分,是下拉的。」
②「观影详情页面,不需要设置补充照片的功能,都放在编辑页面就行了。」
① 时间控件:**v35 的修复根本没生效**(我的锅),而且探针连绿两轮没抓到
真因:upgradeRange 里只给范围**组**加了 `.is-native-range`,
**没给两个端点 `.dt-input` 加 `.is-native`** ⇒ CSS
`html.js .dt-input:not(.is-semi) .dt-native { visibility: hidden }`(特异性 0,3,0)
照样命中 ⇒ 控件 **visibility:hidden**:**占着 48px 高度、完全不可见、点不到**
(用户看到的"一片空白")。
🔴 探针为什么没抓到:它用 `offsetParent !== null` 判"可见" ——
而 **visibility:hidden 的元素 offsetParent 仍然非 null**,于是连绿两轮。
⇒ 判可见改成 computed visibility + display + getBoundingClientRect 有宽高
(_fb_b34_dtrange.py 与 _fb_geom_interact.py 都已改,后者直接用 is_visible())。
修法(比 v35 更笨但更稳):**不造新控件、不加 class 猜 CSS**,直接
① 端点加 `.is-native`(压掉藏原生那条规则);
② 删掉"秒"那一档(连它前面的冒号)—— 用户"只记录到时和分";
③ **inline 强制 display/visibility**(双保险;v35 就是被历史 CSS 规则翻掉的)。
CSS 侧关键几条加 `!important`,并删掉 v35 遗留的 `.dt-merged` / `.dt-time-input`
规则(免得后人误以为还有"收起来的下拉")。
形态 = 「日期框 + 时下拉 + 分下拉」,提交契约仍是 *_date/_hour/_minute
(秒栏不再有,服务端 read_dt_input 对缺失 *_second 按 00)。
② 详情页移除"补一张照片"表单(form.photo-add),留一句"要加照片点上方编辑"。
⚠️ 服务端 `movies/photo_add` 路由**保留**(老书签/直接 POST 仍可用),只是不再有入口。
验证(全部真机 + 反向验证):
· _fb_b34_dtrange.py **37/0**:四个表单页断言「日期框可见 + **时**下拉可见 +
**分**下拉可见 + **没有"秒"** + 只改时分也能提交」;
· _fb_b34_dtrange_falsify.py **5/0**:注入"去掉 .is-native"(= v35 的真错)
→ **12 条断言变红** ⇒ 证明新判据真的能抓到那个 bug;
⚠️ 而"只撤 CSS !important"或"只撤 inline 强制"都 0 条变红(被别的兜底救回来)——
做证伪必须挑"真正决定行为的那一层";
· _fb_b35_movie_media.py **16/0**(⑥ 改为"详情页没有补照片入口");
· e2e **1503/0**(详情页入口断言反过来)、几何交互 **96/0**(+4 条时分下拉);
· 各批真机/反向全绿、部署静态断言空跑通过;
· 线上 app.js / show.php 的 SHA256 与本地一致(v36 已上线)。
|
||
|
|
5c81f1991f |
v35 第 35 批:日期控件改用原生(Semi 时间面板不可用)+ 观影附件上传真修好 + 票根/合影归并成「观影照片」
用户原话(三段):
①「选择日期那个地方给,非常不好用。如果我不选日期,只选时间,框里面仍然是空白的,
也没有确定按钮,这个需要重新调整一下。」
②「现在上传附件,也就是在观影模块里面上传附件,怎么还是有问题?上传的附件现在
都没有保存,有时候我上传两个附件,它只保存一张。」
③「除了海报之外,票根、现场照片等等都不分类别,都只分一个类,放在一块。…
只有海报是单独的,因为它是从豆瓣里面下载回来的。其他的都放在一个模块就行,
然后所有照片都上传到一起,直接写"观影照片"就可以了。」
① 日期控件:**Semi 的时间面板根本不可用**(这条最费时间)
实测(独立最小复现页 + 真机):Semi 的 dateTimeRange 面板**只给日期网格**,
时间藏在 `.semi-datepicker-switch-time` 那个小时钟按钮后面;**点了它切到时间面板,
面板仍然是空的**(timeLists=0、无选项、无 footer、无"确定")→ 根本无法"只选时间"。
单点 dateTime 同样。⇒ 与用户描述完全一致。
修:弃用 Semi 的时间选择,改用**原生** date + time(step=1)
(24 小时制、精确到秒、点了就选、不选日期也有值、**不需要**确认按钮)。
原来的 时/分/秒 三个下拉**仍留在 DOM**(POST 契约 *_date/_hour/_minute/_second
一字不改),只是加 .dt-merged 视觉收起。
app.js 的 upgradeRange()/upgrade() 都不再挂 Semi;CSS 加 .is-native-range 系列
把 Semi 时代的"藏原生 / 两端让位"规则覆盖回来。
② 观影附件上传丢文件:**两条路径都断了**
· 暂存路径(拖放):上传框走"拖进来即刻上传到暂存区",令牌进 stage_token[];
而 movies/create|update 与 movies/photo_add **只读 $_FILES['photo']**
⇒ stage_token[] **从来没人认领** ⇒ 拖进去的一个都没存。
· Movie::update() **完全不写 photos**(那句"photos 由单独的删除路由维护"的注释下
根本没写),而路由明明算好了 $d['photos'] 传进来 ⇒ 编辑保存丢照片。
修:路由新增 stage_token[] 认领循环(与 $_FILES 路径并存);photo_add 加兜底;
Movie::update 补上 savePhotos。
③ 归并:表单去掉独立"票根"上传框,"现场合影"并入**一个**「观影照片」块;
详情页「影像记录」→「观影照片」、去掉票根/照片分类标签。
数据层本来就是平的(event_photos 只有 key/position);
⚠️ 旧数据 movie_details.ticket_key **仍要显示**(历史票根不能丢)+ 服务端仍接收
$_FILES['ticket'](向后兼容,直接 POST 仍可存)。
验证(全部真机跑,不是"看代码觉得对"):
· _fb_b34_dtrange.py 33/0(四个表单页:组可见 + 两端日期框可见 + 两端时间框
可见且 step=1 + 三个原生下拉仍在 DOM + 只选时间也能写回 *_hour/_minute/_second);
· _fb_b35_movie_media.py 14/0(点击上传 2 张→库里 2 行;真 HTTP 打 upload_stage.php
拿 stoken→随表单提交→落库;编辑补传→3 行;详情页补传→4 行;归并断言);
· 反向验证:_fb_b34_dtrange_falsify 4/0、_fb_b35_media_falsify 8/0;
· e2e 1503/0(两条断言随 v35 语义更新)、几何交互 92/0、各批真机/反向全绿、
部署脚本静态断言空跑通过;线上 app.js / form.php 的 SHA256 与本地一致。
顺带修掉的工具链脆弱(本轮暴露):
· push.sh 开头加"确保本地测试库在跑"的守卫(长时间跑流水线时 mysqld 会被环境
回收 → e2e/真机探针全崩,看着像代码坏了);
· 探针别用固定字符窗口找函数体(注释一长就假红)、断言别写死字段名前缀
(改期表单是 rev_ends_at_*)、set_input_files 的 id 要照 _stage_drop 的净化规则
(photo[] → sd-photo)、提交按钮要精确定位(编辑页有多个 submit)。
|
||
|
|
f0437e6c93 |
v24 第 21 批:无刷新删除与新建日程 / 实际行程不回退 / 行程按时间排 / 统一上传框 / 时间范围控件修复(用户 8 条)
用户 8 条原话要点:
①「名册页面,删除一个联系人后,能否不刷新页面,删除成功后,直接把这个人,
从当前页面的 dom 中删除,页面保持不动。」
②「观影模块,海报和票根以及照片都带进来了,但是没有缩放,把页面撑破了,
应该缩放成缩略图。」
③「个人设置页面,天气设置那里,把:只影响你自己首页上的那一格 / 填个城市名就行
(如「青岛」)… 这些提示,都删除。把城市单独放一行,精度维度放在一行。」
④「旅程模块,实际行程编辑后,点击保存,竟然又变成计划行程了,这个不合理,
实际行程永远是实际行程,不会再变回计划行程了。」
⑤「旅程模块,旅程列表,行程列表,都是按照行程的开始时间排序的,最新的在最上面,
而不是按照编辑修改时间。」
⑥「旅程的详情页面,右上角的按钮,设置在最右侧,和其他模块保持统一。」
⑦「所有上传图片等附件的地方,都不再放按钮,而是放一个框,点击这个框实现点击上传
按钮的功能,拖动图片进这个框里,实现拖动多个上传功能。框里面写一句话:
点击或拖动进行上传,其他的功能和现在保持一致。」
⑧「日历中新建日程,无论是点击按钮还是点击日历中间,都是采用弹框的形式进行添加,
这样不影响当前日历的显示,添加完成后,动态修改当前视图,不要刷新。添加日历日程,
开始时间和结束时间那里,用我们统一的时间范围选择器重做,现在功能无法使用。
选择日历本,带上颜色。」
🔴 五个真根因(全都不像 bug):
① 🔴 **④「实际行程变回计划」= 表单字段同名合并**(本批最要紧的一条)
暂存上传的令牌字段原叫 `stage[]`,与行程表单里那个隐藏的 `stage`(plan/actual)
**同名**。PHP 解析表单时同名会**合并进同一个键**:
`stage=actual` + `stage[]=tok` → `$_POST['stage'] = ['actual','tok']`
(`[]` 只是"往同一个键追加"的记号,**不构成另一个键名**。)
于是路由里 `(string)($_POST['stage'] ?? 'plan')` 得到 `"Array"` →
`in_array('Array', ['plan','actual'])` 为假 → **静默回落成 'plan'**。
⚠️ 只有"拖过图片的行程"会犯(没拖就没有 `stage[]`)→ 症状是"时好时坏"。
修法:令牌字段改名成 `stage_token[]`(改的是**字段名本身**,不是"是数组就取第一个"
绕过去 —— 同名合并是 PHP 的表单语义,绕过去只会把坑留给下一次加字段)。
**另加一道语义保险**:编辑路径**一律沿用库里那条自己的 stage**,不读表单 ——
编辑表单里根本没有改阶段的控件(改阶段走详情页那几个显式动作)。
这样即便将来又冒出某个字段名冲突,也不可能把「实际」悄悄变回「计划」。
② 🔴 **⑧「时间范围选择器无法使用」= 三道门叠在一起**
① `_event_fields.php` 的**结束端默认带 `hidden`**(要点「+ 添加结束时间」才出现);
② `app.js` 的 `upgradeRange()` 有「**两端都得可见**才升级」的判断 → 永远不成立
(实测 `data-semi-range-done` 恒为 `null`);
③ CSS `html.js .dt-range:not(.is-semi-range) .dt-input { visibility: hidden }` →
未升级时原生控件**也是不可见的** ⇒ 屏幕上一片空白、点哪儿都没反应。
修法:两端**始终可见**("没有结束"由清空后半段表达,不再靠开关);
并把旧的「+ 添加结束时间 / 去掉结束时间」开关整套删掉。
⚠️ 顺带挖出:`upgradeRange()` 里 `group.insertBefore(host, a)` 在"两端各自被
`.field` 包着"时 `a` 不是 `group` 的子节点 → 直接抛
`Failed to execute 'insertBefore' on 'Node'`。这个异常**以前从没触发过**
(被 ② 的提前 return 挡住了)—— 一旦放出来就炸。修成插在"端点最外层包装"之前。
⚠️ 观察器加了 `attributes: true, attributeFilter: ['hidden']`(并把 childList
分支的 `return` 去掉):弹框/折叠面板/tab 里"稍后才露出来"的日期控件从此都能自动升级。
③ 🔴 **⑧「新建日程默认建出观影记录」= 产品缺口,不只是显示问题**
`Calendar::DEFAULT_KINDS` 原来只有 6 本**派生**本(观影/旅行/农历/节假日/传统节日/
节气),**一本可写的普通本都没有**。于是"放在哪本"里全是派生本、默认选中排最前的
**观影** → 用户直接打字点「加上」,建出来的是一条**观影记录**
(日历里看着是多了条日程,观影列表里莫名多一条)。这是"建错东西",事后极难发现。
→ ① 默认本补成「我的日程」(kind='custom'),模板里默认选中也改成**优先 custom 本**;
② 选择器里把**纯派生只读本**禁用并标「(自动生成)」
(直接不列会让人以为本子丢了;列为可选又会白选一次再吃错误)。
⚠️ 顺带发现 `Calendar::READONLY_KINDS` 把 `movie`/`trip` 也算进去了,而
`event_add` 里**紧跟着**就有两段专门处理它们的分支(「已加进观影」/「已加进旅程」)
—— 那两段**永远走不到**(校验先一步把请求打回去了)。判据要按**能不能写**分,
不是按"是不是派生"。
④ 🔴 **②「观影编辑页图片撑破」= `.mv-slot` 这个类在样式表里根本不存在**
第 21 批加模板时只写了结构没写样式 → `<img>` 按**原始像素**渲染:600×900 的海报
在 236px 宽的格子里撑出 942px 高,还把整页横向顶破(实测 docW 1297 > vw 1280)。
→ 补 `.mv-slot` 规格:固定 108×162 缩略图 + `object-fit: cover`。
⚠️ **高度必须写死** —— 不写死就又会由原图比例决定,等于没修。
⑤ **⑤行程列表排序 = `category, position` 主导**
`position` 默认同一个数(旧迁移给的多是 0/100),于是顺序被 position 挤乱。
实测线上 #222 渲染成「返回青岛(10-5) → 出发去菏泽(9-30) → 去姥姥家(10-2)」。
→ 改成 `ORDER BY (starts_at IS NULL), starts_at, id`。
⚠️ 方向是**正序**(早的在前,已跟用户确认);旅程列表仍是 DESC(最新在最上)。
两者方向不同是**刻意的**:一个是"挑哪趟旅行",一个是"读这趟旅行的行程"。
⚠️ 详情页**按阶段分三组**渲染(计划/实际/未成行),排序只需在**组内**有序。
本批新增能力:
- **统一上传框**(⑦):`templates/_stage_drop.php` 现在**同时**负责
「sr-only 的真文件框 + 看得见的上传框 + 暂存清单」,调用方只 require 一行。
框是 `role="button" tabindex="0"`,JS 转发 `input.click()`(键盘 Enter/Space 也通),
拖文件进框走既有的暂存上传(拖一个立刻传、可续拖、失败标红、未传完拦住保存)。
⚠️ 框用 `<div>` 不是 `<button>`:里面要放暂存清单(含「撤掉这张」按钮),
按钮里嵌按钮是非法 HTML(第 21 批刚在 `<form>` 嵌套上栽过同一个跟头)。
⚠️ 文件框用 `.sr-only` 不是 `display:none`:后者不可聚焦,带 `required` 时
Chrome 会报 "An invalid form control is not focusable" —— 点了什么都不发生。
落了 11 处:行程照片 / 观影海报·票根·合影 / 资源 / 任务 / 日程附件 / 书籍 /
联系人导入 / ics 导入 / 观影补传 / 资源补传。
⚠️ 清单与提示行移到框**外面**(放框内会变成"一个大框套五张图"),
所以 app.js 改从 `.upfield` 容器里找它们。
- **无刷新删除(①)与无刷新新建日程(⑧)**:路由按 `X-Requested-With`
(或 `Accept: application/json`)区分调用者 —— 带它回 JSON、不带照旧 302
(无 JS 的兜底**不变**,两边的反向都写了断言)。
· 名册删除:成功 → 摘卡片 + 就地改「共 N 人」与分组人数 + 派发 `input` 让
filter.js 自己重算(**不抄它的判据**);失败 → 把原因说出来(不刷页);
拿不到 JSON → **退回原生提交**(绝不静默失败)。
· 日历新建:JSON 带齐前端插入当前视图要用的字段(时间/颜色/链接/是否全天/cal)。
月视图**按时间插入**(不能 append —— 服务端只渲染前 3 条、按时间序取,
append 会让"新加的早间日程"掉进「+N 条」里看不见,刷新后又跑到第一位);
周/日视图按"24h = 100%"算 top/height 插块。
· 观影 / 旅行本是**可写派生本**:AJAX 时不再跳详情页(跳走就离开了日历),
改在提示行给一个「去看详情 →」链接。
- **⑧日历本带颜色**:原生 `<option>` 画不出色块(浏览器不允许在 option 里放元素),
所以做成「色块 + 下拉」并排,色块跟着当前选中的本子变色。
- **③天气卡片**:三句提示全删;城市单独一行、纬度/经度同一行(`.loc-row`)。
删的只是说明文字,三个输入框 + 保存 + 状态回显都保留。
- **⑥设置按钮贴最右**:原来顺序是「… → 设置 ☰ → 删除 🗑」,设置被删除键挤到**倒数第二**;
改成删除在前、设置恒为最后一个(与观影/日程/任务/资源同一个手势)。
验证(本地全套):
- e2e **1059/0**(本批新增约 40 条断言)· 结构升级 149/0 · 桌面几何 428/0
- **真机探针 75/0**(`_fb_b21_ui.py`):AJAX 删除真的没导航 / 卡片真被摘掉 /
滚动位置保持;缩略图几何(108×162,不再溢出);上传框(框在、原生框不可见、
点框真打开文件选择框、连拖两张都上去且清单可见);日历弹框(打开时网格一动不动、
范围控件真的挂上且可见、能弹面板、色块跟着换、无刷新提交后当前视图里立刻出现、
周视图也能插时间块、全程无 JS 报错)。
- **反向验证 12 条**(`_fb_b21_falsify.py`):逐条把修复改回旧行为看断言是否变红 ——
令牌字段改回 stage[] / 编辑读表单 stage / 排序改回 position / 设置按钮挪走 /
框不转发点击 / 文件框不再 sr-only / 删缩略图规格 / 删除不回 JSON /
结束端改回 hidden / 日历本默认改回第一个 / 天气提示加回 / 弹框改回内联。
- 部署脚本静态断言段本地空跑 **307/0**。
|
||
|
|
8f80f3e835 |
v24 第 2 轮:19 条清单收尾(附件预览/CalDAV/任务分类看板/全局选人/家谱/纪念日/财务)
用户 2026-10-02 提的 19 条需求,本轮完成剩下的 8 条 + CalDAV 服务端:
- ③ 附件:图片/视频给缩略图,PDF/Word/Excel 给**同尺寸**图标;可预览的在本页
弹层里看(不再新开标签页),渲染不了的点了直接下载;预览层右上角恒有下载按钮。
- ⑦b CalDAV:把「我的日历本」按 RFC 4791 只读子集分享出去(OPTIONS/PROPFIND/
REPORT/GET,写方法一律 403)。用**专用口令**(库里只存 sha256),不是登录口令。
- ⑨⑩ 任务:分类与看板都由用户自建;已完成的任务从看板消失,改在「已完成」页
按分类查看/搜索;设置入口在「新建任务」旁的 ☰。
- ⑪ 全局人员选择器:一个片段铺到观影/旅程/资源/日历共享/任务共享/财务共享;
修掉了老组件「名字和拼音都搜不动」的 bug —— .att-item 自己声明了 display:flex,
压过了浏览器 [hidden]{display:none},过滤根本没生效。
- ⑫ 家谱:头像改方形不再裁切;姻亲不在图上的卡片给「⇄ 姻亲」入口,
以 TA 为中心重画 + 「返回我的家谱」。
- ⑬ 纪念日:阴历/阳历都能设且按对应历法重复;名册里每个人的生日默认可见
(实时派生,不复制一份);全部作为全天日程进日历。
- ⑮ 财务:账本可共享,双方都能记账、都能看全部账目,资产−负债=净资产;
金额一律以「分」存,不用浮点。
验证:端到端 724 项通过 / 0 失败;几何 368、交互 97、移动端 516、家谱 24、
升级 123 全绿;SQL 列名静态校验 0 处可疑;部署 11 条线上探针全过。
|
||
|
|
71a7ec35a0 |
v14: 观影/家谱/日程/旅程/资源/任务六模块统一,首页改 dashboard
用户 14 条需求一次交付(1-13 功能 + 14 工作方式)。 观影(1-4) - 返回文案全模块统一为「返回观影」(不再有「返回列表」「返回观影记录」三种叫法) - 观影人一律用彩色人物标签 person_chip:底色由头像主色算出来(上传时存库), 瀑布流卡片也按人显示;五处观影模板全部改用它 - 人员显示统一走 person_name()(shown → 我给他起的称呼 → 姓名) - 影院管理页与年度报告页的上下两块用 .card + .card 统一给间距(原来紧贴成一整块) 家谱(5) - 树根从「我」换成**辈分最高的祖先**:库画的是"树根的祖先链+后代+树根的兄弟姐妹", 根=我 时伯父/堂兄/嫂子/侄子一个都不画(12 人夹具只画出 8 人) - focusMap 按**代数**圈定三代(不再按步数),normalizeLinks 把子女对称挂到父母双方 —— 修掉"母亲登录看不到孩子" - 新增「看全部 / 回到我」两个按钮,放在画布外(画布整块被库接管了手势) 统一事件原语(6-9) - 旅程:TripLog 全量迁入(3 趟旅程 / 21 段行程 / 72 张照片 / 5 个人)。 复用 events(kind='trip') + trip_details + trip_nodes(计划/执行双轨保留) - 日程:每人可建日历本(自定义色)+ 共享只读/可写;观影与旅行是每人默认两本, 内容是**从各模块实时派生**的(不复制,所以改了观影日期日历立刻跟着变); 没有具体时分 = 全天;月视图参照群晖 Calendar - 资源:挂在某人名下的附属信息(标题+正文+多附件),可共享给多人并设只读/读写, 默认折叠、可搜标题与正文 - 任务:看板三列 + 两级子任务 + 评论@人 + 共享 + 循环(完成时按原截止日推下一期) - 统一附件表:四个模块的附件共用一张,下载地址不落库、一律由 media.php 现算 模块化(10-11) - App\Module 是模块的唯一来源(加一行 = 导航条/主题色/个人设置三处自动出现) - 个人设置里可勾选要显示哪些模块、拖动排序(关掉的模块**不会**被兜底补回来) - 每个模块一套主题色(家庭级),只覆盖 --brand 三兄弟,不碰纸墨底色 其他(12-13) - 新增成员表单可选所属分组(编辑表单没有:分组是"我和他的关系",只归我自己) - 首页改 dashboard:接下来的日子 / 未来七天 / 手头在做 / 往年今日。 家人卡片从首页撤走(看人去名册) 验证(全部通过) - e2e 542 条(新增 4 模块端到端 60+ 条) - 桌面几何 368 / 移动端几何 520 / 家谱 12 / 交互 97 / 老库升级 123 - SQL 列名静态校验 0 处可疑(对着真实表结构) - 反向验证 18 条新注入全部抓住 顺带修掉的既有问题 - 老库升级回填 SQL 按实际存在的列动态拼(缺列不再 1054 炸断升级) - 全新安装时 Schema::ensure 跑在"人还没建"之前 → 补建日历本三处齐备 - 窄屏导航横向溢出(模块从 4 个加到 8 个后顶栏撑破视口) |
||
|
|
7f00d3e553 |
feat(kin,movies): 家谱改用第三方库 family-chart;观影设置按钮改纯图标并统一页头度量
家谱(v13): - 自研布局是死结,不是样式问题:连线的横杆是网格项,只能横跨「父母列→子女列」 整段区间,中间夹着与血缘无关的人(如媳妇那侧的岳父母)时必然从卡片上穿过。 换成 family-chart(D3 生态,MIT)后,排布只走本家血脉 + 配偶,姻亲不进画布, 这个死结在构造上就不存在了。 - 接入踩到并修掉的坑:main_id = data[0].id(「我」必须排最前); 卡片回调里自定义字段在 d.data.data(双层);库的 family-chart.css 是死文件 (全挂在 .f3 作用域,运行时没有 .f3 元素);库的 svg 撑满选择器永不命中 (必须自己补 .kin-chart svg.main_svg);setCanAdd/setOnCardClick 不在 chart 上 (写错会抛异常,把 updateTree 一起带走 → 整张图空白,页面照样 200)。 - 关掉 setSingleParentEmptyCard:库默认给孩子只登记一位父母的人自动补一张无名 配偶卡,看着像「凭空多出一个配偶」。 - 卡片用原生 <a> 链成员页,不依赖库的点击回调。 观影按钮(用户本轮原话): - 「记一场电影」与「设置」高度仍不一致 —— 根因是文字按钮高度由 padding+line-height 自然推出(43.8px),而 .btn-icon 写死了 40px。抽出 --act-* / --act-h 变量,两边同源。 - 设置按钮去掉「设置」二字,只留图标,图标换成三条横线(☰)。 - 设置按钮在所有页面排最右(.movie-set 用 margin-left:auto 顶到行尾)。 - 除设置外其他按钮都有颜色,与设置区分(主按钮实心 / 次要浅底 / 危险淡红)。 顺带修掉一个特异性 bug:删除按钮被 .btn 品牌底盖成实心红,与「编辑」难分辨。 - 设置下拉四个条目文字前都加图标(行内 SVG,不引外部字体)。 测试与部署: - 新增 _fb_kin_check.py(桌面,12 条)+ _fb_kin_falsify.py(6/6)证明 「整张家谱空白」这类只靠通用几何抓不到的坏法能被抓住。 - _fb_geom_mobile.py 家谱段重写为窄屏语义(库按容器 fit 缩放是正常行为, 改为断「比例没被压扁 / 缩放不至于看不清」),并加 _fb_kin_mobile_falsify.py(4/4)。 - _fb_btn_check.py(59 条)+ _fb_btn_falsify.py(3/3)覆盖观影页头按钮五条需求。 - familybook_deploy.sh / familybook_smoke.sh 的家谱断言改为第三方库形态, 设置按钮断言改为 btn-icon 纯图标 + 三条横线 + 条目图标。 - 新增 .gitattributes:vendor 库与 .min.js 标 -text,原样入库不被改写行尾。 |
||
|
|
4060c18f86 |
fix(kin,movies): 家谱连线重构为单一网格 + 观影设置统一页头
需求三点: 1. 观影模块「⚙ 设置」与「记一场电影」按钮高度不一致 → 统一页头动作区 四个选择器的 padding/font-size(.btn 与 .btn-sm 混用是根因);并把设置 下拉推广到观影全部页面(详情/影院/搜索/统计/报告/表单),落在返回按钮旁。 2. 家谱连线「线乱」→ 根因三条:每代各建一个网格(列宽最小值由内容决定, 各代不一致,窄屏实测 93.5px vs 164/78/78/78px);落点用百分比算而 grid 间距不参与百分比;连线行与卡片行不是同一套列。 改法:整棵树只留一个网格,辈分行/卡片行/连线行全是它的行,连线元素本身 也是网格项(靠 justify-self 落位);列宽固定为一卡之宽(不用 minmax/1fr, 否则宽屏卡片中心 ≠ 列中心)。 3. 亲属关系接口改白名单(parent/child/spouse)——原先未知 kind 被静默记成 配偶,夹具传 kind=sibling 让家谱断言长期拿错数据自证。 设置下拉抽成可复用片段 _settings_menu.php($setCurrent 高亮当前页、 $setCanWrite 控「影院管理」显隐),六七个页头不再各抄一份。 验证:e2e 456/0、桌面几何 268/0、移动端几何 504/0、交互 97/0、 家谱连线诊断 0 失败、静态空跑 exit=0、静态断言证伪 13/13、 反向验证 68/68 全抓住。 |
||
|
|
e9b3b8666b |
v11: 家谱按户排布(夫妻并排/长幼排序/父母括号/姻亲);观影设置下拉收拢影院·搜索·统计·年度报告;观影人头像
家谱(用户第 2 点,重写模板与 Kinship::tree): - 同代聚成「夫妻单元」(户):血亲当主位、配偶并排在其右,兄弟_姐妹按长幼(长子左) - 血亲可达性判「姻亲」:只走 parent/child 边从「我」可达者为本家血亲,妻子的父母 走不到 → 标「姻亲」并排到本代末尾(旧判据「上无父母且下无子女」对岳父母恒为假,是真 bug) - 父母括号连线:横杆盖住父母列到其子女各列,竖线落父母列中心、每子女列一道下线 - 称呼按长幼定死:姐夫/妹夫、嫂子/弟媳、大/小舅子、大/小姨子、大伯子/小叔子、 大姑子/小姑子、侄子/侄女、外甥/外甥女、堂/表 - 列宽地板 minmax(min-content,1fr):原 minmax(0,1fr) 在窄屏+户数多时把列压到卡片 (96px)以下 → 同代卡片互相重叠;.tnode 加 flex:0 0 auto 防被 flex 压扁 观影(用户第 3~5 点): - 顶栏删「影院」入口(影院是观影模块的子功能);观影列表右侧加「⚙ 设置」下拉, 内含 影院管理 / 高级搜索 / 统计 / 年度报告(后三者从 MovieLog 迁入, app/MovieStats.php + 三个只读路由,MySQL 方言;图表全用纯 CSS,不引 CDN) - 评价区删除「我」的长条徽标(被 flex column stretch 拉成整行宽的丑色带) - 观影人弹窗列表与已选 chips 带头像;syncChips 点名取 .att-name/.att-av (旧写法取「第一个 span」,v11 起那是隐藏占位,chip 上会没名字) 验证:e2e 432/0、桌面几何 268/0、移动几何 464/0、交互几何 97/0、 静态空跑 199/0、反向验证 v11 十条 10抓/0漏。本轮无 DB 变更(Schema 仍 v10)。 移动几何抓出两个真溢出并已修:高级搜索页 select 长选项撑破 .field、 原生 date 输入撑破 .date-range(360/375px)。 |
||
|
|
2c36335c3e | v10: 修影院编辑回填布尔化 bug;评分改 10 星制;观影人改弹窗+拼音首字母联想;豆瓣自动补充迁移(movie_details.douban_meta,schema v10) | ||
|
|
746f02581e |
v9: 观影对齐 MovieLog —— 瀑布流/影院管理/评价评论/起止时间
- events 起止时间精确到秒(starts_at DATE→DATETIME + ends_at),_datetime.php 复用 - 影院独立成表(cinemas,地方/名称/地址/主题色 + 50 色板),movie_details 归一为 cinema_id - 评价模型 MovieLog 同款:movie_reviews 每账号每片一条(1-10 分+正文+感受 key), movie_review_comments 两级平铺评论(回复的回复规范化到顶层、跨评价回复拦截) - 感受目录迁到 MovieReview::CATALOG(12 种,Emoji 11.0 老码位) - 观影列表改瀑布流(CSS grid 海报墙),详情页五大块 + 灯箱 + 评价区 - 升级路径:v8 老数据自动归一(影院三段按 (family,place,name) 去重建行、 评价回填、中文感受反查英文 key),回填全部幂等;老库补 cinema_id 列+索引+外键 - 修三 bug:建表顺序(cinemas 必须在 movie_details 前,否则全新安装 1215)、 layout 导航用键名当路由(影院链接指向 r=cinemas)、 _datetime.php 分钟循环变量 $m 覆盖宿主模板的观影记录(编辑页简介/备注回填被清空) |
||
|
|
ff353b1eee |
v8:观影落地(MovieLog 整体迁入统一事件原语)+ 三处 UI 撤除
- Schema v8:events / movie_details / event_attendees / event_photos 四张表, uk_event_source(family_id, source_ref) 作迁移幂等锚 - Movie 模型:时间线/详情/增删改、同行人整组替换、照片逐张管理、感受英→中映射 - ImageSlot 新增 poster(2:3) / ticket(free) / photo(free) 三槽位; free 槽位不裁剪、整图等比缩进框(只缩不放大) - OssDriver::copyObject:同桶 CopyObject(手写 V1 签名,x-oss-* 头按字典序进签名串) - 路由:movies 时间线/详情/新增/编辑/删除/补照片/删照片;同行人按家庭过滤; 访客只读(requireWrite) - UI 撤除:首页「我的一句话」、家谱页「补充我的亲属关系 / 看整个名册」两个按钮; 顶栏加「观影」入口 - 修潜伏 bug:ImageSlot::parseRatio 的正则用 / 作分隔符,字符类里的 / 把模式 提前截断(Unknown modifier ']')—— ratio() 只在上传 POST 里被调到,一直没被观察到 - 线上数据迁移(临时 token 门禁页,用完即删):MovieLog 219 条观影 → events, 14 位观影人新增进名册(带 MovieLog 头像),431 个 OSS 对象同桶拷贝 (movielog/uploads/<dir>/x → family/<dir>/x),review/feelings/影院三段/照片全量搬 |