vue實(shí)現(xiàn)導(dǎo)出word文檔功能實(shí)例(含多張圖片)
一、實(shí)現(xiàn)效果
以填寫并導(dǎo)出房屋出租審批表為例,首先填寫表格相應(yīng)內(nèi)容后,點(diǎn)擊" 導(dǎo)出 "按鈕實(shí)現(xiàn)word文檔的導(dǎo)出功能,界面如下所示:
最后導(dǎo)出word文檔如下所示:
二、所需插件
這里使用npm對以下所需依賴進(jìn)行安裝,并在后面封裝的js文件(導(dǎo)出word文檔主要實(shí)現(xiàn)方法)中引入 。
-- 安裝 docxtemplater npm install docxtemplater pizzip --save -- 安裝 jszip-utils npm install jszip-utils --save -- 安裝 jszip npm install jszip --save -- 安裝 FileSaver npm install file-saver --save -- 引入處理圖片的插件1 npm install docxtemplater-image-module-free --save -- 引入處理圖片的插件2 npm install angular-expressions --save
三、word文檔模板
在導(dǎo)出word之前,需要準(zhǔn)備一個(gè)word模板文件(按自己所需最后導(dǎo)出的樣式),放到該vue項(xiàng)目public文件夾下, 房屋出租審批表模板word樣式如下所示:
需要填寫的部分都被定義為變量或者json對象數(shù)組,具體格式如下:
1. 單一變量使用 { } 包含,例如:
{ user } 、{ area }
2. json數(shù)組格式,則包裹一個(gè)循環(huán)對象,例如:
原格式為:
"thinglist": [ { time :"2022-4-1",thing: "在家"}, { time :"2022-4-2",thing: "上班"}, ]
在模板文件中表示為:
{#thinglist} {time}-{thing} {/thinglist}
如果對象是圖片地址時(shí),需要在對象前加上% ,例如:
原格式為:
imglist:[ { imgUrl: " "}, { imgUrl: " "}, ]
在模板文件中表示為:
{#imglist} {%imgUrl} {/imglist}
四、封裝js 文件
這部分主要是實(shí)現(xiàn)word文檔導(dǎo)出含圖片的主要實(shí)現(xiàn)方法,包括將圖片的url路徑轉(zhuǎn)為base64路徑、base64轉(zhuǎn)二進(jìn)制、以及導(dǎo)出圖片的處理,可以直接復(fù)制粘貼在頁面引入使用,具體代碼如下:
import PizZip from 'pizzip' import docxtemplater from 'docxtemplater' import JSZipUtils from 'jszip-utils' import {saveAs} from 'file-saver' /** * 將base64格式的數(shù)據(jù)轉(zhuǎn)為ArrayBuffer * @param {Object} dataURL base64格式的數(shù)據(jù) */ 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; } /** * 導(dǎo)出word,支持圖片 * @param {Object} tempDocxPath 模板文件路徑 * @param {Object} wordData 導(dǎo)出數(shù)據(jù) * @param {Object} fileName 導(dǎo)出文件名 * @param {Object} imgSize 自定義圖片尺寸 */ export const exportWord = (tempDocxPath, wordData, fileName,imgSize) => { //這里要引入處理圖片的插件 var ImageModule = require('docxtemplater-image-module-free'); const expressions = require("angular-expressions"); // 讀取并獲得模板文件的二進(jìn)制內(nèi)容 JSZipUtils.getBinaryContent(tempDocxPath, function(error, content) { if (error) { throw error; } expressions.filters.size = function(input, width, height) { return { data: input, size: [width, height], }; }; function angularParser(tag) { const expr = expressions.compile(tag.replace(/'/g, "'")); return { get(scope) { return expr(scope); }, }; } // 圖片處理 let opts = {} opts = { //圖像是否居中 centered: true }; opts.getImage = (chartId) => { //console.log(chartId);//base64數(shù)據(jù) //將base64的數(shù)據(jù)轉(zhuǎn)為ArrayBuffer return base64DataURLToArrayBuffer(chartId); } opts.getSize = function(img, tagValue, tagName) { //自定義指定圖像大小 if(imgSize.hasOwnProperty(tagName)){ return imgSize[tagName]; }else{ return [300, 300]; } } // 創(chuàng)建一個(gè)PizZip實(shí)例,內(nèi)容為模板的內(nèi)容 let zip = new PizZip(content); // 創(chuàng)建并加載docxtemplater實(shí)例對象 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; } // 生成一個(gè)代表docxtemplater對象的zip文件(不是一個(gè)真實(shí)的文件,而是在內(nèi)存中的表示) let out = doc.getZip().generate({ type: "blob", mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }); // 將目標(biāo)文件對象保存為目標(biāo)類型的文件,并命名 saveAs(out, fileName); }); } /** * 將圖片的url路徑轉(zhuǎn)為base64路徑 * 可以用await等待Promise的異步返回 * @param {Object} imgUrl 圖片路徑 */ export function getBase64Sync(imgUrl) { return new Promise(function(resolve, reject) { // 一定要設(shè)置為let,不然圖片不顯示 let image = new Image(); //圖片地址 image.src = imgUrl; // 解決跨域問題 image.setAttribute("crossOrigin", '*'); // 支持跨域圖片 // 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(); //圖片質(zhì)量 let quality = 0.8; //轉(zhuǎn)成base64 let dataurl = canvas.toDataURL("image/" + ext, quality); //返回 resolve(dataurl); }; }) }
五、實(shí)現(xiàn)導(dǎo)出word文檔
1. 首先是前端部分,這里使用ElementPlus進(jìn)行前端頁面實(shí)現(xiàn),代碼如下:
<template> <div class="page-css"> <el-card class="box-card" shadow="never"> <div class="search-data"> <el-button type="success" @click="editVisible=true">填寫審批表</el-button> </div> </el-card> <el-dialog v-model="editVisible" title="房屋出租審批表" width="50%" custom-class="role-mask"> <div> <div class="tablename"> <h2>房屋出租審批表</h2> </div> <table class="tb" border="1"> <tr> <td height="60">承租人</td> <td colspan="2" width="180"> <input class="inputone" v-model="user"/> </td> <td colspan="2" width="125">房屋面積</td> <td colspan="2" width="175"> <input class="inputtwo" v-model="area" /> 平方米 </td> </tr> <tr> <td height="60">年租金</td> <td colspan="2" > <input class="inputtwo" v-model="annualrent" /> 元/年 </td> <td colspan="2" >出租用途</td> <td colspan="2" > <input class="inputone" v-model="purpose"/> </td> </tr> <tr> <td height="300">房屋平面示意圖</td> <td colspan="6"> <div v-for="(item,index) in imglist"> <img style="width: 60%;" :src="item.imgUrl"/> </div> </td> </tr> </table> </div> <template #footer> <span class="dialog-footer"> <el-button type="info" @click="editVisible=false">取消</el-button> <el-button type="primary" @click="exportWordFile" >導(dǎo)出</el-button> </span> </template> </el-dialog> </div> </template>
實(shí)現(xiàn)過程遇到一個(gè)問題:使用el-dialog彈出框時(shí),想固定其在頁面居中、距離頁面頂部以及底部的固定距離,但是里面的表格內(nèi)容卻超出其顯示范圍,該如何實(shí)現(xiàn)喃?css設(shè)置如下:
/* 彈出框居中顯示 */ /deep/.el-dialog { left: 50%; top: 50%; transform: translate(-50%, -50%); margin: 0px !important; } /* 彈出框超出部分滑動(dòng) */ /deep/.el-dialog__body { height: 75vh; overflow: hidden; overflow-y: auto; }
包括更改el-dialog彈出框頭部以及底部區(qū)域樣式,css設(shè)置如下:
/deep/.el-dialog__header { width: 100%; background-color:#f8f8f8 ; } /deep/ .el-dialog__footer { width: 100%; border-top: 1px #ebebeb solid ; }
2.然后在頁面內(nèi)引入封裝js里的exportWord以及getBase64Sync方法,data部分定義的是雙向綁定填寫的內(nèi)容以及圖片地址,考慮到圖片可能不知一張,需要循環(huán)對其處理轉(zhuǎn)為base64路徑,代碼如下:
// 引入將圖片的url路徑轉(zhuǎn)為base64路徑的方法 for (let i in this.imglist) { this.imglist[i].imgUrl = await getBase64Sync(this.imglist[i].imgUrl) }
完整代碼如下所示:
<script> import {exportWord,getBase64Sync} from '@/assets/js/outword.js' export default { data () { return { editVisible:false, user:'', area:'', annualrent:'', purpose:'', imglist:[ { imgUrl: "https://img2.baidu.com/it/u=2709954499,581919391&fm=253&fmt=auto&app=138&f=JPEG?w=468&h=518" }, { imgUrl: "https://img0.baidu.com/it/u=1462004956,1440895436&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=353" } ] } }, methods:{ async exportWordFile (){ for (let i in this.imglist) { this.imglist[i].imgUrl = await getBase64Sync(this.imglist[i].imgUrl) } let data= { user:this.user, area:this.area, annualrent:this.annualrent, purpose:this.purpose, imglist:this.imglist } let imgSize = { //控制導(dǎo)出的word圖片大小 imgurl:[200, 200], }; exportWord("/房屋出租審批表.docx", data, "房屋出租審批表.docx", imgSize); } } } </script>
總結(jié)
到此這篇關(guān)于vue實(shí)現(xiàn)導(dǎo)出word文檔功能(含多張圖片)的文章就介紹到這了,更多相關(guān)vue導(dǎo)出word文檔內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue?transition組件簡單實(shí)現(xiàn)數(shù)字滾動(dòng)
這篇文章主要為大家介紹了Vue?transition組件簡單實(shí)現(xiàn)數(shù)字滾動(dòng)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09vue3界面使用router及使用watch監(jiān)聽router的改變
vue2中使用router非常簡單,但是vue3中略微有些改變,通過本文講解下他的改變,對vue3?watch監(jiān)聽router相關(guān)知識感興趣的朋友一起看看吧2022-11-11vue 進(jìn)階之實(shí)現(xiàn)父子組件間的傳值
這篇文章主要介紹了vue 進(jìn)階之實(shí)現(xiàn)父子組件間的傳值,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04Vue-Cli中自定義過濾器的實(shí)現(xiàn)代碼
本篇文章主要介紹了Vue-Cli中自定義過濾器的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08詳解Vue+elementUI build打包部署后字體圖標(biāo)丟失問題
這篇文章主要介紹了詳解Vue+elementUI build打包部署后字體圖標(biāo)丟失問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07vue使用Element的Tree樹形控件實(shí)現(xiàn)拖動(dòng)改變節(jié)點(diǎn)順序方式
這篇文章主要介紹了vue使用Element的Tree樹形控件實(shí)現(xiàn)拖動(dòng)改變節(jié)點(diǎn)順序方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12詳解基于vue的移動(dòng)web app頁面緩存解決方案
這篇文章主要介紹了詳解基于vue的移動(dòng)web app頁面緩存解決方案,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-08-08vue中@click綁定事件點(diǎn)擊不生效的原因及解決方案
根據(jù)Vue2.0官方文檔關(guān)于父子組件通訊的原則,父組件通過prop傳遞數(shù)據(jù)給子組件,子組件觸發(fā)事件給父組件,這篇文章主要介紹了vue中@click綁定事件點(diǎn)擊不生效的解決方案,需要的朋友可以參考下2022-12-12