vue + docxtemplater 導出 word 文檔

一、痛點

word 導出 這種功能其實之前都是后端實現的,但最近有個項目沒得后端。所以研究下前端導出。
ps: 前端還可以導出 pdf,但是其分頁問題需要話精力去計算才可能實現,并且都不是很完善。可參考之前的文章:利用 html2canvas 和 jspdf 導出 echarts ( html頁面 )為pdf

二、依賴安裝

// 實現word下載的主要三方庫
npm install docxtemplater pizzip  --save// 文件操作;大佬們可以不需要,自己用fs、path等模塊實現
npm install jszip jszip-utils --save // 文件存儲
npm install file-saver --save// 圖片處理模塊,沒有圖片需求可以不裝
npm install docxtemplater-image-module-free  --save

三、創建導出word的公用方法 exportWord.js

ps:這個方法大同小異,網上很多

import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'// 將圖片地址轉為base64,導出word圖片只能是base64
export function getBase64Sync(imgUrl) {return new Promise(function (resolve, reject) {// 一定要設置為let,不然圖片不顯示let image = new Image();// 解決跨域問題image.crossOrigin = 'anonymous';//圖片地址image.src = imgUrl;// image.onload為異步加載image.onload = function () {let canvas = document.createElement('canvas');canvas.width = image.width;canvas.height = image.height;let context = canvas.getContext('2d');context.drawImage(image, 0, 0, image.width, image.height);//圖片后綴名let ext = image.src.substring(image.src.lastIndexOf('.') + 1).toLowerCase();//圖片質量let quality = 0.8;//轉成base64let dataurl = canvas.toDataURL('image/' + ext, quality);//返回resolve(dataurl);};});
}
/*** 將base64格式的數據轉為ArrayBuffer* @param {Object} dataURL base64格式的數據*/
function base64DataURLToArrayBuffer(dataURL) {const base64Regex = /^data:image\/(png|jpg|jpeg|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;
}/*** 導出word,支持圖片* @param {Object} tempDocxPath 模板文件路徑* @param {Object} wordData 導出數據* @param {Object} fileName 導出文件名* @param {Object} imgSize 預留,自定義圖片尺寸 => 暫沒使用*/
export const exportWord = (tempDocxPath, wordData, fileName, imgSize) => {// 這里要引入處理圖片的插件var ImageModule = require('docxtemplater-image-module-free');JSZipUtils.getBinaryContent(tempDocxPath, function (error, content) {if (error) {throw error;}// 圖片處理let opts = {};opts = {centered: true, //圖像是否居中,true:在word中圖片居中getImage: (chartId) => { // 將base64轉成ArrayBufferreturn base64DataURLToArrayBuffer(chartId);},//自定義指定圖像大小,此處可動態調試各別圖片的大小getSize: (img, tagValue, tagName) => {// tagName 是指我們自己定義圖片使用的字段名,如path、url等// if (tagName === 'imgurl') return [700, 350]; //設置圖片寬高,tagName :傳入的變量// return [200, 150]; if (Object.prototype.hasOwnProperty.call(imgSize, tagName)) {return imgSize[tagName];} else {return [150, 150];}}};// 創建一個PizZip實例,內容為模板的內容let zip = new PizZip(content);// 創建并加載docxtemplater實例對象let doc = new Docxtemplater();doc.attachModule(new ImageModule(opts));doc.loadZip(zip);// 設置模板變量的值doc.setData(wordData);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文件(不是一個真實的文件,而是在內存中的表示)let out = doc.getZip().generate({type: 'blob',mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'});// 將目標文件對象保存為目標類型的文件,并命名saveAs(out, fileName);});
}

四、組件中調用

前幾章都是基礎,調用才是重點。

1. 創建模板

導出word其實就是解析我們提供的模板,然后將對應字段填入,最新進行導出即可。所以,模板 至關重要

  • 創建.docx文件
    該文件只能直接創建為docx 或者 另存為docx ;不能直接修改后綴名。
  • vue2 將模板放在static文件下;vue3 將模板放在public文件下
2. 模板語法

在這里插入圖片描述

  • 語法用 { } 即可
  • 普通字段直接填入字段名即可
  • 如果字段是圖片地址,需要加上 %,例如:
{#imgList} {%pathUrl}
{/imgList}

踩坑:圖片這里我一直報錯‘%imgUrl’,最后發現必須要換行寫,而其他數組可以在一行寫。

  • 遍歷列表 以 {#list} 開頭 … 列表元素字段名 … {/list} 結尾
list: [{name:'張三', age:'18'},{name:'李四', age:'28'}
]

模板: 在這里插入圖片描述
導出實際結果:
在這里插入圖片描述

3. 組件調用
<template><div><!-- 頁面只有一個echarts 和 導出按鈕 --><div id="myChart6" :style="{ width: '800px', height: '800px' }"></div><el-button type="primary" @click="exprodWord">導出word</el-button></div>
</template><script>import * as echarts from 'echarts';import { onMounted } from 'vue'import { getBase64Sync, exportWord } from './exportFile'export default {name: 'WordTemplate',setup () {let myChartDom = null;const wordData = {title: '環境工業風險審核報告',des: '對于需要判斷顯示的要用{#isProblem}開始,{/isProblem}結束,isProblem的類型是Boolean,true的時候是顯示。如下圖,isFull==true的時候,才顯示下面這句話',userList: [{indexNo: 1,name: '張三',age: '18',address: '上海',imgList: [{url: 'https://i.postimg.cc/qqcRNJ1y/b3c2e029c5deda297e29680e26a5c48c.jpg'},{url: 'https://i.postimg.cc/9Q5b3J7k/797c9c2bbf47b1ad4632670e508e0d5d.jpg'}],status: 1,},{indexNo: 2,name: '李四',age: '28',address: '四川',imgList: [],status: 1,},{indexNo: 3,name: '王五',age: '38',address: '北京',imgList: [],status: 0,},{indexNo: 4,name: '張柳',age: '48',address: '成都',imgList: [],status: 0,}]}const exprodWord = async () => { const chartPath = getChartImg(); // 獲取到echarts的圖片地址const renderData = JSON.parse(JSON.stringify(wordData))renderData.chartPath = chartPath// 將圖片轉成base64是異步操作,需要等待圖片base64返回,所以使用Promise.allrenderData.userList = await Promise.all(renderData.userList.map(async item => {return {...item,imgList: await Promise.all(item.imgList.map(async em => {return {...em,path: await getBase64Sync(em.url)}})) };}))let imgSize = {imgurl: [200, 200], // 定義圖片字段名為 'imgurl' 的尺寸, 該實例中沒有圖片字段名是imgurl,所以不生效chartPath: [1000, 800] // 定義圖片字段名為 'chartPath' 的尺寸, 即該實例中的echarts圖片// ... 更多}console.log('------------renderData', renderData)exportWord('template.docx', renderData, '環境工業風險審核報告.docx', imgSize)}// 基于準備好的dom,初始化echarts實例const initChart = () => {myChartDom = echarts.init(document.getElementById('myChart6'));// 繪制圖表myChartDom.setOption({title: {text: 'ECharts 入門示例'},tooltip: {},xAxis: {data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子']},yAxis: {},series: [{name: '銷量',type: 'bar',data: [5, 20, 36, 10, 10, 20]}]});}// 獲取圖表base64圖const getChartImg = () => {return myChartDom.getDataURL({pixelRatio: 2, // 解決模糊backgroundColor: '#fff'});}onMounted(() => {initChart()})return {exprodWord}}}
</script><style lang='scss' scoped>
</style>

注意:導出操作可能涉及異步操作,請多使用 Promise.all、nextTick等異步方法,盡量少使用setTimeout

五、導出word 結果

在這里插入圖片描述



文章僅為本人學習過程的一個記錄,僅供參考,如有問題,歡迎指出!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/165096.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/165096.shtml
英文地址,請注明出處:http://en.pswp.cn/news/165096.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MIT6.824-Raft筆記:腦裂、Majority Vote(過半投票/過半選舉)

本部分主要是問題引入&#xff0c;以及給出一個解決方案 1 腦裂&#xff08;Split Brain&#xff09; replication system的共同點&#xff1a;單點 前面幾個容錯特性&#xff08;fault-tolerant&#xff09;的系統&#xff0c;有一個共同的特點。 MapReduce復制了計算&…

JavaScript框架 Angular、React、Vue.js 的全棧解決方案比較

在 Web 開發領域&#xff0c;JavaScript 提供大量技術棧可供選擇。其中最典型的三套組合&#xff0c;分別是 MERN、MEAN 和 MEVN。前端框架&#xff08;React、Angular 和 Vue&#xff09;進行簡化比較。 MERN 技術棧詳解 MERN 技術棧包含四大具體組件&#xff1a; MongoDB&am…

藍橋杯物聯網競賽_STM32L071_3_Oled顯示

地位&#xff1a; 對于任何一門編程語言的學習&#xff0c;print函數毫無疑問是一種最好的調試手段&#xff0c;調試者不僅能通過它獲取程序變量的運行狀態而且通過對其合理使用獲取程序的運行流程&#xff0c;更能通過關鍵變量的輸出幫你驗證推理的正確與否&#xff0c;樸素的…

常見網絡安全防護

1 阻斷服務攻擊&#xff08;DOS&#xff09; 阻斷服務攻擊&#xff0c;想辦法目標網絡資源用盡變種&#xff1a;分布式阻斷服務攻擊 影響&#xff1a; 寬帶消耗性&#xff08;消耗目標的帶寬&#xff09;資源消耗型&#xff08;消耗目標的計算資源&#xff09; 解決方案&am…

人工智能對網絡安全的影響越來越大

如果問當前IT行業最熱門的話題是什么&#xff0c;很少有人會回答除了人工智能&#xff08;AI&#xff09;之外的任何話題。 在不到 12 個月的時間里&#xff0c;人工智能已經從一項只有 IT 專業人員才能理解的技術發展成為從小學生到作家、程序員和藝術家的每個人都使用的工具…

MySQL索引事務基礎

目錄 1. 索引 1.1索引的概念 1.2索引的特點 1.3 索引的使用場景 1.4索引的使用 1.4.1查看索引 1.4.2創建索引 1.4.3刪除索引 1.5索引保存的數據結構 2.事務 2.1經典例子 2.2事務的概念 2.3事務的使用 2.4事務的4個核心特性 2.5事務的并發問題 2.5.1臟讀 2.5.2不可…

Python + Docker 還是 Rust + WebAssembly?

在不斷發展的技術世界中&#xff0c;由大語言模型驅動的應用程序&#xff0c;通常被稱為“LLM 應用”&#xff0c;已成為各種行業技術創新背后的驅動力。隨著這些應用程序的普及&#xff0c;用戶需求的大量涌入對底層基礎設施的性能、安全性和可靠性提出了新的挑戰。 Python 和…

Java項目如何打包成Jar(最簡單)

最簡單的辦法&#xff0c;使用Maven插件&#xff08;idea自帶&#xff09; 1.選擇需要打包的mudule&#xff0c;點擊idea右側的maven插件 2.clean操作 3.選擇需要的其他mudule&#xff0c;進行install操作&#xff08;如果有&#xff09; 4.再次選擇需要打包的module&#…

Vue.observable 是什么

Observable 翻譯過來我們可以理解成可觀察的 Vue.js2.6 新增 Vue.observable&#xff0c;讓一個對象變成響應式數據。Vue 內部會用它來處理 data 函數返回的對象 。 返回的對象可以直接用于渲染函數和計算屬性內&#xff0c;并且會在發生變更時觸發相應的更新。也可以作為最小化…

Git的指令

Git 各平臺安裝包下載地址為&#xff1a;http://git-scm.com/downloads Ubuntu Git 安裝命令為&#xff1a; $ apt-get install git用戶信息 配置個人的用戶名稱和電子郵件地址&#xff1a; $ git config --global user.name "runoob" $ git config --global user.…

Python----類對象和實例對象

目錄 一.類和類的實例 二.類屬性和實例屬性 三.私有屬性和公有屬性 四.靜態方法和類方法 五.__init__方法&#xff0c;__new__方法和__del__方法&#xff1a; 六.私有方法和公有方法 七.方法的重載 八.方法的繼承 九.方法的重寫 十.對象的特殊方法 十一.對象的引用&a…

軟件開發模式開源和閉源的優劣之爭

開源和閉源&#xff0c;兩種截然不同的開發模式&#xff0c;對于大模型的發展有著重要影響。開源讓技術共享&#xff0c;吸引了眾多人才加入&#xff0c;推動了大模的創新。而閉源則保護了商業利益和技術優勢&#xff0c;為大模型的商業應用提供了更好的保障。 開源與閉源軟件的…

基于命令行模式設計退款請求處理

前言 這篇文章的業務背景是基于我的另一篇文章: 對接蘋果支付退款退單接口-CSDN博客 然后就是說設計模式是很開放的東西,可能我覺得合適,你可能覺得不合適,這里只是做下討論,沒有一定要各位同意的意思.... 相關圖文件 這里我先把相關的圖文件放上來,可能看著會比較清晰點 代碼邏…

sql之left join、right join、inner join的區別

sql之left join、right join、inner join的區別 left join(左聯接) 返回包括左表中的所有記錄和右表中聯結字段相等的記錄 right join(右聯接) 返回包括右表中的所有記錄和左表中聯結字段相等的記錄 inner join(等值連接) 只返回兩個表中聯結字段相等的行 舉例如下&#xff1…

Web服務器(go net/http) 處理Get、Post請求

大家好 我是寸鐵&#x1f44a; 總結了一篇Go Web服務器(go net/http) 處理Get、Post請求的文章? 喜歡的小伙伴可以點點關注 &#x1f49d; 前言 go http請求如何編寫簡單的函數去拿到前端的請求(Get和Post) 服務器(后端)接收到請求后&#xff0c;又是怎么處理請求&#xff0c…

【限時免費】20天拿下華為OD筆試之【前綴和】2023B-尋找連續區間【歐弟算法】全網注釋最詳細分類最全的華為OD真題題解

文章目錄 題目描述與示例題目描述輸入描述輸出描述示例一輸入輸出說明 示例二輸入輸出 解題思路代碼PythonJavaC時空復雜度 華為OD算法/大廠面試高頻題算法練習沖刺訓練 題目描述與示例 題目描述 給定一個含有N個正整數的數組&#xff0c;求出有多少個連續區間&#xff08;包…

【網絡奇緣】- 計算機網絡|分層結構|ISO模型

&#x1f308;個人主頁: Aileen_0v0&#x1f525;系列專欄: 一見傾心,再見傾城 --- 計算機網絡~&#x1f4ab;個人格言:"沒有羅馬,那就自己創造羅馬~" 目錄 計算機網絡分層結構 OSI參考模型 OSI模型起源 失敗原因: OSI模型組成 協議的作用 &#x1f4dd;全文…

二十四、RestClient操作文檔

目錄 一、新增文檔 1、編寫測試代碼 二、查詢文檔 1、編寫測試代碼 三、刪除文檔 1、編寫測試代碼 四、修改文檔 1、編寫測試代碼 五、批量導入文檔 批量查詢 一、新增文檔 1、編寫測試代碼 SpringBootTest public class HotelDocumentTest {private RestHighLevelC…

【棧】不同字符的最小子序列

題目&#xff1a; /*** 思路&#xff1a;棧,使用數組記錄每個字母出現的次數&#xff0c;再用一個數組標記字符是否在棧中* 遍歷棧&#xff0c;存儲字符時比較棧頂字符&#xff0c;若小于棧頂字符并且后面有重復的字符則* 棧頂元素出棧&#xff0c;否則入棧。** au…

PS 注釋工具 基礎使用方法講解

好 上文PS 顏色取樣器&標尺工具 基本使用講解中 我們講了 顏色取樣器和標尺工具的基本用法 下面我們來看一下 注釋工具 這個 主要是后面 比較大的作品 可能不是我們一個人取設計 團隊作圖 就需要用到它 選擇 注釋工具 后 我們隨便點擊圖像任何一個位置 右側就會出現一個輸…