vue + docxtemplater 导出 word 文档

一、痛点

word 导出 这种功能其实之前都是后端实现的,但最近有个项目没得后端。所以研究下前端导出。
ps: 前端还可以导出 pdf,但是其分页问题需要话精力去计算才可能实现,并且都不是很完善。可参考之前的文章:利用 html2canvas 和 jspdf 导出 echarts ( html页面 )为pdf

二、依赖安装

// 实现word下载的主要三方库
npm install docxtemplater pizzip  --save// 文件操作;大佬们可以不需要,自己用fs、path等模块实现
npm install jszip jszip-utils --save // 文件存储
npm install file-saver --save// 图片处理模块,没有图片需求可以不装
npm install docxtemplater-image-module-free  --save

三、创建导出word的公用方法 exportWord.js

ps:这个方法大同小异,网上很多

import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'// 将图片地址转为base64,导出word图片只能是base64
export function getBase64Sync(imgUrl) {return new Promise(function (resolve, reject) {// 一定要设置为let,不然图片不显示let image = new Image();// 解决跨域问题image.crossOrigin = 'anonymous';//图片地址image.src = imgUrl;// 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;//转成base64let dataurl = canvas.toDataURL('image/' + ext, quality);//返回resolve(dataurl);};});
}
/*** 将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');JSZipUtils.getBinaryContent(tempDocxPath, function (error, content) {if (error) {throw error;}// 图片处理let opts = {};opts = {centered: true, //图像是否居中,true:在word中图片居中getImage: (chartId) => { // 将base64转成ArrayBufferreturn base64DataURLToArrayBuffer(chartId);},//自定义指定图像大小,此处可动态调试各别图片的大小getSize: (img, tagValue, tagName) => {// tagName 是指我们自己定义图片使用的字段名,如path、url等// if (tagName === 'imgurl') return [700, 350]; //设置图片宽高,tagName :传入的变量// return [200, 150]; if (Object.prototype.hasOwnProperty.call(imgSize, tagName)) {return imgSize[tagName];} else {return [150, 150];}}};// 创建一个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);});
}

四、组件中调用

前几章都是基础,调用才是重点。

1. 创建模板

导出word其实就是解析我们提供的模板,然后将对应字段填入,最新进行导出即可。所以,模板 至关重要

  • 创建.docx文件
    该文件只能直接创建为docx 或者 另存为docx ;不能直接修改后缀名。
  • vue2 将模板放在static文件下;vue3 将模板放在public文件下
2. 模板语法

在这里插入图片描述

  • 语法用 { } 即可
  • 普通字段直接填入字段名即可
  • 如果字段是图片地址,需要加上 %,例如:
{#imgList} {%pathUrl}
{/imgList}

踩坑:图片这里我一直报错‘%imgUrl’,最后发现必须要换行写,而其他数组可以在一行写。

  • 遍历列表 以 {#list} 开头 … 列表元素字段名 … {/list} 结尾
list: [{name:'张三', age:'18'},{name:'李四', age:'28'}
]

模板: 在这里插入图片描述
导出实际结果:
在这里插入图片描述

3. 组件调用
<template><div><!-- 页面只有一个echarts 和 导出按钮 --><div id="myChart6" :style="{ width: '800px', height: '800px' }"></div><el-button type="primary" @click="exprodWord">导出word</el-button></div>
</template><script>import * as echarts from 'echarts';import { onMounted } from 'vue'import { getBase64Sync, exportWord } from './exportFile'export default {name: 'WordTemplate',setup () {let myChartDom = null;const wordData = {title: '环境工业风险审核报告',des: '对于需要判断显示的要用{#isProblem}开始,{/isProblem}结束,isProblem的类型是Boolean,true的时候是显示。如下图,isFull==true的时候,才显示下面这句话',userList: [{indexNo: 1,name: '张三',age: '18',address: '上海',imgList: [{url: 'https://i.postimg.cc/qqcRNJ1y/b3c2e029c5deda297e29680e26a5c48c.jpg'},{url: 'https://i.postimg.cc/9Q5b3J7k/797c9c2bbf47b1ad4632670e508e0d5d.jpg'}],status: 1,},{indexNo: 2,name: '李四',age: '28',address: '四川',imgList: [],status: 1,},{indexNo: 3,name: '王五',age: '38',address: '北京',imgList: [],status: 0,},{indexNo: 4,name: '张柳',age: '48',address: '成都',imgList: [],status: 0,}]}const exprodWord = async () => { const chartPath = getChartImg(); // 获取到echarts的图片地址const renderData = JSON.parse(JSON.stringify(wordData))renderData.chartPath = chartPath// 将图片转成base64是异步操作,需要等待图片base64返回,所以使用Promise.allrenderData.userList = await Promise.all(renderData.userList.map(async item => {return {...item,imgList: await Promise.all(item.imgList.map(async em => {return {...em,path: await getBase64Sync(em.url)}})) };}))let imgSize = {imgurl: [200, 200], // 定义图片字段名为 'imgurl' 的尺寸, 该实例中没有图片字段名是imgurl,所以不生效chartPath: [1000, 800] // 定义图片字段名为 'chartPath' 的尺寸, 即该实例中的echarts图片// ... 更多}console.log('------------renderData', renderData)exportWord('template.docx', renderData, '环境工业风险审核报告.docx', imgSize)}// 基于准备好的dom,初始化echarts实例const initChart = () => {myChartDom = echarts.init(document.getElementById('myChart6'));// 绘制图表myChartDom.setOption({title: {text: 'ECharts 入门示例'},tooltip: {},xAxis: {data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']},yAxis: {},series: [{name: '销量',type: 'bar',data: [5, 20, 36, 10, 10, 20]}]});}// 获取图表base64图const getChartImg = () => {return myChartDom.getDataURL({pixelRatio: 2, // 解决模糊backgroundColor: '#fff'});}onMounted(() => {initChart()})return {exprodWord}}}
</script><style lang='scss' scoped>
</style>

注意:导出操作可能涉及异步操作,请多使用 Promise.all、nextTick等异步方法,尽量少使用setTimeout

五、导出word 结果

在这里插入图片描述



文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/165096.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript框架 Angular、React、Vue.js 的全栈解决方案比较

在 Web 开发领域&#xff0c;JavaScript 提供大量技术栈可供选择。其中最典型的三套组合&#xff0c;分别是 MERN、MEAN 和 MEVN。前端框架&#xff08;React、Angular 和 Vue&#xff09;进行简化比较。 MERN 技术栈详解 MERN 技术栈包含四大具体组件&#xff1a; MongoDB&am…

蓝桥杯物联网竞赛_STM32L071_3_Oled显示

地位&#xff1a; 对于任何一门编程语言的学习&#xff0c;print函数毫无疑问是一种最好的调试手段&#xff0c;调试者不仅能通过它获取程序变量的运行状态而且通过对其合理使用获取程序的运行流程&#xff0c;更能通过关键变量的输出帮你验证推理的正确与否&#xff0c;朴素的…

常见网络安全防护

1 阻断服务攻击&#xff08;DOS&#xff09; 阻断服务攻击&#xff0c;想办法目标网络资源用尽变种&#xff1a;分布式阻断服务攻击 影响&#xff1a; 宽带消耗性&#xff08;消耗目标的带宽&#xff09;资源消耗型&#xff08;消耗目标的计算资源&#xff09; 解决方案&am…

人工智能对网络安全的影响越来越大

如果问当前IT行业最热门的话题是什么&#xff0c;很少有人会回答除了人工智能&#xff08;AI&#xff09;之外的任何话题。 在不到 12 个月的时间里&#xff0c;人工智能已经从一项只有 IT 专业人员才能理解的技术发展成为从小学生到作家、程序员和艺术家的每个人都使用的工具…

MySQL索引事务基础

目录 1. 索引 1.1索引的概念 1.2索引的特点 1.3 索引的使用场景 1.4索引的使用 1.4.1查看索引 1.4.2创建索引 1.4.3删除索引 1.5索引保存的数据结构 2.事务 2.1经典例子 2.2事务的概念 2.3事务的使用 2.4事务的4个核心特性 2.5事务的并发问题 2.5.1脏读 2.5.2不可…

Python + Docker 还是 Rust + WebAssembly?

在不断发展的技术世界中&#xff0c;由大语言模型驱动的应用程序&#xff0c;通常被称为“LLM 应用”&#xff0c;已成为各种行业技术创新背后的驱动力。随着这些应用程序的普及&#xff0c;用户需求的大量涌入对底层基础设施的性能、安全性和可靠性提出了新的挑战。 Python 和…

Java项目如何打包成Jar(最简单)

最简单的办法&#xff0c;使用Maven插件&#xff08;idea自带&#xff09; 1.选择需要打包的mudule&#xff0c;点击idea右侧的maven插件 2.clean操作 3.选择需要的其他mudule&#xff0c;进行install操作&#xff08;如果有&#xff09; 4.再次选择需要打包的module&#…

Python----类对象和实例对象

目录 一.类和类的实例 二.类属性和实例属性 三.私有属性和公有属性 四.静态方法和类方法 五.__init__方法&#xff0c;__new__方法和__del__方法&#xff1a; 六.私有方法和公有方法 七.方法的重载 八.方法的继承 九.方法的重写 十.对象的特殊方法 十一.对象的引用&a…

基于命令行模式设计退款请求处理

前言 这篇文章的业务背景是基于我的另一篇文章: 对接苹果支付退款退单接口-CSDN博客 然后就是说设计模式是很开放的东西,可能我觉得合适,你可能觉得不合适,这里只是做下讨论,没有一定要各位同意的意思.... 相关图文件 这里我先把相关的图文件放上来,可能看着会比较清晰点 代码逻…

Web服务器(go net/http) 处理Get、Post请求

大家好 我是寸铁&#x1f44a; 总结了一篇Go Web服务器(go net/http) 处理Get、Post请求的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 go http请求如何编写简单的函数去拿到前端的请求(Get和Post) 服务器(后端)接收到请求后&#xff0c;又是怎么处理请求&#xff0c…

【网络奇缘】- 计算机网络|分层结构|ISO模型

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏: 一见倾心,再见倾城 --- 计算机网络~&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 计算机网络分层结构 OSI参考模型 OSI模型起源 失败原因: OSI模型组成 协议的作用 &#x1f4dd;全文…

二十四、RestClient操作文档

目录 一、新增文档 1、编写测试代码 二、查询文档 1、编写测试代码 三、删除文档 1、编写测试代码 四、修改文档 1、编写测试代码 五、批量导入文档 批量查询 一、新增文档 1、编写测试代码 SpringBootTest public class HotelDocumentTest {private RestHighLevelC…

【栈】不同字符的最小子序列

题目&#xff1a; /*** 思路&#xff1a;栈,使用数组记录每个字母出现的次数&#xff0c;再用一个数组标记字符是否在栈中* 遍历栈&#xff0c;存储字符时比较栈顶字符&#xff0c;若小于栈顶字符并且后面有重复的字符则* 栈顶元素出栈&#xff0c;否则入栈。** au…

PS 注释工具 基础使用方法讲解

好 上文PS 颜色取样器&标尺工具 基本使用讲解中 我们讲了 颜色取样器和标尺工具的基本用法 下面我们来看一下 注释工具 这个 主要是后面 比较大的作品 可能不是我们一个人取设计 团队作图 就需要用到它 选择 注释工具 后 我们随便点击图像任何一个位置 右侧就会出现一个输…

gitlab各版本安装注意点:

研发团队在安装gitlab各版本过程中可能遇到各种问题&#xff0c;为了后续容易查看特将我们在实践过程中遇到的各类问题要点总结如下&#xff1a; gitlab 10.8.3 (564c342&#xff09;安装 centos Linux yum安装网址查找网址&#xff1a;gitlab/gitlab-ce - Results for gitla…

黑马点评12-实现好友关注/取关功能,查看好友共同关注列表

好友关注 数据模型 数据库中的tb_follow记录博主与粉丝的关系 tb_follow表对应的实体类 Data EqualsAndHashCode(callSuper false) Accessors(chain true) TableName("tb_follow") public class Follow implements Serializable {private static final long ser…

栈和队列的OJ题--12.括号匹配

12.括号匹配 20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a;该题比较简单&#xff0c;是对栈特性很好的应用&#xff0c;具体操作如下&#xff1a;循环遍历String中的字符&#xff0c;逐个取到每个括号&#xff0c;如果该括号是&#xff1a;1. …

Git工作流和Commit规范

Git大家都非常熟悉了&#xff0c;就不做过多介绍&#xff0c;但是如何用好Git、如何进行合理的分支开发、Merge你是否有一个规范流程呢&#xff1f;&#x1f4a4; 不论是一个团队一起开发一个项目&#xff0c;还是自己独立开发一个项目&#xff0c;都少不了要和Git打交道&…

紫光展锐V8821荣获“中国芯”重大创新突破产品奖

近日&#xff0c;“中国芯”优秀产品评选落下帷幕&#xff0c;紫光展锐首颗5G IoT-NTN卫星通信SoC芯片V8821凭借在卫星通信前沿领域的技术创新&#xff0c;从285家芯片企业、398款芯片产品中脱颖而出&#xff0c;荣获第十八届“中国芯”年度重大创新突破产品奖。 “中国芯”优…

ReentrantLock源码解析

ReentrantLock源码解析 文章目录 ReentrantLock源码解析一、ReentrantLock二、ReentrantLock 的 Sync、FairSync、NonfairSync2.1 Sync、FairSync、NonfairSync2.2 NonfairSync 下的 tryAcquire2.3 FairSync下的 tryAcquire2.4 tryRelease 三、lock.lock()3.1 NonfairSync.lock…