目录
- 一、实现效果
- 二、所需插件
- 三、word文档模板
- 四、封装js 文件
- 五、实现导出word文档
- 总结
一、实现效果
以填写并导出房屋出租审批表为例,首先填写表格相应内容后,点击" 导出 "按钮实现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>
总结
到此这篇关于vue实现导出word文档功能(含多张图片)的文章就介绍到这了,更多相关vue导出word文档内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!