vue3中html導(dǎo)出word文檔實踐

其實在調(diào)研之初是知道幾個插件的讼撒,但大部分都不在維護(hù)了且導(dǎo)出效果不理想炎滞。
本文主要使用docxtemplater導(dǎo)出word文檔并記錄以下詳細(xì)步驟震嫉。
實現(xiàn)效果:
原始頁面是有一個列表和兩個echarts圖表


企業(yè)微信截圖_17262755197124.png

導(dǎo)出的word效果:


企業(yè)微信截圖_17262756411764.png

其中列表是可修改的,echarts是兩張圖片不可修改。
詳細(xì)實現(xiàn)步驟
1.安裝依賴

pnpm install  docxtemplater pizzip --save  
pnpm install  jszip-utils --save
pnpm install  jszip --save   
pnpm install  file-saver --save  
pnpm install  docxtemplater-image-module-free

docxtemplater:這個插件可以通過預(yù)先寫好的word官硝,excel等文件模板生成對應(yīng)帶數(shù)據(jù)的文件
pizzip:這個插件用來創(chuàng)建朋魔,讀取或編輯.zip的文件(同步的,還有一個插件是jszip拓售,異步的)
jszip-utils:與jszip/pizzip一起使用哨苛,jszip-utils 提供一個getBinaryContent(path, data)接口亿蒸,path即是文件的 路徑,支持AJAX get請求盆色,data為讀取的文件內(nèi)容。
file-saver:適合在客戶端生成文件的工具,它提供的接口saveAs(blob, "1.docx")將會使用到,方便我們保存 文件抖拴。
docxtemplater-image-module-free:導(dǎo)出圖片的話需要這個插件

2.創(chuàng)建word模板文件
創(chuàng)建名為test的word模板洒放,放至項目的public文件夾內(nèi):public/test.docx


企業(yè)微信截圖_17262770268607.png
  1. docxtemplater 語法

{%img} 圖片
{#list}{/list} 循環(huán)惨好、if判斷
{#list}{/list}{^list}{/list} if else
{str} 文字

這一步將說明如何在test.docx內(nèi)編寫模板


企業(yè)微信截圖_1726277127573.png

列表部分需要自己先在word內(nèi)插入一張表格回论,然后表頭自己定義好。如果有什么特殊樣式自己都可以在word內(nèi)提前編輯好谱净。
列表數(shù)據(jù)對應(yīng)字段是logList李请,列表循環(huán)以{#logList}開始白翻,又以{/logList}結(jié)尾。注意這里的所有字段都要與代碼傳入的一致。{%leftImage}和{%rightImage}就是那兩張echarts圖所在的位置纯续。

4.代碼實現(xiàn)部分

import JSZipUtils from "jszip-utils";
import docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import saveAs from "file-saver"
import ImageModule from "docxtemplater-image-module-free"

由于需要拿到echarts相關(guān)dom所以需要定義ref并綁定

  <div class="h-400px flex">
        <LineChart
          id="item0"
          :options="chatOptions"
          height="400px"
          width="100%"
          ref="leftChartRef"
          class="bg-[var(--el-bg-color-overlay)]"/>
        <LineChart
          id="item1"
          :options="chatOptions2"
          height="400px"
          width="100%"
          ref="rightChartRef"
          class="bg-[var(--el-bg-color-overlay)]"/>
      </div>
const leftChartRef = ref(HTMLDivElement)
const leftImage = ref(null) //存儲圖片資源

const rightChartRef = ref(HTMLDivElement)
const rightImage = ref(null)

const logList = ref<LogPageVO[]>(); //接口獲取的表格數(shù)據(jù)

導(dǎo)出按鈕點擊和調(diào)用的相關(guān)方法

//導(dǎo)出點擊
const exportWordClick = ()=> {
  leftImage.value = leftChartRef.value.getChartsImage();
  rightImage.value = rightChartRef.value.getChartsImage();
  let docxData= {
    logList: logList.value //列表數(shù)據(jù),類似于[{name:'系統(tǒng)管理員'逢唤,ip:'114.0.57....'}]
  }
  downLoadDoc('public/test.docx',docxData,"下載測試模板")
}


const downLoadDoc = (demoUrl, docxData, fileName)=>{
  // 讀取并獲得模板文件的二進(jìn)制內(nèi)容
  JSZipUtils.getBinaryContent(
    demoUrl,
    function (error, content) {
      // 拋出異常
      if (error) {
        throw error;
      }

      // 圖片處理
      let opts = { centered: false }
      opts.getImage = chartId => {
        return base64DataURLToArrayBuffer(chartId)
      }
      opts.getSize =  (img, tagValue, tagName)=> {
        //自定義指定圖像大小魔慷,此處可動態(tài)調(diào)試各別圖片的大小
        return [500, 300]  //例子:寬500px  高度300px
      }

      // 創(chuàng)建一個PizZip實例邀摆,內(nèi)容為模板的內(nèi)容
      let zip = new PizZip(content);
      // 創(chuàng)建并加載docxtemplater實例對象
      let doc = new docxtemplater().loadZip(zip).attachModule(new ImageModule(opts));
      // 去除未定義值所顯示的undefined
      doc.setOptions({
        nullGetter: function () {
          return "";
        }
      });
      // 設(shè)置模板變量的值贞盯,對象的鍵需要和模板上的變量名一致,值就是你要放在模板上的值
      doc.setData({
        ...docxData,
        leftImage: leftImage.value,
        rightImage: rightImage.value
      });

      try {
        // 用模板變量的值替換所有模板變量
        doc.render();
      } catch (error) {
        // 拋出異常
        let e = {
          message: error.message,
          name: error.name,
          stack: error.stack,
          properties: error.properties,
        };
        console.log(JSON.stringify({ error: e }));
        throw error;
      }

      // 生成一個代表docxtemplater對象的zip文件(不是一個真實的文件话侧,而是在內(nèi)存中的表示)
      let out = doc.getZip().generate({
        type: "blob",
        mimeType:
          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      });
      // 將目標(biāo)文件對象保存為目標(biāo)類型的文件栗精,并命名
      saveAs(out, fileName);
    }
  );
}

const base64DataURLToArrayBuffer = (dataURL)=> {
  const base64Regex = /^data:image\/(png|jpg|svg|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
}

這里額外需要說的一點是導(dǎo)出點擊方法內(nèi)

  leftImage.value = leftChartRef.value.getChartsImage();
  rightImage.value = rightChartRef.value.getChartsImage();

這也是我們?yōu)槭裁炊xref的原因,因為需要將echarts圖片轉(zhuǎn)為base64格式瞻鹏。
echarts子組件內(nèi)

//將echarts圖片轉(zhuǎn)為base64格式
const getChartsImage = ()=>{
  return chart.value.getDataURL({
    pixelRatio: 2, // 導(dǎo)出的圖片分辨率比例术羔,默認(rèn)為 1。
    backgroundColor: 'transparent' // 導(dǎo)出的圖片背景色乙漓,默認(rèn)使用 option 里的 backgroundColor
  })
}

/**
 * 當(dāng)使用 <script setup> 寫法會導(dǎo)致父組件無法訪問到子組件中的屬性和方法级历。
 * 使用 <script setup> 的組件,想要讓父組件訪問到它的屬性和方法需要借助與defineExpose來指定需要暴露給父組件的屬性叭披。
 * */
defineExpose({
  getChartsImage
})

5.結(jié)語
至此寥殖,我們在vue3中使用docxtemplater導(dǎo)出word文檔的實踐就告一段落了。文章從介紹docxtemplater基本使用語法涩蜘,再到完整的代碼示例實現(xiàn)表格和echarts圖表的導(dǎo)出功能嚼贡。如果有什么想法也可以繼續(xù)留言交流,感謝同诫!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粤策,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子误窖,更是在濱河造成了極大的恐慌叮盘,老刑警劉巖秩贰,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異柔吼,居然都是意外死亡毒费,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門愈魏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來觅玻,“玉大人,你說我怎么就攤上這事培漏∠澹” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵牌柄,是天一觀的道長桩匪。 經(jīng)常有香客問我,道長友鼻,這世上最難降的妖魔是什么傻昙? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮彩扔,結(jié)果婚禮上妆档,老公的妹妹穿的比我還像新娘。我一直安慰自己虫碉,他們只是感情好贾惦,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敦捧,像睡著了一般须板。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兢卵,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天习瑰,我揣著相機與錄音,去河邊找鬼秽荤。 笑死甜奄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窃款。 我是一名探鬼主播课兄,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晨继!你這毒婦竟也來了烟阐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜒茄,沒想到半個月后唉擂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡扩淀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了啤挎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驻谆。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖庆聘,靈堂內(nèi)的尸體忽然破棺而出胜臊,到底是詐尸還是另有隱情,我是刑警寧澤伙判,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布象对,位于F島的核電站,受9級特大地震影響宴抚,放射性物質(zhì)發(fā)生泄漏勒魔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一菇曲、第九天 我趴在偏房一處隱蔽的房頂上張望冠绢。 院中可真熱鬧,春花似錦常潮、人聲如沸弟胀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孵户。三九已至,卻和暖如春岔留,著一層夾襖步出監(jiān)牢的瞬間夏哭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工献联, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留方庭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓酱固,卻偏偏與公主長得像械念,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子运悲,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容