這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 很多時候在工作中會碰到完全由前端導出word文件的需求,因此特地記錄一下比較常用的幾種方式。 一、提供一個word模板 該方法提供一個word模板文件,數據通過參數替換的方式傳入word文件中,靈活性較差,適用於簡單的文件導出。需要依賴: ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
很多時候在工作中會碰到完全由前端導出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文件如下: