v81:首页卡片「新增电影」「新建纪念日」弹框(不离开首页就把事办了)

用户 2026-10-22:
「首页上的其他卡片也应该有这些基本功能。比如电影观影,它有一个"新增电影"的功能;
  如果是纪念日,就有一个"新增纪念日"。实际上,就是把原来模块里弹的那个框,现在拿到
  这边来。目的就是为了在首页上就能快速完成一定工作,而不用进到模块里面。」

做法:复用模块页**同一份**表单,不另写。
- app/HomeCards.php:TYPES 每张卡多登记 add(按钮文案)/ add_modal(弹框容器 id)。
- templates/home.php:按登记表挂 data-fb-add-open="#<id>"(没登记的仍用 v78 的
  data-tk-add-open,两种写法并存但各有唯一来源);页尾 require 模块的表单模板。
- assets/js/add_modal.js(新):通用开关,只认 [data-fb-add-modal] /
  data-fb-add-open / data-fb-add-close;幂等闸门;Esc 先让 antd 收面板;
  打开那一刻才 boot 日期控件(hidden 时尺寸为 0,antd 会定位到屏幕左上角)。
- templates/{movies,days}/form.php:只在首页内嵌时($fbAddModalAttr)挂通用属性
  并输出 back=home,模块页行为一字不变。
- index.php:days/save 与 movies/create 加 back=home 白名单(取 POST 再比字面量,
  不把 POST 当跳转地址)。

过程中修掉两个真问题:
- 登记表第一版写 anAddCard,而 days/form.php 实际是 anNewCard ⇒ 按钮属性写对了、
  DOM 也不报错,点下去毫无反应。已加「按钮指向的容器真的存在」判据与部署静态
  断言里的交叉校验(拿登记表 id 去 templates/ 里逐个找 id="...")。
- 探针判「提交后回首页」原本读 pg.url,而 wait_for_load_state(networkidle) 在
  点击那一刻本就 idle 时立刻返回 ⇒ 读到提交前的地址,恒绿。改成拦那次 POST 的
  响应看 (状态码, Location),拆成 ⑭a 提交真发生 / ⑭b 302 指向 home / ⑭c 浏览器停在 home。

验证:正向探针 23/0 · 反向验证 14/0(七注入各自变红)· e2e 1572/0 · 几何 428/0。
This commit is contained in:
2026-10-22 22:05:17 +08:00
parent b1d4502148
commit fd90e33d94
7 changed files with 288 additions and 13 deletions
+53
View File
@@ -935,6 +935,40 @@ switch ($r) {
}
}
/* 🔴 v81(用户 2026-10-22):「首页上的其他卡片也应该有这些基本功能。
比如电影观影,它有一个"新增电影"的功能;如果是纪念日,就有一个"新增纪念日"。
实际上,就是把原来模块里弹的那个框,现在拿到这边来。」
⇒ 首页要能内嵌 `movies/form.php` 与 `days/form.php`,就要给它们准备好数据。
**判据与 `$tkAdd` 完全一致**:卡片真摆在首页 + 账号可写,才准备;
否则关掉卡片之后还白白跑 `Person::roster()` / `Cinema::all()`。
⚠️ 这里的 key 必须与 `HomeCards::TYPES` 里的 `add_modal` **一一对应**
(`movies` ⇄ `mvAddCard`、`days` ⇄ `anAddCard`):
`home.php` 按 key 走不同分支 require 不同模板,写错一个就是
"按钮点开了一张不存在的弹框"(点了没反应,最难查的那种)。
⚠️ 别把这里扩成"遍历所有卡片的 add"就完事:每张卡要的数据不同,
新增一张要新建弹框的卡片时必须回来加一个分支(宁可多写三行,
也不要写一个"猜字段名"的通用装配器)。 */
$homeAddModals = [];
if (Auth::canWrite()) {
foreach ($homeCards as $hcRow) {
$hcKey = (string)($hcRow['k'] ?? '');
if ($hcKey === 'movies' && !isset($homeAddModals['movies'])) {
// 与 movies/index.php 内嵌时**同一组**数据(同一个表单要同一份料)
$homeAddModals['movies'] = [
'people' => Person::roster($familyId, $me, 'member', ''),
'cinemas' => Cinema::all($familyId),
];
} elseif ($hcKey === 'days' && !isset($homeAddModals['days'])) {
// 与 days/index.php 内嵌时同一组:分类清单(名称 + 图标)+ 名册
$homeAddModals['days'] = [
'kindsFull' => Anniversary::kindsFull($familyId),
'people' => Person::roster($familyId, $me, 'member', ''),
];
}
}
}
View::render('home', [
'title' => '首页',
'family' => Family::find($familyId),
@@ -970,6 +1004,8 @@ switch ($r) {
'cardData' => $cardData,
// v78:首页「新建任务」弹框的数据(卡片没摆/只读账号时为 null)
'tkAdd' => $tkAdd,
// v81:首页「新增电影」「新建纪念日」弹框的数据(同上,卡片没摆就没有)
'homeAddModals' => $homeAddModals,
/* 🔴 v71c(用户 2026-10-21):「通知消息这个,也是首页的卡片,可以调整
顺序和大小。」
⇒ 通知**不再是**这里单独传的两个变量(`notifs` / `notifUnread`),
@@ -3327,6 +3363,15 @@ switch ($r) {
);
clear_old();
flash('ok', '已记下这场电影。' . ($warns ? '(' . implode(';', $warns) . ')' : ''));
/* 🔴 v81(用户 2026-10-22):「把原来模块里弹的那个框,现在拿到这边来。
目的就是为了在首页上就能快速完成一定工作,而不用进到模块里面。」
⇒ 从**首页**那张弹框提交的,回首页(卡片上立刻能翻到刚记的这部)。
⚠️ 默认仍是「跳详情页」—— 从观影模块新建时,用户接着就要补
评价、加照片、传票根,跳详情页才对;**只有首页来的才改道**。
⚠️ 白名单取值,不把 POST 字符串直接当跳转地址(开放重定向)。 */
if ((string)($_POST['back'] ?? '') === 'home') {
redirect('home');
}
redirect('movies/show', ['id' => $newId]);
}
break;
@@ -5005,6 +5050,14 @@ $want = array_keys($want);
'person_ids' => array_map('intval', $pids),
], $id);
flash('ok', $id > 0 ? '纪念日已保存。' : '纪念日已添加。');
/* 🔴 v81(用户 2026-10-22):「把原来模块里弹的那个框,现在拿到这边来。
目的就是为了在首页上就能快速完成一定工作,而不用进到模块里面。」
⇒ 首页那张弹框提交时带 `back=home`,回首页(卡片上立刻能看到新加的)。
⚠️ 白名单取值,绝不把 POST 的字符串直接当跳转地址(开放重定向)。
⚠️ 编辑不从首页走(首页只有"新建"入口),所以 `$id > 0` 时不认这个参数。 */
if ($id <= 0 && (string)($_POST['back'] ?? '') === 'home') {
redirect('home');
}
redirect('days');
break;