flnxi-Wu/app/admin/_utils/departmentTree.ts
WuYongJie e4dcb6260a
Some checks failed
CI Check / check (push) Has been cancelled
feat: Admin页面重构 & UI三层架构系统
- 创建统一UI三层架构系统(基础层、颜色层、组合层)
- 拆分Admin页面为独立Tab组件(MonitorTab、UsersTab、DepartmentsTab等)
- 提取公共逻辑到Hooks(useToast、useDataFetch、useUserOperations等)
- 提取工具函数(validators、formatters、api等)
- 创建统一弹窗组件(Modal)和useModal Hook
- 添加错误边界和统一状态组件
- 完善类型定义和可访问性支持
- 更新文档(优化记录、优化计划、更新日志)
2025-11-22 15:17:10 +08:00

80 lines
2.0 KiB
TypeScript

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)
}