vue 表格内容异步导出
在做表格导出的时候,之前的框架一贯的做法是,导出拿到页面中缓存的查询条件,将条件参数在拼接,通过window.loaction的方式将参数拼接给后台,在打开,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 导出 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; } }
但是在实际过程会有一个问题,就是数据量特别大的时候,就会等待很长的时间,才跳转,如果这个时候一直点击导出,可能导致系统崩溃问题
解决办法:
异步导出,通过接口向后台发送查询参数,后台在返回一个标识,前段通过拿到的这个标识,进行轮询,如果获取到了路径,就打开并且关闭定时器
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<template> <TBLayout> <!-- search-form --> <content-box :slot="searchFormInfos.slot" :options="{width:'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>