封装一个vue3的表格拖拽功能:
1.在utils下创建dragDropManager.ts
interface DragConfig {
draggableColumns?: string[]; // 可拖拽列的名称
undraggableRows?: number[]; // 禁止拖拽的行索引
}
export class TableDragManager<T> {
private list: T[]; // 数据源
private config: DragConfig; // 拖拽配置
private draggingIndex: number | null = null; //当前拖动元素的索引
private onDropCallback?: (draggedId: number, newList: any[]) => void; //回调
constructor(list: T[], config: DragConfig) {
this.list = list;
this.config = config;
}
// 设置拖拽完成之后的回调方法,用于调用API,保存拖拽之后的数据
setOnDropCallback(callback: (draggedId: number, newList: any[]) => void) {
this.onDropCallback = callback;
}
onDragStart(index: number, column: string | null, event: DragEvent): void {
if (this.config.undraggableRows?.includes(index)) {
event.preventDefault();
return;
}
if (column && this.config.draggableColumns && !this.config.draggableColumns.includes(column)) {
event.preventDefault();
return;
}
this.draggingIndex = index;
}
// targetIndex: 将要拖动到的位置的索引 event: 拖拽事件 canDrop:用于判断能否拖拽到指定位置(因为有些行不允许拖拽,也就意味着别的行也不能拖拽到对应位置)
onDrop(targetIndex: number, event: DragEvent, canDrop: (index: number) => boolean): void {
event.preventDefault();
console.log("targetIndex is:", targetIndex);
console.log("draggingIndex is:", this.draggingIndex);
if (this.draggingIndex !== null && this.draggingIndex !== targetIndex && canDrop(targetIndex)) {
const draggedItem = this.list[this.draggingIndex];
this.list.splice(this.draggingIndex, 1);
this.list.splice(targetIndex, 0, draggedItem);
if (this.onDropCallback) {
this.onDropCallback(draggedItem.id, this.list);
}
}
this.draggingIndex = null;
}
onDragOver(event: DragEvent): void {
event.preventDefault(); // 必须阻止默认行为以允许放置
}
}
2.在vue3页面test.vue调用
1>禁止部分行拖拽以及指定字段可拖拽
<script setup lang="ts">
import { TableDragManager } from '~/utils/dragDropManager';
interface TableRow {
id: number;
name: string;
age: number;
}
const list = ref<TableRow[]>([
{ id: 1, name: 'Row 1', age: 25 },
{ id: 2, name: 'Row 2', age: 30 },
{ id: 3, name: 'Row 3', age: 35 },
{ id: 4, name: 'Row 4', age: 55 },
]);
// 配置
const config = {
draggableColumns: ['id'], // 仅允许 ID 列拖拽
undraggableRows: [0,1], // 第 0 1 行禁止拖拽 (默认按顺序禁止)
};
// 实例化拖拽管理器
const dragManager = new TableDragManager(list.value, config);
const canDrop = (index: number) => {
// 禁止前两行行拖拽
if (config.undraggableRows && index < config.undraggableRows.length) {
return false;
}
return true;
};
const handleDrop = (draggedId: number, newList: any[]) => {
console.log("draggedId is:", draggedId);
console.log("newList is:", newList);
}
onMounted(() => {
dragManager.setOnDropCallback(handleDrop);
});
</script>
<template>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, index) in list"
:key="row.id"
>
<td
:draggable="true"
@dragstart="dragManager.onDragStart(index, 'id', $event)"
@dragover="dragManager.onDragOver($event)"
@drop="dragManager.onDrop(index, $event, canDrop)"
>
{{ row.id }}
</td>
<td>{{ row.name }}</td>
<td>{{ row.age }}</td>
</tr>
</tbody>
</table>
</template>
2>常规拖拽(没有任何限制)
<script setup lang="ts">
import { TableDragManager } from '~/utils/dragDropManager';
interface TableRow {
id: number;
name: string;
age: number;
}
const list = ref<TableRow[]>([
{ id: 1, name: 'Row 1', age: 25 },
{ id: 2, name: 'Row 2', age: 30 },
{ id: 3, name: 'Row 3', age: 35 },
{ id: 4, name: 'Row 4', age: 55 },
]);
// 配置
const config = {
draggableColumns: [], // 仅允许 ID 列拖拽
undraggableRows: [], // 第 0 1 行禁止拖拽 (默认按顺序禁止)
};
// 实例化拖拽管理器
const dragManager = new TableDragManager(list.value, config);
const canDrop = (index: number) => {
// 禁止前两行行拖拽
if (config.undraggableRows && index < config.undraggableRows.length) {
return false;
}
return true;
};
const handleDrop = (draggedId: number, newList: any[]) => {
console.log("draggedId is:", draggedId);
console.log("newList is:", newList);
}
onMounted(() => {
dragManager.setOnDropCallback(handleDrop);
});
</script>
<template>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, index) in list"
:key="row.id"
:draggable="true"
@dragstart="dragManager.onDragStart(index, null, $event)"
@dragover="dragManager.onDragOver($event)"
@drop="dragManager.onDrop(index, $event, canDrop)"
>
<td
>
{{ row.id }}
</td>
<td>{{ row.name }}</td>
<td>{{ row.age }}</td>
</tr>
</tbody>
</table>
</template>
3>关于splice的使用分析
//
let lists = ['A', 'B', 'C', 'D'];
// 假设要把 'B' 移动到 'D' 前面
// 1. 保存 'B'
const draggedItem = lists[1]; // 'B'
// 2. 删除索引1的 'B'
lists.splice(1, 1); // list 现在是 ['A', 'C', 'D']
// 3. 在索引2处插入 'B'
lists.splice(2, 0, draggedItem); // list 变成 ['A', 'C', 'B', 'D']
