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名称
    *.csvtext/csv
    *.docapplication/msword
    *.dotapplication/msword
    *.xlsapplication/vnd.ms-excel
    *.xlsxapplication/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请求方法Stringpost/getpost
    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 --save

npm install file-saver --save

复制代码

  • 在servie文件夹中引入Export2Excel.js,在这脚本中对xlsx和file-saver插件中的方法进行封装。

  • 下面把方法封装一下,挂在Vue原型链上

    • 参数文档

    参数说明类型默认可选值示例
    header表格头数据array['一级客户', '申请客户', '支付宝账号', '提现金额', '申请人', '申请时间']
    data表格数据array[{},{}]
    filenameExcel文件名称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表格内容是否自适应宽度booleantruetrue/false
    bookType生成文件类型stringxlsxxlsx/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

回到顶部