Переглянути джерело

Phase8: 管理后台按角色过滤菜单与路由守卫

liaoxg 2 тижнів тому
батько
коміт
6a08ebd

+ 36 - 0
train-web/src/config/menu-roles.js

@@ -0,0 +1,36 @@
+// 管理后台菜单可见性:role → 可访问菜单路径(Layout 过滤与路由守卫共用,防两处漂移)
+// admin 全量 / lecturer 审核评分 / assistant 限负责组(审核与评分)
+const MENU_ROLES = {
+  '/dashboard': ['admin', 'lecturer', 'assistant'],
+  '/classes': ['admin'],
+  '/enrollments': ['admin'],
+  '/orders': ['admin'],
+  '/invites': ['admin'],
+  '/materials': ['admin'],
+  '/groups': ['admin'],
+  '/users': ['admin'],
+  '/review/submissions': ['admin', 'lecturer', 'assistant'],
+  '/review/cards': ['admin', 'lecturer', 'assistant'],
+  '/review/roadmaps': ['admin', 'lecturer', 'assistant'],
+  '/scoreboard': ['admin', 'lecturer', 'assistant'],
+  '/manual-score': ['admin', 'lecturer'],
+  '/stats': ['admin', 'lecturer']
+}
+
+// 各角色默认落点(越权重定向目标)
+const ROLE_DEFAULT_PATH = {
+  admin: '/dashboard',
+  lecturer: '/review/submissions',
+  assistant: '/review/submissions'
+}
+
+export function isAllowed(role, path) {
+  var allowed = MENU_ROLES[path]
+  return !allowed || allowed.indexOf(role) !== -1
+}
+
+export function defaultPath(role) {
+  return ROLE_DEFAULT_PATH[role] || '/dashboard'
+}
+
+export default MENU_ROLES

+ 6 - 0
train-web/src/router/index.js

@@ -1,6 +1,7 @@
 import Vue from 'vue'
 import VueRouter from 'vue-router'
 import Layout from '@/views/Layout.vue'
+import { isAllowed, defaultPath } from '@/config/menu-roles.js'
 
 Vue.use(VueRouter)
 
@@ -122,6 +123,11 @@ router.beforeEach(function (to, from, next) {
   if (!token) {
     return next('/login')
   }
+  // 角色越权检查:非授权菜单访问 → 重定向到该角色默认页
+  var role = localStorage.getItem('role') || 'admin'
+  if (!isAllowed(role, to.path)) {
+    return next(defaultPath(role))
+  }
   next()
 })
 

+ 18 - 12
train-web/src/views/Layout.vue

@@ -11,39 +11,39 @@
           active-text-color="#6366F1"
           @select="handleMenuSelect"
         >
-          <el-menu-item index="/dashboard">
+          <el-menu-item index="/dashboard" v-if="canSee('/dashboard')">
             <i class="el-icon-s-home"></i>
             <span slot="title">概览</span>
           </el-menu-item>
-          <el-menu-item index="/classes">
+          <el-menu-item index="/classes" v-if="canSee('/classes')">
             <i class="el-icon-s-grid"></i>
             <span slot="title">班级管理</span>
           </el-menu-item>
-          <el-menu-item index="/enrollments">
+          <el-menu-item index="/enrollments" v-if="canSee('/enrollments')">
             <i class="el-icon-tickets"></i>
             <span slot="title">报名管理</span>
           </el-menu-item>
-          <el-menu-item index="/orders">
+          <el-menu-item index="/orders" v-if="canSee('/orders')">
             <i class="el-icon-wallet"></i>
             <span slot="title">订单管理</span>
           </el-menu-item>
-          <el-menu-item index="/invites">
+          <el-menu-item index="/invites" v-if="canSee('/invites')">
             <i class="el-icon-share"></i>
             <span slot="title">转介绍漏斗</span>
           </el-menu-item>
-          <el-menu-item index="/materials">
+          <el-menu-item index="/materials" v-if="canSee('/materials')">
             <i class="el-icon-document"></i>
             <span slot="title">课前资料</span>
           </el-menu-item>
-          <el-menu-item index="/groups">
+          <el-menu-item index="/groups" v-if="canSee('/groups')">
             <i class="el-icon-s-group"></i>
             <span slot="title">分组管理</span>
           </el-menu-item>
-          <el-menu-item index="/users">
+          <el-menu-item index="/users" v-if="canSee('/users')">
             <i class="el-icon-user"></i>
             <span slot="title">学员管理</span>
           </el-menu-item>
-          <el-submenu index="review">
+          <el-submenu index="review" v-if="canSee('/review/submissions')">
             <template slot="title">
               <i class="el-icon-s-check"></i>
               <span>审核与评分</span>
@@ -52,15 +52,15 @@
             <el-menu-item index="/review/cards">教学卡评分</el-menu-item>
             <el-menu-item index="/review/roadmaps">路演评审</el-menu-item>
           </el-submenu>
-          <el-menu-item index="/scoreboard">
+          <el-menu-item index="/scoreboard" v-if="canSee('/scoreboard')">
             <i class="el-icon-trophy"></i>
             <span slot="title">积分榜</span>
           </el-menu-item>
-          <el-menu-item index="/manual-score">
+          <el-menu-item index="/manual-score" v-if="canSee('/manual-score')">
             <i class="el-icon-edit-outline"></i>
             <span slot="title">人工加减分</span>
           </el-menu-item>
-          <el-menu-item index="/stats">
+          <el-menu-item index="/stats" v-if="canSee('/stats')">
             <i class="el-icon-data-analysis"></i>
             <span slot="title">统计</span>
           </el-menu-item>
@@ -90,6 +90,8 @@
 </template>
 
 <script>
+import { isAllowed } from '@/config/menu-roles.js'
+
 export default {
   name: 'Layout',
   computed: {
@@ -104,6 +106,10 @@ export default {
     }
   },
   methods: {
+    canSee: function (path) {
+      var role = this.$store.state.adminInfo.role || 'admin'
+      return isAllowed(role, path)
+    },
     handleMenuSelect: function (index) {
       if (index && index.startsWith('/')) {
         this.$router.push(index)