126 lines
4.1 KiB
JavaScript
126 lines
4.1 KiB
JavaScript
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
|