#!/usr/bin/env python3 """Batch apply 运输计划 toolbar pattern across list pages: 1) Move「已选」from list-toolbar to list-pager-bar (left) 2) Ensure ListToolbarDateShortcuts uses variant=\"select\" """ from __future__ import annotations import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] / "web-admin" / "src" SKIP_PARTS = { "components/bx/template/ListToolbar.vue", # handled separately "components/core/list-toolbar-date-shortcuts/index.vue", } # Dialog / selector toolbars are not top-level menu pages — still move selected if pager exists SELECTED_RE = re.compile( r"[ \t]*(?:]*>\s*" r"已选\s*[\s\S]*?\s*(?:项|条)\s*" r"\s*\n?", re.MULTILINE, ) PAGER_OPEN_RE = re.compile( r'(<(?:div|section)\s+class="[^"]*(?:pagination\s+list-pager-bar|list-pager-bar|list-pager)[^"]*"[^>]*>)', ) LIST_PAGER_SELECTED_RE = re.compile(r'class="[^"]*list-pager-selected') def extract_selected_blocks(text: str) -> tuple[str, list[str]]: blocks = [] def _keep(m: re.Match[str]) -> str: blocks.append(m.group(0).strip()) return "" new_text = SELECTED_RE.sub(_keep, text) return new_text, blocks def selected_to_pager_html(block: str) -> str: # Normalize to list-pager-selected div inner = re.search( r"已选\s*([\s\S]*?)\s*(项|条)", block, ) if not inner: return "" expr = inner.group(1).strip() unit = inner.group(2) return ( f'
\n' f' 已选 {expr} {unit}\n' f"
\n" ) def inject_into_pagers(text: str, selected_htmls: list[str]) -> tuple[str, int]: if not selected_htmls: return text, 0 # Already has pager selected somewhere — just keep removals (caller already removed toolbar blocks) if LIST_PAGER_SELECTED_RE.search(text): return text, len(selected_htmls) idx = 0 injected = 0 def _repl(m: re.Match[str]) -> str: nonlocal idx, injected open_tag = m.group(1) html = selected_htmls[min(idx, len(selected_htmls) - 1)] idx += 1 injected += 1 return open_tag + "\n" + html new_text = PAGER_OPEN_RE.sub(_repl, text, count=len(selected_htmls)) return new_text, injected def ensure_date_select_variant_safe(text: str) -> tuple[str, int]: changed = 0 pattern = re.compile(r"]*)(/?)>", re.DOTALL) def repl(m: re.Match[str]) -> str: nonlocal changed attrs, slash = m.group(1), m.group(2) if 'variant="select"' in attrs or "variant='select'" in attrs: return m.group(0) if re.search(r"variant\s*=", attrs): attrs2 = re.sub(r'''variant\s*=\s*["'][^"']*["']''', 'variant="select"', attrs) changed += 1 return f"" changed += 1 return f'' return pattern.sub(repl, text), changed def process_file(path: Path) -> list[str]: rel = str(path.relative_to(ROOT)).replace("\\", "/") if rel in SKIP_PARTS: return [] text = path.read_text(encoding="utf-8") orig = text notes: list[str] = [] text_wo, blocks = extract_selected_blocks(text) if blocks: selected_htmls = [h for h in (selected_to_pager_html(b) for b in blocks) if h] has_pager = bool( re.search(r"list-pager-bar|class=\"[^\"]*list-pager[^\"]*\"", text) ) already_pager_selected = bool(LIST_PAGER_SELECTED_RE.search(text_wo)) if already_pager_selected: text = text_wo notes.append(f"removed-toolbar-selected×{len(blocks)}") elif has_pager: text2, injected = inject_into_pagers(text_wo, selected_htmls) if injected: text = text2 notes.append(f"moved-selected×{injected}") else: text = orig notes.append("selected-move-FAILED") else: # Tree / non-pager list: append a footer bar once before closing panel section footer = ( '\n \n" ) # Prefer insert after list-body/list-wrap block m = re.search( r'(\s*\n)(\s*)', text_wo, ) if m and selected_htmls: text = text_wo[: m.start(1)] + m.group(1) + footer + m.group(2) + text_wo[m.end() :] notes.append("selected-footer-no-pager") else: text = orig notes.append("selected-skip-no-pager") text, n_var = ensure_date_select_variant_safe(text) if n_var: notes.append(f"date-select×{n_var}") if text != orig: path.write_text(text, encoding="utf-8") return notes def main() -> int: targets = [] for p in ROOT.rglob("*.vue"): rel = str(p.relative_to(ROOT)).replace("\\", "/") if "node_modules" in rel: continue t = p.read_text(encoding="utf-8") if "list-toolbar-selected" in t or "ListToolbarDateShortcuts" in t: targets.append(p) report = [] for p in sorted(targets): notes = process_file(p) if notes: report.append(f"{p.relative_to(ROOT)}: {', '.join(notes)}") print(f"processed candidates: {len(targets)}") print(f"changed: {len(report)}") for line in report: print(line) return 0 if __name__ == "__main__": sys.exit(main())