记一次elementui图片上传功能的重构


为实现图片的拖拽排序而重写upload组件的踩坑填坑过程

1. 拖拽的实现

1.dom操作

原本已经封装好的elementui来说,它的图片是一个fileList的数组,而拖拽的话,需要对dom进行操作,因此,我们首先想到的是能否取到img的src并将其放在一个div中,循环遍历显示出我们上传后的图片(下文中的fileArray只是一个假定值,代表我们在data里面定义的这个model值)

1
2
3
<div v-for="item in fileArray">
<img src="item中存放的地址">
</div>

这里要说的是,已经上传成功的response值和我们在上传图片成功时的返回值的数据结构并不一样。因此我们需要想办法取其共同点并且构造成一个新的数组,得到我们的fileArray。

1
2
3
4
5
fnBsnUploadPicSuccess(response, file, fileList) {
if (response && response.result && response.result.res_path) {
this.fileArray = this.fileArray.concat(response.result);
}
}

2.拖拽

这个直接利用组件去实现,最开始的时候我选用的是vue.draggable,star还不错,效果也有,但是问题出现了,保存之后的结果依然是未排序之前的?这,难道它只实现了拖拽效果,而没有实现排序之后的数据结构的改变?尴尬了。我要去怎么搞?再搞一插件?给每个类都加一个class?来存储这排序之后的东西么?网上查了很久,也没有发现特别有用的,有些垂直列表之类的可以利用偏移值,然而我这是不确定的一个个排列下来的行内元素啊。突然转机,发现一个感觉还可以的插件,既能排序,也能保存,开心。立马抛弃了之前的vue.draggable,换上了新的vue-dragging。效果也是棒棒哒~开心!好景不长!当我把这个插件用到同一个页面中的另一个功能相同的组件中时,报错了!这里面一个非常关键的值就是找不到!悲伤的是,这个并不晓得去哪里解决这个问题,而且发现issue里面也有和我类似的问题,,,悲伤,,,好吧,再次回归到vue.draggable,star这么多一定是有的它的道理,而且人家应该也会考虑到这种问题吧。认真看了它的文档,重要的是down下它的demo。好吧,傻的人家有model用来存放他们的顺序。

1
2
3
4
5
<draggable v-model="myArray" :options="{draggable:'.item'}">
<div v-for="item in fileArray">
<img src="item中存放的地址">
</div>
</draggable>

其中myArray就是用来存放顺序的数组,options呢暂时还没有发现它的作用。折腾来折腾去又回到了原点。

  • 学习一个插件最快的方式是它的demo
  • 插件的话,一定要选择可用性可维护性更强的。
  • 文档要好好读

    3.图片的查看

    直接将图片放入一个对话框,并且通过查看按钮控制对话框的显示与隐藏即可。
    1
    2
    3
    4
    5
    // 点击查看图片,注意此时还是利用数组下标来实现
    fnClickPreviewPic(index) {
    this.布尔值 = true;
    this.sImgUrl = 图片地址
    },

4.删除

与查看相似利用数组的原生方法即可

1
2
3
4
// 删除图片
fnBsnRemovePic(index) {
this.fileArray.splice(index, 1);
},

5. 类似elementui的遮罩层和按钮的实现

这个网站还不错,很多hover效果大家都可以好好参考一下
利用tranform和opacity,比较关键的点就是元素hover之后对其他标签做的效果。

1
2
3
4
<div class="mask mask1")>
<i class="el-icon-zoom-in"></i>
<i class="el-icon-delete"></i>
</div>

拖拽并没有单独做一个按钮,因为其实它的效果并不是你点到某个按钮才会去实现这个效果。而是随便点到图片的某个地方即可,因此只是将鼠标的样式改成了move。这样可能会和上面的按钮冲突,因此我们将按钮的点击区域设置为一个固定的很小的地方,在这里面cursor为pointer,而其他地方我们则给他换回move。

以上步骤都完成之后,看似很完美,但是如果你需要对上传的总数做限制,那么你就happy了,这个limit真的是脑细胞都要死光了。

2.让我想砸电脑的limit

elementui里面实现一个这个真的是很简单啊,如果你设置了limit为一个数之后,只要它超过这个数,就是走到on-exceed函数。然而,我们现在弃用了它,只保留了上传的功能,如果只是将它设置为一个固定值,是肯定不行滴。

think one

因为我们能得到这次上传的值,以及我们已经上传的值,如果它超过这个值,就抛出错误,这样好像很完美,ok,试之。ok,为limit绑定一个值num,并且在初始化时将它的值设置为总长度-已经上传的图片数量,每次上传成功或者删除图片时都去修改这个值。ok,开始时没有问题,可是当我们删除几个再次添加时候函数走到on-exceed。失败~

think two

不绑变量了,ok,我们直接给他设置成总长度-已经上传的图片数量,并且我打印了这个值,嗯嗯,看起来好像很符合要求,简单试了一下上传多个的,ok,完全没有问题啊,就在我以为问题解决了而窃喜的时候,问题又来了、、、,如果我此时已经上传了4张,限制数量为6张,limit此时为3,而我即使只是上传一张,也没有成功。。。各个环节都没有问题啊,到底是为什么。

think three

在on-success中直接对fileArray做剪切,只显示其中的前6个,ok,这样确实看起来起到了效果,但是问题是,这些图片已经全部上传成功,对后端服务的压力很大,也并不符合我们设计的初衷。

think four

before-upload,这个函数是我曾经一度想使用看看的,可以在上传图片之前对图片做处理。如果超出限制,可以避免think three中的问题,然而,这个函数每上传一张都会执行一次,我们可以去维护一个变量监测到底有没有超出limit,但是当我们批量上传的时候,它可能还是存在只是没有超出limit的图片上传成功,而后面的都失败了,这样的用户体验并不好。也许排在后面的才是我们想上传的。

think five

想来想去,还是要从limit入手。到底为什么删删传传几次之后即使 要上传的数量<limit还是会执行on-exceed函数呢???后面查了很多资料,有些不太记得了,elementui的源码中,封装的这个函数的源码,如果files的长度+fileList的长度>limit那么就会执行这个函数。ok算是有点思路了,毕竟知道问题处在哪里,就有一点点儿方向了。

删除上传的时候,我们并没有对fileList做操作。导致它可能会有一个累加的效果。因此,我们要对这一部分数据做处理。

1
limit长度=限制长度 - fileArray的长度 + fileList.length;

这大概是最核心的地方了。虽然只有一行代码。

自己测试了一下,暂时并没有发现什么问题。

3.写在最后

  • 也许当你想修改一个东西的时候,搞清它的原理很重要,毕竟知己知彼方能百战不殆
  • 要想学一个陌生的东西,demo是个不错的快速上手的方式
  • 选择是一个很重要的方式,有些东西不必坚持,不必备选,有些需要坚持。看它的star值不值得了。
  • 不得不说的是,写这个东西真的花费了我很多的时间,确实最后代码量并没有多少,让人觉得你可能有偷懒的成分在里面,但是我并不想因此被别人否定如果它的时间与代码的长度不成正比。因为我真的有认真思考这个东西。
  • 有些你觉得不可能的事情还是会实现,比如这个东西我真的很想放弃它
文章目录
  1. 1. 1. 拖拽的实现
    1. 1.1. 1.dom操作
    2. 1.2. 2.拖拽
    3. 1.3. 3.图片的查看
    4. 1.4. 4.删除
    5. 1.5. 5. 类似elementui的遮罩层和按钮的实现
  2. 2. 2.让我想砸电脑的limit
    1. 2.1. think one
    2. 2.2. think two
    3. 2.3. think three
    4. 2.4. think four
    5. 2.5. think five
  3. 3. 3.写在最后
|