这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
很多时候在工作中会碰到完全由前端导出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 = ` <!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`); }); }
使用案例:
<div ref="word"> <h3 style="text-align: center">word标题</h3> <table border="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。一个简单的案例是:
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
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容