/** * scripts/shot-v12.mjs —— v12 交付截图:桌面 1440×900 与手机 393×852,各 4 页(工作台 / 池子 / 报告中心 / 阶段)。 * 用法:node scripts/shot-v12.mjs [输出目录](默认 docs/screenshots/v12) * 与验收脚本同一套启动方式(v11b-lib 起本地服务 + 固定 Chromium),截图本身不做任何样式注入。 */ process.env.CHROME_PATH ||= '/opt/hermes/.playwright/chromium_headless_shell-1243/chrome-headless-shell-linux64/chrome-headless-shell'; const { startServer, launch, ROOT } = await import('./v11b-lib.mjs'); const { mkdirSync } = await import('node:fs'); const { join } = await import('node:path'); const dir = process.argv[2] || join(ROOT, 'docs/screenshots/v12'); mkdirSync(dir, { recursive: true }); const srv = await startServer(4210); const b = await launch(); const pages = { 'workbench': ['/', 'view-workbench'], 'pool': ['/pool', 'view-pool'], 'reports': ['/reports', 'view-reports'], 'stages': ['/stages', 'view-stages'] }; const sizes = { desktop: { width: 1440, height: 900, deviceScaleFactor: 1 }, mobile: { width: 393, height: 852, deviceScaleFactor: 2, isMobile: true, hasTouch: true } }; for (const [tag, vp] of Object.entries(sizes)) { const pg = await (await b.newContext({ viewport: { width: vp.width, height: vp.height }, deviceScaleFactor: vp.deviceScaleFactor, isMobile: !!vp.isMobile, hasTouch: !!vp.hasTouch })).newPage(); for (const [name, [path, view]] of Object.entries(pages)) { await pg.goto(new URL(path, srv.base).href, { waitUntil: 'networkidle' }); await pg.waitForFunction((n) => { const a = document.querySelector('kf-app'); const v = a?.shadowRoot?.querySelector(n); return !!v && v.shadowRoot && v.shadowRoot.childElementCount > 0; }, view, { timeout: 15000 }); await pg.waitForTimeout(1200); await pg.mouse.move(1400, 500); await pg.screenshot({ path: `${dir}/${tag}-${name}.png`, fullPage: true, animations: 'disabled' }); console.log(tag, name, 'ok'); } } await b.close(); srv.kill();