Files
1818uniapp-admin/src/router/index.js

283 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createRouter, createWebHistory } from 'vue-router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { useUserStore } from '@/store/modules/user'
NProgress.configure({ showSpinner: false })
// 静态路由
export const constantRoutes = [
{
path: '/',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/dashboard',
meta: { title: '首页', icon: 'DashboardOutlined' },
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index.vue'),
meta: { title: '控制台', icon: 'DashboardOutlined' }
}
]
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/login/index.vue'),
meta: { title: '登录', hidden: true }
},
{
path: '/403',
name: '403',
component: () => import('@/views/error/403.vue'),
meta: { title: '无权限', hidden: true }
},
{
path: '/404',
name: '404',
component: () => import('@/views/error/404.vue'),
meta: { title: '页面不存在', hidden: true }
}
]
// 动态路由(根据权限加载)
export const asyncRoutes = [
{
path: '/user',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/user/list',
meta: { title: '用户管理', icon: 'TeamOutlined' },
children: [
{
path: 'list',
name: 'UserList',
component: () => import('@/views/user/list.vue'),
meta: { title: '用户列表', icon: 'UnorderedListOutlined' }
}
]
},
{
path: '/order',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/order/list',
meta: { title: '订单管理', icon: 'ShoppingOutlined' },
children: [
{
path: 'list',
name: 'OrderList',
component: () => import('@/views/order/list.vue'),
meta: { title: '订单列表', icon: 'UnorderedListOutlined' }
}
]
},
{
path: '/work',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/work/list',
meta: { title: '作品管理', icon: 'PictureOutlined' },
children: [
{
path: 'list',
name: 'WorkList',
component: () => import('@/views/work/list.vue'),
meta: { title: '作品列表', icon: 'FileImageOutlined' }
},
{
path: 'audit',
name: 'WorkAudit',
component: () => import('@/views/work/audit.vue'),
meta: { title: '作品审核', icon: 'AuditOutlined' }
},
{
path: 'category',
name: 'WorkCategory',
component: () => import('@/views/work/category.vue'),
meta: { title: '分类管理', icon: 'AppstoreOutlined' }
}
]
},
{
path: '/ai',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/ai/provider',
meta: { title: 'AI管理', icon: 'RobotOutlined' },
children: [
{
path: 'provider',
name: 'AiProvider',
component: () => import('@/views/ai/provider.vue'),
meta: { title: 'AI厂商', icon: 'CloudServerOutlined' }
},
{
path: 'model',
name: 'AiModel',
component: () => import('@/views/ai/model.vue'),
meta: { title: 'AI模型', icon: 'ApiOutlined' }
},
{
path: 'task',
name: 'AiTask',
component: () => import('@/views/ai/task.vue'),
meta: { title: 'AI任务', icon: 'ScheduleOutlined' }
},
{
path: 'debug',
name: 'AiDebug',
component: () => import('@/views/ai/debug.vue'),
meta: { title: 'AI调试', icon: 'BugOutlined' }
}
]
},
{
path: '/config',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/config/reward',
meta: { title: '系统配置', icon: 'SettingOutlined' },
children: [
{
path: 'reward',
name: 'RewardConfig',
component: () => import('@/views/config/reward.vue'),
meta: { title: '奖励配置', icon: 'GiftOutlined' }
},
{
path: 'redeem',
name: 'RedeemConfig',
component: () => import('@/views/config/redeem.vue'),
meta: { title: '兑换码管理', icon: 'KeyOutlined' }
},
{
path: 'vip',
name: 'VipConfig',
component: () => import('@/views/config/vip.vue'),
meta: { title: 'VIP套餐', icon: 'CrownOutlined' }
},
{
path: 'points',
name: 'PointsConfig',
component: () => import('@/views/config/points.vue'),
meta: { title: '积分套餐', icon: 'StarOutlined' }
},
{
path: 'banner',
name: 'BannerConfig',
component: () => import('@/views/config/banner.vue'),
meta: { title: 'Banner管理', icon: 'FileImageOutlined' }
},
{
path: 'notice',
name: 'NoticeConfig',
component: () => import('@/views/config/notice.vue'),
meta: { title: '公告管理', icon: 'NotificationOutlined' }
}
]
},
{
path: '/system',
component: () => import('@/layouts/BasicLayout.vue'),
redirect: '/system/admin',
meta: { title: '系统管理', icon: 'ToolOutlined' },
children: [
{
path: 'admin',
name: 'AdminList',
component: () => import('@/views/system/admin.vue'),
meta: { title: '管理员管理', icon: 'UserOutlined' }
},
{
path: 'role',
name: 'RoleList',
component: () => import('@/views/system/role.vue'),
meta: { title: '角色管理', icon: 'SafetyOutlined' }
},
{
path: 'permission',
name: 'PermissionList',
component: () => import('@/views/system/permission.vue'),
meta: { title: '权限管理', icon: 'KeyOutlined' }
}
]
},
{ path: '/:pathMatch(.*)*', name: 'NotFound', redirect: '/404', meta: { hidden: true } }
]
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes
})
// 白名单
const whiteList = ['/login', '/403', '/404']
// 标记是否已添加动态路由
let dynamicRoutesAdded = false
// 添加动态路由
function addDynamicRoutes() {
if (dynamicRoutesAdded) return
asyncRoutes.forEach(route => router.addRoute(route))
dynamicRoutesAdded = true
}
// 重置路由(登出时调用)
export function resetRouter() {
dynamicRoutesAdded = false
// 移除动态添加的路由
asyncRoutes.forEach(route => {
if (route.name && router.hasRoute(route.name)) {
router.removeRoute(route.name)
}
})
}
// 路由守卫
router.beforeEach(async (to, _from, next) => {
NProgress.start()
const token = getToken()
if (token) {
if (to.path === '/login') {
next({ path: '/' })
} else {
// 确保动态路由已添加
addDynamicRoutes()
const userStore = useUserStore()
// 确保用户信息已获取(包含权限和角色)
if (!userStore.userInfo || !userStore.roles.length) {
try {
await userStore.getUserInfo()
} catch (error) {
console.error('获取用户信息失败', error)
// Token可能已失效跳转登录
userStore.resetState()
next(`/login?redirect=${to.path}`)
return
}
}
// 检查路由是否存在
if (router.hasRoute(to.name) || to.matched.length > 0) {
next()
} else {
// 路由刚添加,需要重新导航
next({ ...to, replace: true })
}
}
} else {
if (whiteList.includes(to.path)) {
next()
} else {
next(`/login?redirect=${to.path}`)
}
}
})
router.afterEach(() => {
NProgress.done()
})
export default router