Sfoglia il codice sorgente

feat: api-成绩复核

zhangjie 1 settimana fa
parent
commit
788c7cae7b

+ 106 - 5
src/api/review.ts

@@ -1,31 +1,132 @@
-import axios from 'axios';
+import axios, { AxiosResponse } from 'axios';
 import {
   ReviewStatInfo,
   ReviewStatListPageParams,
+  FullReviewListFilter,
   ReviewStatListPageRes,
   FullReviewListPageParams,
   FullReviewListPageRes,
   ScoreCheckListPageRes,
+  CheckReviewRes,
+  SubjectStatusInfo,
 } from './types/review';
 
-// 复核管理
+// 复核进度 -------->
 // 获取复核进度统计信息
 export function getReviewStatInfo(): Promise<ReviewStatInfo> {
-  return axios.post('/api/review/stat/info');
+  return axios.post('/api/admin/exam/inspected/chat', {});
 }
 
 // 获取复核进度列表信息
 export function getReviewStatList(
   params: ReviewStatListPageParams
 ): Promise<ReviewStatListPageRes> {
-  return axios.post('/api/review/stat/list', {}, { params });
+  return axios.post('/api/admin/exam/inspected/info', {}, { params });
 }
 
+// 导出复核进度
+export function exportReviewStatList(
+  params: ReviewStatListPageParams
+): Promise<AxiosResponse<Blob>> {
+  return axios.post(
+    '/api/admin/exam/inspected/info/export',
+    {},
+    { params, responseType: 'blob' }
+  );
+}
+
+// 全卷复核 ------->
 // 获取全卷复核列表信息
 export function getFullReviewList(
   params: FullReviewListPageParams
 ): Promise<FullReviewListPageRes> {
-  return axios.post('/api/review/full/list', {}, { params });
+  return axios.post('/api/admin/exam/inspected/list', {}, { params });
+}
+// 导出全卷复核列表
+export function exportFullReviewList(
+  params: FullReviewListFilter
+): Promise<AxiosResponse<Blob>> {
+  return axios.post(
+    '/api/admin/exam/inspected/list/export',
+    {},
+    { params, responseType: 'blob' }
+  );
+}
+
+// 再次复核
+export function reReview(subjectCode: string): Promise<boolean> {
+  return axios.post(
+    '/api/admin/exam/inspected/nextround',
+    {},
+    {
+      params: { subjectCode },
+    }
+  );
+}
+// 校验是否可以开启再次复核
+export function checkReReview(subjectCode: string): Promise<CheckReviewRes> {
+  return axios.post(
+    '/api/admin/exam/inspected/nextround/check',
+    {},
+    {
+      params: { subjectCode },
+    }
+  );
+}
+
+// 取消复核
+export function cancelReview(studentIds: number[]): Promise<CancelReviewRes> {
+  return axios.post(
+    '/api/admin/exam/inspected/batch-cancel',
+    {},
+    {
+      params: { studentIds },
+    }
+  );
+}
+
+// 复核轮数列表
+export function getReviewRoundList(
+  params: FullReviewListPageParams
+): Promise<number[]> {
+  return axios.post(
+    '/api/admin/exam/inspected/round/list',
+    {},
+    {
+      params,
+    }
+  );
+}
+
+// 科目状态信息
+export function getSubjectStatusInfo(
+  params: FullReviewListPageParams
+): Promise<SubjectStatusInfo> {
+  return axios.post(
+    '/api/admin/exam/inspected/subject/status',
+    {},
+    {
+      params,
+    }
+  );
+}
+
+// 复核工作量导出
+export function exportReviewWorkload(): Promise<AxiosResponse<Blob>> {
+  return axios.post(
+    '/api/admin/exam/inspected/work/export',
+    {},
+    { params, responseType: 'blob' }
+  );
+}
+
+// TODO: 复核导入模板下载
+export function reviewImportTemplate(): Promise<AxiosResponse<Blob>> {
+  return axios.post(
+    '/api/admin/exam/inspected/import/template/export',
+    {},
+    { params, responseType: 'blob' }
+  );
 }
 
 // 获取成绩校验列表信息

+ 72 - 38
src/api/types/review.ts

@@ -3,33 +3,34 @@ import { PageResult, PageParams } from './common';
 
 export interface ReviewStatInfo {
   // 任务已完成数
-  taskFinishedCount: number;
+  markedCount: number;
   // 任务待完成数
-  taskUnfinishedCount: number;
+  unMarkedCount: number;
   // 科目已完成数
-  courseFinishedCount: number;
+  finishCount: number;
   // 科目待完成数
-  courseUnfinishedCount: number;
+  unFinishCount: number;
 }
 
 // 复核统计列表项目: 科目名称 选做科目 试卷总量	任务数	已复核数	待复核数	完成进度	已复核次数
 export interface ReviewStatItem {
   // 科目名称
-  courseName: string;
+  subjectName: string;
+  subjectCode: string;
   // 选做科目
-  isOptional: boolean;
+  selectiveText: string;
   // 试卷总量
-  paperCount: number;
+  totalPaperCount: number;
   // 任务数
-  taskCount: number;
+  totalCount: number;
   // 已复核数
-  reviewedCount: number;
+  inspectedCount: number;
   // 待复核数
-  unReviewedCount: number;
+  leftCount: number;
   // 完成进度
-  progress: number;
+  percent: number;
   // 已复核次数
-  reviewedTimes: number;
+  finishCount: number;
 }
 export type ReviewStatListPageRes = PageResult<ReviewStatItem>;
 
@@ -37,9 +38,9 @@ export interface ReviewStatListFilter {
   // 科目
   subjectCode: string | null;
   // 选做科目
-  isOptional: boolean | null;
+  selective: boolean | null;
   // 完成进度
-  isFinished: boolean | null;
+  finished: boolean | null;
 }
 export type ReviewStatListPageParams = PageParams<ReviewStatListFilter>;
 
@@ -49,49 +50,64 @@ export interface FullReviewItem {
   // 主键
   id: number;
   // 科目
-  courseName: string;
+  subjectName: string;
+  subjectCode: string;
   // 密号
   secretNumber: string;
   // 选做异常
-  isOptionalException: boolean;
+  selectiveError: string;
   // 客观分
-  objectiveScore: number;
+  objectiveScoreString: string;
   // 主观分
-  subjectiveScore: number;
+  subjectiveScoreString: string;
   // 试卷总分
   totalScore: number;
   // 得分明细
-  scoreDetail: string;
+  subjectiveScoreList: string;
 }
 export type FullReviewListPageRes = PageResult<FullReviewItem>;
 
 export interface FullReviewListFilter {
   // 科目
   subjectCode: string;
-  // 选做异常
-  isOptionalException?: boolean;
+  // 选做科目
+  selective?: boolean;
   // 状态:已复核、未复核
-  isReviewed?: boolean;
+  inspected?: boolean;
   // 学院
-  academyId?: number;
+  college?: number;
   // 试卷总分
-  totalStartScore?: number;
-  totalEndScore?: number;
+  startScore?: number;
+  endScore?: number;
   // 选做题
-  optionalExceptionType?: OptionalExceptionType;
+  selectiveStatus?: OptionalExceptionType;
   // 复核次数
-  reviewedTimes?: number;
+  inspectRound?: number;
   // 复核人
-  reviewer?: string;
+  inspectorName?: string;
   // 大题号
-  bigQuestionNo?: number;
+  mainNumber?: number;
   // 大题得分
-  bigQuestionStartScore?: number;
-  bigQuestionEndScore?: number;
+  mainStartScore?: number;
+  mainEndScore?: number;
   // 小题得分
-  smallQuestionScore?: number;
+  questionScore?: number;
   // 密号
   secretNumber?: string;
+  // 复核轮数
+  inspectHistoryMap?: Record<
+    string,
+    {
+      examId: number;
+      id: number;
+      inspectRound: number;
+      inspectTime: string;
+      inspectorId: number;
+      inspectorName: string;
+      studentId: number;
+      subjectCode: string;
+    }
+  >;
 }
 export type FullReviewListPageParams = PageParams<FullReviewListFilter>;
 
@@ -101,18 +117,36 @@ export interface ScoreCheckItem {
   // 主键
   id: number;
   // 考生编号
-  studentNo: string;
+  studentCode: string;
   // 科目
-  courseName: string;
+  subjectName: string;
   // 科目编号
-  courseCode: string;
+  subjectCode: string;
   // 满分
-  totalScore: number;
+  fullScore: number;
   // 状态
   status: string;
   // 试卷总分
-  paperScore: number;
+  totalScore: number;
   // 得分明细
-  scoreDetail: string;
+  subjectiveScoreList: string;
 }
 export type ScoreCheckListPageRes = PageResult<ScoreCheckItem>;
+
+export interface CheckReviewRes {
+  message: string;
+  // -1-不可以再次复核,并弹出消息,0-需要弹出消息以待确认,1-可以再次复核
+  status: -1 | 0 | 1;
+}
+
+export interface CancelReviewRes {
+  message: string;
+  success: boolean;
+}
+
+export interface SubjectStatusInfo {
+  // 是否显示再次复核按钮
+  showNextInspect: boolean;
+  // 是否显示再次复核提示
+  showNextInspectMsg: boolean;
+}

+ 3 - 3
src/constants/enumerate.ts

@@ -89,9 +89,9 @@ export type NumberScopeMode = keyof typeof NUMBER_SCOPE_MODE;
 
 // 选做题异常类型:未选做 多选做 少选做
 export const OPTIONAL_EXCEPTION_TYPE = {
-  NOT_SELECTED: '未选做',
-  MULTI_SELECTED: '多选做',
-  LESS_SELECTED: '少选做',
+  UN_SELECTIVE: '未选做',
+  MUTI_SELECTIVE: '多选做',
+  LESS_SELECTIVE: '少选做',
 };
 export type OptionalExceptionType = keyof typeof OPTIONAL_EXCEPTION_TYPE;
 

+ 15 - 0
src/utils/download-export.ts

@@ -39,6 +39,12 @@ import {
   exportMarkTaskList,
   markerImportTemplate,
 } from '@/api/mark';
+import {
+  exportReviewStatList,
+  exportFullReviewList,
+  exportReviewWorkload,
+  reviewImportTemplate,
+} from '@/api/review';
 
 import useLoading from '@/hooks/loading';
 
@@ -118,6 +124,15 @@ const downloadConfig = {
   exportMarkTaskList,
   // 评卷员导入模板下载
   markerImportTemplate,
+  // review
+  // 导出复核进度
+  exportReviewStatList,
+  // 导出全卷复核列表
+  exportFullReviewList,
+  // 导出复核工作量
+  exportReviewWorkload,
+  // 复核导入模板下载
+  reviewImportTemplate,
 };
 type DownloadType = keyof typeof downloadConfig;
 

+ 233 - 86
src/views/review/AllReview.vue

@@ -6,7 +6,7 @@
       </el-form-item>
       <el-form-item label="选做科目">
         <el-select
-          v-model="searchModel.isOptionalException"
+          v-model="searchModel.selective"
           placeholder="不限"
           clearable
           style="width: 120px"
@@ -17,7 +17,7 @@
       </el-form-item>
       <el-form-item label="状态">
         <el-select
-          v-model="searchModel.isReviewed"
+          v-model="searchModel.inspected"
           placeholder="请选择"
           clearable
           style="width: 120px"
@@ -27,19 +27,13 @@
         </el-select>
       </el-form-item>
       <el-form-item label="学院">
-        <el-select
-          v-model="searchModel.academyId"
-          placeholder="请选择"
-          clearable
-          style="width: 120px"
-        >
-          <el-option label="请选11择" :value="11" />
-        </el-select>
+        <select-option v-model="searchModel.college" type="college">
+        </select-option>
       </el-form-item>
       <el-form-item label="试卷总分">
         <el-space>
           <el-input-number
-            v-model="searchModel.totalStartScore"
+            v-model="searchModel.startScore"
             placeholder="低分"
             :min="0"
             :max="999"
@@ -51,7 +45,7 @@
           />
           <span>到</span>
           <el-input-number
-            v-model="searchModel.totalEndScore"
+            v-model="searchModel.endScore"
             placeholder="高分"
             :min="0"
             :max="999"
@@ -65,7 +59,7 @@
       </el-form-item>
       <el-form-item label="选做题">
         <el-select
-          v-model="searchModel.optionalExceptionType"
+          v-model="searchModel.selectiveStatus"
           placeholder="请选择"
           clearable
           style="width: 120px"
@@ -80,17 +74,22 @@
       </el-form-item>
       <el-form-item label="复核次数">
         <el-select
-          v-model="searchModel.reviewedTimes"
+          v-model="searchModel.inspectRound"
           placeholder="请选择"
           clearable
           style="width: 120px"
         >
-          <el-option label="1" :value="1" />
+          <el-option
+            v-for="item in reviewRoundList"
+            :key="item"
+            :label="item"
+            :value="item"
+          />
         </el-select>
       </el-form-item>
       <el-form-item label="复核人">
         <el-input
-          v-model.trim="searchModel.reviewer"
+          v-model.trim="searchModel.inspectorName"
           placeholder=""
           clearable
           style="width: 120px"
@@ -98,7 +97,7 @@
       </el-form-item>
       <el-form-item label="大题">
         <el-select
-          v-model="searchModel.bigQuestionNo"
+          v-model="searchModel.mainNumber"
           placeholder="请选择"
           clearable
           style="width: 120px"
@@ -109,7 +108,7 @@
       <el-form-item label="大题得分">
         <el-space>
           <el-input-number
-            v-model="searchModel.bigQuestionStartScore"
+            v-model="searchModel.mainStartScore"
             placeholder="低分"
             :min="0"
             :max="999"
@@ -121,7 +120,7 @@
           />
           <span>到</span>
           <el-input-number
-            v-model="searchModel.bigQuestionEndScore"
+            v-model="searchModel.mainEndScore"
             placeholder="高分"
             :min="0"
             :max="999"
@@ -135,7 +134,7 @@
       </el-form-item>
       <el-form-item label="小题得分">
         <el-input-number
-          v-model="searchModel.smallQuestionScore"
+          v-model="searchModel.questionScore"
           placeholder=""
           :min="0"
           :max="999"
@@ -159,10 +158,11 @@
 
     <div class="part-action">
       <el-space wrap>
-        <el-button type="primary" @click="toPage(1)">查询</el-button>
+        <el-button type="primary" @click="search">查询</el-button>
         <el-button @click="onBatchReview"
           >批量复核({{ pagination.total }})</el-button
         >
+        <el-button @click="onRestartReview">再次复核</el-button>
         <el-button @click="onBatchCancelReview">取消复核</el-button>
         <el-button @click="onImport">导入</el-button>
         <el-dropdown @command="onExportCommand">
@@ -172,13 +172,15 @@
           </el-button>
           <template #dropdown>
             <el-dropdown-menu>
-              <el-dropdown-item command="list">复核列表</el-dropdown-item>
-              <el-dropdown-item command="gzl">工作量</el-dropdown-item>
+              <el-dropdown-item command="exportFullReviewList"
+                >复核列表</el-dropdown-item
+              >
+              <el-dropdown-item command="exportReviewWorkload"
+                >工作量</el-dropdown-item
+              >
             </el-dropdown-menu>
           </template>
         </el-dropdown>
-        <el-button @click="onExportSign">导出标记卷</el-button>
-        <el-button @click="onExportLog">错误日志</el-button>
       </el-space>
     </div>
   </div>
@@ -195,7 +197,7 @@
     >
       <el-table-column type="selection" width="55" />
       <el-table-column
-        prop="courseName"
+        prop="subjectName"
         label="科目"
         min-width="200"
         sortable
@@ -203,20 +205,28 @@
       <el-table-column prop="secretNumber" label="密号" width="120" />
       <el-table-column label="选做异常" width="100">
         <template #default="scope">
-          <el-tag :type="scope.row.isOptionalException ? 'warning' : 'success'">
-            {{ scope.row.isOptionalException ? '是' : '否' }}
+          <el-tag :type="scope.row.selectiveError ? 'warning' : 'success'">
+            {{ scope.row.selectiveError || '否' }}
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="objectiveScore" label="客观分" width="100" />
-      <el-table-column prop="subjectiveScore" label="主观分" width="100" />
+      <el-table-column prop="objectiveScoreString" label="客观分" width="100" />
+      <el-table-column
+        prop="subjectiveScoreString"
+        label="主观分"
+        width="100"
+      />
       <el-table-column
         prop="totalScore"
         label="试卷总分"
         width="110"
         sortable
       />
-      <el-table-column prop="scoreDetail" label="得分明细" min-width="200" />
+      <el-table-column
+        prop="subjectiveScoreList"
+        label="得分明细"
+        min-width="200"
+      />
       <el-table-column label="操作" width="100" fixed="right">
         <template #default="scope">
           <el-button size="small" link @click="onTrackView(scope.row)">
@@ -230,14 +240,48 @@
           </el-button>
         </template>
       </el-table-column>
-      <!-- TODO:复核人,复核时间  sortable -->
-      <el-table-column prop="reviewer" label="复核人" width="120" sortable />
-      <el-table-column
-        prop="reviewTime"
-        label="复核时间"
-        width="180"
-        sortable
-      />
+      <template v-for="item in reviewRoundList" :key="item">
+        <el-table-column
+          :prop="`inspectHistoryMap.additionalProperties${item}.inspectorName`"
+          label="复核人"
+          width="120"
+          sortable
+        >
+          <template #default="scope">
+            <span
+              v-if="
+                scope.row.inspectHistoryMap &&
+                scope.row.inspectHistoryMap[`additionalProperties${item}`]
+              "
+            >
+              {{
+                scope.row.inspectHistoryMap?.[`additionalProperties${item}`]
+                  .inspectorName
+              }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column
+          :prop="`inspectHistoryMap.additionalProperties${item}.inspectTime`"
+          label="复核时间"
+          width="180"
+          sortable
+        >
+          <template #default="scope">
+            <span
+              v-if="
+                scope.row.inspectHistoryMap &&
+                scope.row.inspectHistoryMap[`additionalProperties${item}`]
+              "
+            >
+              {{
+                scope.row.inspectHistoryMap?.[`additionalProperties${item}`]
+                  .inspectTime
+              }}
+            </span>
+          </template>
+        </el-table-column>
+      </template>
     </el-table>
     <el-pagination
       v-model:current-page="pagination.pageNumber"
@@ -248,17 +292,39 @@
       @current-change="toPage"
     />
   </div>
+
+  <!-- 导入 -->
+  <ImportDialog
+    ref="importDialogRef"
+    title="导入数据"
+    upload-url="/api/admin/user/class/import"
+    :format="['xls', 'xlsx']"
+    :download-handle="() => downloadExport('reviewImportTemplate')"
+    download-filename="导入模板.xlsx"
+  />
 </template>
 
 <script setup lang="ts">
-  import { reactive } from 'vue';
+  import { reactive, ref, computed } from 'vue';
   import { ElMessage } from 'element-plus';
   import { ArrowDown } from '@element-plus/icons-vue';
-  import { getFullReviewList } from '@/api/review';
-  import { FullReviewItem, FullReviewListFilter } from '@/api/types/review';
-  import useTable from '@/hooks/table';
-
+  import {
+    getFullReviewList,
+    cancelReview,
+    checkReReview,
+    reReview,
+    getSubjectStatusInfo,
+    getReviewRoundList,
+  } from '@/api/review';
   import { OPTIONAL_EXCEPTION_TYPE } from '@/constants/enumerate';
+  import {
+    FullReviewItem,
+    FullReviewListFilter,
+    SubjectStatusInfo,
+  } from '@/api/types/review';
+  import useTable from '@/hooks/table';
+  import { modalConfirm } from '@/utils/ui';
+  import { downloadExport } from '@/utils/download-export';
 
   defineOptions({
     name: 'AllReview',
@@ -266,18 +332,18 @@
 
   const searchModel = reactive<FullReviewListFilter>({
     subjectCode: null,
-    isOptionalException: undefined,
-    isReviewed: undefined,
-    academyId: undefined,
-    totalStartScore: undefined,
-    totalEndScore: undefined,
-    optionalExceptionType: undefined,
-    reviewedTimes: undefined,
-    reviewer: undefined,
-    bigQuestionNo: undefined,
-    bigQuestionStartScore: undefined,
-    bigQuestionEndScore: undefined,
-    smallQuestionScore: undefined,
+    selective: undefined,
+    inspected: undefined,
+    college: undefined,
+    startScore: undefined,
+    endScore: undefined,
+    selectiveStatus: undefined,
+    inspectRound: undefined,
+    inspectorName: undefined,
+    mainNumber: undefined,
+    mainStartScore: undefined,
+    mainEndScore: undefined,
+    questionScore: undefined,
     secretNumber: undefined,
   });
 
@@ -287,11 +353,43 @@
     loading,
     selectedRows,
     toPage,
+    getList,
     pageSizeChange,
     handleSelectionChange,
     handleSortChange,
   } = useTable<FullReviewItem>(getFullReviewList, searchModel, false);
 
+  async function search() {
+    await toPage(1);
+    await updateSubjectInfo();
+    await updateReviewRoundList();
+  }
+
+  // 计算选中的学生ID列表
+  const selectedStudentIds = computed(() => {
+    return selectedRows.value.map((row) => row.id);
+  });
+
+  // 科目状态信息
+  const curSubjectInfo = ref<SubjectStatusInfo>({
+    showNextInspect: false,
+    showNextInspectMsg: false,
+  });
+  async function updateSubjectInfo() {
+    const res = await getSubjectStatusInfo(searchModel.subjectCode);
+    curSubjectInfo.value = res || {
+      showNextInspect: false,
+      showNextInspectMsg: false,
+    };
+  }
+
+  // 复核轮数信息
+  const reviewRoundList = ref<number[]>([]);
+  async function updateReviewRoundList() {
+    const res = await getReviewRoundList(searchModel);
+    reviewRoundList.value = res || [];
+  }
+
   // 批量复核
   function onBatchReview() {
     if (selectedRows.length === 0) {
@@ -301,14 +399,11 @@
     // TODO: 批量复核
     console.log('批量复核:', selectedRows);
   }
-  // 批量取消复核
-  function onBatchCancelReview() {
-    if (selectedRows.length === 0) {
-      ElMessage.warning('请选择要取消复核的试卷');
-      return;
-    }
-    // TODO: 批量取消复核
-    console.log('批量复核:', selectedRows);
+
+  // 进入复核
+  function onReview(row: FullReviewItem) {
+    // TODO: 进入复核
+    console.log('复核:', row);
   }
 
   // 轨迹图
@@ -316,35 +411,87 @@
     // TODO: 轨迹图
     console.log('复核:', row);
   }
-  // 取消复核
-  function onCancelReview(row: FullReviewItem) {
-    // TODO: 取消复核
-    console.log('复核:', row);
+
+  // 再次复核
+  async function onRestartReview() {
+    const checkRes = await checkReReview(searchModel.subjectCode);
+    // -1-不可以再次复核,并弹出消息,0-需要弹出消息以待确认,1-可以再次复核
+    if (checkRes.status === 1) {
+      await reReview(searchModel.subjectCode);
+      return;
+    }
+
+    if (checkRes.status === 0) {
+      const confirm = await modalConfirm(checkRes.message, '提示 ').catch(
+        () => false
+      );
+      if (!confirm) return;
+
+      await reReview(searchModel.subjectCode);
+      return;
+    }
+
+    if (checkRes.status === -1) {
+      ElMessage.error(checkRes.message);
+    }
   }
-  // 进入复核
-  function onReview(row: FullReviewItem) {
-    // TODO: 进入复核
-    console.log('复核:', row);
+
+  // 批量取消复核
+  async function onBatchCancelReview() {
+    if (selectedRows.length === 0) {
+      ElMessage.warning('请选择要取消复核的数据');
+      return;
+    }
+    const confirm = await modalConfirm(
+      `确认取消复核选中的 ${selectedStudentIds.value.length}条数据吗?`,
+      '提示 '
+    ).catch(() => false);
+    if (!confirm) return;
+
+    try {
+      const res = await cancelReview(selectedStudentIds.value);
+      if (res.success) {
+        ElMessage.success('取消复核成功');
+        getList();
+      } else {
+        ElMessage.error(res.message);
+      }
+    } catch (error) {
+      console.error('取消复核失败:', error);
+    }
+  }
+
+  // 取消复核
+  async function onCancelReview(row: FullReviewItem) {
+    const confirm = await modalConfirm(
+      `确认取消当前复核数据吗?`,
+      '提示 '
+    ).catch(() => false);
+    if (!confirm) return;
+
+    try {
+      const res = await cancelReview([row.id]);
+      if (res.success) {
+        ElMessage.success('取消复核成功');
+        getList();
+      } else {
+        ElMessage.error(res.message);
+      }
+    } catch (error) {
+      console.error('取消复核失败:', error);
+    }
   }
 
   // 导入
+  const importDialogRef = ref();
   function onImport() {
-    // TODO: 实现导入功能
-    console.log('导入');
+    importDialogRef.value?.open();
   }
 
   // 导出
-  const onExportCommand = (command: string) => {
-    console.log('导出命令:', command);
+  const onExportCommand = async (
+    command: 'exportReviewWorkload' | 'exportReviewStatList'
+  ) => {
+    await downloadExport(command, searchModel);
   };
-  // 导出标记卷
-  function onExportSign() {
-    // TODO: 导出标记卷
-    console.log('导出标记卷');
-  }
-  // 导出错误日志
-  function onExportLog() {
-    // TODO: 导出错误日志
-    console.log('导出错误日志');
-  }
 </script>

+ 12 - 8
src/views/review/ScoreQuery.vue

@@ -16,9 +16,9 @@
       stripe
     >
       <el-table-column type="selection" width="55" />
-      <el-table-column prop="studentNo" label="考生编号" width="150" />
-      <el-table-column prop="courseName" label="科目" width="200" />
-      <el-table-column prop="totalScore" label="满分" width="100" />
+      <el-table-column prop="studentCode" label="考生编号" width="150" />
+      <el-table-column prop="subjectName" label="科目" width="200" />
+      <el-table-column prop="fullScore" label="满分" width="100" />
       <el-table-column label="状态" width="120">
         <template #default="scope">
           <el-tag :type="getStatusType(scope.row.status)">
@@ -26,8 +26,12 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column prop="paperScore" label="试卷总分" width="100" />
-      <el-table-column prop="scoreDetail" label="得分明细" min-width="200">
+      <el-table-column prop="totalScore" label="试卷总分" width="100" />
+      <el-table-column
+        prop="subjectiveScoreList"
+        label="得分明细"
+        min-width="200"
+      >
       </el-table-column>
     </el-table>
     <el-pagination
@@ -46,6 +50,7 @@
   import { getScoreCheckList } from '@/api/review';
   import { ScoreCheckItem } from '@/api/types/review';
   import useTable from '@/hooks/table';
+  import { downloadExport } from '@/utils/download-export';
 
   defineOptions({
     name: 'ScoreQuery',
@@ -72,8 +77,7 @@
   }
 
   // 导出
-  function onExport() {
-    // TODO: 实现导出功能
-    console.log('导出');
+  async function onExport() {
+    await downloadExport('exportReviewStatList', searchModel);
   }
 </script>

+ 29 - 22
src/views/review/ScoreReviewStatistics.vue

@@ -20,7 +20,7 @@
       </el-form-item>
       <el-form-item label="选做科目">
         <el-select
-          v-model="searchModel.isOptional"
+          v-model="searchModel.selective"
           placeholder="请选择"
           clearable
           style="width: 120px"
@@ -31,7 +31,7 @@
       </el-form-item>
       <el-form-item label="完成进度">
         <el-select
-          v-model="searchModel.isFinished"
+          v-model="searchModel.finished"
           placeholder="请选择"
           clearable
           style="width: 120px"
@@ -42,6 +42,7 @@
       </el-form-item>
       <el-form-item>
         <el-button type="primary" @click="toPage(1)">查询</el-button>
+        <el-button @click="onExport">导出</el-button>
       </el-form-item>
     </el-form>
     <el-divider class="form-divider" />
@@ -54,34 +55,34 @@
       stripe
       @sort-change="handleSortChange"
     >
-      <el-table-column prop="courseName" label="科目" min-width="200" />
+      <el-table-column prop="subjectName" label="科目" min-width="200" />
       <el-table-column label="选做科目" min-width="110" sortable>
         <template #default="scope">
-          <el-tag :type="scope.row.isOptional ? 'success' : 'info'">
-            {{ scope.row.isOptional ? '是' : '否' }}
+          <el-tag :type="scope.row.selectiveText ? 'success' : 'info'">
+            {{ scope.row.selectiveText || '否' }}
           </el-tag>
         </template>
       </el-table-column>
       <el-table-column
-        prop="paperCount"
+        prop="totalPaperCount"
         label="试卷总量"
         min-width="110"
         sortable
       />
       <el-table-column
-        prop="taskCount"
+        prop="totalCount"
         label="任务数"
         min-width="100"
         sortable
       />
       <el-table-column
-        prop="reviewedCount"
+        prop="inspectedCount"
         label="已复核数"
         width="110"
         sortable
       />
       <el-table-column
-        prop="unReviewedCount"
+        prop="leftCount"
         label="待复核数"
         min-width="110"
         sortable
@@ -89,13 +90,13 @@
       <el-table-column label="完成进度" min-width="110" sortable>
         <template #default="scope">
           <el-progress
-            :percentage="scope.row.progress"
-            :color="scope.row.progress === 100 ? '#67c23a' : '#409eff'"
+            :percentage="scope.row.percent"
+            :color="scope.row.percent === 100 ? '#67c23a' : '#409eff'"
           />
         </template>
       </el-table-column>
       <el-table-column
-        prop="reviewedTimes"
+        prop="finishCount"
         label="已复核次数"
         min-width="120"
         sortable
@@ -121,6 +122,8 @@
     ReviewStatListFilter,
   } from '@/api/types/review';
   import useTable from '@/hooks/table';
+  import { downloadExport } from '@/utils/download-export';
+
   import Chart from '@/components/chart/index.vue';
 
   defineOptions({
@@ -129,8 +132,8 @@
 
   const searchModel = reactive<ReviewStatListFilter>({
     subjectCode: null,
-    isOptional: null,
-    isFinished: null,
+    selective: null,
+    finished: null,
   });
 
   const {
@@ -144,10 +147,10 @@
 
   // 统计信息
   const statInfo = ref<ReviewStatInfo>({
-    taskFinishedCount: 0,
-    taskUnfinishedCount: 0,
-    courseFinishedCount: 0,
-    courseUnfinishedCount: 0,
+    markedCount: 0,
+    unMarkedCount: 0,
+    finishCount: 0,
+    unFinishCount: 0,
   });
 
   // 任务进度饼状图配置
@@ -169,12 +172,12 @@
         center: ['50%', '40%'],
         data: [
           {
-            value: statInfo.value.taskUnfinishedCount,
+            value: statInfo.value.unMarkedCount,
             name: '任务待完成',
             itemStyle: { color: '#5470c6' },
           },
           {
-            value: statInfo.value.taskFinishedCount,
+            value: statInfo.value.markedCount,
             name: '任务已完成',
             itemStyle: { color: '#ff7875' },
           },
@@ -209,12 +212,12 @@
         center: ['50%', '40%'],
         data: [
           {
-            value: statInfo.value.courseUnfinishedCount,
+            value: statInfo.value.unFinishCount,
             name: '科目待完成',
             itemStyle: { color: '#5470c6' },
           },
           {
-            value: statInfo.value.courseFinishedCount,
+            value: statInfo.value.finishCount,
             name: '科目已完成',
             itemStyle: { color: '#ff7875' },
           },
@@ -240,6 +243,10 @@
     }
   }
 
+  async function onExport() {
+    await downloadExport('exportReviewStatList', searchModel);
+  }
+
   onMounted(() => {
     getStatInfo();
   });