需求描述
- 多图上传组件,1-9 张图
- 选择完文件后自动上传,不需要上传按钮来进行手动上传
难点
- 接口有两种,多图集合上传接口 uploadImgs、单图上传接口 uploadImg
- 使用 uploadImgs 接口,参数为图片集合 fileList,但是缺少上传按钮,无法监听用户选择完图片的时间点
- 使用 uploadImg 接口,参数为图片 file,接口被多次同时调用造成服务器压力
解决思路
- 关闭 auto-upload 属性,手动控制上传进程,监听组件的 change 事件
- 使用防抖函数,监听 change 事件
- 选择多张图片后,组件每读取到一张图片就会调用一次 change 事件
- change 事件被触发后的某时间段内如果没有再次被触发,视为用户此次选择图片结束
- 获取待上传的文件列表,将图片集合传递给后台
- 如果调用单图上传,需要加入递归,重复多次调用上传接口,将待上传文件列表中的内容逐一上传
<el-upload
action=""
list-type="picture-card"
multiple
show-file-list
:on-change="changeFile"
:on-exceed="exceedFile"
:auto-upload="false"
:file-list="fileList"
:limit="9"
:disabled="disabled"
ref="upload"
>
<i class="el-icon-plus"></i>
</el-upload>
data() {
return {
timmer: null,
fileList: [],
disabled: false,
uploadFileList: [],
successFileUrls: [],
};
},
debounce(func, delay) {
return (() => {
if (this.timmer) {
console.log("%c清理定时器的操作~", "color: blue;");
clearTimeout(this.timmer);
}
this.timmer = setTimeout(func, delay);
})();
},
changeFile(file, fileList) {
console.log("触发组件change事件", file, fileList);
this.disabled = true;
this.debounce(() => {
this.uploadFileList = JSON.parse(JSON.stringify(fileList));
this.uploadImgs();
this.timmer = null;
}, 1000);
},
exceedFile() {
this.$message.error("上传文件最多9个");
},
uploadImgs() {
console.log("%c模拟接口执行上传事件----开始上传", "color: red;");
console.log(this.uploadFileList);
if (this.uploadFileList.length > 0) {
this.mockFunc(2, this.uploadFileList)
.then((res) => {
console.log("%c模拟接口执行上传事件----成功", "color: green;");
this.successFileUrls.push(res);
console.log("当前已上传成功的文件", this.successFileUrls);
})
.catch(() => {
console.log(
"%c模拟接口执行上传事件----失败",
"color: yellowgreen;"
);
});
}
},
uploadImg() {
console.log("%c模拟接口执行上传事件----开始上传", "color: red;");
console.log(this.uploadFileList);
if (this.uploadFileList.length > 0) {
this.mockFunc(1, this.uploadFileList[0])
.then((res) => {
console.log("%c模拟接口执行上传事件----成功", "color: green;");
this.successFileUrls.push(res);
console.log("当前已上传成功的文件", this.successFileUrls);
this.uploadFileList.shift();
this.uploadImg();
})
.catch(() => {
console.log(
"%c模拟接口执行上传事件----失败",
"color: yellowgreen;"
);
});
} else {
console.log("待上传文件列表全部传递完毕");
this.disabled = false;
}
},
mockFunc(type, file) {
if (type == 1) {
return new Promise((resolve, reject) => {
console.log("上传的文件为", file.name);
let params = new FormData();
params.append("file", file);
console.log("file", file);
setTimeout(() => {
let radio = file.size / 1024 / 1024;
if (radio <= 2) {
resolve(file.name);
} else {
reject();
}
}, 200);
});
} else {
return new Promise((resolve, reject) => {
let params = new FormData();
let fileName = [];
file.forEach((item) => {
let radio = item.size / 1024 / 1024;
if (radio <= 2) {
params.append("file", item);
fileName.push(item.name);
}
});
console.log("file", file);
setTimeout(() => {
resolve(fileName);
}, 200);
});
}
},
结果演示
- 未调用实际接口,在 mockFunc 中记录文件名称,来模拟后台接口响应中返回的文件云存储路径。
- 调用接口结束后,使用接口响应中返回的云存储路径覆盖掉 fileList,使上传组件回显的图片集合,与上传成功后的图片集合保持一致
调用 uploadImgs 多图上传
- 选择三张小于 2M 的图片上传
- change 事件被触发 3 次,在第 3 次结束后,1000ms 内,未被再次触发,开始调用上传接口
- 上传接口调用成功,返回结果
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f739199c356e13f10ba5429fefd6df05.png)
- 选择两张小于 2M 的图片,一张大于 2M 的图片上传
- change 事件被触发 3 次,每触发一次,向待上传数组 uploadFileList 中塞一个 file 对象,在第 3 次结束后,1000ms 内,未被再次触发,开始调用上传接口,将 uploadFileList 传递给后台
- 上传接口调用成功,返回结果,结果集合的长度为 2,表示上传图片列表中有一张上传失败了,在实际接口应用中,使用接口响应结果覆盖掉 el-upload 绑定的 fileList 即可
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a13542e6bd8d42c79780c44d380ed8d5.png)
调用 uploadImg 单图上传
- 选择三张小于 2M 的图片上传
- change 事件被触发 3 次,每触发一次,向待上传数组 uploadFileList 中塞一个 file 对象,在第 3 次结束后,1000ms 内,未被再次触发,开始递归调用上传接口
- 假设 uploadFileList = [img1,img2,img3] ,此时 uploadFileList 长度为 3,表示当前有 3 张图片待上传,调用接口,传递 uploadFileList[0] 对象,即 img1,调用结束后,将 uploadFileList 第一项删除,此时 uploadFileList = [img2,img3]
- 继续调用上传接口,此时 uploadFileList 长度为 2,表示当前有两张图片待上传,调用接口,传递 uploadFileList[0] 对象,即 img2,调用结束后,将 uploadFileList 第一项删除,此时 uploadFileList = [img3]
- 继续调用上传接口,此时 uploadFileList 长度为 1,表示当前有 1 张图片待上传,调用接口,传递 uploadFileList[0] 对象,即 img3,调用结束后,将 uploadFileList 第一项删除,此时 uploadFileList = []
- 继续嗲用上传接口,此时 uploadFileList 长度为 0,表示待上传图片已经全部上传完毕,结束上传进程
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/cdec307c488d1e9fc1942fd92c2fd1c9.png)
- 选择两张小于 2M 的图片,一张大于 2M 的图片上传
- 递归流程同上
- 上传接口调用成功,返回结果,结果集合的长度为 2,表示上传图片列表中有一张上传失败了,在实际接口应用中,使用接口响应结果覆盖掉 el-upload 绑定的 fileList 即可
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a50d0dddd644d10d4d72bce061cf4af4.png)