90 lines
2.2 KiB
TypeScript
90 lines
2.2 KiB
TypeScript
/**
|
||
* 部门树工具函数
|
||
*
|
||
* 部门树结构处理工具函数
|
||
* 包括:构建部门树、克隆节点、排序等功能
|
||
*
|
||
* @created 2025-01-XX(从 page.tsx 提取)
|
||
* @utils
|
||
*/
|
||
|
||
import { getDepartmentCollator } from '@/lib/departments'
|
||
import type { Department, DepartmentNode } from '../_components/types'
|
||
|
||
const cloneNode = (node: DepartmentNode, children: DepartmentNode[] = []) => ({
|
||
...node,
|
||
children,
|
||
})
|
||
|
||
export const buildDepartmentTree = (
|
||
departments: Department[] = [],
|
||
collator: Intl.Collator = getDepartmentCollator(),
|
||
): DepartmentNode[] => {
|
||
if (!departments.length) return []
|
||
|
||
const nodeMap = new Map<string, DepartmentNode>()
|
||
|
||
departments.forEach((dept) => {
|
||
nodeMap.set(dept.name, {
|
||
id: dept.id,
|
||
name: dept.name,
|
||
fullName: dept.name,
|
||
children: [],
|
||
})
|
||
})
|
||
|
||
const roots: DepartmentNode[] = []
|
||
|
||
departments.forEach((dept) => {
|
||
const node = nodeMap.get(dept.name)
|
||
if (!node) {
|
||
return
|
||
}
|
||
const lastDashIndex = dept.name.lastIndexOf('-')
|
||
if (lastDashIndex === -1) {
|
||
roots.push(node)
|
||
} else {
|
||
const parentName = dept.name.substring(0, lastDashIndex)
|
||
const parentNode = nodeMap.get(parentName)
|
||
if (parentNode) {
|
||
parentNode.children.push(node)
|
||
} else {
|
||
roots.push(node)
|
||
}
|
||
}
|
||
})
|
||
|
||
const sortNodes = (nodes: DepartmentNode[]) => {
|
||
nodes.sort((a, b) => collator.compare(a.fullName, b.fullName))
|
||
nodes.forEach((child) => sortNodes(child.children))
|
||
}
|
||
|
||
sortNodes(roots)
|
||
return roots
|
||
}
|
||
|
||
export const filterDepartmentTree = (
|
||
tree: DepartmentNode[],
|
||
term: string,
|
||
): DepartmentNode[] => {
|
||
const normalized = term.trim().toLowerCase()
|
||
if (!normalized) return tree
|
||
|
||
const filterNodes = (nodes: DepartmentNode[]): DepartmentNode[] =>
|
||
nodes
|
||
.map((node) => {
|
||
const matchesSelf =
|
||
node.name.toLowerCase().includes(normalized) ||
|
||
node.fullName.toLowerCase().includes(normalized)
|
||
const filteredChildren = filterNodes(node.children)
|
||
return matchesSelf || filteredChildren.length
|
||
? cloneNode(node, filteredChildren)
|
||
: null
|
||
})
|
||
.filter((node): node is DepartmentNode => Boolean(node))
|
||
|
||
return filterNodes(tree)
|
||
}
|
||
|
||
|