Element-Puls中el-upload组件结合vue-draggable-plus实现上传支持拖拽排序(并保留el-upload原有样式、预览、删除)等功能

2 篇文章 0 订阅
2 篇文章 0 订阅

需求

需求想要一个可拖拽排序的图片列表,但是发现el-upload虽然可以实现照片墙,但是没办法拖拽

实现思路

使用 vue-draggable-plus
拖拽插件,隐藏Upload原有的已上传文件列表,自定义上传后文件列表的样式并绑定预览和删除功能

安装VueDraggablePlus库

功能齐全:全面继承 Sortable.js 的所有功能

无缝迁移:适用于 Vue 3 和 Vue2

灵活使用:支持组件、指令、函数式调用

类型强:用 TypeScript 编写,带有完整的 TS 文档

数据绑定:支持 v-model 双向绑定,不需要单独维护排序数据

自定义容器:可以自定某个容器作为拖拽容器,比 Sortable.js 更灵活

安装指令

npm install vue-draggable-plus

yarn add vue-draggable-plus

pnpm add vue-draggable-plus

使用(组件方式)

封装成公共组件 /src/components/DraggableImageUpload/index.vue

HTML代码

<template>
  <div class="draggable_image_upload">
    <VueDraggable 
    	class="box-uploader" 
    	ref="draggableRef" 
    	v-model="curList" 
    	:animation="600" 
    	easing="ease-out"
     	ghostClass="ghost" 
      	draggable="ul" 
      	@start="onStart" 
      	@update="onUpdate"
    >
      <!-- 使用element-ui el-upload自带样式 -->
      <ul v-for="(item, index) in curList" :key="index" class="el-upload-list el-upload-list--picture-card">
        <li class="el-upload-list__item is-success animated">
          <el-image class="originalImg" :src="item.url" :preview-src-list="[item.url]" />
          <!--  <el-icon>
            <Close />
          </el-icon> -->
          <label class="el-upload-list__item-status-label">
            <el-icon class="el-icon--upload-success el-icon--check">
              <Check />
            </el-icon>
          </label>
          <span class="el-upload-list__item-actions">
            <!-- 预览    -->
            <span class="el-upload-list__item-preview" @click="handleImgPreview('originalImg', index)">
              <el-icon>
                <ZoomIn />
              </el-icon>
            </span>
            <!-- 删除    -->
            <span class="el-upload-list__item-delete" @click="onImageRemove(item.url)">
              <el-icon>
                <Delete />
              </el-icon>
            </span>
          </span>
        </li>
      </ul>
      <!-- 上传组件 -->
      <el-upload
          v-model:file-list="curList"
          :action="uploadUrl"
          :multiple="multiple"
          list-type="picture-card"
          :accept="accept"
          :show-file-list="false"
          :before-upload="beforeImgUpload"
          :on-success="handleImgSuccess"
          :headers="{
            Authorization: token
          }"
        >
          <el-icon><Plus /></el-icon>
        </el-upload>
    </VueDraggable>
  </div>
</template>

JS代码

<script name="DraggableImageUpload" setup lang="ts">
import { uploadUrl } from "@/api/utils"; //请求url
import { formatToken, getToken } from "@/utils/auth"; //请求头
import { message } from "@/utils/message";
import { Check, Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
import type { UploadProps } from "element-plus";
import { ElMessage } from "element-plus";
import { ComponentInternalInstance, computed, getCurrentInstance, onMounted, reactive, ref, toRefs } from 'vue';
import { type UseDraggableReturn, VueDraggable } from 'vue-draggable-plus';
 
const draggableRef = ref<UseDraggableReturn>()
 
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
const props = defineProps({
  //接受上传的文件类型(thumbnail-mode 模式下此参数无效)
  accept: {
    type: String,
    default: ".jpg,.jpeg,.png"
  },
  //允许上传文件的最大数量
  limit: {
    type: Number,
    default: 9999
  },
  //是否支持多选文件
  multiple: {
    type: Boolean,
    default: true
  },
  //是否自动上传文件
  autoUpload: {
    type: Boolean,
    default: false
  },
  modelValue: {
    type: Array,
    default: () => []
  },
});

const token = ref();
onMounted(() => { 
  token.value = formatToken(getToken().accessToken);
})

const data = reactive<any>({
  maxImgsLen: 0,
  imgList: [],
});
 
const { maxImgsLen, imgList} = toRefs(data)
 
const emit = defineEmits(["update:modelValue"]);
const curList: any = computed({
  get() {
    return props.modelValue;
  },
  set(newValue) {
    emit("update:modelValue", newValue);
  }
});
 
//元素开始拖拽
const onStart = () => {
  console.log('start')
}
 
//元素顺序更新时触发
const onUpdate = () => {
  console.log(curList.value, 'update++++++++++++++')
}
 
// 图片预览
const handleImgPreview = (domClass: string, index: number) => {
  /*   showViewer.value = true
    previewList.value = [url] */
  const dom = document.getElementsByClassName(domClass);
  (dom[index].firstElementChild as any).click(); //调用 el-image 的预览方法 
}
 
// 删除图片
const onImageRemove = (url: string) => {
  let list = utils.deepClone(curList.value)
  list.forEach((item: any, index: number) => {
    if (url === item.url) {
      list.splice(index, 1)
    }
  })
  curList.value = list
}
 
// 上传前校验
const beforeImgUpload: UploadProps["beforeUpload"] = rawFile => {
  let types = ["image/jpeg", "image/jpg", "image/png"];
  const size = rawFile.size
  const isImage = types.includes(rawFile.type);
  const isLt5M = size / 1024 / 1024 < 5
  if (!isImage) {
    message("请上传jpeg、jpg、png类型的图片", {
      type: "error"
    });
    return false;
  } else if(!isLt5M) {
  	message("上传图片大小不能超过5MB", {
      type: "error"
    });
    return false
  }
  return true;
};

// 上传成功,给图片加前缀,防止切换时图片不显示
function handleImgSuccess() {
  curList.value.forEach(item => {
    item.url = import.meta.env.VITE_IMG_URL + item.response.data.url;
  });
  // console.log('curList',curList.value)
}

</script>

style

<style lang="scss" scoped>
.draggable_image_upload {
 
  .box-uploader {
    display: flex;
    flex-wrap: wrap;
    vertical-align: middle;
 
    :deep(.el-upload) {
      border-radius: 4px;
      .circle-plus {
        width: 24px;
        height: 24px;
      }
 
      &.el-upload-list {
        &.el-upload__item {
          width: 100px;
          height: 100px;
          margin: 0 17px 17px 0;
          border-color: #e7e7e7;
          padding: 3px;
        }
      }
 
      &.el-upload--picture-card {
        width: 100px;
        height: 100px;
        line-height: 100px;
        border-style: dashed;
        margin-right: 17px;
      }
    }
 
    .el-upload-list__item {
      width: 100px;
      height: 100px;
      margin: 0 17px 17px 0;
      border-color: #e7e7e7;
      padding: 3px;
 
      .originalImg {
        :deep(.el-image__preview) {
          width: 100%;
          height: 100%;
          -o-object-fit: contain;
          object-fit: contain;
        }
      }
    }
 
    ul:nth-child(6n+6) {
      li {
        margin-right: 0;
      }
    }
  }
}
</style>

在 xxx.vue 文件中使用

<template>
  <el-dialog title="上传商品图片" v-model="infoVisible" :close-on-click-modal="false">
    <!-- 图片拖拽组件 -->
    <DraggableImageUpload accept=".jpg,.jpeg,.png,.gif" v-model="curList" />
  </el-dialog>
</template>
 
<script name="" setup lang="ts">
import DraggableImageUpload from '@/components/DraggableImageUpload/index.vue'
import { ComponentInternalInstance, getCurrentInstance, toRefs } from 'vue';
 
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
const data = reactive<any>({
  curList: [], // 需亚预览的数组
  infoVisible: false, //预览组件可见性
});
 
const { infoVisible, curList } = toRefs(data)
</script>

图片预览第二种实现方式

将 li 中 el-image 组件更改为 img标签,引入
el-image-viewer组件,并修改图片预览方法,下面只给出要调整的部分代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值