JS word下載(含兩種方式:html轉(zhuǎn)word、模板轉(zhuǎn)word)

最近遇到一個需求-->報表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)象

table.png

表格說明:我的表格全是后端返回表頭和表格數(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%"
          ); //表格

類型二、固定模板轉(zhuǎn)word

我沒有實際操作過,不過看下面兩篇參考應(yīng)該足以解決這個需求吧
參考一
參考二

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容