当前位置: 首页 > backend >正文

React实现列表拖拽排序

本文主要介绍一下React实现列表拖拽排序方法,具体样式如下图
在这里插入图片描述

首先,简单展示一下组件的数据结构

const CodeSetting = props => {const {$t,                    // 国际化翻译函数vm,                    // 视图模型数据vm: {CodeSet: { Enable = [],    // 启用的编码列表Disable = []    // 停用的编码列表}},getConfig,             // 获取配置的函数save,                  // 保存配置的函数vmChange               // 更新视图模型的函数} = props;
};

完整的数据结构示例

const vm = {CodeSet: {Enable: [{ Compression: "H.264" },{ Compression: "H.265" },{ Compression: "MPEG-4" }],Disable: [{ Compression: "AVC" },{ Compression: "HEVC" }]}
};

主要用到的代码如下,简单看后我将介绍拖拽方法

{Enable.length ? (<Cardtitle={`${$t('com.EnableCode')} (${Enable.length})`}extra={<Buttonsize='small'className='clear-all-btn'type='link'onClick={clearAllEnabled}>{$t('com.ClearAll')}</Button>}>{Enable.map((item, index) => (<divkey={index}className='drag-item'draggableonDragStart={e => {handleDragStart(e, index);}}onDragEnd={handleDragEnd}onDragOver={handleDragOver}onDrop={e => handleDrop(e, index)}><div className='drag-handle'>⋮⋮</div><LabelText text={item.Compression} /><div className='delete-btn-container'><Iconcomponent={remove}onClick={() => codeSetChange('remove', index)}style={{fontSize: '20px'}}/></div></div>))}</Card>

首先用到的组件是Card组件,title是card标题,extra是card后缀

之后遍历Enable数组,将拿到的每一个值渲染到card中

这个组件实现了 HTML5 原生拖拽 API 来实现编码列表的拖拽排序功能。主要使用了以下拖拽事件:

onDragStart - 拖拽开始
onDragOver - 拖拽悬停
onDrop - 拖拽放置
onDragEnd - 拖拽结束

状态管理

const [draggedIndex, setDraggedIndex] = useState(null); // 记录当前拖拽项的索引

拖拽事件处理函数

1 拖拽开始 (handleDragStart)

const handleDragStart = (e, index) => {setDraggedIndex(index);                    // 记录拖拽项的索引e.dataTransfer.effectAllowed = 'move';    // 设置拖拽效果为移动e.currentTarget.classList.add('dragging'); // 添加拖拽样式
};

2 拖拽悬停 (handleDragOver)

const handleDragOver = e => {e.preventDefault();                         // 阻止默认行为e.dataTransfer.dropEffect = 'move';        // 设置放置效果为移动// 清除所有拖拽项的悬停样式const dragItems = document.querySelectorAll('.drag-item');dragItems.forEach(item => {item.classList.remove('drag-over');});// 为当前悬停元素添加悬停样式e.currentTarget.classList.add('drag-over');
};

3 拖拽放置 (handleDrop)

const handleDrop = (e, dropIndex) => {e.preventDefault();e.currentTarget.classList.remove('drag-over');if (draggedIndex === null || draggedIndex === dropIndex) {return;}// 重新排序 Enable 数组const enableList = [...Enable];const draggedItem = enableList[draggedIndex];// 移除拖拽项enableList.splice(draggedIndex, 1);// 在目标位置插入enableList.splice(dropIndex, 0, draggedItem);// 更新vm数据const newCodeSet = {...vm.CodeSet,Enable: enableList};vmChange({ CodeSet: newCodeSet });setDraggedIndex(null);
};

4 拖拽结束 (handleDragEnd)

const handleDragEnd = e => {setDraggedIndex(null);e.currentTarget.classList.remove('dragging');// 清除所有拖拽项的悬停样式const dragItems = document.querySelectorAll('.drag-item');dragItems.forEach(item => {item.classList.remove('drag-over');});
};

5.JSX 结构

<divkey={index}className='drag-item'draggable                                    // 设置为可拖拽onDragStart={e => handleDragStart(e, index)} // 拖拽开始onDragEnd={handleDragEnd}                    // 拖拽结束onDragOver={handleDragOver}                  // 拖拽悬停onDrop={e => handleDrop(e, index)}>          // 拖拽放置<div className='drag-handle'>⋮⋮</div>        // 拖拽手柄<LabelText text={item.Compression} /><div className='delete-btn-container'>{/* 删除按钮 */}</div>
</div>

6. 核心算法

  1. 拖拽排序的核心算法是数组重排序:
  2. 获取拖拽项:从原位置取出拖拽的元素
  3. 移除拖拽项:在原位置删除该元素
  4. 插入新位置:在目标位置插入该元素
  5. 更新状态:将新的数组顺序更新到组件状态

7. 样式处理
组件通过 CSS 类名来管理拖拽状态:
.dragging - 拖拽中的样式
.drag-over - 拖拽悬停的样式
.drag-item - 可拖拽项的基础样式

8.样式代码

    // 拖拽项容器.drag-item {display: flex;align-items: center;cursor: grab;&:hover {background-color: #f5f5f5;}// 拖拽中状态&.dragging {background-color: #e6f7ff;opacity: 0.5;transform: scale(0.95);}// 拖拽悬停状态&.drag-over {background-color: #e6f7ff;border: 1px solid #91d5ff;border-radius: 4px;}}// 拖拽手柄.drag-handle {margin-right: 8px;color: #2f2e2e;font-size: 12px;user-select: none;}
http://www.xdnf.cn/news/19814.html

相关文章:

  • LiteFlow:国产流程编排引擎体验
  • DAY20-新世纪DL(DeepLearning/深度学习)战士:终(目标检测/YOLO)3
  • 【医疗行业案例】基于 React 的预约系统:DHTMLX 助力高效排班与预约管理
  • CAD/BIM软件产品技术深度分析文章写作计划
  • 全渠道 + 低代码:如何打造 “内外协同” 的客服管理系统体系?
  • 【FastDDS】Layer DDS之Domain ( 02-DomainParticipant )
  • unity中的交互控制脚本
  • 云手机将要面临的挑战有哪些?
  • 【学习记录】github私人仓库创建和本地克隆
  • CSS 伪类与伪元素:深度解析
  • 从零构建Linux Shell解释器深入理解Bash进程创建机制
  • 【Spring Cloud微服务】11.微服务通信演义:从飞鸽传书到5G全息,一部消息中间件的进化史诗
  • Java项目打包成EXE全攻略​
  • Ubuntu22.04下编译googletest源代码生成.so动态库
  • 利用 openssl api 实现 TLS 双向认证
  • MySQL-MVCC多版本并发控制详解
  • LangChain实战(十二):自定义Tools扩展Agent能力
  • Python+DRVT 从外部调用 Revit:批量创建门
  • Streamable HTTP
  • sv中forever如何结束
  • AI 在金融、医疗、教育、制造业等领域有着广泛的应用,以下是这些领域的一些落地案例
  • STM32HAL 快速入门(十七):UART 硬件结构 —— 从寄存器到数据收发流程
  • 告别剪辑烦恼!3个超实用技巧,让你的视频瞬间高级起来
  • 【音视频】视频秒播优化实践
  • UnityWebRequest 数据获取和提交
  • wpf 只能输入int类型的文本框
  • WebSocket客户端库:websocket-fruge365
  • Ubuntu下把 SD 卡格式化为 FAT32
  • Hostol Magento电商服务器套餐:基于阿里云,预配置高性能环境,一键开店
  • 如何用java给局域网的电脑发送开机数据包