tree.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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. export const defaultProps = {
  12. children: 'children',
  13. label: 'name',
  14. value: 'id',
  15. }
  16. const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
  17. // tree from list
  18. export function listToTree<T = any>(list: any[], config: Partial<TreeHelperConfig> = {}): T[] {
  19. const conf = getConfig(config) as TreeHelperConfig
  20. const nodeMap = new Map()
  21. const result: T[] = []
  22. const { id, children, pid } = conf
  23. for (const node of list) {
  24. node[children] = node[children] || []
  25. nodeMap.set(node[id], node)
  26. }
  27. for (const node of list) {
  28. const parent = nodeMap.get(node[pid])
  29. ;(parent ? parent.children : result).push(node)
  30. }
  31. return result
  32. }
  33. export function treeToList<T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T {
  34. config = getConfig(config)
  35. const { children } = config
  36. const result: any = [...tree]
  37. for (let i = 0; i < result.length; i++) {
  38. if (!result[i][children!])
  39. continue
  40. result.splice(i + 1, 0, ...result[i][children!])
  41. }
  42. return result
  43. }
  44. export function findNode<T = any>(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}): T | null {
  45. config = getConfig(config)
  46. const { children } = config
  47. const list = [...tree]
  48. for (const node of list) {
  49. if (func(node))
  50. return node
  51. node[children!] && list.push(...node[children!])
  52. }
  53. return null
  54. }
  55. export function findNodeAll<T = any>(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}): T[] {
  56. config = getConfig(config)
  57. const { children } = config
  58. const list = [...tree]
  59. const result: T[] = []
  60. for (const node of list) {
  61. func(node) && result.push(node)
  62. node[children!] && list.push(...node[children!])
  63. }
  64. return result
  65. }
  66. export function findPath<T = any>(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}): T | T[] | null {
  67. config = getConfig(config)
  68. const path: T[] = []
  69. const list = [...tree]
  70. const visitedSet = new Set()
  71. const { children } = config
  72. while (list.length) {
  73. const node = list[0]
  74. if (visitedSet.has(node)) {
  75. path.pop()
  76. list.shift()
  77. }
  78. else {
  79. visitedSet.add(node)
  80. node[children!] && list.unshift(...node[children!])
  81. path.push(node)
  82. if (func(node))
  83. return path
  84. }
  85. }
  86. return null
  87. }
  88. export function findPathAll(tree: any, func: Fn, config: Partial<TreeHelperConfig> = {}) {
  89. config = getConfig(config)
  90. const path: any[] = []
  91. const list = [...tree]
  92. const result: any[] = []
  93. const visitedSet = new Set()
  94. const { children } = config
  95. while (list.length) {
  96. const node = list[0]
  97. if (visitedSet.has(node)) {
  98. path.pop()
  99. list.shift()
  100. }
  101. else {
  102. visitedSet.add(node)
  103. node[children!] && list.unshift(...node[children!])
  104. path.push(node)
  105. func(node) && result.push([...path])
  106. }
  107. }
  108. return result
  109. }
  110. export function filter<T = any>(tree: T[], func: (n: T) => boolean, config: Partial<TreeHelperConfig> = {}): T[] {
  111. config = getConfig(config)
  112. const children = config.children as string
  113. function listFilter(list: T[]) {
  114. return list
  115. .map((node: any) => ({ ...node }))
  116. .filter((node) => {
  117. node[children] = node[children] && listFilter(node[children])
  118. return func(node) || (node[children] && node[children].length)
  119. })
  120. }
  121. return listFilter(tree)
  122. }
  123. export function forEach<T = any>(tree: T[], func: (n: T) => any, config: Partial<TreeHelperConfig> = {}): void {
  124. config = getConfig(config)
  125. const list: any[] = [...tree]
  126. const { children } = config
  127. for (let i = 0; i < list.length; i++) {
  128. // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿
  129. if (func(list[i]))
  130. return
  131. children && list[i][children] && list.splice(i + 1, 0, ...list[i][children])
  132. }
  133. }
  134. /**
  135. * @description: Extract tree specified structure
  136. */
  137. export function treeMap<T = any>(treeData: T[], opt: { children?: string; conversion: Fn }): T[] {
  138. return treeData.map(item => treeMapEach(item, opt))
  139. }
  140. /**
  141. * @description: Extract tree specified structure
  142. */
  143. export function treeMapEach(data: any, { children = 'children', conversion }: { children?: string; conversion: Fn }) {
  144. const haveChildren = Array.isArray(data[children]) && data[children].length > 0
  145. const conversionData = conversion(data) || {}
  146. if (haveChildren) {
  147. return {
  148. ...conversionData,
  149. [children]: data[children].map((i: number) =>
  150. treeMapEach(i, {
  151. children,
  152. conversion,
  153. }),
  154. ),
  155. }
  156. }
  157. else {
  158. return {
  159. ...conversionData,
  160. }
  161. }
  162. }
  163. /**
  164. * 递归遍历树结构
  165. * @param treeDatas 树
  166. * @param callBack 回调
  167. * @param parentNode 父节点
  168. */
  169. export function eachTree(treeDatas: any[], callBack: Fn, parentNode = {}) {
  170. treeDatas.forEach((element) => {
  171. const newNode = callBack(element, parentNode) || element
  172. if (element.children)
  173. eachTree(element.children, callBack, newNode)
  174. })
  175. }
  176. /**
  177. * 构造树型结构数据
  178. * @param {*} data 数据源
  179. * @param {*} id id字段 默认 'id'
  180. * @param {*} parentId 父节点字段 默认 'parentId'
  181. * @param {*} children 孩子节点字段 默认 'children'
  182. */
  183. export function handleTree(data: any[], id?: string, parentId?: string, children?: string) {
  184. if (!Array.isArray(data)) {
  185. console.warn('data must be an array')
  186. return []
  187. }
  188. const config = {
  189. id: id || 'id',
  190. parentId: parentId || 'parentId',
  191. childrenList: children || 'children',
  192. }
  193. const childrenListMap = {}
  194. const nodeIds = {}
  195. const tree: any[] = []
  196. for (const d of data) {
  197. const parentId = d[config.parentId]
  198. if (childrenListMap[parentId] == null)
  199. childrenListMap[parentId] = []
  200. nodeIds[d[config.id]] = d
  201. childrenListMap[parentId].push(d)
  202. }
  203. for (const d of data) {
  204. const parentId = d[config.parentId]
  205. if (nodeIds[parentId] == null)
  206. tree.push(d)
  207. }
  208. for (const t of tree)
  209. adaptToChildrenList(t)
  210. function adaptToChildrenList(o) {
  211. if (childrenListMap[o[config.id]] !== null)
  212. o[config.childrenList] = childrenListMap[o[config.id]]
  213. if (o[config.childrenList]) {
  214. for (const c of o[config.childrenList])
  215. adaptToChildrenList(c)
  216. }
  217. }
  218. return tree
  219. }
  220. /**
  221. * 构造树型结构数据
  222. * @param {*} data 数据源
  223. * @param {*} id id字段 默认 'id'
  224. * @param {*} parentId 父节点字段 默认 'parentId'
  225. * @param {*} children 孩子节点字段 默认 'children'
  226. * @param {*} rootId 根Id 默认 0
  227. */
  228. export function handleTree2(data, id, parentId, children, rootId) {
  229. id = id || 'id'
  230. parentId = parentId || 'parentId'
  231. children = children || 'children'
  232. rootId
  233. = rootId
  234. || Math.min(
  235. ...data.map((item) => {
  236. return item[parentId]
  237. }),
  238. )
  239. || 0
  240. // 对源数据深度克隆
  241. const cloneData = JSON.parse(JSON.stringify(data))
  242. // 循环所有项
  243. const treeData = cloneData.filter((father) => {
  244. const branchArr = cloneData.filter((child) => {
  245. // 返回每一项的子级数组
  246. return father[id] === child[parentId]
  247. })
  248. // eslint-disable-next-line no-unused-expressions
  249. branchArr.length > 0 ? (father.children = branchArr) : ''
  250. // 返回第一层
  251. return father[parentId] === rootId
  252. })
  253. return treeData !== '' ? treeData : data
  254. }