feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
270
frontend/src/router/guard.ts
Normal file
270
frontend/src/router/guard.ts
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 路由守卫
|
||||
* 处理路由权限验证、登录状态检查等
|
||||
*/
|
||||
|
||||
import { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { authManager } from '@/utils/auth'
|
||||
import { loadingManager } from '@/utils/loadingManager'
|
||||
|
||||
// 白名单路由(不需要登录)
|
||||
const WHITE_LIST = ['/login', '/register', '/404']
|
||||
|
||||
// 需要特殊权限的路由映射
|
||||
const ROUTE_PERMISSIONS: Record<string, string[]> = {
|
||||
'/admin': ['admin'],
|
||||
'/manager': ['manager', 'admin'],
|
||||
'/analysis': ['manager', 'admin']
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路由守卫
|
||||
*/
|
||||
export function setupRouterGuard(router: Router) {
|
||||
// 全局前置守卫
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 显示页面加载状态
|
||||
loadingManager.start('page-loading', {
|
||||
message: '页面加载中...',
|
||||
background: 'rgba(255, 255, 255, 0.8)'
|
||||
})
|
||||
|
||||
try {
|
||||
await handleRouteGuard(to, from, next)
|
||||
} catch (error) {
|
||||
console.error('Route guard error:', error)
|
||||
ElMessage.error('页面加载失败,请重试')
|
||||
next('/404')
|
||||
}
|
||||
})
|
||||
|
||||
// 全局后置守卫
|
||||
router.afterEach((to, from) => {
|
||||
// 隐藏页面加载状态
|
||||
loadingManager.stop('page-loading')
|
||||
|
||||
// 设置页面标题
|
||||
document.title = getPageTitle(to.meta?.title as string)
|
||||
|
||||
// 记录页面访问日志
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(`[Router] Navigate from ${from.path} to ${to.path}`)
|
||||
}
|
||||
})
|
||||
|
||||
// 路由错误处理
|
||||
router.onError((error) => {
|
||||
console.error('Router error:', error)
|
||||
ElMessage.error('路由加载失败')
|
||||
loadingManager.stop('page-loading')
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理路由守卫逻辑
|
||||
*/
|
||||
async function handleRouteGuard(
|
||||
to: RouteLocationNormalized,
|
||||
_from: RouteLocationNormalized,
|
||||
next: NavigationGuardNext
|
||||
) {
|
||||
const { path } = to
|
||||
|
||||
// 白名单路由直接通过
|
||||
if (WHITE_LIST.includes(path)) {
|
||||
// 如果已登录用户访问登录页,重定向到首页
|
||||
if (path === '/login' && authManager.isAuthenticated()) {
|
||||
next(authManager.getDefaultRoute())
|
||||
return
|
||||
}
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查登录状态
|
||||
if (!authManager.isAuthenticated()) {
|
||||
ElMessage.warning('请先登录')
|
||||
next(`/login?redirect=${encodeURIComponent(path)}`)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查token是否过期
|
||||
if (authManager.isTokenExpired()) {
|
||||
try {
|
||||
// 尝试刷新token
|
||||
await authManager.refreshToken()
|
||||
} catch (error) {
|
||||
ElMessage.error('登录已过期,请重新登录')
|
||||
next(`/login?redirect=${encodeURIComponent(path)}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 检查路由权限
|
||||
if (!checkRoutePermission(path)) {
|
||||
ElMessage.error('您没有访问此页面的权限')
|
||||
// 根据用户角色重定向到合适的页面
|
||||
next(authManager.getDefaultRoute())
|
||||
return
|
||||
}
|
||||
|
||||
// 检查特殊路由规则
|
||||
if (!checkSpecialRouteRules(to)) {
|
||||
ElMessage.error('访问被拒绝')
|
||||
next(authManager.getDefaultRoute())
|
||||
return
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查路由权限
|
||||
*/
|
||||
function checkRoutePermission(path: string): boolean {
|
||||
// 检查是否可以访问路由
|
||||
if (!authManager.canAccessRoute(path)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查特定路由的权限要求
|
||||
for (const [routePrefix, roles] of Object.entries(ROUTE_PERMISSIONS)) {
|
||||
if (path.startsWith(routePrefix)) {
|
||||
const userRole = authManager.getUserRole()
|
||||
if (!userRole || !roles.includes(userRole)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查特殊路由规则
|
||||
*/
|
||||
function checkSpecialRouteRules(to: RouteLocationNormalized): boolean {
|
||||
const { path, params } = to
|
||||
|
||||
// 检查用户ID参数权限(只能访问自己的数据,管理员除外)
|
||||
if (params.userId && !authManager.isAdmin()) {
|
||||
const currentUser = authManager.getCurrentUser()
|
||||
if (currentUser && String(params.userId) !== String(currentUser.id)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 检查团队ID参数权限
|
||||
if (params.teamId && !authManager.isAdmin()) {
|
||||
// 这里可以添加团队权限检查逻辑
|
||||
// 暂时允许通过,实际项目中需要检查用户是否属于该团队
|
||||
}
|
||||
|
||||
// 检查课程访问权限
|
||||
if (path.includes('/course/') && params.courseId) {
|
||||
// 这里可以添加课程访问权限检查
|
||||
// 例如检查课程是否分配给用户的岗位
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页面标题
|
||||
*/
|
||||
function getPageTitle(title?: string): string {
|
||||
const appTitle = '考培练系统'
|
||||
return title ? `${title} - ${appTitle}` : appTitle
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态添加路由(用于角色权限路由)
|
||||
*/
|
||||
export function addDynamicRoutes(router: Router) {
|
||||
const userRole = authManager.getUserRole()
|
||||
if (!userRole) return
|
||||
|
||||
// 根据角色动态添加路由
|
||||
const dynamicRoutes = getDynamicRoutesByRole(userRole)
|
||||
|
||||
dynamicRoutes.forEach(route => {
|
||||
router.addRoute(route)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色获取动态路由
|
||||
*/
|
||||
function getDynamicRoutesByRole(role: string) {
|
||||
const routes: any[] = []
|
||||
|
||||
// 根据角色添加不同的路由
|
||||
switch (role) {
|
||||
case 'admin':
|
||||
// 管理员可以访问所有路由
|
||||
break
|
||||
case 'manager':
|
||||
// 管理者路由
|
||||
break
|
||||
case 'trainee':
|
||||
// 学员路由
|
||||
break
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查页面权限(组件内使用)
|
||||
*/
|
||||
export function checkPagePermission(permissions: string[]): boolean {
|
||||
if (!permissions || permissions.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
const userRole = authManager.getUserRole()
|
||||
if (!userRole) return false
|
||||
|
||||
return permissions.includes(userRole) || authManager.isAdmin()
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限指令(v-permission)
|
||||
*/
|
||||
export const permissionDirective = {
|
||||
mounted(el: HTMLElement, binding: any) {
|
||||
const { value } = binding
|
||||
|
||||
if (value && !checkPagePermission(Array.isArray(value) ? value : [value])) {
|
||||
el.style.display = 'none'
|
||||
}
|
||||
},
|
||||
|
||||
updated(el: HTMLElement, binding: any) {
|
||||
const { value } = binding
|
||||
|
||||
if (value && !checkPagePermission(Array.isArray(value) ? value : [value])) {
|
||||
el.style.display = 'none'
|
||||
} else {
|
||||
el.style.display = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由元信息接口扩展
|
||||
*/
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
title?: string
|
||||
icon?: string
|
||||
hidden?: boolean
|
||||
roles?: string[]
|
||||
permissions?: string[]
|
||||
keepAlive?: boolean
|
||||
affix?: boolean
|
||||
breadcrumb?: boolean
|
||||
activeMenu?: string
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user