HEL-342: 全站切换霜曜日间与黑金夜间主题
按定稿把运行时 token 换成霜曜/黑金双主题,登录与壳层接入官方 Logo 与主题记忆,不改业务接口与金额计算。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
2d0ae4d7d1
commit
b905cba52c
@@ -0,0 +1,126 @@
|
||||
"""HEL-342: static visual shots + computed-style probe (no business data writes)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from functools import partial
|
||||
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
WEB = ROOT / "web"
|
||||
OUT = ROOT.parent / "hel342-shots"
|
||||
|
||||
PROBES = [
|
||||
(".sidebar", ["width", "backgroundColor", "backdropFilter"]),
|
||||
(".brand-logo-day", ["width", "height"]),
|
||||
(".btn-primary", ["backgroundColor", "height", "borderRadius", "color"]),
|
||||
(".stat-card.gold .stat-value, .stat-card .stat-value", ["color", "fontSize", "fontVariantNumeric"]),
|
||||
(".page-head h1", ["fontSize", "fontWeight"]),
|
||||
(".ds-table th", ["fontSize", "color"]),
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
handler = partial(SimpleHTTPRequestHandler, directory=str(WEB))
|
||||
httpd = ThreadingHTTPServer(("127.0.0.1", 0), handler)
|
||||
Thread(target=httpd.serve_forever, daemon=True).start()
|
||||
base = f"http://127.0.0.1:{httpd.server_address[1]}"
|
||||
report = []
|
||||
|
||||
pages = [
|
||||
("index", "index.html", None, None),
|
||||
("login-admin", "login-admin.html", None, None),
|
||||
("login-company", "login-company.html", None, None),
|
||||
("admin-dashboard", "admin.html", "dashboard", None),
|
||||
("admin-flows", "admin.html", "flows", None),
|
||||
("admin-settings", "admin.html", "settings", None),
|
||||
("admin-audit", "admin.html", "audit", None),
|
||||
("admin-reminders", "admin.html", "reminders", None),
|
||||
("admin-period-audit", "admin.html", "period-audit", None),
|
||||
("admin-companies", "admin.html", "companies", None),
|
||||
("admin-pair", "admin.html", "pair", None),
|
||||
("company-workspace", "company.html", "workspace", None),
|
||||
("company-transfers", "company.html", "transfers", None),
|
||||
("company-reconcile", "company.html", "reconcile", None),
|
||||
("company-upload", "company.html", "upload", None),
|
||||
]
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True, args=["--no-sandbox"])
|
||||
for theme in ("day", "night"):
|
||||
for width, height, tag in ((1440, 900, "1440"), (820, 900, "820"), (390, 844, "390")):
|
||||
if tag != "1440" and theme == "night" and width != 390:
|
||||
continue
|
||||
page = browser.new_page(viewport={"width": width, "height": height})
|
||||
page.add_init_script(
|
||||
f"() => {{ try {{ localStorage.setItem('jinniu-theme', '{theme}'); }} catch (e) {{}} }}"
|
||||
)
|
||||
for name, html, view, _ in pages:
|
||||
if tag != "1440" and name not in ("login-admin", "admin-flows", "company-transfers", "admin-dashboard"):
|
||||
continue
|
||||
page.route("**/app.js**", lambda route: route.abort())
|
||||
page.goto(f"{base}/{html}", wait_until="domcontentloaded")
|
||||
page.evaluate(
|
||||
"""(args) => {
|
||||
document.documentElement.classList.add('v-fusion');
|
||||
document.documentElement.setAttribute('data-theme', args.theme);
|
||||
if (args.view) {
|
||||
document.querySelectorAll('.app-view').forEach((el) => {
|
||||
el.classList.toggle('is-active', el.dataset.page === args.view);
|
||||
});
|
||||
}
|
||||
}""",
|
||||
{"theme": theme, "view": view},
|
||||
)
|
||||
page.wait_for_timeout(80)
|
||||
shot = OUT / f"{name}-{theme}-{tag}.png"
|
||||
page.screenshot(path=str(shot), full_page=False)
|
||||
if tag == "1440" and name in ("login-admin", "admin-dashboard", "admin-flows"):
|
||||
probe = page.evaluate(
|
||||
"""(sels) => sels.map(([sel, props]) => {
|
||||
const el = document.querySelector(sel);
|
||||
if (!el) return { sel, missing: true };
|
||||
const cs = getComputedStyle(el);
|
||||
const out = { sel };
|
||||
props.forEach((p) => { out[p] = cs[p]; });
|
||||
const box = el.getBoundingClientRect();
|
||||
out.box = { w: Math.round(box.width), h: Math.round(box.height) };
|
||||
return out;
|
||||
})""",
|
||||
PROBES,
|
||||
)
|
||||
overflow = page.evaluate(
|
||||
"() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1"
|
||||
)
|
||||
report.append({"page": name, "theme": theme, "overflow": overflow, "probe": probe})
|
||||
page.close()
|
||||
|
||||
# 390 drawer
|
||||
page = browser.new_page(viewport={"width": 390, "height": 844})
|
||||
page.add_init_script("() => { try { localStorage.setItem('jinniu-theme', 'day'); } catch (e) {} }")
|
||||
page.route("**/app.js**", lambda route: route.abort())
|
||||
page.goto(f"{base}/company.html", wait_until="domcontentloaded")
|
||||
page.evaluate(
|
||||
"""() => {
|
||||
document.documentElement.classList.add('v-fusion');
|
||||
document.querySelectorAll('.app-view').forEach((el) => {
|
||||
el.classList.toggle('is-active', el.dataset.page === 'transfers');
|
||||
});
|
||||
const d = document.getElementById('transferEvidenceDrawer');
|
||||
if (d) d.classList.add('is-open');
|
||||
}"""
|
||||
)
|
||||
page.screenshot(path=str(OUT / "company-transfers-drawer-day-390.png"), full_page=False)
|
||||
page.close()
|
||||
browser.close()
|
||||
httpd.shutdown()
|
||||
(OUT / "probe.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(f"wrote {OUT} ({len(list(OUT.glob('*.png')))} png)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user