目录
- 一、后台(pc端,vue2)
- 1. dialog对话框被黑色蒙层盖住
- 2. 将前端表格导出为word文档
- 3. 在线查看、下载 .docx、.doc、.pdf文档
一、后台(pc端,vue2)
1. dialog对话框被黑色蒙层盖住
问题: dialog被黑色蒙层盖住(如下图),我希望dialog背景是黑色蒙层,但是dialog对话框不被盖住
解决办法: 给el-dialog添加如下两个属性
<el-dialog :modal-append-to-body="false" :append-to-body="true">
添加后效果如下:
2. 将前端表格导出为word文档
需求:
- 前端独立完成
- 将审批通过的数据,按照班级分类打印出通过人员名单
- word文档的名字可以指定(前端人员传参决定),例如传参为“入团积极分子审查表”
- word文档的内容标题为动态的,是各期活动名字,例如“入团申请第十一期”
- word文档的后缀为.docx
效果图如下:
解决方法:
一. 参考文档
- vue3实现包含表格的Word文件导出
- Vue中前端导出word文件
二. 具体方法
- 安装第三方包:
- vue3下载包参考版本
"dependencies": {"angular-expressions": "^1.2.1","docx-preview": "^0.3.2","docxtemplater": "^3.49.1","docxtemplater-image-module-free": "^1.1.1","file-saver": "^2.0.5","lodash": "^4.17.21","pizzip": "^3.1.7",},
- vue2下载包参考版本(docx-preview版本过高可能会报错)
"dependencies": {"angular-expressions": "^1.2.1","docx-preview": "^0.1.20","docxtemplater": "^3.49.1","docxtemplater-image-module-free": "^1.1.1","file-saver": "^2.0.5","lodash": "^4.17.21","pizzip": "^3.1.7",},
- 根据自己的需求,创建word文档,我创建的word文档如下:(具体书写规则参考vue3实现包含表格的Word文件导出里的解释)
- 编写导出Word的工具函数(我在utils文件夹下创建了exportFile.js文件)
注意:
- 下面代码是完整版代码(包括word的 导出 和预览,导出功能包括支持图片导出和不支持图片导出)
- 由于我只需要 “导出word,不支持图片” 的功能,所以最后只使用了下列代码中的exportWord函数,大家用的时候可以根据需要填写
// 编写导出word的工具函数// 引入基本模块
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
import { saveAs } from "file-saver";
// 图片模块
import ImageModule from "docxtemplater-image-module-free";
// 解析语法模块
import expressions from "angular-expressions";
import assign from "lodash/assign";
// 文档预览模块
import { renderAsync } from "docx-preview";expressions.filters.lower = function (input) {if (!input) return input;return input.toLowerCase();
};function angularParser(tag) {tag = tag.replace(/^\.$/, "this").replace(/('|')/g, "'").replace(/("|")/g, '"');const expr = expressions.compile(tag);return {get: function (scope, context) {let obj = {};const scopeList = context.scopeList;const num = context.num;for (let i = 0, len = num + 1; i < len; i++) {obj = assign(obj, scopeList[i]);}return expr(scope, obj);},};
}// 加载文件
function loadFile(url, callback) {PizZipUtils.getBinaryContent(url, callback);
}// 配置空值替换函数 作为配置参数可配置在setOptions中
function nullGetter(part, scopeManager) {if (!part.module) {return "-null-";}if (part.module === "rawxml") {return "";}return "--";
}/*** 预览word,支持图片* @param {Object} tempDocxPath 模板文件路径* @param {Object} wordData 导出数据* @param {Object} fileName 导出文件名* @param {Arrsy} imgSize 自定义图片尺寸*/
export const getWordImage = (tempDocxPath, wordData, imgSize, file) => {loadFile(tempDocxPath, (error, content) => {if (error) {throw error;}// 图片配置const imageOpts = {getImage: function (tagValue, tagName) {return new Promise(function (resolve, reject) {PizZipUtils.getBinaryContent(tagValue, function (error, content) {if (error) {return reject(error);}return resolve(content);});});},getSize: function (img, tagValue, tagName) {const size = imgSize[tagName] ? imgSize[tagName] : [150, 150];return size;},};let imageModule = new ImageModule(imageOpts);const zip = new PizZip(content);// 实例化有两种方式 这里是链式const doc = new Docxtemplater().loadZip(zip).setOptions({// delimiters: { start: "[[", end: "]]" },paragraphLoop: true,linebreaks: true,nullGetter: nullGetter,parser: angularParser,}).attachModule(imageModule).compile();doc.renderAsync(wordData).then(() => {const out = doc.getZip().generate({type: "blob",mimeType:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",});renderAsync(out, file);});});
};/*** 导出word,不支持图片* @param {Object} tempDocxPath 模板文件路径* @param {Object} wordData 导出数据* @param {Object} fileName 导出文件名*/
export const exportWord = (tempDocxPath, wordData, fileName) => {loadFile(tempDocxPath, (error, content) => {if (error) {throw error;}const zip = new PizZip(content);const doc = new Docxtemplater().loadZip(zip)doc.setData({...wordData.form,user_list: wordData.user_list,outsideList: wordData.outsideList})try {doc.render()} catch (error) {const e = {message: error.message,name: error.name,stack: error.stack,properties: error.properties}throw error}const out = doc.getZip().generate({type: "blob",mimeType:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",});// Output the document using Data-URIsaveAs(out, `${fileName}.docx`);});
}/*** 导出word,支持图片* @param {Object} tempDocxPath 模板文件路径* @param {Object} wordData 导出数据* @param {Object} fileName 导出文件名* @param {Arrsy} imgSize 自定义图片尺寸*/
export const exportWordImage = (tempDocxPath, wordData, fileName, imgSize) => {loadFile(tempDocxPath, (error, content) => {if (error) {throw error;}// 图片配置const imageOpts = {getImage: function (tagValue, tagName) {return new Promise(function (resolve, reject) {PizZipUtils.getBinaryContent(tagValue, function (error, content) {if (error) {return reject(error);}return resolve(content);});});},getSize: function (img, tagValue, tagName) {const size = imgSize[tagName] ? imgSize[tagName] : [150, 150]return size;},};let imageModule = new ImageModule(imageOpts);const zip = new PizZip(content);// 实例化有两种方式 这里是链式const doc = new Docxtemplater().loadZip(zip).setOptions({// delimiters: { start: "[[", end: "]]" },paragraphLoop: true,linebreaks: true,nullGetter: nullGetter,parser: angularParser,}).attachModule(imageModule).compile();doc.renderAsync(wordData).then(function () {const out = doc.getZip().generate({type: "blob",mimeType:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",});saveAs(out, `${fileName}.docx`);});});
}
- vue页面使用
import { exportWord } from '../../utils/exportFile';export default {data() {return {activity_name: '入团积极分子第十一期',filename: '入团积极分子审查表'}},methods: {exportTable() {// 需要导出的所有数据都需要写在wordData里面// 因为我们写的exportWord工具函数只接受三个参数,第一个是模版文件路径,第二个是导出数据,第三个是表的名称// 当然,大家也可以根据需要修改../../utils/exportFile里面的函数let wordData = { form: {activity_name: this.activity_name // 活动名称},outsideList: [ // 要导出的班级人员名单数据{user_class: '计科211',user_list: [{ name: '袁云熙'},{ name: '莫睿'}]},{user_class: '数本221',user_list: [{ name: '向致远'}]}]}exportWord("../../../static/template.docx", wordData, this.filename)}}
}
3. 在线查看、下载 .docx、.doc、.pdf文档
需求:
- 前端独立完成
- 后端返回的是文件地址,例如:
- 点击“查看”按钮,在新窗口打开并查看文档内容
- 点击“下载”按钮,可以下载对应的文档
- 点击“一键导出”,可以下载全部的文档
参考文档:
- 纯前端vue在线预览编辑Office,支持doc/docx、xls/xlsx、ptt/pttx、pdf等格式
解决方案:
<div class="top"><el-button @click="downAllfile" style="padding: 3px 0" type="text">一键导出</el-button>
</div><el-table :data="wordPdfData" border><el-table-column label="操作" align="center" width="250"><template #default="{ row }"><span class="check"><a :href="getFileExtension(row.file_name) === 'pdf' ? `${row.file_path}` : `https://view.officeapps.live.com/op/view.aspx?src=${row.file_path}`" target="_blank">查看</a></span><span class="download"><a @click="downFile(row.file_path, row.file_name)">下载</a></span></template></el-table-column>
</el-table>
export default {data() {return {wordPdfData: []}},methods: {// 获取文件后缀名getFileExtension(filename) {return filename.split('.').pop().toLowerCase()},// 下载文件downFile(url, filename) { // url:文件地址,filename:文件名称return new Promise((resolve, reject) => {// 创建一个隐藏的<a>元素用于触发下载const a = document.createElement("a");// 使用fetch API下载文件fetch(url).then(res => {// 检查网络响应是否成功if (!res.ok) {// 如果响应不成功,则抛出一个错误throw new Error(`网络响应失败,状态码:${res.status}`);}// 如果响应成功,则返回Blob对象return res.blob();}).then(blob => {// 创建一个对象URL用于下载const objectURL = URL.createObjectURL(blob);a.href = objectURL;a.download = filename; // 设置下载的文件名a.style.display = 'none'; // 隐藏<a>元素document.body.appendChild(a); // 将<a>元素添加到文档中a.click(); // 触发下载// 使用setTimeout在下一轮事件循环中释放对象URL并移除<a>元素// 同时解析Promise表示下载成功setTimeout(() => {URL.revokeObjectURL(objectURL);document.body.removeChild(a);resolve(); // 下载成功,解析Promise}, 0);}).catch(error => {// 如果在下载过程中发生错误,则拒绝Promisereject(error); // 下载失败,拒绝Promise});});},// 批量下载文件downAllfile() {let downList = this.wordPdfDataconst downloadPromises = downList.map(item => {const downloadUrl = item.file_path; // 构造下载URLreturn new Promise((resolve, reject) => {// 调用downImage方法下载图片,并在下载完成后解析或拒绝Promisethis.downFile(downloadUrl, item.file_name).then(() => resolve()) .catch(error => reject(error)); });})// 使用Promise.all等待所有下载任务完成Promise.all(downloadPromises).then(() => {// 所有文件都已成功下载this.$message({type: 'success',message: '所有导出成功!',showClose: true,duration: 3000});}).catch(error => {// 至少有一个文件下载失败console.error('至少有一个文件下载失败:', error);this.$message({type: 'error',message: '导出失败,请重试。',showClose: true,duration: 3000});});} }
}