简单记录工作中树形数据常用的函数
转换为树结构
/**
* 构造树型结构数据
* @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]
}