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:
@@ -108,11 +108,24 @@ final class HomeCards
|
||||
'name' => '纪念日', 'desc' => '接下来 60 天的纪念日与生日',
|
||||
'w' => 4, 'h' => 4, 'route' => 'days', 'more' => '全部纪念日', 'n' => 0,
|
||||
'mod' => 'days',
|
||||
/* 🔴 v81(用户 2026-10-22):「如果是纪念日,就有一个"新增纪念日"。」
|
||||
⇒ 首页卡片右上角直接开新建弹框(与纪念日页**同一张** `days/form.php`)。
|
||||
`add_modal` = 那张弹框在首页里的容器 id,`home.php` 拼成
|
||||
`data-fb-add-open="#anNewCard"`;掉进 `assets/js/add_modal.js` 的通用逻辑。
|
||||
🔴 这个 id 必须与 `days/form.php` 里真实的容器 id **逐字一致**:
|
||||
第一版我按"an + AddCard"的规律写成 `anAddCard`,而实际是 `anNewCard`
|
||||
⇒ 按钮指向了一个不存在的元素 ⇒ **点下去毫无反应**(探针⑥当场抓到)。
|
||||
这正是"点了没反应最难查"的那一类:属性在、DOM 也没报错。 */
|
||||
'add' => '新建纪念日', 'add_modal' => 'anNewCard',
|
||||
],
|
||||
'movies' => [
|
||||
'name' => '观影', 'desc' => '最近看过的片子',
|
||||
'w' => 4, 'h' => 4, 'route' => 'movies', 'more' => '观影记录', 'n' => 0,
|
||||
'mod' => 'movies',
|
||||
/* 🔴 v81(同一条):「比如电影观影,它有一个"新增电影"的功能。」
|
||||
⚠️ 文案按用户说的「新增电影」,但弹框标题仍是模块里的「记一场电影」——
|
||||
不为了统一而改模块文案(那是模块自己的措辞,用户没要求动)。 */
|
||||
'add' => '新增电影', 'add_modal' => 'mvAddCard',
|
||||
],
|
||||
'books' => [
|
||||
'name' => '读书', 'desc' => '在读与最近加进书架的书',
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/* ============================================================================
|
||||
通用「新建弹框」开关(v81)
|
||||
----------------------------------------------------------------------------
|
||||
用户原话(2026-10-22):
|
||||
「首页上的其他卡片也应该有这些基本功能。比如电影观影,它有一个"新增电影"的
|
||||
功能;如果是纪念日,就有一个"新增纪念日"。实际上,就是把原来模块里弹的那个框,
|
||||
现在拿到这边来。目的就是为了在首页上就能快速完成一定工作,而不用进到模块里面。」
|
||||
|
||||
v78 给「近期任务」写过一份 `task_add.js`(只能管那一张弹框)。这一批要把
|
||||
观影、纪念日也搬上来 ⇒ 三份几乎一样的脚本 = 三处迟早不同步。所以抽这一份**通用的**:
|
||||
每张弹框只需声明自己的「容器 id / 开按钮 / 关按钮」三个名字。
|
||||
|
||||
用法(在弹框模板里给容器加两个属性即可,不需要改 JS):
|
||||
<div class="cal-modal" id="mvAddCard"
|
||||
data-fb-modal="mvAddCard" ← 容器 id(= 触发器的 data-fb-modal-open 值)
|
||||
data-fb-modal-open="[data-mv-add-open]"
|
||||
data-fb-modal-close="[data-mv-add-close]"
|
||||
hidden>
|
||||
或者更省事:容器上加 `data-fb-add-modal`,触发器上加
|
||||
`data-fb-add-open="#<容器id>"`、`data-fb-add-close` —— 全站约定见下。
|
||||
|
||||
行为(与站内其它浮层一致,v78 那套原样保留):
|
||||
· 点触发器开、点 ✕/取消 关、点**遮罩**关、点面板内部**不**关、Esc 关;
|
||||
· Esc 时若 antd 的日期面板开着,**先让 antd 收自己的**(否则一次 Esc 关两层,用户懵);
|
||||
· 打开那一刻才升级日期控件 —— 弹框初始 `hidden`,antd 面板拿到 0 尺寸会定位到
|
||||
屏幕左上角(task_add.js 与 tasks/show.php 都为此专门 boot 过,同一条坑)。
|
||||
|
||||
⚠️ 全站加载,页面里没有带 `data-fb-add-modal` 的容器时**什么都不做**(几行判断)。
|
||||
⚠️ 幂等闸门 `window.__fbAddModalBound`:脚本可能被重复引入(AJAX 换正文、模板重复),
|
||||
不加会绑两遍 ⇒ 点一次开两次、关两次。
|
||||
⚠️ 为什么不用内联 script:内联脚本在 AJAX 换正文时**不会被重跑**(v42/v47 连着栽过)。
|
||||
============================================================================ */
|
||||
(function () {
|
||||
'use strict';
|
||||
if (window.__fbAddModalBound) { return; }
|
||||
window.__fbAddModalBound = true;
|
||||
|
||||
/* 容器:`data-fb-add-modal` 自带 id 也算(省去写两遍) */
|
||||
function containers() {
|
||||
return Array.prototype.slice.call(
|
||||
document.querySelectorAll('[data-fb-add-modal]'));
|
||||
}
|
||||
|
||||
function antdOpen() {
|
||||
var d = document.querySelector('.ant-picker-dropdown');
|
||||
return !!(d && !d.classList.contains('ant-picker-dropdown-hidden'));
|
||||
}
|
||||
|
||||
/* 打开时要做的"补动作":日期控件必须等可见之后再升级(hidden 时宽高为 0)。
|
||||
两步都要:先绑自建下拉(兜底),再挂 antd(正主);两个函数都自判幂等。 */
|
||||
function wake(c) {
|
||||
if (window.fbUpgradeRange) { window.fbUpgradeRange(); }
|
||||
if (window.fbBootAntd) { window.fbBootAntd(); }
|
||||
/* 焦点落到第一颗输入框:键盘用户不用先 Tab 一遍 */
|
||||
var f = c.querySelector('input:not([type=hidden]), textarea, select');
|
||||
if (f && !f.disabled) { try { f.focus(); } catch (e) {} }
|
||||
}
|
||||
|
||||
function show(c, on) {
|
||||
if (!c) { return; }
|
||||
c.hidden = !on;
|
||||
/* 触发器的 aria-expanded 跟着走(有就更新,没有不报错) */
|
||||
var sel = c.getAttribute('data-fb-modal-open');
|
||||
if (sel) {
|
||||
Array.prototype.forEach.call(document.querySelectorAll(sel), function (b) {
|
||||
b.setAttribute('aria-expanded', on ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
if (on) { wake(c); }
|
||||
}
|
||||
|
||||
/* 由点击位置反推"该开/该关哪一张":
|
||||
· 触发器上写 `data-fb-add-open="#id"`(显式指定容器);
|
||||
· 关闭按钮/遮罩则就近找祖先里的 `[data-fb-add-modal]`。 */
|
||||
function targetFor(t) {
|
||||
if (t.closest('[data-fb-add-close]')) {
|
||||
return { c: t.closest('[data-fb-add-modal]'), on: false };
|
||||
}
|
||||
var opener = t.closest('[data-fb-add-open]');
|
||||
if (opener) {
|
||||
var sel = opener.getAttribute('data-fb-add-open') || '';
|
||||
var c = sel ? document.querySelector(sel) : null;
|
||||
return { c: c, on: true };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var t = e.target;
|
||||
if (!t || !t.closest) { return; }
|
||||
var hit = targetFor(t);
|
||||
if (hit) {
|
||||
if (hit.c) { e.preventDefault(); }
|
||||
show(hit.c, hit.on);
|
||||
return;
|
||||
}
|
||||
/* 点遮罩关、点面板内不关 —— 容器本身就是遮罩层,所以只认 === */
|
||||
if (t.hasAttribute && t.hasAttribute('data-fb-add-modal')) {
|
||||
show(t, false);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key !== 'Escape') { return; }
|
||||
var open = containers().filter(function (c) { return !c.hidden; });
|
||||
if (!open.length) { return; }
|
||||
if (antdOpen()) { return; } // 让 antd 先收它自己的面板
|
||||
show(open[open.length - 1], false);
|
||||
});
|
||||
|
||||
/* 供其它脚本(提交成功后就地插入回包等)主动开合:
|
||||
window.fbAddModal.open('#id') / .close('#id') */
|
||||
window.fbAddModal = {
|
||||
open: function (sel) { show(document.querySelector(sel), true); },
|
||||
close: function (sel) { show(document.querySelector(sel), false); }
|
||||
};
|
||||
})();
|
||||
@@ -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;
|
||||
|
||||
|
||||
+13
-3
@@ -56,6 +56,11 @@ $anModalOnly = !empty($anModalOnly);
|
||||
$anOpen = !empty($anOpen);
|
||||
$anEmbed = !empty($anEmbed);
|
||||
$anShell = !isset($anShell) ? !$anEmbed : (bool)$anShell;
|
||||
/* 🔴 v81:首页内嵌时挂通用开关属性(`assets/js/add_modal.js`)。
|
||||
⚠️ **只在首页设**(`$fbAddModalAttr` 由 `templates/home.php` 传):
|
||||
纪念日列表页有一套自己的内联开关脚本(还要管"编辑后替换卡片"那套 AJAX),
|
||||
再挂通用属性就是两套同时绑 —— 按需挂,模块页行为不变。 */
|
||||
$fbAdd = !empty($fbAddModalAttr);
|
||||
|
||||
$backUrl = url('days');
|
||||
?>
|
||||
@@ -79,12 +84,15 @@ $backUrl = url('days');
|
||||
if (!$anModalOnly) {
|
||||
echo ' data-an-standalone="1" data-an-back="' . e($backUrl) . '"';
|
||||
}
|
||||
if ($fbAdd) {
|
||||
echo ' data-fb-add-modal';
|
||||
}
|
||||
} ?>>
|
||||
<?php if ($anModal): ?>
|
||||
<div class="cal-modal-panel mv-panel">
|
||||
<div class="cal-modal-head">
|
||||
<b><?= $editing ? '编辑纪念日' : '新建纪念日' ?></b>
|
||||
<button type="button" class="cal-modal-x" id="anNewX" aria-label="关闭">✕</button>
|
||||
<button type="button" class="cal-modal-x" id="anNewX" aria-label="关闭"<?= $fbAdd ? ' data-fb-add-close' : '' ?>>✕</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
@@ -93,7 +101,9 @@ $backUrl = url('days');
|
||||
<?= csrf_field() ?>
|
||||
<?php if ($editing): ?><input type="hidden" name="id" value="<?= (int)$e0['id'] ?>"><?php endif; ?>
|
||||
<?php if ($anModal && !$editing): ?>
|
||||
<input type="hidden" name="back" value="days">
|
||||
<?php /* 🔴 v81:首页内嵌时提交完回**首页**(`back=home`,服务端白名单见
|
||||
index.php 的 days/save)。模块页内嵌仍回列表页 `days` —— 原样不动。 */ ?>
|
||||
<input type="hidden" name="back" value="<?= $fbAdd ? 'home' : 'days' ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mv-form-inner">
|
||||
@@ -204,7 +214,7 @@ $backUrl = url('days');
|
||||
<div class="cal-modal-foot">
|
||||
<span class="cal-modal-tip" id="anNewTip"></span>
|
||||
<button class="btn btn-sm" type="submit" form="anForm" id="anNewSubmit">确定</button>
|
||||
<button class="btn btn-ghost btn-sm" type="button" id="anNewCancel">取消</button>
|
||||
<button class="btn btn-ghost btn-sm" type="button" id="anNewCancel"<?= $fbAdd ? ' data-fb-add-close' : '' ?>>取消</button>
|
||||
</div>
|
||||
</div><?php /* /.cal-modal-panel */ ?>
|
||||
<?php elseif ($anEmbed): ?>
|
||||
|
||||
+68
-7
@@ -122,14 +122,29 @@
|
||||
<?php endif; ?>
|
||||
<?php /* 🔴 v78(用户 2026-10-22):「在全部任务旁边,放一个新建任务的功能,
|
||||
其实就是把任务中的新建和编辑功能,搬过来,用弹出框来处理。」
|
||||
⇒ 卡片登记表里登记了 `add` 的(目前只有「近期任务」)就在
|
||||
「全部任务」**右边**再多一颗 —— 同一套 `.hd-more` 配色,
|
||||
所以它是任务模块的主题色,不会跟旁边那颗抢眼。
|
||||
⚠️ 按钮本身只带 `data-tk-add-open`:开关逻辑在 `assets/js/task_add.js`,
|
||||
那张弹框的 HTML 在页面末尾(`tasks/_add_modal.php`)。 */ ?>
|
||||
<?php if ((string)($hcDef['add'] ?? '') !== '' && \App\Auth::canWrite()): ?>
|
||||
⇒ 卡片登记表里登记了 `add` 的,就在「更多」**右边**再多一颗 ——
|
||||
同一套 `.hd-more` 配色,所以它是那个模块的主题色,不会抢眼。
|
||||
|
||||
🔴 v81(用户同日):「首页上的其他卡片也应该有这些基本功能。
|
||||
比如电影观影,它有一个"新增电影"的功能;如果是纪念日,就有一个
|
||||
"新增纪念日"。实际上,就是把原来模块里弹的那个框,现在拿到这边来。」
|
||||
⇒ 属性改成**通用**的 `data-fb-add-open="#<弹框容器 id>"`(v81 的
|
||||
`assets/js/add_modal.js` 认这个),弹框 id 由登记表 `add_modal` 给出。
|
||||
⚠️ 任务的弹框仍由老脚本 `task_add.js` + `data-tk-add-open` 管
|
||||
(v78 已上线,不动它以免回归);所以这里**按登记表决定挂哪个属性**,
|
||||
不是一刀切换掉 —— 两种写法并存但各自有唯一来源。 */ ?>
|
||||
<?php
|
||||
$hcAdd = (string)($hcDef['add'] ?? '');
|
||||
$hcAddModal = (string)($hcDef['add_modal'] ?? '');
|
||||
?>
|
||||
<?php if ($hcAdd !== '' && \App\Auth::canWrite()): ?>
|
||||
<button type="button" class="btn btn-sm hd-more<?= $hcColor === '' ? ' btn-ghost' : '' ?>"
|
||||
data-tk-add-open<?= $hcColor !== '' ? ' style="--hd-c: ' . e($hcColor) . ';"' : '' ?>><?= e((string)$hcDef['add']) ?></button>
|
||||
<?php if ($hcAddModal !== ''): ?>
|
||||
data-fb-add-open="#<?= e($hcAddModal) ?>" aria-haspopup="dialog" aria-expanded="false"
|
||||
<?php else: ?>
|
||||
data-tk-add-open
|
||||
<?php endif; ?>
|
||||
<?= $hcColor !== '' ? ' style="--hd-c: ' . e($hcColor) . ';"' : '' ?>><?= e($hcAdd) ?></button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php require $hcTpl; ?>
|
||||
@@ -230,3 +245,49 @@
|
||||
unset($tkAddBack, $tkAddBoards, $tkAddCats, $tkAddPeople, $tkAdd);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* ==================== 「新增电影」「新建纪念日」弹框(v81 用户 2026-10-22)============
|
||||
用户原话:「首页上的其他卡片也应该有这些基本功能。比如电影观影,它有一个"新增电影"
|
||||
的功能;如果是纪念日,就有一个"新增纪念日"。实际上,就是把原来模块里弹的那个框,
|
||||
现在拿到这边来。目的就是为了在首页上就能快速完成一定工作,而不用进到模块里面。」
|
||||
|
||||
⚠️ 与模块页**共用同一份模板**(`movies/form.php` / `days/form.php`),不另写表单:
|
||||
字段、校验、服务端契约、附件上传全部同一套,永远不会不同步。
|
||||
⚠️ 三件事必须一起给,否则这张弹框"长出来了但用不了":
|
||||
① `$fbAddModalAttr = true` —— 让模板挂 `data-fb-add-modal`(通用开关认它);
|
||||
② 模板需要的**数据**(名册 / 影院 / 分类)由路由按"卡片在不在"准备,见 index.php;
|
||||
③ 表单里的 `back=home`(模板按 `$fbAddModalAttr` 自动输出)—— 提交完回首页。
|
||||
⚠️ 只在对应卡片**真的摆在首页**且账号可写时才渲染:卡片被关掉就没有入口,
|
||||
不该白跑 People::roster / Cinema::all 这些查询。 */ ?>
|
||||
<?php if (!empty($homeAddModals) && \App\Auth::canWrite()): ?>
|
||||
<?php foreach ((array)$homeAddModals as $hcAddKey => $hcAddData): ?>
|
||||
<?php
|
||||
$fbAddModalAttr = true; // 通用开关属性(见上面第 ① 条)
|
||||
/* 每个片段自己兜底变量:模板片段不许偷宿主的变量(本项目重复踩过的坑)。 */
|
||||
$m = null; $errors = [];
|
||||
if ($hcAddKey === 'movies') {
|
||||
$people = (array)($hcAddData['people'] ?? []);
|
||||
$cinemas = (array)($hcAddData['cinemas'] ?? []);
|
||||
$mvModalOnly = true; // 不输出 page-head / 设置菜单
|
||||
$mvModalForce = true; // 强制弹框壳(新增态本来就是,写明更稳)
|
||||
$mvOpen = false; // 初始关闭,点卡片按钮才开
|
||||
require __DIR__ . '/movies/form.php';
|
||||
unset($mvModalOnly, $mvModalForce, $mvOpen, $people, $cinemas, $m, $errors);
|
||||
} elseif ($hcAddKey === 'days') {
|
||||
$anModalOnly = true; $anOpen = false; $anModal = true;
|
||||
$anEmbed = false; $anShell = true;
|
||||
/* ⚠️ 这三个必须逐个显式给:`days/form.php` 自己会 `$kindsFull ?? 查库` 兜底,
|
||||
但**兜底会多跑一次查询**,而且与模块页那份不一定同源。
|
||||
这里直接给它容器已经查好的那份(路由里按"卡片在不在"准备)。 */
|
||||
$editing = null;
|
||||
$people = (array)($hcAddData['people'] ?? []);
|
||||
$kindsFull = (array)($hcAddData['kindsFull'] ?? []);
|
||||
$kinds = array_column($kindsFull, 'name');
|
||||
require __DIR__ . '/days/form.php';
|
||||
unset($anModalOnly, $anOpen, $anModal, $anEmbed, $anShell,
|
||||
$editing, $people, $kindsFull, $kinds);
|
||||
}
|
||||
unset($fbAddModalAttr);
|
||||
?>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -258,6 +258,13 @@ require __DIR__ . '/_entry_popup.php'; ?>
|
||||
<?php /* 🔴 v78:「新建任务」弹框的开关。任务页与首页共用同一张弹框,
|
||||
所以开关也只有一份 —— 两处各写一段,必然有一处忘了打开时 boot 日期控件。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/task_add.js')) ?>" defer></script>
|
||||
<?php /* 🔴 v81(用户 2026-10-22):「首页上的其他卡片也应该有这些基本功能……把原来模块里
|
||||
弹的那个框,现在拿到这边来。」⇒ 观影、纪念日的新建弹框也搬上首页。
|
||||
三张弹框的开关逻辑一模一样(点遮罩关 / 点面板不关 / Esc 先让 antd 收面板 /
|
||||
打开那一刻才 boot 日期控件),所以抽成**一份通用的**,各自只声明容器 id。
|
||||
⚠️ 与上面 `task_add.js` 并存不冲突:那一份仍管 `#taskAddCard`(v78 已上线),
|
||||
这一份只认 `[data-fb-add-modal]`,两者选择器不相交。 */ ?>
|
||||
<script src="<?= e(asset('/assets/js/add_modal.js')) ?>" defer></script>
|
||||
<?php /* 🔴 v32 第 3 条:浏览器通知(站内消息 + 日程到期提醒)。
|
||||
⚠️ **只在已登录时加载** —— 登录页轮询会一直 302 去登录页,白跑。
|
||||
同理也不在"免登录的导航展示页"上跑(那页没有会话)。
|
||||
|
||||
@@ -91,8 +91,14 @@ $mvModalOnly = !empty($mvModalOnly);
|
||||
$mvModalForce = !empty($mvModalForce);
|
||||
$mvModal = $mvModalForce || !$isEdit;
|
||||
$mvOpen = isset($mvOpen) ? !empty($mvOpen) : ($mvModal && !$mvModalOnly);
|
||||
/* 🔴 v81:首页内嵌时挂通用开关属性(`assets/js/add_modal.js`)。
|
||||
⚠️ **只在首页设**(`$fbAddModalAttr` 由 `templates/home.php` 传):
|
||||
模块页(观影列表页)自己有内联开关脚本,再挂通用属性会变成两套一起绑,
|
||||
Esc / 点遮罩各关一次 —— 当前虽无害,但那是"两份实现同时生效",迟早出事。
|
||||
所以这里按需挂,模块页行为一个字不变。 */
|
||||
$fbAdd = !empty($fbAddModalAttr);
|
||||
?>
|
||||
<div class="<?= $mvModal ? 'cal-modal' : 'mv-form-wrap' ?>"<?= $mvModal ? ' id="mvAddCard" role="dialog" aria-modal="true" aria-label="' . ($isEdit ? '编辑观影记录' : '记一场电影') . '"' . ($mvOpen ? '' : ' hidden') : '' ?>>
|
||||
<div class="<?= $mvModal ? 'cal-modal' : 'mv-form-wrap' ?>"<?= $mvModal ? ' id="mvAddCard" role="dialog" aria-modal="true" aria-label="' . ($isEdit ? '编辑观影记录' : '记一场电影') . '"' . ($mvOpen ? '' : ' hidden') . ($fbAdd ? ' data-fb-add-modal' : '') : '' ?>>
|
||||
<?php if ($mvModal): ?>
|
||||
<?php /* 🔴 v40:弹框的标题 / 主按钮文案**随用途变** —— 详情页点「编辑」时进来的是
|
||||
同一份模板,标题得写「编辑观影记录」、按钮写「保存」,不能还叫"记下这场电影"。
|
||||
@@ -101,7 +107,7 @@ $mvOpen = isset($mvOpen) ? !empty($mvOpen) : ($mvModal && !$mvModalOnly);
|
||||
<div class="cal-modal-panel mv-panel">
|
||||
<div class="cal-modal-head">
|
||||
<b><?= $isEdit ? '编辑观影记录' : '记一场电影' ?></b>
|
||||
<button type="button" class="cal-modal-x" id="mvAddX" aria-label="关闭">✕</button>
|
||||
<button type="button" class="cal-modal-x" id="mvAddX" aria-label="关闭"<?= $fbAdd ? ' data-fb-add-close' : '' ?>>✕</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form method="post" enctype="multipart/form-data"
|
||||
@@ -111,6 +117,14 @@ $mvOpen = isset($mvOpen) ? !empty($mvOpen) : ($mvModal && !$mvModalOnly);
|
||||
<?php if ($isEdit): ?>
|
||||
<input type="hidden" name="id" value="<?= (int)$m['id'] ?>">
|
||||
<?php endif; ?>
|
||||
<?php /* 🔴 v81:首页内嵌时提交完回**首页**(不是跳去观影列表/详情 ——
|
||||
用户要的是"不离开首页就把事办了",跳走就等于把他从首页赶出去)。
|
||||
取值走服务端白名单(见 index.php 的 movies/create 分支),
|
||||
**不把 POST 的字符串直接当跳转地址**(开放重定向)。
|
||||
⚠️ 只在新建时给(编辑没有"回首页"的语义)。 */ ?>
|
||||
<?php if (!$isEdit && $fbAdd): ?>
|
||||
<input type="hidden" name="back" value="home">
|
||||
<?php endif; ?>
|
||||
<?php /* ⚠️ 弹框模式下"主要动作在右下角"(与日程 / 任务弹框一致):
|
||||
body 滚、foot 固定。编辑态仍是页面上的普通卡片。 */ ?>
|
||||
<?php if ($mvModal): ?><div class="cal-modal-body"><?php endif; ?>
|
||||
@@ -394,7 +408,7 @@ $mDelForms = []; // 收集起来,在表单**外面**统一渲染
|
||||
<div class="cal-modal-foot">
|
||||
<span class="cal-modal-tip" id="mvAddTip"></span>
|
||||
<button class="btn btn-sm" type="submit" form="mvForm" id="mvAddSubmit">确定</button>
|
||||
<button class="btn btn-ghost btn-sm" type="button" id="mvAddCancel">取消</button>
|
||||
<button class="btn btn-ghost btn-sm" type="button" id="mvAddCancel"<?= $fbAdd ? ' data-fb-add-close' : '' ?>>取消</button>
|
||||
</div>
|
||||
</div><?php /* /.cal-modal-panel(仅弹框形态) */ ?>
|
||||
<?php else: ?>
|
||||
|
||||
Reference in New Issue
Block a user