import { createRouter, createWebHistory } from 'vue-router' import NProgress from 'nprogress' import 'nprogress/nprogress.css' import DefaultLayout from '@/layouts/DefaultLayout.vue' import { useAuthStore } from '@/stores/auth' import { usePermissionStore } from '@/stores/permissions' NProgress.configure({ showSpinner: false }) const router = createRouter({ history: createWebHistory(), routes: [ { path: '/login', name: 'login', component: () => import('@/views/login/index.vue'), meta: { title: '登录' }, }, { path: '/', component: DefaultLayout, redirect: '/dashboard', meta: { requiresAuth: true }, children: [ { path: '/dashboard', name: 'dashboard', component: () => import('@/views/dashboard/index.vue'), meta: { title: '工作台', componentName: 'Dashboard' }, }, // 测试页:用于演练 Tabs 导航(无需权限,纯布局验证) { path: '/test/list', name: 'test-list', component: () => import('@/views/test/TestPage.vue'), meta: { title: '测试-列表', componentName: 'TestPage' }, }, { path: '/test/form', name: 'test-form', component: () => import('@/views/test/TestPage.vue'), meta: { title: '测试-表单', componentName: 'TestPage' }, }, { path: '/test/detail', name: 'test-detail', component: () => import('@/views/test/TestPage.vue'), meta: { title: '测试-详情', componentName: 'TestPage' }, }, { path: '/test/chart', name: 'test-chart', component: () => import('@/views/test/TestPage.vue'), meta: { title: '测试-图表', componentName: 'TestPage' }, }, { path: '/test/table', name: 'test-table', component: () => import('@/views/test/TestPage.vue'), meta: { title: '测试-表格', componentName: 'TestPage' }, }, { path: '/test/settings', name: 'test-settings', component: () => import('@/views/test/TestPage.vue'), meta: { title: '测试-设置', componentName: 'TestPage' }, }, // 组件库演练场:纳入业务布局(带侧栏 / 顶栏 / Tabs) { path: '/demo', name: 'demo', component: () => import('@/views/demo/index.vue'), meta: { title: '组件库', componentName: 'Demo' }, }, // 详情页:同一组件按参数复用,但每个 fullPath 独立缓存、关闭即销毁 // (per-param 的命名组件由 DefaultLayout 的 getPageWrapper 按路由动态生成) { path: '/detail/:id', name: 'detail', component: () => import('@/views/detail/DetailPage.vue'), meta: { title: '详情', cacheByKey: true }, }, // 业务页面后续在此追加,配置 meta.moduleId 后自动走权限校验 ], }, { path: '/:pathMatch(.*)*', redirect: '/dashboard', }, ], }) // 登录守卫:未登录访问受保护页跳 /login?redirect=xxx;已登录访问 /login 跳 /dashboard // 权限守卫:路由配置 meta.moduleId 时校验访问权限,无权限跳 /dashboard router.beforeEach(async (to) => { NProgress.start() const authStore = useAuthStore() if (to.path === '/login') { if (authStore.isAuthenticated) return '/dashboard' return } if (to.matched.some((r) => r.meta.requiresAuth)) { if (!authStore.isAuthenticated) { return { path: '/login', query: { redirect: to.fullPath } } } const moduleId = to.meta.moduleId if (moduleId) { const permissionStore = usePermissionStore() // 权限接口失败时静默降级:canAccess 返回 false,与"菜单退化为仅工作台"策略一致 await permissionStore.load().catch(() => {}) if (!permissionStore.canAccess(moduleId)) { return '/dashboard' } } } }) router.afterEach(() => { NProgress.done() }) export default router