import { ref, watch, type Ref } from "vue"; import { getSystemResource } from "@/api/client"; import type { SystemMenuItem, SystemMenuTreeItem, SystemRoleItem } from "@/types"; export type SystemResourceResponse = { items: unknown[] }; export type SystemResourceFetcher = (resource: string) => Promise; type TreeSource = { id: string; name: string; parentId?: string | null; sortOrder?: number; }; export type SystemTreeNode = T & { children: Array> }; export function buildSystemTree(items: T[]): Array> { const nodes = new Map(items.map((item) => [item.id, { ...item, children: [] as Array> }])); const roots: Array> = []; const ordered = [...nodes.values()].sort((left, right) => (left.sortOrder ?? 0) - (right.sortOrder ?? 0) || left.name.localeCompare(right.name)); for (const node of ordered) { if (node.parentId && nodes.has(node.parentId)) nodes.get(node.parentId)!.children.push(node); else roots.push(node); } return roots; } export function hydrateMenuTree(tree: SystemMenuTreeItem[], items: readonly SystemMenuItem[]): SystemMenuTreeItem[] { const itemMap = new Map(items.map((item) => [item.id, item])); const hydrate = (nodes: SystemMenuTreeItem[]): SystemMenuTreeItem[] => nodes.map((node) => { const item = itemMap.get(node.id); return { ...(item ? { ...item, ...node } : node), children: hydrate(node.children ?? []), }; }); return hydrate(tree); } export function filterSystemTree(nodes: Array>, keyword: string): Array> { const normalized = keyword.trim().toLowerCase(); if (!normalized) return nodes; return nodes.reduce>>((result, node) => { const children = filterSystemTree(node.children, normalized); if (node.name.toLowerCase().includes(normalized) || children.length) result.push({ ...node, children }); return result; }, []); } export function dataScopeLabel(scope: SystemRoleItem["dataScope"] | string): string { return { all: "全部数据", dept: "本部门数据", dept_and_children: "本部门及以下", custom_dept: "自定义数据", self: "本人数据", }[scope] ?? scope; } function errorMessage(error: unknown) { return error instanceof Error ? error.message : "系统数据加载失败"; } export function useSystemResource( resource: Readonly>, fetcher: SystemResourceFetcher = getSystemResource, ) { const items = ref([]); const loading = ref(true); const error = ref(""); let requestVersion = 0; const load = async () => { const currentVersion = ++requestVersion; loading.value = true; error.value = ""; try { const response = await fetcher(resource.value); if (currentVersion !== requestVersion) return; items.value = response.items; } catch (err) { if (currentVersion !== requestVersion) return; items.value = []; error.value = errorMessage(err); } finally { if (currentVersion === requestVersion) loading.value = false; } }; watch(resource, load, { immediate: true }); return { items, loading, error, reload: load }; }