export type DeptNode = { id: string; name: string; code?: string | null; parentId?: string | null; }; function companyRootId(depts: DeptNode[]) { const roots = depts.filter((d) => !d.parentId); const named = roots.find((d) => d.name?.includes('公司') || d.code === 'ROOT' || d.code === 'OA_1'); return named?.id || roots[0]?.id; } export function isTopLevelDept(depts: DeptNode[], dept: DeptNode) { const rootId = companyRootId(depts); if (!rootId) return !dept.parentId; if (dept.id === rootId) return false; return dept.parentId === rootId || !dept.parentId; } export function topLevelDepartments(depts: DeptNode[]) { return depts.filter((d) => isTopLevelDept(depts, d)).sort((a, b) => a.name.localeCompare(b.name, 'zh')); } export function topLevelOf(depts: DeptNode[], deptId?: string | null): DeptNode | undefined { if (!deptId) return undefined; const byId = new Map(depts.map((d) => [d.id, d])); const rootId = companyRootId(depts); let cur = byId.get(deptId); const seen = new Set(); while (cur && !seen.has(cur.id)) { seen.add(cur.id); if (isTopLevelDept(depts, cur)) return cur; if (!cur.parentId || cur.parentId === rootId) return cur.id === rootId ? undefined : cur; cur = byId.get(cur.parentId); } return undefined; } export function topLevelNameOf(depts: DeptNode[], deptId?: string | null, fallback?: string | null) { return topLevelOf(depts, deptId)?.name || fallback || ''; } export function topLevelNameByLeafName(depts: DeptNode[], name?: string | null) { if (!name) return ''; const leaf = depts.find((d) => d.name === name); if (!leaf) return name; return topLevelOf(depts, leaf.id)?.name || name; } export function descendantIds(depts: DeptNode[], root?: string) { if (!root) return null; const kids = new Map(); for (const d of depts) { if (!d.parentId) continue; const arr = kids.get(d.parentId) || []; arr.push(d.id); kids.set(d.parentId, arr); } const out = new Set([root]); const stack = [root]; while (stack.length) { const id = stack.pop()!; for (const c of kids.get(id) || []) { if (!out.has(c)) { out.add(c); stack.push(c); } } } return out; } export function topLevelSelectOptions(depts: DeptNode[]) { return topLevelDepartments(depts).map((d) => ({ value: d.id, label: d.name })); }