原理:需要将页面中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; 93%; margin: 0 auto; font-family: '楷体', '楷体_GB2312'; caption { font-size: 16px; text-align: center; line-height: 46px; color: #333; font-weight: bold; } td { 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>