前端实现根据模版导出word【docxtemplater】
场景#
有的时候我们需要根据后端提供的数据,然后结合word模版来生成word。我们可以使用第三方库docxtemplater
效果#
docxtemplater库说明#
原理#
它通过在word模版中使用特殊的标记(形如:{xxx}、{%yyy})的标记进行占位,到时会把数据填充到对应的位置上。
依赖说明#
1、docxtemplater:这个插件可以通过预先写好的word,excel等文件模板生成对应带数据的文件
2、pizzip:这个插件用来创建,读取或编辑.zip的文件(同步的,还有一个插件是jszip,异步的)
3、jszip-utils:与jszip/pizzip一起使用,jszip-utils 提供一个getBinaryContent(path, data)接口,path即是文件的路径,支持AJAX get请求,data为读取的文件内容。
4、file-saver:适合在客户端生成文件的工具,它提供的接口saveAs(blob, "1.docx")将会使用到,方便我们保存文件。
5、docxtemplater-image-module-free:需要导出图片的话需要这个插件
代码#
App.vue
<template>
<div class="app">
<el-divider content-position="center">1.基本使用</el-divider>
<el-button type="primary" @click="exportWord_1">导出word</el-button>
<el-divider content-position="center">2.带有表格的导出</el-divider>
<el-button type="primary" @click="exportWord_2">导出word</el-button>
<el-divider content-position="center">3.带有图片的导出</el-divider>
<el-button type="primary" @click="exportWord_3">导出word</el-button>
</div>
</template>
<script>
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import PizZipUtils from 'pizzip/utils/index.js'
import { saveAs } from 'file-saver'
// import ImageModule from 'docxtemplater-image-module/build/imagemodule'
import ImageModule from 'docxtemplater-image-module-free'
import logo from '@/assets/1.png'
export default {
data() {
return {}
},
methods: {
exportWord_1() {
PizZipUtils.getBinaryContent('/word/template.docx', (err, content) => {
if (err) {
throw err
}
const zip = new PizZip(content)
const doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
})
doc.render({
title: '邮件标题',
name: 'Coder',
content: '这是一封测试邮件',
signer: 'Me',
sign_time: '2024/07/02',
})
const out = doc.getZip().generate({
type: 'blob',
mimeType:
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
saveAs(out, 'Test.docx')
})
},
exportWord_2() {
PizZipUtils.getBinaryContent('/word/template.docx', (err, content) => {
if (err) {
throw err
}
const zip = new PizZip(content)
const doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
})
doc.render({
title: '邮件标题',
name: 'Coder',
content: '这是一封测试邮件',
signer: 'Me',
sign_time: '2024/07/02',
movie_list: [
{
name: '盗梦空间',
price: 56,
release_date: '2013-06-03',
},
{
name: '斗破苍穹',
price: 46,
release_date: '2024-01-03',
},
{
name: '疯狂的麦克斯*狂暴女神',
price: 76,
release_date: '2024-07-02',
},
],
})
const out = doc.getZip().generate({
type: 'blob',
mimeType:
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
saveAs(out, 'Test.docx')
})
},
exportWord_3() {
// 官方示例,尝试不成功
/*
const imageOptions = {
getImage(url) {
return new Promise(function (resolve, reject) {
PizZipUtils.getBinaryContent(url, function (error, content) {
if (error) {
return reject(error)
}
return resolve(content)
})
})
},
getSize(img, url, tagName) {
return new Promise(function (resolve, reject) {
const image = new Image()
image.src = url
image.onload = function () {
resolve([image.width, image.height])
}
image.onerror = function (e) {
console.log('img, url, tagName : ', img, url, tagName)
alert('An error occured while loading ' + url)
reject(e)
}
})
},
}
PizZipUtils.getBinaryContent('/word/template.docx', (err, content) => {
if (err) {
throw err
}
const zip = new PizZip(content)
const doc = new Docxtemplater(zip, {
paragraphLoop: true,
linebreaks: true,
modules: [new ImageModule(imageOptions)],
})
doc
.renderAsync({
logo: logo,
title: '邮件标题',
name: 'Coder',
content: '这是一封测试邮件',
signer: 'Me',
sign_time: '2024/07/02',
movie_list: [
{
name: '盗梦空间',
price: 56,
release_date: '2013-06-03',
},
{
name: '斗破苍穹',
price: 46,
release_date: '2024-01-03',
},
{
name: '疯狂的麦克斯*狂暴女神',
price: 76,
release_date: '2024-07-02',
},
],
})
.then(function () {
const out = doc.getZip().generate({
type: 'blob',
mimeType:
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
saveAs(out, 'Test.docx')
})
})
*/
PizZipUtils.getBinaryContent('/word/template.docx', (err, content) => {
if (err) {
throw err
}
var opts = {}
opts.centered = false
opts.getImage = function (tagValue, tagName) {
return new Promise(function (resolve, reject) {
PizZipUtils.getBinaryContent(tagValue, function (error, content) {
if (error) {
return reject(error)
}
return resolve(content)
})
})
}
opts.getSize = function (img, tagValue, tagName) {
// FOR FIXED SIZE IMAGE :
return [150, 150]
// FOR IMAGE COMING FROM A URL (IF TAGVALUE IS AN ADRESS) :
// To use this feature, you have to be using docxtemplater async
// (if you are calling setData(), you are not using async).
return new Promise(function (resolve, reject) {
var image = new Image()
image.src = url
image.onload = function () {
resolve([image.width, image.height])
}
image.onerror = function (e) {
console.log('img, tagValue, tagName : ', img, tagValue, tagName)
alert('An error occured while loading ' + tagValue)
reject(e)
}
})
}
var imageModule = new ImageModule(opts)
const zip = new PizZip(content)
const doc = new Docxtemplater()
.loadZip(zip)
.attachModule(imageModule)
.compile()
doc
.resolveData({
logo: logo,
title: '邮件标题',
name: 'Coder',
content: '这是一封测试邮件',
signer: 'Me',
sign_time: '2024/07/02',
movie_list: [
{
name: '盗梦空间',
price: 56,
release_date: '2013-06-03',
},
{
name: '斗破苍穹',
price: 46,
release_date: '2024-01-03',
},
{
name: '疯狂的麦克斯*狂暴女神',
price: 76,
release_date: '2024-07-02',
},
],
})
.then(function () {
doc.render()
var out = doc.getZip().generate({
type: 'blob',
mimeType:
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
})
saveAs(out, 'test-image.docx')
})
})
},
},
}
</script>
<style lang="less" scoped>
.app {
padding: 40px;
}
</style>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2022-07-02 echarts的颜色渐变