草庐IT

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

薇森 2023-04-21 原文

一、实现效果  

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

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

二、所需插件 

这里使用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样式如下所示:

需要填写的部分都被定义为变量或者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>

 

有关vue实现导出word文档(含多张图片)的更多相关文章

  1. ruby-on-rails - Rails 3 I18 : translation missing: da. datetime.distance_in_words.about_x_hours - 2

    我看到这个错误:translationmissing:da.datetime.distance_in_words.about_x_hours我的语言环境文件:http://pastie.org/2944890我的看法:我已将其添加到我的application.rb中:config.i18n.load_path+=Dir[Rails.root.join('my','locales','*.{rb,yml}').to_s]config.i18n.default_locale=:da如果我删除I18配置,帮助程序会处理英语。更新:我在config/enviorments/devolpment

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  4. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

随机推荐