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  }