94 lines
3.2 KiB
TypeScript
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 };
|
|
}
|