type TreeNode = { id: number; parentId: number; sort?: number | null; children?: TreeNode[] | null; }; export function buildMenuTree(list: T[]) { const nodeMap = new Map(); const roots: T[] = []; list.forEach(item => { nodeMap.set(item.id, { ...item, children: [] }); }); nodeMap.forEach(node => { if (node.parentId === 0) { roots.push(node); return; } const parent = nodeMap.get(node.parentId); if (!parent) { roots.push(node); return; } parent.children = [...(parent.children ?? []), node]; }); return sortMenuTree(roots); } export function collectDescendantIds>(nodes: T[], targetId: number) { const target = findTreeNode(nodes, targetId); if (!target?.children?.length) { return []; } const ids: number[] = []; walkTree(target.children, item => { ids.push(item.id); }); return ids; } function sortMenuTree(nodes: T[]) { const sortedNodes = [...nodes].sort((prev, next) => Number(prev.sort ?? 0) - Number(next.sort ?? 0)); sortedNodes.forEach(node => { if (node.children?.length) { node.children = sortMenuTree(node.children as T[]); } }); return sortedNodes; } function findTreeNode>(nodes: T[], targetId: number): T | null { for (const node of nodes) { if (node.id === targetId) { return node; } if (node.children?.length) { const target = findTreeNode(node.children as unknown as T[], targetId); if (target) { return target; } } } return null; } function walkTree>(nodes: T[], callback: (node: T) => void) { for (const node of nodes) { callback(node); if (node.children?.length) { walkTree(node.children as unknown as T[], callback); } } }