解决的是导出含有echarts的页面为word文档,需要考虑echarts图表的大小,采用的是设置角度解析器来预设置图片宽高,也考虑到多图导出问题,下面是我的解决过程
添加所需依赖:
- npm install docxtemplater
- npm install pizzip
- npm install jszip-utils
- npm install jszip
- npm install file-saver
- npm install docxtemplater-image-module
导出word的主要方法
建立一个word.js,内容如下
import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import JszipUtils from 'jszip-utils/lib'
import FileSave from 'file-saver'
/**
* echarts图转为base64
* 注意事项,echarts图使用canvas绘制,勿使用SVG绘制
*/
export const downloadImgByChart = (myChart) => {
/** 调用方式
* let myChart = echarts.getInstanceByDom(document.getElementById('energychart'));
* downloadImgByChart(myChart)
*/
var url = myChart.getDataURL({
pixelRatio: 5,//导出的图片分辨率比率,默认是1
backgroundColor: '#fff',//图表背景色
excludeComponents: [ 'toolbox'],//忽略组件的列表
type: 'png',//图片类型支持png和jpeg
});
return url;
}
/**
* base64转为ArrayBuffer
*/
export const base64DataURLToArrayBuffer = (dataURL) => {
const base64Regex = /^data:image\\/(png|jpg|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主函数
*/
export const exportWord = (fileDocx, data, outName) => {
let ImageModule = require('docxtemplater-image-module');
const expressions = require("angular-expressions");
//获取模板中的图片值{%image | size:200:100}
expressions.filters.size = function (input, width, height) {
return {
data: input,
size: [width, height],
};
};
function angularParser(tag) {
const expr = expressions.compile(tag.replace(/’/g, "'"));
return {
get(scope) {
return expr(scope);
},
};
}
// 将文件转为二进制
JszipUtils.getBinaryContent(fileDocx, function(err, res) {
if (err) { throw err}
let opts = {
centered: false,
fileType: "docx"
}
opts.getImage = function (tagValue) {
if (tagValue.size && tagValue.data) {
return base64DataURLToArrayBuffer(tagValue.data);
}
return base64DataURLToArrayBuffer(tagValue);
};
opts.getSize = function (_, tagValue) {
if (tagValue.size && tagValue.data) {
return tagValue.size;
}
return [500, 500];
};
// 将文件转为zip文件
let pizZip = new PizZip(res)
// 创建Docxtemplater对象实例并添加zip文件
let doc = new Docxtemplater()
doc.loadZip(pizZip)
doc.setOptions({ parser: angularParser });
doc.attachModule(new ImageModule(opts));
// 设置填充内容
doc.setData(data)
// 进行内容填充
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;
}
// 获取要下载的文件
let out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
})
// 进行下载
FileSave(out, outName)
})
}
内部的坑
不知道模板文件如何导入或导入错误,JszipUtils.getBinaryContent(fileDocx, function(err, res) {})时,不知道这个fileDocx模板文件放在哪里,然后出现引入路径错误
Error: Can't find end of central directory : is this a zip file?
解决方案:
在使用vue-cli2时,我们需要把模板文件放到static目录下
在使用vue-cli3时,我们需要把模板文件放在public目录下
然后当config中的publicPath是'./'时,直接传入文件名就好,如
JszipUtils.getBinaryContent('template.docx', function(err, res) {})
当config中的publicPath是 ‘/’时,则需要../一层层到相应位置,要特别注意省去public目录,如
JszipUtils.getBinaryContent('../../template.docx', function(err, res) {})
调用导出方法
我的环境是vue-cli3,然后再public目录下建立word文件夹存放模板文件weektmp.docx