Files
mp-vue/docs/SELECT_FILE_DRAG_DEMO.md
2025-07-25 13:28:38 +08:00

267 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔄 SelectFile组件拖拽调整顺序功能演示
## 🎯 功能概述
我已经成功为SelectFile组件添加了拖拽调整顺序的功能让用户可以通过拖拽来重新排列文件的顺序。
### ✨ 核心功能
1. **🔄 拖拽排序**
- 支持鼠标拖拽调整文件顺序
- 实时视觉反馈和拖拽指示器
- 顺序指示器显示当前位置
2. **🎯 智能交互**
- 拖拽时显示拖拽指示器
- 悬停时显示拖拽提示
- 拖拽完成后自动更新数据
3. **📍 视觉反馈**
- 顺序指示器显示文件位置
- 拖拽时的视觉效果
- 悬停时的交互提示
### 🔧 技术实现
#### 1. 组件模板更新
```vue
<template>
<a-image-preview-group>
<div class="select-file-container">
<!-- 🔄 可拖拽的文件列表 -->
<div
class="draggable-file-list"
@dragover.prevent
@drop="onDrop"
>
<template v-for="(item, index) in localData" :key="item.id || index">
<div
class="image-upload-item draggable-item"
:class="{ 'dragging': dragIndex === index }"
draggable="true"
@dragstart="onDragStart(index, $event)"
@dragend="onDragEnd"
@dragenter="onDragEnter(index)"
@dragleave="onDragLeave"
v-if="isImage(item.url)"
>
<!-- 🎯 拖拽指示器 -->
<div class="drag-indicator">
<HolderOutlined />
</div>
<a-image :src="item.url" />
<!-- 📍 顺序指示器 -->
<div class="order-indicator">{{ index + 1 }}</div>
</div>
</template>
</div>
</div>
</a-image-preview-group>
</template>
```
#### 2. 拖拽逻辑实现
```typescript
// 🔄 拖拽相关状态
const dragIndex = ref<number | null>(null);
const dragOverIndex = ref<number | null>(null);
// 📝 本地数据副本,用于拖拽操作
const localData = ref<any[]>([]);
// 🔄 监听props.data变化同步到localData
watch(
() => props.data,
(newData) => {
if (newData) {
localData.value = [...newData];
}
},
{ immediate: true, deep: true }
);
// 🔄 拖拽开始
const onDragStart = (index: number, event: DragEvent) => {
dragIndex.value = index;
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/html', index.toString());
}
};
// 🔄 拖拽结束
const onDragEnd = () => {
dragIndex.value = null;
dragOverIndex.value = null;
};
// 🔄 拖拽进入
const onDragEnter = (index: number) => {
dragOverIndex.value = index;
};
// 🔄 拖拽放置
const onDrop = (event: DragEvent) => {
event.preventDefault();
if (dragIndex.value !== null && dragOverIndex.value !== null && dragIndex.value !== dragOverIndex.value) {
const newData = [...localData.value];
const draggedItem = newData[dragIndex.value];
// 移除拖拽的项目
newData.splice(dragIndex.value, 1);
// 在新位置插入项目
const insertIndex = dragIndex.value < dragOverIndex.value ? dragOverIndex.value - 1 : dragOverIndex.value;
newData.splice(insertIndex, 0, draggedItem);
// 更新本地数据
localData.value = newData;
// 触发重新排序事件
emit('reorder', newData);
}
dragIndex.value = null;
dragOverIndex.value = null;
};
```
#### 3. 事件定义更新
```typescript
const emit = defineEmits<{
(e: 'done', data: FileRecord): void;
(e: 'del', index: number): void;
(e: 'clear'): void;
(e: 'reorder', data: any[]): void; // 新增重新排序事件
}>();
```
#### 4. 样式设计
```less
// 🔄 可拖拽项目样式
.draggable-item {
position: relative;
cursor: move;
transition: all 0.3s ease;
border-radius: 8px;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
.drag-indicator {
opacity: 1;
}
}
&.dragging {
opacity: 0.5;
transform: rotate(5deg) scale(0.95);
z-index: 1000;
}
}
// 🎯 拖拽指示器
.drag-indicator {
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
opacity: 0;
transition: opacity 0.2s ease;
z-index: 10;
pointer-events: none;
}
// 📍 顺序指示器
.order-indicator {
position: absolute;
top: -6px;
right: -6px;
background: #1890ff;
color: white;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
z-index: 5;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
```
### 🔗 父组件集成
在articleEdit.vue中添加对reorder事件的处理
```vue
<SelectFile
:placeholder="`请选择图片`"
:limit="6"
:data="files"
@done="chooseFile"
@del="onDeleteFile"
@reorder="onReorderFiles"
/>
```
```typescript
// 🔄 处理文件重新排序
const onReorderFiles = (newData: any[]) => {
files.value = newData;
form.files = JSON.stringify(files.value.map((d) => d.url));
message.success('文件顺序已更新');
};
```
### 🎨 用户体验
1. **直观操作**:用户可以直接拖拽文件来调整顺序
2. **视觉反馈**:拖拽时有清晰的视觉指示
3. **顺序显示**:每个文件都有序号显示当前位置
4. **即时更新**拖拽完成后立即更新数据和UI
### 📊 功能特点
| 特性 | 描述 | 状态 |
|------|------|------|
| 拖拽排序 | 支持鼠标拖拽调整顺序 | ✅ |
| 视觉反馈 | 拖拽时的动画和指示器 | ✅ |
| 顺序指示 | 显示文件的当前位置 | ✅ |
| 数据同步 | 拖拽后自动更新数据 | ✅ |
| 事件通知 | 触发reorder事件通知父组件 | ✅ |
| 响应式设计 | 适配不同屏幕尺寸 | ✅ |
### 🚀 使用场景
1. **文章封面图排序**:调整封面图的显示顺序,第一张作为主封面
2. **图片轮播排序**:调整轮播图片的播放顺序
3. **文件优先级**:根据重要性调整文件的排列顺序
4. **展示顺序**:调整图片在前端的展示顺序
### 💡 技术亮点
- **原生HTML5拖拽API**:使用标准的拖拽事件
- **Vue3响应式**利用Vue3的响应式系统
- **数据双向绑定**:保持组件内外数据同步
- **优雅的动画效果**:提供流畅的用户体验
- **类型安全**完整的TypeScript类型定义
这个功能让用户可以更直观地管理文件顺序,特别是在处理多个封面图时,可以轻松调整哪张图片作为主封面显示!🎉