v43 第 43 批:六条反馈(界面 5 + 条目详情浮层)
用户 2026-10-11 反馈六条,逐条定位真根因后修复: ① 纪念日「日期」多出一个控件 —— 只保留最新的那个 两层根因:(a)原生 `input[type=date]` 当初没包在 `.dt-native` 里, v42 那条"AntD 挂上后收起原生框"的选择器根本不命中;(b)更要命的是 `.dt-native` 基类写着 `display: contents` —— 该元素**没有盒子**, 给它 position/width 完全无效 ⇒ 必须把 display 显式改回 block 才收得起来。 ② 编辑纪念日把底层列表页弄没 —— 「修改」原来是整页跳 `days/edit` (套 layout = 一张只有弹框的空白页)。改为 `?embed=1` 只吐弹框那一块, 列表页 JS 取回来替换 `#anNewCard` 并打开 —— 与「新建」同一观感。 内嵌形态标 `$anModalOnly` 以免打 standalone 标记(关闭时不再跳页)。 ③ 拖图裁剪的层被弹框盖住 —— `.crop-overlay` 是 z-index:100 而 `.cal-modal` 是 200 ⇒ 裁剪层被整块盖住。提到 260(与 v41 的 .cn-modal 同一处理)。 ④ 行程弹框里"参与的人"弹不出选人框 —— person_picker.js 只绑定一次, 而弹框表单是 AJAX 片段 ⇒ 里面的 `.pp` 从未被 init。三件事一起: 暴露 `window.fbInitPickers()`(幂等)+ layout 暴露 `window.fbLoadPicker()` (脚本是按需加载的)+ 塞完片段后补调初始化。 ⑤ 日程新建排版/控件/文案 —— 改走观影式 `.mv-grid`(标签对齐 + 冒号); 起止补上 `$dtPlugin='antd'`(试点正式推广到日程);标签「时间范围」→「开始 / 结束」; 删三句提示(结束留空 / 多选提醒 / 全天)。 ⑥ 条目详情浮层(首页日程 / 首页近期任务 / 日程页条目)—— 点条目不再直接跳页,弹浮层显示详情;「详细信息」跳该来源详情页; 「编辑」就地换成该来源的编辑表单(四处 `?embed=1` 片段)。 新增 `templates/_entry_popup.php` + `assets/js/entry_popup.js` + `fb_entry_json()`(三个入口共用一个编码函数)。 验证:php -l 157 文件全过;div 深度 33 组合全配平;片段体检 20 片段无越界; 本地 e2e 1510/0;v43 真机 33/0;v43 反向验证 8/0;v38 36/0;v42 33/0; b31/b32 反向验证 8/0 与 5/0;部署断言本地空跑 RC=0;完整流水线 PUSH_RC=0。
This commit is contained in:
+54
-5
@@ -1486,7 +1486,15 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
|
||||
Cropper 的四个角手柄画成实心白点,永远落在面板可见区内。 */
|
||||
.crop-overlay { display: none; }
|
||||
.crop-overlay.is-open {
|
||||
display: flex; position: fixed; inset: 0; z-index: 100;
|
||||
/* 🔴🔴 v43(用户 2026-10-11 第 3 条):「新建行程的时候,如果把地图拖进去,
|
||||
它不是开始裁剪吗?但是这个弹出的框把裁剪那个框又盖住了,裁剪的框应该放到前面来。」
|
||||
真根因:裁剪层是 **100**,而行程/观影那些弹框用的 `.cal-modal` 是 **200**
|
||||
⇒ 从弹框里拖图触发的裁剪层被整块盖在下面 —— 看不见、也点不到。
|
||||
(与 v41 那个「新建影院弹框被记一场电影盖住」是**同一个坑**,当时把 .cn-modal
|
||||
提到了 260;这里漏了。)
|
||||
⇒ 提到 **260**:高于所有自建弹框(.cal-modal 200 / .pp-modal 等),
|
||||
与 .cn-modal 同层;低于灯箱(300)、预览层(320) —— 那两者是"看大图"的顶级层。 */
|
||||
display: flex; position: fixed; inset: 0; z-index: 260;
|
||||
align-items: center; justify-content: center;
|
||||
padding: 20px; background: rgba(44, 35, 29, .55);
|
||||
}
|
||||
@@ -5610,14 +5618,55 @@ h5.md-h, h6.md-h { font-size: 14px; }
|
||||
· `.dt-antd` 是 antd 的挂载点(宽度交给它);
|
||||
· 挂上后(`.is-antd`)把**原生 `<input type="date">` 视觉收起** ——
|
||||
它仍是值载体(antd 通过 change 写它),只是不再显示给用户。
|
||||
⚠️ 用 clip 收起(不是 display:none):保留可聚焦、可被表单读到,
|
||||
与 v40 高级搜索那处的取舍一致。 */
|
||||
.dt-input.is-antd > .dt-native { position: absolute; width: 1px; height: 1px;
|
||||
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px; }
|
||||
⚠️ 用 clip 收起(不是 display:none):保留可聚焦、可被表单读到。
|
||||
🔴🔴 v43(用户 2026-10-11 第 1 条):「日期那里多了一个控件,
|
||||
只保留我们最新的控件就行,之前那种控件删除」——真根因有两层:
|
||||
① 纪念日的原生框当初**没包在 `.dt-native` 里** ⇒ 这条选择器根本不命中
|
||||
(模板已补上包装);
|
||||
② **更要命的一层**:`.dt-input .dt-native { display: contents }`(第 619 行)
|
||||
会让这个包装盒**自己不生成盒子**、子元素"提升"成 `.dt-input` 的直接布局项 ——
|
||||
此时给它 `position:absolute; width:1px` **完全无效**(它没有盒子可定位),
|
||||
原生框照样按 flex 项排出来 ⇒ 两个控件并排。
|
||||
⇒ 修法:这里必须把 `display` **从 contents 改成 block**,定位才生效。
|
||||
(只写 position/width/height 是不够的 —— 这是我第一版的错,探针当场抓到。) */
|
||||
.dt-input.is-antd > .dt-native {
|
||||
display: block; position: absolute; width: 1px; height: 1px;
|
||||
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
|
||||
}
|
||||
.dt-input.is-antd > .dt-antd { display: block; width: 100%; }
|
||||
.dt-input.dt-antd-single { display: block; width: 100%; }
|
||||
/* 兜底:万一哪天原生框又变成 `.dt-input` 的**直接子元素**(不包 .dt-native 了),
|
||||
这条按直接子元素也能把它收起来。 */
|
||||
.dt-input.is-antd > input[type="date"] {
|
||||
display: block; position: absolute; width: 1px; height: 1px;
|
||||
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0; margin: -1px;
|
||||
}
|
||||
|
||||
/* ---- v42(用户第 6 条)简介为空时的那句可点占位 ----
|
||||
与 `.trip-sum` 同一视觉位置,只是颜色淡一档(它是要点进去补的提示,不是内容)。 */
|
||||
.trip-sum-empty { margin: 10px 0 0; font-size: 13px; color: var(--muted); }
|
||||
.trip-sum-empty a { color: var(--brand); }
|
||||
|
||||
/* ============================================================================
|
||||
🔴 v43(用户 2026-10-11 第 6 条)条目详情浮层
|
||||
----------------------------------------------------------------------------
|
||||
与 .cal-modal 同一形态(遮罩 + 居中面板 + 底栏),只是层级更高一点:
|
||||
它可能从**另一个浮层**里打开(例如详情页弹框里点了条目),
|
||||
所以要在 200 之上;但低于"看图"的灯箱(300)/预览层(320)。
|
||||
⚠️ 与 .cn-modal(260)、.crop-overlay(260) 同层 —— 它们不会同时出现。
|
||||
⚠️ 面板用 flex 列 + body 滚动,长内容(编辑表单)才有足够的可视区。 */
|
||||
.fb-entry-modal {
|
||||
position: fixed; inset: 0; z-index: 260;
|
||||
display: flex; align-items: flex-start; justify-content: center;
|
||||
padding: 24px 16px; overflow-y: auto;
|
||||
background: rgba(44, 35, 29, .55); overscroll-behavior: contain;
|
||||
}
|
||||
.fb-entry-modal[hidden] { display: none; }
|
||||
.fb-entry-panel { width: min(620px, 100%); max-height: calc(100vh - 64px); }
|
||||
/* 编辑态下正文里就是一张完整表单(内边距由它自己那份 CSS 给)——
|
||||
这里只保证它不双份留白。 */
|
||||
.fb-entry-panel .cal-modal-body > .mv-form-inner,
|
||||
.fb-entry-panel .cal-modal-body > .nf-wrap { padding: 0; }
|
||||
@media (max-width: 640px) {
|
||||
.fb-entry-panel { width: 100%; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/* ============================================================================
|
||||
🔴 v43(用户 2026-10-11 第 6 条)—— 条目详情浮层
|
||||
----------------------------------------------------------------------------
|
||||
用户原话:
|
||||
「首页上的日程、首页上的近期任务,以及日程里面的日程,点击它们的时候,
|
||||
都会弹出一个框显示更详细的信息。上面有不同的功能按钮,比如说详细信息,
|
||||
在日程里面,如果是观影啊、旅程啊,点击详细信息就会进入到观影或者旅程的
|
||||
那个相关的页面。如果是日程的话,点击详细信息就进到日程的详细页面。
|
||||
当然还可以直接点击编辑,点击编辑不同的来源就进入不同的编辑页面,
|
||||
就在当前的页面弹窗,把之前的窗给去掉,然后弹出编辑的那个框来就可以了。」
|
||||
|
||||
⇒ 一个浮层 + 事件委托,服务全站三种入口:
|
||||
· 首页「今天的日程」(`.ag-ev` / `.ag-allday-item`)
|
||||
· 首页「近期任务」(`.tk-row` 的标题)
|
||||
· 日程页月/周/日视图的条目(`.cal-ev` / `.tg-block`)
|
||||
|
||||
设计取舍(每条都对应一个踩过的坑):
|
||||
① **事件委托挂 document**:列表会被 AJAX 换掉(新建日程后就地插入、
|
||||
首页任务勾选后重画),一次性绑定永久有效(v40 的教训)。
|
||||
② **点条目 = 弹浮层,不跳页**:拦掉 `<a href>` 的默认行为。
|
||||
⚠️ 必须按 `[data-fb-entry]` 精确匹配 —— 不能笼统拦 `.cal-ev`:
|
||||
页面里还有"点某天新建日程"那套逻辑(它同样排除 `.cal-ev`)。
|
||||
③ **数据全在 `data-fb-entry` 上**:浮层不发请求就能画出来,秒开;
|
||||
服务端不参与"点开一条"这个动作。
|
||||
④ **「编辑」就地换内容**:取该来源的编辑表单片段(`?embed=1`)。
|
||||
用户原话「把之前的窗给去掉,然后弹出编辑的那个框来」——
|
||||
所以是"正文整块换掉",不是叠第二层浮层。
|
||||
⑤ **保存走原生提交**:四个来源的 update 路由本来就收普通 POST 并跳转,
|
||||
不依赖任何页面自带脚本 ⇒ 浮层在任何页面都能保存成功。
|
||||
(自己写 AJAX 保存要为四个来源各写一套"成功后回哪"的规则,得不偿失。
|
||||
原生提交会整页刷新 —— 对"改了数据"这件事来说,刷新一次反而更清楚。)
|
||||
⑥ **Esc 只关最上面那层**:antd 面板开着时先让它收(v38/v39 的坑)。
|
||||
⚠️ 本脚本**全站加载**(layout.php):三处入口分散在不同页面,按需加载的判据
|
||||
会在这些页面之间来回漏;它只有几 KB,不值得为省这点流量赌漏加载。
|
||||
============================================================================ */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var modal = null;
|
||||
var detailCache = null; /* 详情态的数据(从编辑态「返回」时用它重画) */
|
||||
|
||||
function M() { if (!modal) { modal = document.getElementById('fbEntryModal'); } return modal; }
|
||||
function bodyEl() { return document.getElementById('fbEntryBody'); }
|
||||
function titleEl(){ return document.getElementById('fbEntryTitle'); }
|
||||
function tipEl() { return document.getElementById('fbEntryTip'); }
|
||||
function byId(id) { return document.getElementById(id); }
|
||||
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s)
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function setTip(msg, err) {
|
||||
var t = tipEl();
|
||||
if (!t) { return; }
|
||||
t.textContent = msg || '';
|
||||
t.className = 'cal-modal-tip' + (err ? ' err' : '');
|
||||
}
|
||||
|
||||
/* ---- 底栏两种形态 --------------------------------------------------------- */
|
||||
function footDetail() {
|
||||
['fbEntryDetail', 'fbEntryEdit'].forEach(function (id) {
|
||||
var e = byId(id); if (e) { e.hidden = false; }
|
||||
});
|
||||
['fbEntryBack', 'fbEntrySave'].forEach(function (id) {
|
||||
var e = byId(id); if (e) { e.hidden = true; }
|
||||
});
|
||||
}
|
||||
function footEdit() {
|
||||
['fbEntryDetail', 'fbEntryEdit'].forEach(function (id) {
|
||||
var e = byId(id); if (e) { e.hidden = true; }
|
||||
});
|
||||
['fbEntryBack', 'fbEntrySave'].forEach(function (id) {
|
||||
var e = byId(id); if (e) { e.hidden = false; }
|
||||
});
|
||||
}
|
||||
function footNone() { /* 载入中 / 出错:全都先藏起来 */
|
||||
['fbEntryDetail', 'fbEntryEdit', 'fbEntryBack', 'fbEntrySave'].forEach(function (id) {
|
||||
var e = byId(id); if (e) { e.hidden = true; }
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- 详情态渲染 ----------------------------------------------------------- */
|
||||
function rowsHtml(list) {
|
||||
var html = '';
|
||||
(list || []).forEach(function (r) {
|
||||
if (!r || r[1] === '' || r[1] == null) { return; }
|
||||
html += '<div class="kv-row"><dt>' + esc(r[0]) + '</dt><dd>' + esc(r[1]) + '</dd></div>';
|
||||
});
|
||||
return html || '<p class="empty-hint">这条没有更多信息。</p>';
|
||||
}
|
||||
|
||||
function paintDetail(data) {
|
||||
if (titleEl()) { titleEl().textContent = data.title || '详情'; }
|
||||
if (bodyEl()) { bodyEl().innerHTML = '<dl class="kv">' + rowsHtml(data.rows) + '</dl>'; }
|
||||
var det = byId('fbEntryDetail');
|
||||
if (det) {
|
||||
if (data.detail) { det.href = data.detail; det.hidden = false; }
|
||||
else { det.hidden = true; }
|
||||
}
|
||||
var ed = byId('fbEntryEdit');
|
||||
if (ed) { ed.disabled = false; ed.hidden = !data.edit; }
|
||||
// 「编辑」不可用时别把它留在底栏上(否则点了没反应)
|
||||
if (!data.edit) { var e2 = byId('fbEntryEdit'); if (e2) { e2.hidden = true; } }
|
||||
setTip('');
|
||||
footDetail();
|
||||
if (!data.edit) { var e3 = byId('fbEntryEdit'); if (e3) { e3.hidden = true; } }
|
||||
}
|
||||
|
||||
function restoreDetail() {
|
||||
if (!detailCache) { return; }
|
||||
var d = detailCache;
|
||||
paintDetail(d);
|
||||
}
|
||||
|
||||
/* ---- 打开 / 关闭 ---------------------------------------------------------- */
|
||||
function setOpen(on) {
|
||||
var m = M();
|
||||
if (!m) { return; }
|
||||
m.hidden = !on;
|
||||
if (!on) { detailCache = null; if (bodyEl()) { bodyEl().innerHTML = ''; } }
|
||||
}
|
||||
|
||||
function antdOpen() {
|
||||
var d = document.querySelector('.ant-picker-dropdown');
|
||||
return !!(d && !d.classList.contains('ant-picker-dropdown-hidden'));
|
||||
}
|
||||
|
||||
/* ---- 编辑片段:取回 → 剥壳 → 塞进正文 → 补跑初始化 ------------------------ */
|
||||
/* innerHTML 不执行脚本(v42 的血泪结论)⇒ 抠出来重跑。 */
|
||||
function runInlineScripts(container, html) {
|
||||
var re = /<script\b[^>]*>([\s\S]*?)<\/script>/gi, m, code = '';
|
||||
while ((m = re.exec(html)) !== null) {
|
||||
if (!/\bsrc\s*=/i.test(m[0].split('>')[0])) { code += m[1] + '\n;\n'; }
|
||||
}
|
||||
if (!code) { return; }
|
||||
var s = document.createElement('script');
|
||||
s.textContent = code;
|
||||
(container || document.body).appendChild(s);
|
||||
s.parentNode.removeChild(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 片段可能是三种形态:整页(带 layout)/只带弹框壳/只有表单。
|
||||
* 我们要的是**表单本体** —— 弹框壳必须剥掉,否则正文里又多一层遮罩。
|
||||
*/
|
||||
function pickForm(html) {
|
||||
var tmp = document.createElement('div');
|
||||
tmp.innerHTML = html;
|
||||
var form = tmp.querySelector('#anForm, #mvForm, #nodeForm, #tripForm, #calEditForm, form');
|
||||
if (form) { return { form: form, html: html }; }
|
||||
return null;
|
||||
}
|
||||
|
||||
function loadEdit(url) {
|
||||
if (!url) { setTip('这条没有可编辑的表单。', true); return; }
|
||||
setTip('');
|
||||
footNone();
|
||||
if (titleEl()) { titleEl().textContent = '载入中…'; }
|
||||
if (bodyEl()) { bodyEl().innerHTML = '<p class="empty-hint">正在载入…</p>'; }
|
||||
if (!window.fetch) { window.location.href = url; return; }
|
||||
fetch(url, { credentials: 'same-origin',
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' } })
|
||||
.then(function (r) { return r.text(); })
|
||||
.then(function (html) {
|
||||
if (!bodyEl()) { return; }
|
||||
var picked = pickForm(html);
|
||||
if (!picked) { window.location.href = url; return; }
|
||||
bodyEl().innerHTML = '';
|
||||
bodyEl().appendChild(picked.form);
|
||||
// 片段里的初始化脚本 + 宿主这侧的通用升级(日期 / antd / 选人 / 上传 / 行程表单)
|
||||
runInlineScripts(bodyEl(), picked.html);
|
||||
if (window.fbUpgradeRange) { window.fbUpgradeRange(); }
|
||||
if (window.fbBootAntd) { window.fbBootAntd(); }
|
||||
if (window.fbNodeForm) { window.fbNodeForm(bodyEl()); }
|
||||
if (typeof window.fbLoadPicker === 'function') { window.fbLoadPicker(); }
|
||||
if (typeof window.fbInitPickers === 'function') { window.fbInitPickers(bodyEl()); }
|
||||
// 标题取表单片段里的自我描述(各模板的弹框头)
|
||||
var h = picked.form.querySelector('.cal-modal-head b')
|
||||
|| (html.match(/aria-label="([^"]*编辑[^"]*)"/) || [])[1];
|
||||
if (titleEl()) { titleEl().textContent = h ? String(h).trim() : '编辑'; }
|
||||
footEdit();
|
||||
var f = bodyEl().querySelector('#nf-title, #an-title, #ev-title, #title, input[name="title"]');
|
||||
if (f) { f.focus(); }
|
||||
})
|
||||
.catch(function () { window.location.href = url; });
|
||||
}
|
||||
|
||||
/** 编辑态「保存」= 原生提交那张表单(服务端 update 路由会跳转,刷新到新页)。 */
|
||||
function submitEdit() {
|
||||
var form = bodyEl() && (bodyEl().querySelector('form#anForm, form#mvForm, form#nodeForm, form#tripForm, form#calEditForm')
|
||||
|| bodyEl().querySelector('form'));
|
||||
if (!form) { setTip('找不到可保存的表单。', true); return; }
|
||||
form.setAttribute('data-fb-entry-submit', '1');
|
||||
if (typeof form.requestSubmit === 'function') { form.requestSubmit(); }
|
||||
else { form.submit(); }
|
||||
}
|
||||
|
||||
/* ---- 打开一条 ------------------------------------------------------------- */
|
||||
function openEntry(el) {
|
||||
var raw = el.getAttribute('data-fb-entry') || '';
|
||||
var data;
|
||||
try { data = JSON.parse(raw); } catch (e) { return; } /* 数据坏了就别拦点击 */
|
||||
if (!data || typeof data !== 'object') { return; }
|
||||
detailCache = data;
|
||||
paintDetail(data);
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var t = e.target;
|
||||
if (!t || !t.closest) { return; }
|
||||
|
||||
if (t.closest('[data-fb-entry-close]')) { e.preventDefault(); setOpen(false); return; }
|
||||
if (t.closest('#fbEntryEdit')) {
|
||||
e.preventDefault();
|
||||
if (detailCache && detailCache.edit) { loadEdit(detailCache.edit); }
|
||||
return;
|
||||
}
|
||||
if (t.closest('#fbEntryBack')) { e.preventDefault(); restoreDetail(); return; }
|
||||
if (t.closest('#fbEntrySave')) { e.preventDefault(); submitEdit(); return; }
|
||||
|
||||
var item = t.closest('[data-fb-entry]');
|
||||
if (item) { e.preventDefault(); openEntry(item); return; }
|
||||
|
||||
var m = M();
|
||||
if (m && t === m) { setOpen(false); } /* 点遮罩关;点面板内不关 */
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key !== 'Escape') { return; }
|
||||
var m = M();
|
||||
if (!m || m.hidden) { return; }
|
||||
if (antdOpen()) { return; } /* 让 antd 先收它自己的面板 */
|
||||
e.preventDefault();
|
||||
setOpen(false);
|
||||
});
|
||||
|
||||
/* 编辑态里表单用回车提交时,别让浮层的 Esc 逻辑挡住(默认行为即可)——
|
||||
这里不再另挂 submit 监听:原生提交就是要它跳走。 */
|
||||
|
||||
window.fbEntryOpen = openEntry;
|
||||
window.fbEntryClose = function () { setOpen(false); };
|
||||
})();
|
||||
@@ -83,6 +83,14 @@
|
||||
|
||||
function init(root) {
|
||||
var id = root.getAttribute('data-pp') || '';
|
||||
/* 🔴 v43:**幂等闸门**。组件现在可能在"动态插入"之后被重复 init
|
||||
(见文件末尾 `window.fbInitPickers`)。没有这道闸门会挂上第二套监听:
|
||||
· 打开弹框被处理两次、暂存区 chip 加两次;
|
||||
· `change` 里来回切选中态。
|
||||
⚠️ 事件委托本来就是"一次绑定、永久有效",重复调用只需要它**什么也不做**。
|
||||
⚠️ 标记放在**取到必需元素之后**打 —— 否则"结构不全时提前 return"会把
|
||||
这个组件永久标记成"已初始化",之后再补它也没用了。 */
|
||||
if (root.getAttribute('data-pp-init') === '1') { return; }
|
||||
var isMulti = root.getAttribute('data-pp-mode') !== 'single';
|
||||
var modal = root.querySelector('.pp-modal');
|
||||
var openBtn = root.querySelector('.pp-open');
|
||||
@@ -92,6 +100,8 @@
|
||||
var chips = root.querySelector('.pp-chips');
|
||||
var emptyTip = root.querySelector('.pp-empty');
|
||||
if (!modal || !list || !chips) { return; }
|
||||
/* 🔴 v43:必需元素取齐了才打"已初始化"标记(见上面幂等闸门的说明)。 */
|
||||
root.setAttribute('data-pp-init', '1');
|
||||
|
||||
// ---- 第 19 批 用户第 8 条:多选弹框隐藏每行前面的复选框 ----
|
||||
// 用户原话:「弹框选择人员那里,暂存区太小了…前面的复选框就没用了吧,去掉吧。」
|
||||
@@ -715,6 +725,30 @@
|
||||
Array.prototype.forEach.call(document.querySelectorAll('.pp'), init);
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔴🔴 v43(用户 2026-10-11 第 4 条):「在旅程功能里面,编辑一个行程的时候,
|
||||
* 现在"参与的人"添加参与人的那个功能已经不好使了,弹不出选人的框了。」
|
||||
*
|
||||
* 真根因:本组件的绑定只跑一次 —— 脚本加载时 `boot()` 扫描当时的 `.pp`。
|
||||
* 而**详情页的行程弹框**是 AJAX 取片段、`innerHTML` 塞进 DOM 的
|
||||
* ⇒ 那一刻才出现的 `.pp` **从来没被 init 过**
|
||||
* ⇒「+ 添加参与的人」按钮上没有任何 click 监听 ⇒ 点了没反应(弹不出弹框)。
|
||||
* (同一个根因也解释了"暂存区/确定/取消"全都按不动。)
|
||||
*
|
||||
* ⇒ 对外暴露 `window.fbInitPickers(scope)`:任何"动态插入含 `.pp` 的 HTML"之后
|
||||
* 调它一次即可。`init()` 内部有 `data-pp-init` 幂等标记,重复调用安全。
|
||||
*
|
||||
* ⚠️ 本脚本是**按需加载**的(layout.php 里页面上有 `.pp` 才拉),而弹框里的 `.pp`
|
||||
* 是后来才出现的 ⇒ 加载判据可能在脚本拉起时还没看到它。所以宿主调这个钩子时,
|
||||
* 要容忍"函数还不存在"(那时从 `.pp` 到脚本的按需加载链还没走完);
|
||||
* 见 trips/show.php 里那段"等它就绪"的处理。
|
||||
*/
|
||||
window.fbInitPickers = function (scope) {
|
||||
var root = scope || document;
|
||||
Array.prototype.forEach.call(root.querySelectorAll('.pp'), init);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user