原理:需要將頁面中table導出一個word文件,在本地做好一個word模板,定義好變量,以這個模板文件為導出依賴,將后台獲取的變量添加進去。
需求:將“倫理審查批件”導出為word
導出word和pdf對實際工作的影響:
上一篇博客實現了前端導出pdf,但是后來發現,當意見內容特別多的時候,pdf分頁會有點問題:
而word會自動處理這種情況:
綜上,如果確定導出文件的高度在一頁內能搞定,那么用pdf會更簡單點,需要多頁的話還是用word更適合使用需求。
導出pdf的原理是 html2canvas 對頁面進行截圖,再用 jspdf 將截圖轉為 pdf,所以這種做法一定會導致多頁的時候出現上述問題,但是現在好像vue導出pdf大多數使用的都是這個方法,以后有時間再研究下其他的方法可以導出好看點的pdf。
操作步驟:
1、下載插件
npm i docxtemplater jszip-utils file-saver jszip@2.6.1
注意:jszip的版本是2.6.1,最新版的可能有問題,或者安裝 pizzip 替代 jszip

3、引入插件和定義導出函數
import docxtemplater from 'docxtemplater' import JSZipUtils from 'jszip-utils' import { saveAs } from 'file-saver' import JSZip from 'jszip'
exportWord: function() { let _this = this // 讀取並獲得模板文件的二進制內容 JSZipUtils.getBinaryContent('approvalNo.docx', function(error, content) { if (error) throw error // 拋出異常 let zip = new JSZip(content) // 創建一個JSZip實例,內容為模板的內容 let doc = new docxtemplater().loadZip(zip) // 創建並加載docxtemplater實例對象 doc.setData({ ..._this.approvalNoOrOpinionNoList }) // 設置模板變量的值 try { doc.render() // 用模板變量的值替換所有模板變量 } catch (error) { let e = { message: error.message, name: error.name, stack: error.stack, properties: error.properties } console.log(JSON.stringify({ error: e })) throw error // 拋出異常 } // 生成一個代表docxtemplater對象的zip文件(不是一個真實的文件,而是在內存中的表示) let out = doc.getZip().generate({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }) // 將目標文件對象保存為目標類型的文件,並命名 saveAs(out, 'aaa.docx') }) }
注意:引入路徑和傳入到模板中的參數
4、定義按鈕點擊導出,實際導出效果:
報錯:
第一種:
Uncaught Error: Error: Can't find end of central directory : is this a zip file ? If it is, see http://stuk.github.io/jszip/documentation/howto/read_zip.html
at XMLHttpRequest.xhr.onreadystatechange
翻譯:
未捕獲的錯誤:錯誤:無法找到結束的中央目錄:這是一個zip文件嗎?如果是,請查看http://stuk.github.io/jszip/documentation/howto/read_zip.html
在XMLHttpRequest.xhr.onreadystatechange
解釋:················意思是說你路徑寫錯了
第二種:
Uncaught Error: InternalError: The filetype for this file could not be identified, is this file corrupted ?
at XMLHttpRequest.xhr.onreadystatechange
翻譯:
無法識別此文件的文件類型,此文件是否已損壞?
在XMLHttpRequest.xhr.onreadystatechange
解釋:················意思是說后綴名doc會有可能出問題,換成docx試試
頁面完整代碼:

<template> <div class="approvalNo-or-opinionNo-list"> <el-button type="primary" size="small" @click="exportWord">點擊下載</el-button> <div id="pdfDom"> <table cellspacing="0"> <caption> 倫理審查批件 </caption> <tr> <td class="key-name">批件號</td> <td colspan="3">{{ approvalNoOrOpinionNoList.approvalNo }}</td> </tr> <tr> <td class="key-name">項目名稱</td> <td colspan="3">{{ approvalNoOrOpinionNoList.projectName }}</td> </tr> <tr> <td class="key-name">項目來源</td> <td colspan="3">{{ approvalNoOrOpinionNoList.sponsorName }}</td> </tr> <tr> <td class="key-name">研究單位</td> <td colspan="3">{{ approvalNoOrOpinionNoList.researchUnit }}</td> </tr> <tr> <td class="key-name">主要研究者</td> <td colspan="3">{{ approvalNoOrOpinionNoList.personName }}</td> </tr> <tr> <td class="key-name">審查類別</td> <td>{{ approvalNoOrOpinionNoList.taskStyle }}</td> <td class="key-name">審查方式</td> <td>{{ approvalNoOrOpinionNoList.taskType }}</td> </tr> <tr> <td class="key-name">審查日期</td> <td>{{ approvalNoOrOpinionNoList.auditDate }}</td> <td class="key-name">審查地點</td> <td>{{ approvalNoOrOpinionNoList.auditAddress }}</td> </tr> <tr> <td class="key-name">審查委員</td> <td colspan="3">{{ approvalNoOrOpinionNoList.auditCommittees }}</td> </tr> <tr> <td class="key-name">批准文件</td> <td colspan="3">見附件</td> </tr> <tr> <td colspan="4" class="options"> {{ approvalNoOrOpinionNoList.auditRemark }} </td> </tr> <tr> <td class="key-name">年度/定期<br />跟蹤審查頻率</td> <td colspan="3">{{ approvalNoOrOpinionNoList.frequency }}</td> </tr> <tr> <td class="key-name">有效期</td> <td colspan="3">{{ approvalNoOrOpinionNoList.effectiveStartDate }} 至 {{ approvalNoOrOpinionNoList.effectiveEndDate }}</td> </tr> <tr> <td class="key-name">聯系人與聯系電話</td> <td colspan="3">{{ approvalNoOrOpinionNoList.sponsorContacts }} {{ approvalNoOrOpinionNoList.sponsorTel }}</td> </tr> <tr> <td class="key-name">倫理委員會</td> <td colspan="3">{{ approvalNoOrOpinionNoList.ethicsCommittee }}</td> </tr> <tr> <td class="key-name">主任簽名</td> <td colspan="3">{{ approvalNoOrOpinionNoList.directorAutograph }}</td> </tr> <tr> <td class="key-name">日期</td> <td colspan="3">{{ approvalNoOrOpinionNoList.todayDate }}</td> </tr> </table> </div> </div> </template> <script> import docxtemplater from 'docxtemplater' import JSZipUtils from 'jszip-utils' import { saveAs } from 'file-saver' import JSZip from 'jszip' export default { data() { return {} }, props: { approvalNoOrOpinionNoList: { type: Object, default: {} } }, methods: { exportWord: function() { let _this = this // 讀取並獲得模板文件的二進制內容 JSZipUtils.getBinaryContent('approvalNo.docx', function(error, content) { if (error) throw error // 拋出異常 let zip = new JSZip(content) // 創建一個JSZip實例,內容為模板的內容 let doc = new docxtemplater().loadZip(zip) // 創建並加載docxtemplater實例對象 doc.setData({ ..._this.approvalNoOrOpinionNoList }) // 設置模板變量的值 try { doc.render() // 用模板變量的值替換所有模板變量 } catch (error) { let e = { message: error.message, name: error.name, stack: error.stack, properties: error.properties } console.log(JSON.stringify({ error: e })) throw error // 拋出異常 } // 生成一個代表docxtemplater對象的zip文件(不是一個真實的文件,而是在內存中的表示) let out = doc.getZip().generate({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }) // 將目標文件對象保存為目標類型的文件,並命名 saveAs(out, 'aaa.docx') }) } } } </script> <style lang="scss"> .approvalNo-or-opinionNo-list { height: 350px; overflow-y: auto; padding-bottom: 20px; border-bottom: 1px solid #ccc; > #pdfDom { table { text-align: center; border-bottom: 1px solid #ccc; width: 93%; margin: 0 auto; font-family: '楷體', '楷體_GB2312'; caption { font-size: 16px; text-align: center; line-height: 46px; color: #333; font-weight: bold; } td { width: 25%; height: 32px; color: #666; border-left: 1px solid #ccc; border-top: 1px solid #ccc; padding: 0 6px; } td:last-child { border-right: 1px solid #ccc; } .key-name { color: #333; font-weight: 600; } .options { padding: 10px; text-align: justify; text-indent: 2em; } } } } </style>
前端vue以數據流方式導出word----借助 jquery