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.pswp.cn/news/899387.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899387.shtml
英文地址,請注明出處:http://en.pswp.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;調用登…