很多时候在工作中会碰到完全由前端导出word文件的需求,因此特地记录一下比较常用的几种方式。
一、提供一个word模板
该方法提供一个word模板文件,数据通过参数替换的方式传入word文件中,灵活性较差,适用于简单的文件导出。需要依赖:docxtemplater、file-saver、jszip-utils、pizzip。
javascript
复制代码
import Docxtemplater from "docxtemplater";
import { saveAs } from "file-saver";
import JSZipUtils from "jszip-utils";
import PizZip from "pizzip";export function downloadWithTemplate(path, data, fileName) {JSZipUtils.getBinaryContent(path, (error, content) => {if (error) throw error;const zip = new PizZip(content);const doc = new Docxtemplater().loadZip(zip);doc.setData({...data.form,// 循环项参数list: data.list,outsideList: data.outsideList,});try {doc.render();} catch (error) {const e = {message: error.message,name: error.name,stack: error.stack,properties: error.properties,};ElMessage.error("文件格式有误!");throw error;}const out = doc.getZip().generate({type: "blob",mimeType:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",});saveAs(out, fileName);});
}let data = {form: {title: "这是word标题",test: "这是表单1的数据",test1: "111",test2: 222,test3: 333,},outsideList: [{list: [{index: 0,table: "表格第一项",table1: "表格第二项",table2: "表格第三项",},{index: 1,table: "表格第一项",table1: "表格第二项",table2: "表格第三项",},],},{list: [{index: 0,table: "表格第一项",table1: "表格第二项",table2: "表格第三项",},{index: 1,table: "表格第一项",table1: "表格第二项",table2: "表格第三项",},],},],};downloadWithTemplate("template.docx", data, "模板word.docx")
调用downloadWithTemplate方法即可导出如下文件:
注: 上述方法中的path参数为你在vue项目中存放公共文件的位置,在vue2中为static文件夹下,在vue3中为public文件夹下。
二、根据html代码转换为word文件(推荐)
顾名思义,这个方法就是将我们在页面上书写的html代码直接转换成word文件,这也是我最推荐的一种方法,因为大部分的样式可控,且毕竟是我们较为熟悉的方式。需要插件: html-docx-js-typescript、file-saver。
xml
复制代码
import { saveAs } from "file-saver";
import { asBlob } from "html-docx-js-typescript";export function downloadWordWithHtmlString(html, name) {let htmlString = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Document</title></head><body>${html}</body></html>`;asBlob(htmlString).then((data) => {saveAs(data, `${name}.docx`);});
}`
使用案例:
ini
复制代码
<div ref="word"><h3 style="text-align: center">word标题</h3><tableborder="1"cellspacing="0"width="600"style="font-size: 12px; color: #000; text-align: center"><tr height="50"><td width="100">1111</td><td widt="200" colspan="2">合并单元格</td><td width="300">最长的一项</td></tr><tr height="100"><td width="100">222</td><td width="100">222</td><td width="100">222</td><td width="100">222</td></tr></table><table width="600" border="1" cellspacing="0"><tr height="50"><td width="100">1111</td><td rowspan="3">合并包括此行在内的下面三行</td></tr><tr height="100"><td>222</td></tr><tr height="300"><td>3333</td></tr><tr><td>50</td></tr></table>
</div>let word = ref(null);
downloadWordWithHtmlString(word.value.innerHTML, 'html字符串word.docx');
生成的word文件可以看到效果和在网页中的html代码一样:
另外需要注意的是,若是需要在word中添加分页符,在需要分页的内容处添加CSS属性page-break-before即可。此时在浏览器上打印出innerHTML值会发现:
mdn上介绍page-break-before属性已经被break-before属性替代,但是经过我实际测试发现当html字符串是page-break: always时生成的word文件没有分页效果,反而是将其替换回page-break-before后实现了分页效果。若有大神知道这是什么问题还望不吝赐教。 因此需要在downloadWordWithHtmlString方法中添加一句正则: htmlString = htmlString.replace( /break-(after|before): page/g, "page-break-$1: always;" );
,此时就能实现分页效果。
三、使用docx插件
第二种方法有个很致命的问题就是它无法在生成的word文件中添加图片页眉,我搜遍了npm也只找到一个能添加文字页眉的插件: html-docx-ts要想实现这个需求,就需要用到docx插件。 docx官网的介绍是"Easily generate and modify .docx files with JS/TS. Works for Node and on the Browser.",意味着是一个专门用于生成word和修改word的文件。该插件就需要一个一个去配置你要生成的项,然后组合成一个word。一个简单的案例是:
css
复制代码
import {Document,Paragraph,Header,TextRun,Table,TableRow,TableCell,WidthType,Packer,
} from "docx";
import { saveAs } from "file-saver";const document = new Document({sections: [{headers: {default: new Header({children: [new Paragraph("我是页眉")],}),},children: [new Paragraph({children: [new TextRun({text: "我是文字内容",size: 16,bold: true,}),],}),new Table({columnWidths: [1500, 7500],rows: [new TableRow({children: [new TableCell({width: {size: 1500,type: WidthType.DXA,},children: [new Paragraph({alignment: "center",children: [new TextRun({text: "测试",size: 24,font: {name: "楷体",},}),],}),],}),],}),],}),],},],});Packer.toBlob(document).then((blob) => {saveAs(blob, "test.docx");});
导出的word文件形式为:
下面是我个人总结的比较常见能用到的功能和配置项:
css
复制代码
// 导出文字
1.new Paragraph(text) -> 默认字体样式: 宋体,五号字
2.new Paragraph({children: [new TextRun({text: "我是文字内容",size: 16, // 对应word中的字体大小8bold: true, // 是否加粗underline: {type: UnderlineType.SINGLE,color: "#2e32ee",}, // 下划线类型及颜色font: {name: "仿宋", // 只要是word中有的字体类型都可以生效},}),],indent: {left: 100,}, // 离左边距离 类似于margin-leftspacing: {before: 150,after: 200,}, // 离上边和下边的距离 类似于margin-top/bottomalignment: "center", // 对齐方式pageBreakBefore: true, // 是否在这段文字前加入分页符})// 导出表格
new Table({columnWidths: [1500, 7500], // 表示单行有几项,总宽度是9000,对应宽度;rows: [new TableRow({children: [new TableCell({width: {size: 1500, // 需与columnWidths的第一项对应type: WidthType.DXA, // 官网的介绍是Value is in twentieths of a point// 因为表格的总宽度是以twips(每英寸的1/20)为单位进行计算的},children: [new Paragraph({alignment: "center",children: [new TextRun({text: "测试",size: 24,font: {name: "楷体",},}),],}),],}),new TableCell({width: {size: 7500,type: WidthType.DXA,},children: [new Paragraph('ccc'),],margins: {top: 500,bottom: 500,left: 500} // 类似于单元格内容的padding}),],}),],
})// 导出图片
new Paragraph({children: [new ImageRun({data: "base64", // 图片需转成base64的形式transformation: {width: 100,height: 30,}, // 图片宽高}),],
})// 设置页眉页脚
headers: {default: new Header({children: [new Paragraph("我是页眉")],}),
},
footers: {default: new Footer({children: [new Paragraph("我是页脚")],}),
}
下面是一个完整的使用案例:
css
复制代码
const document = new Document({sections: [{headers: {default: new Header({children: [new Paragraph({children: [new ImageRun({data: "data:image/jpeg;base64,...",transformation: {width: 150,height: 150,},}),],}),],}),},footers: {default: new Footer({children: [new Paragraph("我是页脚")],}),},children: [new Paragraph("第一行直接默认形式"),new Paragraph({children: [new TextRun({text: "下一页",}),],pageBreakBefore: true,}),new Table({columnWidths: [1500, 7500],rows: [new TableRow({children: [new TableCell({width: {size: 1500,type: WidthType.DXA,},children: [new Paragraph({alignment: "center",children: [new TextRun({text: "测试",size: 24,font: {name: "楷体",},}),],}),],}),new TableCell({width: {size: 7500,type: WidthType.DXA,},children: [new Paragraph({children: [new ImageRun({data: "data:image/jpeg;base64,...",transformation: {width: 150,height: 150,},}),],}),],margins: {top: 500,bottom: 500,left: 500,},}),],}),],}),],},],
});Packer.toBlob(document).then((blob) => {saveAs(blob, "test.docx");
});
此时导出的word文件如下:
学习更多vue开发知识请下载CRMEB开源商城附件学习