Files
shanxia2.0/web-admin/scripts/apply-list-toolbar-refresh.py
2026-06-30 23:44:22 +08:00

187 lines
5.6 KiB
Python

#!/usr/bin/env python3
"""Add ListToolbarEndActions (refresh-only) to list pages missing it."""
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] / "src" / "views"
REFRESH_HANDLERS = [
"fetchData",
"loadData",
"getList",
"handleQuery",
"handleSearch",
"loadList",
"refresh",
"loadWfDef",
"refreshPending",
]
TOOLBAR_SNIPPET = """
<ListToolbarEndActions :loading="loading" @refresh="{handler}" />
"""
IMPORT_COMPONENT = "import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'\n"
TARGETS = [
"system/role/index.vue",
"system/post/index.vue",
"system/tenant/index.vue",
"system/site/index.vue",
"system/dept/index.vue",
"system/menu/index.vue",
"system/operlog/index.vue",
"system/leave/index.vue",
"system/dict/index.vue",
"system/workflow/process/index.vue",
"system/workflow/task-pending/index.vue",
"system/workflow/task-completed/index.vue",
"system/workflow/my-applications/index.vue",
"crm/product-category/index.vue",
"crm/contract/approval/index.vue",
"crm/contract/template/index.vue",
"crm/contract/change/index.vue",
"crm/customer/pool/RuleIndex.vue",
"pl/vehicle-maintenance-apply/index.vue",
"pl/vehicle-maintenance-process/index.vue",
"pl/vehicle-recharge-apply/index.vue",
"pl/vehicle-recharge-record/index.vue",
"pl/vehicle-urea-apply/index.vue",
"pl/vehicle-urea-disburse/index.vue",
"pl/inventory/document-reminder.vue",
"pl/inventory/ledger.vue",
"bx/expense-item/index.vue",
"bx/advance/apply/index.vue",
"bx/reimburse/apply/index.vue",
"bx/view/todo/index.vue",
"pl/vehicle/index.vue",
"pl/self-vehicle/index.vue",
"pl/plan/index.vue",
"pl/plan-archive/index.vue",
"pl/plan-cert/index.vue",
"demo/product/index.vue",
"bx/advance/approve/index.vue",
"bx/advance/issue/index.vue",
"bx/reimburse/approve/index.vue",
"bx/payment/index.vue",
"bx/workflow/advance/index.vue",
"bx/workflow/reimburse/index.vue",
"pl/freight/components/UnsettledPlanPanel.vue",
"pl/freight/components/SettlementBillPanel.vue",
"pl/freight/components/DisbursePanel.vue",
"pl/freight/components/ApprovalPendingPanel.vue",
"pl/freight/components/ApprovalDonePanel.vue",
"pl/freight/components/SettledPanel.vue",
]
def detect_refresh_handler(content: str) -> str | None:
for name in REFRESH_HANDLERS:
if re.search(rf"\b(?:async\s+)?function\s+{name}\s*\(", content):
return name
if re.search(rf"\bconst\s+{name}\s*=", content):
return name
return None
def has_loading_ref(content: str) -> bool:
if re.search(r"\bconst\s+loading\s*=\s*ref\(", content):
return True
if re.search(r"\{[^}]*\bloading\b[^}]*\}\s*=", content):
return True
return False
def find_toolbar_insert_point(content: str) -> re.Match | None:
"""Find end of first list-toolbar-row block before list-body."""
row_start = re.search(r'<div class="list-toolbar-row[^"]*"', content)
if not row_start:
return None
# From row start, find closing </div></div> before list-body
rest = content[row_start.start() :]
pattern = re.compile(
r"(<div class=\"list-toolbar-row[^\"]*\"[\s\S]*?)"
r"(\s*</div>\s*</div>\s*)"
r"(?:<!--[\s\S]*?-->\s*)?"
r"(<div class=\"list-body)",
re.MULTILINE,
)
return pattern.search(rest)
def insert_component_import(content: str) -> str | None:
"""Append ListToolbarEndActions import after existing import block."""
if IMPORT_COMPONENT in content:
return content
script_match = re.search(r'(<script setup lang="ts"[^>]*>\n)', content)
if not script_match:
return None
script_start = script_match.end()
import_stmt = re.compile(
r"^import[\s\S]*?from\s+['\"][^'\"]+['\"]\s*;?\s*$",
re.MULTILINE,
)
insert_at = script_start
for match in import_stmt.finditer(content, script_start):
insert_at = match.end()
if content[insert_at : insert_at + 1] == "\n":
insert_at += 1
return content[:insert_at] + IMPORT_COMPONENT + content[insert_at:]
def apply_file(path: Path) -> str:
content = path.read_text(encoding="utf-8")
if "ListToolbarEndActions" in content:
return "skip"
if "list-toolbar-row" not in content:
return "no-toolbar"
if not has_loading_ref(content):
return "no-loading"
handler = detect_refresh_handler(content)
if not handler:
return "no-handler"
match = find_toolbar_insert_point(content)
if not match:
return "no-match"
row_start = re.search(r'<div class="list-toolbar-row[^"]*"', content)
assert row_start
abs_start = row_start.start() + match.start(1)
abs_end = row_start.start() + match.end(1)
snippet = TOOLBAR_SNIPPET.format(handler=handler)
content = content[:abs_end] + snippet + content[abs_end:]
updated = insert_component_import(content)
if updated is None:
return "no-script"
content = updated
path.write_text(content, encoding="utf-8")
return "ok"
def main() -> None:
results: dict[str, list[str]] = {}
for rel in TARGETS:
path = ROOT / rel
if not path.exists():
results.setdefault("missing", []).append(rel)
continue
status = apply_file(path)
results.setdefault(status, []).append(rel)
for status, files in sorted(results.items()):
print(f"[{status}] {len(files)}")
for f in files:
print(f" - {f}")
if __name__ == "__main__":
main()