参考文档 组件源码
import React from 'react';
import {
DndContext, // DndKit的核心组件,用于处理拖拽和排序的上下文
closestCenter, // 用于检测最接近的位置
KeyboardSensor, // 用于处理键盘拖拽
useSensor, // 自定义Hook,用于使用传感器
useSensors, // 自定义Hook,用于使用多个传感器
MouseSensor, // 用于处理鼠标拖拽
} from '@dnd-kit/core';
import {
arrayMove, // 用于移动数组中的元素位置
SortableContext, // 用于创建可排序的上下文
sortableKeyboardCoordinates, // 用于处理可排序元素的键盘坐标
useSortable, // 自定义Hook,用于实现可排序元素的行为
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
// SortListDndKit组件
const SortListDndKit = ({
list = [],
idKey = 'id',
children,
onDragEnd,
}) => {
// 使用多个传感器
const sensors = useSensors(
useSensor(MouseSensor), // 使用鼠标传感器
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates, // 使用键盘传感器,并指定键盘坐标的获取方式
}),
);
// 拖拽结束的处理函数
const handleDragEnd = (event) => {
const { active, over } = event; // 获取拖拽事件对象中的active和over属性
if (active.id !== over.id) { // 如果拖拽的元素和目标元素不是同一个
const oldIndex = list.findIndex((item) => item[idKey] === active.id); // 找到拖拽元素在列表中的索引
const newIndex = list.findIndex((item) => item[idKey] === over.id); // 找到目标元素在列表中的索引
const ids = list.map((item) => item[idKey]); // 获取列表中所有元素的id
[ids[newIndex], ids[oldIndex]] = [ids[oldIndex], ids[newIndex]]; // 交换目标元素和拖拽元素的id位置
const _val = arrayMove(list, oldIndex, newIndex); // 根据索引交换列表中元素的位置
onDragEnd(_val, ids); // 调用传入的onDragEnd回调函数,并传递更新后的列表和id数组
}
};
return (
<DndContext
sensors={sensors} // 传递传感器给DndContext
collisionDetection={closestCenter} // 设置碰撞检测器为closestCenter
onDragEnd={handleDragEnd} // 设置拖拽结束的处理函数
>
<SortableContext items={list.map((item) => item[idKey])}>
{children}
</SortableContext>
</DndContext>
);
};
// SortItemDndKit组件
const SortItemDndKit = ({ id, children, handle = false, handleNode, ...props }) => {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id }); // 使用useSortable自定义Hook,获取可排序元素的属性和行为
const style = {
transform: CSS.Transform.toString(transform), // 将transform属性转换为字符串
transition,
...(isDragging ? { boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)', zIndex: 1000, position: 'relative' } : {}),
};
const newChild = React.Children.map(children, (child) => {
if (!React.isValidElement(child)) {
return null;
}
const childProps = {
...child.props,
ref: setNodeRef, // 将元素的引用设置为setNodeRef
...(handle ? {} : attributes),
...(handle ? {} : listeners),
};
return React.cloneElement(child, childProps); // 克隆子元素,并传递属性和事件监听器
});
if (handle) {
return <div {...props} style={{ ...style }}>
<div className='handleNode' {...listeners} style={{ cursor: 'pointer' }} >{handleNode || '+'}</div>
<div>{newChild}</div>
</div >
}
if (!handle) {
return <div style={{ ...style, cursor: 'pointer' }}>
{newChild}
</div>
}
};
export { SortListDndKit, SortItemDndKit };
// 使用方法:
// 1. 导入SortListDndKit和SortItemDndKit组件。
// 2. 在需要拖拽和排序的列表的父组件中使用SortListDndKit组件作为容器组件。
// 3. 在SortListDndKit组件内部,将列表数据传递给list属性,列表项的唯一标识符传递给idKey属性,以及在拖拽结束时触发的回调函数传递给onDragEnd属性。
// 4. 使用SortItemDndKit组件作为列表项的包装组件,并将每个列表项的唯一标识符传递给id属性。
// 5. 在SortItemDndKit组件内部,将要排序的内容作为子元素传递。
// 6. 在onDragEnd回调函数中,可以获取排序后的列表数据和id数组,并进行进一步的处理。
使用方法
import React, { useState } from 'react'
import InnerTopBar from "@/components/Layouts/InnerTopBar";
import { Button } from "antd";
import { SortListDndKit, SortItemDndKit } from './Sortable';
export default function RroupedSorting() {
const [list, setList] = useState([
{ id: "1", text: "Item 1" },
{ id: "2", text: "Item 2" },
{ id: "3", text: "Item 3" }
]);
const handleDragEnd = (sortedList, sortedIds) => {
console.log("Sorted List:", sortedList);
console.log("Sorted Ids:", sortedIds);
// 更新列表数据为排序后的顺序
setList(sortedList);
};
return (
<div>
<InnerTopBar
leftTitle="返回"
returnPath="/flow/flowForm"
rightTool={[<Button>保存</Button>]}
/>
<SortListDndKit list={list} onDragEnd={handleDragEnd}>
{list.map((item) => (
<SortItemDndKit
key={item.id}
id={item.id}
handle={true}
handleNode={'🤚'}
className='SortItemDndKit'
>
<div>{item.text}</div>
</SortItemDndKit>
))}
</SortListDndKit>
</div>
)
}