import type { Component } from 'vue' import type { RouteRecordRaw } from 'vue-router' import type { MenuNode } from '@/types/auth' const viewModules = import.meta.glob('../features/**/index.vue') const dynamicRouteNames = new Set() function normalizePath(path: string) { if (!path.startsWith('/')) return `/${path}` return path } function routeNameByMenu(menu: MenuNode) { if (menu.name && menu.name.trim().length > 0) { return menu.name.trim() } return `menu-${menu.id}` } function resolveViewComponent(componentPath?: string | null): Component { if (!componentPath) { return () => import('@/views/errors/NotFoundView.vue') } const normalized = componentPath.endsWith('.vue') ? componentPath : `${componentPath}.vue` const fullPath = `../features/${normalized}` const component = viewModules[fullPath] if (!component) { return () => import('@/views/errors/NotFoundView.vue') } return component as unknown as Component } function toRoute(menu: MenuNode): RouteRecordRaw | null { if (menu.type !== 'MENU') return null if (!menu.path || !menu.component || !menu.visible) return null const name = routeNameByMenu(menu) dynamicRouteNames.add(name) return { path: normalizePath(menu.path), name, component: resolveViewComponent(menu.component), meta: { title: menu.title, permission: menu.permission ?? undefined, keepAlive: menu.keepAlive, requiresAuth: true, dynamic: true } } } function flattenMenus(menus: MenuNode[]): MenuNode[] { return menus.flatMap((item) => [item, ...flattenMenus(item.children ?? [])]) } export function buildDynamicRoutes(menus: MenuNode[]) { return flattenMenus(menus) .map(toRoute) .filter((item): item is RouteRecordRaw => Boolean(item)) } export function consumeDynamicRouteNames() { return [...dynamicRouteNames] } export function resetDynamicRouteNames() { dynamicRouteNames.clear() }