All files / src/router index.js

73.33% Statements 22/30
83.33% Branches 10/12
42.85% Functions 6/14
75% Lines 21/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126              1x   1x           1x                       1x             1x                                                                                                                       1x 14x 14x   14x 6x 6x     8x 8x 1x         7x 7x   7x 7x 1x         1x 12x        
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') {
    Iif (authStore.isAuthenticated) return '/dashboard'
    return
  }
 
  Eif (to.matched.some((r) => r.meta.requiresAuth)) {
    if (!authStore.isAuthenticated) {
      return { path: '/login', query: { redirect: to.fullPath } }
    }
 
    // 统一在路由守卫加载权限:首屏(/dashboard)与业务页都在挂载前 await 完成,
    // 菜单一次性到位、不会“跳出”;load 内部按主体去重,重复进入同一主体不重复请求
    const permissionStore = usePermissionStore()
    await permissionStore.load().catch(() => {})
 
    const moduleId = to.meta.moduleId
    if (moduleId && !permissionStore.canAccess(moduleId)) {
      return '/dashboard'
    }
  }
})
 
router.afterEach(() => {
  NProgress.done()
})
 
export default router