场景
有的时候我们需要根据后端提供的数据,然后结合word模版来生成word。我们可以使用第三方库docxtemplater
效果
代码
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 = urlimage.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 = falseopts.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 = urlimage.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>
public/word/template.docx