VUE使用docxtemplater导出word(带图片) 踩坑 表格循环空格 ,canvas.toDataURL图片失真模糊问题
参考:https://www.codetd.com/article/15219743
- 安装
// 安装 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
- 关键代码JS部分
/*** 导出word文档(带图片) doc.js* */
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
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 = Buffer.from(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;
}export const ExportBriefDataDocx = (tempDocxPath, data, fileName, imgSize) => {console.log(111, tempDocxPath, data, fileName, imgSize)//这里要引入处理图片的插件var ImageModule = require('docxtemplater-image-module-free');var expressions = require('angular-expressions')var assign = require('lodash/assign')var last = require("lodash/last")expressions.filters.lower = function (input) {// This condition should be used to make sure that if your input is// undefined, your output will be undefined as well and will not// throw an errorif (!input) return input// toLowerCase() 方法用于把字符串转换为小写。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 index = last(context.scopePathItem)const scopeList = context.scopeListconst num = context.numfor (let i = 0, len = num + 1; i < len; i++) {obj = assign(obj, scopeList[i])}//word模板中使用 $index+1 创建递增序号obj = assign(obj, { $index: index })return expr(scope, obj)}}}JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {if (error) {console.log(error)}expressions.filters.size = function (input, width, height) {return {data: input,size: [width, height],};};let opts = {}opts = {//图像是否居中centered: true};opts.getImage = (chartId) => {//将base64的数据转为ArrayBufferreturn base64DataURLToArrayBuffer(chartId);}opts.getSize = function (img, tagValue, tagName) {//自定义指定图像大小if (imgSize.hasOwnProperty(tagName)) {return imgSize[tagName];} else {return [200, 200];}}// 创建一个JSZip实例,内容为模板的内容 const zip = new PizZip(content)// 创建并加载 Docxtemplater 实例对象// 设置模板变量的值let doc = new Docxtemplater();doc.attachModule(new ImageModule(opts));doc.loadZip(zip);doc.setOptions({parser:angularParser});doc.setData(data)try {// 呈现文档,会将内部所有变量替换成值,doc.render()} catch (error) {const e = {message: error.message,name: error.name,stack: error.stack,properties: error.properties}console.log('err',{ error: e })// 当使用json记录时,此处抛出错误信息throw error}// 生成一个代表Docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)const out = doc.getZip().generate({type: 'blob',mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'})// 将目标文件对象保存为目标类型的文件,并命名saveAs(out, fileName)})
}/**1. 将图片的url路径转为base64路径2. 可以用await等待Promise的异步返回3. @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;//转成base64let dataurl = canvas.toDataURL("image/" + ext, quality);//返回resolve(dataurl);};})
}
- 导出函数
vue 中引入上述js文件和方法
import {ExportBriefDataDocx,getBase64Sync} from './doc.js'
async ExportBriefDataDocx(){//图片转base64let imgurl= await getBase64Sync('图片路径') let imgurl2= await getBase64Sync('图片路径')let data= {name:this.name,imgurl:this.imgurl,imgurl2:this.imgurl2}let imgSize = {//控制导出的word图片大小,可自定义imgurl:[200, 200],imgurl2:[200, 200],};exportWord("/我的模板.docx", data, `${this.listname}.docx`, imgSize);//docx模板放在public文件夹下,如果是vue2放在static下// ExportBriefDataDocx("/static/我的模板.docx", data, `${listname}.docx`, imgSize);}
- 模板内容
自己准备一个docx文档,然后里面标注好需替换的参数
列表循环-- {#list}{name}{/list}
单个参数–{}
图片–{%imgUrl}

大概就这些,我也是从参考链接里看到的,至此基本能解决大部分问题,但是我还遇到了两个问题,所以自己记录补充一下 - 表格循环打印会多出空格

我想循环表格出来,但是输入数据,出来后实际是这样的
let data= {ld:[{data1:1,data2:2},{data1:1,data2:2}],} ExportBriefDataDocx("/static/wd.docx", data, `${listname}.docx`, imgSize);

多出了很多空格,我想着去掉模板中的换行符,像这样

我本来是想着这样就能少一行,就是正常表格了,但是其实报错了
message: ‘The filetype for this file could not be identified, is this file corrupted ?’, stack: ‘Error: The filetype for this file could not be ide…//./node_modules/jszip-utils/lib/index.js:110:25)’
大概就是类型不对,读不出来什么的
后来灵光一闪,想到参考链接里各种数据都是在表格里的,应该没问题,所以我就想着开始结束都放进去,就像这样

然后结果就对了

7.保存图片模糊问题 quality = 1也没啥用
改成这样,导出后的图片能经得起缩放
export function getBase64Sync(imgUrl,width,height) {
//传入你想要的宽高,最好大一点,这个不会影响导出后的大小,这里的宽高可以理解为分辨率,就是canvas绘制的时候的大小,imgSize里的才是导出到文档的大小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 = width*2; canvas.height = height*2; //宽高放大两倍let context = canvas.getContext("2d");context.drawImage(image, 0, 0); //图片后缀名let ext = image.src.substring(image.src.lastIndexOf(".") + 1).toLowerCase();//图片质量//let quality = 1;//转成base64let dataurl = canvas.toDataURL("image/"+ext);//返回resolve(dataurl);};})
}
相关文章:
VUE使用docxtemplater导出word(带图片) 踩坑 表格循环空格 ,canvas.toDataURL图片失真模糊问题
参考:https://www.codetd.com/article/15219743 安装 // 安装 docxtemplater npm install docxtemplater pizzip --save // 安装 jszip-utils npm install jszip-utils --save // 安装 jszip npm install jszip --save // 安装 FileSaver npm install file-save…...
ubuntu 安装 Pycharm社区版
在Ubuntu中安装pycharm社区版_上玄下纁的博客-CSDN博客 里面可以创建快捷方式,蛮好用的...
IP 监控软件
IP 监控软件可帮助管理员主动监控网络资源。随着各种设备连接到网络,监控设备和接口可能很复杂,为管理员提供这些设备的IP监控,了解其各种性能指标和问题。 使用有效的 IP 监控软件的优势 使用有效的 IP 监控系统和一套全面的 IP 监控工具&…...
C#实现读写CSV文件的方法详解
目录 CSV文件标准 文件示例RFC 4180简化标准读写CSV文件 使用CsvHelper使用自定义方法总结 项目中经常遇到CSV文件的读写需求,其中的难点主要是CSV文件的解析。本文会介绍CsvHelper、TextFieldParser、正则表达式三种解析CSV文件的方法,顺带也会介绍一…...
04 http连接处理(上)
基础知识:epoll、http报文格式、状态码和有限状态机 代码:对服务端处理http请求的全部流程进行简要介绍,然后结合代码对http类及请求接收进行详细分析。 epoll epoll_create函数 #include <sys/epoll.h> int epoll_create(int size)…...
c++(强生成关键字+可变参数模板+emplace)[26]
强制生成 不生成 在C中,可以通过一些方式来控制编译器是否生成某些特殊成员函数(如默认构造函数、拷贝构造函数、拷贝赋值运算符、析构函数等)。 默认生成:如果你没有显式地定义这些特殊成员函数,编译器会自动生成它们…...
Mysql 数据库开发及企业级应用
文章目录 1、Mysql 数据库开发及企业级应用1.1、为什么要使用数据库1.1.1、数据库概念(Database)1.1.2、为什么需要数据库 1.2、程序员为什么要学习数据库1.3、数据库的选择1.3.1、主流数据库简介1.3.2、使用 MySQL 的优势1.3.3、版本选择 1.4、Windows …...
【数据结构】_6.队列
目录 1.概念 2.队列的使用 3.队列模拟实现 4.循环队列 5.双端队列 6.OJ题 6.1 用队列实现栈 6.2 用栈实现队列 1.概念 (1)队列是只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表; (2&am…...
7 网络通信(上)
文章目录 网络通信概述ip地址ip的作用ip地址的分类私有ip 掩码和广播地址 linux 命令(ping ifconfig)查看或配置网卡信息:ifconfig(widows 用ipconfig)测试远程主机连通性:ping路由查看 端口端口是怎样分配的知名端口动态端口 查看…...
MFC图表控件high-speed-charting的使用
high-speed-charting是MFC上的开源图表库,Teechart的替代品。 high-speed-charting的下载地址 https://www.codeproject.com/Articles/14075/High-speed-Charting-Control 特性 High-speed drawing (when axis is fixed) which allows fast plotting of dataUnlimited number …...
Unity中常用方法
1.基础 //初始化引入 [RequireComponent(typeof(BoxCollider2D))] [RequireComponent(typeof(Rigidbody2D))]//游戏帧率设置 60帧Application.targetFrameRate 60;//获取物体对象 //获取到当前物体(根据名称,也可以根据路径)GameObject go GameObject.Find("…...
【监控系统】可视化工具Grafana简介及容器化部署实战
1.什么是Grafana 官网地址:https://grafana.com/ Grafana用Go语言开发的开源数据可视化工具,可以做数据监控和数据统计,带有告警功能。支持快速灵活的客户端图表,面板插件有许多不同方式的可视化指标和日志,官方库中…...
VUE之VueRouter页面跳转
参考资料: 参考视频 参考demo及视频资料 VUE之基本部署及VScode常用插件 VUE之基本组成和使用 VUE之Bootstrap和Element-UI的使用 VUE之axios使用,跨域问题,拦截器添加Token Vue Router官网 Vue Router说明: 说明…...
【188】Java8利用AVL树实现Map
AVL树又被叫做平衡二叉搜索树、平衡二叉树。AVL是其发明者的首字母缩写。 这篇文章中,AVLTreeMap 类集成了 java.util.Map 接口,并利用 AVL 树结构实现了 Map 接口的所有方法。本文还给出了测试代码。 为什么要发明AVL树? 当我按照从小到大…...
[SQL挖掘机] - 右连接: right join
介绍: 右连接是一种多表连接方式,它以右侧的表为基础,并返回满足连接条件的匹配行以及右侧表中的所有行,即使左侧的表中没有匹配的行。右连接将右表的每一行与左表进行比较,并根据连接条件返回结果集。其实, 左连接和右连接原理一…...
bug篇之基于docker安装nacos(2.1.1)使用dubbo连接不上的问题
说明:首先我的nacos安装是2.1.1版本,请注意版本问题。另外启动时用dubbo的话必须先启动服务提供者再启动服务使用者,否则会报错,同时也必须开放三个端口:8848,9848,9849 java.lang.IllegalStat…...
【Python入门系列】第二十一篇:Python物联网和传感器应用
文章目录 前言一、Python在物联网和传感器应用中的优势二、连接传感器和设备三、读取传感器数据四、示例代码和讲解五、进一步处理和分析传感器数据六、更多应用示例1、温湿度监测系统2、智能家居系统 - 灯光控制 总结 前言 物联网和传感器在现代科技中扮演着重要的角色。物联…...
Python爬虫的urlib的学习(学习于b站尚硅谷)
目录 一、页面结构的介绍 1.学习目标 2.为什么要了解页面(html) 3. html中的标签(仅介绍了含表格、无序列表、有序列表、超链接) 4.本节的演示 二、Urllib 1.什么是互联网爬虫? 2.爬虫核心 3.爬虫…...
【MongoDB】--MongoDB聚合Aggregation
目录 一、前言二、聚合管道操作2.1、实际案例1(1)、案例--根据学生no,找到对应班级名称(2)、案例--这个班级有哪些学生和哪些老师在任课 2.2、实际案例2(1)、案例--主表和关联表都有条件限制,且分页返回 一、前言 聚合操作组值来自多个文档,…...
Hadoop学习指南:探索大数据时代的重要组成——Hadoop概述
前言 在当今大数据时代,处理海量数据成为了一项关键任务。Hadoop作为一种开源的分布式计算框架,为大规模数据处理和存储提供了强大的解决方案。本文将介绍Hadoop的组成和其在大数据处理中的重要作用,让我们一同踏上学习Hadoop的旅程。 Hado…...
Agent 时代的开发者技能树重构指南
1. 标题选项 核心关键词:AI Agent、开发者转型、技能树重构、职业跃迁 《Agent 时代降临:普通开发者的技能树重构全指南,从CRUD Boy到AI应用工程师的跃迁之路》 《别再只会写CRUD了!AI Agent浪潮下,你必须掌握的新技能树体系》 《从软件开发到AI Agent开发:2024年开发者技…...
鸿蒙同城兴趣圈页面构建:今晚活动与同频推荐模块详解
鸿蒙同城兴趣圈页面构建:今晚活动与同频推荐模块详解 前言 在 HarmonyOS 6.0 应用开发中,社交类页面的活动展示和用户推荐是提升用户参与度的核心功能模块。本文将以“同城兴趣圈”应用中的“今晚活动”时间线模块和“同频推荐”用户卡片网格为例&#x…...
打卡信奥刷题(3292)用C++实现信奥题 P8976 「DTOI-4」排列
P8976 「DTOI-4」排列 题目背景 Update on 2023.2.1:新增一组针对 yuanjiabao 的 Hack 数据,放置于 #21。 Update on 2023.2.2:新增一组针对 CourtesyWei 和 bizhidaojiaosha 的 Hack 数据,放置于 #22。 构造一个排列 ppp&…...
为什么92%的团队误用Gemini做Java审查?资深架构师拆解3个致命配置陷阱及修复命令集
更多请点击: https://codechina.net 第一章:Gemini Java代码审查的真相与误区 Gemini 并非专为 Java 代码审查设计的工具,其底层模型(如 Gemini 1.5 Pro)虽具备强大的自然语言理解与代码生成能力,但缺乏静…...
+86环境下“纸飞机“登录异常排查:第三方开源客户端的认证与网络适配测试
近期在针对一款基于 MTProto 协议的即时通讯工具进行客户端适配测试时,发现其官方版本在 86 号段环境下存在较为突出的登录与连接稳定性问题。本文记录问题复现过程,以及基于开源代码二次开发的优化实践。一、登录异常现象在 86 手机号、新设备登录场景下…...
vscode+stm32+embedded ide+cortex debug+gcc
用stm32cubemx生成项目。下载三个软件,设置环境变量 openocd是仿真用,gcc-arm-none-eabi-10.3是编译用,w64evkit只用其中的make.exe根据生成的makefile文件,添加c源文件,包含目录,startup文件&#…...
Nodejs开发者三步搞定Taotoken接入并实现异步聊天对话功能
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Nodejs开发者三步搞定Taotoken接入并实现异步聊天对话功能 对于Node.js开发者而言,将大模型能力集成到应用中的第一步&…...
开源项目治理:ECC 社区贡献指南与协作模式
作者注:本文基于 ECC 项目的开源治理实践,帮助中国开发者理解如何参与大型开源项目并建立有效的协作流程。项目开源地址:github.com/affaan-m/ECC摘要 ECC(Everything Claude Code)是一个拥有 170 贡献者、28K Forks 的…...
Godot纸牌游戏框架:分层架构与卡牌状态管理
1. 这不是又一个“通用游戏框架”,而是一套专为纸牌游戏设计的骨骼系统你有没有试过在Godot里从零搭一张卡牌游戏?我试过三次——第一次用Node2D硬堆,拖了二十多个场景,连抽卡动画都得手写Tween;第二次改用Resource做卡…...
观察Taotoken在多模型聚合调用下的月度账单明细
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 观察Taotoken在多模型聚合调用下的月度账单明细 对于个人开发者或项目组而言,在项目中集成多个大语言模型(…...
