这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
很多时候在工作中会碰到完全由前端导出word文件的需求,因此特地记录一下比较常用的几种方式。
一、提供一个word模板
该方法提供一个word模板文件,数据通过参数替换的方式传入word文件中,灵活性较差,适用于简单的文件导出。需要依赖:docxtemplater、file-saver、jszip-utils、pizzip。
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。
import { saveAs } from "file-saver";
import { asBlob } from "html-docx-js-typescript";
export function downloadWordWithHtmlString(html, name) {
let htmlString = `
Document
${html}
`;
asBlob(htmlString).then((data) => {
saveAs(data, `${name}.docx`);
});
}
使用案例:
word标题
1111
合并单元格
最长的一项
222
222
222
222
1111
合并包括此行在内的下面三行
222
3333
50
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。一个简单的案例是:
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文件形式为:
面是我个人总结的比较常见能用到的功能和配置项:
// 导出文字
1.new Paragraph(text) -> 默认字体样式: 宋体,五号字
2.new Paragraph({
children: [
new TextRun({
text: "我是文字内容",
size: 16, // 对应word中的字体大小8
bold: true, // 是否加粗
underline: {
type: UnderlineType.SINGLE,
color: "#2e32ee",
}, // 下划线类型及颜色
font: {
name: "仿宋", // 只要是word中有的字体类型都可以生效
},
}),
],
indent: {
left: 100,
}, // 离左边距离 类似于margin-left
spacing: {
before: 150,
after: 200,
}, // 离上边和下边的距离 类似于margin-top/bottom
alignment: "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("我是页脚")],
}),
}
下面是一个完整的使用案例:
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文件如下:
本文转载于:
https://juejin.cn/post/7269022955471749131
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。