需要
1、前端自定义 html 模板,导出 pdf
2、多个 pdf 导出压缩到一个文件 zip 外面
原理
1、html2Canvas 将 html 模板转为 canvas
2、canvas 转为 img 图片
3、生成的图片增加到 pdf 外面
用到的插件
html2canvasjspdfjszipfile-saver
模板
<template> <div class="table-class"> <div class="name-style">{{ row.meetingName }}会议纪要</div> <table border="0" class="table-style"> <tbody> <tr> <td>会议主题</td> <td colspan="5">{{ row.meetingTheme }}</td> </tr> <tr> <td>会议工夫</td> <td>{{ meetingTime || '-' }}</td> <td>会议地点</td> <td>{{ row.meetingPlace }}</td> <td>会议级别</td> <td>{{ matterLevelObj[row.meetingLevel]||"-" }}</td> </tr> <tr> <td>主持人</td> <td>{{ row.host }}</td> <td>记录人</td> <td>{{ row.recorder }}</td> <td>抄报</td> <td>{{ row.ccleader }}</td> </tr> <tr> <td>审批人</td> <td colspan="5">{{ row.approverName || '-' }}</td> </tr> <tr> <td>参加人</td> <td colspan="5">{{ row.participant }}</td> </tr> <tr> <td>备注</td> <td colspan="5">{{ row.remark }}</td> </tr> <tr> <td colspan="6" class="td-bg">会议内容</td> </tr> <tr> <td colspan="6">{{ row.remark }}</td> </tr> <tr> <td colspan="6" class="td-bg">会议事项</td> </tr> <tr> <td>序号</td> <td>类型</td> <td>工作事项</td> <td>负责人</td> <td>预计实现工夫</td> <td>工作打算</td> </tr> <tr v-for="(item,i) of row.meetMatters" :key="i"> <td>{{ i+1 }}</td> <td>{{ item.type }}</td> <td>{{ item.workItem }}</td> <td>{{ item.principalName }}</td> <td>{{ item.planEndtime }}</td> <td>{{ item.workPlan }}</td> </tr> </tbody> </table> </div></template><script> import mixin from '../mixin'; export default { name: 'ExportPdf', mixins: [mixin], props: { row: { type: Object, default: () => {}, }, }, computed: { meetingTime() { const start = this.row?.meetingStartTime; const end = this.row?.meetingEndTime; if (start && end) { return start.substr(0, start.length - 3) + '~' + end.substr(0, end.length - 3); } else { return ''; } }, }, };</script><style lang="scss" scoped> .table-class { background-color: #fff; width: 1000px; margin: auto; padding: 40px; box-sizing: border-box; .name-style { text-align: center; font-size: 20px; font-weight: bold; margin-bottom: 20px; } } .table-style { border-collapse: collapse; width: 100%; text-align: center; td, th { padding: 10px; font-size: 15px; border: 1px solid black; } .td-bg { background: #ccc; } }</style>
异步导出函数
async exportMeeting (type) { // type:'1' 抉择导出 'all':导出所有 try { this.allLoading = true let selectedData = [] if (type === '1') { const ids = this.multipleSelection.map(el => el.id) selectedData = await this.getMeetingAll(ids) } else if (type === 'all') { selectedData = await this.getMeetingAll() } this.loadingText = '正在拼命导出...' const zip = new JSZip() const promises = [] this.isShowPdf = true for (let i = 0; i < selectedData.length; i++) { const element = selectedData[i] this.data = element // 期待每一个转为pdf const p = await htmlToPdf.getPdf(document.getElementById('pdf'), element.meetingName) promises.push(p) } // 等到所有的promise执行实现顺次压缩到zip中 Promise.all(promises).then(async (pdfs) => { for (let i = 0; i < pdfs.length; i++) { const { PDF, name } = pdfs[i] // 如果只是导出一个pdf,则导出pdf格局 if (pdfs.length === 1) { PDF.save(`${name}-${new Date().getTime()}.pdf`) this.allLoading = false this.loadingText = '正在申请数据' } else { // 否则增加到压缩包外面 await zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')) } } if (pdfs.length > 1) { zip.generateAsync({ type: 'blob' }).then(content => { FileSaver.saveAs(content, '销项治理平台会议纪要.zip') }) } }).finally(() => { this.allLoading = false this.loadingText = '正在申请数据' }) } catch (e) { this.allLoading = false this.loadingText = '正在申请数据' throw new Error(e) } },
获取 pdf 的公共封装函数
// 导出pdfimport html2Canvas from 'html2canvas';import JsPDF from 'jspdf';export default { getPdf: (el, pdfName) => { // 滚轮滑动造成的,次要是html2canvas是依据body进行截图,若内容高度高于body时,就会呈现这样的问题 // 解决方案:(在生成截图前,先把滚动条置顶) window.pageYoffset = 0; document.documentElement.scrollTop = 0; document.body.scrollTop = 0; return new Promise((resolve, reject) => { // 在点击保留图片时,此时要保留的资源较多,造成模块并没有齐全加载结束,就曾经生成了截图。 // 解决方案:(提早) setTimeout(() => { // 这句挺重要 html2Canvas(el, { scale: 4, dpi: 300, useCORS: true, // allowTaint: true }) .then((canvas) => { const contentWidth = canvas.width; const contentHeight = canvas.height; const pageHeight = (contentWidth / 592.28) * 841.89; let leftHeight = contentHeight; // let position = 0; const imgWidth = 595.28; const imgHeight = (592.28 / contentWidth) * contentHeight; const pageData = canvas.toDataURL('image/jpeg', 1.0); const PDF = new JsPDF('', 'pt', 'a4'); if (leftHeight < pageHeight) { // 在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示; PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight); // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight); } else { // 分页 while (leftHeight > 0) { PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight); leftHeight -= pageHeight; position -= 841.89; // 防止增加空白页 if (leftHeight > 0) { PDF.addPage(); } } } resolve({ PDF, name: pdfName }); }) .catch((e) => { reject(e); }); }, 500); }); },};
遇到的问题
zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')); //第二个参数PDF.output('blob')
// 通过给文件名增加一个工夫戳解决zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')); //`${name}-${new Date().getTime()}.pdf`