项目环境
vue2 + js + vue-cli等
依赖包都可以在npm官网找到对应文档 npm官网(英文)
1、依赖
安装依赖
docxtemplater
npm i docxtemplater
file-saver
npm i file-saver
jszip-utils
npm i jszip-utils
jszip
npm i jszip
在对应页面或模块中引入依赖
import Docxtemplater from "docxtemplater";
import { saveAs } from "file-saver";
import JSZipUtils from "jszip-utils";
import JSZip from 'jszip';
2、准备word模板
导出需要依据准备好的word模板导出,word模板放置在vue项目中的public文件夹下;
模板书写依据 docxtemplater ,其是一个用于处理和生成Word文档的模板引擎,使用类似于标签的语法来标记需要被替换或插入的内容;
常见语法:
- 替换变量:使用大括号 { } 将要替换的变量包裹起来。例如:{ 变量名称 }。在运行时,你可以通过提供一个数据对象来替换这些变量。
小技巧:如果有很多需要替换的变量,那么可以把这些变量通过编码的方式糅合成一个或几个大变量后再实现替换,降低工作量,同时也可以实现更高的word文档自定义
- 循环迭代:使用 {{#each}} 和 {{/each}} 标签来循环迭代一个数组,并在每次迭代中生成相应的内容。
{{#each students}}
Name: {{name}}, Age: {{age}}
{{/each}}
students 为一个包含学生数组的数据对象 ; 每个数组项都包含name和age属性
- 条件判断:使用 {{#if}} 和 {{/if}} 标签来进行条件判断,并根据条件的真假来生成相应的内容
{{#if isPassed}}
Congratulations! You have passed the exam.
{{else}}
Sorry, you did not pass the exam.
{{/if}}
-
图片
{%img}
{%%img} 图片在word中居中
模板里的所定义的变量名称,要和程序中定义的变量名称一样,这样才能成功实现替换
模板案例
3、编码导出
// 根据项目需求的不同,需要修改导出函数中的内容
// 切记,在触发该导出函数前 先把 doc.setData() 中需要的数据先装好
handleExport(){
//这里要引入处理图片的插件,下载docxtemplater后,引入的就在其中了
var ImageModule = require("docxtemplater-image-module-free");
// 读取并获得模板文件的二进制内容
JSZipUtils.getBinaryContent("YangGuangFile.docx", (error, content) => {
// YangGuangFile.docx 这里是word模板文档的路径,该项目而言在public根目录下,所以,直接写文件名即可
if (error) {
throw error;
}
// 图片处理
let opts = {};
opts = { centered: false };
opts.getImage = (chartId) => {
return this.base64DataURLToArrayBuffer(chartId);
};
opts.getSize = function (img, tagValue, tagName) {
//自定义指定图像大小 根据文件名 规定导出的图片大小
if (tagName === "img1" || tagName === "img2") {
return [600, 200];
}
if (tagName === "img3" || tagName === "img4" || tagName === "img5") {
return [200, 400];
}
};
// 创建一个PizZip实例,内容为模板的内容
let zip = new PizZip(content);
// 创建并加载docxtemplater实例对象
let doc = new docxtemplater();
doc.attachModule(new ImageModule(opts));
doc.loadZip(zip);
// copyWorldData 是一个对象,包含很多变量,变量名和word模板中定义好的变量名一一对应,其中就有year1,month1等等
// img1 就是导出图片的名称,也和word模板中定义的图片名一一对应
// this.$refs.pictureLine1.getChartImg() 在父组件通过ref的方式调用子组件里的getChartImg()方法生成图片
// 这里的所用的图片子组件是 echarts 二次封装而成的组件
doc.setData({
...copyWorldData,
img1: this.$refs.pictureLine1.getChartImg(),
img2: this.$refs.pictureLine2.getChartImg(),
img3: this.$refs.pictureLine3.getChartImg(),
img4: this.$refs.pictureLine4.getChartImg(),
img5: this.$refs.pictureLine5.getChartImg(),
});
// try的作用 执行一个可能会抛出异常的操作,如果有异常,通过catch 接收eror后抛出,没有异常不执行catch
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",
});
// 将目标文件对象保存为目标类型的文件,并命名
// 也可以使用 模板字符串 加入变量, 让每一个导出的word文件都有自己的名字 `文件名${变量}.docx`
saveAs(out, "自定义导出文件名称.docx");
});
}
图片处理 (图片通过echarts自带的导出函数导出)
// 导出echarts图片,格式转换 该函数基本是一个固定写法,直接拿着用即可
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;
},
注意事项
需要注意因为接口而导致的异步问题,异步问题会导致数据没有填充上去,异步时确保拿到数据之后再执行导出操作
存在的问题:
1、 有时候导出的word文档中的图片,在手机上无法显示,但是在电脑上一直都可以正常显示;
解决办法:word文档导出后,单独下载其中的一张图片(我用的是echarts,支持单独下载单个图片),然后用其替换word文档中的对应的图片,那么所有的图片就都可以在手机上正常显示了
当然,也可以直接把导出word文档转为pdf,其也可以在手机上正常显示图片,由此可见,图片导出后 确确实实是存在的,只不过word格式下有时候在手机上展示不出来,转为pdf后就不会出现这种情况