需求
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.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`