1.说明
空间第一篇技术总结,给我的ElementUI图片上传。最近被安排使用ElementUI+VUE2来开发一个PC的应聘申请界面,在写上传多个图片的效果时,遇到许多问题,在这里记录一下。
2.问题总览
2.1 通过自定义方法上传图片
2.2 JavaScript Array的对象属性
2.3 this.$refs使用
2.4 上传、删除图片数据集合数据问题
2.5 this[] 类似java的反射赋值
2.6 通过http-request自定义方法上传图片
2.7 获取当前文件在数组中的下标
2.8 对象自定义方法
2.9 隐藏图片上传框
2.10 视图更新$set和强制更新视图this.$forceUpdate()页面
3.最终实现代码
<!--标签实现-->
<div class="div-title-two" ><i class="i-btx" ></i>身份证正面照</div>
<div>
<el-upload
action=""
class="upload-idCard"
list-type="picture-card"
:headers="{'Content-Type':'application/json'}"
:http-request="function(val){ customPictureUpload('idCard',val,1) }"
:file-list="idCardImageList"
ref="imgRef"
:on-remove="function(file ,raw){ handleRemovePic('idCard',file ,raw) }">
<i class="el-icon-plus"></i>
</el-upload>
</div>
//自定义上传方法
customPictureUpload (name , obj ,limit) {
//校验图片格式、size
let flag = this.beforePictureUpload(obj.file);
if(!flag) return;
//因为有多个模块上传框,所以这儿写成通过传入类型动态判断类型
//动态拼接ClassName; 即 .upload-idCardImageList
let className = '.upload-'+name;
//动态拼接数组名称 即:idCardImageList
let arrayName = name+"ImageList";
//动态拼接ref引用 : idCardRef
let refName = name+"Ref"
//获取当前上传文件数组里当前文件的下标序号
let index = this.$refs[refName].uploadFiles.indexOf(this.$refs[refName].getFile(obj.file))
//判断图片是否已到达上限limit
var imgs = this.$refs[refName].uploadFiles.length;
limit = limit -1 ;
//如果达到上限,则隐藏上传的框框
if(imgs == limit){
document.querySelector(className+" .el-upload--picture-card").style.display = 'none'
}
readerFile(obj.file).then(fileResult => {
//调用上传图片方法
uploadImg({
data: {
imgInfo: fileResult.base64,
token: sessionStorage.getItem('hr_apply_token'),
phone: this.ruleForm.phone
}
}).then(res=>{
if (+res['responseCode'] === 0) {
//给当前文件设置返回的res_path值
this.$refs[refName].uploadFiles[index].res_path =res.path
//判断当前文件前面是否有还在上传的文件,若有则等待
while(index == this[arrayName].length){
this[arrayName]= this.$refs[refName].uploadFiles
}
} else if(+res['responseCode'] === 5555){
this.$message({
showClose: true,
message: res['responseMsg'],
type: 'error'
});
this.$router.replace('/login');
} else{
this.$message({
showClose: true,
message: res['responseMsg'],
type: 'error'
});
document.querySelector(className+' .el-upload--picture-card').style.display = ''
}
})
})
},
//删除方法
handleRemovePic(name, file ,fileList) {
//拼接calssName
let className = '.upload-'+name;
//拼接数组名字
let arrayName = name+'ImageList';
//将删除后的fileList赋给数组
this[arrayName] = fileList
document.querySelector(className+' .el-upload--picture-card').style.display = ''
},
4.经验总结
4.1 通过自定义方法上传图片
ElementUI默认是通过action去调用图片上传,但是在实际使用中action在传数据方面比较麻烦,所以这儿可以通过 :http-request来自定义一个方法来上传图片。
4.2 JavaScript Array的对象属性
splice:splice在js中是用来对数组进行删除、替换操作,具体实现:
删除:
index代表要删除的数组的下标, num代表删除的数量
this.idCardImageList.splice(index,num)
替换:
item*可以有多个,当item的值为null时,即可置空index下标的值
this.idCardImageList.splice(index,num,item*)
every:用于遍历数组中元素是否都符合条件,若存在不符合元素则直接返回,不继续往下遍历
let picFlag = this.idCardImageList.every( item => {
if(item.res_path){
return true;
}else{
return false;
}
});
some: 与every正好相反,遍历数组中元素是否符合条件,若存在则直接返回,不继续往下遍历
let picFlag = this.idCardImageList.some( item => {
if(item.res_path){
return true;
}else{
return false;
}
});
forEach:遍历数组中的所有数据,并且会遍历遍历完
this.idCardImageList.forEach(item => {
item.validate((valid) => {
if (!valid) {
flagForm = false;
};;
})
});
indexOf: 获取当前元素数组中的下标
this.idCardImageList.forEach(item => let index = this.idCardImageList.indexOf(item))
filter():过滤,即返回符合条件的所有元素数组
this.idCardImageList.filter(item => item.age >18 );
push:向数组的最后面插入一/多个元素,并返回新数组的长度
this.idCardImageList.push({age:18,idcard:'www.baidu.com'});
[].forEach.call():是一种快速的方法访问forEach,并将空数组的this换成想要遍历的list
这个方法的使用,拿到querySelectorAll时需要遍历数组,具体看示例:
//将所有className为must的元素display样式设置为 block
[].forEach.call(document.querySelectorAll(".must"), item => item.style.display="block" );
4.3 this.$refs使用
首先说明下ref
ref:被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs对象上。如果在普通Dom元素上使用,引用指向的就是Dom元素,如果用在自组件上,就引用指向组件实例。
在业务处理中,我需要拿到ElementUI封装的uploadFiles对象,所以通过this.$refs.imgRef.uploadFiles,就能拿到实例中uploadFiles对象的数据然后赋值给我的this.imgList 就拿到了上传图片的数组。
4.4 上传、删除图片数据集合数据问题
在处理中会出现以下情况
情景一:
连续上传两张图片,第一张大,第二张小
问题:
小图上传成功并且大图消失,然后过了几秒大图出现
情景二:
上传一张大图,并且迅速删除一个已上传成功的图
问题:
已上传的图和正在上传的大图会同时被删掉,并且过了一会儿大图又重新加载出来。
情景三:
上传一张大图,并且迅速删除正在上传的图
问题:
正在上传的大图被删掉,并且过了一会儿大图又重新加载出来。
this.imgList.push(file) 引起的错乱
以上三个错误问题都是由于对 imgList赋值引起的,以情景一为例。
首先一个前提,上传图片是异步的。
我的处理逻辑是:
1、调用自定义上传图片方法
2、后台返回上传图片路径,进行this.imgList.push(file)
但是这样处理有一个问题,如果按情景一走就会出现
3、小图上传完成push进imgList,接着刷新视图大图消失
4、大图上传完,大图push进imgList,这时大图才显示
请求前就push?
出现问题后,猜想既然是因为异步问题引起,那么在请求之前就把数据push这种方法可行吗,接着按此操作结果:push之后视图样式就直接显示上传完成,猜想不可行。
拿到父组件的fileList集合
既然这条路走不同,那么把push代码注释掉再尝试,发现不会出现图片错乱的问题。于是猜有一个对象存了当前正在上传和已上传的图片的数据,查看源码发现有一个fileList对象,结合4.3 ref的使用,通过this.$ref.imgList.uploadFiles拿到完成图片上传后的FileList的文件和状态,不需要每次上传成功才push,可以直接把将当前的fileList集合赋给imgList。
push前判断一下
赋值给项目后,发现情景一情况并没有解决,于是再仔细分析发现:大图的位置还是被小图占了,因为不管如何处理肯定是小图先返回,那么就给当前的fileList与我的imgList的长度做一个判断,如果长度相等了代表前面没有待上传的图片,那么就不会把他的坑给占了。
//判断当前文件前面是否有还在上传的文件,若有则等待
while(index == this[arrayName].length){
this[arrayName]= this.$refs[refName].uploadFiles
}
4.5 this[] 类似java的反射赋值
这个方法是类似于java的反射实现对元素赋值取值,例如:this.imgList = this["imgList"], 有了这个就可以动态的设置进来的方法参数了。
4.6 通过http-request自定义方法上传图片
在整个方法http-request的方法执行中,on-change、on-progress、on-success等方法都会无效掉。
4.7 获取当前文件在数组中的下标
let index = this.imgList.indexOf(img);
4.8 方法处理中自定义方法
这个的处理是在array数组上增加删除的方法
Array.prototype.add = (item =>{
let index = this.indexOf(item)
this.splice(index,1)
return this;
})
4.9 隐藏图片上传框
有时图片上传会有个数限制,在达到上限后,上传框框就应该隐藏掉,目前用的是最笨的办法
document.querySelector(className+'.el-upload--picture-card').style.display = 'none'
有时间得去找找有没有vue的属性可以直接隐藏的。
4.10 视图更新$set和强制更新视图this.$forceUpdate()页面
this.$forceUpdate():强制更新视图
当自己设置的元素因为层次太深而无法更新时,可以通过调用this.$forceUpdate()来实现强制视图更新
$set:如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新。
this.$set(target, key, value) 等同于Vue.set( target, key, value )
{Object | Array} target 对象/数组
{string | number} key 设置的元素
{any} value 设置的值