vue导出word文档
薇森 人气:0一、实现效果
以填写并导出房屋出租审批表为例,首先填写表格相应内容后,点击" 导出 "按钮实现word文档的导出功能,界面如下所示:
最后导出word文档如下所示:
二、所需插件
这里使用npm对以下所需依赖进行安装,并在后面封装的js文件(导出word文档主要实现方法)中引入 。
-- 安装 docxtemplater npm install docxtemplater pizzip --save -- 安装 jszip-utils npm install jszip-utils --save -- 安装 jszip npm install jszip --save -- 安装 FileSaver npm install file-saver --save -- 引入处理图片的插件1 npm install docxtemplater-image-module-free --save -- 引入处理图片的插件2 npm install angular-expressions --save
三、word文档模板
在导出word之前,需要准备一个word模板文件(按自己所需最后导出的样式),放到该vue项目public文件夹下, 房屋出租审批表模板word样式如下所示:
需要填写的部分都被定义为变量或者json对象数组,具体格式如下:
1. 单一变量使用 { } 包含,例如:
{ user } 、{ area }
2. json数组格式,则包裹一个循环对象,例如:
原格式为:
"thinglist": [ { time :"2022-4-1",thing: "在家"}, { time :"2022-4-2",thing: "上班"}, ]
在模板文件中表示为:
{#thinglist} {time}-{thing} {/thinglist}
如果对象是图片地址时,需要在对象前加上% ,例如:
原格式为:
imglist:[ { imgUrl: " "}, { imgUrl: " "}, ]
在模板文件中表示为:
{#imglist} {%imgUrl} {/imglist}
四、封装js 文件
这部分主要是实现word文档导出含图片的主要实现方法,包括将图片的url路径转为base64路径、base64转二进制、以及导出图片的处理,可以直接复制粘贴在页面引入使用,具体代码如下:
import PizZip from 'pizzip' import docxtemplater from 'docxtemplater' import JSZipUtils from 'jszip-utils' import {saveAs} from 'file-saver' /** * 将base64格式的数据转为ArrayBuffer * @param {Object} dataURL base64格式的数据 */ function base64DataURLToArrayBuffer(dataURL) { const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/; if (!base64Regex.test(dataURL)) { return false; } const stringBase64 = dataURL.replace(base64Regex, ""); let binaryString; if (typeof window !== "undefined") { binaryString = window.atob(stringBase64); } else { binaryString = new Buffer(stringBase64, "base64").toString("binary"); } const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { const ascii = binaryString.charCodeAt(i); bytes[i] = ascii; } return bytes.buffer; } /** * 导出word,支持图片 * @param {Object} tempDocxPath 模板文件路径 * @param {Object} wordData 导出数据 * @param {Object} fileName 导出文件名 * @param {Object} imgSize 自定义图片尺寸 */ export const exportWord = (tempDocxPath, wordData, fileName,imgSize) => { //这里要引入处理图片的插件 var ImageModule = require('docxtemplater-image-module-free'); const expressions = require("angular-expressions"); // 读取并获得模板文件的二进制内容 JSZipUtils.getBinaryContent(tempDocxPath, function(error, content) { if (error) { throw error; } expressions.filters.size = function(input, width, height) { return { data: input, size: [width, height], }; }; function angularParser(tag) { const expr = expressions.compile(tag.replace(/'/g, "'")); return { get(scope) { return expr(scope); }, }; } // 图片处理 let opts = {} opts = { //图像是否居中 centered: true }; opts.getImage = (chartId) => { //console.log(chartId);//base64数据 //将base64的数据转为ArrayBuffer return base64DataURLToArrayBuffer(chartId); } opts.getSize = function(img, tagValue, tagName) { //自定义指定图像大小 if(imgSize.hasOwnProperty(tagName)){ return imgSize[tagName]; }else{ return [300, 300]; } } // 创建一个PizZip实例,内容为模板的内容 let zip = new PizZip(content); // 创建并加载docxtemplater实例对象 let doc = new docxtemplater(); doc.attachModule(new ImageModule(opts)); doc.loadZip(zip); doc.setData(wordData); 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, fileName); }); } /** * 将图片的url路径转为base64路径 * 可以用await等待Promise的异步返回 * @param {Object} imgUrl 图片路径 */ export function getBase64Sync(imgUrl) { return new Promise(function(resolve, reject) { // 一定要设置为let,不然图片不显示 let image = new Image(); //图片地址 image.src = imgUrl; // 解决跨域问题 image.setAttribute("crossOrigin", '*'); // 支持跨域图片 // image.onload为异步加载 image.onload = function() { let canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; let context = canvas.getContext("2d"); context.drawImage(image, 0, 0, image.width, image.height); //图片后缀名 let ext = image.src.substring(image.src.lastIndexOf(".") + 1).toLowerCase(); //图片质量 let quality = 0.8; //转成base64 let dataurl = canvas.toDataURL("image/" + ext, quality); //返回 resolve(dataurl); }; }) }
五、实现导出word文档
1. 首先是前端部分,这里使用ElementPlus进行前端页面实现,代码如下:
<template> <div class="page-css"> <el-card class="box-card" shadow="never"> <div class="search-data"> <el-button type="success" @click="editVisible=true">填写审批表</el-button> </div> </el-card> <el-dialog v-model="editVisible" title="房屋出租审批表" width="50%" custom-class="role-mask"> <div> <div class="tablename"> <h2>房屋出租审批表</h2> </div> <table class="tb" border="1"> <tr> <td height="60">承租人</td> <td colspan="2" width="180"> <input class="inputone" v-model="user"/> </td> <td colspan="2" width="125">房屋面积</td> <td colspan="2" width="175"> <input class="inputtwo" v-model="area" /> 平方米 </td> </tr> <tr> <td height="60">年租金</td> <td colspan="2" > <input class="inputtwo" v-model="annualrent" /> 元/年 </td> <td colspan="2" >出租用途</td> <td colspan="2" > <input class="inputone" v-model="purpose"/> </td> </tr> <tr> <td height="300">房屋平面示意图</td> <td colspan="6"> <div v-for="(item,index) in imglist"> <img style="width: 60%;" :src="item.imgUrl"/> </div> </td> </tr> </table> </div> <template #footer> <span class="dialog-footer"> <el-button type="info" @click="editVisible=false">取消</el-button> <el-button type="primary" @click="exportWordFile" >导出</el-button> </span> </template> </el-dialog> </div> </template>
实现过程遇到一个问题:使用el-dialog弹出框时,想固定其在页面居中、距离页面顶部以及底部的固定距离,但是里面的表格内容却超出其显示范围,该如何实现喃?css设置如下:
/* 弹出框居中显示 */ /deep/.el-dialog { left: 50%; top: 50%; transform: translate(-50%, -50%); margin: 0px !important; } /* 弹出框超出部分滑动 */ /deep/.el-dialog__body { height: 75vh; overflow: hidden; overflow-y: auto; }
包括更改el-dialog弹出框头部以及底部区域样式,css设置如下:
/deep/.el-dialog__header { width: 100%; background-color:#f8f8f8 ; } /deep/ .el-dialog__footer { width: 100%; border-top: 1px #ebebeb solid ; }
2.然后在页面内引入封装js里的exportWord以及getBase64Sync方法,data部分定义的是双向绑定填写的内容以及图片地址,考虑到图片可能不知一张,需要循环对其处理转为base64路径,代码如下:
// 引入将图片的url路径转为base64路径的方法 for (let i in this.imglist) { this.imglist[i].imgUrl = await getBase64Sync(this.imglist[i].imgUrl) }
完整代码如下所示:
<script> import {exportWord,getBase64Sync} from '@/assets/js/outword.js' export default { data () { return { editVisible:false, user:'', area:'', annualrent:'', purpose:'', imglist:[ { imgUrl: "https://img2.baidu.com/it/u=2709954499,581919391&fm=253&fmt=auto&app=138&f=JPEG?w=468&h=518" }, { imgUrl: "https://img0.baidu.com/it/u=1462004956,1440895436&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=353" } ] } }, methods:{ async exportWordFile (){ for (let i in this.imglist) { this.imglist[i].imgUrl = await getBase64Sync(this.imglist[i].imgUrl) } let data= { user:this.user, area:this.area, annualrent:this.annualrent, purpose:this.purpose, imglist:this.imglist } let imgSize = { //控制导出的word图片大小 imgurl:[200, 200], }; exportWord("/房屋出租审批表.docx", data, "房屋出租审批表.docx", imgSize); } } } </script>
总结
加载全部内容