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.pswp.cn/diannao/97917.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97917.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将 MySQL 表数据导出为 CSV 文件

目录 一、实现思路 二、核心代码 1. 数据库连接部分 2. 数据导出核心逻辑 3. CSV文件写入 三、完整代码实现 五、输出结果 一、实现思路 建立数据库连接 查询目标表的数据总量和具体数据 获取表的列名作为CSV文件的表头 将查询结果转换为二维数组格式 使用Hutool工具…

一文读懂RAG:从生活场景到核心逻辑,AI“查资料答题”原来这么简单

一文读懂RAG&#xff1a;从生活场景到核心逻辑&#xff0c;AI“查资料答题”原来这么简单 要理解 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;&#xff0c;不需要先背复杂公式&#xff0c;我们可以从一个生活场景切入——它本质是让AI…

git将当前分支推送到远端指定分支

在 Git 中&#xff0c;将当前本地分支推送到远程仓库的指定分支&#xff0c;可以使用 git push 命令&#xff0c;并指定本地分支和远程分支的映射关系。 基本语法 git push <远程名称> <本地分支名>:<远程分支名><远程名称>&#xff1a;通常是 origin&…

【Linux】线程封装

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、为什么需要封装线程库&#xff1f; pthread的痛点&#xff1a; 封装带来的好处&#xff1a; 二、线程封装核心代码解析 1. 头文件定义&#xff08;Thread.hpp&a…

智慧交通管理信号灯通信4G工业路由器应用

在交通信号灯管理中传统的有线通讯&#xff08;光纤、网线&#xff09;存在部署成本高、偏远区域覆盖难、故障维修慢等问题&#xff0c;而4G工业路由器凭借无线化、高稳定、强适配的特性&#xff0c;成为信号灯与管控平台间的数据传输核心&#xff0c;适配多场景需求。智慧交通…

《Python Flask 实战:构建一个可交互的 Web 应用,从用户输入到智能响应》

《Python Flask 实战:构建一个可交互的 Web 应用,从用户输入到智能响应》 一、引言:从“Hello, World!”到“你好,用户” 在 Web 应用的世界里,最打动人心的功能往往不是炫酷的界面,而是人与系统之间的真实互动。一个简单的输入框,一句个性化的回应,往往能让用户感受…

开发效率翻倍:资深DBA都在用的MySQL客户端利器

MySQL 连接工具&#xff08;也称为客户端或图形化界面工具&#xff0c;GUI Tools&#xff09;是数据库开发、管理和运维中不可或缺的利器。它们比命令行更直观&#xff0c;能极大提高工作效率。以下是一份主流的 MySQL 连接工具清单&#xff0c;并附上了它们的优缺点和适用场景…

基于Docker和Kubernetes的CI/CD流水线架构设计与优化实践

基于Docker和Kubernetes的CI/CD流水线架构设计与优化实践 本文分享了在生产环境中基于Docker和Kubernetes构建高效可靠的CI/CD流水线的实战经验&#xff0c;包括业务场景、技术选型、详细方案、踩坑与解决方案&#xff0c;以及最终的总结与最佳实践&#xff0c;帮助后端开发者快…

Trae x 图片素描MCP一键将普通图片转换为多风格素描效果

目录前言一、核心工具与优势解析二、操作步骤&#xff1a;从安装到生成素描效果第一步&#xff1a;获取MCP配置代码第二步&#xff1a;下载第三步&#xff1a;在 Trae 中导入 MCP 配置并建立连接第四步&#xff1a;核心功能调用三、三大素描风格差异化应用四.总结前言 在设计创…

2 XSS

XSS的原理 XSS&#xff08;跨站脚本攻击&#xff09;原理 1. 核心机制 XSS攻击的本质是恶意脚本在用户浏览器中执行。攻击者通过向网页注入恶意代码&#xff0c;当其他用户访问该页面时&#xff0c;浏览器会执行这些代码&#xff08;没有对用户的输入进行过滤导致用户输入的…

GitHub每日最火火火项目(9.3)

1. pedroslopez / whatsapp-web.js 项目名称&#xff1a;whatsapp-web.js项目介绍&#xff1a;基于 JavaScript 开发&#xff0c;是一个用于 Node.js 的 WhatsApp 客户端库&#xff0c;通过 WhatsApp Web 浏览器应用进行连接&#xff08;A WhatsApp client library for NodeJS …

Ansible变量

Ansible变量定义变量规则&#xff1a;由字母/数字/下划线组成&#xff0c;变量需要以字母开头&#xff0c;ansible内置的关键字不能作为变量。ansible中&#xff0c;可以将变量简化为三个范围&#xff1a;Global范围&#xff08;高&#xff09;&#xff1a;从命令行和ansible配…

Elasticsearch 核心特性与应用指南

最近在准备面试&#xff0c;正把平时积累的笔记、项目中遇到的问题与解决方案、对核心原理的理解&#xff0c;以及高频业务场景的应对策略系统梳理一遍&#xff0c;既能加深记忆&#xff0c;也能让知识体系更扎实&#xff0c;供大家参考&#xff0c;欢迎讨论。一、核心优势 Ela…

力扣115:不同的子序列

力扣115:不同的子序列题目思路代码题目 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数。 测试用例保证结果在 32 位有符号整数范围内。 思路 首先我们来考虑特殊情况&#xff0c;当s串的长度小于t串时s串肯定就没有t串了。其他情况我们就需…

2004-2023年各省生活垃圾无害化处理率数据(无缺失)

2004-2023年各省生活垃圾无害化处理率数据&#xff08;无缺失&#xff09; 1、时间&#xff1a;2004-2023年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;生活垃圾无害化处理率 4、范围&#xff1a;30省 5、指标解释&#xff1a;生活垃圾无害化处理率指报…

【Python练习题】Python小白必练100题答案-第21-40题

练习题直达链接Python小白必练100题答案-第1-20题点我直达Python小白必练100题答案-第21-40题点我直达Python小白必练100题答案-第41-60题点我直达Python小白必练100题答案-第61-80题点我直达Python小白必练100题答案-第81-97题点我直达目录专栏导读循环结构 字符串操作第三部…

添加⽂件--场景⼆

添加⽂件–场景⼆ 学习到这⾥&#xff0c;我们已经清楚了如何向仓库中添加⽂件&#xff0c;并且对于⼯作区、暂存区、版本库也有了⼀定的认识。那么我们再展⽰⼀种添加⽂件的场景&#xff0c;能加深对⼯作区、暂存区、版本库的理解&#xff0c;⽰例如下&#xff1a; roothcss-e…

华为网路设备学习-31(BGP协议 六)

BGP路由属性的几种常见使用方法&#xff1a; 29章是 BGP路由汇总 与 as-path-filter&#xff08;正则表达式&#xff09; 30章是 Community 的使用方法 本章是 ip前缀列表ip-prefix 、 路由过滤 filter-policy 和路由策略 route-policy 一、在BGP中的 ip前缀列表&#xf…

Windows PostgreSQL JDBC驱动安装包位置

要在Windows系统上获取PostgreSQL JDBC驱动安装包&#xff08;后缀为.jar的文件&#xff09;&#xff0c;可通过以下官方及常用渠道获取&#xff0c;具体位置如下&#xff1a; ###&#x1f527; 1. 官方网站下载&#xff08;推荐&#xff09; 下载地址&#xff1a;https://jdb…

机器学习从入门到精通 - 聚类算法大比拼:K-Means、DBSCAN实战与评估陷阱

机器学习从入门到精通 - 聚类算法大比拼&#xff1a;K-Means、DBSCAN实战与评估陷阱 开场白&#xff1a;推开无监督学习的大门 朋友们&#xff0c;不知道你们有没有对着堆积如山、没有标签的数据发过愁&#xff1f;想从里面找出点规律&#xff0c;分组什么的&#xff0c;结果发…