1.安装依赖
// 安装 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
2.word模版参考官网:Demo of Docxtemplater with all modules active | docxtemplater
word的变量:{name}
对象:
数组:
word的表格-数据结构为数组
判断:{#isShowChart} 内容 {/}
word的图片:{%chart}(这里其实是image 但我导出的是echarts转的图 就用的chart)
word模版位置:
3.图片处理及导出的公共方法
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import {saveAs} from 'file-saver'
import ImageModule from 'docxtemplater-image-module-free'
import expressions from 'angular-expressions'
//图片的处理(这个方法是docxtemplater官网复制的 直接用了)
function base64Parser(tagValue) {
const base64Regex =
/^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
const validBase64 =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
if (
typeof tagValue !== "string" ||
!base64Regex.test(tagValue)
) {
return false;
}
const stringBase64 = tagValue.replace(base64Regex, "");
if (!validBase64.test(stringBase64)) {
throw new Error(
"Error parsing base64 data, your data contains invalid characters"
);
}
// For nodejs, return a Buffer
if (typeof Buffer !== "undefined" && Buffer.from) {
return Buffer.from(stringBase64, "base64");
}
// For browsers, return a string (of binary content) :
const binaryString = window.atob(stringBase64);
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) => {
expressions.filters.lower = function(input) {
if (!input) return input
return input.toLowerCase()
}
JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
if (error) {
console.log(error)
}
expressions.filters.size = function(input, width, height) {
return {
data: input,
size: [width, height]
}
}
const imageOptions = {
getImage(tagValue) {
return base64Parser(tagValue);
},
//图片在word的大小
getSize(img, tagValue, tagName, context) {
return [650, 370];
},
};
// 创建一个JSZip实例,内容为模板的内容
const zip = new PizZip(content)
// 创建并加载 Docxtemplater 实例对象
// 设置模板变量的值
const doc = new Docxtemplater()
doc.attachModule(new ImageModule(imageOptions))
doc.loadZip(zip)
doc.setOptions({
nullGetter: function() { // 设置空值 undefined 为''
return ''
}
});
data = flattenObject(data);
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)
})
}
4.页面上的echarts组件及数据的处理
<template>
<div>
<a-card v-show="false">
<div ref="chart" style="width: 1000px; height: 600px"></div>
</a-card>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick,toRef,watch } from 'vue';
import { xxxApi } from '@/api';
import {message} from 'ant-design-vue';
import * as echarts from 'echarts';
import { ExportBriefDataDocx } from '@/utils/exportFile';
const wordData = ref(null);
const dataLoading = ref(false);
const emit = defineEmits(['dataLoading']);
// 获取数据/word/generateDRGWordDate
const getData = async () => {
try {
dataLoading.value = true;
emit('dataLoading',dataLoading.value);
let req = { };
const res = await xxxApi.getWordData(req);
if (res.code === 200 && res.data) {
wordData.value ={...res.data,isShowChart:res.data.departmentList.length>2};
await nextTick(); // 确保DOM已经渲染完成
renderChart();
}
} catch (err) {
throw err;
}finally{
dataLoading.value = false;
emit('dataLoading',dataLoading.value);
}
};
const chart = ref(null);
let chartInstance = null;
// xx结余排行
const renderChart = () => {
if (chart.value) {
chartInstance = echarts.init(chart.value);
chartInstance.clear();
const departmentList = [...wordData.value.departmentList];
let sortList = departmentList.sort((a, b) => b.balanceMoney - a.balanceMoney).filter(item=>item.department!=='all');
const data = sortList.map(item => item.balanceMoney);
const xData = sortList.map(item => item.department);
const option = {
title: {
text: 'xx结余排行'
},
tooltip: {},
grid: {
// right: 20,
bottom: '30%'
},
xAxis: {
axisLabel: {
interval: 0, //代表显示所有x轴标签显示
rotate: 45 //代表逆时针旋转45度
},
data: xData,
nameTextStyle: {
height: 200
}
},
yAxis: {},
series: [
{
name: 'Sales',
type: 'bar',
label: {
show: true,
position: 'outside'
},
data: data
},
],
// 渲染的动画关掉 注意一些特殊的echarts
animation: false
};
chartInstance.setOption(option);
}
};
// 获取echarts图片
const getUrl = chartInstance => {
const chartImage = chartInstance.getDataURL({
type: 'png',
pixelRatio: 2,
backgroundColor: '#fff'
});
return chartImage;
};
const exportDocx = () => {
wordData.value = { ...wordData.value,
isShowChart:wordData.value.departmentList.length > 2,
// 获取echarts图片
chart: getUrl(chartInstance),
};
const imgSize = [150, 150];
// 导出为docx文件
// 导出逻辑,例如使用插件导出为docx文件
ExportBriefDataDocx(
'/template.docx', // 模板路径
wordData.value, // 数据
'xxx分析报告.docx', // 文件名
imgSize
);
};
// 销毁ECharts实例
onUnmounted(() => {
if (chartInstance != null && chartInstance.dispose) {
chartInstance.dispose();
}
});···
const toExport =async () => {
// 最后一个echarts渲染结束时调用
chartInstance.on('finished',exportDocx())
}
defineExpose({
toExport
})
</script>