VUE2+ElementUI list-type类型照片墙上传爬坑记录


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  设置的值


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM