vue實(shí)現(xiàn)導(dǎo)出word文檔功能實(shí)例(含多張圖片)
一、實(shí)現(xiàn)效果
以填寫(xiě)并導(dǎo)出房屋出租審批表為例,首先填寫(xiě)表格相應(yīng)內(nèi)容后,點(diǎn)擊" 導(dǎo)出 "按鈕實(shí)現(xiàn)word文檔的導(dǎo)出功能,界面如下所示:

最后導(dǎo)出word文檔如下所示:

二、所需插件
這里使用npm對(duì)以下所需依賴(lài)進(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樣式如下所示:

需要填寫(xiě)的部分都被定義為變量或者json對(duì)象數(shù)組,具體格式如下:
1. 單一變量使用 { } 包含,例如:
{ user } 、{ area }2. json數(shù)組格式,則包裹一個(gè)循環(huán)對(duì)象,例如:
原格式為:
"thinglist": [
{ time :"2022-4-1",thing: "在家"},
{ time :"2022-4-2",thing: "上班"},
]在模板文件中表示為:
{#thinglist}
{time}-{thing}
{/thinglist}如果對(duì)象是圖片地址時(shí),需要在對(duì)象前加上% ,例如:
原格式為:
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ù)制粘貼在頁(yè)面引入使用,具體代碼如下:
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í)例對(duì)象
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對(duì)象的zip文件(不是一個(gè)真實(shí)的文件,而是在內(nèi)存中的表示)
let out = doc.getZip().generate({
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
// 將目標(biāo)文件對(duì)象保存為目標(biāo)類(lèi)型的文件,并命名
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;
// 解決跨域問(wèn)題
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)行前端頁(yè)面實(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">填寫(xiě)審批表</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)過(guò)程遇到一個(gè)問(wèn)題:使用el-dialog彈出框時(shí),想固定其在頁(yè)面居中、距離頁(yè)面頂部以及底部的固定距離,但是里面的表格內(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.然后在頁(yè)面內(nèi)引入封裝js里的exportWord以及getBase64Sync方法,data部分定義的是雙向綁定填寫(xiě)的內(nèi)容以及圖片地址,考慮到圖片可能不知一張,需要循環(huán)對(duì)其處理轉(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUE項(xiàng)目axios請(qǐng)求頭更改Content-Type操作
這篇文章主要介紹了VUE項(xiàng)目axios請(qǐng)求頭更改Content-Type操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
vue按住shift鍵多選方式(以element框架的table為例)
這篇文章主要介紹了vue按住shift鍵多選方式(以element框架的table為例),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
Element Table 自適應(yīng)高度的實(shí)現(xiàn)示例
el-table的高度不能適應(yīng)不同電腦的分辨率,也不能跟隨瀏覽器的高度變化而變化的問(wèn)題,本文就來(lái)解決一下Element Table 自適應(yīng)高度,感興趣的可以了解一下2024-07-07
基于Vue3.0開(kāi)發(fā)輕量級(jí)手機(jī)端彈框組件V3Popup的場(chǎng)景分析
這篇文章主要介紹了基于Vue3.0開(kāi)發(fā)輕量級(jí)手機(jī)端彈框組件V3Popup,本文通過(guò)場(chǎng)景分析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
解決vue props傳Array/Object類(lèi)型值,子組件報(bào)錯(cuò)的情況
這篇文章主要介紹了解決vue props傳Array/Object類(lèi)型值,子組件報(bào)錯(cuò)的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
當(dāng)啟動(dòng)vue項(xiàng)目安裝依賴(lài)時(shí)報(bào)錯(cuò)的解決方案
這篇文章主要介紹了當(dāng)啟動(dòng)vue項(xiàng)目安裝依賴(lài)時(shí)報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
Vue2+Elementui?Dialog實(shí)現(xiàn)封裝自定義彈窗組件
在日常的管理系統(tǒng)界面中,我們寫(xiě)的最多的除了列表表格之外,就是各種彈窗組件,本文就來(lái)為大家詳細(xì)介紹一下Vue2如何結(jié)合Elementui?Dialog實(shí)現(xiàn)封裝自定義彈窗組件,希望對(duì)大家有所幫助2023-12-12
基于vue實(shí)現(xiàn)web端超大數(shù)據(jù)量表格的卡頓解決
這篇文章主要介紹了基于vue實(shí)現(xiàn)web端超大數(shù)據(jù)量表格的卡頓解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
詳解mpvue小程序中怎么引入iconfont字體圖標(biāo)
這篇文章主要介紹了詳解mpvue小程序中怎么引入iconfont字體圖標(biāo),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10

