问题场景:问题场景是之前运营提出用户导出数据时候等待时间比较久,看怎么解决,由于数据量大,且服务器资源有限,所以优化方向放在了交互逻辑上。
分析问题:面对问题首先进行分析问题,什么原因让用户比较难受?---点了导出按钮就开始转圈,也没有进度条,也不知道好没好,还不能去别的页面,只能一直等下去,,,,,,。。这就是用户的痛点。
寻找方案:了解用户痛点后,开始将问题分块,前端点导出发送请求-----后端接收开始导出数据并生成文件-------后端生成文件完成返回文件url-----前端下载。 看这几个阶段前端几乎没有任何优化空间,后端也是任务繁重,唯一的突破点似乎就是点击导出按钮如何把导出事件放到全局下,不去影响用户使用,以此来解决用户的问题。
梳理逻辑:由于系统中有很多导出需求,所有把这些东西统统集成到一套事件里是不错的选择,所以跟后端商量解决方案,在我的强烈要(乞)求下,最终同意了我的方案。流程逻辑:
- 用户点击导出,发送请求给服务器,服务器立即将这个导出任务放到定时任务表里,并立即将任务 id 返回。
- 前端点完按钮几乎无延迟的收到了服务器返回的文件id后将id set到vuex中,然后挂在全局根压面的下载组件监听到vuex改变后就开始轮询服务器导出任务list,查到有任务正在进行中的话,就显示弹窗并一直轮询,由于弹窗是在根组件下,所以不影响用户的使用。
- 导出完成,自动执行下载,流程完毕
流程代码:如下
下载文件弹窗组件: vue + ant 把这个组件放在全局页面下就好
<template>
<div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import { selectUserDownload, delUserDownload } from '@/api/download'
const key = 'downloadMessage'
export default {
name: 'PageView',
data () {
return {
a: '',
selectTime: '',
downloadName: '',
timeLength: 1000
}
},
computed: {
...mapState({
download: state => state.user.download
})
},
mounted () {
this.selectUserState()
this.timeLength = 1000
},
watch: {
download (newVal, old) {
if (newVal !== '') {
this.getPageMeta()
this.selectUserState()
this.timeLength = 1000
} else {
this.$notification.close('downloadMessage')
this.Download('')
clearTimeout(this.selectTime)
}
}
},
methods: {
...mapActions(['Download']),
getPageMeta () {
this.$notification.open({
key,
message: `文件${this.downloadName}正在后台导出中`,
description: '系统正在努力为您导出数据,很快就好啦,先喝口水稍等片刻吧!导出完成将会自动下载.',
icon: <a-icon type="sync" spin />,
onClose: function () {
this.Download('')
this.$notification.close('downloadMessage')
clearTimeout(this.selectTime)
},
duration: 0
})
},
// 文件导出流程 点击下载按钮---》发送请求告诉服务器要下载的文件---》改变vuex中的Dowload值---》监听到vuex中值改变开始轮询查
// 页面初次加载会执行一次查询请求 返回的list为0就是没有文件下载 有list就是有文件在下载
// 全局下的查询事件 查询用户下是否有正在下载的文件
selectUserState () {
this.selectTime = setTimeout(() => {
selectUserDownload().then(res => {
if (res.BusinessData.length !== 0) {
this.downloadName = `:《${res.BusinessData[0].taskName}"》`
this.timeLength === 1000 && this.getPageMeta()
// 设置轮询时长 第一次轮询为 1秒 1秒内没有完成就按照 导出的数据量倍增时长
// res.BusinessData.forEach(element => {
if (res.BusinessData[0].status === 1) {
clearTimeout(this.selectTime)
window.location.href = res.BusinessData[0].fileUrl
this.$notification.close('downloadMessage')
delUserDownload(res.BusinessData[0].taskId).then(res => {})
} else if (res.BusinessData[0].status === 0) {
this.selectUserState()
this.timeLength = 5000
}
// })
} else {
this.$notification.close('downloadMessage')
this.Download('')
clearTimeout(this.selectTime)
}
}).catch(err => {
this.Download('')
clearTimeout(this.selectTime)
this.$message.error({
title: '错误',
description: err.message
})
})
}, this.timeLength)
}
}
}
</script>
<style lang="less" scoped>
</style>
最终效果:主要思路赘述很多,代码只晒出来大体,剩下的在需要导出的页面改变vuex就好了,前端菜鸡,细节没有修饰,最终完美的解决了用户干等的问题,最终效果就是用户点完导出,立即出现弹框,这时用户可以该干嘛干嘛,导出完成自动下载。
还有就是你觉得这是在分享代码吗?我觉得更重要的是解决问题的思路和前端业余流程追求