""" [一次性验证工具] 用 Playwright 实际模拟 600px 固定步长滚动, 记录每个页面实际能截几屏(scrollTop 被 clamp 后的真实值)。 用法: 1. 先启动 Live Server(VS Code 右键 Open with Live Server,端口 5500) 2. python scripts/ops/_verify_step_counts.py 输出:每页的 scrollHeight、maxScrollTop、实际步数、scrollTop 序列。 """ import asyncio import json 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 DPR = 1.5 STEP = 600 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", ] # 维度数映射 DIMS = {"board-coach": 4, "board-customer": 8} HIDE_FLOAT_JS = """ () => { const nav = document.getElementById('bottomNav'); if (nav) nav.style.display = 'none'; document.querySelectorAll('.ai-float-btn-container').forEach(el => el.style.display = 'none'); document.documentElement.style.scrollbarWidth = 'none'; const s = document.createElement('style'); s.textContent = '::-webkit-scrollbar { display: none !important; }'; document.head.appendChild(s); } """ EXPAND_ALL_JS = """ () => { document.querySelectorAll('[onclick*="More"], [onclick*="expand"], [onclick*="toggle"]').forEach(b => b.click()); document.querySelectorAll('[id*="More"], [id*="more"]').forEach(el => { el.classList.remove('hidden'); el.style.display = ''; }); } """ async def main(): 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(2000) await page.evaluate(HIDE_FLOAT_JS) await page.evaluate(EXPAND_ALL_JS) await page.wait_for_timeout(500) # 获取页面高度 scroll_height = await page.evaluate("() => document.documentElement.scrollHeight") max_scroll = scroll_height - VIEWPORT_H # 模拟固定步长滚动,记录实际 scrollTop actual_steps = [] target = 0 while True: # 滚动到目标位置 await page.evaluate(f"window.scrollTo(0, {target})") await page.wait_for_timeout(200) # 读取实际 scrollTop actual = await page.evaluate( "() => window.pageYOffset || document.documentElement.scrollTop" ) actual_steps.append({"target": target, "actual": round(actual)}) # 如果实际值已经到达 maxScroll 或者目标已超过 maxScroll,结束 if actual >= max_scroll - 1 or target >= max_scroll: break target += STEP dims = DIMS.get(name, 1) units = len(actual_steps) * dims info = { "page": name, "scrollHeight": scroll_height, "maxScroll": round(max_scroll), "steps": len(actual_steps), "dims": dims, "units": units, "sequence": actual_steps, } results.append(info) seq_str = ", ".join(str(s["actual"]) for s in actual_steps) print(f" {name:35s} H={scroll_height:5d} max={max_scroll:5.0f}" f" steps={len(actual_steps):2d} dims={dims} units={units:3d}" f" seq=[{seq_str}]") except Exception as e: print(f" ❌ {name}: {e}") results.append({"page": name, "error": str(e)}) await browser.close() # 汇总 total = sum(r.get("units", 0) for r in results) print(f"\n总对照处理单元数: {total}") # 输出 JSON out_path = Path(__file__).resolve().parents[2] / "export" / "SYSTEM" / "REPORTS" / "h5_page_heights" / "step_counts_verified.json" out_path.parent.mkdir(parents=True, exist_ok=True) with open(out_path, "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"详细数据已写入: {out_path}") # Markdown 表格 print(f"\n| 页面 | scrollHeight | maxScroll | 步数 | 维度 | 单元 |") print(f"|------|-------------|-----------|------|------|------|") for r in results: if "error" in r: print(f"| {r['page']} | ❌ | | | | |") else: print(f"| {r['page']} | {r['scrollHeight']} | {r['maxScroll']} | {r['steps']} | {r['dims']} | {r['units']} |") print(f"| **合计** | | | | | **{total}** |") if __name__ == "__main__": asyncio.run(main())