基于VueDraggable和Element-ui的图片拖拽改变顺序的组件

我们公司管理后台项目是使用Element-ui组件,这次需求产品要求上传的图片组允许拖拽排序,我就想用vue-draggable插件了,但是相信Element-ui的el-upload组件封装的很好,我这种菜鸟级别的前端哪里敢动,所以我就想着上传依然用el-upload,但是把上传组件的展示图片隐藏,自己根据组件的上传之后拿到的url链接自己形成图片数组,然后展示层由我们自己来写UI和vue-draggable的拖拽,话不多说,上代码,直接莽!

emmm...先来两张效果图吧

 

<template>
  <div class="com-image-drag">
    <div class="button-list">
      <el-button
        @click="openDrag"
        v-if="!drag_open"
        :disabled="banner_list.length <= 1"
        type="text"
        size="small"
        class="operation-success"
      ></el-button>
      <el-button
        @click="save"
        v-if="drag_open"
        type="text"
        size="small"
        class="operation-success"
      ></el-button>
      <el-button
        @click="cancle"
        v-if="drag_open"
        type="text"
        size="small"
        class="operation-error"
      ></el-button>
    </div>
    <div class="image-list">
      <!-- 拖拽层 -->
      <div class="list-wrap" v-show="drag_open">
        <draggable
          v-model="banner_list"
          :options="{
            animation: 150,
            ghostClass: 'sortable-ghost',
            chosenClass: 'chosenClass',
            scroll: true,
            scrollSensitivity: 200
          }"
        >
          <div
            class="image-item"
            v-for="($item, $index) in banner_list"
            :key="$index"
            :style="{ backgroundImage: `url(${$item.url})` }"
          ></div>
        </draggable>
      </div>
      <!-- 展示层 -->
      <div class="list-wrap" v-show="!drag_open">
        <div
          class="image-item"
          v-for="($item, $index) in banner_list"
          :key="$index"
          :style="{ backgroundImage: `url(${$item.url})` }"
          @mouseover.prevent="$item.is_hover = true"
          @mouseleave.prevent="$item.is_hover = false"
        >
          <div class="label" v-show="!$item.is_hover">
            <i class="el-icon-upload-success el-icon-check icon-success"></i>
          </div>
          <div class="mask" v-show="$item.is_hover">
            <i class="el-icon-delete bin" @click="deleteImage($index)"></i>
          </div>
        </div>
        <el-upload
          v-show="limit == 0 || banner_list.length < limit"
          list-type="picture-card"
          name="file"
          class="upload-machine"
          :disabled="drag_open"
          :action="action()"
          :on-error="onError"
          :on-success="onSuccess"
          :before-upload="beforeUpload"
          :show-file-list="false"
          :multiple="multiple"
          enctype="multipart/form-data"
        ></el-upload>
      </div>
    </div>
  </div>
</template>
<script>
/**
 * @author LeeYunxiang
 * @description 为了方便上传图片组件可拖拽排序,不改变饿了么插件的逻辑,只做视图层的展示
 * @param {Array} list 图片数组
 * @param {Number} limit 最多可上传几张图片
 * @param {Function} action 上传接口地址
 * @param {Boolean} multiple 是否批量上传
 * @param {Function} beforeUpload 上传之前的回调,用于校验
 * @param {Function} onSuccess 上传成功的回调函数
 * @param {Function} onError 上传失败的回调函数
 */
import draggable from "vuedraggable";
export default {
  name: "ComImageShow",
  components: {
    draggable
  },
  props: {
    list: {
      type: Array
    },
    limit: {
      type: Number,
      default: 0
    },
    multiple: {
      type: Boolean,
      default: false
    },
    action: {
      type: Function,
      default: () => {}
    },
    beforeUpload: {
      type: Function,
      default: () => {}
    },
    onError: {
      type: Function,
      default: () => {}
    },
    onSuccess: {
      type: Function,
      default: () => {}
    }
  },
  data() {
    return {
      banner_list: [], //拖拽插件不建议直接改变父组件的传值,所以另建一个新数组
      file_list: [], //保存开启拖拽之前排序的数组
      drag_open: false //拖拽开启开关
    };
  },
  methods: {
    // 删除图片
    deleteImage(i) {
      this.banner_list.splice(i, 1);
      this.$emit("update", this.banner_list.map(item => item.url));
    },
    // 开启拖拽
    openDrag() {
      this.file_list = JSON.parse(JSON.stringify(this.banner_list)); //数组深拷贝
      this.drag_open = true;
    },
    // 取消拖拽
    cancle() {
      this.banner_list = this.file_list;
      this.drag_open = false;
    },
    // 拖拽保存
    save() {
      this.$emit("update", this.banner_list.map(item => item.url));
      this.drag_open = false;
    }
  },
  mounted() {
    // 初始数组拷贝
    this.banner_list = this.list.map(url => {
      let obj = {
        url: url,
        is_hover: false
      };
      return obj;
    });
  },
  watch: {
    // 监听父组件传值改变
    list(arr) {
      if (arr.length > this.limit && this.limit != 0) {
        this.$message.warning(`当前最多可上传${this.limit}张图片`);
        return false;
      }
      this.banner_list = arr.map(url => {
        let obj = {
          url: url,
          is_hover: false
        };
        return obj;
      });
    }
  }
};
</script>
<style lang="sass" scoped>
.com-image-drag
  &:after
      display: block
      clear: both
      content: ""
  .image-list
    float: left
    &:after
      display: block
      clear: both
      content: ""
    .list-wrap
     float: left
    .image-item
      width: 148px
      height: 148px
      position: relative
      margin-right: 10px
      margin-bottom: 10px
      border: 1px solid #c0ccda
      background-size: 100% 100%
      border-radius: 6px
      float: left
      overflow: hidden
      cursor: pointer
      .label
        width: 46px
        height: 26px
        background-color: #13ce66
        color: #FFFFFF
        transform: rotate(45deg)
        text-align: center
        position: absolute
        right: -17px
        top: -7px
        .icon-success
          transform: rotate(-45deg)
      .mask
        width: 100%
        height: 100%
        border-radius: 6px
        background-color: rgba(0, 0, 0, 0.5)
        position: relative
        .bin
          color: #FFFFFF
          font-size: 20px
          position: absolute
          left: 45%
          top: 43%
  .upload-machine
    float: left
</style>

调用例子

<template>
    <image-drag
         :list="file_list" 
         :multiple="true"
         :action="uploadUrl" 
         :on-error="uploadError" 
         :on-success="bannerPicSuccess" 
         :before-upload="beforeAvatarUpload"
         @update="updateFile">
    </image-drag>
</template>
<script>
import ImageDrag from "@/components/common/ComImageDrag";
import { Loading } from "element-ui";
export default {
  components: {
    ImageDrag
  },
  data() {
    return {
      banner_list: [], //ele用的
      file_list: [], //自己用的
      bargain: {
        share_image: ""
      },
      number: ""
    };
  },
  methods: {
    goBack() {
      this.$router.go(-1);
    },
    // 上传图片路径
    uploadUrl() {
      return `${process.env.VUE_APP_API_ROOT}upload`;
    },
    // 图片长传-之前
    beforeAvatarUpload(file) {
      let self = this;
      let type_arr = ["image/jpeg", "image/png"];
      let type = file.type;
      if (!type_arr.includes(type)) {
        this.$message.error("图片格式不正确,只支持jpg和png类型图片");
        return false;
      }
      const is_size = new Promise((resolve, reject) => {
        let width = 400;
        let height = 320;
        let img = new Image();
        img.src = window.URL.createObjectURL(file);
        img.onload = () => {
          let valid = img.width === width && img.height === height;
          if (valid) {
            Loading.service({ fullscreen: true, text: "图片上传中,请稍后" });
            resolve(file);
          } else {
            self.$message.error("请上传400*320px大小的图片!");
            reject();
          }
        };
      });
      return is_size;
    },
    // Banner图-成功
    bannerPicSuccess(res) {
      this.bargain.share_image = res.data;
      Loading.service({ fullscreen: true }).close();
      this.file_list.push(res.data);
    },
    // Banner图片上传报错
    uploadError() {
      this.$message.error("上传失败,请重新上传");
      Loading.service({ fullscreen: true }).close();
    },
    updateFile(val) {
      this.file_list = val;
      console.log(this.file_list);
    }
  }
};
</script>

 

posted @ 2019-09-12 17:09  南山无鱼  阅读(7340)  评论(0编辑  收藏  举报