Files
ai-agent-admin/web/apps/web-ele/src/router/access.ts
T

199 lines
5.4 KiB
TypeScript

import type {
ComponentRecordType,
GenerateMenuAndRoutesOptions,
} from '@vben/types';
import { generateAccessible } from '@vben/access';
import { preferences } from '@vben/preferences';
import { getAllMenusApi } from '#/api/core/menu';
import { BasicLayout, IFrameView } from '#/layouts';
import { $t } from '#/locales';
import { useAppContextStore } from '#/store/app-context';
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
const lightMenuNames = new Set([
'AIAgent',
'AIKnowledgeDetail',
'AIModelConfig',
'AIPlatform',
'AIWorkflow',
'AIWorkflowRuns',
'AnnouncementList',
'AnnouncementManage',
'Codex',
'ControlCenter',
'KnowledgeBase',
'Message',
'MessageList',
'SystemManagement',
'SystemMenu',
'SystemPermission',
'SystemRole',
'UserManagement',
]);
function normalizeViewPath(path: string) {
const normalizedPath = path.replace(/^(\.\/|\.\.\/)+/, '');
const viewPath = normalizedPath.startsWith('/')
? normalizedPath
: `/${normalizedPath}`;
return viewPath.replace(/^\/views/, '');
}
function hasPageComponent(
component: string | undefined,
pageMap: ComponentRecordType,
layoutMap: ComponentRecordType,
) {
if (!component) return true;
if (layoutMap[component]) return true;
const normalizedPath = normalizeViewPath(component);
const pageKey = normalizedPath.endsWith('.vue')
? normalizedPath
: `${normalizedPath}.vue`;
return Boolean(pageMap[pageKey]);
}
function normalizePageMap(pageMap: ComponentRecordType) {
return Object.fromEntries(
Object.entries(pageMap).map(([key, value]) => [
normalizeViewPath(key),
value,
]),
) as ComponentRecordType;
}
function normalizeBackendRoute<
T extends {
component?: string;
name?: string;
path?: string;
query?: unknown;
},
>(route: T) {
if (route.name !== 'ControlCenter') return route;
const normalized = { ...route };
normalized.path = '/page-render/main_home';
normalized.component = 'control-center/index';
normalized.query = undefined;
return normalized;
}
function isLightMenuRoute(route: { name?: string }) {
return !route.name || lightMenuNames.has(route.name);
}
function filterAvailableRoutes<
T extends {
children?: T[];
component?: string;
name?: string;
path?: string;
query?: unknown;
},
>(
routes: T[],
pageMap: ComponentRecordType,
layoutMap: ComponentRecordType,
): T[] {
return routes
.filter(isLightMenuRoute)
.map((route) => {
const normalizedRoute = normalizeBackendRoute(route);
const children = normalizedRoute.children
? filterAvailableRoutes(normalizedRoute.children, pageMap, layoutMap)
: undefined;
if (
!normalizedRoute.component &&
normalizedRoute.children &&
!children?.length
) {
return undefined;
}
if (!hasPageComponent(normalizedRoute.component, pageMap, layoutMap)) {
if (!children?.length) return undefined;
const groupRoute = { ...normalizedRoute, children };
delete groupRoute.component;
return groupRoute;
}
return children ? { ...normalizedRoute, children } : normalizedRoute;
})
.filter(Boolean) as T[];
}
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
const pageMap: ComponentRecordType = import.meta.glob([
'../views/_core/agent-chat/**/*.vue',
'../views/_core/announcement/index.vue',
'../views/_core/announcement/list.vue',
'../views/_core/authentication/login.vue',
'../views/_core/authentication/oauth-callback.vue',
'../views/_core/fallback/**/*.vue',
'../views/_core/file-preview/index.vue',
'../views/_core/menu/index.vue',
'../views/_core/message/index.vue',
'../views/_core/permission/index.vue',
'../views/_core/role/index.vue',
'../views/_core/user/index.vue',
'../views/ai-platform/agent/editor/index.vue',
'../views/ai-platform/agent/index.vue',
'../views/ai-platform/knowledge/detail/index.vue',
'../views/ai-platform/knowledge/index.vue',
'../views/ai-platform/model/index.vue',
'../views/ai-platform/workflow/editor/index.vue',
'../views/ai-platform/workflow/index.vue',
'../views/ai-platform/workflow-runs/index.vue',
'../views/control-center/index.vue',
]);
const layoutMap: ComponentRecordType = {
BasicLayout,
IFrameView,
Layout: BasicLayout,
'Layout.vue': BasicLayout,
'/Layout.vue': BasicLayout,
};
const routePageMap = normalizePageMap(pageMap);
return await generateAccessible(preferences.app.accessMode, {
...options,
fetchMenuListAsync: async () => {
const { ElMessage } =
await import('element-plus/es/components/message/index');
ElMessage({
duration: 1500,
message: `${$t('common.loadingMenu')}...`,
});
// 获取应用上下文
const appContextStore = useAppContextStore();
// 如果是子应用模式,传递 appCode 过滤应用专属菜单
if (appContextStore.appCode) {
const menus = await getAllMenusApi(appContextStore.appCode);
return filterAvailableRoutes(menus, routePageMap, layoutMap);
}
// 主应用模式,不传参数
const menus = await getAllMenusApi();
return filterAvailableRoutes(menus, routePageMap, layoutMap);
},
// 可以指定没有权限跳转403页面
forbiddenComponent,
// 如果 route.meta.menuVisibleWithForbidden = true
layoutMap,
pageMap,
});
}
export { generateAccess };