1- import React , { memo , useEffect , useMemo , useState , createContext , useContext , useRef } from 'react' ;
1+ import React , { memo , useEffect , useMemo , useState , createContext , useContext } from 'react' ;
22import styles from './index.less' ;
33import classnames from 'classnames' ;
44import Iconfont from '@/components/Iconfont' ;
@@ -29,6 +29,8 @@ interface TreeNodeIProps {
2929interface IContext {
3030 treeData : ITreeNode [ ] ;
3131 setTreeData : ( value : ITreeNode [ ] | null ) => void ;
32+ searchTreeData : ITreeNode [ ] | null ;
33+ setSearchTreeData : ( value : ITreeNode [ ] | null ) => void ;
3234}
3335
3436export const Context = createContext < IContext > ( { } as any ) ;
@@ -48,6 +50,59 @@ const smoothTree = (treeData: ITreeNode[], result: ITreeNode[] = [], parentNode?
4850 return result ;
4951} ;
5052
53+ // 平级转树
54+ function tranListToTreeData ( list :ITreeNode [ ] , rootValue ) {
55+ const arr :ITreeNode [ ] = [ ]
56+ list . forEach ( ( item :ITreeNode ) => {
57+ if ( item . parentNode ?. uuid === rootValue ) {
58+ arr . push ( item )
59+ const children = tranListToTreeData ( list , item . uuid )
60+ if ( children . length ) {
61+ item . children = children
62+ }
63+ }
64+ } )
65+ return arr
66+ }
67+
68+ // 判断是否匹配
69+ const isMatch = ( target : string , searchValue : string ) => {
70+ const reg = new RegExp ( searchValue , 'i' ) ;
71+ return reg . test ( target || '' ) ;
72+ } ;
73+
74+ // 树结构搜索
75+ function searchTree ( treeData : ITreeNode [ ] , searchValue : string ) : ITreeNode [ ] {
76+ let result : ITreeNode [ ] = [ ] ;
77+
78+ // 深度优先遍历
79+ function dfs ( node : ITreeNode , path : ITreeNode [ ] = [ ] ) {
80+ if ( isMatch ( node . name , searchValue ) ) {
81+ result = [ ...result , ...path , node ] ;
82+ return ;
83+ }
84+ if ( ! node . children ) return ;
85+ node . children . forEach ( ( child ) => {
86+ dfs ( child , [ ...path , node ] ) ;
87+ } ) ;
88+ }
89+
90+ // 遍历树
91+ treeData . forEach ( ( node ) => dfs ( node ) ) ;
92+
93+ // 根据uuid去重
94+ const deWeightList : ITreeNode [ ] = [ ] ;
95+ result . forEach ( ( item ) => {
96+ // 如果不匹配,说明该节点为path,不需要保留该节点的子元素,就把children置空
97+ if ( ! isMatch ( item . name , searchValue ) ) {
98+ item . children = null ;
99+ }
100+ deWeightList . findIndex ( ( i ) => i . uuid === item . uuid ) === - 1 && deWeightList . push ( item ) ;
101+ } ) ;
102+
103+ return tranListToTreeData ( deWeightList , undefined ) ;
104+ }
105+
51106const itemHeight = 26 ; // 每个 item 的高度
52107const paddingCount = 2 ;
53108
@@ -56,6 +111,8 @@ const Tree = (props: IProps) => {
56111 const [ treeData , setTreeData ] = useState < ITreeNode [ ] | null > ( null ) ;
57112 const [ smoothTreeData , setSmoothTreeData ] = useState < ITreeNode [ ] > ( [ ] ) ;
58113 const [ searchTreeData , setSearchTreeData ] = useState < ITreeNode [ ] | null > ( null ) ; // 搜索结果
114+ const [ searchSmoothTreeData , setSearchSmoothTreeData ] = useState < ITreeNode [ ] | null > ( null ) ; // 搜索结果 平级
115+
59116 const [ scrollTop , setScrollTop ] = useState ( 0 ) ; // 滚动位置 // 继续需要渲染的 item 索引有哪些
60117
61118 const startIdx = useMemo ( ( ) => {
@@ -81,39 +138,53 @@ const Tree = (props: IProps) => {
81138 }
82139 } , [ treeData ] ) ;
83140
141+ // 搜索结果转平级
142+ useEffect ( ( ) => {
143+ if ( searchTreeData ) {
144+ const result : ITreeNode [ ] = [ ] ;
145+ smoothTree ( searchTreeData , result ) ;
146+ setSearchSmoothTreeData ( result ) ;
147+ } else {
148+ setSearchSmoothTreeData ( null ) ;
149+ }
150+ } , [ searchTreeData ] ) ;
151+
84152 const treeNodes = useMemo ( ( ) => {
85- const realNodeList = ( searchTreeData || smoothTreeData ) . slice ( startIdx , startIdx + 50 ) ;
153+ const realNodeList = ( searchSmoothTreeData || smoothTreeData ) . slice ( startIdx , startIdx + 50 ) ;
86154 return realNodeList . map ( ( item ) => {
87155 return < TreeNode key = { item . uuid } level = { item . level || 0 } data = { item } /> ;
88156 } ) ;
89- } , [ smoothTreeData , searchTreeData , startIdx ] ) ;
157+ } , [ smoothTreeData , searchSmoothTreeData , startIdx ] ) ;
90158
91159 useEffect ( ( ) => {
92- if ( searchValue ) {
93- const ls = smoothTreeData . filter ( ( item ) => {
94- const reg = new RegExp ( searchValue , 'i' ) ;
95- return reg . test ( item . name || '' ) ;
96- } ) ;
97- setSearchTreeData ( ls ) ;
160+ if ( searchValue && treeData ) {
161+ const _searchTreeData = searchTree ( cloneDeep ( treeData ) , searchValue ) ;
162+ setSearchTreeData ( _searchTreeData ) ;
163+ setScrollTop ( 0 ) ;
98164 } else {
99165 setSearchTreeData ( null ) ;
100166 }
101- setScrollTop ( 0 ) ;
102167 } , [ searchValue ] ) ;
103168
104169 return (
105170 < LoadingContent isLoading = { ! treeData } className = { classnames ( className ) } >
106- < Context . Provider value = { { treeData : cloneDeep ( treeData ! ) , setTreeData : setTreeData ! } } >
171+ < Context . Provider
172+ value = { {
173+ treeData : treeData ! ,
174+ setTreeData : setTreeData ! ,
175+ searchTreeData,
176+ setSearchTreeData
177+ } }
178+ >
107179 < div
108180 className = { classnames ( styles . scrollBox ) }
109181 onScroll = { ( e : any ) => {
110- console . log ( e . target . scrollTop ) ;
111182 setScrollTop ( e . target . scrollTop ) ;
112183 } }
113184 >
114185 < div
115186 className = { styles . treeListHolder }
116- style = { { '--tree-node-count' : ( searchTreeData || smoothTreeData ) ?. length } as any }
187+ style = { { '--tree-node-count' : ( searchSmoothTreeData || smoothTreeData ) ?. length } as any }
117188 >
118189 < div style = { { height : top } } />
119190 { treeNodes }
@@ -128,15 +199,18 @@ const TreeNode = memo((props: TreeNodeIProps) => {
128199 const { data : treeNodeData , level } = props ;
129200 const [ isLoading , setIsLoading ] = useState ( false ) ;
130201 const indentArr = new Array ( level ) . fill ( 'indent' ) ;
131- const { treeData, setTreeData } = useContext ( Context ) ;
202+ const { treeData, setTreeData, searchTreeData , setSearchTreeData } = useContext ( Context ) ;
132203
133204 // 加载数据
134205 function loadData ( _props ?: { refresh : boolean ; pageNo : number ; treeNodeData ?: ITreeNode } ) {
135206 const _treeNodeData = _props ?. treeNodeData || props . data ;
136207 const treeNodeConfig : ITreeConfigItem = treeConfig [ _treeNodeData . pretendNodeType || _treeNodeData . treeNodeType ] ;
137208 setIsLoading ( true ) ;
138209 if ( _props ?. pageNo === 1 || ! _props ?. pageNo ) {
139- insertData ( treeData ! , _treeNodeData . uuid ! , null ) ;
210+ insertData ( treeData ! , _treeNodeData . uuid ! , null , [ treeData , setTreeData ] ) ;
211+ if ( searchTreeData ) {
212+ insertData ( searchTreeData ! , _treeNodeData . uuid ! , null , [ searchTreeData , setSearchTreeData ] ) ;
213+ }
140214 }
141215
142216 treeNodeConfig
@@ -151,16 +225,21 @@ const TreeNode = memo((props: TreeNodeIProps) => {
151225 . then ( ( res : any ) => {
152226 if ( res . length || res . data ) {
153227 if ( res . data ) {
154- insertData ( treeData ! , _treeNodeData . uuid ! , res . data ) ;
155- // TODO:
228+ insertData ( treeData ! , _treeNodeData . uuid ! , res . data , [ treeData , setTreeData ] ) ;
229+ if ( searchTreeData ) {
230+ insertData ( searchTreeData ! , _treeNodeData . uuid ! , res . data , [ searchTreeData , setSearchTreeData ] ) ;
231+ }
156232 if ( res . hasNextPage ) {
157233 loadData ( {
158234 refresh : _props ?. refresh || false ,
159235 pageNo : res . pageNo + 1 ,
160236 } ) ;
161237 }
162238 } else {
163- insertData ( treeData ! , _treeNodeData . uuid ! , res ) ;
239+ insertData ( treeData ! , _treeNodeData . uuid ! , res , [ treeData , setTreeData ] ) ;
240+ if ( searchTreeData ) {
241+ insertData ( searchTreeData ! , _treeNodeData . uuid ! , res , [ searchTreeData , setSearchTreeData ] ) ;
242+ }
164243 }
165244 setIsLoading ( false ) ;
166245 } else {
@@ -169,7 +248,10 @@ const TreeNode = memo((props: TreeNodeIProps) => {
169248 _treeNodeData . pretendNodeType = treeNodeConfig . next ;
170249 loadData ( ) ;
171250 } else {
172- insertData ( treeData ! , _treeNodeData . uuid ! , [ ] ) ;
251+ insertData ( treeData ! , _treeNodeData . uuid ! , [ ] , [ treeData , setTreeData ] ) ;
252+ if ( searchTreeData ) {
253+ insertData ( searchTreeData ! , _treeNodeData . uuid ! , [ ] , [ searchTreeData , setSearchTreeData ] ) ;
254+ }
173255 setIsLoading ( false ) ;
174256 }
175257 }
@@ -183,23 +265,25 @@ const TreeNode = memo((props: TreeNodeIProps) => {
183265 const isFocus = useTreeStore ( ( state ) => state . focusId ) === treeNodeData . uuid ;
184266
185267 // 在treeData中找到对应的节点,插入数据
186- const insertData = ( _treeData : ITreeNode [ ] , uuid : string , data : any ) : ITreeNode | null => {
268+ const insertData = ( _treeData : ITreeNode [ ] , uuid : string , data : any , originalDataList :any ) : ITreeNode | null => {
269+ const [ originalData , setOriginalData ] = originalDataList
187270 let result : ITreeNode | null = null ;
188271 for ( let i = 0 ; i < _treeData ?. length ; i ++ ) {
189272 if ( _treeData [ i ] . uuid === uuid ) {
190273 result = _treeData [ i ] ;
191274 if ( data ) {
275+ data . map ( ( item : any ) => {
276+ item . parentNode = result ;
277+ } ) ;
192278 result . children = [ ...( result . children || [ ] ) , ...( data || [ ] ) ] ;
193279 } else {
194280 result . children = null ;
195281 }
196- result . expanded = ! ! data ;
197- // 这里没写错 就是要改变treeData的引用
198- setTreeData ?.( cloneDeep ( treeData || [ ] ) ) ;
282+ setOriginalData ?.( cloneDeep ( [ ...( originalData || [ ] ) ] ) ) ;
199283 break ;
200284 } else {
201285 if ( _treeData [ i ] . children ) {
202- result = insertData ( _treeData [ i ] . children ! , uuid , data ) ;
286+ result = insertData ( _treeData [ i ] . children ! , uuid , data , originalDataList ) ;
203287 if ( result ) {
204288 break ;
205289 }
@@ -211,8 +295,11 @@ const TreeNode = memo((props: TreeNodeIProps) => {
211295
212296 //展开-收起
213297 const handleClick = ( ) => {
214- if ( treeNodeData . expanded ) {
215- insertData ( treeData ! , treeNodeData . uuid ! , null ) ;
298+ if ( treeNodeData ?. children ) {
299+ insertData ( treeData ! , treeNodeData . uuid ! , null , [ treeData , setTreeData ] ) ;
300+ if ( searchTreeData ) {
301+ insertData ( searchTreeData ! , treeNodeData . uuid ! , null , [ searchTreeData , setSearchTreeData ] ) ;
302+ }
216303 } else {
217304 loadData ( ) ;
218305 }
@@ -267,7 +354,6 @@ const TreeNode = memo((props: TreeNodeIProps) => {
267354 label : < MenuLabel icon = { item . labelProps . icon } label = { item . labelProps . label } /> ,
268355 } ;
269356 } ) ;
270-
271357 return (
272358 < Dropdown
273359 trigger = { [ 'contextMenu' ] }
@@ -326,7 +412,7 @@ const TreeNode = memo((props: TreeNodeIProps) => {
326412 </ Tooltip >
327413 </ Dropdown >
328414 ) ;
329- } , [ isFocus , isLoading , rightClickMenu ] ) ;
415+ } , [ isFocus , isLoading , rightClickMenu , treeNodeData . children ] ) ;
330416
331417 return treeNodeDom ;
332418} ) ;
0 commit comments