""" [一次性诊断工具] 测量 17 个 H5 页面的实际高度,校准 anchor_compare.py 锚点配置。 测量 430×752 视口下各页面展开全部折叠区域后的 scrollHeight,计算需要几屏。 输出全页面截图 + JSON 数据到 export/SYSTEM/REPORTS/h5_page_heights/。 2026-03-09 执行结果已写入 design.md §5.1,后续无需重复运行(除非 H5 原型页面结构变更)。 用法: 1. 先启动 Live Server(VS Code 右键 Open with Live Server) 2. python scripts/ops/_measure_h5_page_heights.py """ import asyncio import json import sys from pathlib import Path from playwright.async_api import async_playwright BASE_URL = "http://127.0.0.1:5500/docs/h5_ui/pages" VIEWPORT_W = 430 VIEWPORT_H = 752 # 与 anchor_compare.py 一致 DPR = 3 # 17 个目标页面 PAGES = [ "board-finance", "board-coach", "board-customer", "task-detail", "task-detail-callback", "task-detail-priority", "task-detail-relationship", "coach-detail", "customer-detail", "performance", "task-list", "my-profile", "customer-service-records", "performance-records", "chat", "chat-history", "notes", ] OUT_DIR = Path(__file__).resolve().parents[2] / "export" / "SYSTEM" / "REPORTS" / "h5_page_heights" HIDE_SCROLLBAR_JS = """ () => { document.documentElement.style.overflow = 'auto'; document.documentElement.style.scrollbarWidth = 'none'; const s = document.createElement('style'); s.textContent = '::-webkit-scrollbar { display: none !important; }'; document.head.appendChild(s); } """ EXPAND_ALL_JS = """ () => { // 点击所有"展开更多"/"查看更多"按钮 const btns = document.querySelectorAll('[onclick*="More"], [onclick*="expand"], [onclick*="toggle"]'); btns.forEach(b => b.click()); // 显示所有 hidden 的展开区域 document.querySelectorAll('[id*="More"], [id*="more"]').forEach(el => { el.classList.remove('hidden'); el.style.display = ''; }); } """ async def main(): OUT_DIR.mkdir(parents=True, exist_ok=True) results = [] async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context( viewport={"width": VIEWPORT_W, "height": VIEWPORT_H}, device_scale_factor=DPR, ) page = await context.new_page() for name in PAGES: url = f"{BASE_URL}/{name}.html" try: await page.goto(url, wait_until="load", timeout=15000) await page.wait_for_timeout(2500) # Tailwind CDN JIT await page.evaluate(HIDE_SCROLLBAR_JS) await page.wait_for_timeout(300) # 展开所有折叠区域 await page.evaluate(EXPAND_ALL_JS) await page.wait_for_timeout(500) # 测量页面高度 heights = await page.evaluate(""" () => ({ scrollHeight: document.documentElement.scrollHeight, bodyScrollHeight: document.body.scrollHeight, clientHeight: document.documentElement.clientHeight, }) """) scroll_h = heights["scrollHeight"] screens = round(scroll_h / VIEWPORT_H, 2) # 全页面截图 out_path = OUT_DIR / f"{name}.png" await page.screenshot(path=str(out_path), full_page=True) file_size = out_path.stat().st_size # 物理像素高度 phys_h = scroll_h * DPR info = { "page": name, "scrollHeight_css": scroll_h, "scrollHeight_phys": phys_h, "viewportHeight": VIEWPORT_H, "screens": screens, "screenshot_size_bytes": file_size, } results.append(info) print(f" {name:35s} {scroll_h:5d}px = {screens:5.2f} 屏 ({file_size:,} bytes)") except Exception as e: print(f" ❌ {name}: {e}", file=sys.stderr) results.append({"page": name, "error": str(e)}) await browser.close() # 输出 JSON 汇总 json_path = OUT_DIR / "page_heights.json" with open(json_path, "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"\n汇总已写入: {json_path}") # 输出 Markdown 表格 print("\n| # | 页面 | CSS高度(px) | 物理高度(px) | 屏数 |") print("|---|------|------------|-------------|------|") for i, r in enumerate(results, 1): if "error" in r: print(f"| {i} | {r['page']} | ❌ | ❌ | {r['error']} |") else: print(f"| {i} | {r['page']} | {r['scrollHeight_css']} | {r['scrollHeight_phys']} | {r['screens']} |") if __name__ == "__main__": asyncio.run(main())