官网地址: Puppeteer
nodejs 中的代码实现:
import express from 'express';
import puppeteer from 'puppeteer';
const router = express.Router();
router.get('/out/printPDF', async (req, res) => {
try{
const browser = await puppeteer.launch({
// 是否开启无头浏览器
// headless 设置为 false 时会自动打开浏览器,可以方便调试
headless: true
});
const page = await browser.newPage();
// 设置浏览器宽高
await page.setViewport({
width: 1280,
height: 1080,
})
await page.goto('这里放需要转为 pdf 的 html 页面URL', { waitUntil: ['load', 'networkidle0'] });
// 如果页面中需要加载 sessionStore 或 localStore 中的数据,可以先实现下列代码向 store 中存入数据再刷新页面
// 向 localStore 中添加数据
await page.evaluate(() => {
// 如果有需要可以先清空 store 数据
// localStorage.clear()
localStorage.setItem('REMEMBER_USER_ID', '123456879')
})
// 重新刷新页面以加载 store 中的数据
await page.reload({ waitUntil: ['load', 'networkidle0'] })
// 下列代码用于等待某个特定的元素渲染完毕,可以确保某个元素加载完成后再打印页面
// await page.waitForSelector('.userID')
// 将页面转为 pdf
const pdfBuffer = await page.pdf({
path: '给 PDF 文件命名.pdf',
format: 'A4', // pdf 纸张模式 A4、A5 等等
printBackground: true, // 是否打印背景颜色
// 如果这个属性为 true, 就意味着 puppeteer 在转换 pdf 时优先使用通过 @page 设置的样式。
preferCSSPageSize: true
});
// 关闭浏览器
await browser.close();
res.send(pdfBuffer)
} catch(e) {
res.send(`导出失败: ${e}`)
}
});
export default router;
前端测试页面:
如果需要某一部分内容(例如 表格)一直保持在同一页不被切割隔开,可以通过设置该内容的css 为 break-inside: avoid; 来实现,如下代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test-general-pdf</title>
<style>
.container {
width: 100vw;
background: #000;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
color: #FFF;
box-sizing: border-box;
}
.header {
width: 100%;
height: 10rem;
}
.context {
width: 100%;
flex: 1;
}
.footer {
width: 100%;
height: 10rem;
}
@media screen and (max-width: 1280px) {
.container {
background-color: red !important;
}
}
/* 转化为 pdf 的时候会优先使用这里设置的样式 */
@media print {
.container {
background-color: blue !important;
}
.not-break {
/* 避免内容被切割开 */
break-inside: avoid;
}
}
</style>
</head>
<body>
<div class="container">
<header class="header">
<h1>123456</h1>
</header>
<div class="context">
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<p>文字文字文字文字</p>
<div class="not-break">
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
<p>这里不分页</p>
</div>
</div>
<div class="footer">
<p>底部</p>
</div>
</div>
</body>
</html>