| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- /**
- * 自动截图脚本 - 遍历所有角色和页面,生成截图并拼接为横版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 `
- <div class="page">
- <div class="page-title">${s.title}</div>
- <img src="data:image/png;base64,${base64}" />
- </div>
- `;
- }).join('');
- const html = `<!DOCTYPE html>
- <html>
- <head>
- <style>
- * { margin: 0; padding: 0; box-sizing: border-box; }
- body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; }
- .page {
- width: 297mm;
- height: 210mm;
- padding: 8mm;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- page-break-after: always;
- overflow: hidden;
- }
- .page:last-child { page-break-after: auto; }
- .page-title {
- font-size: 14px;
- font-weight: 600;
- color: #333;
- margin-bottom: 6mm;
- text-align: center;
- }
- .page img {
- max-width: 100%;
- max-height: 185mm;
- object-fit: contain;
- border: 1px solid #e0e0e0;
- border-radius: 4px;
- }
- </style>
- </head>
- <body>${imagesHtml}</body>
- </html>`;
- 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));
- }
|