前言
本文使用前端框架React趣惠。
最近項(xiàng)目遇到一個需求狸棍,需要前端去導(dǎo)出word。百度了一圈后信卡,找到了一個導(dǎo)出word的模版庫隔缀,非常好用。
貼上地址:docxtemplater
借鑒了大佬的圖片導(dǎo)出寫法傍菇,下面貼上地址:
Word導(dǎo)出圖片
功能點(diǎn)
1.導(dǎo)出指定排版的文章
2.導(dǎo)出帶樣式的文字
3.導(dǎo)出圖片
4.導(dǎo)出表格
使用方法
首先講一下在代碼中怎么使用
首先導(dǎo)入第三方庫:
import Docxtemplaterfrom 'docxtemplater'
import PizZipfrom 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs }from 'file-saver'
import ImageModule from "docxtemplater-image-module-free";
直接上代碼(可直接復(fù)制):
/**
*函數(shù)傳入?yún)?shù)說明:filepath:word保存的地址猾瘸,
*wordData:word導(dǎo)出需要的數(shù)據(jù),
*outPath:word導(dǎo)出文件名
* */
generateDocument = (filePath,wordData,outPath) => {
// 讀取并獲得模板文件的二進(jìn)制內(nèi)容丢习,是docxtemplater提供的固定寫法
JSZipUtils.getBinaryContent(filePath, function (error, content) {
// exportTemplate.docx是模板牵触,React寫在public里。我們在導(dǎo)出的時候咐低,會根據(jù)此模板來導(dǎo)出對應(yīng)的數(shù)據(jù)
//圖片導(dǎo)出功能
let opts = {
centered:false,
fileType:"docx",
getSize: (img, tagValue, tagName)=>{
let width =100;
let height =100;
return [width,height];
},
getImage:(tagValue)=>{
/ /圖片base64轉(zhuǎn)字節(jié)數(shù)組
return base64DataURLToArrayBuffer(tagValue)
}
}
let imageModule = new ImageModule(opts);
// 創(chuàng)建一個PizZip實(shí)例揽思,內(nèi)容為模板的內(nèi)容
let zip =new PizZip(content);
// 創(chuàng)建并加載docxtemplater實(shí)例對象
let doc =new Docxtemplater(zip,{modules:[imageModule]}).setData(wordData)
try{
doc.render();
}catch (error) {
function replaceErrors(key, value) {
if (valueinstanceof Error) {
return Object.getOwnPropertyNames(value).reduce(function(error, key) {
error[key] = value[key];
return error;
}, {});
}
return value;
}
if (error.properties && error.properties.errors instanceof Array) {
const errorMessages = error.properties.errors.map(function (error) {
return error.properties.explanation;
}).join("\n");
}
throw error;
}
// 生成一個代表docxtemplater對象的zip文件(不是一個真實(shí)的文件,而是在內(nèi)存中的表示)
let out = doc.getZip().generate({
type : "blob",
mimeType : "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
//Output the document using Data-URI
saveAs(out, outPath);
})
}
//圖片轉(zhuǎn)base64字節(jié)碼
function base64DataURLToArrayBuffer(dataURL) {
const base64Regex =/^data:image\/(png|jpg|svg|jpeg|svg\+xml);base64,/;
if (!base64Regex.test(dataURL)) {
return false;
}
const stringBase64 = dataURL.replace(base64Regex, "");
let binaryString;
if (typeof window !=="undefined") {
binaryString =window.atob(stringBase64);
}else {
binaryString =new Buffer(stringBase64, "base64").toString("binary");
}
const len = binaryString.length;
const bytes =new Uint8Array(len);
for (let i =0; i < len; i++) {
const ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes.buffer;
}
問題說明
JSZipUtils讀取文件操作:
如果你的JSZipUtils讀取不到word路徑见擦,你可以把文件放在public文件夾下钉汗。如果你的項(xiàng)目沒有public文件夾。
1.重新創(chuàng)建項(xiàng)目
2.你可以把word放在src文件夾下任意文字
然后你需要在打包文件中指定word文件上傳后讀取的位置鲤屡,不然打包發(fā)布后服務(wù)器讀取不到文件
具體操作如下:
我的打包文件:
導(dǎo)入一個第三方庫:const CopyWebpackPlugin = require('copy-webpack-plugin');
在module.exports的plugins下加入
new CopyWebpackPlugin([
// {
// from: './src/MP_verify_Jf3BC19m68lvFLyl.txt',
// to: path.join(__dirname, '../dist/')
// },
{
from:'./src/word/1.docx',
to:path.join(__dirname, '../dist/')
},{
from:'./src/word/dz.docx',
to:path.join(__dirname, '../dist/')
},{
from:'./src/word/hx.docx',
to:path.join(__dirname, '../dist/')
},{
from:'./src/reports/x-text-1.html',
to:path.join(__dirname, '../dist/')
}
]),
如圖所示
dist為build打包生成的文件
word模版講解
代碼加載word操作已經(jīng)講完了损痰,下面輪到word模版和數(shù)據(jù)處理了。
首先wordData傳遞的數(shù)據(jù)是object鍵值對類型的數(shù)據(jù)酒来。具體可看官方文檔docxtemplater
1.排版
如果你需要插入固定格式的文字卢未,例如下面這種
第一種模版寫法為:
{-w:p products}{title}{/products}
數(shù)據(jù)格式:products:[{title:'name'}]
第二種模版寫法:
{#products}{.}{/products}
數(shù)據(jù)格式:products:[‘name’]
具體使用那種看你的數(shù)據(jù)格式
2.帶格式的文本
如這種正數(shù)為紅色,負(fù)數(shù)為綠色堰汉。
需要插入xml格式
模版非常簡單辽社,直接加上@就行了,如{@name1}
代碼則需要插入docxXml格式
直接上代碼
static indexInformationToXML = (color,content) =>{
return (
`<w:p>
<w:pPr>
<w:widowControl/>
<w:jc w:val="center"/>
<w:textAlignment w:val="center"/>
<w:rPr>
<w:rFonts w:ascii="楷體"
w:hAnsi="楷體"
w:eastAsia="楷體"
w:cs="楷體"/>
<w:color w:val="000000"/>
<w:sz w:val="20"/>
<w:szCs w:val="20"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"
w:ascii="楷體"
w:hAnsi="楷體"
w:eastAsia="楷體"
w:cs="楷體"/>
<w:sz w:val="20"/>
<w:szCs w:val="20"/>
<w:color w:val="${color}"/>
</w:rPr>
<w:t>${content}</w:t>
</w:r>
</w:p>`
)
}
如果你還想插入更復(fù)雜的格式翘鸭,則需要根據(jù)需要去查看生成好的docx xml模版的代碼滴铅。
這個我們放到最后來說。
3.插入圖片就乓。
插入圖片也很簡單失息。模版寫法:{%image}
不過官方的圖片導(dǎo)出需要收費(fèi),所以我們用了一個免費(fèi)的第三方庫档址。具體寫法前面已經(jīng)給出盹兢,這里就不細(xì)講。說一下導(dǎo)出圖片需要注意的地方守伸。
你傳過去的數(shù)據(jù)中绎秒,圖片必須是base64格式的。
關(guān)于圖片轉(zhuǎn)base64尼摹,下面直接上代碼:
export function anyGetBase64(url,callback) {
let Img =new Image(),dataURL ='';
Img.src = url+'?t='+new Date().valueOf(); // 處理緩存,fix緩存bug,有緩存见芹,瀏覽器會報錯;
Img.setAttribute("crossOrigin", 'Anonymous')// 解決控制臺跨域報錯的問題
return new Promise((resolve, reject)=>{
Img.onload =function () {//要先確保圖片完整獲取到剂娄,這是個異步事件
let canvas =document.createElement("canvas"), //創(chuàng)建canvas元素
width = Img.width, //確保canvas的尺寸和圖片一樣
height = Img.height;
canvas.width = width;
canvas.height = height;
canvas.getContext("2d").drawImage(Img, 0, 0, width, height); //將圖片繪制到canvas中
dataURL = canvas.toDataURL('image/png'); //轉(zhuǎn)換圖片為dataURL
// callback ? callback(dataURL) : null; //調(diào)用回調(diào)函數(shù)
resolve(dataURL)
}
})
}
圖片轉(zhuǎn)base64是異步操作,所以使用的時候邏輯處理最好寫在finally后面玄呛。用法如下:
如果你訪問的是服務(wù)器給的地址阅懦,有可能會報跨域的問題,前后端兩邊都要進(jìn)行跨域配置徘铝。這里就不講跨域操作了耳胎,請自行百度。
4.word導(dǎo)出表格
表格操作還是很常見的惕它,這里講講動態(tài)導(dǎo)出多行表格怕午。
首先還是先看模版寫法,這里的寫法和段落循環(huán)是一致的:
再來看看數(shù)據(jù)格式:
tfProductArray:[{productName:'name'}],
具體就是這樣。
下面是贈送內(nèi)容
圖文混排(打字有點(diǎn)累了淹魄,直接上代碼)
我們還是先來看看模版
knowledges在最上面是為了去掉段落的空行郁惜,空行太多了不好看
因?yàn)閳D片的標(biāo)簽和段落的標(biāo)簽不一致,所以需要把他們分開寫甲锡。錄入數(shù)據(jù)的時候就不能全部錄完段落兆蕉,最后在錄圖片,這樣的話圖片就會在最后缤沦,所以需要在段落中依次錄入
還是貼一下數(shù)據(jù)吧虎韵。數(shù)據(jù)就是這個樣子。
最后說一下如何查看docx格式文件的xml代碼
首先將你生成好的模版后綴名改成zip疚俱,然后通過解壓工具解壓劝术,得到的就是一個文件夾
打開word缩多,里面有個document.xml的文件呆奕,打開文件里面就是xml的代碼了。
好了衬吆,關(guān)于React導(dǎo)出word的知識就講完了梁钾。感謝大家耐心看完,如有問題可私信我逊抡,也可以在評論區(qū)發(fā)布姆泻。