web/src/router/index.ts
@@ -1,5 +1,5 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import { isLoggedIn } from '@/utils/auth'
import { isLoggedIn, getToken, isTokenExpired } from '@/utils/auth'
const routes: RouteRecordRaw[] = [
  {
@@ -13,17 +13,12 @@
        component: () => import('@/views/dashboard/index.vue'),
        meta: { title: '工作台', icon: 'Grid' }
      },
      {
        path: '/activity',
        name: 'Activity',
        component: () => import('@/views/activity/index.vue'),
        component: () => import('@/views/activity-list.vue'),
        meta: { title: '比赛管理', icon: 'Trophy' }
      },
      {
        path: '/activity/:id',
        name: 'ActivityDetail',
        component: () => import('@/views/ActivityDetail.vue'),
        meta: { title: '比赛详情', icon: 'Trophy' }
      },
      {
        path: '/activity/new',
@@ -38,40 +33,46 @@
        meta: { title: '编辑比赛', icon: 'Trophy' }
      },
      {
        path: '/activity/:id',
        name: 'ActivityDetail',
        component: () => import('@/views/ActivityDetail.vue'),
        meta: { title: '比赛详情', icon: 'Trophy' }
      },
      {
        path: '/judge',
        name: 'Judge',
        component: () => import('@/views/judge/index.vue'),
        component: () => import('@/views/judge-list.vue'),
        meta: { title: '评委管理', icon: 'UserFilled' }
      },
      {
        path: '/rating-scheme',
        name: 'RatingScheme',
        component: () => import('@/views/rating/index.vue'),
        meta: { title: '评分模板', icon: 'Score' }
        component: () => import('@/views/rating-list.vue'),
        meta: { title: '评分模板', icon: 'Document' }
      },
      {
        path: '/rating-scheme/new',
        name: 'RatingSchemeCreate',
        component: () => import('@/views/rating/Form.vue'),
        meta: { title: '新增评分模板', icon: 'Score' }
        component: () => import('@/views/rating-detail.vue'),
        meta: { title: '新建评分模板', hidden: true }
      },
      {
        path: '/rating-scheme/edit/:id',
        name: 'RatingSchemeEdit',
        component: () => import('@/views/rating/Form.vue'),
        meta: { title: '编辑评分模板', icon: 'Score' }
        component: () => import('@/views/rating-detail.vue'),
        meta: { title: '编辑评分模板', hidden: true }
      },
      {
        path: '/player',
        name: 'Player',
        component: () => import('@/views/player/index.vue'),
        meta: { title: '报名审核', icon: 'UserFilled' }
        component: () => import('@/views/check-list.vue'),
        meta: { title: '参赛人员', icon: 'UserFilled' }
      },
      {
        path: '/player/:id/detail',
        name: 'PlayerDetail',
        component: () => import('@/views/player/detail.vue'),
        meta: { title: '报名详情', icon: 'UserFilled' }
        component: () => import('@/views/check-detail.vue'),
        meta: { title: '参赛人员详情' }
      },
      {
        path: '/activity-player/:id/rating',
@@ -94,51 +95,40 @@
      {
        path: '/employee',
        name: 'Employee',
        component: () => import('@/views/employee/index.vue'),
        meta: { title: '员工管理', icon: 'Avatar' }
        component: () => import('@/views/employee-list.vue'),
        meta: { title: '员工管理', icon: 'User' }
      },
      {
        path: '/project-review',
        name: 'ProjectReview',
        component: () => import('@/views/project-review/index.vue'),
        component: () => import('@/views/review-list.vue'),
        meta: { title: '项目评审', icon: 'View' }
      },
      {
        path: '/project-review/:id/detail',
        name: 'ProjectReviewDetail',
        component: () => import('@/views/project-review/detail.vue'),
        component: () => import('@/views/review-detail.vue'),
        meta: { title: '项目评审详情', hidden: true }
      },
      {
        path: '/review',
        name: 'Review',
        component: () => import('@/views/review/index.vue'),
        meta: { title: '项目评审', icon: 'Edit' }
        component: () => import('@/views/judge-review-list.vue'),
        meta: { title: '评委评审', icon: 'Edit' }
      },
      {
        path: '/review/:id/detail',
        name: 'ReviewDetail',
        component: () => import('@/views/review/detail.vue'),
        meta: { title: '项目评审详情', hidden: true }
        component: () => import('@/views/judge-review-detail.vue'),
        meta: { title: '评委评审详情', hidden: true }
      },
      {
        path: '/competition-promotion',
        name: 'CompetitionPromotion',
        component: () => import('@/views/competition-promotion/index.vue'),
        component: () => import('@/views/next-list.vue'),
        meta: { title: '比赛晋级', icon: 'Promotion' }
      },
      {
        path: '/test/graphql',
        name: 'GraphQLTest',
        component: () => import('@/views/test/graphql-test.vue'),
        meta: { title: 'GraphQL测试', icon: 'Connection' }
      },
      {
        path: '/test/user-prefill',
        name: 'TestUserPrefill',
        component: () => import('@/pages/TestUserPrefill.vue'),
        meta: { title: '用户信息预填充测试', icon: 'User' }
      }
    ]
  },
  {
@@ -158,8 +148,9 @@
router.beforeEach((to, from, next) => {
  // 如果是登录页面,直接放行
  if (to.path === '/login') {
    // 如果已经登录,重定向到首页
    if (isLoggedIn()) {
    // 仅在“有token且未过期且本地已记录登录信息”时才从登录页跳转到首页
    const t = getToken()
    if (t && !isTokenExpired(t) && isLoggedIn()) {
      next('/')
    } else {
      next()
@@ -167,9 +158,9 @@
    return
  }
  // 检查是否已登录
  if (!isLoggedIn()) {
    // 未登录,重定向到登录页
  // 检查是否已登录且token未过期
  const token = getToken()
  if (!token || isTokenExpired(token) || !isLoggedIn()) {
    next('/login')
    return
  }