今天遇到一个Vue移动端项目的小问题,在一个表单提交页面进行批量上传图片的过程中途,从当前应用切出去一段时间,再切回当前应用时,发现图片一直处于上传中的状态,只能通过刷新或返回上一页重置,如下图所示。
现在需要对这种情况进行优化处理,使用户在不刷新或返回的情况下就能正常继续编辑表单。
看到这里,首先想到的是监听当前标签页切换,然后根据事件触发的状态变更进行随后的逻辑处理。于是就用到了这个Web API :document.visibilitychangehttps://developer.mozilla.org/zh-CN/docs/Web/API/Document/visibilitychange_event
MDN对该API的描述为:当其选项卡的内容变得可见或被隐藏时,会在文档上触发 visibilitychange
(能见度更改) 事件。
不管切出标签页还是切出整个浏览器应用,都会触发这个事件,而这个事件会对document的visibilityState属性进行修改,页面打开的时候默认为"visible",切出后document.visibilityState会被修改为"hidden",再次切回页面又会变回"visible"。利用这一特性,即可轻松解决这个问题。
接下来进一步分析该问题。当应用切出再切进后,可能会有至少四种情况发生:
- 全部上传失败
- 全部上传成功
- 部分上传成功,部分彻底上传失败
- 部分上传成功,部分上传中,过一会全部或部分上传成功
因此,就需要根据四种不同的情况,再结合document.visibilityState进行后续处理。
由于该页面是表单提交页,除了上传图片还有其他信息的填写,因此不管哪种情况都不能粗暴的把所有表单内容全部清空,只需要对formData中的附件数组attachments进行操作。
前两种情况很好处理:
- 如果切回页面发现图片全部上传失败,那么就应该将图片上传列表清空,并toast提示用户上传失败,请重新上传。
- 如果切回页面后图片全部上传成功,什么都不做。
而后两种,关于如何区分上传中和上传失败有多种方法,可以遍历每个图片上传对象的状态;也可以直接设定一个响应超时时间,只要超时就默认没传完的都上传失败,我这里选择了后者。
- 如果部分上传成功,部分上传彻底失败,那就将上传失败的过滤掉,仅保留上传成功的图片,并toast提示用户部分图片上传失败,请重新上传
- 如果部分上传成功,部分上传中,也就是在超时响应时间内还在上传的,那就保留最终上传成功的所有图片,过滤掉超时了的,并根据情况toast提示
代码如下:
data() {
return {
uploadTimeout: 2000,
...
}
},
mounted() {
this.handleUploadAfterPageChanged()
},
//页面切换后处理上传附件列表
methods:{
handleUploadAfterPageChanged() {
this.$nextTick(() => {
document.addEventListener("visibilitychange", async () => {
//由hidden变回visible后,过滤掉上传失败的附件
if (document.visibilityState === 'visible') {
//响应超时时间结束后再处理,避免部分即将完成的上传操作直接终止
await utils.delay(this.uploadTimeout)
let doneList = this.formData.attachments.filter(attachment => attachment.status === 'done')
let length = this.formData.attachments.length
if (length > doneList.length) {
// 多图上传的情况下,如果有图片上传失败,需要toast提示用户
let errMsg = doneList.length > 0 ? '部分图片上传超时,请重新上传' :'图片上传超时,请重新上传'
this.$toast(errMsg)
// 仅保留上传成功的图片
this.formData.attachments = doneList
}
}
});
})
},
...
}
效果如下:
全部失败:
切出前:
切入并超时后:
部分失败,部分成功:
切出前:
切入并超时后: