diff --git a/scripts/patch_admin_timeline_ui.py b/scripts/patch_admin_timeline_ui.py index 2d901ec..c7f985e 100644 --- a/scripts/patch_admin_timeline_ui.py +++ b/scripts/patch_admin_timeline_ui.py @@ -2,7 +2,7 @@ from pathlib import Path import gzip -ASSETS = Path('/app/themes/2024/assets') +ASSETS_ROOT = Path('/app') def rewrite(path: Path, text: str) -> None: @@ -53,14 +53,75 @@ def patch_clipboard(path: Path) -> bool: return False +def write_detail_collapse_script(theme_dir: Path) -> bool: + assets = theme_dir / 'assets' + index = theme_dir / 'index.html' + if not assets.exists() or not index.exists(): + return False + js_path = assets / 'fcb-detail-collapse.js' + js = r'''(() => { + const EVENT_RE = /(第\s*\d+\s*次取件|取件\s*[##]\s*\d+|Retrieval\s*#\s*\d+)/i; + const LIFE_RE = /(生命周期|Lifecycle)/i; + const markRows = () => { + document.querySelectorAll('section').forEach(section => { + const title = section.querySelector('h1,h2,h3,h4,h5'); + if (!title || !LIFE_RE.test(title.textContent || '')) return; + const rows = [...section.querySelectorAll('div.grid')].filter(row => EVENT_RE.test(row.textContent || '')); + if (rows.length <= 3) return; + if (section.dataset.fcbxLifecycleReady !== '1') { + section.dataset.fcbxLifecycleReady = '1'; + section.dataset.fcbxLifecycleCollapsed = '1'; + const 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;'; + btn.addEventListener('click', () => { + section.dataset.fcbxLifecycleCollapsed = section.dataset.fcbxLifecycleCollapsed === '1' ? '0' : '1'; + apply(section); + }); + (section.lastElementChild || section).appendChild(btn); + } + apply(section); + }); + }; + const apply = section => { + const rows = [...section.querySelectorAll('div.grid')].filter(row => EVENT_RE.test(row.textContent || '')); + const collapsed = section.dataset.fcbxLifecycleCollapsed !== '0'; + rows.forEach((row, idx) => { row.style.display = collapsed && idx >= 3 ? 'none' : ''; }); + const btn = section.querySelector('.fcbx-life-toggle'); + if (btn) btn.textContent = collapsed ? `展开全部取件记录(共 ${rows.length} 次)` : '收起取件记录'; + }; + let timer = 0; + const schedule = () => { clearTimeout(timer); timer = setTimeout(markRows, 80); }; + window.addEventListener('hashchange', schedule); + document.addEventListener('DOMContentLoaded', schedule); + new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true }); + schedule(); +})();''' + rewrite(js_path, js) + html = index.read_text(encoding='utf-8') + tag = '' + if 'fcb-detail-collapse.js' not in html: + html = html.replace('', tag + '\n') if '' in html else html + '\n' + tag + '\n' + index.write_text(html, encoding='utf-8') + gz = index.with_suffix(index.suffix + '.gz') + if gz.exists(): + with gzip.open(gz, 'wb', compresslevel=9) as fh: + fh.write(html.encode('utf-8')) + return True + + def main() -> None: changed = [] - for path in ASSETS.glob('FileManageView-*.js'): - if patch_file_manage(path): - changed.append(path.name) - for path in ASSETS.glob('clipboard-*.js'): - if patch_clipboard(path): - changed.append(path.name) + for assets in ASSETS_ROOT.glob('themes/*/assets'): + for path in assets.glob('FileManageView-*.js'): + if patch_file_manage(path): + changed.append(str(path.relative_to(ASSETS_ROOT))) + for path in assets.glob('clipboard-*.js'): + if patch_clipboard(path): + changed.append(str(path.relative_to(ASSETS_ROOT))) + if write_detail_collapse_script(assets.parent): + changed.append(str((assets / 'fcb-detail-collapse.js').relative_to(ASSETS_ROOT))) if not changed: raise SystemExit('admin timeline/clipboard patch markers not found or already patched') print('patched admin assets:', ', '.join(changed))