就是把input type=file的opacity置为0,然后在底下放有样式的元素,然后通过axios的 onUploadProgress 的参数total和loaded显示进度,当然要在后端返回成功之后再把进度置为100%。然后监听一下文件数组,当所有进度都为1的时候就表示完成了。
<template>
<div id="upload">
<div class="bg-whi">
<div class="extendex-top" ref="top">
<div class="top-box">
<div class="g-title">上传附件</div>
</div>
</div>
<div class="pub-con" ref="bg">
<div class="input-zone">
<span class="upload-des">选择文件(不超过30m)</span>
<input
class="input"
type="file"
name="file"
value
placeholder="请选择文件"
ref="file"
v-show="canSub"
@change="changeFile()"
accept="application/pdf;image/*;application/mp4"
/>
</div>
</div>
<div class="pad">
<div class="file-list">
<div class="file-item" v-for="(item,index) in fileArray" :key="index">
<p>
{{item.name}}
<img
src="../../assets/image/upload-delete.png"
@click="deleteFile(index)"
v-show="item.progress==0"
class="upload-delete"
/>
</p>
<div class="progress-container" v-show="item.progress!=0">
<div class="progress-wrapper">
<div class="progress-progress" :style="'width:'+item.progress*100+'%'"></div>
</div>
<div class="progress-rate">
<span v-if="item.progress!=1">{{(item.progress*100).toFixed(0)}}%</span>
<img v-else class="upload-success" src="../../assets/image/upload-success.png" />
</div>
</div>
</div>
</div>
<div class="tips">支持上传jpg、png、ppt、pptx、MP3、MP4格式的附件!</div>
<div class="btn-container">
<button class="btn btn-blue" v-if="fileArray.length&&canSub" @click="upload">开始上传</button>
<button class="btn btn-gray" v-else>开始上传</button>
</div>
</div>
</div>
</div>
</template>
<script>
import serverUrl from "../../utils/common.js"; //取出公共url
import axios from "axios";
import { Message } from "element-ui";
export default {
data() {
return {
fileArray: [],//文件数组
canSub: true,
finish: false,
};
},
watch: {
//监听文件列表数组,当全部完成时给出提示并跳转页面
fileArray: {
handler(newValue, oldValue) {
console.log(newValue);
var finish = newValue.find(function(currentValue, index) {
return currentValue.progress != 1;
});
if (finish === undefined && this.fileArray.length) {
this.finish = true;
Message({
message: "上传成功",
type: "success",
duration: 1 * 1000,
customClass: "message-box",
iconClass: "message-icon"
});
this.$router.go(-1);
}
},
deep: true
}
},
methods: {
//删除指定文件
deleteFile(index) {
this.fileArray.splice(index, 1);
var fileTarget = this.$refs.file;
fileTarget.value = "";
},
//选择文件
changeFile() {
var fileTarget = this.$refs.file;
if (this.fileArray.length >= 10) {
Message({
message: "最多只能上传十个附件",
type: "success",
duration: 1 * 1000,
customClass: "message-box",
iconClass: "message-icon"
});
fileTarget.value = "";
} else {
if (fileTarget.files[0].size > 31457280) {
Message({
message: "文件大小不能超过30m",
type: "success",
duration: 1 * 1000,
customClass: "message-box",
iconClass: "message-icon"
});
fileTarget.value = "";
} else {
//避免重复的文件
if (
this.fileArray.indexOf(fileTarget.files[0]) == -1 ||
this.fileArray.length == 0
) {
var fileItem = Object.assign(fileTarget.files[0], { progress: 0 });
this.fileArray.push(fileItem);
}else{
fileTarget.value = "";
}
}
}
},
//上传
upload() {
if (this.docLength >= 10) {
Message({
message: "最多只能上传十个附件",
type: "success",
duration: 1 * 1000,
customClass: "message-box",
iconClass: "message-icon"
});
} else {
this.canSub = false;
var myArr = this.fileArray;
var vm = this;
let user = localStorage.user;
user = JSON.parse(user);
let token = user.token;
let id = this.$route.params.id;
//循环文件数组挨个上传
myArr.forEach((element, index) => {
var config = {
headers: { "Content-Type": "multipart/form-data" },
onUploadProgress: function(e) {
//属性lengthComputable主要表明总共需要完成的工作量和已经完成的工作是否可以被测量
//如果lengthComputable为false,就获取不到e.total和e.loaded
if (e.lengthComputable) {
var rate = e.loaded / e.total; //已上传的比例
console.log(index, e.loaded, e.total, rate);
if (rate < 1) {
//这里的进度只能表明文件已经上传到后台,但是后台有没有处理完还不知道
//因此不能直接显示为100%,不然用户会误以为已经上传完毕,关掉浏览器的话就可能导致上传失败
//等响应回来时,再将进度设为100%
myArr[index].progress = rate;
vm.$set(vm.fileArray, index, myArr[index]);
} else {
myArr[index].progress = 0.99;
vm.$set(vm.fileArray, index, myArr[index]);
}
}
}
};
var data = myArr[index];
var myForm = new FormData();
myForm.append("file", data);
myForm.append("courseId", id);
axios.defaults.headers.post["token"] = token;
axios
.post(serverUrl + "mobile/course/uploadFile", myForm, config)
.then(function(res) {
if (res.data.code == 0) {
//后端返回成功,将进度置为100%
myArr[index].progress = 1;
vm.$set(vm.fileArray, index, myArr[index]);
console.log(vm.fileArray, index);
} else {
alert(JSON.stringify(res.data));
}
})
.catch(function(err) {
console.log(err);
});
});
}
},
}
};
</script>
<style scoped>
.extendex-top {
background: none;
box-shadow: none;
}
.bg-whi {
min-height: 100vh;
box-sizing: border-box;
}
.top-box {
background: url(../../assets/image/detail-bg.png) top no-repeat;
background-size: 100%;
}
.pub-con {
width: 100%;
box-sizing: border-box;
background: url(../../assets/image/detail-bg.png) top no-repeat;
background-size: 100%;
text-align: center;
position: relative;
}
.input-zone {
width: 14.13rem;
height: 2.75rem;
line-height: 2.75rem;
background: url("../../assets/image/upload.png") no-repeat 0.63rem center
#ffffff;
background-size: 2.13rem;
border-radius: 1.38rem;
border: 0.06rem solid rgba(90, 88, 199, 1);
margin: 2.06rem auto;
position: relative;
color: #5a58c7;
font-size: 0.88rem;
padding-left: 2.5rem;
box-sizing: border-box;
}
.input {
opacity: 0;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 2;
}
.pad {
padding: 0.5rem 1.7rem 0 1.7rem;
font-size: 0.88rem;
}
.btn-container {
margin: 0.69rem auto;
text-align: center;
}
.btn-container .btn {
width: 10.56rem;
height: 2.5rem;
border-radius: 1.25rem;
border: none;
color: #ffffff;
}
.btn-container .btn.btn-gray {
background: rgba(201, 201, 201, 1);
}
.btn-container .btn.btn-blue {
background: linear-gradient(
180deg,
rgba(128, 137, 229, 1) 0%,
rgba(87, 84, 196, 1) 100%
);
font-size: 1rem;
}
.tips {
margin-top: 1.69rem;
}
.file-list {
font-size: 0.88rem;
color: #5a5cc6;
}
.file-list .file-item {
margin-top: 0.63rem;
}
.file-list .file-item p {
line-height: 1.25rem;
position: relative;
}
.file-list img {
width: 1.25rem;
cursor: pointer;
}
.file-list img.upload-delete {
position: absolute;
bottom: 0;
margin: 0 auto;
margin-left: 1rem;
}
.progress-wrapper {
position: relative;
height: 0.5rem;
border: 0.06rem solid rgba(92, 91, 200, 1);
border-radius: 1px;
box-sizing: border-box;
width: 87%;
}
.progress-wrapper .progress-progress {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 0%;
border-radius: 1px;
background-color: #5c5bc8;
z-index: 1;
}
.progress-rate {
font-size: 14px;
height: 100%;
z-index: 2;
width: 12%;
display: flex;
justify-content: center;
align-items: center;
}
.progress-rate span {
display: inline-block;
width: 100%;
text-align: right;
}
.progress-container {
display: flex;
align-items: center;
justify-content: space-between;
}
.file-list img.upload-success {
margin-left: 0;
}
</style>