最近遇到一個需求-->報表word下載,網(wǎng)上查了很多資料,發(fā)現(xiàn)其實無外乎就兩種,一種是無模板html節(jié)點直接轉(zhuǎn)word,一種是固定模板填充值的word,恰好我的業(yè)務(wù)報表是動態(tài)的非固定格式的,因此我的處理方式是第一種。
類型一、html無模板轉(zhuǎn)word
框架:vue
插件:jquery + file-saver + html-docx-js
邏輯:通過jquery$('節(jié)點')獲取需要轉(zhuǎn)為word文檔的html節(jié)點,借助file-saver提供的saveAs方法和html-docx-js提供的asBlob方法轉(zhuǎn)換我們設(shè)置好的html即可
核心代碼:
import htmlDocx from "html-docx-js/dist/html-docx";
import { saveAs } from "file-saver";
import $ from "jquery";
function exportWord(dom, fileName) {
setTimeout(() => {
let htmlStr = `
<!DOCTYPE html>
<html lang="en">
<body style="font-family:方正仿宋_GBK;mso-ascii-font-family:'Times New Roman'">
${dom.html()}
</body>
</html>
`;
saveAs( htmlDocx.asBlob(htmlStr, { orientation: "landscape" }), fileName );
}, 200);
}
如果你需要轉(zhuǎn)換的html不涉及圖片(包括echarts),那么上面的轉(zhuǎn)換就已經(jīng)完全可以了,另外fileName的后綴建議使用‘.docx’而不是‘.doc’
如果有圖片(包括echarts)需要轉(zhuǎn)換為word,我們需要用線上圖片替代本地的圖片,這樣圖片才能完美兼容Microsoft Office Word和WPC Office兩個軟件 ,如果不替代的話Microsoft Office Word不兼容、WPC Office部分版本不兼容,關(guān)于本地圖片不兼容的問題,個人猜測是因為base64 不能完美兼容word軟件,但是二進制可以做到完美兼容。
線上替代本地圖片具體操作邏輯如下:
第一步:通過Jquery方法.clone()復(fù)制需要轉(zhuǎn)換為word的html節(jié)點 (注意:我們后續(xù)的一系列都是操作的此節(jié)點)
第二步:獲取圖片節(jié)點,將圖片轉(zhuǎn)換為base64上傳至服務(wù)器 (注意:echarts可直接通過自帶的方法.getConnectedDataURL({ type: "png" })獲取base64)
第三步:獲取圖片線上地址
第四步:創(chuàng)建節(jié)點<img style="margin:0 auto" width="600" src="'+ 圖片的線上地址 +" />(注意:img的src必須是全路徑?。。?/strong> 所以如果接口返回的地址不是全路徑,需要自行拼接為全路徑)
第五步:通過jquery方法.html()將第四步創(chuàng)建好的節(jié)點去替換之前第二步獲取到的圖片節(jié)點至此圖片替換就全部完成了
代碼如下:(下面例中圖片是echarts,如果是img自行百度如何轉(zhuǎn)換base64哦)
import htmlDocx from "html-docx-js/dist/html-docx";
import { saveAs } from "file-saver";
import $ from "jquery";
function exportWord(dom, fileName) {
setTimeout(async() => {
// 1、復(fù)制
let DOM=dom.clone();
// 2、獲取echarts節(jié)點
const echart = DOM.find(".my-echarts");
for (let i = 0; i < echart.length; i++) {
var curEchart = this.$echarts.getInstanceByDom(echart[i]);
if (curEchart) {
//獲取base64
var base = curEchart.getConnectedDataURL({ type: "png" });
//base64轉(zhuǎn)File(這塊需不需要轉(zhuǎn)看項目的后端,因為我的后端作了上傳接口固定規(guī)定,只能傳file對象,所以需要base64轉(zhuǎn)file)
var file = this.dataURLtoFile(base, "test.png");
console.log(file);
//上傳并獲取線上地址
let { data } = await apiUploadFormData(file); //接口上傳
console.log(123123, data);
//創(chuàng)建新節(jié)點
var _img = $(
'<img style="margin:0 auto" width="600" src="' + data.url + '"/>'
);
//替換
$(echart[i]).html(_img);
}
}
let htmlStr = `
<!DOCTYPE html>
<html lang="en">
<body style="font-family:方正仿宋_GBK;mso-ascii-font-family:'Times New Roman'">
${DOM.html()}
</body>
</html>
`;
saveAs( htmlDocx.asBlob(htmlStr, { orientation: "landscape" }), fileName );
}, 200);
}
//base64轉(zhuǎn)File
function dataURLtoFile(dataurl, fileName) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], fileName, { type: mime });
},
//圖片上傳接口請求處理(在這里不能使用,相信每個小可愛都看得懂這塊想表達啥)
import request from '@/utils/request'
export const apiUploadFormData = (file) => {
let formData = new FormData()
formData.append("file", file);
return request.post('圖片上傳接口地址',formData)
}
以上,Html轉(zhuǎn)word就全部完成了
題外話一:關(guān)于word 個性化樣式
如果想設(shè)置個性化的樣式,建議使用jquery方法
.find()查找節(jié)點直接添加style屬性.attr('style','樣式')設(shè)置,即找到需要操作的dom元素,dom添加行內(nèi)樣式即可,注意:添加行內(nèi)樣式時,需要注意代碼執(zhí)行順序,避免有些行內(nèi)樣式被覆蓋
// dom是需要轉(zhuǎn)換為word的html節(jié)點
dom.find('.title').attr('style','font-weight: bold;margin: 10px 0;text-align:center')
題外話二:關(guān)于word table樣式(按需取用)
我使用的ui庫是element,然后我的表格是基于el-table二次封裝好的組件,它在被轉(zhuǎn)換成js原生table表格后,不僅樣式很丑,而且thead和tbody還被識別成了2個表格,雖然視覺上不影響,但是還是存在thead和tbody單元格無法對齊的現(xiàn)象

表格說明:我的表格全是后端返回表頭和表格數(shù)據(jù)的動態(tài)表格
解決方法:復(fù)制原thead內(nèi)容,插入到tbody成為tbody第一行,然后刪除thead
//處理所有表格
dom.find(".my-global-table").each((key, value) => {
//處理單個表格
//1、獲取表格thead內(nèi)容
let thArr = [];
dom
.find(`.my-global-table:eq(${key}) th div.cell`)
.each((_key, value) => {
let m = value.innerHTML;
let str = `<td><div class="cell">${m}</div></td>`;
thArr.push(str);
});
let thStr = `<tr>${thArr.join("")}</tr>`;
//2、插入新表頭
if(thArr.length!==0){
$(thStr).insertBefore(
dom.find(`.my-global-table:eq(${key}) tr:eq(1)`)
);
}
//3、刪除舊表頭
dom
.find(`.my-global-table:eq(${key}) .el-table__header-wrapper`)
.remove();
//4、如果表格本身有表頭但是無數(shù)據(jù),我們需要重新添加表頭回去
let Length = dom.find(`.my-global-table:eq(${key}) tr`).length;
if (Length == 0 && thArr.length!==0) {
dom
.find(`.my-global-table:eq(${key}) tbody`)
.html(`<tbody>${thStr}</tbody>`);
}
});
console.log(8888, dom);
//統(tǒng)一設(shè)置所有表格樣式
dom
.find(".the-text-table .my-global-table")
.attr("style", "text-align:center;margin:0 auto;width:80%"); //表格
dom
.find(".my-global-table tr")
.attr(
"style",
"height:44px;vertical-align:middle;padding-left:4px;width:80%"
); //表格