Bläddra i källkod

feat: 评卷进度

zhangjie 2 dagar sedan
förälder
incheckning
7dc1b37485

+ 8 - 8
src/api/analysis.ts

@@ -20,54 +20,54 @@ import {
 export function getTotalAnalysisList(
   params: AnalysisListPageParam
 ): Promise<TotalAnalysisListRes> {
-  return axios.post('/api/analysis/total', { params });
+  return axios.post('/api/analysis/total', {}, { params });
 }
 
 // 分段统计列表
 export function getSegmentAnalysisList(
   params: ScoreSegmentListParam
 ): Promise<ScoreSegmentListRes> {
-  return axios.post('/api/analysis/segment', { params });
+  return axios.post('/api/analysis/segment', {}, { params });
 }
 
 // 学院分析列表
 export function getCollegeAnalysisList(
   params: CollegeAnalysisListParam
 ): Promise<CollegeAnalysisListRes> {
-  return axios.post('/api/analysis/college', { params });
+  return axios.post('/api/analysis/college', {}, { params });
 }
 
 // 任课老师分析列表
 export function getTeacherAnalysisList(
   params: TeacherAnalysisListParam
 ): Promise<TeacherAnalysisListRes> {
-  return axios.post('/api/analysis/teacher', { params });
+  return axios.post('/api/analysis/teacher', {}, { params });
 }
 
 // 班级分析列表
 export function getClassAnalysisList(
   params: ClassAnalysisListParam
 ): Promise<ClassAnalysisListRes> {
-  return axios.post('/api/analysis/class', { params });
+  return axios.post('/api/analysis/class', {}, { params });
 }
 
 // 客观题分析列表
 export function getObjectiveQuestionAnalysisList(
   params: QuestionAnalysisListParam
 ): Promise<QuestionAnalysisListRes> {
-  return axios.post('/api/analysis/question', { params });
+  return axios.post('/api/analysis/question', {}, { params });
 }
 
 // 主观题分析列表
 export function getSubjectiveQuestionAnalysisList(
   params: QuestionAnalysisListParam
 ): Promise<QuestionAnalysisListRes> {
-  return axios.post('/api/analysis/question', { params });
+  return axios.post('/api/analysis/question', {}, { params });
 }
 
 // 大题分析列表
 export function getBigQuestionAnalysisList(
   params: AnalysisListPageParam
 ): Promise<BigQuestionAnalysisListRes> {
-  return axios.post('/api/analysis/big-question', { params });
+  return axios.post('/api/analysis/big-question', {}, { params });
 }

+ 3 - 3
src/api/check.ts

@@ -11,19 +11,19 @@ import {
 export function getManualConfirmDataList(
   params: ManualConfirmDataListFilter
 ): Promise<ManualConfirmPageRes> {
-  return axios.post('/api/manualConfirmDataList', { params });
+  return axios.post('/api/manualConfirmDataList', {}, { params });
 }
 
 // 图片检查查询
 export function getImageCheckDataList(
   params: ImageCheckDataListFilter
 ): Promise<ImageCheckPageRes> {
-  return axios.post('/api/imageCheckDataList', { params });
+  return axios.post('/api/imageCheckDataList', {}, { params });
 }
 
 // 识别结果查询
 export function getResultCheckDataList(
   params: ResultCheckDataListFilter
 ): Promise<number[]> {
-  return axios.post('/api/recognitionResultDataList', { params });
+  return axios.post('/api/recognitionResultDataList', {}, { params });
 }

+ 2 - 2
src/api/exam.ts

@@ -11,11 +11,11 @@ import {
 export function getExamList(
   params: ExamListPageParam
 ): Promise<ExamListPageRes> {
-  return axios.post('/api/exam/list', { params });
+  return axios.post('/api/exam/list', {}, { params });
 }
 // 考试详情
 export function getExamDetail(examId: number): Promise<ExamItem> {
-  return axios.post('/api/exam/detail', { examId });
+  return axios.post('/api/exam/detail', {}, { examId });
 }
 
 // 新增或编辑考试

+ 1 - 1
src/api/issue-paper.ts

@@ -10,7 +10,7 @@ import {
 export function getIssuePaperList(
   params: IssuePaperListPageParam
 ): Promise<IssuePaperListPageRes> {
-  return axios.post('/api/score/list', { params });
+  return axios.post('/api/score/list', {}, { params });
 }
 
 // 重置问题卷

+ 1 - 1
src/api/log.ts

@@ -3,5 +3,5 @@ import { LogListPageParam, LogListPageRes } from './types/log';
 
 // 日志管理列表
 export function getLogList(params: LogListPageParam): Promise<LogListPageRes> {
-  return axios.post('/api/log/list', { params });
+  return axios.post('/api/log/list', {}, { params });
 }

+ 183 - 0
src/api/mark.ts

@@ -0,0 +1,183 @@
+import axios from 'axios';
+import {
+  MarkQualityMonitorListFilter,
+  MarkQualityMonitorListPageParam,
+  MarkQualityMonitorListPageRes,
+  QMScoreItem,
+  MarkArbitrationListPageRes,
+  MarkArbitrationListPageParam,
+  MarkTaskListPageRes,
+  MarkTaskListPageParam,
+  MarkTrialListPageRes,
+  MarkTrialListPageParam,
+  MarkMarkerListPageParam,
+  MarkMarkerListPageRes,
+  MarkGroupListPageParam,
+  MarkGroupListPageRes,
+  MarkGroupUpdateParams,
+  MarkStatInfo,
+  MarkStatListPageParam,
+  MarkStatListPageRes,
+} from './types/mark';
+
+// 质量监控
+// 获取质量监控列表
+export function getQualityMonitorList(
+  params: MarkQualityMonitorListPageParam
+): Promise<MarkQualityMonitorListPageRes> {
+  return axios.post('/api/mark/quality/monitor/list', {}, { params });
+}
+// 重新计算
+export function qualityMonitorCalculate(
+  params: MarkQualityMonitorListFilter
+): Promise<boolean> {
+  return axios.post('/api/mark/re-calculate', {}, { params });
+}
+// 给分曲线
+export function qualityMonitorScoreList(
+  params: MarkQualityMonitorListFilter
+): Promise<QMScoreItem[]> {
+  return axios.post('/api/mark/re-calculate', {}, { params });
+}
+
+// 仲裁管理
+// 获取仲裁列表
+export function getArbitrationList(
+  params: MarkArbitrationListPageParam
+): Promise<MarkArbitrationListPageRes> {
+  return axios.post('/api/mark/arbitration/list', {}, { params });
+}
+
+// 任务管理
+// 获取任务列表
+export function getMarkTaskList(
+  params: MarkTaskListPageParam
+): Promise<MarkTaskListPageRes> {
+  return axios.post('/api/mark/task/list', {}, { params });
+}
+
+// 任务打回
+export function markTaskReject(id: number): Promise<boolean> {
+  return axios.post('/api/mark/task/reject', {}, { params: { id } });
+}
+
+// 试评管理
+// 获取试评列表
+export function getMarkTrialList(
+  params: MarkTrialListPageParam
+): Promise<MarkTrialListPageRes> {
+  return axios.post('/api/mark/task/list', {}, { params });
+}
+
+// 评卷员管理
+// 获取评卷员列表
+export function getMarkMarkerList(
+  params: MarkMarkerListPageParam
+): Promise<MarkMarkerListPageRes> {
+  return axios.post('/api/mark/marker/list', {}, { params });
+}
+
+// 启用、禁用评卷员
+export function markMarkerEnable({
+  ids,
+  enable,
+}: {
+  ids: number[];
+  enable: boolean;
+}): Promise<boolean> {
+  return axios.post('/api/mark/marker/enable', {}, { params: { ids, enable } });
+}
+// 解绑评卷员
+export function markMarkerUnbind(id: number) {
+  return axios.post('/api/mark/marker/unbind', {}, { params: { id } });
+}
+
+// 回收任务
+export function markMarkerTaskRecycle(ids: number[]) {
+  return axios.post('/api/mark/task/recycle', {}, { params: { ids } });
+}
+
+// 设置评卷数量
+export function markMarkerSetTaskCount({
+  ids,
+  count,
+}: {
+  ids: number[];
+  count: number;
+}) {
+  return axios.post('/api/mark/task/set-count', {}, { params: { ids, count } });
+}
+
+// 重置密码
+export function markMarkerResetPassword(ids: number[]) {
+  return axios.post('/api/mark/task/reset-password', {}, { params: { ids } });
+}
+// 重置已评数量
+export function markMarkerResetFinishCount(id: number) {
+  return axios.post(
+    '/api/mark/task/reset-finish-count',
+    {},
+    { params: { id } }
+  );
+}
+
+// 分组管理
+// 获取分组列表
+export function getMarkGroupList(
+  params: MarkGroupListPageParam
+): Promise<MarkGroupListPageRes> {
+  return axios.post('/api/mark/group/list', {}, { params });
+}
+// 结束分组
+export function markGroupEnd(id: number): Promise<boolean> {
+  return axios.post('/api/mark/group/end', {}, { params: { id } });
+}
+
+// 修改分组
+export function markGroupUpdate(
+  params: MarkGroupUpdateParams
+): Promise<boolean> {
+  return axios.post('/api/mark/group/update', params);
+}
+
+// 设置试评数量
+export function markGroupSetTrialCount({
+  subjectId,
+  count,
+}: {
+  subjectId: number;
+  count: number;
+}): Promise<boolean> {
+  return axios.post(
+    '/api/mark/group/set-trial-count',
+    {},
+    { params: { subjectId, count } }
+  );
+}
+
+// 数据校对
+export function markGroupDataCheck(subjectId: number) {
+  return axios.post(
+    '/api/mark/group/data-check',
+    {},
+    { params: { subjectId } }
+  );
+}
+
+// 关闭分组
+export function markGroupClose(ids: number) {
+  return axios.post('/api/mark/group/close', {}, { params: { ids } });
+}
+
+// 评卷进度统计
+// 获取评卷进度统计信息
+export function getMarkStatInfo(): Promise<MarkStatInfo> {
+  return axios.post('/api/mark/stat/info');
+}
+
+// 获取评卷进度统计列表
+export function getMarkStatList(
+  params: MarkStatListPageParam
+): Promise<MarkStatListPageRes> {
+  return axios.post('/api/mark/stat/list', {}, { params });
+}

+ 1 - 1
src/api/order.ts

@@ -49,7 +49,7 @@ export function cityQuery(): Promise<OptionItem[]> {
 export function taskListPage(
   params: TaskListPageParam
 ): Promise<TaskListPageRes> {
-  return axios.post('/api/admin/apply/task/page', params);
+  return axios.post('/api/admin/apply/task/page', {}, params);
 }
 // 预约任务管理-详情
 export function taskDetailInfo(id: number): Promise<TaskItemDetail> {

+ 4 - 4
src/api/reject.ts

@@ -12,20 +12,20 @@ import {
 export function getRejectList(
   params: RejectListPageParams
 ): Promise<RejectListPageRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }
 // 获取打回记录列表
 export function getRejectRecordList(
   params: RejectListPageParams
 ): Promise<RejectListPageRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }
 
 // 获取打回统计列表
 export function getRejectStatisticsList(
   params: RejectStatisticsListParams
 ): Promise<RejectStatisticsListRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }
 
 // 查询打回类型
@@ -34,7 +34,7 @@ export function getRejectTypeList(examId: number): Promise<RejectTypeItem[]> {
 }
 // 新增/修改打回类型
 export function updateRejectType(params: RejectTypeUpdateParam): Promise<any> {
-  return axios.post('/api/student/list', params);
+  return axios.post('/api/student/list', {}, params);
 }
 // 删除打回类型
 export function deleteRejectType(id: number): Promise<any> {

+ 2 - 2
src/api/review.ts

@@ -18,14 +18,14 @@ export function getReviewStatInfo(): Promise<ReviewStatInfo> {
 export function getReviewStatList(
   params: ReviewStatListPageParams
 ): Promise<ReviewStatListPageRes> {
-  return axios.post('/api/review/stat/list', { params });
+  return axios.post('/api/review/stat/list', {}, { params });
 }
 
 // 获取全卷复核列表信息
 export function getFullReviewList(
   params: FullReviewListPageParams
 ): Promise<FullReviewListPageRes> {
-  return axios.post('/api/review/full/list', { params });
+  return axios.post('/api/review/full/list', {}, { params });
 }
 
 // 获取成绩校验列表信息

+ 3 - 3
src/api/scan.ts

@@ -12,19 +12,19 @@ import {
 export function getScanCourseList(
   params: ScanCousreListPageParam
 ): Promise<ScanListPageRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }
 
 // 按考点统计列表
 export function getScanPointList(
   params: ScanPointListPageParam
 ): Promise<ScanListPageRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }
 
 // 签到表统计列表
 export function getSignPaperStatList(
   params: SignPaperStatListPageParam
 ): Promise<SignPaperStatListPageRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }

+ 1 - 1
src/api/score.ts

@@ -5,5 +5,5 @@ import { ScoreListPageParam, ScoreListPageRes } from './types/score';
 export function getScoreList(
   params: ScoreListPageParam
 ): Promise<ScoreListPageRes> {
-  return axios.post('/api/score/list', { params });
+  return axios.post('/api/score/list', {}, { params });
 }

+ 1 - 1
src/api/student.ts

@@ -11,7 +11,7 @@ import {
 export function getStudentList(
   params: StudentListPageParam
 ): Promise<StudentListPageRes> {
-  return axios.post('/api/student/list', { params });
+  return axios.post('/api/student/list', {}, { params });
 }
 // 新增或编辑考生
 export function updateStudent(data: StudentUpdateParam): Promise<StudentItem> {

+ 38 - 18
src/api/subject.ts

@@ -25,11 +25,11 @@ export function getSubjectTotalScoreStatList(
 export function getSubjectList(
   params: SubjectListPageParam
 ): Promise<SubjectListPageRes> {
-  return axios.post('/api/subject/list', { params });
+  return axios.post('/api/subject/list', {}, { params });
 }
 // 获取科目详情
 export function getSubjectDetail(subjectId: number): Promise<SubjectItem> {
-  return axios.post('/api/subject/list', { params: { subjectId } });
+  return axios.post('/api/subject/list', {}, { params: { subjectId } });
 }
 
 // 科目分析计算
@@ -42,9 +42,13 @@ export function subjectAnalysis(
 
 // 客观题统分
 export function subjectObjectiveMarkScore(examId: number): Promise<boolean> {
-  return axios.post('/api/subject/objective-stat', {
-    params: { examId },
-  });
+  return axios.post(
+    '/api/subject/objective-stat',
+    {},
+    {
+      params: { examId },
+    }
+  );
 }
 
 // 获取科目试卷结构列表
@@ -58,7 +62,7 @@ export function getPaperStructureList(
 export function getSubjectSetting(
   subjectId: number
 ): Promise<SubjectSettingInfo> {
-  return axios.post('/api/subject/setting', { params: { subjectId } });
+  return axios.post('/api/subject/setting', {}, { params: { subjectId } });
 }
 // 保存科目设置信息
 export function saveSubjectSetting(
@@ -76,9 +80,13 @@ export function savePaperStructure(
 
 // 批量删除试卷结构
 export function deletePaperStructures(ids: number[]): Promise<boolean> {
-  return axios.post('/api/subject/paper-structure/batch', {
-    data: { ids },
-  });
+  return axios.post(
+    '/api/subject/paper-structure/batch',
+    {},
+    {
+      params: { ids },
+    }
+  );
 }
 
 // 选做题设置
@@ -86,18 +94,26 @@ export function deletePaperStructures(ids: number[]): Promise<boolean> {
 export function getOptionalQuestionList(
   subjectId: number
 ): Promise<OptionalQuestionItem[]> {
-  return axios.post('/api/subject/optional-question/list', {
-    params: { subjectId },
-  });
+  return axios.post(
+    '/api/subject/optional-question/list',
+    {},
+    {
+      params: { subjectId },
+    }
+  );
 }
 
 // 获取选做题规则列表
 export function getOptionalQuestionRuleList(
   subjectId: number
 ): Promise<OptionalRuleItem[]> {
-  return axios.post('/api/subject/optional-question/rule/list', {
-    params: { subjectId },
-  });
+  return axios.post(
+    '/api/subject/optional-question/rule/list',
+    {},
+    {
+      params: { subjectId },
+    }
+  );
 }
 
 // 保存选做题设置
@@ -109,7 +125,11 @@ export function saveOptionalQuestionRule(
 
 // 删除选做题规则
 export function deleteOptionalQuestionRule(id: number): Promise<boolean> {
-  return axios.post('/api/subject/optional-question/rule/delete', {
-    params: { id },
-  });
+  return axios.post(
+    '/api/subject/optional-question/rule/delete',
+    {},
+    {
+      params: { id },
+    }
+  );
 }

+ 309 - 0
src/api/types/mark.ts

@@ -0,0 +1,309 @@
+import { PageResult, PageParams, CoverArea } from './common';
+
+// 质量监控
+// 质量监控列表:分组	评卷员	姓名	完成任务数	仲裁任务数	仲裁率	打回次数	评卷采用率	评卷速度(秒)	平均分	标准差
+export interface MarkQualityMonitorItem {
+  // 分组
+  group: string;
+  // 评卷员ID
+  markerId: string;
+  // 评卷员姓名
+  name: string;
+  // 完成任务数
+  completedTasks: number;
+  // 仲裁任务数
+  arbitrationTasks: number;
+  // 仲裁率
+  arbitrationRate: number;
+  // 打回次数
+  returnCount: number;
+  // 评卷采用率
+  adoptionRate: number;
+  // 评卷速度(秒)
+  markingSpeed: number;
+  // 平均分
+  averageScore: number;
+  // 标准差
+  standardDeviation: number;
+}
+export type MarkQualityMonitorListPageRes = PageResult<MarkQualityMonitorItem>;
+
+export interface MarkQualityMonitorListFilter {
+  // 科目
+  subject?: number | null;
+  // 分组
+  group?: string;
+  // 已评卷
+  marked?: boolean;
+  // 不含仲裁
+  noArbitration?: boolean;
+}
+export type MarkQualityMonitorListPageParam =
+  PageParams<MarkQualityMonitorListFilter>;
+
+// 给分曲线列表: 评卷员	姓名 给分
+export interface QMScoreItem {
+  // 评卷员
+  marker: string;
+  // 姓名
+  name: string;
+  // 给分
+  scores: Array<{ score: number; count: number }>;
+}
+
+// 仲裁管理
+// 仲裁管理列表:科目代码	分组序号	准考证号	状态	创建时间	处理时间	处理人
+export interface MarkArbitrationItem {
+  id: number;
+  // 科目代码
+  subjectCode: string;
+  // 分组序号
+  groupNo: number;
+  // 准考证号
+  examCardNo: string;
+  // 状态
+  status: string;
+  // 创建时间
+  createTime: string;
+  // 处理时间
+  handleTime: string;
+  // 处理人
+  handler: string;
+}
+export type MarkArbitrationListPageRes = PageResult<MarkArbitrationItem>;
+export interface MarkArbitrationListFilter {
+  // 科目
+  subject?: number | null;
+  // 分组
+  group?: string;
+  // 状态
+  status?: string;
+  // 选做题
+  optional?: boolean;
+  // 准考证号
+  examCardNo?: string;
+}
+export type MarkArbitrationListPageParam =
+  PageParams<MarkArbitrationListFilter>;
+
+// 任务管理
+// 任务管理列表:科目代码	分组序号	准考证号	密号	状态	打回原因	评卷员	评卷总分	给分明细	评卷时间	复核人	复核时间
+export interface MarkTaskItem {
+  // 任务ID
+  id: number;
+  // 科目代码
+  subjectCode: string;
+  // 分组序号
+  groupNo: number;
+  // 准考证号
+  examCardNo: string;
+  // 密号
+  secretNo: string;
+  // 状态
+  status: string;
+  // 打回原因
+  returnReason: string;
+  // 评卷员
+  marker: string;
+  // 评卷总分
+  totalScore: number;
+  // 给分明细
+  giveScoreDetail: string;
+  // 评卷时间
+  markingTime: string;
+  // 复核人
+  reviewer: string;
+  // 复核时间
+  reviewTime: string;
+}
+export type MarkTaskListPageRes = PageResult<MarkTaskItem>;
+export interface MarkTaskListFilter {
+  // 科目
+  subject?: number | null;
+  // 分组
+  group?: string;
+  // 状态
+  status?: string;
+  // 评卷员
+  markerId?: number;
+  // 选做题
+  optional?: boolean;
+  // 准考证号
+  examCardNo?: string;
+  // 密号
+  secretNo?: string;
+  // 总分
+  totalStartScore?: number;
+  totalEndScore?: number;
+  // 小题得分
+  questionScore?: number;
+}
+export type MarkTaskListPageParam = PageParams<MarkTaskListFilter>;
+
+// 试评管理
+// 试评管理列表:科目代码	分组序号	准考证号	密号	评卷员	评卷总分	给分明细	评卷时间
+export interface MarkTrialItem {
+  // 任务ID
+  id: number;
+  // 科目代码
+  subjectCode: string;
+  // 分组序号
+  groupNo: number;
+  // 准考证号
+  examCardNo: string;
+  // 密号
+  secretNo: string;
+  // 评卷员
+  marker: string;
+  // 评卷总分
+  totalScore: number;
+  // 给分明细
+  giveScoreDetail: string;
+  // 评卷时间
+  markingTime: string;
+}
+export type MarkTrialListPageRes = PageResult<MarkTrialItem>;
+export interface MarkTrialListFilter {
+  // 科目
+  subject?: number | null;
+  // 分组
+  group?: string;
+  // 准考证号
+  examCardNo?: string;
+  // 密号
+  secretNo?: string;
+}
+export type MarkTrialListPageParam = PageParams<MarkTrialListFilter>;
+
+// 评卷员管理
+// 评卷员管理列表:登录名	姓名	科目	分组	状态	已评数量	正在评卷	任务数	绑定班级
+export interface MarkMarkerItem {
+  // 登录名
+  loginName: string;
+  // 姓名
+  name: string;
+  // 科目
+  subject: string;
+  // 分组
+  group: string;
+  // 状态
+  status: string;
+  // 已评数量
+  completedCount: number;
+  // 正在评卷
+  marking: boolean;
+  // 任务数
+  taskCount: number;
+  // 绑定班级
+  bindClass: string;
+}
+export type MarkMarkerListPageRes = PageResult<MarkMarkerItem>;
+export interface MarkMarkerListFilter {
+  // 登录名
+  loginName?: string;
+  // 姓名
+  name?: string;
+  // 科目
+  subject?: number | null;
+  // 分组
+  group?: string;
+}
+export type MarkMarkerListPageParam = PageParams<MarkMarkerListFilter>;
+
+// 分组管理
+// 分组管理列表:分组序号	大题号	大题名称	步骤分	包含选做题	评卷员人数	任务总数	完成总数	剩余总数	正在评卷	进度	评卷区设置	状态
+export interface MarkGroupItem {
+  // 分组序号
+  groupNo: number;
+  // 大题号
+  questionNo: string;
+  // 大题名称
+  questionName: string;
+  // 步骤分
+  stepScore: number;
+  // 包含选做题
+  optional: boolean;
+  // 评卷员人数
+  markerCount: number;
+  // 任务总数
+  totalTaskCount: number;
+  // 完成总数
+  completedTaskCount: number;
+  // 剩余总数
+  remainingTaskCount: number;
+  // 正在评卷
+  marking: boolean;
+  // 进度
+  progress: number;
+  // 评卷区设置
+  markingAreaSetting: string;
+  // 状态
+  status: string;
+}
+export type MarkGroupListPageRes = MarkGroupItem[];
+export interface MarkGroupListFilter {
+  // 科目
+  subject?: number | null;
+}
+export type MarkGroupListPageParam = PageParams<MarkGroupListFilter>;
+
+export interface MarkGroupUpdateParams {
+  // id
+  id?: number;
+  // 分组序号
+  groupNo: number;
+  // 名称
+  name: string;
+  // 题目
+  questions: Array<{
+    // 大题号
+    questionNo: string;
+    // 小题号
+    smallQuestionNo: string;
+    // 间隔分
+    intervalScore: number;
+  }>;
+  // 评卷图
+  markingArea?: CoverArea[];
+  // 双评
+  doubleMarking: boolean;
+}
+
+// 评卷进度
+// 评卷进度统计信息
+export interface MarkStatInfo {
+  // 任务已完成数
+  taskFinishedCount: number;
+  // 任务待完成数
+  taskUnfinishedCount: number;
+  // 科目已完成数
+  courseFinishedCount: number;
+  // 科目待完成数
+  courseUnfinishedCount: number;
+}
+
+// 评卷进度统计列表:科目	选做异常	上传人数	主观总分	分组总数	完成进度	待复核量
+export interface MarkStatItem {
+  // 科目
+  courseName: string;
+  // 选做异常
+  isOptionalException: boolean;
+  // 上传人数
+  uploadCount: number;
+  // 主观总分
+  subjectiveTotalScore: number;
+  // 分组总数
+  groupCount: number;
+  // 完成进度
+  progress: number;
+  // 待复核量
+  unReviewedCount: number;
+}
+export type MarkStatListPageRes = PageResult<MarkStatItem>;
+export interface MarkStatListFilter {
+  // 科目
+  subject?: number | null;
+  // 完成进度
+  progress?: string;
+}
+export type MarkStatListPageParam = PageParams<MarkStatListFilter>;

+ 9 - 0
src/router/routes/modules/base.ts

@@ -198,6 +198,15 @@ const BASE: AppRouteRecordRaw = {
         requiresAuth: true,
       },
     },
+    {
+      path: '/mark-progress',
+      name: 'MarkProgress',
+      component: () => import('@/views/mark/MarkProgress.vue'),
+      meta: {
+        title: '评卷进度',
+        requiresAuth: true,
+      },
+    },
   ],
 };
 

+ 229 - 0
src/views/mark/MarkProgress.vue

@@ -0,0 +1,229 @@
+<template>
+  <div class="part-box">
+    <!-- 统计图表区域 -->
+    <div class="chart-container">
+      <div class="chart-item">
+        <h3>阅卷总进度</h3>
+        <Chart :options="taskChartOptions" width="400px" height="300px" />
+      </div>
+      <div class="chart-item">
+        <h3>科目阅卷进度</h3>
+        <Chart :options="courseChartOptions" width="400px" height="300px" />
+      </div>
+    </div>
+  </div>
+
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="科目">
+        <select-subject v-model="searchModel.subject"></select-subject>
+      </el-form-item>
+      <el-form-item label="完成进度">
+        <el-select
+          v-model="searchModel.progress"
+          placeholder="请选择"
+          clearable
+          style="width: 120px"
+        >
+          <el-option label="已完成" value="100" />
+          <el-option label="未完成" value="0-99" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+
+  <div class="part-box">
+    <el-table class="page-table" :data="dataList" :loading="loading">
+      <el-table-column property="courseName" label="科目" min-width="200" />
+      <el-table-column label="选做异常" min-width="100">
+        <template #default="scope">
+          <el-tag :type="scope.row.isOptionalException ? 'danger' : 'success'">
+            {{ scope.row.isOptionalException ? '是' : '否' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        property="uploadCount"
+        label="上传人数"
+        min-width="100"
+      />
+      <el-table-column
+        property="subjectiveTotalScore"
+        label="主观总分"
+        min-width="100"
+      />
+      <el-table-column property="groupCount" label="分组总数" min-width="100" />
+      <el-table-column label="完成进度" min-width="120">
+        <template #default="scope">
+          <el-progress
+            :percentage="scope.row.progress"
+            :color="scope.row.progress === 100 ? '#67c23a' : '#409eff'"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column
+        property="unReviewedCount"
+        label="待复核量"
+        min-width="100"
+      />
+    </el-table>
+    <el-pagination
+      v-model:current-page="pagination.pageNumber"
+      v-model:page-size="pagination.pageSize"
+      :layout="pagination.layout"
+      :total="pagination.total"
+      @size-change="pageSizeChange"
+      @current-change="toPage"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive, ref, computed, onMounted } from 'vue';
+  import { getMarkStatInfo, getMarkStatList } from '@/api/mark';
+  import {
+    MarkStatInfo,
+    MarkStatItem,
+    MarkStatListFilter,
+  } from '@/api/types/mark';
+  import useTable from '@/hooks/table';
+  import Chart from '@/components/chart/index.vue';
+
+  defineOptions({
+    name: 'MarkProgress',
+  });
+
+  const searchModel = reactive<MarkStatListFilter>({
+    subject: null,
+    progress: null,
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<MarkStatItem>(getMarkStatList, searchModel, false);
+
+  // 统计信息
+  const statInfo = ref<MarkStatInfo>({
+    taskFinishedCount: 0,
+    taskUnfinishedCount: 0,
+    courseFinishedCount: 0,
+    courseUnfinishedCount: 0,
+  });
+
+  // 任务进度饼状图配置
+  const taskChartOptions = computed(() => ({
+    tooltip: {
+      trigger: 'item',
+      formatter: '{a} <br/>{b}: {c} ({d}%)',
+    },
+    legend: {
+      orient: 'horizontal',
+      bottom: '10%',
+      data: ['任务待完成', '任务已完成'],
+    },
+    series: [
+      {
+        name: '任务进度',
+        type: 'pie',
+        radius: ['40%', '70%'],
+        center: ['50%', '40%'],
+        data: [
+          {
+            value: statInfo.value.taskUnfinishedCount,
+            name: '任务待完成',
+            itemStyle: { color: '#5470c6' },
+          },
+          {
+            value: statInfo.value.taskFinishedCount,
+            name: '任务已完成',
+            itemStyle: { color: '#ff7875' },
+          },
+        ],
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowOffsetX: 0,
+            shadowColor: 'rgba(0, 0, 0, 0.5)',
+          },
+        },
+      },
+    ],
+  }));
+
+  // 科目进度饼状图配置
+  const courseChartOptions = computed(() => ({
+    tooltip: {
+      trigger: 'item',
+      formatter: '{a} <br/>{b}: {c} ({d}%)',
+    },
+    legend: {
+      orient: 'horizontal',
+      bottom: '10%',
+      data: ['科目待完成', '科目已完成'],
+    },
+    series: [
+      {
+        name: '科目进度',
+        type: 'pie',
+        radius: ['40%', '70%'],
+        center: ['50%', '40%'],
+        data: [
+          {
+            value: statInfo.value.courseUnfinishedCount,
+            name: '科目待完成',
+            itemStyle: { color: '#5470c6' },
+          },
+          {
+            value: statInfo.value.courseFinishedCount,
+            name: '科目已完成',
+            itemStyle: { color: '#ff7875' },
+          },
+        ],
+        emphasis: {
+          itemStyle: {
+            shadowBlur: 10,
+            shadowOffsetX: 0,
+            shadowColor: 'rgba(0, 0, 0, 0.5)',
+          },
+        },
+      },
+    ],
+  }));
+
+  // 获取统计信息
+  async function getStatInfo() {
+    try {
+      const data = await getMarkStatInfo();
+      statInfo.value = data;
+    } catch (error) {
+      console.error('获取统计信息失败:', error);
+    }
+  }
+
+  onMounted(() => {
+    getStatInfo();
+  });
+</script>
+
+<style scoped lang="less">
+  .chart-container {
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    padding: 20px;
+    gap: 40px;
+
+    .chart-item {
+      text-align: center;
+
+      h3 {
+        margin-bottom: 20px;
+        color: #333;
+        font-size: 16px;
+        font-weight: 500;
+      }
+    }
+  }
+</style>