一、后台(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下载包参考版本
"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-URI
saveAs(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文档
需求:
- 前端独立完成
- 后端返回的是文件地址,例如:
- 点击“查看”按钮,在新窗口打开并查看文档内容
- 点击“下载”按钮,可以下载对应的文档
- 点击“一键导出”,可以下载全部的文档
参考文档:
解决方案:
<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 => {
// 如果在下载过程中发生错误,则拒绝Promise
reject(error); // 下载失败,拒绝Promise
});
});
},
// 批量下载文件
downAllfile() {
let downList = this.wordPdfData
const downloadPromises = downList.map(item => {
const downloadUrl = item.file_path; // 构造下载URL
return new Promise((resolve, reject) => {
// 调用downImage方法下载图片,并在下载完成后解析或拒绝Promise
this.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
});
});
}
}
}