其实在调研之初是知道几个插件的,但大部分都不在维护了且导出效果不理想。
本文主要使用docxtemplater导出word文档并记录以下详细步骤。
实现效果:
原始页面是有一个列表和两个echarts图表
导出的word效果:
其中列表是可修改的,echarts是两张图片不可修改。
详细实现步骤
1.安装依赖
pnpm install docxtemplater pizzip --save
pnpm install jszip-utils --save
pnpm install jszip --save
pnpm install file-saver --save
pnpm install docxtemplater-image-module-free
docxtemplater:这个插件可以通过预先写好的word,excel等文件模板生成对应带数据的文件
pizzip:这个插件用来创建,读取或编辑.zip的文件(同步的,还有一个插件是jszip,异步的)
jszip-utils:与jszip/pizzip一起使用,jszip-utils 提供一个getBinaryContent(path, data)接口,path即是文件的 路径,支持AJAX get请求,data为读取的文件内容。
file-saver:适合在客户端生成文件的工具,它提供的接口saveAs(blob, "1.docx")将会使用到,方便我们保存 文件。
docxtemplater-image-module-free:导出图片的话需要这个插件
2.创建word模板文件
创建名为test的word模板,放至项目的public文件夹内:public/test.docx
- docxtemplater 语法
{%img} 图片
{#list}{/list} 循环、if判断
{#list}{/list}{^list}{/list} if else
{str} 文字
这一步将说明如何在test.docx内编写模板
列表部分需要自己先在word内插入一张表格,然后表头自己定义好。如果有什么特殊样式自己都可以在word内提前编辑好。
列表数据对应字段是logList,列表循环以{#logList}开始,又以{/logList}结尾。注意这里的所有字段都要与代码传入的一致。{%leftImage}和{%rightImage}就是那两张echarts图所在的位置。
4.代码实现部分
import JSZipUtils from "jszip-utils";
import docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import saveAs from "file-saver"
import ImageModule from "docxtemplater-image-module-free"
由于需要拿到echarts相关dom所以需要定义ref并绑定
<div class="h-400px flex">
<LineChart
id="item0"
:options="chatOptions"
height="400px"
width="100%"
ref="leftChartRef"
class="bg-[var(--el-bg-color-overlay)]"/>
<LineChart
id="item1"
:options="chatOptions2"
height="400px"
width="100%"
ref="rightChartRef"
class="bg-[var(--el-bg-color-overlay)]"/>
</div>
const leftChartRef = ref(HTMLDivElement)
const leftImage = ref(null) //存储图片资源
const rightChartRef = ref(HTMLDivElement)
const rightImage = ref(null)
const logList = ref<LogPageVO[]>(); //接口获取的表格数据
导出按钮点击和调用的相关方法
//导出点击
const exportWordClick = ()=> {
leftImage.value = leftChartRef.value.getChartsImage();
rightImage.value = rightChartRef.value.getChartsImage();
let docxData= {
logList: logList.value //列表数据,类似于[{name:'系统管理员',ip:'114.0.57....'}]
}
downLoadDoc('public/test.docx',docxData,"下载测试模板")
}
const downLoadDoc = (demoUrl, docxData, fileName)=>{
// 读取并获得模板文件的二进制内容
JSZipUtils.getBinaryContent(
demoUrl,
function (error, content) {
// 抛出异常
if (error) {
throw error;
}
// 图片处理
let opts = { centered: false }
opts.getImage = chartId => {
return base64DataURLToArrayBuffer(chartId)
}
opts.getSize = (img, tagValue, tagName)=> {
//自定义指定图像大小,此处可动态调试各别图片的大小
return [500, 300] //例子:宽500px 高度300px
}
// 创建一个PizZip实例,内容为模板的内容
let zip = new PizZip(content);
// 创建并加载docxtemplater实例对象
let doc = new docxtemplater().loadZip(zip).attachModule(new ImageModule(opts));
// 去除未定义值所显示的undefined
doc.setOptions({
nullGetter: function () {
return "";
}
});
// 设置模板变量的值,对象的键需要和模板上的变量名一致,值就是你要放在模板上的值
doc.setData({
...docxData,
leftImage: leftImage.value,
rightImage: rightImage.value
});
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);
}
);
}
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
}
这里额外需要说的一点是导出点击方法内
leftImage.value = leftChartRef.value.getChartsImage();
rightImage.value = rightChartRef.value.getChartsImage();
这也是我们为什么定义ref的原因,因为需要将echarts图片转为base64格式。
echarts子组件内
//将echarts图片转为base64格式
const getChartsImage = ()=>{
return chart.value.getDataURL({
pixelRatio: 2, // 导出的图片分辨率比例,默认为 1。
backgroundColor: 'transparent' // 导出的图片背景色,默认使用 option 里的 backgroundColor
})
}
/**
* 当使用 <script setup> 写法会导致父组件无法访问到子组件中的属性和方法。
* 使用 <script setup> 的组件,想要让父组件访问到它的属性和方法需要借助与defineExpose来指定需要暴露给父组件的属性。
* */
defineExpose({
getChartsImage
})
5.结语
至此,我们在vue3中使用docxtemplater导出word文档的实践就告一段落了。文章从介绍docxtemplater基本使用语法,再到完整的代码示例实现表格和echarts图表的导出功能。如果有什么想法也可以继续留言交流,感谢!