Procházet zdrojové kódy

feat: 用户权限相关

zhangjie před 1 týdnem
rodič
revize
141d7b4de4

+ 10 - 0
src/api/types/user.ts

@@ -158,3 +158,13 @@ export interface RolePrivilegeUpdateParam {
   // 选中的权限编码
   selectPrivileges: string[];
 }
+
+export interface UserMenuItem {
+  name: string;
+  code: string;
+  icon: string;
+  level: number;
+  privilegeType: string;
+  parentCode: string;
+  seq: number;
+}

+ 6 - 0
src/api/user.ts

@@ -16,6 +16,7 @@ import type {
   RolePrivilegeUpdateParam,
   RoleListPageParam,
   RoleItem,
+  UserMenuItem,
 } from './types/user';
 
 // 登录
@@ -167,3 +168,8 @@ export function updateRolePrivilege(
 ): Promise<boolean> {
   return axios.post('/api/admin/role/privilege/save', data);
 }
+
+// 获取用户权限菜单
+export function getUserPrivilegeMenu(): Promise<UserMenuItem[]> {
+  return axios.post('/api/admin/role/privilege/menu', {});
+}

+ 1 - 1
src/layout/default-layout.vue

@@ -208,7 +208,7 @@
 
   onMounted(() => {
     // TODO:开发设置
-    appStore.fetchServerMenu('ADMIN');
+    appStore.fetchLocalMenu();
 
     initData();
   });

+ 2 - 2
src/router/guard/permission.ts

@@ -5,7 +5,7 @@ import { useAppStore, useUserStore } from '@/store';
 import { WHITE_LIST, NOT_FOUND, DEFAULT_ROUTE_NAME } from '../constants';
 
 export default function setupUserLoginInfoGuard(router: Router) {
-  router.beforeEach((to, from, next) => {
+  router.beforeEach(async (to, from, next) => {
     if (WHITE_LIST.find((el) => el.name === to.name)) {
       next();
       NProgress.done();
@@ -15,7 +15,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
     const appStore = useAppStore();
     if (!appStore.appMenus.length) {
       const useStore = useUserStore();
-      appStore.fetchServerMenu(useStore.role);
+      await appStore.fetchServerMenu(useStore.role);
     }
 
     if (to.name === DEFAULT_ROUTE_NAME) {

+ 31 - 37
src/store/modules/app/index.ts

@@ -1,43 +1,36 @@
 import { defineStore } from 'pinia';
-import { omit } from 'lodash';
+import { UserMenuItem } from '@/api/types/user';
+import { getUserPrivilegeMenu } from '@/api/user';
+import { AppState, AppMenuItem } from './types';
+
 // import { sysAdminMenus as menus } from './menuData';
 import { adminMenus as menus } from './menuData';
 
-import { AppState, AppMenuItem, UserMenuItem } from './types';
-import { RoleType } from '../../../constants/enumerate';
-
-function transformMenu(list: typeof menus): UserMenuItem[] {
-  return list.map((menu) => omit(menu, ['roles']));
-}
-
 function getMenu(privilegeData: UserMenuItem[]): {
   menuList: AppMenuItem[];
   validRoutes: string[];
 } {
-  const getChildren = (id: number) => {
-    return privilegeData
-      .filter((item) => item.parentId === id)
-      .map((item) => {
-        const nitem: AppMenuItem = { ...item, children: [] };
-        return nitem;
-      });
-  };
-  const menuList = getChildren(-1);
+  const map = new Map<string, AppMenuItem>();
+  const menuList: AppMenuItem[] = [];
   const validRoutes: string[] = [];
-  const toTree = (data: AppMenuItem[]) => {
-    data.forEach((menu) => {
-      const children = getChildren(menu.id);
-      if (children.length) {
-        menu.children = children;
-        toTree(menu.children);
-      } else {
-        validRoutes.push(menu.url);
-      }
-    });
-  };
-  toTree(menuList);
 
-  // menuList = menuList.filter((item) => item.children && item.children.length);
+  // 创建节点映射
+  privilegeData.forEach((item) => {
+    const node: AppMenuItem = { ...item, url: item.code, children: [] };
+    map.set(item.code, node);
+    validRoutes.push(item.code);
+  });
+
+  // 构建树形结构
+  privilegeData.forEach((item) => {
+    const node = map.get(item.code);
+    if (!node) return;
+    if (item.parentCode && map.has(item.parentCode)) {
+      map.get(item.parentCode).children.push(node);
+    } else {
+      menuList.push(node);
+    }
+  });
 
   return { menuList, validRoutes };
 }
@@ -66,14 +59,15 @@ const useAppStore = defineStore('app', {
     setInfo(partial: Partial<AppState>) {
       this.$patch(partial);
     },
-    fetchServerMenu(type: RoleType | null) {
-      // const userMenus = type
-      //   ? menus.filter((menu) => !menu.roles || menu.roles.includes(type))
-      //   : ([] as typeof menus);
-      // // 开发时不管权限
-      console.log(type);
+    fetchLocalMenu() {
       const userMenus = menus;
-      const { menuList, validRoutes } = getMenu(transformMenu(userMenus));
+      const { menuList, validRoutes } = getMenu(userMenus);
+      this.appMenus = menuList;
+      this.validRoutes = validRoutes;
+    },
+    async fetchServerMenu() {
+      const userMenus = await getUserPrivilegeMenu();
+      const { menuList, validRoutes } = getMenu(userMenus);
       this.appMenus = menuList;
       this.validRoutes = validRoutes;
     },

+ 37 - 37
src/store/modules/app/menuData.ts

@@ -2,7 +2,7 @@ export const sysAdminMenus = [
   {
     id: 1,
     name: '学校管理',
-    url: 'SchoolManage',
+    code: 'SchoolManage',
     type: 'MENU',
     parentId: -1,
     sequence: 1,
@@ -11,7 +11,7 @@ export const sysAdminMenus = [
   {
     id: 2,
     name: '配置管理',
-    url: 'SetManage',
+    code: 'SetManage',
     type: 'MENU',
     parentId: -1,
     sequence: 2,
@@ -21,7 +21,7 @@ export const sysAdminMenus = [
   {
     id: 3,
     name: '授权管理',
-    url: 'AuthManage',
+    code: 'AuthManage',
     type: 'MENU',
     parentId: -1,
     sequence: 3,
@@ -33,7 +33,7 @@ export const adminMenus = [
   {
     id: 1,
     name: '主页导览',
-    url: 'HomeGuide',
+    code: 'HomeGuide',
     icon: 'icon-home',
     type: 'MENU',
     parentId: -1,
@@ -43,7 +43,7 @@ export const adminMenus = [
   {
     id: 2,
     name: '用户管理',
-    url: 'user',
+    code: 'user',
     icon: 'icon-user-manage',
     type: 'MENU',
     parentId: -1,
@@ -53,7 +53,7 @@ export const adminMenus = [
   {
     id: 201,
     name: '用户管理',
-    url: 'UserManage',
+    code: 'UserManage',
     icon: '',
     type: 'MENU',
     parentId: 2,
@@ -63,7 +63,7 @@ export const adminMenus = [
   {
     id: 3,
     name: '考试管理',
-    url: 'exam',
+    code: 'exam',
     icon: 'icon-exam-manage',
     type: 'MENU',
     parentId: -1,
@@ -73,7 +73,7 @@ export const adminMenus = [
   {
     id: 301,
     name: '考试管理',
-    url: 'ExamManage',
+    code: 'ExamManage',
     icon: '',
     type: 'MENU',
     parentId: 3,
@@ -83,7 +83,7 @@ export const adminMenus = [
   {
     id: 4,
     name: '考生管理',
-    url: 'student',
+    code: 'student',
     icon: 'icon-student-manage',
     type: 'MENU',
     parentId: -1,
@@ -93,7 +93,7 @@ export const adminMenus = [
   {
     id: 401,
     name: '考生管理',
-    url: 'StudentManage',
+    code: 'StudentManage',
     icon: '',
     type: 'MENU',
     parentId: 4,
@@ -103,7 +103,7 @@ export const adminMenus = [
   {
     id: 5,
     name: '扫描进度',
-    url: 'scan',
+    code: 'scan',
     icon: 'icon-scan-progress',
     type: 'MENU',
     parentId: -1,
@@ -113,7 +113,7 @@ export const adminMenus = [
   {
     id: 501,
     name: '扫描进度统计',
-    url: 'ScanManage',
+    code: 'ScanManage',
     icon: '',
     type: 'MENU',
     parentId: 5,
@@ -123,7 +123,7 @@ export const adminMenus = [
   {
     id: 6,
     name: '科目管理',
-    url: 'subject',
+    code: 'subject',
     icon: 'icon-subject-manage',
     type: 'MENU',
     parentId: -1,
@@ -133,7 +133,7 @@ export const adminMenus = [
   {
     id: 601,
     name: '科目管理',
-    url: 'SubjectManage',
+    code: 'SubjectManage',
     icon: '',
     type: 'MENU',
     parentId: 6,
@@ -144,7 +144,7 @@ export const adminMenus = [
     id: 7,
     name: '评卷管理',
     icon: 'icon-mark-manage',
-    url: 'mark',
+    code: 'mark',
     type: 'MENU',
     parentId: -1,
     sequence: 2,
@@ -153,7 +153,7 @@ export const adminMenus = [
   {
     id: 701,
     name: '评卷进度',
-    url: 'MarkProgress',
+    code: 'MarkProgress',
     icon: '',
     type: 'MENU',
     parentId: 7,
@@ -164,7 +164,7 @@ export const adminMenus = [
     id: 702,
     name: '科目评卷管理',
     icon: '',
-    url: 'MarkManage',
+    code: 'MarkManage',
     type: 'MENU',
     parentId: 7,
     sequence: 2,
@@ -173,7 +173,7 @@ export const adminMenus = [
   {
     id: 8,
     name: '打回卷管理',
-    url: 'reject',
+    code: 'reject',
     icon: 'icon-reject-manage',
     type: 'MENU',
     parentId: -1,
@@ -183,7 +183,7 @@ export const adminMenus = [
   {
     id: 801,
     name: '打回卷',
-    url: 'RejectManage',
+    code: 'RejectManage',
     icon: '',
     type: 'MENU',
     parentId: 8,
@@ -193,7 +193,7 @@ export const adminMenus = [
   {
     id: 802,
     name: '打回记录',
-    url: 'RejectRecord',
+    code: 'RejectRecord',
     icon: '',
     type: 'MENU',
     parentId: 8,
@@ -203,7 +203,7 @@ export const adminMenus = [
   {
     id: 803,
     name: '打回统计',
-    url: 'RejectStatistics',
+    code: 'RejectStatistics',
     icon: '',
     type: 'MENU',
     parentId: 8,
@@ -213,7 +213,7 @@ export const adminMenus = [
   {
     id: 9,
     name: '问题卷管理',
-    url: 'issue',
+    code: 'issue',
     icon: 'icon-issue-paper',
     type: 'MENU',
     parentId: -1,
@@ -223,7 +223,7 @@ export const adminMenus = [
   {
     id: 901,
     name: '问题卷',
-    url: 'IssuePaper',
+    code: 'IssuePaper',
     icon: '',
     type: 'MENU',
     parentId: 9,
@@ -233,7 +233,7 @@ export const adminMenus = [
   {
     id: 10,
     name: '成绩复核',
-    url: 'review',
+    code: 'review',
     icon: 'icon-score-review',
     type: 'MENU',
     parentId: -1,
@@ -243,7 +243,7 @@ export const adminMenus = [
   {
     id: 1001,
     name: '复核进度统计',
-    url: 'ScoreReviewStatistics',
+    code: 'ScoreReviewStatistics',
     icon: '',
     type: 'MENU',
     parentId: 10,
@@ -253,7 +253,7 @@ export const adminMenus = [
   {
     id: 1002,
     name: '全卷复核',
-    url: 'AllReview',
+    code: 'AllReview',
     icon: '',
     type: 'MENU',
     parentId: 10,
@@ -263,7 +263,7 @@ export const adminMenus = [
   {
     id: 1003,
     name: '成绩校验',
-    url: 'ScoreCheck',
+    code: 'ScoreCheck',
     icon: '',
     type: 'MENU',
     parentId: 10,
@@ -273,7 +273,7 @@ export const adminMenus = [
   {
     id: 11,
     name: '成绩查询',
-    url: 'score',
+    code: 'score',
     icon: 'icon-score-query',
     type: 'MENU',
     parentId: -1,
@@ -283,7 +283,7 @@ export const adminMenus = [
   {
     id: 1101,
     name: '成绩查询',
-    url: 'ScoreQuery',
+    code: 'ScoreQuery',
     icon: '',
     type: 'MENU',
     parentId: 11,
@@ -293,7 +293,7 @@ export const adminMenus = [
   {
     id: 12,
     name: '科目分析',
-    url: 'analysis',
+    code: 'analysis',
     icon: 'icon-subject-analysis',
     type: 'MENU',
     parentId: -1,
@@ -303,7 +303,7 @@ export const adminMenus = [
   {
     id: 1201,
     name: '科目分析',
-    url: 'AnalysisManage',
+    code: 'AnalysisManage',
     icon: '',
     type: 'MENU',
     parentId: 12,
@@ -313,7 +313,7 @@ export const adminMenus = [
   {
     id: 13,
     name: '数据检查',
-    url: 'check',
+    code: 'check',
     icon: 'icon-data-check',
     type: 'MENU',
     parentId: -1,
@@ -323,7 +323,7 @@ export const adminMenus = [
   {
     id: 1301,
     name: '人工确认',
-    url: 'ManualConfirm',
+    code: 'ManualConfirm',
     icon: '',
     type: 'MENU',
     parentId: 13,
@@ -333,7 +333,7 @@ export const adminMenus = [
   {
     id: 1302,
     name: '识别结果检查',
-    url: 'ResultCheck',
+    code: 'ResultCheck',
     icon: '',
     type: 'MENU',
     parentId: 13,
@@ -343,7 +343,7 @@ export const adminMenus = [
   {
     id: 1303,
     name: '图片检查',
-    url: 'ImageCheck',
+    code: 'ImageCheck',
     icon: '',
     type: 'MENU',
     parentId: 13,
@@ -353,7 +353,7 @@ export const adminMenus = [
   {
     id: 14,
     name: '操作日志',
-    url: 'log',
+    code: 'log',
     icon: 'icon-log',
     type: 'MENU',
     parentId: -1,
@@ -363,7 +363,7 @@ export const adminMenus = [
   {
     id: 1401,
     name: '操作日志',
-    url: 'LogManage',
+    code: 'LogManage',
     icon: '',
     type: 'MENU',
     parentId: 14,

+ 5 - 16
src/store/modules/app/types.ts

@@ -1,25 +1,14 @@
 import { ExamQueryItem } from '@/api/types/exam';
 
-export interface UserMenuItem {
-  id: number;
-  name: string;
-  url: string;
-  icon: string;
-  type: string;
-  parentId: number;
-  sequence: number;
-  enable: boolean;
-}
-
 export type AppMenuItem = {
-  id: number;
   name: string;
+  code: string;
   url: string;
   icon: string;
-  type: string;
-  parentId: number;
-  sequence: number;
-  enable: boolean;
+  level: number;
+  privilegeType: string;
+  parentCode: string;
+  seq: number;
   children: AppMenuItem[];
 };
 

+ 8 - 1
src/views/admin/school-manage/SchoolManage.vue

@@ -62,6 +62,7 @@
     ref="importDialogRef"
     title="科目拆分"
     upload-url="/api/admin/site/import"
+    :upload-data="uploadData"
     :format="['xls', 'xlsx']"
     :download-handle="() => downloadExport('splitCourseTemplate')"
     download-filename="科目拆分模板.xlsx"
@@ -81,7 +82,7 @@
 </template>
 
 <script setup lang="ts">
-  import { reactive, ref } from 'vue';
+  import { computed, reactive, ref } from 'vue';
   import { useRoute } from 'vue-router';
   import { ElMessage } from 'element-plus';
   import { schoolListPage } from '@/api/admin';
@@ -143,6 +144,12 @@
   // 导入
   const importDialogRef = ref();
   const importExamId = ref(0);
+  const uploadData = computed(() => {
+    return {
+      examId: importExamId.value,
+      schoolId: curRow.value.id,
+    };
+  });
   function onSplitCourse(row: SchoolItem) {
     curRow.value = row;
     importDialogRef.value?.open();