前言
今天分享一个在 vue-element-admin 中使用的上传组件,使用 elementUI 仪表盘形进度条,可以上传图片、视频。图片、视频尺寸根据传入的参数改变。
效果
报错 Invalid prop: custom validator check failed for prop "percentage".
出现这个错误的原因,并不是因为 dom 已经渲染完成了,而数据还没回来。而是因为 percentage 超出正常取值(0~100)范围。
因为人为的控制进度,所以 percentage 会出现负数现象。
控制进度是因为,后端处理上传数据需要时间,前端已经传完了,但是资源地址返回会慢一些。如果进度条过早走到 100% 而资源地址还没拿到显示不出来,会让用户迷惑。
// 上传进度调用方法实现
xhr.upload.onprogress = function(evt) {
// evt.total是需要传输的总字节,evt.loaded是已经传输的字节。如果evt.lengthComputable不为真,则evt.total等于0
if (evt.lengthComputable) {
// 进度条限制 上传时永远不能达到100% 最大95% 除非收到后端返回值 才设置为100%
$v.percentage = Math.floor(evt.loaded / evt.total * 100) - 5
console.log($v.percentage)
}
}
找到问题后就很好解决了
$v.percentage < 0 ? $v.percentage = 0 : $v.percentage
源码
<template>
<div>
<el-upload
v-if="type=='img'"
:multiple="false"
class="avatar-uploader"
action="file/cp-file/upload"
:show-file-list="false"
:before-upload="beforeUpload"
>
<img v-if="sourceURL" :src="sourceURL" :class="['avatar',wh]">
<i v-else :class="['el-icon-plus','avatar-uploader-icon',wh]" />
</el-upload>
<el-upload
v-if="type=='video'"
:multiple="false"
class="avatar-uploader"
action="file/cp-file/upload"
:show-file-list="false"
:before-upload="beforeUpload"
>
<video v-if="sourceURL" :poster="postUrl" :src="sourceURL" autoplay loop>
您的浏览器不支持 video 标签。
</video>
<i v-else :class="['el-icon-plus','avatar-uploader-icon',wh]" />
</el-upload>
<!-- 上传进度条 -->
<!-- Invalid prop: custom validator check failed for prop "percentage". -->
<el-progress v-if="showProgress" type="dashboard" :percentage="percentage" :color="colors" />
</div>
</template>
<script>
// import { fileUpload } from '@/api/common'
import { getToken } from '@/utils/auth'
export default {
name: 'FileUpload',
props: {
name: {
type: String,
default: ''
},
fileUrl: {
type: String,
default: ''
},
postUrl: {
type: String,
default: ''
},
wh: {
type: String,
default: ''
},
type: {
type: String,
default: 'img'
}
},
data() {
return {
percentage: 0,
colors: [
{ color: '#f56c6c', percentage: 20 },
{ color: '#e6a23c', percentage: 40 },
{ color: '#5cb87a', percentage: 60 },
{ color: '#1989fa', percentage: 80 },
{ color: '#6f7ad3', percentage: 100 }
],
showProgress: false,
sourceURL: ''
}
},
watch: {
fileUrl() {
this.sourceURL = this.fileUrl
}
},
mounted() {
this.sourceURL = this.fileUrl
},
methods: {
// 上传预处理
beforeUpload(file) {
if (this.type === 'img') {
const isLt20M = file.size / 1024 / 1024 < 20
if (file.type.indexOf('image/') === -1) {
this.$message.error('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。')
} else {
if (!isLt20M) {
this.$message.error('上传图片大小不能超过 20MB!')
} else {
this.handleUpload(file)
}
}
} else {
// 视频上传
const isLt100M = file.size / 1024 / 1024 < 100
if (file.type.indexOf('video/') === -1) {
this.$message.error('文件格式错误,请上传MP4格式的视频。')
} else {
if (!isLt100M) {
this.$message.error('上传视频大小不能超过 100MB!')
} else {
this.handleUpload(file)
}
}
}
},
handleUpload(file) {
var $v = this
$v.percentage = 0
$v.showProgress = true
var form = new FormData() // FormData 对象
form.append('file', file) // 文件对象
var xhr
xhr = new XMLHttpRequest()// XMLHttpRequest 对象
// Token不能为空
xhr.open('post', 'http://xxxxx/file/cp-file/upload', true) // post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。
xhr.setRequestHeader('User-Token', getToken())
xhr.onload = function(evt) { // 请求完成
// 服务器接收完文件返回的结果
var res = JSON.parse(evt.target.responseText)
if (res.code === 200) {
// 上传成功
$v.percentage = 100
$v.sourceURL = res.data.url
$v.$emit('handleUploadSuccess', $v.name, res.data)
$v.showProgress = false
}
}
xhr.onerror = function() {
$v.$message.error('上传失败')
}
// 上传进度调用方法实现
xhr.upload.onprogress = function(evt) {
// evt.total是需要传输的总字节,evt.loaded是已经传输的字节。如果evt.lengthComputable不为真,则evt.total等于0
if (evt.lengthComputable) {
// 进度条限制 上传时永远不能达到100% 最大95% 除非收到后端返回值 才设置为100%
$v.percentage = Math.floor(evt.loaded / evt.total * 100) - 5
$v.percentage < 0 ? $v.percentage = 0 : $v.percentage
}
}
xhr.send(form) // 开始上传,发送form数据
}
}
}
</script>
<style lang="scss">
// undo 如有 scoped 则父组件样式无法渲染?
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
//资讯话题分类
.newTypeWidthHeight{
width: 159px;
height: 207px;
line-height: 207px;
}
//会员权益身份icon样式
.memberPriceWidthHeight {
width: 100px;
height: 100px;
line-height: 100px;
}
//资讯封面
.newImgWidthHeight{
width: 270px;
height: 180px;
line-height: 180px;
}
//视频封面
.newVideoWidthHeight{
width: 640px;
height: 360px;
line-height: 360px;
}
.avatar {
display: block;
}
video{
width: 640px;
height: 360px;
// 拉伸以适应内容框,解决视频黑边
object-fit: cover;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
text-align: center;
}
</style>