Browse Source

feat: 数据检查

zhangjie 1 week ago
parent
commit
2b989c34e1

+ 1 - 0
components.d.ts

@@ -44,6 +44,7 @@ declare module '@vue/runtime-core' {
     ElTabPane: typeof import('element-plus/es')['ElTabPane'];
     ElTabs: typeof import('element-plus/es')['ElTabs'];
     ElTag: typeof import('element-plus/es')['ElTag'];
+    ElTextarea: typeof import('element-plus/es')['ElTextarea'];
     ElTooltip: typeof import('element-plus/es')['ElTooltip'];
     ElUpload: typeof import('element-plus/es')['ElUpload'];
     FileUpload: typeof import('./src/components/file-upload/index.vue')['default'];

+ 29 - 0
src/api/check.ts

@@ -0,0 +1,29 @@
+import axios from 'axios';
+import {
+  ImageCheckDataListFilter,
+  ImageCheckPageRes,
+  ManualConfirmDataListFilter,
+  ManualConfirmPageRes,
+  ResultCheckDataListFilter,
+} from './types/check';
+
+// 人工确认查询
+export function getManualConfirmDataList(
+  params: ManualConfirmDataListFilter
+): Promise<ManualConfirmPageRes> {
+  return axios.post('/api/manualConfirmDataList', { params });
+}
+
+// 图片检查查询
+export function getImageCheckDataList(
+  params: ImageCheckDataListFilter
+): Promise<ImageCheckPageRes> {
+  return axios.post('/api/imageCheckDataList', { params });
+}
+
+// 识别结果查询
+export function getResultCheckDataList(
+  params: ResultCheckDataListFilter
+): Promise<number[]> {
+  return axios.post('/api/recognitionResultDataList', { params });
+}

+ 94 - 0
src/api/types/check.ts

@@ -0,0 +1,94 @@
+import { DataScopeMode, NumberScopeMode } from '@/constants/enumerate';
+import { PageResult, PageParams } from './common';
+
+// 识别结果检查 数据列表筛选条件
+export interface ResultCheckDataListFilter {
+  // 是否缺考
+  isMissing: boolean | undefined;
+  // 准考证号
+  examCardNo: string;
+  // 科目
+  subject: string;
+  // 科目范围模式
+  subjectRangeMode: DataScopeMode;
+  // 考点
+  examPoint: string;
+  // 考点范围模式
+  examPointRangeMode: DataScopeMode;
+  // 客观题是否有识别结果
+  objectiveQuestionHasResult: boolean | undefined;
+  // 客观总分
+  objectiveTotalScore: number | null;
+  // 客观总分范围模式
+  objectiveTotalScoreRangeMode: NumberScopeMode;
+  // 主观总分
+  subjectiveTotalScore: number | null;
+  // 主观总分范围模式
+  subjectiveTotalScoreRangeMode: NumberScopeMode;
+  // 试卷类型
+  paperType: string;
+}
+
+// 图片检查 数据列表筛选条件
+export interface ImageCheckDataListFilter {
+  // 姓名
+  name: string;
+  // 准考证号
+  examCardNo: string;
+  // 学号
+  studentNo: string;
+  // 科目
+  subject: number | null;
+}
+export type ImageCheckPageParams = PageParams<ImageCheckDataListFilter>;
+
+// 图片检查列表: 准考证号	姓名	学号	科目	扫描批次	上传时间
+export interface ImageCheckDataListItem {
+  // 主键
+  id: number;
+  // 姓名
+  name: string;
+  // 准考证号
+  examCardNo: string;
+  // 学号
+  studentNo: string;
+  // 科目
+  subject: number | null;
+  // 扫描批次
+  scanBatch: string;
+  // 上传时间
+  uploadTime: string;
+}
+export type ImageCheckPageRes = PageResult<ImageCheckDataListItem>;
+
+// 人工确认 数据列表筛选条件
+export interface ManualConfirmDataListFilter {
+  // 确认类型
+  confirmType: number | null;
+  // 科目
+  subject: number | null;
+  // 考点
+  examPoint: string;
+}
+export type ManualConfirmPageParams = PageParams<ManualConfirmDataListFilter>;
+
+// 人工确认列表: 准考证号	姓名	学号	科目	考点	扫描批次	上传时间
+export interface ManualConfirmDataListItem {
+  // 主键
+  id: number;
+  // 姓名
+  name: string;
+  // 准考证号
+  examCardNo: string;
+  // 学号
+  studentNo: string;
+  // 科目
+  subject: number | null;
+  // 考点
+  examPoint: string;
+  // 扫描批次
+  scanBatch: string;
+  // 上传时间
+  uploadTime: string;
+}
+export type ManualConfirmPageRes = PageResult<ManualConfirmDataListItem>;

+ 14 - 0
src/constants/enumerate.ts

@@ -55,3 +55,17 @@ export const LOG_TYPE = {
   EXPORT: '导出',
 };
 export type LogType = keyof typeof LOG_TYPE;
+
+// 数据范围模式: 属于 不属于
+export const DATA_SCOPE_MODE = {
+  BELONG: '属于',
+  NOT_BELONG: '不属于',
+};
+export type DataScopeMode = keyof typeof DATA_SCOPE_MODE;
+// 数值范围模式: 大于 小于 等于
+export const NUMBER_SCOPE_MODE = {
+  GREATER_THAN: '大于',
+  LESS_THAN: '小于',
+  EQUAL: '等于',
+};
+export type NumberScopeMode = keyof typeof NUMBER_SCOPE_MODE;

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

@@ -117,6 +117,33 @@ const BASE: AppRouteRecordRaw = {
         requiresAuth: true,
       },
     },
+    {
+      path: '/result-check',
+      name: 'ResultCheck',
+      component: () => import('@/views/check/ResultCheck.vue'),
+      meta: {
+        title: '识别结果检查',
+        requiresAuth: true,
+      },
+    },
+    {
+      path: '/image-check',
+      name: 'ImageCheck',
+      component: () => import('@/views/check/ImageCheck.vue'),
+      meta: {
+        title: '图片检查',
+        requiresAuth: true,
+      },
+    },
+    {
+      path: '/manual-confirm',
+      name: 'ManualConfirm',
+      component: () => import('@/views/check/ManualConfirm.vue'),
+      meta: {
+        title: '人工确认',
+        requiresAuth: true,
+      },
+    },
   ],
 };
 

+ 90 - 0
src/views/check/ImageCheck.vue

@@ -0,0 +1,90 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="姓名">
+        <el-input
+          v-model.trim="searchModel.name"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        />
+      </el-form-item>
+      <el-form-item label="准考证号">
+        <el-input
+          v-model.trim="searchModel.examCardNo"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        />
+      </el-form-item>
+      <el-form-item label="学号">
+        <el-input
+          v-model.trim="searchModel.studentNo"
+          placeholder="请输入"
+          clearable
+          style="width: 120px"
+        />
+      </el-form-item>
+      <el-form-item label="科目">
+        <select-subject v-model="searchModel.subject"></select-subject>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+        <el-button @click="exportData">导出</el-button>
+        <el-button type="success" @click="startCheck">开始检查</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div class="part-box">
+    <el-table class="page-table" :data="dataList" :loading="loading">
+      <el-table-column type="index" label="序号" width="60" />
+      <el-table-column property="examCardNo" label="准考证号" width="120" />
+      <el-table-column property="name" label="姓名" min-width="100" />
+      <el-table-column property="studentNo" label="学号" width="120" />
+      <el-table-column property="subject" label="科目" min-width="100" />
+      <el-table-column property="scanBatch" label="扫描批次" width="120" />
+      <el-table-column property="uploadTime" label="上传时间" width="160" />
+    </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 } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { getImageCheckDataList } from '@/api/check';
+  import type {
+    ImageCheckDataListItem,
+    ImageCheckDataListFilter,
+  } from '@/api/types/check';
+  import useTable from '@/hooks/table';
+
+  defineOptions({
+    name: 'ImageCheck',
+  });
+
+  const searchModel = reactive<ImageCheckDataListFilter>({
+    name: '',
+    examCardNo: '',
+    studentNo: '',
+    subject: null,
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<ImageCheckDataListItem>(getImageCheckDataList, searchModel, false);
+
+  function exportData() {
+    ElMessage.info('导出功能待实现');
+  }
+
+  function startCheck() {
+    ElMessage.info('开始检查功能待实现');
+  }
+</script>

+ 83 - 0
src/views/check/ManualConfirm.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form inline>
+      <el-form-item label="确认类型">
+        <el-select
+          v-model="searchModel.confirmType"
+          placeholder="手动更新"
+          clearable
+          style="width: 120px"
+        >
+          <el-option label="手动更新" :value="1" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="科目">
+        <select-subject v-model="searchModel.subject"></select-subject>
+      </el-form-item>
+      <el-form-item label="考点">
+        <el-input
+          v-model.trim="searchModel.examPoint"
+          placeholder="请选择"
+          clearable
+          style="width: 120px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="toPage(1)">查询</el-button>
+        <el-button type="success" @click="startCheck">开始检查</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+  <div class="part-box">
+    <el-table class="page-table" :data="dataList" :loading="loading">
+      <el-table-column type="index" label="序号" width="60" />
+      <el-table-column property="examCardNo" label="准考证号" width="120" />
+      <el-table-column property="name" label="姓名" min-width="100" />
+      <el-table-column property="studentNo" label="学号" width="120" />
+      <el-table-column property="subject" label="科目" min-width="100" />
+      <el-table-column property="examPoint" label="考点" width="120" />
+      <el-table-column property="scanBatch" label="扫描批次" width="120" />
+      <el-table-column property="uploadTime" label="上传时间" width="160" />
+    </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 } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { getManualConfirmDataList } from '@/api/check';
+  import type {
+    ManualConfirmDataListItem,
+    ManualConfirmDataListFilter,
+  } from '@/api/types/check';
+  import useTable from '@/hooks/table';
+
+  defineOptions({
+    name: 'ManualConfirm',
+  });
+
+  const searchModel = reactive<ManualConfirmDataListFilter>({
+    confirmType: null,
+    subject: null,
+    examPoint: '',
+  });
+
+  const { dataList, pagination, loading, toPage, pageSizeChange } =
+    useTable<ManualConfirmDataListItem>(
+      getManualConfirmDataList,
+      searchModel,
+      false
+    );
+
+  function startCheck() {
+    ElMessage.info('开始检查功能待实现');
+  }
+</script>

+ 198 - 0
src/views/check/ResultCheck.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="part-box is-filter">
+    <el-form label-width="200px">
+      <el-form-item label="是否缺考">
+        <el-select
+          v-model="searchModel.isMissing"
+          placeholder="请选择"
+          clearable
+          style="width: 120px"
+        >
+          <el-option label="是" :value="true" />
+          <el-option label="否" :value="false" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="准考证号">
+        <el-input
+          v-model.trim="searchModel.examCardNo"
+          placeholder="多个准考证号用英文逗号分隔"
+          :rows="4"
+          type="textarea"
+          style="width: 400px"
+        />
+      </el-form-item>
+      <el-form-item label="科目">
+        <el-select
+          v-model="searchModel.subjectRangeMode"
+          placeholder="请选择"
+          style="width: 200px"
+        >
+          <el-option
+            v-for="(val, key) in DATA_SCOPE_MODE"
+            :key="key"
+            :label="val"
+            :value="key"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="">
+        <el-input
+          v-model.trim="searchModel.subjectCodes"
+          placeholder="多个科目代码用英文逗号分隔"
+          :rows="4"
+          type="textarea"
+          style="width: 400px"
+        />
+      </el-form-item>
+      <el-form-item label="考点">
+        <el-select
+          v-model="searchModel.examPointRangeMode"
+          placeholder="请选择"
+          style="width: 200px"
+        >
+          <el-option
+            v-for="(val, key) in DATA_SCOPE_MODE"
+            :key="key"
+            :label="val"
+            :value="key"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="">
+        <el-input
+          v-model.trim="searchModel.examPoint"
+          placeholder="多个考点用英文逗号分隔"
+          type="textarea"
+          :rows="4"
+          style="width: 400px"
+        />
+      </el-form-item>
+      <el-form-item label="客观题是否有识别结果">
+        <el-select
+          v-model="searchModel.objectiveQuestionHasResult"
+          placeholder="请选择"
+          clearable
+          style="width: 120px"
+        >
+          <el-option label="是" :value="true" />
+          <el-option label="否" :value="false" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="客观总分">
+        <el-select
+          v-model="searchModel.objectiveTotalScoreMode"
+          placeholder="请选择"
+          style="width: 100px"
+        >
+          <el-option
+            v-for="(val, key) in NUMBER_SCOPE_MODE"
+            :key="key"
+            :label="val"
+            :value="key"
+          />
+        </el-select>
+        <el-input-number
+          v-model="searchModel.objectiveTotalScore"
+          :min="0"
+          :max="9999"
+          :step="0.1"
+          :precision="1"
+          :controls="false"
+          step-strictly
+          placeholder="请输入"
+          style="width: 200px"
+        />
+      </el-form-item>
+      <el-form-item label="主观总分">
+        <el-select
+          v-model="searchModel.subjectiveTotalScoreMode"
+          placeholder="请选择"
+          style="width: 100px"
+        >
+          <el-option
+            v-for="(val, key) in NUMBER_SCOPE_MODE"
+            :key="key"
+            :label="val"
+            :value="key"
+          />
+        </el-select>
+        <el-input-number
+          v-model="searchModel.subjectiveTotalScore"
+          :min="0"
+          :max="9999"
+          :step="0.1"
+          :precision="1"
+          :controls="false"
+          step-strictly
+          placeholder="请输入"
+          style="width: 200px"
+        />
+      </el-form-item>
+      <el-form-item label="试卷类型">
+        <el-input
+          v-model.trim="searchModel.paperType"
+          placeholder="请输入"
+          clearable
+          style="width: 200px"
+        />
+      </el-form-item>
+      <el-form-item v-if="queryResult" label="查询结果">
+        <el-input :value="queryResult.length" readonly style="width: 120px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="queryData">查询数量</el-button>
+        <el-button
+          v-if="queryResult && queryResult.length"
+          type="primary"
+          @click="startProcess"
+          >开始处理</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { reactive, ref } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { getResultCheckDataList } from '@/api/check';
+  import type { ResultCheckDataListFilter } from '@/api/types/check';
+  import { DATA_SCOPE_MODE, NUMBER_SCOPE_MODE } from '@/constants/enumerate';
+
+  defineOptions({
+    name: 'ResultCheck',
+  });
+
+  const searchModel = reactive<ResultCheckDataListFilter>({
+    isMissing: undefined,
+    examCardNo: '',
+    subject: '',
+    subjectRangeMode: 'BELONG',
+    examPoint: '',
+    examPointRangeMode: 'BELONG',
+    objectiveQuestionHasResult: undefined,
+    objectiveTotalScore: undefined,
+    objectiveTotalScoreRangeMode: 'GREATER_THAN',
+    subjectiveTotalScore: undefined,
+    subjectiveTotalScoreRangeMode: 'GREATER_THAN',
+    paperType: '',
+  });
+
+  const queryResult = ref<number[] | null>(null);
+  const loading = ref(false);
+  async function queryData() {
+    loading.value = true;
+    try {
+      const result = await getResultCheckDataList({ ...searchModel });
+      queryResult.value = result || [];
+    } catch (error) {
+      console.error('查询失败:', error);
+    } finally {
+      loading.value = false;
+    }
+  }
+
+  function startProcess() {
+    ElMessage.info('开始处理功能待实现');
+  }
+</script>