vue实现导出word文档(含多张图片)

news2025/2/27 8:21:41

一、实现效果  

以填写并导出房屋出租审批表为例,首先填写表格相应内容后,点击" 导出 "按钮实现word文档的导出功能,界面如下所示: 

18f507430db54d04abf5a31280ee715d.gif

最后导出word文档如下所示:

ded5c5f01cc140abb3800fd187172662.jpeg

二、所需插件 

这里使用npm对以下所需依赖进行安装,并在后面封装的js文件(导出word文档主要实现方法)中引入 。

-- 安装 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文档模板 

在导出word之前,需要准备一个word模板文件(按自己所需最后导出的样式),放到该vue项目public文件夹下, 房屋出租审批表模板word样式如下所示:

ed329decadf045c699760052e86b97ce.jpeg

需要填写的部分都被定义为变量或者json对象数组,具体格式如下:

1.  单一变量使用  {  }  包含,例如:

{ user } 、{ area }

2.  json数组格式,则包裹一个循环对象,例如:

 原格式为:

"thinglist": [
      { time :"2022-4-1",thing: "在家"},
      { time :"2022-4-2",thing: "上班"},
 ]

 在模板文件中表示为:

{#thinglist}
    {time}-{thing}
{/thinglist}

如果对象是图片地址时,需要在对象前加上% ,例如:

 原格式为:

imglist:[
	{ imgUrl: "  "},
	{ imgUrl: "  "},
]

 在模板文件中表示为:

{#imglist}
    {%imgUrl}
{/imglist}

四、封装js 文件

这部分主要是实现word文档导出含图片的主要实现方法,包括将图片的url路径转为base64路径、base64转二进制、以及导出图片的处理,可以直接复制粘贴在页面引入使用,具体代码如下: 

import PizZip from 'pizzip'
import docxtemplater from 'docxtemplater'
import JSZipUtils from 'jszip-utils'
import {saveAs} from 'file-saver'
 
/**
 * 将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');
 
	const expressions = require("angular-expressions");
 
	// 读取并获得模板文件的二进制内容
	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数据
			//将base64的数据转为ArrayBuffer
			return base64DataURLToArrayBuffer(chartId);
		}
		opts.getSize = function(img, tagValue, tagName) {
			//自定义指定图像大小
			if(imgSize.hasOwnProperty(tagName)){
				return imgSize[tagName];
			}else{
				return [300, 300];
			}
		}
		// 创建一个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);
	});
}

/**
 * 将图片的url路径转为base64路径
 * 可以用await等待Promise的异步返回
 * @param {Object} imgUrl 图片路径
 */
export function getBase64Sync(imgUrl) {	
	return new Promise(function(resolve, reject) {
		// 一定要设置为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();
			//图片质量
			let quality = 0.8;
			//转成base64
			let dataurl = canvas.toDataURL("image/" + ext, quality);
			//返回
			resolve(dataurl);
		};
	})
}

五、实现导出word文档

1.  首先是前端部分,这里使用ElementPlus进行前端页面实现,代码如下:

<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" >导出</el-button>
		   		 </span>
		      </template>
		   </el-dialog> 
	</div>
</template>

实现过程遇到一个问题:使用el-dialog弹出框时,想固定其在页面居中、距离页面顶部以及底部的固定距离,但是里面的表格内容却超出其显示范围,该如何实现喃?css设置如下:

/* 弹出框居中显示 */
/deep/.el-dialog {
   left: 50%;
   top: 50%;
   transform: translate(-50%, -50%);
   margin: 0px !important;
}
/* 弹出框超出部分滑动 */
/deep/.el-dialog__body {
	 height: 75vh;
	 overflow: hidden;
	 overflow-y: auto;
}

 包括更改el-dialog弹出框头部以及底部区域样式,css设置如下:

/deep/.el-dialog__header {
    width: 100%;
    background-color:#f8f8f8 ;
}
/deep/ .el-dialog__footer {
    width: 100%;
	border-top: 1px #ebebeb solid ;
}

 2.然后在页面内引入封装js里的exportWord以及getBase64Sync方法,data部分定义的是双向绑定填写的内容以及图片地址,考虑到图片可能不知一张,需要循环对其处理转为base64路径,代码如下:

// 引入将图片的url路径转为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 = {
			//控制导出的word图片大小
	          imgurl:[200, 200],
	        };
	        exportWord("/房屋出租审批表.docx", data, "房屋出租审批表.docx", imgSize);
	      }	
  }
}
</script>

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/405242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【SpringBoot+Vue】全网最简单但实用的前后端分离项目实战笔记 - 前端

配套视频地址&#xff1a;https://www.bilibili.com/video/BV1dG4y1T7yp/ 前端笔记 1. node环境 官网&#xff1a;https://nodejs.org 注意&#xff0c;node可以比我稍低&#xff0c;但不要更高 2. 下载vue-admin-template https://panjiachen.gitee.io/vue-element-admin…

HTML表格合并行和列

HTML表格合并行和列1.合并行&#xff1a;rowspan2.合并列&#xff1a;colspan1.合并行&#xff1a;rowspan 在设计表格时&#xff0c;有时我们需要将“横向的N个单元格”或者“纵向的N个单元格”合并成一个单元格&#xff08;类似Word的表格合并&#xff09;&#xff0c;这个时…

2023前端最新高频面试题总结(附答案)

目录 1.vue双向数据绑定的原理&#xff1f; 2.vue的生命周期有哪些 3.v-if 和v-show有什么区别&#xff1f; 4.async await 是什么&#xff1f;它有哪些作用&#xff1f; 5、数组常用的方法&#xff1f;哪些方法会改变原数组&#xff0c;哪些不会 6.什么是原型链&#xf…

前端网页设计必逛的六个宝藏网站(非常值得收藏)

&#x1f389;个人主页&#xff1a;这个昵称我想了20分钟 ✨往期专栏&#xff1a; 【速成之路】jQuery 【SQL server速成之路】 素材网站✨iconfont阿里巴巴矢量图标库  ✨美叶  ✨IconPark  ✨pexels  ✨COLOR  ✨Uigradients✨iconfont阿里巴巴矢量图标库 网站入口…

【微信小程序】使用uni-app——开发首页搜索框导航栏(可同时兼容APP、H5、小程序)

目录 前言 App、H5效果 小程序效果 一、兼容APP、H5的方式 二、兼容小程序 三、实现同时兼容 前言 首页都会提供一个搜索框给到客户&#xff0c;让客户自己去搜索自己想要的内容&#xff0c;这里就需要导航栏&#xff0c;来实现搜索页面的跳转&#xff0c;效果如下 App…

Vue框架教程-从入门到项目实战

创建Vue项目 我们通过vue-cli创建一个vue项目&#xff0c; 在cmd窗口输入 vue ui 进入vue-cli可视化界面&#xff08;如果无效请升级vue-cli版本&#xff09;点击创建&#xff0c;选择一个项目目录输入项目名称和git初始化窗口(可选)选择预设&#xff0c;可以选择手动和预定的…

如何清除全部的定时器

通过 setTimeout() 函数来建立定时器&#xff0c;并通过 clearTimeout() 函数来清除定时器。 let timerTimeout setTimeout(() > {console.log("2222222-----------------------------"); }, 1000); clearTimeout(timerTimeout);通过 setInterval() 函数来建立定…

Node.js error: ERR_OSSL_EVP_UNSUPPORTED

Node.js 17版本的更新日志&#xff1a; 2021-10-19, Version 17.0.0 (Current), BethGriggs Notable Changes ... OpenSSL 3.0 Node.js now includes OpenSSL 3.0, specifically quictls/openssl which provides QUIC support. With OpenSSL 3.0 FIPS support is again availab…

npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

报错起因 在VScode中运行vue项目时&#xff0c;【前提&#xff1a;把项目文件换到另一个网盘存放&#xff0c;存放失败&#xff0c;又重新放回原位置再次运行时】 报错如下&#xff1a; npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查 解决…

C++——WebServer服务器项目

项目场景&#xff1a; C——WebServer服务器编程 项目搭建 &#xff08;1&#xff09;配置虚拟机&#xff0c;下载XShell、Xftp以及windows版本的VScode&#xff1b; &#xff08;2&#xff09;安装SSH&#xff1a; sudo apt install openssh-server&#xff08;3&#xff…

React组件的生命周期函数

文章目录React组件生命周期认识生命周期生命周期函数不常用生命周期函数React组件生命周期 认识生命周期 很多的事物都有从创建到销毁的整个过程&#xff0c;这个过程称之为是生命周期&#xff1b; React组件也有自己的生命周期&#xff0c;了解组件的生命周期可以让我们在最…

controller层,service层,mapper层,entity层的作用与联系。

一. controller层 controller层是用来接受前台数据和返回页面请求信息的&#xff0c;Controller层是不允许直接操作数据库的&#xff01;它就像一个服务员&#xff0c;哪桌客人需要点菜了&#xff0c;就喊一声服务员&#xff01; 对应的&#xff0c;外界需要完成什么样的业务&…

Css 弹性布局(Flex)详细介绍(Flex 属性详解、场景分析)

目录​​​​​​​ 前言 Flex 布局是什么&#xff1f; Flex 简介 Flex 容器属性 Flex 基本使用 场景一 flex-direction 场景二 justify-content align-items flex-wrap 场景三 前言 我们知道&#xff0c;网页展示就好比一个个盒子堆叠在一起&#xff0c;通过调整…

2022版完整版web前端学习路线图(超详细自学路线)

跟着路线图认真坚持学习从前端小白到大神不是梦&#xff0c;0基础看这一篇足矣&#xff01; 学们记得加关注点赞收藏&#xff0c;自学路上不迷糊&#xff01; 零基础小白自学前端路线图速览&#xff1a; 阶段一&#xff1a;核心基础入门 前端计算机常识 ➾ htmlcss基础 ➾ h…

JDBC 连接 MySQL

哈喽~大家好&#xff0c;这次我们来看看 JDBC 如何 连接 MySQL. 目录 一、开头 二、介绍 1、JDBC 的概念 2、JDBC 的功能 3、JDBC 的常用接口和类 三、数据库的创建&#xff08;MySQL&#xff09; 1、连接 MySQL (1)、注册驱动 (2)、获取连接 (3)、获取执行者连接 …

猿创征文|超实用的前端开发工具分享

&#x1f373;作者&#xff1a;贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛\color{#FF0000}{贤蛋 大眼萌 &#xff0c;一名很普通但不想普通的程序媛}贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛&#x1f933; &#x1f64a;语录&#xff1a;多一些不为什么的…

Element UI 及 Element Plus框架

一&#xff0c;何为Element UI 及 Element Plus&#xff1f; 它们是前端框架。它是包含很多有自己风格的组件库。 Element目前有两个版本&#xff1a;element-ui 及 element-plus两个版本。它将HTML的基础控件进行了封装&#xff0c;用户只需要调用这些控件就可以了。而不需要…

实验一 基于CSS+HTML+JS开发简单个人网站

目录&#xff1a; 实验要求 实验代码 1.注册 2.登录 3.主页 4.个人简介 5.我的理想 6.我的生活 7.学习内容 总结 实验要求 实验一 基于CSSHTMLJS开发简单个人网站 实验学时&#xff1a;4 实验类型&#xff1a;设计 一、目的与任务 目的&#xff1a;熟悉在静态网…

Vue3-Pinia的基本使用

什么是Pinia呢&#xff1f; Pina开始于大概2019&#xff0c;是一个状态管理的库&#xff0c;用于跨组件、页面进行状态共享&#xff08;这和Vuex、Redux一样&#xff09;,用起来像组合式API&#xff08;Composition API&#xff09; Pinia和Vuex的区别 PInia的最初是为了探索…

web期末作业网页设计——我的家乡(网页源码)

作品介绍 1.网页作品简介方面 &#xff1a;HTML网站模板。主要有&#xff1a;首页 家乡简介 风景名胜 特色美食 站长介绍 在线调查 等总共 6 个页面html下载。 2.网页作品编辑&#xff1a;此作品为学生个人主页网页设计题材&#xff0c;代码为简单学生水平 htmlcss 布局制作&am…