197 lines
5.6 KiB
JavaScript
197 lines
5.6 KiB
JavaScript
import { createRouter, createWebHistory } from 'vue-router'
|
||
import { hasValidToken, getUserInfo, logoutUser, getStoredUser } from '../utils/jwt';
|
||
import { justLoggedIn } from '../utils/authSessionState';
|
||
import EditorsMaps from '@/views/index/EditorsMaps.vue'
|
||
import mitt from 'mitt'
|
||
import { hasPrivilege, hasPrivilegeWithTemp } from '@/utils/privilege'
|
||
|
||
const routes = [
|
||
{
|
||
path: '/dashboard',
|
||
redirect: '/backend/dashboard'
|
||
},
|
||
{
|
||
path: '/',
|
||
component: () => import('@/views/index.vue'),
|
||
children: [
|
||
{
|
||
path: '',
|
||
name: 'Home',
|
||
component: () => import('@/views/index/Home.vue')
|
||
},
|
||
{
|
||
path: 'demands',
|
||
name: 'DemandList',
|
||
component: () => import('@/views/index/DemandList.vue'),
|
||
meta: { requiresAuth: true }
|
||
},
|
||
{
|
||
path: 'maps',
|
||
name: 'Maps',
|
||
component: () => import('@/views/index/Maps.vue')
|
||
},
|
||
{
|
||
path: 'weekly',
|
||
name: 'WeeklyRecommend',
|
||
component: () => import('@/views/index/WeeklyRecommend.vue')
|
||
},
|
||
{
|
||
path: 'map/:id',
|
||
name: 'MapDetail',
|
||
component: () => import('@/views/index/MapDetail.vue')
|
||
},
|
||
{
|
||
path: 'author',
|
||
name: 'ActiveAuthor',
|
||
component: () => import('@/views/index/ActiveAuthor.vue')
|
||
},
|
||
{
|
||
path: 'weapon-match',
|
||
name: 'WeaponMatch',
|
||
component: () => import('@/views/weapon/WeaponMatch.vue'),
|
||
meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
|
||
},
|
||
{
|
||
path: 'configEditor',
|
||
name: 'ConfigEditor',
|
||
component: () => import('@/views/index/ConfigEditor.vue'),
|
||
meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-mod'] }
|
||
},
|
||
{
|
||
path: 'competition',
|
||
name: 'Competition',
|
||
component: () => import('@/views/competition/Competition.vue'),
|
||
// meta: { requiresAuth: true, requiredPrivilege: ['lv-admin','lv-competitor'] }
|
||
meta: { requiresAuth: true}
|
||
},
|
||
{
|
||
path: 'competition/add',
|
||
name: 'AddCompetition',
|
||
component: () => import('@/views/competition/AddContestant.vue'),
|
||
meta: { requiresAuth: true }
|
||
},
|
||
{
|
||
path: 'competition/detail',
|
||
name: 'CompetitionDetail',
|
||
component: () => import('@/views/competition/CompetitionDetail.vue'),
|
||
meta: { requiresAuth: true }
|
||
},
|
||
{
|
||
path: 'competition/signup',
|
||
name: 'CompetitionSignUp',
|
||
component: () => import('@/views/competition/CompetitionSignUp.vue'),
|
||
meta: { requiresAuth: true }
|
||
},
|
||
// {
|
||
// path: 'competition',
|
||
// name: 'Competition',
|
||
// component: () => import('@/views/competition/Competition.vue'),
|
||
// meta: { requiresAuth: true}
|
||
// },
|
||
{
|
||
path: 'editors-maps',
|
||
name: 'EditorsMaps',
|
||
component: () => import('@/views/index/EditorsMaps.vue')
|
||
},
|
||
{
|
||
path:'terrain',
|
||
name: 'Terrain',
|
||
component: () => import('@/views/index/TerrainList.vue')
|
||
},
|
||
{
|
||
path: 'PIC2TGA',
|
||
name: 'PIC2TGA',
|
||
component: () => import('@/views/index/PIC2TGA.vue'),
|
||
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||
},
|
||
{
|
||
path: 'terrainGenerate',
|
||
name: 'TerrainGenerate',
|
||
component: () => import('@/views/index/TerrainGenerate.vue'),
|
||
meta: { requiredPrivilege: ['lv-admin','lv-mod','lv-map','lv-competitor'] }
|
||
}
|
||
]
|
||
},
|
||
{
|
||
path: '/backend',
|
||
component: () => import('@/views/backend.vue'),
|
||
children: [
|
||
{
|
||
path: '',
|
||
redirect: 'dashboard'
|
||
},
|
||
{
|
||
path: 'login',
|
||
name: 'Login',
|
||
component: () => import('@/views/backend/Login.vue')
|
||
},
|
||
{
|
||
path: 'dashboard',
|
||
name: 'Dashboard',
|
||
component: () => import('@/views/backend/Dashboard.vue'),
|
||
meta: { requiresAuth: true }
|
||
}
|
||
]
|
||
},
|
||
{
|
||
path: '/user/resetpassword/:token',
|
||
name: 'ResetPassword',
|
||
component: () => import('@/views/ResetPassword.vue')
|
||
}
|
||
]
|
||
|
||
const router = createRouter({
|
||
history: createWebHistory(),
|
||
routes,
|
||
linkActiveClass: 'router-link-active',
|
||
linkExactActiveClass: 'router-link-exact-active'
|
||
})
|
||
|
||
export const eventBus = mitt()
|
||
|
||
// 路由守卫
|
||
router.beforeEach(async (to, from, next) => {
|
||
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
|
||
const requiredPrivilege = to.matched.find(record => record.meta.requiredPrivilege)?.meta.requiredPrivilege;
|
||
|
||
if (!requiresAuth && !requiredPrivilege) {
|
||
return next(); // 如果页面不需要认证和权限,直接放行
|
||
}
|
||
|
||
if (hasValidToken()) {
|
||
try {
|
||
await getUserInfo(); // 尝试获取用户信息,该函数会把用户信息存入SessionStorage
|
||
// 继续检查权限
|
||
} catch (error) {
|
||
// 获取失败 (token无效, 网络问题等)
|
||
logoutUser(); // 清除所有凭证
|
||
return next({
|
||
path: '/backend/login',
|
||
query: { redirect: to.fullPath, sessionExpired: 'true' }
|
||
});
|
||
// return next('/maps')
|
||
}
|
||
}
|
||
|
||
// 登录校验:如果页面需要登录但本地没有有效 token,则跳转登录页
|
||
if (requiresAuth && !hasValidToken()) {
|
||
return next({
|
||
path: '/',
|
||
query: { redirect: to.fullPath }
|
||
})
|
||
}
|
||
|
||
// 权限校验
|
||
if (requiredPrivilege) {
|
||
const user = getStoredUser();
|
||
if (!user || !hasPrivilegeWithTemp(user, requiredPrivilege)) {
|
||
// 通过事件总线通知弹窗
|
||
eventBus.emit('no-privilege')
|
||
return next({ path: '/', replace: true })
|
||
}
|
||
}
|
||
|
||
return next();
|
||
});
|
||
|
||
export default router
|