283 lines
7.6 KiB
JavaScript
283 lines
7.6 KiB
JavaScript
|
|
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
|