Vue 实现多图上传并可以调整图片顺序

更新日期: 2019-07-31 阅读次数: 18611 字数: 919 分类: VueJS

由于 Element UI 的 Upload 上传组件只支持多图上传,但并不支持图片的顺序调整,而实际使用场景中,例如,产品封面轮播图,经常要用到图片顺序调整功能。所以,找了一下解决方案。

一些备选方案

JQuery UI sortable

效果演示,参考:https://codepen.io/malkafly/pen/gbVYZb

上面示例中使用的排序组件是 jquery ui 中的 sortable 功能。

但是,我测试的过程中,感觉 jquery ui 的这个功能非常不流畅,经常出现没有响应的情况。

Sortablejs

找到一个竞品 sortablejs, 从对比视频上看,优化效果明显。里面有视频演示,及对比:https://sortablejs.github.io/Sortable/

而且有 vue 版本的实现。

Vue draggable 项目地址:https://github.com/SortableJS/Vue.Draggable

带删除按钮的 vue draggable 实现:https://jsfiddle.net/dede89/5Leuhh1n/

代码实现

我觉得使用 Element UI upload 组件结合 Vue draggable 来实现上传后的图片调整顺序。

补充:结合使用,还间接修复了一个 Element UI upload 的一个巨大缺陷,即,无法同时上传多张图片。因为,选择多张图片上传时,upload 组件的 on-success 回调,只会被调用一次。最简单的解决方案是,将 file-list 去掉,引入了 draggable 之后,不再需要 upload 组件来管理删除,和排序,所有 file-list 去掉,没有任何额外工作量。

查看 Demo 效果页面

效果 Gif 截图:

Vue 实现多图上传并可以调整图片顺序

实现代码

注意,这是一个基于 Laravel PHP 模板的代码,vue 变量为了避免冲突,在变量前加上了 @ 符号。转换的时候,需要去掉

模板代码

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Element UI 上传组件结合 Vue Draggable 实现拖拽排序</title>
    <meta name="description" content="">
    <meta name="keywords" content="">

    <!-- Set render engine for 360 browser -->
    <meta name="renderer" content="webkit">

    <!-- 禁止百度移动搜索转码 -->
    <meta http-equiv="Cache-Control" content="no-transform" />
    <meta http-equiv="Cache-Control" content="no-siteapp" />

    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.4.0/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.4.11/theme-chalk/index.css" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/animate.css/3.7.0/animate.min.css" />
    <link rel="stylesheet" href="{{ URL::asset('css/footer.css') }}?t=201812061243">

    <style type="text/css">
        input.el-upload__input {
          display: none;
        }
    </style>
</head>

<body>
    <div class="container" style="margin-bottom: 20px;">
        <h1>Element UI 上传组件结合 Vue Draggable 实现拖拽排序</h1>
    </div>

    <div class="container" id="upload_images">
        <p>
        由于默认的 Element UI upload 组件不支持多图上传后的拖拽排序。
        </p>
        <p>
        所以,测试一下 Element UI upload 组件 + Vue Draggable 来实现排序功能。
        </p>
        <p>
        Vue Draggable 是基于 sortablejs 实现的,所以操作体验上相对 jQuery UI Sortable 要流畅很多。
        </p>
        <div class="row">
            <div class="col-sm-12 col-md-12 main">
				<el-upload
                     class="upload-demo"
                     action=""
                     multiple
                     :limit="10"
                     :on-success="handle_success"
                     list-type="picture-card"
                     :file-list="images">
                      <el-button size="small" type="primary">点击上传</el-button>
                      <div slot="tip" class="el-upload__tip">上方为 element ui upload 组件自带的列表样式,不支持拖拽排序</div>
				</el-upload>

				<div class="drag">
					<h2>Vue Draggable - 可在下方尝试拖拽</h2>
                    <ul class="el-upload-list el-upload-list--picture-card">
                      <draggable v-model="images" class="dragArea">
                        <li :tabindex="index" class="el-upload-list__item is-success animated" style=""
                            v-for="(element, index) in images"
                            v-bind:class="{flash: element.to_del}"
                            >
                            <img :src="element.url" alt="" class="el-upload-list__item-thumbnail ">
                            <a class="el-upload-list__item-name">
                                <i class="el-icon-document"></i>
                                @{{element.name}}
                            </a>
                            <label class="el-upload-list__item-status-label">
                                <i class="el-icon-upload-success el-icon-check"></i>
                            </label>
                            <i class="el-icon-close"></i>
                            <span class="el-upload-list__item-actions"><!---->
                                <span class="el-upload-list__item-delete">
                                    <i class="el-icon-delete" @click="remove(index)"></i>
                                </span>
                            </span>
                        </li>
                      </draggable>
                    </ul>
				</div>

                <div style="margin-bottom: 50px;">
					<h2>列表数据</h2>
                    <div >
                        <p v-for="(element, index) in images">
                            @{{element.name}} - @{{element.url}}
                        </p>
                    </div>
                </div>

            </div>
        </div>
    </div>

    <script type="text/javascript" src="https://cdn.staticfile.org/vue/2.5.22/vue.min.js"></script>
    <script type="text/javascript" src="https://cdn.staticfile.org/Sortable/1.8.1/Sortable.min.js"></script>
    <script type="text/javascript" src="https://cdn.staticfile.org/Vue.Draggable/2.17.0/vuedraggable.min.js"></script>
    <script type="text/javascript" src="https://cdn.staticfile.org/element-ui/2.4.11/index.js"></script>
    <script type="text/javascript" src="{{ asset('js/upload_images_and_sort.js') }}?t=201901212007"></script>
</body>

</html>

vuejs 代码

new Vue({
  el: "#upload_images",

  data: {
    images: [
      {
        name: 'img1',
        to_del: false,      // 纯粹为了删除特效,可不加
        url: 'http://temp.im/401x401/4CD964/fff'
      },
      {
        name: 'img2',
        to_del: false,
        url: 'http://temp.im/402x402/4CD964/fff'
      },
      {
        name: 'img3',
        to_del: false,
        url: 'http://temp.im/403x403/4CD964/fff'
      },
      {
        name: 'img4',
        to_del: false,
        url: 'http://temp.im/404x404/4CD964/fff'
      }
    ]
  },

  mounted: function() {
  },

  methods: {
    handle_success: function() {
    },

    remove: function(index) {
      var that = this;
      this.$confirm('确认删除?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(function () {
        that.$message({
          type: 'success',
          message: '删除成功!'
        });
        that.images[index].to_del = true;
        setTimeout(function() {
          that.images.splice(index, 1);
        }, 1000);
      })
    }
  }
})

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式