需要
1、前端自定义 html 模板,导出 pdf
2、多个 pdf 导出压缩到一个文件 zip 外面
原理
1、html2Canvas 将 html 模板转为 canvas
2、canvas 转为 img 图片
3、生成的图片增加到 pdf 外面
用到的插件
html2canvas
jspdf
jszip
file-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 的公共封装函数
// 导出 pdf
import 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 时,获取 pdf 的内容
zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')); // 第二个参数 PDF.output('blob')
- 增加到 zip 时,同名文件会被笼罩
// 通过给文件名增加一个工夫戳解决
zip.file(`${name}-${new Date().getTime()}.pdf`, PDF.output('blob')); //`${name}-${new Date().getTime()}.pdf`