react ant design树穿梭框实现搜索并展开到子节点、同级节点选择及同时选择数量限制功能

 功能点:

  • 点击节点前的箭头,可以手动展开或折叠该节点的子节点。
  • 在搜索框中输入关键词,匹配的节点及其父节点会自动展开。
  • 清空搜索框后,恢复到用户手动控制的展开状态。
  • 勾选节点时仍然遵守 "最多勾选 6 个节点" 和 "只能选择同级节点" 的限制。
import { Transfer, Tree, Input, message } from 'antd';
import React, { useState } from 'react';// 判断是否已选中
const isChecked = (selectedKeys, eventKey) => selectedKeys.includes(eventKey);// 生成树结构,并标记禁用状态
const generateTree = (treeNodes = [], checkedKeys = []) =>treeNodes.map(({ children, ...props }) => ({...props,disabled: checkedKeys.includes(props.key),children: generateTree(children, checkedKeys),}));// 搜索功能:过滤树节点并展开到匹配节点
const filterTreeData = (treeData, searchValue) => {const loop = (data) =>data.map((item) => {const match = item.title.toLowerCase().includes(searchValue.toLowerCase());const children = item.children ? loop(item.children) : [];return {...item,children,isMatch: match || children.some((child) => child.isMatch),};}).filter((item) => item.isMatch);return loop(treeData);
};// 自定义 TreeTransfer 组件
const TreeTransfer = ({ dataSource, targetKeys, ...restProps }) => {const transferDataSource = [];const [searchValue, setSearchValue] = useState('');const [expandedKeys, setExpandedKeys] = useState([]); // 控制展开的节点// 展平树结构为一维数组function flatten(list = []) {list.forEach((item) => {transferDataSource.push(item);flatten(item.children);});}flatten(dataSource);// 动态计算需要展开的节点(用于搜索)const calculateExpandedKeys = (treeData) => {const keys = [];const loop = (data) =>data.forEach((item) => {if (item.isMatch) {keys.push(item.key);if (item.children) loop(item.children);}});loop(treeData);return keys;};// 过滤后的树数据const filteredTreeData = filterTreeData(dataSource, searchValue);const autoExpandedKeys = calculateExpandedKeys(filteredTreeData);// 更新展开状态const handleExpand = (newExpandedKeys) => {setExpandedKeys(newExpandedKeys);};return (<Transfer{...restProps}targetKeys={targetKeys}dataSource={transferDataSource}className="tree-transfer"render={(item) => item.title}showSelectAll={false}>{({ direction, onItemSelect, selectedKeys }) => {if (direction === 'left') {const checkedKeys = [...selectedKeys, ...targetKeys];return (<>{/* 搜索框 */}<Inputplaceholder="搜索节点"value={searchValue}onChange={(e) => {setSearchValue(e.target.value);setExpandedKeys(autoExpandedKeys); // 搜索时自动展开匹配节点}}style={{ marginBottom: 16 }}/><TreeblockNodecheckablecheckStrictlydefaultExpandAllcheckedKeys={checkedKeys}treeData={filteredTreeData}expandedKeys={searchValue ? autoExpandedKeys : expandedKeys} // 根据搜索状态决定展开哪些节点onExpand={handleExpand} // 手动控制展开/折叠onCheck={(_, { node }) => {const { key, parentKey } = node;// 检查是否在同一层级const isSameLevel = checkedKeys.every((k) => {const checkedNode = transferDataSource.find((item) => item.key === k);return !checkedNode || checkedNode.parentKey === parentKey;});// 检查是否超过最大勾选数量限制const isWithinLimit = checkedKeys.length < 6 || checkedKeys.includes(key);if (isSameLevel && isWithinLimit) {onItemSelect(key, !isChecked(checkedKeys, key));} else {message.warn(!isSameLevel? '只能选择同级节点': '最多只能同时勾选 6 个节点');}}}onSelect={(_, { node }) => {const { key, parentKey } = node;// 检查是否在同一层级const isSameLevel = checkedKeys.every((k) => {const checkedNode = transferDataSource.find((item) => item.key === k);return !checkedNode || checkedNode.parentKey === parentKey;});// 检查是否超过最大勾选数量限制const isWithinLimit = checkedKeys.length < 6 || checkedKeys.includes(key);if (isSameLevel && isWithinLimit) {onItemSelect(key, !isChecked(checkedKeys, key));} else {message.warn(!isSameLevel? '只能选择同级节点': '最多只能同时勾选 6 个节点');}}}/></>);}}}</Transfer>);
};// 测试数据
const treeData = [{key: '0-0',title: 'Node 0-0',parentKey: null, // 根节点没有父节点},{key: '0-1',title: 'Node 0-1',parentKey: null,children: [{key: '0-1-0',title: 'Node 0-1-0',parentKey: '0-1', // 父节点是 '0-1'},{key: '0-1-1',title: 'Node 0-1-1',parentKey: '0-1', // 父节点是 '0-1'},],},{key: '0-2',title: 'Node 0-3',parentKey: null,},
];// 主应用组件
const App = () => {const [targetKeys, setTargetKeys] = useState([]);const onChange = (keys) => {setTargetKeys(keys);};return <TreeTransfer dataSource={treeData} targetKeys={targetKeys} onChange={onChange} />;
};export default App;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/899387.shtml

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

相关文章

阿里云云效 Maven

阿里云云效 Maven 官网&#xff1a;https://developer.aliyun.com/mvn/guide 阿里云Maven中央仓库为 阿里云云效 提供的公共代理仓库&#xff0c;帮助研发人员提高研发生产效率&#xff0c;使用阿里云Maven中央仓库作为下载源&#xff0c;速度更快更稳定。 阿里云云效 是企业…

Go 语言标准库中Channels,Goroutines详细功能介绍与示例

在 Go 语言中&#xff0c;Goroutines&#xff08;协程&#xff09;和 Channels&#xff08;通道&#xff09;是并发编程的核心组件。它们共同协作&#xff0c;简化了并发任务的管理和数据同步。以下通过详细示例说明它们的用法和常见模式。 1. Goroutines&#xff08;协程&…

如何在 Postman 中正确设置 Session 以维持用户状态?

在 Postman 里面设置有 session 的请求。如果你还不知道什么是 session&#xff0c;那么请看这里—— session 是一种记录客户端和服务器之间状态的机制&#xff0c;用于保持用户的登录状态或者其他数据&#xff0c;从而让用户在不同页面之间保持一致的体验。 Postman 设置带 …

DQN与PPO在算法层面的核心区别

DQN与PPO在算法层面的核心区别 1. 学习目标不同 DQN(基于价值): 核心:学习动作价值函数 Q ( s , a ) Q(s, a)

Linux: 网络,arp的数量为什么会对交换机/路由器有性能的影响

这个问题也是非常普遍的问题。比如最近比较火的一个OVS相关的问题: ARP request packets put high pressure on the pinctrl thread in ovn-controller 另一个在工作种也遇到了相似的问题,当一个网络里发了同时发了小一百个GARP之后,路由器的gateway就会有ARP处理延迟。 A…

解析 HTML 网站架构规范

2025/3/28 向全栈工程师迈进&#xff01; 一、网页基本的组成部分 网页的外观多种多样&#xff0c;但是除了全屏视频或游戏&#xff0c;或艺术作品页面&#xff0c;或只是结构不当的页面以外&#xff0c;都倾向于使用类似的标准组件。 1.1页眉 通常横跨于整个页面顶部有一…

Golang 当中 byte 和 rune 类型的区别

文章目录 Golang 当中 byte 和 rune 类型的区别类型定义与用途字符串处理差异内存占用典型引用场景 Golang 当中 byte 和 rune 类型的区别 在 Golang 中&#xff0c;rune 和 byte 类型的区别主要体现在字符处理的方式和编码支持上。 类型定义与用途 byte 类型 本质是 uint8…

vue将页面导出成word

方法一&#xff1a;使用 html-docx-js html-docx-js 是一个轻量级的库&#xff0c;可以将 HTML 转换为 Word 文档。 安装依赖 首先安装 html-docx-js&#xff1a; Bash深色版本 npm install html-docx-js --save创建导出逻辑 在 Vue 组件中实现导出功能的代码如下&#xff1…

Three.js 快速入门教程【二十】3D模型加载优化实战:使用gltf-pipeline与Draco对模型进行压缩,提高加载速度和流畅性

系列文章目录 Three.js 快速入门教程【一】开启你的 3D Web 开发之旅 Three.js 快速入门教程【二】透视投影相机 Three.js 快速入门教程【三】渲染器 Three.js 快速入门教程【四】三维坐标系 Three.js 快速入门教程【五】动画渲染循环 Three.js 快速入门教程【六】相机控件 Or…

前端框架入门:Angular

Angular 是由 Google 维护的前端框架,适用于构建单页应用(SPA)。它使用TypeScript 作为主要开发语言,并提供了强大的模块化、依赖注入(DI)、路由管理等特性。 一、Angular 基础 1. Angular 介绍 Angular 是一个组件化、模块化、双向数据绑定的前端框架,适用于构建复杂…

基于51单片机的速度检测报警器proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1I7roZEjrk349Is_YdMcsxQ 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C51 是一款常用的 8 位单片机&#xff0c;由 Atmel 公司&#xff08;现已被 Microchip 收…

具身系列——Diffusion Policy算法实现CartPole游戏

代码原理分析 1. 核心思想 该代码实现了一个基于扩散模型&#xff08;Diffusion Model&#xff09;的强化学习策略网络。扩散模型通过逐步去噪过程生成动作&#xff0c;核心思想是&#xff1a; • 前向过程&#xff1a;通过T步逐渐将专家动作添加高斯噪声&#xff0c;最终变成…

DeepSeek 本地化部署教程

1 概述 1.1 配置参考图 科普&#xff1a; B&#xff0c;Billion&#xff08;十亿&#xff09;&#xff0c;是 “参数量” 的单位。 模型量超过 一亿&#xff0c;可称之为 “大模型”。 2 软件安装 2.1 下载 Ollama 官方主页&#xff1a;https://ollama.com/download主页截图…

matlab打开两个工程

1、问题描述 写代码时&#xff0c;需要实时参考别人的代码&#xff0c;需要同时打开2个模型&#xff0c;当模型在同一个工程内时&#xff0c;这是可以直接打开的&#xff0c;如图所示 2、解决方案 再打开一个MATLAB主窗口 这个时候就可以同时打开多个模型了 3、正确的打开方…

mac 下配置flutter 总是失败,请参考文章重新配置flutter 环境MacOS Flutter环境配置和安装

一、安装和运行Flutter的系统环境要求 想要安装并运行 Flutter&#xff0c;你的开发环境需要最低满足以下要求&#xff1a; 操作系统:macOS磁盘空间:2.8 GB(不包括IDE/tools的磁盘空间)。工具:Flutter使用git进行安装和升级。我们建议安装Xcode&#xff0c;其中包括git&#x…

第4.1节:使用正则表达式

1 第4.1节&#xff1a;使用正则表达式 将正则表达式用斜杠括起来&#xff0c;就能用作模式。随后&#xff0c;该正则表达式会与每条输入记录的完整文本进行比对。&#xff08;通常情况下&#xff0c;它只需匹配文本的部分内容就能视作匹配成功。&#xff09;例如&#xff0c;以…

Java 代理(一) 静态代理

学习代理的设计模式的时候&#xff0c;经常碰到的一个经典场景就是想统计某个方法的执行时间。 1 静态代理模式的产生 需求1. 统计方法执行时间 统计方法执行时间&#xff0c;在很多API/性能监控中都有这个需求。 下面以简单的计算器为例子&#xff0c;计算加法耗时。代码如下…

每日总结3.28

蓝桥刷题 3227 找到最多的数 方法一&#xff1a;摩尔投票法 #include <bits/stdc.h> using namespace std; #define int long long signed main() { int n,m; cin>>n>>m; int a[m*n]; for(int i0;i<n*m;i) { cin>>a[i]; } int cand…

Flutter快速搭建聊天

之前项目中使用的环信聊天&#xff0c;我们的App使用的Flutter开发的 。 所以&#xff0c;就使用的 em_chat_uikit &#xff0c;这个是环信开发的Flutter版本的聊天。 一开始&#xff0c;我们也用的环信的聊天&#xff0c;是收费的&#xff0c;但是&#xff0c;后面就发现&…

Sa-Token

简介 Sa-Token 是一个轻量级 Java 权限认证框架&#xff0c;主要解决&#xff1a;登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。 官方文档 常见功能 登录认证 本框架 用户提交 name password 参数&#xff0c;调用登…