瀏覽代碼

feat: test-登录与导览

zhangjie 2 天之前
父節點
當前提交
eb1386d62e

+ 13 - 0
src/api/admin.ts

@@ -7,6 +7,7 @@ import {
   SchoolStatusInfo,
   AuthInfo,
   AuthUpdateParams,
+  SettingUpdateParam,
 } from './types/admin';
 import { CommonActionRes } from './types/common';
 
@@ -75,3 +76,15 @@ export function updateAuthInfo(
 export function exportHardwareInfo(): Promise<AxiosResponse<blob>> {
   return axios.post('/api/admin/auth/export', {}, { responseType: 'blob' });
 }
+
+// 配置管理
+// 配置管理列表
+export function getSystemSettings(): Promise<SettingItem[]> {
+  return axios.post('/api/admin/sys/config/list');
+}
+// 配置管理更新
+export function updateSystemSetting(
+  params: SettingUpdateParam
+): Promise<boolean> {
+  return axios.post('/api/admin/sys/config/update', params);
+}

+ 6 - 0
src/api/exam.ts

@@ -5,6 +5,7 @@ import {
   ExamItem,
   ExamUpdateParam,
   ExamStatDetailInfo,
+  ExamGuideInfo,
 } from './types/exam';
 
 // 考试列表
@@ -41,3 +42,8 @@ export function getExamStatisticInfo(
 export function finishExam(ids: number[]): Promise<boolean> {
   return axios.post('/api/admin/exam/finish', {}, { params: { ids } });
 }
+
+// 获取考试导览信息
+export function getExamGuideInfo(): Promise<ExamGuideInfo> {
+  return axios.post('/api/admin/exam/overview', {});
+}

+ 14 - 13
src/api/interceptor.ts

@@ -9,12 +9,13 @@ import { ElMessage, ElMessageBox, ElNotification } from 'element-plus';
 import type { MessageHandler } from 'element-plus';
 import { initSyncTime, fetchTime } from '../utils/syncServerTime';
 import { getAuthorization } from '../utils/crypto';
-import { DEVICE_ID, PLATFORM } from '../constants/app';
+// import { DEVICE_ID, PLATFORM } from '../constants/app';
 import { objTypeOf } from '../utils/utils';
 import { useUserStore } from '../store';
 
 axios.defaults.timeout = 60 * 1000;
-
+axios.defaults.headers.post['Content-Type'] =
+  'application/x-www-form-urlencoded';
 let load: MessageHandler | null = null;
 // 同一时间有多个请求时,会形成队列。在第一个请求创建loading,在最后一个响应关闭loading
 const queue: Array<string | undefined> = [];
@@ -25,16 +26,14 @@ const modifyConfig = (config: AxiosRequestConfig): void => {
   const userStore = useUserStore();
   if (!config.headers) config.headers = {};
 
-  if (userStore.token) {
-    config.headers.userId = String(userStore.id);
-
+  if (userStore.accessToken) {
     // 新版鉴权
     const timestamp = fetchTime();
     const Authorization = getAuthorization(
       {
-        token: userStore.token,
+        token: userStore.accessToken,
         timestamp,
-        account: userStore.sessionId,
+        account: userStore.loginName,
         uri: config.url?.split('?')[0] || '',
         method: config.method as string,
       },
@@ -43,9 +42,9 @@ const modifyConfig = (config: AxiosRequestConfig): void => {
     config.headers.Authorization = Authorization;
     config.headers.time = String(timestamp);
   }
-  config.headers.deviceId = DEVICE_ID;
-  config.headers.platform = PLATFORM;
-  config.headers.domain = window.location.origin;
+  // config.headers.deviceId = DEVICE_ID;
+  // config.headers.platform = PLATFORM;
+  // config.headers.domain = window.location.origin;
 };
 
 axios.interceptors.request.use(
@@ -61,7 +60,7 @@ axios.interceptors.request.use(
     }
     queue.push(config.url);
 
-    modifyConfig(config as AxiosRequestConfig); // Cast to AxiosRequestConfig if modifyConfig expects that
+    modifyConfig(config as AxiosRequestConfig);
     return config;
   },
   (error) => {
@@ -94,6 +93,8 @@ axios.interceptors.response.use(
     return response.data;
   },
   (error) => {
+    console.log(error);
+
     // 关闭loading提示
     setTimeout(() => {
       queue.shift();
@@ -154,7 +155,7 @@ axios.interceptors.response.use(
     ElMessageBox.confirm(message, '重新登陆?', {
       confirmButtonText: '确定',
       showCancelButton: false,
-      type: 'warning',
+      type: 'error',
       autofocus: false,
       closeOnClickModal: false,
       closeOnPressEscape: false,
@@ -163,7 +164,7 @@ axios.interceptors.response.use(
       .then(() => {
         unauthMsgBoxIsShow = false;
         const userStore = useUserStore();
-        userStore.logout();
+        userStore.logout(false);
       })
       .catch(() => {
         // 用户点击了关闭按钮或者按了 ESC,理论上不会发生,因为 showClose 和 closeOnPressEscape 都是 false

+ 17 - 3
src/api/types/admin.ts

@@ -59,14 +59,28 @@ export interface SchoolStatusInfo {
 }
 
 // set
+export type SettingType =
+  | 'FILE_SERVER'
+  | 'MARK_TIME'
+  | 'AUTO_REPORT'
+  | 'STUDENT_SHEET_COUNT'
+  | 'QUALITY_ANALYSIS';
 export interface SettingItem {
   id: string | number; // 假设配置项有唯一ID
   // 配置项类型
-  type: string;
+  name: string;
+  // 配置项key: FILE_SERVER,MARK_TIME,AUTO_REPORT,STUDENT_SHEET_COUNT,QUALITY_ANALYSIS
+  type: SettingType;
   // 配置项值
   description: string | boolean | number;
-  // 配置项key
-  key: string; // 后端可能用key来区分配置项
+  descriptionText: string;
+}
+
+export interface SettingUpdateParam {
+  id: number;
+  name: string;
+  type: SettingType;
+  description: string | boolean | number;
 }
 
 // auth

+ 13 - 0
src/api/types/exam.ts

@@ -112,3 +112,16 @@ export interface ExamStatDetailInfo {
   // 评卷进度
   markedCount: number;
 }
+
+export interface ExamGuideInfo {
+  // 检查缺考
+  checkAbsent: boolean;
+  // 检查考生
+  checkStudent: boolean;
+  // 检查科目
+  checkSubject: boolean;
+  // 考生数量
+  studentCount: number;
+  // 科目数量
+  subjectCount: number;
+}

+ 8 - 4
src/api/types/user.ts

@@ -9,6 +9,7 @@ export interface LoginData {
 // 登录名	名称	来源	角色	状态	关联账号
 export interface UserItem {
   id: number;
+  account: string;
   // 登录名
   loginName: string;
   // 名称
@@ -35,12 +36,10 @@ export interface UserItem {
   description: string;
   // 访问令牌
   accessToken: string;
-  // 扫描令牌
-  scanToken: string;
+  // 访问令牌刷新时间
+  accessTokenRefreshTime: number;
   // 学校名称
   schoolName: string;
-  // 学校ID
-  schoolId: number;
 }
 export type UserListPageRes = PageResult<UserItem>;
 
@@ -118,6 +117,10 @@ export interface SystemInfo {
   versionName: string;
   // 版本日期
   versionDate: string;
+  // 文件服务器
+  fileServer: string;
+  // 首页Logo
+  indexLogo: string;
 }
 
 // 角色相关 --------->
@@ -165,6 +168,7 @@ export interface UserMenuItem {
   icon: string;
   level: number;
   privilegeType: string;
+  privilegeUri: string;
   parentCode: string;
   seq: number;
 }

+ 21 - 6
src/api/user.ts

@@ -1,5 +1,4 @@
 import axios, { AxiosResponse } from 'axios';
-import { UserState } from '@/store/modules/user/types';
 import type {
   LoginData,
   UserItem,
@@ -20,22 +19,38 @@ import type {
 } from './types/user';
 
 // 登录
-export function login(data: LoginData): Promise<UserState> {
-  return axios.post('/api/user/login', data);
+export function login(data: LoginData): Promise<UserItem> {
+  return axios.post('/api/admin/sys/login', data);
 }
+
 // 修改密码
-export function updatePwd(datas: UpdatePasswordParam): Promise<UserState> {
+export function updatePwd(datas: UpdatePasswordParam): Promise<boolean> {
   return axios.post('/api/user/password/modify', {}, { params: datas });
 }
+
 // 退出登录
 export function userLogout() {
-  return axios.post('/api/user/logout', {});
+  return axios.post('/api/admin/sys/logout', {});
+}
+
+// 选择考试
+export function switchExam(examId: number): Promise<boolean> {
+  return axios.post('/api/admin/sys/select/exam', {}, { params: { examId } });
+}
+// 选做分组
+export function switchGroup(groupId: number): Promise<boolean> {
+  return axios.post('/api/admin/sys/select/group', {}, { params: { groupId } });
+}
+
+// 首次登录修改用户信息
+export function updateFirstUserInfo(data: UserUpdateParam): Promise<boolean> {
+  return axios.post('/api/admin/user/resetName', data);
 }
 
 // 系统信息
 // 系统版本
 export function systemVersion(): Promise<SystemInfo> {
-  return axios.post('/api/sys/version', {});
+  return axios.post('/api/admin/sys/version', {});
 }
 
 // 用户管理

+ 11 - 0
src/assets/style/element-custom.scss

@@ -166,6 +166,17 @@
     }
   }
 }
+.el-select--large {
+  .el-select__wrapper {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+  .el-select__suffix {
+    height: 38px;
+    width: 39px;
+    margin-right: -15px;
+  }
+}
 .el-select-dropdown {
   .el-select-option {
     &.el-select-option-selected {

+ 3 - 0
src/assets/style/pages.scss

@@ -30,6 +30,9 @@
       color: var(--color-primary);
     }
   }
+  span {
+    margin: 0 2px;
+  }
 }
 
 .login-box {

+ 7 - 11
src/layout/default-layout.vue

@@ -117,6 +117,7 @@
   import { useRoute, useRouter } from 'vue-router';
   import { useAppStore, useUserStore } from '@/store';
   import { ElMessageBox } from 'element-plus';
+  import { modalConfirm } from '@/utils/ui';
 
   import ResetPwdDialog from '@/views/login/components/ResetPwdDialog.vue';
   import UserInfoDialog from '@/views/login/components/UserInfoDialog.vue';
@@ -193,17 +194,12 @@
   }
 
   async function toLogout() {
-    try {
-      await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      });
-      userStore.logout();
-    } catch (error) {
-      // User clicked cancel or closed the dialog
-      console.log('Logout cancelled');
-    }
+    const confirm = await modalConfirm(`确定要退出登录吗?`, '提示').catch(
+      () => false
+    );
+    if (!confirm) return;
+
+    await userStore.logout();
   }
 
   onMounted(() => {

+ 8 - 1
src/router/constants.ts

@@ -4,4 +4,11 @@ export const NOT_FOUND = {
 
 export const DEFAULT_ROUTE_NAME = 'Workplace';
 
-export const WHITE_LIST = [NOT_FOUND, { name: 'Login' }, { name: 'CompTest' }];
+export const WHITE_LIST = [
+  NOT_FOUND,
+  { name: 'Login' },
+  { name: 'CompTest' },
+  { name: 'SwitchExam' },
+  { name: 'ResetInfo' },
+  { name: 'SelectMarkGroup' },
+];

+ 4 - 4
src/router/guard/index.ts

@@ -1,7 +1,7 @@
 import type { Router } from 'vue-router';
 import { setRouteEmitter } from '@/utils/route-listener';
-// import setupUserLoginInfoGuard from './userLoginInfo';
-// import setupPermissionGuard from './permission';
+import setupUserLoginInfoGuard from './userLoginInfo';
+import setupPermissionGuard from './permission';
 
 function setupPageGuard(router: Router) {
   router.beforeEach(async (to) => {
@@ -12,6 +12,6 @@ function setupPageGuard(router: Router) {
 
 export default function createRouteGuard(router: Router) {
   setupPageGuard(router);
-  // setupUserLoginInfoGuard(router);
-  // setupPermissionGuard(router);
+  setupUserLoginInfoGuard(router);
+  setupPermissionGuard(router);
 }

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

@@ -15,7 +15,8 @@ export default function setupUserLoginInfoGuard(router: Router) {
     const appStore = useAppStore();
     if (!appStore.appMenus.length) {
       const useStore = useUserStore();
-      await appStore.fetchServerMenu(useStore.role);
+      // await appStore.fetchServerMenu(useStore.role);
+      console.log(useStore.role);
     }
 
     if (to.name === DEFAULT_ROUTE_NAME) {

+ 1 - 1
src/router/guard/userLoginInfo.ts

@@ -11,7 +11,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
       next();
       return;
     }
-    if (userStore.token) {
+    if (userStore.accessToken) {
       next();
     } else {
       next({

+ 9 - 4
src/store/modules/app/index.ts

@@ -16,9 +16,9 @@ function getMenu(privilegeData: UserMenuItem[]): {
 
   // 创建节点映射
   privilegeData.forEach((item) => {
-    const node: AppMenuItem = { ...item, url: item.code, children: [] };
+    const node: AppMenuItem = { ...item, url: item.privilegeUri, children: [] };
     map.set(item.code, node);
-    validRoutes.push(item.code);
+    validRoutes.push(item.privilegeUri);
   });
 
   // 构建树形结构
@@ -37,8 +37,13 @@ function getMenu(privilegeData: UserMenuItem[]): {
 
 const useAppStore = defineStore('app', {
   state: (): AppState => ({
-    versionName: '',
-    versionDate: '',
+    // 系统信息
+    system: {
+      versionName: '',
+      versionDate: '',
+      fileServer: '',
+      indexLogo: '',
+    },
     appMenus: [],
     validRoutes: [],
     breadcrumbs: [],

+ 69 - 36
src/store/modules/app/menuData.ts

@@ -3,7 +3,7 @@ export const sysAdminMenus = [
     id: 1,
     name: '学校管理',
     code: 'SchoolManage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 1,
   },
@@ -11,7 +11,7 @@ export const sysAdminMenus = [
     id: 2,
     name: '配置管理',
     code: 'SetManage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
 
@@ -21,7 +21,7 @@ export const sysAdminMenus = [
     id: 3,
     name: '授权管理',
     code: 'AuthManage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 3,
   },
@@ -33,296 +33,329 @@ export const adminMenus = [
     name: '主页导览',
     code: 'HomeGuide',
     icon: 'icon-home',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 1,
+    privilegeUri: 'HomeGuide',
   },
   {
     id: 2,
     name: '用户管理',
     code: 'user',
     icon: 'icon-user-manage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'user',
   },
   {
     id: 201,
     name: '用户管理',
     code: 'UserManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'user',
     seq: 1,
+    privilegeUri: 'UserManage',
   },
   {
     id: 3,
     name: '考试管理',
     code: 'exam',
     icon: 'icon-exam-manage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'exam',
   },
   {
     id: 301,
     name: '考试管理',
     code: 'ExamManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'exam',
     seq: 1,
+    privilegeUri: 'ExamManage',
   },
   {
     id: 4,
     name: '考生管理',
     code: 'student',
     icon: 'icon-student-manage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'student',
   },
   {
     id: 401,
     name: '考生管理',
     code: 'StudentManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'student',
     seq: 1,
+    privilegeUri: 'StudentManage',
   },
   {
     id: 5,
     name: '扫描进度',
     code: 'scan',
     icon: 'icon-scan-progress',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'scan',
   },
   {
     id: 501,
     name: '扫描进度统计',
     code: 'ScanManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'scan',
     seq: 1,
+    privilegeUri: 'ScanManage',
   },
   {
     id: 6,
     name: '科目管理',
     code: 'subject',
     icon: 'icon-subject-manage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'subject',
   },
   {
     id: 601,
     name: '科目管理',
     code: 'SubjectManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'subject',
     seq: 1,
+    privilegeUri: 'SubjectManage',
   },
   {
     id: 7,
     name: '评卷管理',
     icon: 'icon-mark-manage',
     code: 'mark',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'mark',
   },
   {
     id: 701,
     name: '评卷进度',
     code: 'MarkProgress',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'mark',
     seq: 1,
+    privilegeUri: 'MarkProgress',
   },
   {
     id: 702,
     name: '科目评卷管理',
     icon: '',
     code: 'MarkManage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'mark',
     seq: 2,
+    privilegeUri: 'MarkManage',
   },
   {
     id: 8,
     name: '打回卷管理',
     code: 'reject',
     icon: 'icon-reject-manage',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'reject',
   },
   {
     id: 801,
     name: '打回卷',
     code: 'RejectManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'reject',
     seq: 1,
+    privilegeUri: 'RejectManage',
   },
   {
     id: 802,
     name: '打回统计',
     code: 'RejectStatistics',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'reject',
     seq: 2,
+    privilegeUri: 'RejectStatistics',
   },
   {
     id: 9,
     name: '问题卷管理',
     code: 'issue',
     icon: 'icon-issue-paper',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'issue',
   },
   {
     id: 901,
     name: '问题卷',
     code: 'IssuePaper',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'issue',
     seq: 1,
+    privilegeUri: 'IssuePaper',
   },
   {
     id: 10,
     name: '成绩复核',
     code: 'review',
     icon: 'icon-score-review',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'review',
   },
   {
     id: 1001,
     name: '复核进度统计',
     code: 'ScoreReviewStatistics',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'review',
     seq: 1,
+    privilegeUri: 'ScoreReviewStatistics',
   },
   {
     id: 1002,
     name: '全卷复核',
     code: 'AllReview',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'review',
     seq: 2,
+    privilegeUri: 'AllReview',
   },
   {
     id: 1003,
     name: '成绩校验',
     code: 'ScoreCheck',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'review',
     seq: 3,
+    privilegeUri: 'ScoreCheck',
   },
   {
     id: 11,
     name: '成绩查询',
     code: 'score',
     icon: 'icon-score-query',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'score',
   },
   {
     id: 1101,
     name: '成绩查询',
     code: 'ScoreQuery',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'score',
     seq: 1,
+    privilegeUri: 'ScoreQuery',
   },
   {
     id: 12,
     name: '科目分析',
     code: 'analysis',
     icon: 'icon-subject-analysis',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'analysis',
   },
   {
     id: 1201,
     name: '科目分析',
     code: 'AnalysisManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'analysis',
     seq: 1,
+    privilegeUri: 'AnalysisManage',
   },
   {
     id: 13,
     name: '数据检查',
     code: 'check',
     icon: 'icon-data-check',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'check',
   },
   {
     id: 1301,
     name: '人工确认',
     code: 'ManualConfirm',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'check',
     seq: 1,
+    privilegeUri: 'ManualConfirm',
   },
   {
     id: 1302,
     name: '识别结果检查',
     code: 'ResultCheck',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'check',
     seq: 2,
+    privilegeUri: 'ResultCheck',
   },
   {
     id: 1303,
     name: '图片检查',
     code: 'ImageCheck',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'check',
     seq: 3,
+    privilegeUri: 'ImageCheck',
   },
   {
     id: 14,
     name: '操作日志',
     code: 'log',
     icon: 'icon-log',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: '',
     seq: 2,
+    privilegeUri: 'log',
   },
   {
     id: 1401,
     name: '操作日志',
     code: 'LogManage',
     icon: '',
-    type: 'MENU',
+    privilegetype: 'MENU',
     parentCode: 'log',
     seq: 1,
+    privilegeUri: 'LogManage',
   },
 ];

+ 4 - 2
src/store/modules/app/types.ts

@@ -1,4 +1,5 @@
 import { ExamQueryItem } from '@/api/types/exam';
+import { SystemInfo } from '@/api/types/user';
 
 export type AppMenuItem = {
   name: string;
@@ -7,14 +8,15 @@ export type AppMenuItem = {
   icon: string;
   level: number;
   privilegeType: string;
+  privilegeUri: string;
   parentCode: string;
   seq: number;
   children: AppMenuItem[];
 };
 
 export interface AppState {
-  versionName: string;
-  versionDate: string;
+  // 系统信息
+  system: SystemInfo;
   appMenus: AppMenuItem[];
   validRoutes: string[];
   breadcrumbs: string[];

+ 19 - 12
src/store/modules/user/index.ts

@@ -5,17 +5,23 @@ import { UserState } from './types';
 
 const useUserStore = defineStore('user', {
   state: (): UserState => ({
-    id: null,
+    id: 0,
     account: '',
+    loginName: '',
     name: '',
-    role: null,
-    orgId: null,
-    categoryId: null,
-    applyTaskId: null,
-    openId: null,
-    sessionId: '',
-    token: '',
-    firstLogin: false,
+    empno: '',
+    source: '',
+    role: '',
+    roleName: '',
+    enable: true,
+    relatedAccount: '',
+    schoolId: 0,
+    lastLoginIp: '',
+    lastLoginTime: '',
+    description: '',
+    accessToken: '',
+    accessTokenRefreshTime: '',
+    schoolName: '',
   }),
 
   getters: {
@@ -35,9 +41,10 @@ const useUserStore = defineStore('user', {
       this.$reset();
     },
     // Logout
-    async logout() {
-      // TODO:如果token已经失效,不用调退出接口
-      await userLogout().catch(() => {});
+    async logout(authValid = true) {
+      if (authValid) {
+        await userLogout().catch(() => {});
+      }
       this.resetInfo();
       removeRouteListener();
 

+ 3 - 13
src/store/modules/user/types.ts

@@ -1,15 +1,5 @@
-import type { RoleType } from '@/constants/enumerate';
+import type { UserItem } from '@/api/types/user';
 
-export interface UserState {
-  id: number | null;
-  account: string;
-  name: string;
-  role: RoleType | null;
-  orgId: number | null;
-  categoryId: number | null;
-  applyTaskId: number | null;
-  openId: number | null;
-  sessionId: string;
-  token: string;
-  firstLogin: boolean;
+export interface UserState extends UserItem {
+  [key: string]: string;
 }

+ 1 - 0
src/utils/crypto.ts

@@ -38,6 +38,7 @@ interface AuthInfo {
  */
 export const getAuthorization = (infos: AuthInfo, type: AuthType): string => {
   // {type} {invoker}:base64(sha1(method&uri&timestamp&{secret}))
+  // console.log(infos);
 
   if (type === 'token') {
     // userId | method&uri&timestamp&token

+ 1 - 1
src/utils/ui.ts

@@ -7,7 +7,7 @@ export async function modalConfirm(
   return ElMessageBox.confirm(content, title, {
     confirmButtonText: '确定',
     cancelButtonText: '取消',
-    type: 'warning',
+    type: 'error',
     autofocus: false, // 根据需要调整
     closeOnClickModal: false, // maskClosable
     closeOnPressEscape: false, // escToClose

+ 42 - 8
src/views/HomeGuide.vue

@@ -22,11 +22,11 @@
               </div>
               <div class="menu-body">
                 <span>考生:</span>
-                <span class="number orange" style="margin-right: 15px"
-                  >124490</span
-                >
+                <span class="number orange" style="margin-right: 15px">{{
+                  guideInfo.studentCount
+                }}</span>
                 <span>科目:</span>
-                <span class="number orange">98</span>
+                <span class="number orange">{{ guideInfo.subjectCount }}</span>
               </div>
             </div>
             <div class="menu-item" @click="toPage('SubjectManage')">
@@ -149,7 +149,11 @@
               <div class="step-number">1</div>
               <div class="menu-item" @click="toPage('ManualConfirm')">
                 <span>数据检查 - 人工确认</span>
-                <el-icon class="status-icon success"><SuccessFilled /></el-icon>
+                <el-icon
+                  v-if="guideInfo.checkStudent"
+                  class="status-icon success"
+                  ><SuccessFilled
+                /></el-icon>
                 <div class="arrow-right"></div>
               </div>
             </div>
@@ -157,7 +161,11 @@
               <div class="step-number">2</div>
               <div class="menu-item" @click="toPage('MarkProgress')">
                 <span>检查科目进度100%且关闭</span>
-                <el-icon class="status-icon success"><SuccessFilled /></el-icon>
+                <el-icon
+                  v-if="guideInfo.checkSubject"
+                  class="status-icon success"
+                  ><SuccessFilled
+                /></el-icon>
                 <div class="arrow-right"></div>
               </div>
             </div>
@@ -165,7 +173,11 @@
               <div class="step-number">3</div>
               <div class="menu-item" @click="toPage('StudentManage')">
                 <span>缺考考生导入</span>
-                <el-icon class="status-icon success"><SuccessFilled /></el-icon>
+                <el-icon
+                  v-if="guideInfo.checkAbsent"
+                  class="status-icon success"
+                  ><SuccessFilled
+                /></el-icon>
                 <div class="arrow-right"></div>
               </div>
             </div>
@@ -201,19 +213,41 @@
 </template>
 
 <script setup lang="ts">
-  import { defineOptions } from 'vue';
+  import { defineOptions, reactive } from 'vue';
   import { SuccessFilled } from '@element-plus/icons-vue';
   import { useRouter } from 'vue-router';
+  import { getExamGuideInfo } from '@/api/exam';
+  import { ExamGuideInfo } from '@/api/types/exam';
 
   defineOptions({
     name: 'HomeGuide',
   });
 
   const router = useRouter();
+  const guideInfo = reactive<ExamGuideInfo>({
+    checkAbsent: false,
+    checkStudent: false,
+    checkSubject: false,
+    studentCount: 0,
+    subjectCount: 0,
+  });
 
   function toPage(val: string) {
     router.push({
       name: val,
     });
   }
+
+  async function getGuideInfo() {
+    const res = await getExamGuideInfo();
+    if (res) {
+      guideInfo.checkAbsent = res.checkAbsent;
+      guideInfo.checkStudent = res.checkStudent;
+      guideInfo.checkSubject = res.checkSubject;
+      guideInfo.studentCount = res.studentCount;
+      guideInfo.subjectCount = res.subjectCount;
+    }
+  }
+
+  getGuideInfo();
 </script>

+ 10 - 48
src/views/admin/set-manage/SetManage.vue

@@ -1,8 +1,8 @@
 <template>
   <div class="part-box">
     <el-table class="page-table" :data="tableData" border stripe>
-      <el-table-column prop="type" label="类型" width="180" />
-      <el-table-column prop="description" label="详情" />
+      <el-table-column prop="name" label="类型" width="180" />
+      <el-table-column prop="descriptionText" label="详情" />
       <el-table-column label="操作" width="100">
         <template #default="scope">
           <el-button type="primary" link @click="handleEdit(scope.row)"
@@ -23,7 +23,7 @@
   >
     <el-form ref="formRef" :model="formModel" :rules="rules" label-width="80px">
       <el-form-item label="类型">
-        <el-input v-model="formModel.type" readonly disabled />
+        <el-input v-model="formModel.name" readonly disabled />
       </el-form-item>
       <el-form-item label="详情" prop="description">
         <el-input
@@ -54,7 +54,7 @@
   import { SettingItem } from '@/api/types/admin';
 
   // 假设有API获取和更新配置
-  // import { getSystemSettings, updateSystemSetting } from '@/api/admin';
+  import { getSystemSettings, updateSystemSetting } from '@/api/admin';
 
   defineOptions({
     name: 'SetManage',
@@ -67,16 +67,16 @@
 
   const initialFormState: Partial<SettingItem> = {
     id: '',
-    type: '',
+    name: '',
+    type: 'FILE_SERVER',
     description: '',
-    key: '',
   };
   const formModel = reactive({ ...initialFormState });
 
   const isEnableDetail = computed(() => {
     return (
-      formModel.key === 'enable_marking_process' ||
-      formModel.key === 'calculate_scanned_cards'
+      formModel.type === 'AUTO_REPORT' ||
+      formModel.type === 'STUDENT_SHEET_COUNT'
     );
   });
 
@@ -84,44 +84,9 @@
     description: [{ required: true, message: '请输入详情', trigger: 'blur' }],
   });
 
-  // 模拟API调用获取数据
-  const mockFetchSettings = async () => {
-    return new Promise<SettingItem[]>((resolve) => {
-      setTimeout(() => {
-        resolve([
-          {
-            id: 1,
-            type: '图片服务',
-            description: 'http://192.168.10.83:9004/',
-            key: 'image_service_url',
-          },
-          {
-            id: 2,
-            type: '评阅时长',
-            description: '60',
-            key: 'review_duration_minutes',
-          },
-          {
-            id: 3,
-            type: '启动阅卷',
-            description: false,
-            key: 'enable_marking_process',
-          },
-          {
-            id: 4,
-            type: '计算扫描卡张数',
-            description: false,
-            key: 'calculate_scanned_cards',
-          },
-        ]);
-      }, 500);
-    });
-  };
-
   const loadSettings = async () => {
     try {
-      // const res = await getSystemSettings(); // 实际API调用
-      const res = await mockFetchSettings(); // 使用模拟数据
+      const res = await getSystemSettings(); // 实际API调用
       tableData.value = res;
     } catch (error) {
       console.error('获取配置失败:', error);
@@ -149,15 +114,12 @@
       const valid = await formRef.value?.validate().catch(() => false);
       if (!valid) return;
       loading.value = true;
-      // const paramsToUpdate = { key: formModel.key, value: formModel.description, id: formModel.id };
-      // await updateSystemSetting(paramsToUpdate); // 实际API调用
-      console.log('保存的配置:', formModel);
+      await updateSystemSetting(formModel);
       ElMessage.success('保存成功');
       dialogVisible.value = false;
       await loadSettings(); // 重新加载数据
     } catch (error) {
       console.error('保存失败:', error);
-      // ElMessage.error('保存失败');
     } finally {
       loading.value = false;
     }

+ 16 - 11
src/views/login/ResetInfo.vue

@@ -68,10 +68,12 @@
   import { useRouter } from 'vue-router';
   import type { FormInstance, FormRules } from 'element-plus';
   import useLoading from '@/hooks/loading';
-  // import { updateUserProfile } from '@/api/user'; // 假设有更新用户信息的API
+  import { updateFirstUserInfo } from '@/api/user';
   import { ElMessage } from 'element-plus';
+  import { useUserStore } from '@/store';
 
   const router = useRouter();
+  const userStore = useUserStore();
   const formRef = ref<FormInstance>();
   const { loading, setLoading } = useLoading();
 
@@ -134,22 +136,25 @@
 
     setLoading(true);
     try {
-      // const res = await updateUserProfile(formData); // 调用API更新用户信息
-      // if (res) {
-      ElMessage.success('资料完善成功,请重新登录');
-      // 清空store信息,跳转到登录页
-      // userStore.resetInfo();
-      // appStore.resetInfo();
-      router.push({ name: 'Login' }); // 假设登录页路由名称为 Login
-      // }
+      const res = await updateFirstUserInfo(formData);
+      if (res) {
+        ElMessage.success('资料完善成功,请重新登录');
+        // 清空store信息,跳转到登录页
+        userStore.setInfo({
+          name: formData.name,
+          empno: formData.empno,
+        });
+
+        router.push({ name: 'Login' });
+      }
     } catch (e) {
-      // console.error(e);
+      console.error(e);
     } finally {
       setLoading(false);
     }
   }
 
   function goBack() {
-    router.push({ name: 'Login' }); // 假设登录页路由名称为 Login
+    router.push({ name: 'Login' });
   }
 </script>

+ 1 - 1
src/views/login/SelectMarkGroup.vue

@@ -160,7 +160,7 @@
 
     // 保存选择的信息到
 
-    // 提交成功后的操作,例如跳转到评卷页面
+    // TODO:提交成功后的操作,例如跳转到评卷页面
     // router.push({ name: 'MarkPage' });
   };
 

+ 18 - 10
src/views/login/SwitchExam.vue

@@ -20,10 +20,18 @@
       </el-form-item>
 
       <el-form-item>
-        <el-button class="submit-btn" type="primary" @click="handleSubmit"
+        <el-button
+          class="submit-btn"
+          type="primary"
+          style="width: 100%"
+          @click="submit"
           >确认</el-button
         >
-        <el-button @click="handleGoBack">退出</el-button>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" style="width: 100%" link @click="goBack"
+          >退出</el-button
+        >
       </el-form-item>
     </el-form>
   </div>
@@ -35,6 +43,7 @@
   import type { FormInstance, FormRules } from 'element-plus';
   import { useAppStore } from '@/store';
   import { ExamItem } from '@/api/types/exam';
+  import { DEFAULT_ROUTE_NAME } from '@/router/constants';
 
   const appStore = useAppStore();
   const router = useRouter();
@@ -56,24 +65,23 @@
   const selectExam = ref({} as ExamItem);
 
   const onExamChange = (val: ExamItem) => {
-    console.log('选择的考试ID:', val);
     selectExam.value = val || ({} as ExamItem);
   };
 
   // 确认按钮点击事件
-  const handleSubmit = async () => {
+  const submit = async () => {
     const valid = await formRef.value?.validate().catch(() => false);
     if (!valid) return;
 
-    console.log('选择的考试ID:', formData.examId);
     appStore.setInfo({ curExam: selectExam.value });
-    // 提交成功后的操作,例如跳转到其他页面
+
+    router.push({
+      name: DEFAULT_ROUTE_NAME,
+    });
   };
 
   // 回退按钮点击事件
-  const handleGoBack = () => {
-    // 假设登录页面的路由名称是 'Login' 或路径是 '/login'
-    // 你可能需要根据项目的实际路由配置进行调整
-    router.push({ name: 'Login' }); // 或者 router.back(); 如果是从登录页跳转过来的
+  const goBack = () => {
+    router.push({ name: 'Login' });
   };
 </script>

+ 8 - 5
src/views/login/home.vue

@@ -16,8 +16,12 @@
       <a href="http://www.qmth.com.cn" target="_blank">
         Copyright © 2024 启明泰和
       </a>
-      <span v-if="appStore.versionName"> {{ appStore.versionName }} </span>
-      <span v-if="appStore.versionDate"> {{ appStore.versionDate }} </span>
+      <span v-if="appStore.system.versionName">
+        {{ appStore.system.versionName }}
+      </span>
+      <span v-if="appStore.system.versionDate">
+        {{ appStore.system.versionDate }}
+      </span>
       <a href="https://beian.miit.gov.cn/" target="_blank">
         鄂ICP备12000033号-3</a
       >
@@ -37,10 +41,9 @@
   const appStore = useAppStore();
 
   async function getVersion() {
-    const version = await systemVersion();
+    const systemInfo = await systemVersion();
     appStore.setInfo({
-      versionName: version.versionName,
-      versionDate: version.versionDate,
+      system: systemInfo,
     });
   }
 

+ 31 - 9
src/views/login/login.vue

@@ -40,14 +40,6 @@
           >登录</el-button
         >
       </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="router.push({ name: 'SwitchExam' })"
-          >选择考试</el-button
-        >
-        <el-button type="primary" @click="router.push({ name: 'ResetInfo' })"
-          >个人信息</el-button
-        >
-      </el-form-item>
     </el-form>
   </div>
 </template>
@@ -101,13 +93,43 @@
     userStore.setInfo(data);
 
     // 首次登录强制修个人信息
-    if (data.firstLogin) {
+    const needResetInfoRoles = [
+      'SCANNER',
+      'SUBJECT_HEADER',
+      'INSPECTOR',
+      'MARKER',
+      'SCHOOL_VIEWER',
+      'COLLEGE_ADMIN',
+      'SCAN_ADMIN',
+    ];
+    if (!data.lastLoginTime && needResetInfoRoles.includes(data.role)) {
       router.push({
         name: 'ResetInfo',
       });
       return;
     }
 
+    if (data.role === 'MARKER') {
+      router.push({
+        name: 'SelectMarkGroup',
+      });
+      return;
+    }
+
+    const selectExamRoles = [
+      'SUBJECT_HEADER',
+      'INSPECTOR',
+      'SCHOOL_ADMIN',
+      'SCHOOL_VIEWER',
+      'COLLEGE_ADMIN',
+    ];
+    if (selectExamRoles.includes(data.role)) {
+      router.push({
+        name: 'SwitchExam',
+      });
+      return;
+    }
+
     router.push({
       name: DEFAULT_ROUTE_NAME,
     });