做网站什么硬盘好,如何进行搜索引擎的优化,企业网站开源代码,如何在linux服务器上架设网站vue3实现包含表格的Word文件导出
近期遇到一个要求#xff0c;需要在网页上导出Word文档#xff0c;文档中有表格#xff0c;也有普通的数据#xff0c;查阅了很多资料#xff0c;总算比较完美的解决了#xff0c;记录一下
先上一下最终效果
演示视频 vue3项目根据Wor…vue3实现包含表格的Word文件导出
近期遇到一个要求需要在网页上导出Word文档文档中有表格也有普通的数据查阅了很多资料总算比较完美的解决了记录一下
先上一下最终效果
演示视频 vue3项目根据Word模板导出Word文件 当然个人的项目要比这个演示的视频复杂多了需要配合后端完成
实现过程主要参考了这篇文章
一、第三方库的安装
要实现Word的导出功能需要额外安装以下第三方包
dependencies: {angular-expressions: ^1.2.1,docx-preview: ^0.3.2,docxtemplater: ^3.49.1,docxtemplater-image-module-free: ^1.1.1,file-saver: ^2.0.5,lodash: ^4.17.21,pizzip: ^3.1.7,},版本安装最新的就行了这是我自己的目前最新的版本2024年8月
二、Word模板的创建
必须要有一个Word模板根据自己的需求创建我的模板如下 这个模板中有常规变量、图片变量和表格变量表格变量需要循环表格数据获取
1、普通变量
普通变量直接用{变量名}的形式放在模板中就行了注意这里的变量名必须与前端vue组件中的变量名保持一致
2、图片变量
图片变量用{%变量名}表示就是普通变量前加%符号
3、表格变量
通常情况下表格都是多行的也就是说需要循环遍历前端的表格数据表格数据的处理比较复杂处理步骤如下
需要循环的表格数据用{#变量名}开始用{/变量名}结束循环也就是{#tableData}和{/tableData}列变量用{变量名}表示也就是{order}、{col1}、{col}和{col3}、{col5}
看下我前端的tableData变量
const tableData ref([{ order: 0, col1: 合计, col: 6266, col3: 23, col5: 2 },{ order: 1, col1: 徐州, col: 706, col3: 1, col5: 0 },{ order: 2, col1: 苏州, col: 668, col3: 2, col5: 0 },{ order: 3, col1: 盐城, col: 624, col3: 2, col5: 0 },{ order: 4, col1: 南通, col: 518, col3: 0, col5: 0 },{ order: 5, col1: 连云港, col: 498, col3: 3, col5: 0 },{ order: 6, col1: 淮安, col: 490, col3: 3, col5: 1 },{ order: 7, col1: 常州, col: 458, col3: 1, col5: 0 },{ order: 8, col1: 泰州, col: 454, col3: 1, col5: 0 },{ order: 9, col1: 无锡, col: 433, col3: 2, col5: 0 },{ order: 10, col1: 南京, col: 400, col3: 2, col5: 1 },{ order: 11, col1: 扬州, col: 383, col3: 3, col5: 0 },{ order: 12, col1: 宿迁, col: 363, col3: 1, col5: 0 },{ order: 13, col1: 镇江, col: 271, col3: 2, col5: 0 },
]);在看下最后生成的表格 创建好Word模板后放在静态文件夹中就行了
三、编写导出Word的工具函数
在utils文件夹中创建exportFile.js文件编写以下代码
// 引入基本模块
import Docxtemplater from docxtemplater;
import PizZip from pizzip;
import PizZipUtils from pizzip/utils/index.js;
import { saveAs } from file-saver;
// 图片模块
import ImageModule from docxtemplater-image-module-free;
// 解析语法模块
import expressions from angular-expressions;
import assign from lodash/assign;
// 文档预览模块
import { renderAsync } from docx-preview;expressions.filters.lower function (input) {if (!input) return input;return input.toLowerCase();
};function angularParser(tag) {tag tag.replace(/^\.$/, this).replace(/(|)/g, ).replace(/(|)/g, );const expr expressions.compile(tag);return {get: function (scope, context) {let obj {};const scopeList context.scopeList;const num context.num;for (let i 0, len num 1; i len; i) {obj assign(obj, scopeList[i]);}return expr(scope, obj);},};
}// 加载文件
function loadFile(url, callback) {PizZipUtils.getBinaryContent(url, callback);
}// 配置空值替换函数 作为配置参数可配置在setOptions中
function nullGetter(part, scopeManager) {if (!part.module) {return -null-;}if (part.module rawxml) {return ;}return --;
}/*** 预览word,支持图片* param {Object} tempDocxPath 模板文件路径* param {Object} wordData 导出数据* param {Object} fileName 导出文件名* param {Arrsy} imgSize 自定义图片尺寸*/
export const getWordImage (tempDocxPath, wordData, imgSize, file) {// 本地word.docx文件需要放在public目录下loadFile(tempDocxPath, (error, content) {if (error) {throw error;}// 图片配置const imageOpts {getImage: function (tagValue, tagName) {return new Promise(function (resolve, reject) {PizZipUtils.getBinaryContent(tagValue, function (error, content) {if (error) {return reject(error);}return resolve(content);});});},getSize: function (img, tagValue, tagName) {const size imgSize[tagName] ? imgSize[tagName] : [150, 150];return size;},};let imageModule new ImageModule(imageOpts);const zip new PizZip(content);// 实例化有两种方式 这里是链式const doc new Docxtemplater().loadZip(zip).setOptions({// delimiters: { start: [[, end: ]] },paragraphLoop: true,linebreaks: true,nullGetter: nullGetter,parser: angularParser,}).attachModule(imageModule).compile();doc.renderAsync(wordData).then(() {const out doc.getZip().generate({type: blob,mimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document,});renderAsync(out, file);});});
};/*** 导出word,不支持图片* param {Object} tempDocxPath 模板文件路径* param {Object} wordData 导出数据* param {Object} fileName 导出文件名*/
export const exportWord (tempDocxPath, wordData, fileName) {// 本地word.docx文件需要放在public目录下loadFile(tempDocxPath, (error, content) {if (error) {throw error;}const zip new PizZip(content);// 没有配置解析语法深层次对象语法obj.xx.xx不可识别const doc new Docxtemplater(zip, {paragraphLoop: true,linebreaks: true,});doc.render(wordData);const out doc.getZip().generate({type: blob,mimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document,});// Output the document using Data-URIsaveAs(out, ${fileName}.docx);});
}/*** 导出word,支持图片* param {Object} tempDocxPath 模板文件路径* param {Object} wordData 导出数据* param {Object} fileName 导出文件名* param {Arrsy} imgSize 自定义图片尺寸*/
export const exportWordImage (tempDocxPath, wordData, fileName, imgSize) {// 本地word.docx文件需要放在public目录下loadFile(tempDocxPath, (error, content) {if (error) {throw error;}// 图片配置const imageOpts {getImage: function (tagValue, tagName) {return new Promise(function (resolve, reject) {PizZipUtils.getBinaryContent(tagValue, function (error, content) {if (error) {return reject(error);}return resolve(content);});});},getSize: function (img, tagValue, tagName) {const size imgSize[tagName] ? imgSize[tagName] : [150, 150]return size;},};let imageModule new ImageModule(imageOpts);const zip new PizZip(content);// 实例化有两种方式 这里是链式const doc new Docxtemplater().loadZip(zip).setOptions({// delimiters: { start: [[, end: ]] },paragraphLoop: true,linebreaks: true,nullGetter: nullGetter,parser: angularParser,}).attachModule(imageModule).compile();doc.renderAsync(wordData).then(function () {const out doc.getZip().generate({type: blob,mimeType:application/vnd.openxmlformats-officedocument.wordprocessingml.document,});saveAs(out, ${fileName}.docx);});});
}
这里有Word的预览以及Word的导出处理函数我直接参考的他人的没做修改可以自己根据需求进行修改后面有时间我再慢慢理解
四、前端页面预览和导出
先上代码我这里只写了个演示所有没有用路由什么的就是直接放在App.vue根组件中
script setup
import { exportWordImage, getWordImage } from /utils/exportFile;
import { ref } from vue;const dialogVisible ref(false);
const startSchemeTemplate ref({name: 启动方案名称,time: 2023-12-12,scope: 1XXXX所有一、二次设备 2XXXX主变、XXXX主变XX管辖,projectAdjuster: 1XXXX,XXXX主变冲击五次、核相。2XXXXX设备冲击一次XXXXXXX二次定相。3XXXXXX,XXXX差动保护带负荷试验。XX管辖4XXXXXX备自投实跳试验。,condition: 1XXX启动范围内的所有一、二次设备施工结束验收合格监控信息与相应调控人员核对完备设备可以带电站内一次设备相位正确。2XXX待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、银标XXXXXX、银阳XXXXXX、银区XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、XXXXXX开关保护按定值单整定并投入。3启动范围内所有设备均为冷备用状态。,stepAdjuster: 1.XXXXXX冲击一次、定相。2.XXXXXX一次设备冲击见附图2,imgPath: https://docxtemplater.com/puffin.png,tableData: []
});
const imgSize ref({imgPath: [150, 150],imgPath1: [550, 250],
});const tableData ref([{ order: 0, col1: 合计, col: 6266, col3: 23, col5: 2 },{ order: 1, col1: 徐州, col: 706, col3: 1, col5: 0 },{ order: 2, col1: 苏州, col: 668, col3: 2, col5: 0 },{ order: 3, col1: 盐城, col: 624, col3: 2, col5: 0 },{ order: 4, col1: 南通, col: 518, col3: 0, col5: 0 },{ order: 5, col1: 连云港, col: 498, col3: 3, col5: 0 },{ order: 6, col1: 淮安, col: 490, col3: 3, col5: 1 },{ order: 7, col1: 常州, col: 458, col3: 1, col5: 0 },{ order: 8, col1: 泰州, col: 454, col3: 1, col5: 0 },{ order: 9, col1: 无锡, col: 433, col3: 2, col5: 0 },{ order: 10, col1: 南京, col: 400, col3: 2, col5: 1 },{ order: 11, col1: 扬州, col: 383, col3: 3, col5: 0 },{ order: 12, col1: 宿迁, col: 363, col3: 1, col5: 0 },{ order: 13, col1: 镇江, col: 271, col3: 2, col5: 0 },
]);const htmlTitle ref(启动方案);const downLoad () {exportWordImage(../template.docx,startSchemeTemplate.value,htmlTitle.value,imgSize.value);
};const goPreview () {dialogVisible.value true;
};const file ref(null);
const handleOpened () {startSchemeTemplate.value.tableData tableData.valuegetWordImage(../template.docx,startSchemeTemplate.value,imgSize.value,file.value);
};
/scripttemplatediv styleheight: 90%; background: #fff; padding: 24pxdiv stylemargin-bottom: 17px; text-align: leftel-button typeprimary clickdownLoad 下载启动方案 /el-buttonel-button typeprimary clickgoPreview 预览启动方案 /el-button/divel-divider /div stylemargin-top: 24px!--搜索区域--el-form :modelstartSchemeTemplate label-width110pxel-row :gutter24el-col :span12el-form-item label启动方案名称el-inputv-modelstartSchemeTemplate.nameplaceholder请输入//el-form-item/el-colel-col :span12el-form-item label预定启动时间el-date-pickerv-modelstartSchemeTemplate.timetypedateplaceholder请选择//el-form-item/el-col/el-rowel-row :gutter24 styleheight: 280pxel-col :span12el-form-item label启动范围el-inputv-modelstartSchemeTemplate.scopeplaceholder请输入typetextarea:autosize{ minRows: 13.5, maxRows: 14 }//el-form-item/el-colel-col :span12el-form-item label调试项目el-inputv-modelstartSchemeTemplate.projectAdjusterplaceholder请输入typetextarea:autosize{ minRows: 13.5, maxRows: 14 }//el-form-item/el-col/el-rowel-row :gutter24 styleheight: 280pxel-col :span12el-form-item label启动条件el-inputv-modelstartSchemeTemplate.conditionplaceholder请输入typetextarea:autosize{ minRows: 13.5, maxRows: 14 }//el-form-item/el-colel-col :span12el-form-item label调试步骤el-inputv-modelstartSchemeTemplate.stepAdjusterplaceholder请输入typetextarea:autosize{ minRows: 13.5, maxRows: 14 }//el-form-item/el-col/el-row/el-form/div/divel-dialogv-modeldialogVisibleopenedhandleOpenedtitle流程图width1200pxtop5vhdiv classdocWrapdiv reffile/div/div/el-dialog
/templatestyle scoped
.btn {float: left;margin: 0 0 24px;
}
.docWrap {height: 700px;overflow: auto;clear: both;
}
/style
变量全部放在startSchemeTemplate这个响应式变量中根据前端的表单或者输入来更改模板中的数据把模板和模板中需要的数据对应起来看就更直观了
const startSchemeTemplate ref({name: 启动方案名称,time: 2023-12-12,scope: 1XXXX所有一、二次设备 2XXXX主变、XXXX主变XX管辖,projectAdjuster: 1XXXX,XXXX主变冲击五次、核相。2XXXXX设备冲击一次XXXXXXX二次定相。3XXXXXX,XXXX差动保护带负荷试验。XX管辖4XXXXXX备自投实跳试验。,condition: 1XXX启动范围内的所有一、二次设备施工结束验收合格监控信息与相应调控人员核对完备设备可以带电站内一次设备相位正确。2XXX待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、银标XXXXXX、银阳XXXXXX、银区XXXXXX、待用XXXXXX、待用XXXXXX、待用XXXXXX、XXXXXX开关保护按定值单整定并投入。3启动范围内所有设备均为冷备用状态。,stepAdjuster: 1.XXXXXX冲击一次、定相。2.XXXXXX一次设备冲击见附图2,imgPath: https://docxtemplater.com/puffin.png,tableData: []
});一言蔽之把Word模板中需要动态变化的数据放在响应式数据中这里是startSchemeTemplate然后根据响应式数据填充模板
预览和导出功能调用utils/exportFile.js中对应的方法就可以了
五、代码仓库
我已经把这个程序的所有代码和模板文件都传到了代码仓库有需要的可以自行下载理解 文章转载自: http://www.morning.knnc.cn.gov.cn.knnc.cn http://www.morning.yhglt.cn.gov.cn.yhglt.cn http://www.morning.xrksf.cn.gov.cn.xrksf.cn http://www.morning.rnsjp.cn.gov.cn.rnsjp.cn http://www.morning.xrhst.cn.gov.cn.xrhst.cn http://www.morning.rpjyl.cn.gov.cn.rpjyl.cn http://www.morning.ggnfy.cn.gov.cn.ggnfy.cn http://www.morning.xbnkm.cn.gov.cn.xbnkm.cn http://www.morning.prxqd.cn.gov.cn.prxqd.cn http://www.morning.xxwhz.cn.gov.cn.xxwhz.cn http://www.morning.rfpq.cn.gov.cn.rfpq.cn http://www.morning.kghhl.cn.gov.cn.kghhl.cn http://www.morning.kkwgg.cn.gov.cn.kkwgg.cn http://www.morning.hhxpl.cn.gov.cn.hhxpl.cn http://www.morning.ylkkh.cn.gov.cn.ylkkh.cn http://www.morning.rcrnw.cn.gov.cn.rcrnw.cn http://www.morning.prysb.cn.gov.cn.prysb.cn http://www.morning.hqpyt.cn.gov.cn.hqpyt.cn http://www.morning.qjldz.cn.gov.cn.qjldz.cn http://www.morning.sbczr.cn.gov.cn.sbczr.cn http://www.morning.hghhy.cn.gov.cn.hghhy.cn http://www.morning.npmpn.cn.gov.cn.npmpn.cn http://www.morning.shuanga.com.cn.gov.cn.shuanga.com.cn http://www.morning.gxklx.cn.gov.cn.gxklx.cn http://www.morning.rckmz.cn.gov.cn.rckmz.cn http://www.morning.btwrj.cn.gov.cn.btwrj.cn http://www.morning.bmyrl.cn.gov.cn.bmyrl.cn http://www.morning.mtdfn.cn.gov.cn.mtdfn.cn http://www.morning.ssjtr.cn.gov.cn.ssjtr.cn http://www.morning.qcwck.cn.gov.cn.qcwck.cn http://www.morning.bchhr.cn.gov.cn.bchhr.cn http://www.morning.dqwykj.com.gov.cn.dqwykj.com http://www.morning.rmdwp.cn.gov.cn.rmdwp.cn http://www.morning.jxfsm.cn.gov.cn.jxfsm.cn http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn http://www.morning.jokesm.com.gov.cn.jokesm.com http://www.morning.ygbq.cn.gov.cn.ygbq.cn http://www.morning.prmbb.cn.gov.cn.prmbb.cn http://www.morning.hmxrs.cn.gov.cn.hmxrs.cn http://www.morning.htbsk.cn.gov.cn.htbsk.cn http://www.morning.zlzpz.cn.gov.cn.zlzpz.cn http://www.morning.ryrpq.cn.gov.cn.ryrpq.cn http://www.morning.qtzk.cn.gov.cn.qtzk.cn http://www.morning.sooong.com.gov.cn.sooong.com http://www.morning.wjjxr.cn.gov.cn.wjjxr.cn http://www.morning.llxqj.cn.gov.cn.llxqj.cn http://www.morning.mtgnd.cn.gov.cn.mtgnd.cn http://www.morning.ytrbq.cn.gov.cn.ytrbq.cn http://www.morning.fqqcd.cn.gov.cn.fqqcd.cn http://www.morning.nqwz.cn.gov.cn.nqwz.cn http://www.morning.lzttq.cn.gov.cn.lzttq.cn http://www.morning.madamli.com.gov.cn.madamli.com http://www.morning.hxxwq.cn.gov.cn.hxxwq.cn http://www.morning.grwgw.cn.gov.cn.grwgw.cn http://www.morning.kczkq.cn.gov.cn.kczkq.cn http://www.morning.807yy.cn.gov.cn.807yy.cn http://www.morning.bhrbr.cn.gov.cn.bhrbr.cn http://www.morning.rfqk.cn.gov.cn.rfqk.cn http://www.morning.fdrch.cn.gov.cn.fdrch.cn http://www.morning.yrbhf.cn.gov.cn.yrbhf.cn http://www.morning.cmldr.cn.gov.cn.cmldr.cn http://www.morning.sjsfw.cn.gov.cn.sjsfw.cn http://www.morning.cwwbm.cn.gov.cn.cwwbm.cn http://www.morning.tmzlt.cn.gov.cn.tmzlt.cn http://www.morning.yrqb.cn.gov.cn.yrqb.cn http://www.morning.wzyfk.cn.gov.cn.wzyfk.cn http://www.morning.swkzr.cn.gov.cn.swkzr.cn http://www.morning.jpbpc.cn.gov.cn.jpbpc.cn http://www.morning.lkthj.cn.gov.cn.lkthj.cn http://www.morning.mpsnb.cn.gov.cn.mpsnb.cn http://www.morning.trqsm.cn.gov.cn.trqsm.cn http://www.morning.zkbxx.cn.gov.cn.zkbxx.cn http://www.morning.qgqck.cn.gov.cn.qgqck.cn http://www.morning.ffbp.cn.gov.cn.ffbp.cn http://www.morning.xxgfl.cn.gov.cn.xxgfl.cn http://www.morning.rksg.cn.gov.cn.rksg.cn http://www.morning.wcgfy.cn.gov.cn.wcgfy.cn http://www.morning.bfwk.cn.gov.cn.bfwk.cn http://www.morning.ftnhr.cn.gov.cn.ftnhr.cn http://www.morning.fwjfh.cn.gov.cn.fwjfh.cn