import { chromium } from "playwright"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const OUT = path.resolve(__dirname, "../../shots"); const BASE = process.env.BASE_URL || "http://localhost:5174"; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); fs.rmSync(OUT, { recursive: true, force: true }); fs.mkdirSync(OUT, { recursive: true }); const manifest = []; let idx = 0; async function expand(page) { await page.evaluate(() => { document.querySelectorAll(".h-screen").forEach((el) => { el.style.height = "auto"; el.style.minHeight = "100vh"; el.style.overflow = "visible"; }); document.querySelectorAll(".overflow-hidden").forEach((el) => (el.style.overflow = "visible")); document.querySelectorAll("main").forEach((el) => { el.style.overflow = "visible"; el.style.height = "auto"; }); }); } async function shot(page, section, title, { full = true, wait = 500, expandLayout = true } = {}) { idx += 1; const file = `${String(idx).padStart(2, "0")}.png`; if (expandLayout) await expand(page).catch(() => {}); await sleep(wait); await page.screenshot({ path: path.join(OUT, file), fullPage: full }); manifest.push({ file, section, title }); console.log(`[${idx}] ${section} / ${title} -> ${file}`); } async function goto(page, url) { await page.goto(BASE + url, { waitUntil: "networkidle" }).catch(async () => { await page.goto(BASE + url, { waitUntil: "domcontentloaded" }); }); await sleep(450); } async function clickIf(page, locator, wait = 300) { const n = await locator.count(); if (n > 0) { await locator.first().click().catch(() => {}); await sleep(wait); } } async function switchRole(page, name) { await page.locator("header button:has(svg.lucide-refresh-cw)").first().click(); await sleep(250); await page.getByRole("menuitem", { name }).first().click(); await sleep(700); } async function run() { const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1.3 }); page.setDefaultTimeout(15000); const A = "一、门户与登录"; await goto(page, "/portal"); await shot(page, A, "首页(门户)", { wait: 700 }); await clickIf(page, page.getByRole("link", { name: "交易信息" })); await shot(page, A, "门户导航 · 交易信息页"); await goto(page, "/portal/policies"); await shot(page, A, "门户导航 · 政策法规页"); await goto(page, "/portal/guides"); await shot(page, A, "门户导航 · 操作指南页"); await goto(page, "/portal/faq"); await shot(page, A, "门户导航 · 常见问题页"); await goto(page, "/portal/procurement-platform"); await shot(page, A, "门户导航 · 采购平台页"); await goto(page, "/"); await shot(page, A, "登录页(含可拖拽小滨)", { full: false, expandLayout: false }); const B = "二、角色工作台"; await goto(page, "/app"); await shot(page, B, "招标专员工作台", { wait: 800 }); for (const rn of ["采购需求人员", "评标专家", "评标委员会工作领导小组", "供应商", "系统管理员", "合规 / 审计"]) { await switchRole(page, rn); await shot(page, B, `${rn}工作台`, { wait: 700 }); } await switchRole(page, "招标专员"); const C = "三、核心业务页面"; for (const [url, title] of [ ["/app/delegation", "发起招标委托"], ["/app/projects", "项目管理列表"], ["/app/projects/P2026001", "项目详情"], ["/app/projects/P2026001/shortlist", "供应商长短名单"], ["/app/projects/P2026001/sourcing", "AI智慧寻源"], ["/app/projects/P2026001/opening", "开标大厅"], ["/app/projects/P2026001/evaluation", "在线评标室"], ["/app/deposits", "保证金管理"], ["/app/suppliers", "供应商管理"], ["/app/experts", "专家管理"], ["/app/reports", "报表中心"], ["/app/exceptions", "异常决策"], ["/app/expert-tasks", "评标任务"], ["/app/bidding", "投标中心"], ["/app/audit-logs", "审计日志"], ["/app/collusion", "围串标分析"], ["/app/evidence", "存证验证"], ["/app/admin/users", "用户与权限管理"], ["/app/admin/keys", "密钥管理"], ["/app/settings", "系统设置"], ]) { await goto(page, url); await shot(page, C, title, { wait: 600 }); } const D = "四、弹窗与抽屉"; await goto(page, "/app/suppliers"); await clickIf(page, page.locator("tbody tr"), 700); await shot(page, D, "供应商详情抽屉", { full: false, expandLayout: false }); await goto(page, "/app/expert-tasks"); await clickIf(page, page.getByRole("button", { name: /确认邀请/ }), 500); await clickIf(page, page.getByRole("button", { name: /签署评标承诺书/ }), 700); await shot(page, D, "评标纪律承诺书弹窗", { full: false, expandLayout: false }); await goto(page, "/app/admin/keys"); await clickIf(page, page.getByRole("button", { name: /手动触发密钥漂移/ }), 700); await shot(page, D, "密钥漂移二次授权弹窗", { full: false, expandLayout: false }); fs.writeFileSync(path.join(OUT, "manifest.json"), JSON.stringify(manifest, null, 2)); await browser.close(); console.log(`完成:共 ${manifest.length} 张截图`); } run().catch((e) => { console.error(e); fs.writeFileSync(path.join(OUT, "manifest.json"), JSON.stringify(manifest, null, 2)); process.exit(1); });