vue 表格内容异步导出

在做表格导出的时候,之前的框架一贯的做法是,导出拿到页面中缓存的查询条件,将条件参数在拼接,通过window.loaction的方式将参数拼接给后台,在打开,

// 导出
handleExport() {
    if(this.total > this.exportLimit){
        this.$_c.msg('导出数据个数不能大于'+this.exportLimit,{type:'warning'})
        return
    }
    if(this.url.export){
        this.handleFieldsChangeBefore && this.handleFieldsChangeBefore()
        this.exportParams = this.$lockr.get('exportParams') || this.searchFields

        if(!this.exportParams.hasOwnProperty(this.exportTime[0]) || !this.exportParams.hasOwnProperty(this.exportTime[1])){//如果没传时间则默认查最近7天
            if(!this.exportParams[this.exportTime[0]] || !this.exportParams[this.exportTime[1]]){
                let now = new Date().getTime()
                this.exportParams[this.exportTime[0]] = now - 7*24*3600*1000
                this.exportParams[this.exportTime[1]] = now
            }
        }
        this.initExportParams && this.initExportParams()
        this.downloadFileByUrl(this.url.export,this.exportParams,this.exportFileName)
    }
},
/**
 * 通过Url下载文件
 * @param {String} url 下载路径
 * @param {Object} params 参数
 * @param {String} fileName 文件名
 */
export function downloadFileByUrl(url,params={},fileName="file") {
  url = process.env.VUE_APP_URL2 + url 
  const keys = Object.keys(params)
  for(let i=0; i<keys.length; i++) {
    url = url + (i === 0 ? '?' : '&') + keys[i] + '=' + params[keys[i]]
  }
  url += (keys.length > 0 ? '&' : '?') + 'access_token=' + cookies.get('access_token')
  if (fileName) {
    var a = document.createElement('a');
    // safari doesn't support this yet
    if (typeof a.download === 'undefined') {
        window.location = url
    } else {
        a.href = url;
        a.download = fileName;
        document.body.appendChild(a);
        a.click();
        a.remove();
    }
  } else {
    window.location = url;
  }
}
View Code

但是在实际过程会有一个问题,就是数据量特别大的时候,就会等待很长的时间,才跳转,如果这个时候一直点击导出,可能导致系统崩溃问题

解决办法:

异步导出,通过接口向后台发送查询参数,后台在返回一个标识,前段通过拿到的这个标识,进行轮询,如果获取到了路径,就打开并且关闭定时器

handleExport(val) {
    if(this.total > this.exportLimit){
        this.$_c.msg('导出数据个数不能大于'+this.exportLimit,{type:'warning'})
        return
    }
    if (this.loadExcelTimer != null) {
        window.clearInterval(this.loadExcelTimer)
        this.loadExcelTimer = null
    }
    this.$loading.showLoading({text:'导出中……'})
    this.$ajax.post(this.url.export, this.exportParams).then(res => {
        if (res.code == 200) {
            this.loadExcelTimer = setInterval(() => {
                this.getAsyncExcel(res)
            }, 2000)
        }
    }).catch(err => {
        this.restInterval()
        this.$message.error('导出失败');
    })
},

getAsyncExcel(result) {
    console.log('export table...')
    if (!result.data) return false
    this.$ajax.get(this.url.download, { id: result.data }).then(res => {
        if (res.data != null) {
            this.restInterval()
            //可以去下载了
            let url = process.env.VUE_APP_URL2 + preUrl.files + res.data //创建下载链接
            let link = document.createElement('a') //创建a标签
            link.style.display = 'none'  //将a标签隐藏
            link.href = url  //给a标签添加下载链接
            link.setAttribute('download', '文件') //此处注意,要给a标签添加一个download属性,属性值就是文件名称,否则下载出来的文件是没有属性的,空白白
            document.body.appendChild(link)
            link.click()  //执行a标签
        }
    })
},

完整的vue

<template>
    <TBLayout>
        <!-- search-form -->
        <content-box :slot="searchFormInfos.slot" :options="{'100%',padding:'20px 20px 0 20px'}">
            <self-form :formInfos="searchFormInfos" :fields="searchFields" @setFields="changeFields" @button-click="handleButtonClick" />
        </content-box>
        <btns-group slot="btnsgroup" :data="batchButtons" @button-click="handleButtonClick" v-if="isShowTable" />
        <!-- table -->
        <self-table :slot="tableInfos.slot" v-if="isShowTable" :tableData="tableData" :tableInfos="tableInfos" :status="originBaseData" @selection-change="handleSelectionChange" @button-click="handleButtonClick" />
        <!--  pager -->
        <self-pager slot="pager" @button-click="handleButtonClick" v-if="isShowTable">
            <el-pagination :current-page="pageNum" :page-sizes="pageSizes" :page-size="pageSize" :layout="layout" :total="total" slot="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange">
            </el-pagination>
        </self-pager>
        <div slot="otherPager" class="chart-page" style="" v-if="!isShowTable">
            <div class="echarts" ref="chart"></div>
        </div>
    </TBLayout>
</template>
<script>
import echarts from 'echarts'
import Crud from '@frameworks/assets/js/Crud'
import TBLayout from '@modules/baselayout/views/TBLayout'
import { searchFormInfos, batchButtons, tableInfos } from './PointHistory'
import { preUrl } from '@frameworks/conf/api'
import Api from '@frameworks/conf/api'
import { formatDate } from '@frameworks/assets/js/Filters'
export default {
    mixins: [Crud],
    components: { TBLayout },
    data() {
        return {
            url: {
                getData: Api.getPageByTime,
                export: Api.exportHistory,
                download: Api.downLoadExcel
            },
            detailParams: {
                url: '',
                params: {}
            },
            methodGet: 'post',
            batchButtons,
            searchFormInfos: searchFormInfos,
            tableInfos: tableInfos,
            isShowTable: true,//是否展示表格
            checkNodes: [],//树选中的节点
            chart: null,
            resizeTimer: null,
            chartOption: {
                title: {
                    text: '趋势图'
                },
                tooltip: {
                    trigger: 'axis',
                    appendToBody: true,
                    formatter: (params) => {
                        let format = "yyyy-MM-dd HH:mm:ss"
                        let str = ""
                        for (let i = 0; i < params.length; i++) {
                            let p = params[i]
                            let node = this.checkNodes[p.seriesIndex]
                            let date = new Date(p.name)
                            str +=
                                '<tr><td style="color:' +
                                p.color +
                                '">' +
                                p.marker +
                                "</td><td>" +
                                p.seriesName +
                                ":</td><td>" +
                                p.value[1] +
                                (p.data.unit || '') +
                                "</td></tr>";
                        }
                        str =
                            "<p>" +
                            formatDate(params[0].data.name, format) +
                            "</p><table>" +
                            str +
                            "</table>";
                        return str
                    },
                    axisPointer: {
                        animation: false
                    }
                },
                xAxis: {
                    type: 'time',
                    splitLine: {
                        show: false
                    }
                },
                yAxis: {
                    name: '',
                    type: 'value',
                    boundaryGap: [0, '100%'],
                    splitLine: {
                        show: false
                    }
                },
                color: ['rgb(0, 137, 249)', 'rgb(0, 201, 134)', 'rgb(255, 132, 31)', 'rgb(216, 86, 207)', 'rgb(252, 206, 8)', 'rgb(133, 172, 193)'],
                series: [{
                    name: '模拟数据',
                    type: 'line',
                    showSymbol: false,
                    hoverAnimation: false,
                    data: []
                }]
            },
            exportLimit: 1000000,
            unit: '',
            loadExcelTimer: null
        }
    },
    methods: {
        async handleSearchBefore() {
            let now = new Date().getTime()
            this.searchFields.startTime = now - 7 * 24 * 3600 * 1000
            this.searchFields.endTime = now
            this.searchFormInfos.children[3].options.defaultValue = ''
            this.searchFields.tagNames = ''
            return await false
        },

        handleFieldsChangeBefore() {
            delete this.searchFields.checkd
            delete this.searchFields.data
            delete this.searchFields.initDefault
        },

        handleFieldsChangeAfter(val) {
            if (val.field.pointType) {
                this.searchFormInfos.children[3].options.pointType = val.field.pointType
                this.searchFields.tagNames = ''
            } else if (val.field.tagNames) {
                this.searchFormInfos.children[3].options.defaultValue = val.field.tagNames
                this.checkNodes = val.field.checkNodes || []
            }

        },

        //
        handleSearch(showMsg = false) {
            if (this.searchFields.showTypeData == 1) {
                this.isShowTable = true
            } else if (this.searchFields.showTypeData == 2) {
                this.isShowTable = false
            }
            if (!this.isShowTable) {
                this.$loading.showLoading()
                if (this.checkNodes && this.checkNodes.length > 10) {
                    this.$_c.msg('曲线查看选择的测点不能超过10个,请重新选择', { type: 'warning' })
                    this.$loading.hideLoading()
                    return false
                }
            }
            this.handleFieldsChangeBefore && this.handleFieldsChangeBefore()
            //如果是曲线是查所有的数据,表格的是需要进行分页的
            let data = this.isShowTable ? Object.assign({
                pageNum: this.startNum == 0 ? this.pageNum : this.startNum,
                pageSize: this.pageSize,
            }, this.searchFields) : this.searchFields
            //统一删除showTypeData字段
            //delete data.showTypeData
            delete this.searchFields.checkNodes
            //获取数据
            this.$ajax.asyncAjax(this.url.getData, data, this.methodGet).then((res) => {
                if (res.code == 200) {
                    //如果是表格
                    if (this.isShowTable) {
                        this.startNum = 0
                        this.tableData = []
                        this.total = res.data.total ? res.data.total : 0
                        this.pageNum = res.data.pageNum ? res.data.pageNum : 1
                        this.pageCount = res.data.pages ? res.data.pages : 0
                        let result = this.isPager ? res.data[this.dataListField] : res.data
                        if (result.length == 0 && this.pageNum != 1) {//如果最后一页已经没有数据,那就查前一页
                            if (this.searchForwardCount < 1) {
                                this.pageNum -= 1;
                                this.searchForwardCount += 1;
                            } else {
                                this.pageNum = 1;
                            }
                            this.handleSearch();
                            return
                        }
                        this.tableData = this.parseSearchResult(result)
                        //如果需要,把数据返回给父类
                        this.$emit('dto-data', res)
                        //缓存查询条件
                        this.isSearchParamsToLocalStorage && this.setSearchFieldStorage()
                        //获取数据后对数据进行修饰
                        this.handleSearchSuccess && this.handleSearchSuccess(res, showMsg)
                    } else {//曲线
                        this.initChart()
                        this.initData(res)
                    }
                } else {
                    this.tableData = []
                    this.handleSearchFailed && this.handleSearchFailed(res)
                }
                this.searchForwardCount = 0;
            }).catch(res => {
                this.$loading.hideLoading()
                this.searchForwardCount = 0;
                this.tableData = []
                this.handleSearchError && this.handleSearchError(res)
            })
            if (this.clearSelection) this.handleSelectionChange()
        },

        initChart() {
            this.chart = echarts.init(this.$refs.chart)
            this.chart.clear()
            this.chart.setOption(this.chartOption)
            this.chart.resize()
        },

        handleExport(val) {
            if(this.total > this.exportLimit){
                this.$_c.msg('导出数据个数不能大于'+this.exportLimit,{type:'warning'})
                return
            }
            if (this.loadExcelTimer != null) {
                window.clearInterval(this.loadExcelTimer)
                this.loadExcelTimer = null
            }
            this.$loading.showLoading({text:'导出中……'})
            // this.exportParams = {
            //     startTime: 1607909692028,
            //     endTime: 1608514492028,
            //     pointType: 1,
            //     tagNames: 'M0101.BL.ITCWSD_LTD_1,M0101.BL.ITCWSD_LTD_1.1.itEnvCCDownHum,M0101.BL.ITCWSD_LTD_1.1.itEnvCCDownTemp,M0101.BL.ITCWSD_LTD_1.1.itEnvCCIndex,M0101.BL.ITCWSD_LTD_1.1.itEnvCCUpHum',
            //     showTypeData: 1,
            //     rowNum: -1,
            //     activeField: 'pointType',
            //     timeZone: -8,
            //     pageNum: 1,
            //     pageSize: 10
            // }
            this.$ajax.post(this.url.export, this.exportParams).then(res => {
                if (res.code == 200) {
                    this.loadExcelTimer = setInterval(() => {
                        this.getAsyncExcel(res)
                    }, 2000)
                }
            }).catch(err => {
                this.restInterval()
                this.$message.error('导出失败');
            })
        },

        getAsyncExcel(result) {
            console.log('export table...')
            if (!result.data) return false
            this.$ajax.get(this.url.download, { id: result.data }).then(res => {
                if (res.data != null) {
                    this.restInterval()
                    //可以去下载了
                    let url = process.env.VUE_APP_URL2 + preUrl.files + res.data //创建下载链接
                    let link = document.createElement('a') //创建a标签
                    link.style.display = 'none'  //将a标签隐藏
                    link.href = url  //给a标签添加下载链接
                    link.setAttribute('download', '文件') //此处注意,要给a标签添加一个download属性,属性值就是文件名称,否则下载出来的文件是没有属性的,空白白
                    document.body.appendChild(link)
                    link.click()  //执行a标签
                }
            })
        },

        initData(result) {
            let rowData = result.data && result.data.list
            this.unit = rowData[0].unitName
            let legendArr = [], series = []
            for (let item of this.checkNodes) {
                legendArr.push(item.name)
            }
            //首先遍历一遍进行分组
            for (let i = 0; i < this.checkNodes.length; i++) {
                let row = this.checkNodes[i]
                series.push(
                    {
                        name: legendArr[i] || row.id,
                        type: 'line',
                        showSymbol: false,
                        hoverAnimation: false,
                        data: this.getDataByCode(row, rowData)
                    }
                )
            }
            this.$loading.hideLoading()
            this.chart.setOption({
                legend: {
                    data: legendArr
                },
                series
            })
            this.chart.resize()
        },

        //数据处理
        getDataByCode(rowObj, rowData) {
            let dataArr = []
            for (let i = 0; i < rowData.length; i++) {
                let row = rowData[i]
                let _arr = []
                _arr.push(formatDate(row.time, 'yyyy-MM-dd HH:mm:ss'))
                _arr.push(row.value)
                rowObj.id == row.tagName && dataArr.push({
                    name: new Date(row.time),
                    value: _arr,
                    unit: row.unitName
                })
            }
            return dataArr
        },

        init() {
            window.onresize = () => {
                return (() => {
                    if (this.resizeTimer) { clearTimeout(this.resizeTimer) }
                    this.resizeTimer = setTimeout(() => {
                        if (this.chart) {
                            this.chart.resize();
                        }
                    }, 300)
                })()
            }
        },

        restInterval() {
            this.$loading.hideLoading()
            window.clearInterval(this.loadExcelTimer)
            this.loadExcelTimer = null
        }
    },
    mounted() {
        this.init()
    },
    activated() {
        this.searchFormInfos.children[3].options.pointType = '0'
        this.searchFields.tagNames = ''
    },
    destroyed() {
        this.restInterval()
    },
};
</script>
<style lang="scss">
@import '@frameworks/assets/css/common.scss';
.chart-page {
    box-shadow: 0 0 6px 1px rgba(0, 0, 0, 0.1);
    border-radius: 8px;
    height: 500px;
    padding: 20px;
    margin-right: 20px;
}
</style>
View Code

原文地址:https://www.cnblogs.com/pengfei25/p/14168716.html