Files
shanxia2.0/scripts/apply-list-toolbar-selected-to-pager.py
T
zlwuandCursor 10c3c5470c chore: sync local PL/CRM/BX and migration work to new Gogs host
Bundle pending backend/web-admin feature updates, data-migration scripts,
and related docs so the V1.5 branch on the Tencent Cloud Gogs matches the
current workspace (excluding local secrets).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 09:19:21 +08:00

183 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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]*(?:<div|<span)\s+class=\"list-toolbar-selected\"[^>]*>\s*"
r"已选\s*<span class=\"sel-num\">[\s\S]*?</span>\s*(?:项|条)\s*"
r"</(?:div|span)>\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*<span class=\"sel-num\">([\s\S]*?)</span>\s*(项|条)",
block,
)
if not inner:
return ""
expr = inner.group(1).strip()
unit = inner.group(2)
return (
f' <div class="list-pager-selected">\n'
f' 已选 <span class="sel-num">{expr}</span> {unit}\n'
f" </div>\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"<ListToolbarDateShortcuts\b([^>]*)(/?)>", 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"<ListToolbarDateShortcuts{attrs2}{slash}>"
changed += 1
return f'<ListToolbarDateShortcuts\n variant="select"{attrs}{slash}>'
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 <div class="pagination list-pager-bar">\n'
+ selected_htmls[0]
+ " </div>\n"
)
# Prefer insert after list-body/list-wrap block
m = re.search(
r'(</div>\s*\n)(\s*</section>)',
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())