laoyang
7f3df7a40b
日程新建/修改不再刷新页面:服务端片段 + 就地重绘 (#37)
用户原话(2026-11-01):
「修改和新建,都不应该刷新页面,而是使用js动态调整。」
## 改法:不再手写复刻渲染,改成"服务端渲染片段 → 前端整块替换"
历史上「新建不刷新」是靠手写复刻服务端渲染做的(`putMonth` / `putTimed` /
`relayoutColumn`),它自己的注释里就记着一次真实事故:新加一条 07:00 的日程被
append 到末尾 ⇒ 落进「+N 条」看不见,刷新后又跑到第一位。修改要处理的情况更多
(跨天 / 全天状态 / 换日历本改颜色 / 改重复规则 / 挪到别的月),复刻必然出错。
⇒ 现在只有**一处渲染**:前端取服务端画好的视图片段整块替换。
"插进去的样子"与"刷新一次的样子"必然相同。
## 服务端
- `calendar/event_update` 认 `X-Requested-With`(与 event_add / event_del 同一套约定)
回 JSON;失败也回 JSON(可预期的拒绝不该把用户甩到一个刷新过的页面上)。
三处失败出口 + 四处成功出口收口成两个闭包,避免"某条路径仍刷页"。
- 抽出 `calendar_view_data()`:整页渲染与局部重绘共用同一份取数/建网格逻辑。
- 新增 `calendar/view` 片段路由(月/周/日视图片段)。
- 抽出 `home_agenda_events()` + 新增 `home/agenda` 片段路由(首页卡片)。
- 抽出 `templates/calendar/_view.php`:视图区从页面模板里搬出来(原文一字未改,
已用脚本逐行比对证明 270 行零差异)。
- `templates/layout.php` 加载 `cal_sync.js`。
## 前端
- 新增 `assets/js/cal_sync.js`:`refresh()` 按当前视图取片段替换;
只换内容不换容器;保首页卡片滚动位置;重绘后补跑红线定时器;
失败退回整页刷新(宁可刷一次,也不要"没刷新却也没变")。
- `entry_popup.js` 的「保存」改 fetch 提交 → 关浮层 + 轻提示 + 局部重绘。
名册/观影/行程/任务四个来源保持原生提交,范围不收不必要的回归面。
- 删掉日历页那 ~270 行手写插入,新建统一走同一个 `refresh()`。
- 「点格子新建」的 per-element 绑定改成 document 委托(重绘后新节点才有监听)。
## 本轮实测抓到并修掉的真 bug
`form.id` 被同名 input 遮蔽:日程编辑表里有 `<input name="id">`,
HTMLFormElement 的具名访问优先级高于原型上的 `id` ⇒ `form.id === 'calEditForm'`
恒为 false ⇒ 日程也走原生提交、整页刷新。
改用 `form.getAttribute('id')`。
## 验证(全部做过反向验证)
- 浏览器端到端 28 项:编辑/新建都不导航、改动原地可见、视图不跳、数据岛同步、
重绘后点格子仍有效、红线定时器重建、无 JS 异常
- 反向验证 7 项:把 refresh 置空 / 改成 reload / 不调 reinit / 抹掉上下文属性,
判据逐条转红(证明判据有牙齿)
- DOM 等价性 4 项:月/周/日视图与首页卡片,重绘后与刷新后的 DOM **逐字节相同**
(这条判据本身也做了反向验证:删一个字、多一个节点都能抓到)
- 服务端 11 项:非 AJAX 仍 302 且真存库、AJAX 回 JSON、失败也回 JSON、
片段路由可用、未登录被拒
- #35 跨天回归 11 项:三段的 seg/cont/is-seg-* 标记全在、贴满整列、不进全天行
- 静态检查:37 个模板片段无作用域违规;20 块内联 JS 语法全过;运行期零 Warning
2026-11-01 09:04:35 +08:00
..
2026-10-30 23:43:10 +08:00
2026-10-04 08:56:40 +08:00
2026-09-29 23:48:45 +08:00
2026-11-01 09:04:35 +08:00
2026-10-10 12:42:43 +08:00
2026-09-30 00:49:53 +08:00