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

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 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. }