github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/static_source/admin/src/utils/tree.ts (about) 1 interface TreeHelperConfig { 2 id: string 3 children: string 4 pid: string 5 } 6 const DEFAULT_CONFIG: TreeHelperConfig = { 7 id: 'id', 8 children: 'children', 9 pid: 'pid' 10 } 11 12 const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config) 13 14 // tree from list 15 export const listToTree = <T = any>(list: any[], config: Partial<TreeHelperConfig> = {}): T[] => { 16 const conf = getConfig(config) as TreeHelperConfig 17 const nodeMap = new Map() 18 const result: T[] = [] 19 const { id, children, pid } = conf 20 21 for (const node of list) { 22 node[children] = node[children] || [] 23 nodeMap.set(node[id], node) 24 } 25 for (const node of list) { 26 const parent = nodeMap.get(node[pid]) 27 ;(parent ? parent.children : result).push(node) 28 } 29 return result 30 } 31 32 export const treeToList = <T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T => { 33 config = getConfig(config) 34 const { children } = config 35 const result: any = [...tree] 36 for (let i = 0; i < result.length; i++) { 37 if (!result[i][children!]) continue 38 result.splice(i + 1, 0, ...result[i][children!]) 39 } 40 return result 41 } 42 43 export const findNode = <T = any>( 44 tree: any, 45 func: Fn, 46 config: Partial<TreeHelperConfig> = {} 47 ): T | null => { 48 config = getConfig(config) 49 const { children } = config 50 const list = [...tree] 51 for (const node of list) { 52 if (func(node)) return node 53 node[children!] && list.push(...node[children!]) 54 } 55 return null 56 } 57 58 export const findNodeAll = <T = any>( 59 tree: any, 60 func: Fn, 61 config: Partial<TreeHelperConfig> = {} 62 ): T[] => { 63 config = getConfig(config) 64 const { children } = config 65 const list = [...tree] 66 const result: T[] = [] 67 for (const node of list) { 68 func(node) && result.push(node) 69 node[children!] && list.push(...node[children!]) 70 } 71 return result 72 } 73 74 export const findPath = <T = any>( 75 tree: any, 76 func: Fn, 77 config: Partial<TreeHelperConfig> = {} 78 ): T | T[] | null => { 79 config = getConfig(config) 80 const path: T[] = [] 81 const list = [...tree] 82 const visitedSet = new Set() 83 const { children } = config 84 while (list.length) { 85 const node = list[0] 86 if (visitedSet.has(node)) { 87 path.pop() 88 list.shift() 89 } else { 90 visitedSet.add(node) 91 node[children!] && list.unshift(...node[children!]) 92 path.push(node) 93 if (func(node)) { 94 return path 95 } 96 } 97 } 98 return null 99 } 100 101 export const findPathAll = (tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}) => { 102 config = getConfig(config) 103 const path: any[] = [] 104 const list = [...tree] 105 const result: any[] = [] 106 const visitedSet = new Set(), 107 { children } = config 108 while (list.length) { 109 const node = list[0] 110 if (visitedSet.has(node)) { 111 path.pop() 112 list.shift() 113 } else { 114 visitedSet.add(node) 115 node[children!] && list.unshift(...node[children!]) 116 path.push(node) 117 func(node) && result.push([...path]) 118 } 119 } 120 return result 121 } 122 123 export const filter = <T = any>( 124 tree: T[], 125 func: (n: T) => boolean, 126 config: Partial<TreeHelperConfig> = {} 127 ): T[] => { 128 config = getConfig(config) 129 const children = config.children as string 130 function listFilter(list: T[]) { 131 return list 132 .map((node: any) => ({ ...node })) 133 .filter((node) => { 134 node[children] = node[children] && listFilter(node[children]) 135 return func(node) || (node[children] && node[children].length) 136 }) 137 } 138 return listFilter(tree) 139 } 140 141 export const forEach = <T = any>( 142 tree: T[], 143 func: (n: T) => any, 144 config: Partial<TreeHelperConfig> = {} 145 ): void => { 146 config = getConfig(config) 147 const list: any[] = [...tree] 148 const { children } = config 149 for (let i = 0; i < list.length; i++) { 150 // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 151 if (func(list[i])) { 152 return 153 } 154 children && list[i][children] && list.splice(i + 1, 0, ...list[i][children]) 155 } 156 } 157 158 /** 159 * @description: Extract tree specified structure 160 */ 161 export const treeMap = <T = any>( 162 treeData: T[], 163 opt: { children?: string; conversion: Fn } 164 ): T[] => { 165 return treeData.map((item) => treeMapEach(item, opt)) 166 } 167 168 /** 169 * @description: Extract tree specified structure 170 */ 171 export const treeMapEach = ( 172 data: any, 173 { children = 'children', conversion }: { children?: string; conversion: Fn } 174 ) => { 175 const haveChildren = Array.isArray(data[children]) && data[children].length > 0 176 const conversionData = conversion(data) || {} 177 if (haveChildren) { 178 return { 179 ...conversionData, 180 [children]: data[children].map((i: number) => 181 treeMapEach(i, { 182 children, 183 conversion 184 }) 185 ) 186 } 187 } else { 188 return { 189 ...conversionData 190 } 191 } 192 } 193 194 /** 195 * 递归遍历树结构 196 * @param treeDatas 树 197 * @param callBack 回调 198 * @param parentNode 父节点 199 */ 200 export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { 201 treeDatas.forEach((element) => { 202 const newNode = callBack(element, parentNode) || element 203 if (element.children) { 204 eachTree(element.children, callBack, newNode) 205 } 206 }) 207 }