Web前端实现导出功能
本文是用来记录项目中遇到各种业务场景下的导出功能实现。欢迎点赞收藏
一、后端返回链接的导出
这种导出,我最喜欢了,后端比较有良心,前端很省事。当然在这种场景下,还分两种情况。
- 链接带域名
if (res.code == 200) {window.location.href = res.data;
}
- 链接不带域名
域名是请求后端的域名,不是前端页面的域名。项目一般是给多个客户使用,故后端的域名一般都交给运维来配置。项目中这么实现。
在静态资源public文件夹中建立config.js文件和config.js.example文件,其中config.js文件是忽悠上传到git上的,开发环境可以里面配置后端的域名,在生产环境是由运维在里面配置后端的域名,因为已经忽悠上传到git上的,更新代码时不会覆盖运维所配置的。config.js.example文件是一个示例作用,告诉运维怎么配置。
config.js和config.js.example内容一样
/*
配置文件示列:
配置文件路径 public/config.js
*/
window.apiConfig = {
baseUrl: '后端的域名',
};
复制代码
然后再public/index.html引入config.js
<script>
var script = document.createElement('script');
var num = Math.floor(Math.random() * 10000);
script.src = 'config.js?a=' + num;
document.getElementsByTagName('head')[0].appendChild(script);
script = document.getElementById('scriptConfig');
script.parentNode.removeChild(script);
script = null;
</script>
复制代码
最后这么使用即可
if (res.code == 200) {
window.location.href = window.apiConfig.baseUrl+res.data;
}
复制代码
二、后端返回二进制数据的导出
- 首先我们要配置一下axios,因为默认服务器响应的数据类型是json,要改为blob。
exportfunctionexport(data){
return service.get('接口地址',{
params:data,
responseType:'blob'
})
}
复制代码
- 然后利用
new Blob()
来处理二进制数据,生成一文件,再用createObjectURL()
创建链接后,用a链接自动下载。下面把方法封装一下,挂在Vue原型链上。
const install = function(Vue,opts){* 处理二进制数据导出
* @param blob 二进制流
* @param name 文件名
*/
Vue.prototype.exportExcels = function(blob,name){
// type 为需要导出的文件类型,此处为xls表格类型
const file = new Blob([blob], { type: 'application/vnd.ms-excel' });
// 兼容不同浏览器的URL对象
const url = window.URL || window.webkitURL || window.moxURL;
// 创建下载链接
const downloadHref = url.createObjectURL(file);
// 创建a标签并为其添加属性
let downloadLink = document.createElement('a');
downloadLink.setAttribute('href', downloadHref);
downloadLink.setAttribute('download', name);
//将a标签添加到body中
document.body.appendChild(downloadLink);
// 触发a标签的点击,自动下载
downloadLink.click();
//下载完成后移除a标签
document.body.removeChild(downloadLink);
//释放下载链接
url.revokeObjectURL(downloadHref);
}
}
export default{
install
}
复制代码
- 其中
new Blob()
的第一个参数是array,里面每项是一个二进制流,第二个参数是可选属性,其中type
属性是文件的MIME类型,这个类型由后端决定是什么类型。常用的MIME类型如下
后缀名 MIME名称 *.csv text/csv *.doc application/msword *.dot application/msword *.xls application/vnd.ms-excel *.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
三、在导出接口地址上加参数直接打开下载
上面两种场景都是先要请求服务器,得到返回数据后处理后再下载。这种场景是在导出接口地址上加参数直接打开下载,例如:
window.location.href = '导出接口地址'?user='lhy'&date='2020-05';复制代码
上面的请求方法相当get方法,但是当请求导出接口时候参数太多了,使用get方法请求会导致参数缺少。这时候就想办法用post方法请求。
由于这种场景是直接打开导出接口地址下载,有点不好用post方法,那么这时候就要借助HTML<form>
标签和DOM Form 对象来解决。
我们封装一个组件来实现。
<template><form :action="action" :target="target" :method="method" ref="exports">
<template v-if="data.length">
<input type="hidden" autocomplete="off" v-for="(item,i) in data" :name="item.name" :value="item.value"/>
</template>
<input type="hidden" autocomplete="off"readonly name="token" :value="token"/>
</form>
</template>
<script>
export default {
name: 'formExport',
props: {
action: {
type: String,
default: '',
},
target: {
type: String,
default: '_blank',
},
method: {
type: String,
default: 'post',
},
token:{
type: String,
default: '',
},
data: {
type: Array,
default() {
return [];
}
}
},
methods: {
submit() {
return new Promise((resoleve,reject) =>{
if (this.token) {
this.$refs.exports.submit();
resolv()
}else{
reject()
}
}
}
}
}
</script>
复制代码
组件文档
- 参数
参数 说明 类型 可选值 默认值 action 必填,导出接口地址 String — — target 规定在何处打开导出接口地址 String _blank:在新窗口打开 _self:在当前窗口打开
_blank method 请求方法 String post/get post token 必填,鉴权 String — — data 必填,传给服务器的参数 {name:参数名称,value:参数值}
String — — - 方法
事件名称 说明 回调参数 submit 提交表单 Promise对象 - 示例
<template>
<formExport ref="export" :action="exportData.url" :data="exportData.url" :token="exportData.token"></formExport>
<el-button @click="handleExport">导出</el-button>
</template>
<script>
export default {
data(){
return{
exportData:{
url:'导出接口地址',
data:{
user:'lds',
page:1,
pageSize:20,
statTime:'2020-04',
endTime:'2020-05'
},
token:'12334f'
}
}
},
components:{
formExport: () =>import('./formExport.vue')
},
methods:{
handleExport(){
setTimeout(() => {
this.$refs.export.submit();
}, 500);
}
}
}
</script>
复制代码
四、后端只返回json数据前端生成Excel下载
这里要借助xlsx和file-saver两个插件实现。其中xlsx是生成Excel文件,file-save是保存下载Excel文件
- 使用npm安装xlsx和file-saver插件,执行命令
npm install xlsx --savenpm install file-saver --save
复制代码
- 在servie文件夹中引入Export2Excel.js,在这脚本中对xlsx和file-saver插件中的方法进行封装。
- 下面把方法封装一下,挂在Vue原型链上
- 参数文档
参数 说明 类型 默认 可选值 示例 header 表格头数据 array — — ['一级客户', '申请客户', '支付宝账号', '提现金额', '申请人', '申请时间'] data 表格数据 array — — [{},{}] filename Excel文件名称 string — — 'excle1' opition 额外配置 object — — {} - 参数 opition 文档
参数 说明 类型 默认 可选值 示例 filterVal 过滤表格数据 array — — ['id','name'] multiHeader 表格头数据除最后一行表格头的数据,是个二维数据,不够的用''补全,只在bookType为xlsx或xls下有效。 array — — [ ['序号', '客户信息', '', '', '', ''], ['', '客户姓名', '提现信息', '', '', '']] merges 合并表格头的规则,只在bookType为xlsx或xls下有效。 array — — ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2'] autoWidth 表格内容是否自适应宽度 boolean true true/false — bookType 生成文件类型 string xlsx xlsx/xls/csv —
const install = function(Vue,opts){/**
* json数据生成Excel并下载
* @param header 表格头数据
* @param data 表格数据
* @param filename Excel文件名称
* @param opition 额外配置
*/
Vue.prototype.downloadExcels = function (header, data, filename, opition) {
let defaultOpition = {
filterVal: [],
multiHeader: [],
merges: [],
autoWidth: true,
bookType: 'xlsx',
}
if (header && Object.prototype.toString.call(header) != '[object Array]') {
throw new Error('header请传入数组');
}
if (data && Object.prototype.toString.call(data) != '[object Array]') {
throw new Error('data请传入数组');
}
if (opition && Object.prototype.toString.call(opition) == '[object Object]') {
defaultOpition = Object.assign({}, defaultOpition, opition);
}
if (Object.prototype.toString.call(defaultOpition.filterVal) != '[object Array]') {
throw new Error('filterVal请传入数组');
}
if (Object.prototype.toString.call(defaultOpition.multiHeader) != '[object Array]') {
throw new Error('multiHeader请传入数组');
}
if (Object.prototype.toString.call(defaultOpition.merges) != '[object Array]') {
throw new Error('merges请传入数组');
}
const formatJson = function (filterVal, jsonData) {
if (filterVal.length == 0) {
return jsonData;
} else {
return jsonData.map(v => filterVal.map(j => v[j]));
}
}
data = formatJson(defaultOpition.filterVal, data);
if (data[0].length > header.length) {
throw new Error('data中每项数据长度大于头部长度');
}
defaultOpition['data'] = data;
defaultOpition['header'] = header;
defaultOpition['filename'] = filename;
import('./Export2Excel').then(res => {
res.export_json_to_excel(defaultOpition);
})
}
}
export default{
install
}
复制代码
- 示例
handleExport() {const header = ['一级客户', '申请客户', '支付宝账号', '提现金额', '申请人', '申请时间'];
const option = {
bookType: 'xlsx',
filterVal: ['firstCustomName', 'custom_name', 'withdrawals_bank', 'withdrawals_amount', 'do_username', 'add_time'],
multiHeader: [
['序号', '客户信息', '', '', '', ''],
['', '客户姓名', '提现信息', '', '', '']
],
merges: ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2']
}
this.downloadExcels(header, this.tableData, '审核记录', option)
},
复制代码
五、通过PDF导出
这种导出适用可视化数据场景
- 在index.html 引入生成PDF的脚本jspdf.js。
- 在index.html 引入截屏的脚本html2canvas.js。
- 下面把方法封装一下,挂在Vue原型链上
const install = function(Vue,opts){* 将页面导出成pdf文件
* @param id 要生成PDF文件DOM区域的id
* @param fileName 导出的文件名称
* @param height 导出的pdf高度不够,需要设置额外高度,默认80
*/
Vue.prototype.exportPDF= function(id, fileName, height = 80){
//html2canvas只截取dom的可视区域,将dom的可视区域设置大解决导出视图不全的问题
document.getElementById(id).ownerDocument.defaultView.innerHeight = document.getElementById(id).scrollHeight + height;
html2canvas(document.getElementById(id), {
scale: 2,//按比例增加分辨率 (2=双倍).
dpi: 1080,//导出pdf清晰度 将分辨率提高到特定的DPI(每英寸点数)
background: "#fff", //背景设为白色(默认为黑色)
onrendered: function (canvas) {
let contentWidth = canvas.width;
let contentHeight = canvas.height;
//一页pdf显示html页面生成的canvas高度;
//a4纸的尺寸[595.28,841.89]
let pageHeight = contentWidth / 592.28 * 841.89;
//未生成pdf的html页面高度
let leftHeight = contentHeight;
//pdf页面偏移
let position = 0;
//html页面生成的canvas在pdf中图片的宽高
let imgWidth = 595.28;
let imgHeight = 592.28 / contentWidth * contentHeight;
let pageData = canvas.toDataURL('image/jpeg', 1.0);
let pdf = new jsPDF('', 'pt', 'a4');
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89;
//避免添加空白页
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(fileName);
}
)
}
}
export default{
install
}
以上是 Web前端实现导出功能 的全部内容, 来源链接: utcz.com/a/18158.html