Files
WonderQ-Project/WonderQ-Admin-UI-Vue/src/lib/system-resource.ts
2026-08-27 10:26:12 +08:00

94 lines
3.2 KiB
TypeScript

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<SystemResourceResponse>;
type TreeSource = {
id: string;
name: string;
parentId?: string | null;
sortOrder?: number;
};
export type SystemTreeNode<T extends TreeSource> = T & { children: Array<SystemTreeNode<T>> };
export function buildSystemTree<T extends TreeSource>(items: T[]): Array<SystemTreeNode<T>> {
const nodes = new Map(items.map((item) => [item.id, { ...item, children: [] as Array<SystemTreeNode<T>> }]));
const roots: Array<SystemTreeNode<T>> = [];
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<T extends TreeSource>(nodes: Array<SystemTreeNode<T>>, keyword: string): Array<SystemTreeNode<T>> {
const normalized = keyword.trim().toLowerCase();
if (!normalized) return nodes;
return nodes.reduce<Array<SystemTreeNode<T>>>((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<Ref<string>>,
fetcher: SystemResourceFetcher = getSystemResource,
) {
const items = ref<unknown[]>([]);
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 };
}