#!/usr/bin/env node /** * 神射手前端全站截图脚本 * 使用中文文件名:{序号}-{页面中文名}-视口.png / {序号}-{页面中文名}-长图.png * 使用前请先启动:pnpm dev(端口 3117) * 运行:node scripts/screenshot-pages.mjs 或 pnpm exec node scripts/screenshot-pages.mjs */ import { chromium } from "playwright"; import { existsSync, mkdirSync } from "fs"; import { join, dirname } from "path"; import { fileURLToPath } from "url"; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = join(__dirname, ".."); const BASE_URL = process.env.BASE_URL || "http://localhost:3117"; const OUT_DIR = process.env.OUT_DIR || join(ROOT, "开发文档", "4、前端", "截图"); const VIEWPORT = { width: 1280, height: 800 }; const WAIT_MS = 2500; const NAV_TIMEOUT = 15000; // 路由与中文名称(序号从 06 起,01-05 已存在) const ROUTES = [ { path: "/", name: "首页" }, { path: "/ai-analysis", name: "AI分析" }, { path: "/data-market", name: "数据市场" }, { path: "/tag-portrait", name: "标签画像" }, { path: "/value-model", name: "估值模型" }, { path: "/settings", name: "设置" }, { path: "/documentation", name: "文档" }, { path: "/login", name: "登录" }, { path: "/traffic-pool", name: "流量池" }, { path: "/user-portrait", name: "用户画像" }, { path: "/user-portrait/tags", name: "用户画像-标签" }, { path: "/user-portrait/1", name: "用户画像详情" }, { path: "/tag-portrait/tags", name: "标签画像-标签" }, { path: "/tag-portrait/portrait", name: "标签画像-画像" }, { path: "/tag-portrait/crowd", name: "标签画像-人群" }, { path: "/data-asset", name: "数据资产" }, { path: "/data-asset/packages", name: "数据资产-包" }, { path: "/data-asset/api-market", name: "数据资产-API市场" }, { path: "/data-market/packages", name: "数据市场-包" }, { path: "/data-market/api", name: "数据市场-API" }, { path: "/data-market/open-api", name: "数据市场-开放API" }, { path: "/value-model/models", name: "模型列表" }, { path: "/value-model/assessment", name: "价值评估" }, { path: "/value-model/reports", name: "估值报表" }, { path: "/value-assessment", name: "价值评估页" }, { path: "/user-valuation", name: "用户估值" }, { path: "/user-value", name: "用户价值" }, { path: "/data-integration", name: "数据集成" }, { path: "/data-ingestion", name: "数据接入" }, { path: "/data-ingestion/sources", name: "数据接入-数据源" }, { path: "/data-ingestion/tasks", name: "数据接入-任务" }, { path: "/data-ingestion/lineage", name: "数据接入-血缘" }, { path: "/data-ingestion/cleaning", name: "数据接入-清洗" }, { path: "/data-ingestion/ai-engine", name: "数据接入-AI引擎" }, { path: "/data-governance", name: "数据治理" }, { path: "/data-governance/tasks", name: "数据治理-任务" }, { path: "/data-governance/quality", name: "数据治理-质量" }, { path: "/data-governance/sources", name: "数据治理-数据源" }, { path: "/data-governance/cleaning", name: "数据治理-清洗" }, { path: "/platform/dashboard", name: "平台-仪表盘" }, { path: "/platform/data-management", name: "平台-数据管理" }, { path: "/platform/user-portrait", name: "平台-用户画像" }, { path: "/platform/value-assessment", name: "平台-价值评估" }, { path: "/platform/ai-assistant", name: "平台-AI助手" }, { path: "/workspace/auto-group", name: "工作台-自动分组" }, { path: "/workspace/moments-sync", name: "工作台-朋友圈同步" }, { path: "/workspace/moments-sync/new", name: "工作台-朋友圈同步-新建" }, { path: "/workspace/pricing", name: "工作台-定价" }, { path: "/workspace/pricing/new", name: "工作台-定价-新建" }, { path: "/content", name: "内容" }, { path: "/content/new", name: "内容-新建" }, { path: "/scenarios", name: "场景" }, { path: "/scenarios/phone", name: "场景-手机" }, { path: "/scenarios/api", name: "场景-API" }, { path: "/system/health", name: "系统-健康" }, { path: "/system/metrics", name: "系统-指标" }, { path: "/system/alerts", name: "系统-告警" }, { path: "/system/logs", name: "系统-日志" }, { path: "/monitoring", name: "监控" }, { path: "/monitoring/health", name: "监控-健康" }, { path: "/monitoring/business", name: "监控-业务" }, { path: "/monitoring/alerts", name: "监控-告警" }, { path: "/ai-assistant", name: "AI助手" }, { path: "/ai-agent", name: "AI智能体" }, { path: "/ai-agent/chat", name: "AI智能体-对话" }, { path: "/ai-agent/smart-tag", name: "AI智能体-智能打标" }, { path: "/ai-agent/nlq", name: "AI智能体-NLQ" }, { path: "/ai-agent/report", name: "AI智能体-报告" }, { path: "/ai-insight", name: "AI洞察" }, { path: "/wechat-accounts", name: "企微账号" }, { path: "/data-output/subscription", name: "数据输出-订阅" }, { path: "/data-output/packages", name: "数据输出-包" }, { path: "/data-output/api-market", name: "数据输出-API市场" }, { path: "/overview/dashboard", name: "概览-仪表盘" }, { path: "/overview/search", name: "概览-搜索" }, { path: "/overview/monitoring", name: "概览-监控" }, { path: "/intelligent-search", name: "智能搜索" }, { path: "/database-structure", name: "数据库结构" }, { path: "/data-dictionary", name: "数据字典" }, { path: "/data-platform", name: "数据平台" }, { path: "/data-middle-platform", name: "数据中台" }, { path: "/api-interface", name: "API接口" }, { path: "/user-discovery", name: "用户发现" }, { path: "/user-profile", name: "用户资料" }, { path: "/rfm", name: "RFM" }, { path: "/group-sync", name: "群同步" }, { path: "/conversion", name: "转化" }, { path: "/devices", name: "设备" }, ]; function safeFilename(name) { return name.replace(/[/\\?*:|"]/g, "-").trim(); } async function main() { if (!existsSync(OUT_DIR)) mkdirSync(OUT_DIR, { recursive: true }); console.log("输出目录:", OUT_DIR); console.log("基础URL:", BASE_URL); console.log("共", ROUTES.length, "个页面\n"); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: VIEWPORT, ignoreHTTPSErrors: true, }); const page = await context.newPage(); page.setDefaultNavigationTimeout(NAV_TIMEOUT); let ok = 0; let fail = 0; for (let i = 0; i < ROUTES.length; i++) { const { path, name } = ROUTES[i]; const num = String(i + 1).padStart(2, "0"); const safeName = safeFilename(name); const url = BASE_URL + path; const viewportPath = join(OUT_DIR, `${num}-${safeName}-视口.png`); const fullPath = join(OUT_DIR, `${num}-${safeName}-长图.png`); try { await page.goto(url, { waitUntil: "domcontentloaded", timeout: NAV_TIMEOUT }); await page.waitForTimeout(WAIT_MS); await page.screenshot({ path: viewportPath, type: "png" }); await page.screenshot({ path: fullPath, type: "png", fullPage: true }); console.log(`[${num}] ${name} 视口+长图 OK`); ok++; } catch (e) { console.error(`[${num}] ${name} 失败:`, e.message); try { await page.screenshot({ path: fullPath, type: "png", fullPage: true }).catch(() => {}); console.log(`[${num}] ${name} 已保存长图`); } catch (_) {} fail++; } } await browser.close(); console.log("\n完成. 成功:", ok, "失败:", fail); } main().catch((e) => { console.error(e); process.exit(1); });