89 lines
2.4 KiB
TypeScript
89 lines
2.4 KiB
TypeScript
const departmentCollator = new Intl.Collator('zh-Hans-CN-u-co-pinyin', {
|
|
numeric: true,
|
|
sensitivity: 'base',
|
|
})
|
|
|
|
export const sortDepartmentNames = (names: string[]): string[] => {
|
|
return [...names].sort((a, b) => departmentCollator.compare(a, b))
|
|
}
|
|
|
|
const normalizeName = (name?: string | null) => (name || '').trim()
|
|
|
|
export const buildDepartmentDescendants = (rawDepartments: Array<{ name: string }> | string[]) => {
|
|
const names =
|
|
Array.isArray(rawDepartments) && typeof rawDepartments[0] === 'string'
|
|
? (rawDepartments as string[])
|
|
: (rawDepartments as Array<{ name: string }>).map((dept) => dept.name)
|
|
|
|
const cleanedNames = names.map(normalizeName).filter(Boolean)
|
|
const childrenMap = new Map<string, string[]>()
|
|
|
|
const ensureNode = (name: string) => {
|
|
if (!childrenMap.has(name)) {
|
|
childrenMap.set(name, [])
|
|
}
|
|
}
|
|
|
|
cleanedNames.forEach((name) => ensureNode(name))
|
|
|
|
cleanedNames.forEach((name) => {
|
|
const lastDashIndex = name.lastIndexOf('-')
|
|
if (lastDashIndex > 0) {
|
|
const parent = name.substring(0, lastDashIndex)
|
|
ensureNode(parent)
|
|
childrenMap.get(parent)!.push(name)
|
|
}
|
|
})
|
|
|
|
childrenMap.forEach((children) =>
|
|
children.sort((a, b) => departmentCollator.compare(a, b))
|
|
)
|
|
|
|
const descendantMap = new Map<string, Set<string>>()
|
|
|
|
const visit = (name: string): Set<string> => {
|
|
if (descendantMap.has(name)) {
|
|
return descendantMap.get(name)!
|
|
}
|
|
const descendants = new Set<string>([name])
|
|
const children = childrenMap.get(name) || []
|
|
children.forEach((child) => {
|
|
visit(child).forEach((desc) => descendants.add(desc))
|
|
})
|
|
descendantMap.set(name, descendants)
|
|
return descendants
|
|
}
|
|
|
|
childrenMap.forEach((_children, name) => visit(name))
|
|
|
|
return descendantMap
|
|
}
|
|
|
|
export const isDepartmentWithinScope = (
|
|
scope: string | null | undefined,
|
|
target: string | null | undefined,
|
|
descendantMap?: Map<string, Set<string>>
|
|
): boolean => {
|
|
const normalizedScope = normalizeName(scope)
|
|
const normalizedTarget = normalizeName(target)
|
|
|
|
if (!normalizedScope || !normalizedTarget) {
|
|
return false
|
|
}
|
|
|
|
if (normalizedScope === normalizedTarget) {
|
|
return true
|
|
}
|
|
|
|
const scopedDescendants = descendantMap?.get(normalizedScope)
|
|
if (scopedDescendants?.has(normalizedTarget)) {
|
|
return true
|
|
}
|
|
|
|
return normalizedTarget.startsWith(`${normalizedScope}-`)
|
|
}
|
|
|
|
export const getDepartmentCollator = () => departmentCollator
|
|
|
|
|