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 };