当前位置: 首页 > news >正文

dedecms网站制作教程百度推广是怎么做的

dedecms网站制作教程,百度推广是怎么做的,全国工商企业信息查询网,廉洁广州在线网站建设vue3实现包含表格的Word文件导出 近期遇到一个要求,需要在网页上导出Word文档,文档中有表格,也有普通的数据,查阅了很多资料,总算比较完美的解决了,记录一下 先上一下最终效果 演示视频 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模板,根据自己的需求创建,我的模板如下:

image-20240805093049148

这个模板中有常规变量、图片变量和表格变量,表格变量需要循环表格数据获取

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 },
]);

在看下最后生成的表格:

image-20240805094606833

创建好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: `1.XXXX所有一、二次设备 2.XXXX主变、XXXX主变(XX管辖)`,projectAdjuster: `1.XXXX,XXXX主变冲击五次、核相。2.XXXXX设备冲击一次,XXXXXXX二次定相。3.XXXXXX,XXXX差动保护带负荷试验。(XX管辖)4.XXXXXX备自投实跳试验。`,condition: `1.XXX启动范围内的所有一、二次设备施工结束,验收合格,监控信息与相应调控人员核对完备,设备可以带电,站内一次设备相位正确。2.XXX待用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);
};
</script><template><div style="height: 90%; background: #fff; padding: 24px"><div style="margin-bottom: 17px; text-align: left"><el-button type="primary" @click="downLoad"> 下载启动方案 </el-button><el-button type="primary" @click="goPreview"> 预览启动方案 </el-button></div><el-divider /><div style="margin-top: 24px"><!--搜索区域--><el-form :model="startSchemeTemplate" label-width="110px"><el-row :gutter="24"><el-col :span="12"><el-form-item label="启动方案名称:"><el-inputv-model="startSchemeTemplate.name"placeholder="请输入"/></el-form-item></el-col><el-col :span="12"><el-form-item label="预定启动时间:"><el-date-pickerv-model="startSchemeTemplate.time"type="date"placeholder="请选择"/></el-form-item></el-col></el-row><el-row :gutter="24" style="height: 280px"><el-col :span="12"><el-form-item label="启动范围:"><el-inputv-model="startSchemeTemplate.scope"placeholder="请输入"type="textarea":autosize="{ minRows: 13.5, maxRows: 14 }"/></el-form-item></el-col><el-col :span="12"><el-form-item label="调试项目:"><el-inputv-model="startSchemeTemplate.projectAdjuster"placeholder="请输入"type="textarea":autosize="{ minRows: 13.5, maxRows: 14 }"/></el-form-item></el-col></el-row><el-row :gutter="24" style="height: 280px"><el-col :span="12"><el-form-item label="启动条件:"><el-inputv-model="startSchemeTemplate.condition"placeholder="请输入"type="textarea":autosize="{ minRows: 13.5, maxRows: 14 }"/></el-form-item></el-col><el-col :span="12"><el-form-item label="调试步骤:"><el-inputv-model="startSchemeTemplate.stepAdjuster"placeholder="请输入"type="textarea":autosize="{ minRows: 13.5, maxRows: 14 }"/></el-form-item></el-col></el-row></el-form></div></div><el-dialogv-model="dialogVisible"@opened="handleOpened"title="流程图"width="1200px"top="5vh"><div class="docWrap"><div ref="file"></div></div></el-dialog>
</template><style 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: `1.XXXX所有一、二次设备 2.XXXX主变、XXXX主变(XX管辖)`,projectAdjuster: `1.XXXX,XXXX主变冲击五次、核相。2.XXXXX设备冲击一次,XXXXXXX二次定相。3.XXXXXX,XXXX差动保护带负荷试验。(XX管辖)4.XXXXXX备自投实跳试验。`,condition: `1.XXX启动范围内的所有一、二次设备施工结束,验收合格,监控信息与相应调控人员核对完备,设备可以带电,站内一次设备相位正确。2.XXX待用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: []
});

image-20240805094918478

一言蔽之,把Word模板中需要动态变化的数据放在响应式数据中(这里是startSchemeTemplate),然后根据响应式数据填充模板

预览和导出功能,调用utils/exportFile.js中对应的方法就可以了

五、代码仓库

我已经把这个程序的所有代码和模板文件都传到了代码仓库,有需要的可以自行下载理解

http://www.hkea.cn/news/31501/

相关文章:

  • wordpress主菜单下拉箭头怎么设置台州seo排名优化
  • 网站系统管理员模块关键词查找工具
  • 望江县建设局网站外贸seo推广招聘
  • 微信网站上传图片手机怎么制作网站
  • 简单做网站需要学什么搜索引擎有哪些网站
  • 网站备案信息加到哪里如何进行网站推广
  • 昭通网站制作aso优化技巧
  • 制作网站时怎样做滚动字幕新网站多久会被百度收录
  • 余姚物流做网站微信指数是搜索量吗
  • 怎样做网站轮播今日国内重大新闻事件
  • 想给大学做网站百度网盘搜索神器
  • jsp网站开发论文官方app下载安装
  • 关于机场建设的网站今日疫情最新情况
  • 网站域名注册服务商google浏览器官方
  • 通过网站开发工具怎么改自动跳网站百度指数有哪些功能
  • 可以发锚文本的网站百度搜索官方网站
  • 东莞网站建设企慕简述如何优化网站的方法
  • 可以做网站的公司seo外包
  • 自己怎么做网站视频赚钱5g网络优化培训
  • 数据库修改网站管理员密码seo网站有优化培训吗
  • 福田做商城网站建设找哪家公司好抖音怎么运营和引流
  • 厘米售卡站怎么做网站禁止搜索引擎收录的方法
  • 网站首页滚动图片怎么做谷歌搜索关键词排名
  • 嵩县网站开发友情链接获取的途径有哪些
  • 国家企业信息公示网(广东)海南快速seo排名优化
  • 高端网站设计 上海徐州seo排名公司
  • 泰安网站建设公司排名石家庄最新消息
  • 域名只做邮箱没网站要备案吗常见的网络推广方式包括
  • 昆山建设局网站360搜索首页
  • 正常做网站多少钱无锡网站制作无锡做网站