同事给到我一个新的需求,其中包括一个上传的图片列表的顺序调整功能,还需要通过拖拽图片实现调序,简单实现这个功能,并做一个记录
环境:Vue3 + element-ui,在组件el-upload中的多文件上传列表中实现,先上运行图,动图演示功能符合你的需求,你就接着往下看,不符合也节约你的时间

elementui dialog可拖动_elementui dialog可拖动

直接上源码吧,先是上布局部分
<el-upload
      v-model:file-list="fileList"
      :accept="fileType.join(',')"
      :action="updateUrl"
      :before-upload="beforeUpload"
      :class="['upload', drag ? 'no-border' : '']"
      :drag="drag"
      :headers="uploadHeaders"
      :limit="limit"
      :multiple="true"
      :on-error="uploadError"
      :on-exceed="handleExceed"
      :on-success="uploadSuccess"
      list-type="picture-card"
    >
      <div class="upload-empty">
        <slot name="empty">
          <Icon icon="ep:plus"/>
          <!-- <span>请上传图片</span> -->
        </slot>
      </div>
      <template #file="{ file }">
        <!--这里是拖拽的关键代码,使用一个div包裹住需要拖拽的控件,在此div上进行设置拖拽事件-->
        <div draggable="true" @dragstart="handleDragStart($event,file)" @drop="handleDrop($event,file)" @dragover.prevent>
          <img :src="file.url" class="upload-image"/>
          <div class="upload-handle" @click.stop>
            <div class="handle-icon" @click="handlePictureCardPreview(file)">
              <Icon icon="ep:zoom-in"/>
              <span>查看</span>
            </div>
            <div class="handle-icon" @click="handleRemove(file)">
              <Icon icon="ep:delete"/>
              <span>删除</span>
            </div>
          </div>

        </div>
      </template>
    </el-upload>

说明一下这段代码:这段代码的关键部分就是template中的div了,div上面也有注释,这里详细说一下,首先我们需要向div添加两个事件,

@dragstart="handleDragStart($event,file)" 
@drop="handleDrop($event,file)"

这两个事件第一个是点击按下的时候执行,第二个事件是鼠标松开时执行。传入第一个必传参数$event,再传入第二个自选参数file,我这里因为是用的上传组件,所以只能拿到file,拿不到file在fileList中的index,也可能是我没见识,欢迎给我指教,拿到file以后也可以获取到在fileList中的index,倒是没啥影响,这里有一点很关键,就是这两个事件传入的file不是同一个,start传入的是点击时被拖拽的file,drop中的file是鼠标指针释放时所指的file,这也是el-upload组件提供给我们的一个比较方便的处理方案,否则我可能还需要更复杂的方法来获取释放位置。

如果大家使用v-for实现的组件列表拖拽功能,就可以直接传入index了,稍后我会把这两个方法的具体实现放出来,
我们先看在div中很重要的一项设置:

@dragover.prevent

缺了这一项配置,那么可能会导致鼠标释放时不触发,这项配置是为了阻止浏览器默认的拖拽行为,有助于确保 @drop 事件被正确触发。

然后下面就到了关键的两个事件方法的实现了:
// 拖拽排序相关的函数
const handleDragStart = (event, file) => {
  // 输出被拖动的文件对象
  console.log(file);

  // 在当前fileList中查找被拖动文件的索引
  const index = fileList.value.findIndex(element => element === file);
  console.log(index);

  // 将被拖动文件的索引设置到dataTransfer对象中,以便在拖放时使用
  event.dataTransfer.setData('index', index.toString());
};

const handleDrop = (event, file) => {
  // 在当前fileList中查找被释放文件的索引
  const index = fileList.value.findIndex(element => element === file);
  console.log(index);

  // 阻止默认的拖放行为(例如打开链接等)
  event.preventDefault();

  // 从dataTransfer对象中获取被拖动项的索引
  const draggedIndex = Number(event.dataTransfer.getData('index'));

  // 从原始fileList中获取被拖动的项
  const draggedItem = fileList.value[draggedIndex];

  // 创建当前fileList的副本以进行修改
  const updatedList = [...fileList.value];

  // 从原始位置删除被拖动的项
  updatedList.splice(draggedIndex, 1);

  // 将被拖动的项插入到列表的新位置
  updatedList.splice(index, 0, draggedItem);

  // 使用修改后的列表更新fileList
  fileList.value = updatedList;

  // 发送一个事件通知父组件fileList已更新
  emit('update:modelValue', updatedList);
};

这两个方法每一句的注释都很清晰了,所以不需要再多做说明了。最后附上运行效果吧

elementui dialog可拖动_前端_02