/** * 自动截图脚本 - 遍历所有角色和页面,生成截图并拼接为横版A4 PDF * 依赖:puppeteer-core (使用本地 Chrome) * 用法:node screenshot.js */ const puppeteer = require('puppeteer-core'); const path = require('path'); const fs = require('fs'); const PROTO_PATH = path.resolve(__dirname, '../../prototype/index.html'); const OUTPUT_DIR = path.resolve(__dirname, '../../prd'); const OUTPUT_PDF = path.join(OUTPUT_DIR, 'nwafu-exam-demoImages-202604.pdf'); const TEMP_DIR = path.join(__dirname, '_temp_screenshots'); // 本地 Chrome 路径 const CHROME_PATH = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; // 角色列表 const ROLES = [ { key: 'newGraduate', label: '研究生新入学' }, { key: 'graduateSenior', label: '研究生在读(研四)' }, { key: 'newUndergraduate', label: '本科生新入学' }, { key: 'undergraduateSenior', label: '本科生在读(大四)' }, { key: 'newStaff', label: '教职工新入职' }, { key: 'existingStaff', label: '教职工存量' } ]; // 页面列表 const PAGES = [ { key: 'workbench', label: '工作台' }, { key: 'statistics', label: '数据统计' }, { key: 'approval', label: '人员审批' } ]; (async () => { // 确保临时目录存在 if (!fs.existsSync(TEMP_DIR)) fs.mkdirSync(TEMP_DIR, { recursive: true }); const browser = await puppeteer.launch({ headless: 'new', executablePath: CHROME_PATH, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); // 横版 1920x1080 await page.setViewport({ width: 1920, height: 1080 }); const fileUrl = 'file://' + PROTO_PATH; await page.goto(fileUrl, { waitUntil: 'networkidle0', timeout: 30000 }); // 等待 Vue 渲染 await page.waitForSelector('#app .layout', { timeout: 10000 }); const screenshots = []; for (const role of ROLES) { // 切换角色 await page.evaluate((roleKey) => { const app = document.querySelector('#app').__vue__; app.switchRole(roleKey); }, role.key); await sleep(300); // 关闭所有弹窗 await page.evaluate(() => { const app = document.querySelector('#app').__vue__; app.showBindDialog = false; app.showGraduateReport = false; app.showRejectDialog = false; }); await sleep(300); for (const pg of PAGES) { // 切换页面 await page.evaluate((pageKey) => { const app = document.querySelector('#app').__vue__; app.activePage = pageKey; }, pg.key); await sleep(800); // 如果是统计页面,等待图表渲染 if (pg.key === 'statistics') { await page.evaluate(() => { const app = document.querySelector('#app').__vue__; if (app.renderStatsCharts) app.renderStatsCharts(); }); await sleep(1000); } const filename = `${role.key}_${pg.key}.png`; const filepath = path.join(TEMP_DIR, filename); // 隐藏左侧菜单栏,只截取主内容区 await page.evaluate(() => { const sidebar = document.querySelector('.sidebar'); if (sidebar) sidebar.style.display = 'none'; const mainWrapper = document.querySelector('.main-wrapper'); if (mainWrapper) mainWrapper.style.marginLeft = '0'; }); await sleep(200); await page.screenshot({ path: filepath, fullPage: false }); // 恢复侧边栏 await page.evaluate(() => { const sidebar = document.querySelector('.sidebar'); if (sidebar) sidebar.style.display = ''; const mainWrapper = document.querySelector('.main-wrapper'); if (mainWrapper) mainWrapper.style.marginLeft = ''; }); screenshots.push({ filepath, title: `${role.label} - ${pg.label}` }); console.log(` 截图完成: ${role.label} - ${pg.label}`); } } // 生成横版 A4 PDF (297mm x 210mm) console.log('\n正在生成PDF...'); const pdfPage = await browser.newPage(); await pdfPage.setViewport({ width: 1920, height: 1080 }); // 构建 HTML 页面,每张截图占一页 const imagesHtml = screenshots.map(s => { const imgData = fs.readFileSync(s.filepath); const base64 = imgData.toString('base64'); return `
${s.title}
`; }).join(''); const html = ` ${imagesHtml} `; await pdfPage.setContent(html, { waitUntil: 'networkidle0' }); await pdfPage.pdf({ path: OUTPUT_PDF, landscape: true, format: 'A4', printBackground: true, margin: { top: '0', bottom: '0', left: '0', right: '0' } }); console.log(`PDF已生成: ${OUTPUT_PDF}`); // 清理临时文件 for (const s of screenshots) { fs.unlinkSync(s.filepath); } fs.rmdirSync(TEMP_DIR); console.log('临时文件已清理'); await browser.close(); })(); function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }