1. 业务场景
使用 Element 的 upload 上传组件,手动上传文件失败,再次点击无效。
特别是上传之前,整个页面加了 loading,这时候的体验就非常不好。
<template>
<el-upload
ref="upload"
:limit="1"
accept=".xlsx"
:action="$baseUrl + '/api/xxx'"
:headers="headers"
:auto-upload="false"
:on-success="handleSuccess"
:on-error="handleError"
:on-exceed="handleExceed"
>
<el-button icon="el-icon-folder-opened" size="mini">
选取文件
</el-button>
</el-upload>
<el-button
type="primary"
icon="el-icon-upload"
size="mini"
:loading="loading.upload"
@click="btn_submit"
>
上传
</el-button>
</template>
<script>
export default {
data() {
retutn {
loading: { upload: false },
headers: { Authorization: "Bearer " + getToken() },
}
},
methods: {
btn_submit() {
const upload = this.$refs.upload;
/** 文件校验 */
...
this.loading.upload = true;
upload.submit(); // 提交
},
/** 文件超出个数限制时的钩子 */
handleExceed(file, fileList) {
this.$message.warning("超出文件上传数量限制");
},
/** 文件上传成功时的钩子 */
handleSuccess(res, file, fileList) {
this.loading.upload = false;
if(res.code == 200) {
...
this.$message.success("文件上传成功");
} else {
file.status = "fail";
this.$message.error(res.msg || "文件上传失败");
}
},
/** 文件上传失败时的钩子 */
handleError(err, file, fileList) {
this.loading.upload = false;
this.$message.error("文件上传失败");
},
}
}
</script>
2. 解决方案
2.1 省去上传步骤,直接提示失败。
我们可以把首次的失败信息用一个字段 upfilefail
保存下来,如果 upfilefail 不为空,就说明已经提交并且失败了,这时直接抛出错误提示。
然后文件移除的时候,需要把 upfilefail 置空。
btn_submit() {
const upload = this.$refs.upload;
/** 文件校验 */
...
// 首次正常提交
if(!this.upfilefail) {
this.loading.upload = true;
upload.submit(); // 提交
}
// 提交失败之后,直接抛出错误提示
else {
this.$message.error(this.upfilefail);
}
},
/** 文件上传成功时的钩子 */
handleSuccess(res, file, fileList) {
this.loading.upload = false;
if(res.code == 200) {
...
this.$message.success("文件上传成功");
} else {
file.status = "fail";
this.upfilefail = res.msg || "文件上传失败";
this.$message.error(this.upfilefail);
}
},
/** 文件上传失败时的钩子 */
handleError(err, file, fileList) {
this.loading.upload = false;
this.upfilefail = "文件上传失败";
this.$message.error(this.upfilefail);
},
/** 文件列表移除文件时的钩子 */
handleRemove(file, fileList) {
this.upfilefail = ""; // 文件移除,文件上传失败信息置空
},
2.2 重置状态,重新上传
如果我们确实有重新上传的需求怎么办呢?
但直接二次上传是无效的,因为文件列表中没有新的文件,已有的文件状态都是已上传,所以不会再次走上传流程。
为了更好理解,我们直接上源码,简单了解一下上传流程!
-
- 选取文件的时候,会调用一个
handleStart
的方法对文件进行格式化。
- 选取文件的时候,会调用一个
element-ui/packages/upload/index.vue
handleStart(rawFile) {
rawFile.uid = Date.now() + this.tempIndex++;
let file = {
status: 'ready',
name: rawFile.name,
size: rawFile.size,
percentage: 0,
uid: rawFile.uid,
raw: rawFile
};
if (this.listType === 'picture-card' || this.listType === 'picture') {
try {
file.url = URL.createObjectURL(rawFile);
} catch (err) {
console.error('[Element Error][Upload]', err);
return;
}
}
this.uploadFiles.push(file);
this.onChange(file, this.uploadFiles);
}
选择的文件都放在了 uploadFiles
中,这时文件状态是 ready
。
-
- 调用组件的
submit
方法
- 调用组件的
element-ui/packages/upload/index.vue
submit() {
this.uploadFiles
.filter(file => file.status === 'ready')
.forEach(file => {
this.$refs['upload-inner'].upload(file.raw);
});
}
文件提交的时候,会先把文件列表中 ready
状态的文件过滤出来。
更具体的上传过程在 upload.vue
中,这里就不过多展开了。
OK,这两步就已经能大致看出问题了。
补充:其他几种文件状态
file.status = 'uploading'; // 上传中
file.status = 'success'; // 上传成功
file.status = 'fail'; // 上传失败
所以我们要重新上传文件走接口的话,需要我们手动重置一下文件状态。
btn_submit() {
const upload = this.$refs.upload;
/** 文件校验 */
...
let file = upload.uploadFiles[0];
// 首次正常提交
if(file.status == "ready") {
this.loading.upload = true;
upload.submit(); // 提交
}
// 再次提交的处理 fail success
else {
upload.clearFiles(); // 清空已上传的文件列表
upload.handleStart(file.raw); // 手动格式化文件,这时文件状态已经置为 ready
upload.submit(); // 重新提交
}
}
如此,就大功告成!