ソースを参照

feat: 类型检查完毕

zhangjie 8 ヶ月 前
コミット
b0bcb3dfcd
39 ファイル変更273 行追加172 行削除
  1. 2 2
      src/main/index.ts
  2. 1 1
      src/render/Layout/index.vue
  3. 2 2
      src/render/ap/absentCheck.ts
  4. 12 9
      src/render/ap/base.ts
  5. 2 4
      src/render/ap/dataCheck.ts
  6. 1 2
      src/render/ap/imageCheck.ts
  7. 3 4
      src/render/ap/resultExport.ts
  8. 1 2
      src/render/ap/review.ts
  9. 1 0
      src/render/ap/types/base.ts
  10. 10 10
      src/render/ap/types/dataCheck.ts
  11. 22 6
      src/render/components/ElementResize/index.vue
  12. 12 12
      src/render/components/ElementResize/types.ts
  13. 57 0
      src/render/components/SelectBoolean/index.vue
  14. 1 1
      src/render/components/SelectCourse/index.vue
  15. 2 0
      src/render/components/register.ts
  16. 8 10
      src/render/constants/enumerate.ts
  17. 7 4
      src/render/directives/eleClickOutside.ts
  18. 4 4
      src/render/directives/eleMove.ts
  19. 10 5
      src/render/hooks/dictOption.ts
  20. 10 2
      src/render/hooks/useLoop.ts
  21. 5 4
      src/render/store/modules/user/index.ts
  22. 1 1
      src/render/utils/recog/recog.ts
  23. 1 3
      src/render/utils/tool.ts
  24. 14 12
      src/render/views/AbsentCheck/CheckAction.vue
  25. 1 1
      src/render/views/AbsentCheck/ImportTypeDialog.vue
  26. 6 5
      src/render/views/AbsentCheck/index.vue
  27. 2 2
      src/render/views/Audit/ImageCheck/index.vue
  28. 8 5
      src/render/views/Audit/Intime/index.vue
  29. 3 1
      src/render/views/Audit/Review/ReviewAction.vue
  30. 27 28
      src/render/views/DataCheck/CheckAction.vue
  31. 12 5
      src/render/views/DataCheck/QuestionPanel.vue
  32. 1 1
      src/render/views/DataCheck/ScanImage/ColorPickerDialog.vue
  33. 1 1
      src/render/views/DataCheck/ScanImage/FillAreaSetDialog.vue
  34. 2 1
      src/render/views/DataCheck/ScanImage/index.vue
  35. 5 4
      src/render/views/DataCheck/index.vue
  36. 6 6
      src/render/views/DataCheck/types.ts
  37. 1 2
      src/render/views/RecognizeCheck/index.vue
  38. 9 5
      src/render/views/ScanManage/ImageView.vue
  39. 0 5
      types/global.d.ts

+ 2 - 2
src/main/index.ts

@@ -41,7 +41,7 @@ function createWin() {
   });
   win.webContents.on("before-input-event", (event: any, input: any) => {
     if (input.key === "F12") {
-      win.webContents.openDevTools();
+      win?.webContents.openDevTools();
     }
   });
   // loadWin.destroy();
@@ -106,7 +106,7 @@ ipcMain.on("change-win-size", (event, args: string) => {
   win.center();
 });
 ipcMain.on("window-min", () => {
-  win.minimize();
+  win?.minimize();
 });
 
 function startExe(exePath: string) {

+ 1 - 1
src/render/Layout/index.vue

@@ -72,7 +72,7 @@ const isIndexPage = computed(() => {
   return route.name === "CurExam" || route.name === "Audit";
 });
 const backToIndexPage = () => {
-  if (userStore.userInfo.role === "AUDITOR") {
+  if (userStore.userInfo?.role === "AUDITOR") {
     router.push({ name: "Audit" });
   } else {
     router.push({ name: "CurExam" });

+ 2 - 2
src/render/ap/absentCheck.ts

@@ -33,7 +33,7 @@ export const examStatusSave = (
 // 按考生导出缺考校验
 export const absentCheckStudentExport = (
   data: AbsentCheckListFilter
-): Promise<AxiosResponse<Blob>> =>
+): Promise<Blob> =>
   request({
     url: "/api/admin/check/exam-status/student/export",
     method: "post",
@@ -44,7 +44,7 @@ export const absentCheckStudentExport = (
 // 按考场导出缺考校验
 export const absentCheckRoomExport = (
   data: AbsentCheckListFilter
-): Promise<AxiosResponse<Blob>> =>
+): Promise<Blob> =>
   request({
     url: "/api/admin/check/exam-status/exam-room/export",
     method: "post",

+ 12 - 9
src/render/ap/base.ts

@@ -30,9 +30,10 @@ export const updateSheet = (
 ): Promise<UploadFileResult> => {
   const formData = new FormData();
   for (const key in data) {
-    if (Object.prototype.hasOwnProperty.call(data, key)) {
-      const val = data[key];
-      formData.append(key, val);
+    const propKey = key as keyof UploadSheetParams;
+    if (Object.prototype.hasOwnProperty.call(data, propKey)) {
+      const val = data[propKey];
+      formData.append(propKey, val instanceof Blob ? val : `${val}`);
     }
   }
   return request({
@@ -47,9 +48,10 @@ export const updateSlice = (
 ): Promise<UploadFileResult> => {
   const formData = new FormData();
   for (const key in data) {
-    if (Object.prototype.hasOwnProperty.call(data, key)) {
-      const val = data[key];
-      formData.append(key, val);
+    const propKey = key as keyof UploadSliceParams;
+    if (Object.prototype.hasOwnProperty.call(data, propKey)) {
+      const val = data[propKey];
+      formData.append(propKey, val instanceof Blob ? val : `${val}`);
     }
   }
   return request({
@@ -65,9 +67,10 @@ export const uploadSlice = (
 ): Promise<UploadFileResult> => {
   const formData = new FormData();
   for (const key in data) {
-    if (Object.prototype.hasOwnProperty.call(data, key)) {
-      const val = data[key];
-      formData.append(key, val);
+    const propKey = key as keyof UploadSliceParams;
+    if (Object.prototype.hasOwnProperty.call(data, propKey)) {
+      const val = data[propKey];
+      formData.append(propKey, val instanceof Blob ? val : `${val}`);
     }
   }
   return request({

+ 2 - 4
src/render/ap/dataCheck.ts

@@ -23,7 +23,7 @@ export const dataCheckList = (
 // 按考生导出答题卡扫描详情
 export const dataCheckStudentExport = (
   data: DataCheckListFilter
-): Promise<AxiosResponse<Blob>> =>
+): Promise<Blob> =>
   request({
     url: "/api/admin/scan/answer/student/export",
     method: "post",
@@ -32,9 +32,7 @@ export const dataCheckStudentExport = (
   });
 
 // 按考场导出答题卡扫描详情
-export const dataCheckRoomExport = (
-  data: DataCheckListFilter
-): Promise<AxiosResponse<Blob>> =>
+export const dataCheckRoomExport = (data: DataCheckListFilter): Promise<Blob> =>
   request({
     url: "/api/admin/scan/answer/exam-room/export",
     method: "post",

+ 1 - 2
src/render/ap/imageCheck.ts

@@ -1,4 +1,3 @@
-import { AxiosResponse } from "axios";
 import { request } from "@/utils/request";
 
 import {
@@ -33,7 +32,7 @@ export const imageCheckFailedList = (
 
 export const imageCheckFailedExport = (
   data: ExamSubjectParams
-): Promise<AxiosResponse<Blob>> =>
+): Promise<Blob> =>
   request({
     url: "/api/admin/check/image/failed/export",
     method: "get",

+ 3 - 4
src/render/ap/resultExport.ts

@@ -1,4 +1,3 @@
-import { AxiosResponse } from "axios";
 import { request } from "@/utils/request";
 
 import {
@@ -24,7 +23,7 @@ export const breachList = (data: ExamParams): Promise<BreachListItem[]> =>
     data,
   });
 
-export const breachTemplateDownload = (): Promise<AxiosResponse<Blob>> =>
+export const breachTemplateDownload = (): Promise<Blob> =>
   request({
     url: "/api/admin/student/breach/template",
     method: "post",
@@ -42,7 +41,7 @@ export const studentStatusList = (
     data,
   });
 
-export const statusTemplateDownload = (): Promise<AxiosResponse<Blob>> =>
+export const statusTemplateDownload = (): Promise<Blob> =>
   request({
     url: "/api/admin/student/cust-status/template",
     method: "post",
@@ -122,7 +121,7 @@ export const asyncTaskProgress = (
     data: { taskId },
   });
 
-export const dbfExportTaskDownload = (taskId: string): Promise<blob> =>
+export const dbfExportTaskDownload = (taskId: string): Promise<Blob> =>
   request({
     url: "/api/admin/subject/data/down",
     method: "post",

+ 1 - 2
src/render/ap/review.ts

@@ -1,4 +1,3 @@
-import { AxiosResponse } from "axios";
 import { request } from "@/utils/request";
 
 import { ExamSubjectParams, RequestActionResult } from "./types/common";
@@ -87,7 +86,7 @@ export const reviewTaskResetStatus = (
 // 复核校验异常导出
 export const reviewWarningTaskExport = (
   data: ReviewWarningTaskExportParams
-): Promise<AxiosResponse<Blob>> =>
+): Promise<Blob> =>
   request({
     url: "/api/admin/check/assigned/export",
     method: "post",

+ 1 - 0
src/render/ap/types/base.ts

@@ -10,6 +10,7 @@ export interface ExamItem {
   name: string;
   mode: string;
   enable: boolean;
+  schoolName: string;
   updateTime: number;
 }
 

+ 10 - 10
src/render/ap/types/dataCheck.ts

@@ -17,16 +17,16 @@ export interface DataCheckListFilter {
   examSite: string;
   examRoom: string;
   province: string;
-  paperTypeStatus: PaperTypeStatus;
+  paperTypeStatus: PaperTypeStatus | string;
   device: string;
-  absentSuspect: boolean;
-  omrAbsent: boolean;
-  assigned: boolean;
-  incomplete: boolean;
-  questionFilled: boolean;
-  subjectiveFilled: boolean;
-  hasFilled: boolean;
-  withOmrDetail: boolean;
+  absentSuspect: boolean | null;
+  omrAbsent: boolean | null;
+  assigned: boolean | null;
+  incomplete: boolean | null;
+  questionFilled: boolean | null;
+  subjectiveFilled: boolean | null;
+  hasFilled: boolean | null;
+  withOmrDetail: boolean | null;
 }
 
 export type DataCheckListParams = PageParams<DataCheckListFilter>;
@@ -112,7 +112,7 @@ export type OmrFiledType =
 export interface DataCheckOmrFieldEditParams {
   examId: number;
   examNumber: string;
-  paperNumber: string;
+  paperNumber: number;
   pageIndex: number;
   subjectCode: string;
   field: OmrFiledType;

+ 22 - 6
src/render/components/ElementResize/index.vue

@@ -29,7 +29,14 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, computed, onMounted, onBeforeMount } from "vue";
+import {
+  reactive,
+  ref,
+  computed,
+  onMounted,
+  onBeforeMount,
+  CSSProperties,
+} from "vue";
 import { vEleMoveDirective } from "../../directives/eleMove";
 import { objModifyAssign } from "../../utils/tool";
 
@@ -93,9 +100,9 @@ const styles = computed(() => {
         width: `${sizePos.w}px`,
         height: `${sizePos.h}px`,
         zIndex: props.modelValue.zindex || "auto",
-        position: "absolute",
+        position: "absolute" as CSSProperties["position"],
       }
-    : {};
+    : undefined;
 });
 
 const classes = computed(() => {
@@ -288,7 +295,16 @@ function moveRightBottomPoint({ left, top }: PositionData) {
   emitChange();
 }
 
-function moveOver() {
+function moveOver({ left, top }: PositionData) {
+  const sp = {
+    ...sizePos,
+    ...{
+      x: left + sizePosOrigin.x,
+      y: top + sizePosOrigin.y,
+    },
+  };
+  objModifyAssign(sizePos, fetchValidSizePos(sp, "move"));
+
   objModifyAssign(sizePosOrigin, sizePos);
   objModifyAssign(lastSizePos, sizePos);
   emitChange();
@@ -313,9 +329,9 @@ function moveElement({ left, top }: PositionData) {
   emitChange();
 }
 
-function moveElementOver() {
+function moveElementOver(pos: PositionData) {
   if (!props.move) return;
-  moveOver();
+  moveOver(pos);
 }
 
 function emitChange() {

+ 12 - 12
src/render/components/ElementResize/types.ts

@@ -1,15 +1,15 @@
 export const defaultActive = [
-  'r',
-  'rb',
-  'b',
-  'lb',
-  'l',
-  'lt',
-  't',
-  'rt',
+  "r",
+  "rb",
+  "b",
+  "lb",
+  "l",
+  "lt",
+  "t",
+  "rt",
 ] as const;
 export type ActionType = (typeof defaultActive)[number];
-export const defaultFitParent = ['w', 'h'] as const;
+export const defaultFitParent = ["w", "h"] as const;
 export type FitParentItem = (typeof defaultFitParent)[number];
 
 export interface SizeData {
@@ -27,8 +27,8 @@ export interface PositionData {
 
 export interface ContronItem {
   classes: string[];
-  movePoint: ({ left, top }: PositionData) => void;
-  movePointOver: (e: MouseEvent) => void;
+  movePoint: (pos: PositionData) => void;
+  movePointOver: (pos: PositionData) => void;
 }
 
 export interface TransformFitParam extends SizeData {
@@ -37,5 +37,5 @@ export interface TransformFitParam extends SizeData {
 
 export type TransformFitFunction = (
   data: TransformFitParam,
-  actionType: ActionType | 'move'
+  actionType: ActionType | "move"
 ) => SizeData;

+ 57 - 0
src/render/components/SelectBoolean/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <a-select
+    v-model:value="selected"
+    :placeholder="placeholder"
+    :options="optionList"
+    :multiple="false"
+    :allow-clear="allowClear"
+    :disabled="disabled"
+    @change="onChange"
+  >
+  </a-select>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from "vue";
+import useDictOption from "@/hooks/dictOption";
+
+defineOptions({
+  name: "SelectBoolean",
+});
+
+const props = withDefaults(
+  defineProps<{
+    value: boolean | null;
+    type: "BOOLEAN_TYPE" | "BOOLEAN_ENABLE_TYPE";
+    placeholder?: string;
+    allowClear: boolean;
+    disabled: boolean;
+  }>(),
+  {
+    placeholder: "请选择",
+    type: "BOOLEAN_TYPE",
+    allowClear: true,
+  }
+);
+const emit = defineEmits(["update:value", "change"]);
+
+const { optionList } = useDictOption(props.type);
+
+const selected = ref<number | undefined>();
+
+const onChange = () => {
+  const val = selected.value === undefined ? null : Boolean(selected.value);
+  emit("update:value", val);
+  emit("change", val);
+};
+
+watch(
+  () => props.value,
+  (val) => {
+    selected.value = val === null ? undefined : val ? 1 : 0;
+  },
+  {
+    immediate: true,
+  }
+);
+</script>

+ 1 - 1
src/render/components/SelectCourse/index.vue

@@ -48,7 +48,7 @@ search();
 
 const onChange = () => {
   const selectedData = optionList.value.filter(
-    (item) => selected.value === item.subjectCode
+    (item) => selected.value === item.code
   );
   emit("update:value", selected.value);
   emit("change", selectedData[0]);

+ 2 - 0
src/render/components/register.ts

@@ -22,6 +22,7 @@ import {
 import MyModal from "./MyModal/index.vue";
 import FooterInfo from "./FooterInfo/index.vue";
 import SelectCourse from "./SelectCourse/index.vue";
+import SelectBoolean from "./SelectBoolean/index.vue";
 import MyQuote from "./MyQuote/index.vue";
 import Accordion from "./Accordion/index.vue";
 import ImportButton from "./ImportButton/index.vue";
@@ -49,6 +50,7 @@ export default {
     Vue.component("MyModal", MyModal);
     Vue.component("FooterInfo", FooterInfo);
     Vue.component("SelectCourse", SelectCourse);
+    Vue.component("SelectBoolean", SelectBoolean);
     Vue.component("MyQuote", MyQuote);
     Vue.component("Accordion", Accordion);
     Vue.component("ImportButton", ImportButton);

+ 8 - 10
src/render/constants/enumerate.ts

@@ -37,15 +37,13 @@ export const IMAGE_TYPE = {
 
 export type ImageType = keyof typeof IMAGE_TYPE;
 
-export const booleanOptionList = [
-  {
-    label: "是",
-    value: true,
-  },
-  {
-    label: "否",
-    value: false,
-  },
-];
+export const BOOLEAN_TYPE = {
+  0: "否",
+  1: "是",
+};
+export const BOOLEAN_ENABLE_TYPE = {
+  0: "禁用",
+  1: "启用",
+};
 
 export const abc = "abcdefghijklmnopqrstuvwxyz".toUpperCase();

+ 7 - 4
src/render/directives/eleClickOutside.ts

@@ -3,19 +3,22 @@ import type { Directive } from "vue";
 type BindValue = () => void;
 
 const clickhandle = (e: MouseEvent, el: HTMLElement, value: BindValue) => {
-  if (!el.contains(e.target) && value) {
+  const targetNode = e.target as Node | null;
+  if (!el.contains(targetNode) && value) {
     value();
   }
 };
 
-let documentClickHandle = null;
+type MouseFunc = (e: MouseEvent) => void;
+let documentClickHandle: null | MouseFunc = null;
 
 export const vEleClickOutsideDirective: Directive<HTMLElement, BindValue> = {
   mounted(el, { value }) {
-    documentClickHandle = (e) => clickhandle(e, el, value);
+    documentClickHandle = (e: MouseEvent) => clickhandle(e, el, value);
     document.addEventListener("mouseup", documentClickHandle);
   },
   unmounted(el, { value }) {
-    document.removeEventListener("mouseup", documentClickHandle);
+    documentClickHandle &&
+      document.removeEventListener("mouseup", documentClickHandle);
   },
 };

+ 4 - 4
src/render/directives/eleMove.ts

@@ -1,9 +1,9 @@
 import type { Directive } from "vue";
 
 interface BindValue {
-  moveElement?: ({ left, top }: { left: number; top: number }) => void;
-  moveStop?: ({ left, top }: { left: number; top: number }) => void;
-  moveStart?: (e: MouseEvent) => void;
+  moveElement?: (pos: { left: number; top: number }) => void;
+  moveStop?: (pos: { left: number; top: number }) => void;
+  moveStart?: () => void;
   emitOriginLeftTop?: boolean;
 }
 
@@ -72,7 +72,7 @@ export const vEleMoveDirective: Directive<HTMLElement, BindValue> = {
       _y = e.pageY;
       ox = el.offsetLeft;
       oy = el.offsetTop;
-      value.moveStart && value.moveStart(e);
+      value.moveStart && value.moveStart();
 
       document.addEventListener("mousemove", moveHandle);
       document.addEventListener("mouseup", upHandle);

+ 10 - 5
src/render/hooks/dictOption.ts

@@ -1,4 +1,6 @@
 import { ref } from "vue";
+import type { DefaultOptionType } from "ant-design-vue/es/vc-select/Select";
+
 import {
   DEFAULT_LABEL,
   DATA_CHECK_TYPE,
@@ -7,6 +9,8 @@ import {
   EXAM_STATUS,
   EXAM_SIMPLE_STATUS,
   IMAGE_TYPE,
+  BOOLEAN_TYPE,
+  BOOLEAN_ENABLE_TYPE,
 } from "@/constants/enumerate";
 
 const dicts = {
@@ -16,6 +20,8 @@ const dicts = {
   EXAM_STATUS,
   EXAM_SIMPLE_STATUS,
   IMAGE_TYPE,
+  BOOLEAN_TYPE,
+  BOOLEAN_ENABLE_TYPE,
 };
 
 type DictTypeType = keyof typeof dicts;
@@ -26,10 +32,7 @@ type DictTypeType = keyof typeof dicts;
  * @returns option列表
  */
 export function dictToOption(dict: Record<any, string>) {
-  const options = [] as Array<{
-    value: number | string | boolean;
-    label: string;
-  }>;
+  const options = [] as DefaultOptionType[];
   Object.keys(dict).forEach((k) => {
     options.push({
       value: k,
@@ -40,7 +43,9 @@ export function dictToOption(dict: Record<any, string>) {
 }
 
 export default function useDictOption(dictType: DictTypeType) {
-  const optionList = ref<SelectOption[]>(dictToOption(dicts[dictType] || {}));
+  const optionList = ref<DefaultOptionType[]>(
+    dictToOption(dicts[dictType] || {})
+  );
 
   function getLabel(val: string): string {
     // @ts-ignore

+ 10 - 2
src/render/hooks/useLoop.ts

@@ -1,8 +1,12 @@
 import { ref } from "vue";
 
 type ActionFunc = () => Promise<unknown>;
+type CommonFunc = () => void;
 
-export default function useLoop(action: ActionFunc, interval: number) {
+export default function useLoop(
+  action: ActionFunc | CommonFunc,
+  interval: number
+) {
   let sts: NodeJS.Timeout[] = [];
   const stoped = ref(true);
 
@@ -10,7 +14,11 @@ export default function useLoop(action: ActionFunc, interval: number) {
     clear();
     if (stoped.value) return;
     try {
-      await action();
+      if (action instanceof Promise) {
+        await action();
+      } else {
+        action();
+      }
     } catch (error) {
       console.log(error);
     }

+ 5 - 4
src/render/store/modules/user/index.ts

@@ -21,9 +21,10 @@ interface UserInfo {
 
 interface UserState {
   heatBeatWorker: Worker | null;
+  heatBeatIsActive: boolean;
   curExam: Exam;
   imageCheckLoopTime: number;
-  userInfo: UserInfo;
+  userInfo: UserInfo | null;
   recogFillSet: RecogFillSetType;
 }
 
@@ -72,14 +73,14 @@ export const useUserStore = defineStore("user", {
       };
     },
     startHeatBeat() {
-      this.heatBeatWorker.postMessage(true);
+      this.heatBeatWorker?.postMessage(true);
     },
     setUserInfo(info: any) {
       this.userInfo = info;
       if (!info) {
-        this.heatBeatWorker.postMessage(false);
+        this.heatBeatWorker?.postMessage(false);
       } else {
-        this.heatBeatWorker.postMessage(true);
+        this.heatBeatWorker?.postMessage(true);
       }
     },
     setCurExam(exam: Exam) {

+ 1 - 1
src/render/utils/recog/recog.ts

@@ -86,7 +86,7 @@ export interface RecogBlock extends RecognizeArea {
 }
 
 // functions
-export function parseRecogData(data: string) {
+export function parseRecogData(data: string | null) {
   if (!data) return null;
   const precogData = window.atob(data);
   const recogData: RecogDataType | null = precogData

+ 1 - 3
src/render/utils/tool.ts

@@ -1,5 +1,3 @@
-import { AxiosResponse } from "axios";
-
 const storagePrefix = "cet_";
 /**
  * LocalStorage
@@ -326,7 +324,7 @@ export const obj2formData = (obj: Record<string, any>): FormData => {
 };
 
 export function getFileUrl(url: string) {
-  const baseUrl = local.get("baseUrl", "");
+  const baseUrl = local.get("baseUrl") || "";
   return `${baseUrl}/file/${url}`;
 }
 

+ 14 - 12
src/render/views/AbsentCheck/CheckAction.vue

@@ -68,8 +68,8 @@
 
         <a-radio-group v-model:value="imageType" @change="onImageTypeChange">
           <a-radio
-            v-for="item in imageTypeOptions"
-            :key="item.value"
+            v-for="(item, index) in imageTypeOptions"
+            :key="index"
             :value="item.value"
             >{{ item.label }}</a-radio
           >
@@ -79,6 +79,7 @@
         <template #header><IdcardFilled />题卡信息 </template>
 
         <QuestionPanel
+          v-if="dataCheckStore.curStudent && dataCheckStore.curPage"
           v-model:questions="questions"
           :info="questionInfo"
           :simple="isSliceImage"
@@ -149,6 +150,7 @@ import ResetDialog from "./ResetDialog.vue";
 import ImportTypeDialog from "./ImportTypeDialog.vue";
 import QuestionPanel from "../DataCheck/QuestionPanel.vue";
 import MoreExamNumberDialog from "../DataCheck/MoreExamNumberDialog.vue";
+import { QuestionInfo } from "../DataCheck/types";
 
 defineOptions({
   name: "CheckAction",
@@ -175,7 +177,6 @@ const initSearchModel = {
 const searchModel = reactive<AbsentCheckListFilter>({ ...initSearchModel });
 const searchDataCheckType = ref();
 const imageType = ref(dataCheckStore.imageType);
-const actionType = ref("common");
 
 // imageType
 const isSliceImage = computed(() => {
@@ -226,16 +227,18 @@ function onImageTypeChange() {
 
 // question panel
 const questionInfo = computed(() => {
+  if (!dataCheckStore.curStudent) return {} as QuestionInfo;
   return {
-    examNumber: dataCheckStore.curStudent?.examNumber,
-    name: dataCheckStore.curStudent?.name,
-    examSite: dataCheckStore.curStudent?.examSite,
-    seatNumber: dataCheckStore.curStudent?.seatNumber,
-    paperType: dataCheckStore.curStudent?.paperType,
+    examNumber: dataCheckStore.curStudent.examNumber,
+    name: dataCheckStore.curStudent.name,
+    examSite: dataCheckStore.curStudent.examSite,
+    seatNumber: dataCheckStore.curStudent.seatNumber,
+    paperType: dataCheckStore.curStudent.paperType,
+    examStatus: dataCheckStore.curStudent.examStatus as string,
   };
 });
 
-const questions = ref([]);
+const questions = ref([] as string[]);
 watch(
   () => dataCheckStore.curPageIndex,
   (val, oldval) => {
@@ -248,7 +251,7 @@ watch(
 
 async function onQuestionsChange() {
   if (!dataCheckStore.curPage) return;
-  dataCheckStore.curPage.question = [...questions.value];
+  dataCheckStore.curPage.question.result = [...questions.value];
 
   await dataCheckStore
     .updateField({
@@ -268,9 +271,8 @@ async function onExamStatusChange(val: ExamStatus) {
 // 导出
 const { loading: downloading, setLoading } = useLoading();
 const exportTypeDialogRef = ref();
-function onExport(type: string) {
+function onExport() {
   if (downloading.value) return;
-  actionType.value = type;
   exportTypeDialogRef.value?.open();
 }
 

+ 1 - 1
src/render/views/AbsentCheck/ImportTypeDialog.vue

@@ -93,7 +93,7 @@ function seleted(type: ImportType) {
 }
 
 function onValidError(data: { message: string }) {
-  message.error(message);
+  message.error(data.message);
 }
 
 // 下载模板

+ 6 - 5
src/render/views/AbsentCheck/index.vue

@@ -43,7 +43,7 @@ import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons-vue";
 import { AbsentCheckListFilter } from "@/ap/types/absentCheck";
 import { DataCheckListItem } from "@/ap/types/dataCheck";
 import { absentCheckList } from "@/ap/absentCheck";
-import { StudentPage } from "./types";
+import { StudentPage } from "../DataCheck/types";
 import { useDataCheckStore, useUserStore } from "@/store";
 
 import SimplePagination from "@/components/SimplePagination/index.vue";
@@ -84,13 +84,13 @@ async function getList() {
 }
 
 function parseStudentPageList(students: DataCheckListItem[]) {
-  dataList.value = [];
+  dataList.value = [] as StudentPage[];
 
   students.forEach((student, studentIndex) => {
     student.papers.forEach((paper, paperIndex) => {
       if (!paper.pages) return;
       paper.pages.forEach((page, pageIndex) => {
-        dataList.value.push({
+        const row: StudentPage = {
           ...page,
           paperId: paper.id,
           paperIndex,
@@ -100,7 +100,8 @@ function parseStudentPageList(students: DataCheckListItem[]) {
           studentId: student.id,
           examId: searchModel.examId,
           kid: `${student.id}-${studentIndex}-${paperIndex}-${pageIndex}`,
-        });
+        };
+        dataList.value.push(row);
       });
     });
   });
@@ -132,7 +133,7 @@ async function onSearch(datas: AbsentCheckListFilter) {
   await getList();
   selectPage(0);
 }
-onSearch({ examId: userStore.curExam.id });
+onSearch({ examId: userStore.curExam.id } as AbsentCheckListFilter);
 
 // page
 function selectPage(index: number) {

+ 2 - 2
src/render/views/Audit/ImageCheck/index.vue

@@ -23,13 +23,13 @@
           <span class="head-cont remain-time">{{ remainTime }}秒</span>
           <a-button
             v-if="loopImageStoped"
-            type="primary-light"
+            class="ant-btn-primary-light"
             @click="startImageLoop"
           >
             <template #icon><PlayCircleOutlined /></template>
             开始
           </a-button>
-          <a-button v-else type="error-light" @click="stopImageLoop">
+          <a-button v-else class="ant-btn-error-light" @click="stopImageLoop">
             <template #icon><PauseCircleOutlined /></template>
             暂停
           </a-button>

+ 8 - 5
src/render/views/Audit/Intime/index.vue

@@ -58,7 +58,7 @@
             />
           </template>
           <a-button
-            type="success"
+            class="ant-btn-success"
             :disabled="canSubmit"
             :loading="loading"
             @click="onConfirm(true)"
@@ -67,7 +67,7 @@
             通过
           </a-button>
           <a-button
-            type="error-light"
+            class="ant-btn-error-light"
             :loading="loading"
             @click="onConfirm(false)"
           >
@@ -155,10 +155,10 @@ const canSubmit = computed(() => {
 });
 
 function initData() {
-  dataList.value = [];
+  dataList.value = [] as StudentItem[];
   curStudent.value = null;
   curStudentIndex.value = 0;
-  batchInfo.value = {};
+  batchInfo.value = {} as AuditBatchData;
   hasTask.value = false;
 }
 
@@ -175,11 +175,14 @@ async function getData() {
   stopLoopGetData();
   hasTask.value = true;
   batchInfo.value = omit(res, "students");
-  dataList.value = res.students || [];
+  dataList.value = (res.students || []).map((item) => {
+    return { ...item, status: false };
+  });
   setCurStudent(0);
 }
 
 function onMark() {
+  if (!curStudent.value) return;
   curStudent.value.status = true;
   getNextStudent();
 }

+ 3 - 1
src/render/views/Audit/Review/ReviewAction.vue

@@ -175,7 +175,9 @@ async function getHistory() {
 }
 
 function updateTaskStatus(assignedSuspect: boolean) {
-  const row = dataList.value.find((item) => item.id === reviewStore.curTask.id);
+  const row = dataList.value.find(
+    (item) => item.id === reviewStore.curTask?.id
+  );
   if (!row) return;
   row.assignedSuspect = assignedSuspect;
 }

+ 27 - 28
src/render/views/DataCheck/CheckAction.vue

@@ -80,35 +80,26 @@
           <a-row>
             <a-col :span="12">
               <a-form-item label="客观题作答">
-                <a-select
+                <SelectBoolean
                   v-model:value="customSearchModel.questionFilled"
-                  placeholder="请选择"
-                  :options="booleanOptions"
                   style="width: 85px"
-                  allow-clear
-                ></a-select>
+                />
               </a-form-item>
             </a-col>
             <a-col :span="12">
               <a-form-item label="主观题作答">
-                <a-select
+                <SelectBoolean
                   v-model:value="customSearchModel.subjectiveFilled"
-                  placeholder="请选择"
-                  :options="booleanOptions"
                   style="width: 100%"
-                  allow-clear
-                ></a-select>
+                />
               </a-form-item>
             </a-col>
             <a-col :span="12">
               <a-form-item label="有作答">
-                <a-select
+                <SelectBoolean
                   v-model:value="customSearchModel.hasFilled"
-                  placeholder="请选择"
-                  :options="booleanOptions"
                   style="width: 85px"
-                  allow-clear
-                ></a-select>
+                />
               </a-form-item>
             </a-col>
             <a-col :span="12">
@@ -152,8 +143,8 @@
 
         <a-radio-group v-model:value="imageType" @change="onImageTypeChange">
           <a-radio
-            v-for="item in imageTypeOptions"
-            :key="item.value"
+            v-for="(item, index) in imageTypeOptions"
+            :key="index"
             :value="item.value"
             >{{ item.label }}</a-radio
           >
@@ -163,6 +154,7 @@
         <template #header><IdcardFilled />题卡信息 </template>
 
         <QuestionPanel
+          v-if="dataCheckStore.curStudent && dataCheckStore.curPage"
           v-model:questions="questions"
           :info="questionInfo"
           :simple="isSliceImage"
@@ -194,14 +186,20 @@ import {
 import { message } from "ant-design-vue";
 
 import { useUserStore, useDataCheckStore } from "@/store";
-import { DataCheckListFilter, ExamStatus } from "@/ap/types/dataCheck";
+import {
+  DataCheckListFilter,
+  DataStatus,
+  ExamStatus,
+} from "@/ap/types/dataCheck";
 import { SubjectItem } from "@/ap/types/base";
 import { ReviewExportType } from "@/ap/types/review";
+import { QuestionInfo } from "./types";
+
 import { dataCheckStudentExport, dataCheckRoomExport } from "@/ap/dataCheck";
 import { examStatusSave } from "@/ap/absentCheck";
 import useDictOption from "@/hooks/dictOption";
 import useLoading from "@/hooks/useLoading";
-import { ImageType, booleanOptionList } from "@/constants/enumerate";
+import { ImageType } from "@/constants/enumerate";
 import { objModifyAssign } from "@/utils/tool";
 
 import ExportTypeDialog from "../Review/ExportTypeDialog.vue";
@@ -221,7 +219,6 @@ const { optionList: dataCheckOptions } = useDictOption("DATA_CHECK_TYPE");
 const { optionList: paperTypeOptions } = useDictOption("PAPER_TYPE_STATUS");
 const { optionList: imageTypeOptions } = useDictOption("IMAGE_TYPE");
 const { optionList: examStatusOptions } = useDictOption("EXAM_STATUS");
-const booleanOptions = ref(booleanOptionList);
 const panelKey = ref(["1", "2", "3", "4"]);
 
 // course data
@@ -236,8 +233,8 @@ const fieldNames = { label: "name", value: "code" };
 // search
 const initSearchModel = {
   examId: userStore.curExam.id,
-  status: "",
-  examStatus: [],
+  status: [] as DataStatus[],
+  examStatus: [] as ExamStatus[],
   examNumber: "",
   studentCode: "",
   name: "",
@@ -348,16 +345,18 @@ function onImageTypeChange() {
 
 // question panel
 const questionInfo = computed(() => {
+  if (!dataCheckStore.curStudent) return {} as QuestionInfo;
   return {
-    examNumber: dataCheckStore.curStudent?.examNumber,
-    name: dataCheckStore.curStudent?.name,
-    examSite: dataCheckStore.curStudent?.examSite,
-    seatNumber: dataCheckStore.curStudent?.seatNumber,
-    paperType: dataCheckStore.curStudent?.paperType,
+    examNumber: dataCheckStore.curStudent.examNumber,
+    name: dataCheckStore.curStudent.name,
+    examSite: dataCheckStore.curStudent.examSite,
+    seatNumber: dataCheckStore.curStudent.seatNumber,
+    paperType: dataCheckStore.curStudent.paperType,
+    examStatus: dataCheckStore.curStudent.examStatus as string,
   };
 });
 
-const questions = ref([]);
+const questions = ref([] as string[]);
 watch(
   () => dataCheckStore.curPageIndex,
   (val, oldval) => {

+ 12 - 5
src/render/views/DataCheck/QuestionPanel.vue

@@ -32,9 +32,14 @@
         <a-radio-group
           v-model:value="examStatus"
           name="examStatus"
-          :options="examStatusOptions"
           @change="onExamStatusChange"
         >
+          <a-radio
+            v-for="(item, index) in examStatusOptions"
+            :key="index"
+            :value="item.value"
+            >{{ item.label }}</a-radio
+          >
         </a-radio-group>
       </a-descriptions-item>
     </a-descriptions>
@@ -116,7 +121,7 @@ const dataCheckStore = useDataCheckStore();
 
 const { optionList: examStatusOptions } = useDictOption("EXAM_SIMPLE_STATUS");
 const examStatus = ref("");
-const questionList = ref([]);
+const questionList = ref([] as string[]);
 const curQuestion = ref("");
 const curQuestionIndex = ref(-1);
 
@@ -200,12 +205,14 @@ const paperTypeArea = ref<AreaSize | null>(null);
 const paperTypeImg = ref("");
 const paperTypeResult = ref("");
 async function onEditPaperType() {
+  if (!dataCheckStore.curPage) return;
+
   if (paperTypeArea.value) {
     paperTypeImg.value = await getSliceFileUrl(
-      dataCheckStore.curPage?.sheetUri,
+      dataCheckStore.curPage.sheetUri,
       paperTypeArea.value
     );
-    paperTypeResult.value = dataCheckStore.curPage?.paperType.result;
+    paperTypeResult.value = dataCheckStore.curPage.paperType.result;
   } else {
     paperTypeImg.value = "";
   }
@@ -231,7 +238,7 @@ watch(
     paperTypeArea.value = null;
     if (!val) return;
 
-    const regdata = parseRecogData(dataCheckStore.curPage.recogData);
+    const regdata = parseRecogData(val);
     if (!regdata) return;
 
     const rect = regdata.paperType.rect || null;

+ 1 - 1
src/render/views/DataCheck/ScanImage/ColorPickerDialog.vue

@@ -29,7 +29,7 @@ defineExpose({ open, close });
 const props = defineProps<{
   color: string;
 }>();
-const emit = defineEmits(["modified"]);
+const emit = defineEmits(["confirm"]);
 
 const selectColor = ref("");
 function updateColor(eventData: ColorChangeDetail) {

+ 1 - 1
src/render/views/DataCheck/ScanImage/FillAreaSetDialog.vue

@@ -57,7 +57,7 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, watch } from "vue";
+import { reactive, ref, watch, UnwrapRef } from "vue";
 import { message } from "ant-design-vue";
 
 import useModal from "@/hooks/useModal";

+ 2 - 1
src/render/views/DataCheck/ScanImage/index.vue

@@ -10,7 +10,8 @@
     >
       <img
         ref="imgRef"
-        :src="getFileUrl(curPage?.sheetUri)"
+        v-if="curPage"
+        :src="getFileUrl(curPage.sheetUri)"
         alt="原图"
         @load="initImageSize"
       />

+ 5 - 4
src/render/views/DataCheck/index.vue

@@ -83,13 +83,13 @@ async function getList() {
 }
 
 function parseStudentPageList(students: DataCheckListItem[]) {
-  dataList.value = [];
+  dataList.value = [] as StudentPage[];
 
   students.forEach((student, studentIndex) => {
     student.papers.forEach((paper, paperIndex) => {
       if (!paper.pages) return;
       paper.pages.forEach((page, pageIndex) => {
-        dataList.value.push({
+        const row: StudentPage = {
           ...page,
           paperId: paper.id,
           paperIndex,
@@ -99,7 +99,8 @@ function parseStudentPageList(students: DataCheckListItem[]) {
           studentId: student.id,
           examId: searchModel.examId,
           kid: `${student.id}-${studentIndex}-${paperIndex}-${pageIndex}`,
-        });
+        };
+        dataList.value.push(row);
       });
     });
   });
@@ -132,7 +133,7 @@ async function onSearch(datas: DataCheckListFilter) {
   selectPage(0);
 }
 
-onSearch({ examId: userStore.curExam.id });
+onSearch({ examId: userStore.curExam.id } as DataCheckListFilter);
 
 // page
 function selectPage(index: number) {

+ 6 - 6
src/render/views/DataCheck/types.ts

@@ -8,14 +8,14 @@ export interface StudentPage extends PaperPageItem {
   paperIndex: number;
   paperNumber: number;
   pageIndex: number;
-  pagePageIndex: number;
   kid: string;
 }
 
 export interface QuestionInfo {
-  examNumber: string | undefined;
-  name: string | undefined;
-  examSite: string | undefined;
-  seatNumber: number | undefined;
-  paperType: string | undefined;
+  examNumber: string;
+  name: string;
+  examSite: string;
+  seatNumber: number;
+  paperType: string;
+  examStatus: string;
 }

+ 1 - 2
src/render/views/RecognizeCheck/index.vue

@@ -104,7 +104,6 @@ import useTable from "@/hooks/useTable";
 import {
   RecognizeCheckListItem,
   RecognizeConditionItem,
-  RecognizeCheckTaskSaveParams,
 } from "@/ap/types/recognizeCheck";
 import {
   recognizeCheckListPage,
@@ -193,7 +192,7 @@ const columns: TableProps["columns"] = [
     },
   },
 ];
-const curRow = ref(null as RecognizeCheckTaskSaveParams | null);
+const curRow = ref<RecognizeCheckListItem | null>(null);
 
 const { dataList, pagination, loading, getList, toPage, deletePageLastItem } =
   useTable<RecognizeCheckListItem>(recognizeCheckListPage, searchModel, false);

+ 9 - 5
src/render/views/ScanManage/ImageView.vue

@@ -68,6 +68,7 @@
         <template #three>
           <div class="accordion-body">
             <QuestionPanel
+              v-if="dataCheckStore.curStudent && dataCheckStore.curPage"
               v-model:questions="questions"
               :info="questionInfo"
               :simple="isSliceImage"
@@ -103,6 +104,7 @@ import {
   batchStudentList,
   getStuCardDetail,
 } from "@/ap/scanManage";
+import { QuestionInfo } from "../DataCheck/types";
 
 const imageTypeOptions = enum2Options(IMAGE_TYPE);
 const dataCheckStore = useDataCheckStore();
@@ -293,12 +295,14 @@ const isSliceImage = computed(() => {
 
 // question panel
 const questionInfo = computed(() => {
+  if (!dataCheckStore.curStudent) return {} as QuestionInfo;
   return {
-    examNumber: dataCheckStore.curStudent?.examNumber,
-    name: dataCheckStore.curStudent?.name,
-    examSite: dataCheckStore.curStudent?.examSite,
-    seatNumber: dataCheckStore.curStudent?.seatNumber,
-    paperType: dataCheckStore.curStudent?.paperType,
+    examNumber: dataCheckStore.curStudent.examNumber,
+    name: dataCheckStore.curStudent.name,
+    examSite: dataCheckStore.curStudent.examSite,
+    seatNumber: dataCheckStore.curStudent.seatNumber,
+    paperType: dataCheckStore.curStudent.paperType,
+    examStatus: dataCheckStore.curStudent.examStatus as string,
   };
 });
 

+ 0 - 5
types/global.d.ts

@@ -14,9 +14,4 @@ declare global {
   }
 
   type FormRules<T extends string> = Partial<Record<T, Rule[]>>;
-
-  interface SelectOption {
-    value: string | number | boolean;
-    label: string;
-  }
 }