You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
4.1 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
  1. import { FiledNames } from '@/global'
  2. type TreeKey = string | number;
  3. type TreeNode<T> = {
  4. [key in keyof T]: T[keyof T];
  5. } & {
  6. key: TreeKey;
  7. id?: TreeKey;
  8. children?: TreeNode<T>[];
  9. };
  10. export function getTreeCheckedStatus<T>(tree: TreeNode<T>[], selectKeys: TreeKey[]): {
  11. checked: TreeKey[],
  12. halfChecked: TreeKey[]
  13. } {
  14. const checked: TreeKey[] = []
  15. const halfChecked: TreeKey[] = []
  16. if (!tree || tree.length === 0) return { checked, halfChecked }
  17. if (!selectKeys || selectKeys.length === 0) return { checked, halfChecked }
  18. // 辅助函数来递归地检查每个节点
  19. function checkNode(node: TreeNode<T>, ancestors: TreeKey[]): void {
  20. const key = node.key ?? node.id
  21. const isLeaf = !node.children || node.children.length === 0
  22. const isSelected = selectKeys.includes(key)
  23. // 如果是叶节点并且被选中,则直接加入到checked数组
  24. if (isLeaf && isSelected) {
  25. checked.push(key)
  26. // 标记所有祖先为半选状态,除非它们已经被完全选中
  27. ancestors.forEach(ancestorKey => {
  28. if (!halfChecked.includes(ancestorKey) && !checked.includes(ancestorKey)) {
  29. halfChecked.push(ancestorKey)
  30. }
  31. })
  32. return
  33. }
  34. // 非叶节点,递归检查其子节点
  35. if (node.children) {
  36. const childAncestors = [ ...ancestors, key ]
  37. node.children.forEach(child => checkNode(child, childAncestors))
  38. // 检查当前节点的所有子节点是否全部或部分被选中
  39. const childSelectedCount = node.children.filter(child => checked.includes(child.key ?? child.id)).length
  40. if (childSelectedCount === node.children.length) {
  41. // 如果所有子节点都被选中,将当前节点标为全选
  42. checked.push(key)
  43. } else if (childSelectedCount > 0) {
  44. // 如果部分子节点被选中,将当前节点标为半选
  45. halfChecked.push(key)
  46. }
  47. }
  48. }
  49. // 遍历每一个根节点
  50. tree.forEach(node => checkNode(node, []))
  51. return { checked, halfChecked }
  52. }
  53. export function findValuePath<T>(tree: TreeNode<T>[], targetValue: string | number, filedNames?: FiledNames): (string | number)[] | null {
  54. const f = {
  55. key: filedNames?.key ?? 'key',
  56. title: filedNames?.title ?? 'title',
  57. children: filedNames?.children ?? 'children',
  58. }
  59. const findPathRecursive = (node: TreeNode<T>, pathSoFar: (string | number)[]): (string | number)[] | null => {
  60. if (node[f.key] === targetValue) {
  61. return [ ...pathSoFar, node[f.key] ]
  62. }
  63. if (node[f.children]) {
  64. for (const child of node[f.children]) {
  65. const result = findPathRecursive(child, [ ...pathSoFar, node[f.key] ])
  66. if (result !== null) {
  67. return result
  68. }
  69. }
  70. }
  71. return null
  72. }
  73. for (const root of tree) {
  74. const result = findPathRecursive(root, [])
  75. if (result !== null) {
  76. return result
  77. }
  78. }
  79. return null // 如果未找到目标值,则返回null
  80. }
  81. //将tree中指定key的string json转为json
  82. export function treeStringToJson<T>(tree: TreeNode<T>[], key: string): TreeNode<T>[] {
  83. return tree.map(node => {
  84. const children = node.children ? treeStringToJson(node.children, key) : undefined
  85. return {
  86. ...node,
  87. [key]: node[key] ? JSON.parse(node[key] as string) : undefined,
  88. children,
  89. }
  90. })
  91. }
  92. //遍历tree, 对每个节点执行fn
  93. export function traverseTree<T>(tree: TreeNode<T>[], fn: (node: TreeNode<T>) => void, filedNames?: FiledNames): void {
  94. const { children = 'children' } = filedNames ?? { children: 'children' }
  95. const did = (node: TreeNode<T>) => {
  96. fn(node)
  97. if (node[children]) {
  98. node[children].forEach(did)
  99. }
  100. }
  101. tree.forEach(did)
  102. }
  103. //遍历tree, 对每个节点执行fn, 返回新的tree
  104. export function mapTree<T, R>(tree: TreeNode<T>[], fn: (node: TreeNode<T>) => R, filedNames?: FiledNames): TreeNode<R>[] {
  105. const { children = 'children' } = filedNames ?? { children: 'children' }
  106. const did = (node: TreeNode<T>): TreeNode<R> => {
  107. const newNode = fn(node)
  108. if (node[children]) {
  109. newNode[children] = node[children].map(did)
  110. }
  111. return newNode as TreeNode<R>
  112. }
  113. return tree.map(did)
  114. }