日程新建/修改不再刷新页面:服务端片段 + 就地重绘 (#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
This commit is contained in:
@@ -251,6 +251,17 @@ require __DIR__ . '/_entry_popup.php'; ?>
|
||||
三处入口分散在首页/日程页,按需加载的判据会在这些页面之间来回漏;
|
||||
它只有几 KB,不值得为省这点流量赌"某个页面漏加载"。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/entry_popup.js')) ?>" defer></script>
|
||||
<?php /* 🔵 工单 #37(用户 2026-11-01):「修改和新建,都不应该刷新页面,
|
||||
而是使用js动态调整。」
|
||||
⇒ 新建/修改成功后**取服务端渲染好的片段整块替换**,不手写复刻渲染。
|
||||
历史教训:手写复刻必然出错(`templates/calendar/index.php` 里 `putMonth` 的注释记着
|
||||
一次真实事故 —— 新加一条 07:00 的日程被 append 到末尾,落进「+N 条」看不见,
|
||||
刷新后却又跑到第一位)。
|
||||
⚠️ 全站加载:它在**首页**和**日程页**各有落点,按需加载的判据会来回漏;
|
||||
自带"两个落点都不在就什么都不做"的判据,开销可忽略。
|
||||
⚠️ 必须排在 `entry_popup.js` 之后(浮层保存成功后要调它的 refresh)。
|
||||
`defer` 保证按文档顺序执行,所以这两行的先后就是执行先后。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/cal_sync.js')) ?>" defer></script>
|
||||
<?php /* 🔴 v78:首页任务面板的动作脚本(状态/看板/子任务/附件/评论)。
|
||||
同样全站加载:面板是浮层的一部分,而浮层本身就挂在本布局里;
|
||||
脚本自带"面板不在就什么都不做"的判据。 */ ?>
|
||||
|
||||
Reference in New Issue
Block a user