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 error
if (!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.scopeList
const num = context.num
for (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的数据转为ArrayBuffer
return 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;
//转成base64
let dataurl = canvas.toDataURL("image/" + ext, quality);
//返回
resolve(dataurl);
};
})
}
- 导出函数
vue 中引入上述js文件和方法
import {ExportBriefDataDocx,getBase64Sync} from './doc.js'
async ExportBriefDataDocx(){
//图片转base64
let 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;
//转成base64
let dataurl = canvas.toDataURL("image/"+ext);
//返回
resolve(dataurl);
};
})
}