diff --git a/scripts/patch_admin_timeline_ui.py b/scripts/patch_admin_timeline_ui.py
index da18ffe..24e9882 100644
--- a/scripts/patch_admin_timeline_ui.py
+++ b/scripts/patch_admin_timeline_ui.py
@@ -58,27 +58,48 @@ def write_detail_collapse_script(theme_dir: Path) -> bool:
index = theme_dir / 'index.html'
if not assets.exists() or not index.exists():
return False
- js_path = assets / 'fcb-detail-collapse.js'
+ js_path = assets / 'fcb-lifecycle-collapse-v3.js'
js = r'''(() => {
- const EVENT_RE = /(第\s*\d+\s*次取件|取件\s*[##]\s*\d+|Retrieval\s*#\s*\d+)/i;
const LIFE_RE = /(生命周期|Lifecycle)/i;
const STATE = new WeakMap();
- const getRows = section => [...section.querySelectorAll('div.grid')].filter(row => EVENT_RE.test(row.textContent || ''));
+ const getSection = section => {
+ const title = section.querySelector('h1,h2,h3,h4,h5');
+ if (!title || !LIFE_RE.test(title.textContent || '')) return null;
+ const box = title.nextElementSibling;
+ if (!box) return null;
+ return { title, box };
+ };
+ const getRows = section => {
+ const ctx = getSection(section);
+ if (!ctx) return [];
+ return [...ctx.box.children].filter(el => el instanceof HTMLElement);
+ };
const apply = section => {
- const rows = getRows(section);
+ const ctx = getSection(section);
+ if (!ctx) return;
+ const rows = [...ctx.box.children].filter(el => el instanceof HTMLElement);
if (rows.length <= 3) return;
const expanded = STATE.get(section) === true;
- rows.forEach((row, idx) => { row.hidden = !expanded && idx >= 3; });
+ section.classList.toggle('fcbx-life-expanded', expanded);
+ rows.forEach((row, idx) => {
+ const shouldHide = idx >= 3 && !expanded;
+ row.classList.toggle('fcbx-life-row-extra', idx >= 3);
+ if (shouldHide) {
+ if (row.style.display !== 'none') row.style.setProperty('display', 'none', 'important');
+ } else if (row.style.display) {
+ row.style.removeProperty('display');
+ }
+ });
const btn = section.querySelector(':scope > .fcbx-life-toggle') || section.querySelector('.fcbx-life-toggle');
if (btn) {
- btn.textContent = expanded ? '收起取件记录' : `展开全部取件记录(共 ${rows.length} 次)`;
+ btn.textContent = expanded ? '收起生命周期记录' : `展开全部生命周期记录(共 ${rows.length} 条)`;
btn.setAttribute('aria-expanded', expanded ? 'true' : 'false');
}
};
const ensureSection = section => {
- const title = section.querySelector('h1,h2,h3,h4,h5');
- if (!title || !LIFE_RE.test(title.textContent || '')) return;
- const rows = getRows(section);
+ const ctx = getSection(section);
+ if (!ctx) return;
+ const rows = [...ctx.box.children].filter(el => el instanceof HTMLElement);
if (rows.length <= 3) return;
if (!STATE.has(section)) STATE.set(section, false);
let btn = section.querySelector(':scope > .fcbx-life-toggle');
@@ -86,40 +107,44 @@ def write_detail_collapse_script(theme_dir: Path) -> bool:
btn = document.createElement('button');
btn.type = 'button';
btn.className = 'fcbx-life-toggle mt-3 rounded-lg border px-3 py-2 text-sm font-medium transition';
- btn.style.cssText = 'border-color:rgba(148,163,184,.55);display:inline-flex;align-items:center;gap:.35rem;touch-action:manipulation;user-select:none;';
+ btn.style.cssText = 'border-color:rgba(148,163,184,.55);display:inline-flex;align-items:center;gap:.35rem;touch-action:manipulation;user-select:none;cursor:pointer;';
+ btn.onclick = event => {
+ event.preventDefault();
+ event.stopPropagation();
+ STATE.set(section, STATE.get(section) !== true);
+ apply(section);
+ };
section.appendChild(btn);
}
apply(section);
};
- const markRows = () => document.querySelectorAll('section').forEach(ensureSection);
- const toggle = target => {
- const btn = target.closest?.('.fcbx-life-toggle');
- if (!btn) return false;
- const section = btn.closest('section');
- if (!section) return false;
- STATE.set(section, STATE.get(section) !== true);
- apply(section);
- return true;
+ const markRows = root => {
+ const scope = root?.querySelectorAll ? root : document;
+ scope.querySelectorAll('section').forEach(ensureSection);
};
- const onToggle = event => {
- if (!toggle(event.target)) return;
- event.preventDefault();
- event.stopPropagation();
- event.stopImmediatePropagation?.();
- };
- document.addEventListener('pointerdown', onToggle, true);
- document.addEventListener('click', onToggle, true);
let raf = 0;
- const schedule = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(markRows); };
- window.addEventListener('hashchange', schedule);
- document.addEventListener('DOMContentLoaded', schedule);
- new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true });
- schedule();
+ const schedule = root => { cancelAnimationFrame(raf); raf = requestAnimationFrame(() => markRows(root)); };
+ window.addEventListener('hashchange', () => schedule(document));
+ document.addEventListener('DOMContentLoaded', () => schedule(document));
+ new MutationObserver(records => {
+ for (const rec of records) {
+ if ([...rec.addedNodes].some(n => n.nodeType === 1 && ((n.matches?.('section')) || n.querySelector?.('section')))) {
+ schedule(document);
+ break;
+ }
+ }
+ }).observe(document.body || document.documentElement, { childList: true, subtree: true });
+ schedule(document);
})();'''
rewrite(js_path, js)
html = index.read_text(encoding='utf-8')
- tag = ''
- if 'fcb-detail-collapse.js' not in html:
+ # Remove old detail-collapse injection so browsers cannot keep loading the stale “取件记录” script.
+ html = html.replace('\n', '')
+ html = html.replace('', '')
+ html = html.replace('\n', '')
+ html = html.replace('', '')
+ tag = ''
+ if 'fcb-lifecycle-collapse-v3.js' not in html:
html = html.replace('