import { createRouter, createWebHistory, type RouteLocationNormalized, type RouteRecordRaw, } from 'vue-router' import type { App } from 'vue' import { routes } from 'vue-router/auto-routes' import { setupLayouts } from 'virtual:generated-layouts' import i18n, { loadLanguage } from '@/i18n' import { getAccessToken } from '@/utils/token' import { useMenuStore } from '@/stores/modules/menu' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), // routes routes: setupLayouts(routes as RouteRecordRaw[]), }) async function loadRouteLocales(to: RouteLocationNormalized) { const currentLang = localStorage.getItem('locale') || 'zh-CN' // 确保通用语言已加载 await loadLanguage(currentLang) // 加载路由所需的语言模块 if (to.meta.locales) { for (const locale of to.meta.locales as string[]) { try { await import(`@/i18n/locales/modules/${currentLang}/${locale}.ts`).then((messages) => { // 合并到当前语言的消息中 i18n.global.mergeLocaleMessage(currentLang, { [locale]: messages.default, }) }) } catch (e) { console.warn(`Failed to load locale module: ${locale}`, e) } } } } // 无需菜单权限校验的路径(首页 + 错误页) const FREE_PATHS = new Set(['/', '/403']) /** 收集菜单树中所有可访问 path。 */ function collectMenuPaths(nodes: { path?: string | null; children?: unknown[] }[]): string[] { const paths: string[] = [] for (const n of nodes) { if (n.path) paths.push(n.path) if (n.children?.length) paths.push(...collectMenuPaths(n.children as { path?: string | null; children?: unknown[] }[])) } return paths } // 路由前置守卫 router.beforeEach(async (to, from, next) => { await loadRouteLocales(to) // 未标记 requiresAuth: false 的路由默认需要登录 const requiresAuth = to.meta.requiresAuth !== false if (!requiresAuth) { next() return } if (!getAccessToken()) { next('/login') return } // 路由级权限:自由路径放行,其余须在可访问菜单中 if (FREE_PATHS.has(to.path)) { next() return } const menuStore = useMenuStore() try { await menuStore.fetch() } catch { next('/login') return } const accessible = new Set(collectMenuPaths(menuStore.menus)) if (accessible.has(to.path)) { next() } else { next('/403') } }) /** * 安装路由 * @param app Vue 应用实例 * 采用模块化方法注册路由,提高代码可维护性 */ export const installRouter = (app: App) => { app.use(router) } export default router