同事给到我一个新的需求,其中包括一个上传的图片列表的顺序调整功能,还需要通过拖拽图片实现调序,简单实现这个功能,并做一个记录
环境:Vue3 + element-ui,在组件el-upload中的多文件上传列表中实现,先上运行图,动图演示功能符合你的需求,你就接着往下看,不符合也节约你的时间
直接上源码吧,先是上布局部分
<el-uploadv-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);// 使用修改后的列表更新fileListfileList.value = updatedList;// 发送一个事件通知父组件fileList已更新emit('update:modelValue', updatedList);
};
这两个方法每一句的注释都很清晰了,所以不需要再多做说明了。最后附上运行效果吧