本文首发于公众号:执行上下文,同步更新个人博客:坚果大叔,转载请署名。更多知识点不断更新中!!

前提
项目中使用element-plus ui框架作为基础项目组件,使用了其中的upload上传组件,在后续过程中提出需求需要支持拖拽排序,我...
但是原有的 upload 组件在其他方面都支持的很好,唯独没有没有支持拖拽这个功能,没有办法只能自己二次加工了。
组件的结构
组件主要分为三个部分:
- 上传 : 使用原有的
upload
组件 - 拖拽 : 使用第三方插件
draggable
来实现图片列表的拖拽效果 - 预览: 因为没有使用原生组件,所以自带的预览也就没有了。
拖拽功能的思路
1. 使用draggable并设置相应的参数
vue
<draggable
v-model="fileList"
item-key="uid"
class="draggable-list"
:animation="200"
ghost-class="ghost"
@end="onDragEnd"
>
关键配置:
item-key="uid"
: 指定每个拖拽项的唯一标识符:animation="200"
: 设置拖拽动画时长为200msghost-class="ghost"
: 拖拽时的幽灵元素样式类@end="onDragEnd"
: 拖拽结束时的回调函数
2. 拖拽template的整体结构
vue
<template #item="{ element: file }">
<div class="draggable-item">
<img
class="el-upload-list__item-thumbnail"
:src="file.url"
alt=""
/>
<!-- 状态显示层 -->
<div v-if="file.status === 'uploading'" class="upload-progress">
<el-progress
type="circle"
:percentage="file.percentage || 0"
:width="60"
/>
</div>
<!-- 操作按钮层 -->
<span class="el-upload-list__item-actions">
<!-- 预览和删除按钮 -->
</span>
</div>
</template>
3. 拖拽事件处理
因为是使用的成熟的第三方库,所以我们不需要关系拖拽的底层逻辑,只需要在应用层做相关的处理。
typescript
// 拖拽结束处理函数
const onDragEnd = () => {
emit("update:modelValue", fileList.value);
};
一些注意点
1. 数据的实时更新
typescript
const fileList = ref<UploadUserFile[]>(props.initFiles);
// 监听文件列表变化,同步到父组件
watch(fileList, (newVal) => {
emit("update:modelValue", newVal);
});
2. 删除已上传的文件并且更新
typescript
const handleRemove = (
uploadFile: UploadUserFile,
uploadFiles?: UploadUserFile[]
) => {
if (uploadFiles) {
fileList.value = uploadFiles;
} else {
// 从拖拽列表中删除
const index = fileList.value.findIndex(
(file) => file.uid === uploadFile.uid
);
if (index > -1) {
fileList.value.splice(index, 1);
emit('update:modelValue', fileList.value);
}
}
};
3. 添加上传进度展示
typescript
// 上传进度回调
(percent) => {
const fileIndex = fileList.value.findIndex(
(f) => f.uid === file.uid
);
if (fileIndex !== -1) {
fileList.value[fileIndex] = {
...fileList.value[fileIndex],
percentage: percent,
};
}
}
设计亮点:
- 实时更新上传进度
- 通过
uid
精确定位文件 - 使用展开运算符保持对象不可变性
CSS 拖拽状态样式
scss
// 添加幽灵效果
.ghost {
opacity: 0.5;
transform: scale(0.95);
}
总结和效果
既然原有组件不支持相关业务功能,我们不必执着于去改造他,换个思路,可以只保留他的核心上传的功能,其他像拖拽、预览、上传进度可以通过自定义的方式来实现。
毕竟没有哪个第三方组件能完美适配每一个项目,该造的轮子还是要造的。