前端常用树形结构的操作

11 篇文章 0 订阅
7 篇文章 0 订阅
文章提供了一组用于处理树形数据的JavaScript函数,包括将数据转换为树结构,替换键以适应树形下拉框,获取选中值的label路径,以及在回显和处理选择时的策略。此外,还介绍了获取指定节点所有父级节点ID的方法。
摘要由CSDN通过智能技术生成

简单记录工作中树形数据常用的函数

转换为树结构

/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 */
export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => {
  if (!Array.isArray(data)) {
    console.warn('data must be an array')
    return []
  }
  const config = {
    id: id || 'id',
    parentId: parentId || 'parentId',
    childrenList: children || 'children'
  }

  const childrenListMap = {}
  const nodeIds = {}
  const tree: any[] = []

  for (const d of data) {
    const parentId = d[config.parentId]
    if (childrenListMap[parentId] == null) {
      childrenListMap[parentId] = []
    }
    nodeIds[d[config.id]] = d
    childrenListMap[parentId].push(d)
  }

  for (const d of data) {
    const parentId = d[config.parentId]
    if (nodeIds[parentId] == null) {
      tree.push(d)
    }
  }

  for (const t of tree) {
    adaptToChildrenList(t)
  }

  function adaptToChildrenList(o) {
    if (childrenListMap[o[config.id]] !== null) {
      o[config.childrenList] = childrenListMap[o[config.id]]
    }
    if (o[config.childrenList]) {
      for (const c of o[config.childrenList]) {
        adaptToChildrenList(c)
      }
    }
  }
  return tree
}

替换树形数据的key常用于处理树形下拉框选择器的数据

type tTree = {
  id: string | number
  name: string
  children?: Array<tTree>
}
type tTree2 = {
  value: string | number
  label: string
  children?: Array<tTree2>
}

/**
 * 处理树形数据的字段key
 * */
export function transformTreeData(treeData: tTree[]): tTree2[] {
  if (!Array.isArray(treeData)) {
    return []
  }

  const transformedData = treeData.map((item) => {
    const transformedItem: tTree2 = {
      value: item.id,
      label: item.name
    }

    if (Array.isArray(item.children) && item.children.length > 0) {
      // @ts-ignore
      transformedItem['children'] = transformTreeData(item.children)
    }

    return transformedItem
  })

  return transformedData
}

树形下拉框选中某个值后获取label路径


function getParent(data, childNode) {
  for (let i = 0; i < data.length; i++) {
    const node = data[i]
    if (node.children && node.children.find((child) => child.value === childNode.value)) {
      return node
    }
  }
  return null
}
/**
 * 根据输入的子节点value值,获取该子节点的路径
 * */
export function findLabelPath(data: tTree2[], targetValue: string | number) {
  for (let i = 0; i < data.length; i++) {
    const node = data[i]
    if (node.value === targetValue) {
      let path = node.label
      let parent = node
      while (parent && parent.label) {
        parent = getParent(data, parent)
        if (parent) {
          path = `${parent.label}/${path}`
        }
      }
      return path
    } else if (node.children) {
      const path = findLabelPath(node.children, targetValue)
      if (path) {
        return `${node.label}/${path}`
      }
    }
  }
  return null
}

const tree = [{ label: '一级',value: '1', children: [{ label: '二级', value: '1-1' }] }]
console.log(findLabelPath(tree, '1-1'))

树形结构回显时数据处理

树形选择通常需要在选中的列表里面选上对应的父节点,这会造成回显的时候该父节点所有子节点被选中的问题

      /** 删除选中列表中的父节点id , 只保留最深一层的子节点id
       * 输入 tree: {id, children}[], arr: number[]
       * 输出 arr: number[]
       * 列:输入 [{id: 1, children: [{id: 11}, {id: 12}]}, {id: 2}] [1,11,2] 输出[11,2]
       * */
      function getSelectedLeaves(tree, arr) {
        const leaves = []
        function traverse(node, parentSelected) {
          if (arr.includes(node.id)) {
            // node is selected
            if (node.children) {
              // node has children
              node.children.forEach((child) => traverse(child, true))
            } else {
              // node is a leaf
              leaves.push(node.id)
            }
          } else if (parentSelected) {
            // node is not selected but has a selected parent
            return // do not add any descendants to the leaves array
          } else {
            // node is not selected and has no selected parent
            if (node.children) {
              // node has children
              node.children.forEach((child) => traverse(child, false))
            }
          }
        }
        tree.forEach((node) => traverse(node, false))
        return leaves
      }

获取指定树节点的所有父级节点数组

function getParentIds(data, id) {
  const parentIds = []

  function findParentIds(items, targetId, currentParentIds) {
    for (let i = 0; i < items.length; i++) {
      const item = items[i]
      if (item.id === targetId) {
        parentIds.push(...currentParentIds, item.parentId)
        break
      }
      if (item.children && item.children.length > 0) {
        currentParentIds.push(item.parentId)
        findParentIds(item.children, targetId, currentParentIds)
        currentParentIds.pop()
      }
    }
  }

  findParentIds(data, id, [])

  return [...parentIds, id]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值