刘洋 hai 8 meses
pai
achega
e7a8a6c328

+ 1 - 0
src/render/ap/baseDataConfig.ts

@@ -126,6 +126,7 @@ export const clearStuData = (params: { examId: number; subjectCode: string }) =>
 export const importStu = async (params: {
   examId: number;
   file: File | null;
+  subjectCode: string;
 }) => {
   const md5 = await getFileMD5(params.file as File);
   const formData = obj2formData(params);

+ 8 - 0
src/render/ap/recognizeCheck.ts

@@ -33,6 +33,14 @@ export const recognizeConditionsList = (): Promise<RecognizeConditionItem[]> =>
     url: "/api/admin/check/omr/conditions",
     method: "post",
   });
+// 所有可用的识别对照条件
+export const recognizeConditionsListAll = (): Promise<
+  RecognizeConditionItem[]
+> =>
+  request({
+    url: "/api/admin/check/omr/conditions/all",
+    method: "post",
+  });
 // 创建/修改识别对照任务组
 export const recognizeCheckTaskSave = (
   data: RecognizeCheckTaskSaveParams

+ 3 - 2
src/render/components/VerticalDateRange/index.vue

@@ -55,8 +55,9 @@ const props = withDefaults(
 const time = useVModel(props);
 const _time = ref([]);
 
-const startTime = ref();
-const endTime = ref();
+const startTime = ref(dayjs(time.value[0]));
+const endTime = ref(dayjs(time.value[1]));
+
 const showTimeStart =
   props.type === "datetime"
     ? { defaultValue: dayjs("00:00:00", "HH:mm:ss") }

+ 1 - 0
src/render/utils/request.ts

@@ -118,6 +118,7 @@ function createService() {
             message.error("登录状态已过期");
             userStore.setUserInfo(null);
             router.replace({ name: "Login" });
+            window.electronApi.changeWinSize("small");
             break;
           case 403:
             err(`没有权限访问该资源`);

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

@@ -59,7 +59,7 @@
           </template>
           <a-button
             class="ant-btn-success"
-            :disabled="canSubmit"
+            :disabled="!canSubmit"
             :loading="loading"
             @click="onConfirm(true)"
           >
@@ -79,7 +79,7 @@
     </div>
     <div class="audit-body">
       <template v-if="curStudent">
-        <div class="audit-body-imgs">
+        <div class="audit-body-imgs" ref="auditBodyImgs">
           <div v-for="paper in curStudent.papers" :key="paper.number">
             <img
               v-for="(page, pindex) in paper.pages"
@@ -137,7 +137,7 @@ import useLoading from "@/hooks/useLoading";
 defineOptions({
   name: "IntimeAudit",
 });
-
+const auditBodyImgs = ref();
 const userStore = useUserStore();
 
 interface StudentItem extends AuditBatchStudent {
@@ -151,7 +151,8 @@ const batchInfo = ref({} as AuditBatchData);
 const hasTask = ref(false);
 
 const canSubmit = computed(() => {
-  return !dataList.value.some((item) => !item.status);
+  // return !dataList.value.some((item) => !item.status);
+  return dataList.value.every((item) => !!item.status);
 });
 
 function initData() {
@@ -175,7 +176,27 @@ async function getData() {
   stopLoopGetData();
   hasTask.value = true;
   batchInfo.value = omit(res, "students");
-  dataList.value = (res.students || []).map((item) => {
+  dataList.value = [];
+  let arr = res.students || [];
+  for (let i = 0; i < arr.length; i++) {
+    let stu: any = arr[i];
+    stu.papers = (stu.papers || []).filter((v: any) => v.assgined);
+    for (let j = 0; j < stu.papers.length; j++) {
+      stu.papers[j].pages = (stu.papers[j].pages || []).filter(
+        (page: string) => {
+          let url = page.split(".")[0];
+          let indexStr = url[url.length - 1];
+          let indexNum = Number(indexStr);
+          if (!isNaN(indexNum) && indexNum % 2 == 0) {
+            return false;
+          } else {
+            return true;
+          }
+        }
+      );
+    }
+  }
+  dataList.value = arr.map((item) => {
     return { ...item, status: false };
   });
   setCurStudent(0);
@@ -191,6 +212,9 @@ function onMark() {
 function setCurStudent(index: number) {
   curStudentIndex.value = index;
   curStudent.value = dataList.value[curStudentIndex.value];
+  setTimeout(() => {
+    auditBodyImgs.value.scrollTop = 0;
+  });
 }
 function getNextStudent() {
   if (curStudentIndex.value === dataList.value.length - 1) return;

+ 5 - 1
src/render/views/Audit/Main/index.vue

@@ -38,7 +38,11 @@
 
         <div class="audit-box">
           <div class="audit-box-head">
-            <h4>复核校验</h4>
+            <h4>
+              复核校验<span style="color: red; font-weight: normal"
+                >(人工绑定审核)</span
+              >
+            </h4>
           </div>
           <div class="audit-box-body">
             <div class="audit-card">

+ 108 - 81
src/render/views/BaseDataConfig/ScanParams.vue

@@ -15,12 +15,13 @@
               style="width: 200px"
               v-model:value="params.paperTypeBarcodeContentItem"
               allow-clear
+              :disabled="!canEdit"
             />
             <qm-button
               class="m-l-8px"
               :icon="h(PlusCircleOutlined)"
               @click="addTag"
-              :disabled="!params.paperTypeBarcodeContentItem"
+              :disabled="!params.paperTypeBarcodeContentItem || !canEdit"
               >新增</qm-button
             >
           </div>
@@ -35,7 +36,9 @@
               :style="{ color: token.colorPrimary }"
             >
               {{ item }}
-              <span class="close" @click="delTag(index)">&times;</span>
+              <span class="close" v-if="canEdit" @click="delTag(index)"
+                >&times;</span
+              >
             </div>
           </div>
         </template>
@@ -46,6 +49,7 @@
               v-model:value="params.imageCheckRatio"
               :max="100"
               :min="0"
+              :disabled="!canEdit"
             />
             <span>%</span>
           </div>
@@ -56,6 +60,7 @@
               v-model:value="params.scannerAssignedMaxCount"
               :max="100"
               :min="0"
+              :disabled="!canEdit"
             />
             <span>张</span>
           </div>
@@ -63,22 +68,37 @@
       </qm-low-form>
 
       <div class="btns">
-        <!-- <qm-button :icon="h(EditOutlined)">编辑</qm-button> -->
-        <qm-button type="primary" @click="save">保存</qm-button>
+        <qm-button
+          :icon="h(EditOutlined)"
+          class="mr-10px"
+          v-if="!canEdit"
+          @click="canEdit = !canEdit"
+          >编辑</qm-button
+        >
+        <qm-button
+          :icon="h(EditOutlined)"
+          class="mr-10px"
+          v-else
+          @click="canEdit = !canEdit"
+          >取消编辑</qm-button
+        >
+        <qm-button type="primary" :icon="h(SaveOutlined)" @click="save"
+          >保存</qm-button
+        >
       </div>
     </div>
   </div>
 </template>
 <script name="ScanParams" lang="ts" setup>
-import { ref, h, onMounted, reactive } from "vue";
-import { PlusCircleOutlined } from "@ant-design/icons-vue";
+import { ref, h, onMounted, reactive, computed } from "vue";
+import { PlusCircleOutlined, SaveOutlined } from "@ant-design/icons-vue";
 import useToken from "@/hooks/useToken";
 import { EditOutlined } from "@ant-design/icons-vue";
 import { getBaseDataConfig, saveBaseDataConfig } from "@/ap/baseDataConfig";
 import { useUserStore } from "@/store";
 
 const form = ref();
-
+const canEdit = ref(false);
 const userStore = useUserStore();
 const { token } = useToken();
 const getData = () => {
@@ -103,81 +123,88 @@ const params = reactive<any>({
   scannerAssignedMaxCount: "",
   scannerAssignedVerifyPassword: "",
 });
-const fields = ref([
-  {
-    cell: "label1",
-    label: "卷型条码内容",
-    colSpan: 4,
-    labelWidth: 130,
-  },
-  {
-    prop: "paperTypeBarcodeContentItem",
-    cell: "paperTypeBarcodeContentItem",
-    label: "",
-    labelWidth: 0,
-    colSpan: 20,
-  },
-  {
-    cell: "label2",
-    label: "图片检查",
-    colSpan: 4,
-    labelWidth: 130,
-  },
-  {
-    prop: "imageCheckRatio",
-    cell: "imageCheckRatio",
-    label: "抽查比例",
-    colSpan: 10,
-  },
-  {
-    prop: "imageCheckOrder",
-    type: "select",
-    attrs: {
-      options: [
-        { value: "DESC", label: "最新扫描批次" },
-        { value: "ASC", label: "批次顺序" },
-      ],
+const fields = computed(() => {
+  return [
+    {
+      cell: "label1",
+      label: "卷型条码内容",
+      colSpan: 4,
+      labelWidth: 130,
+    },
+    {
+      prop: "paperTypeBarcodeContentItem",
+      cell: "paperTypeBarcodeContentItem",
+      label: "",
+      labelWidth: 0,
+      colSpan: 20,
+    },
+    {
+      cell: "label2",
+      label: "图片检查",
+      colSpan: 4,
+      labelWidth: 130,
     },
-    label: "检查顺序",
-    colSpan: 10,
-  },
-  {
-    cell: "label3",
-    label: "实时审核",
-    colSpan: 4,
-    labelWidth: 130,
-  },
-  {
-    prop: "enableSyncVerify",
-    label: "",
-    colSpan: 20,
-    type: "radio",
-    attrs: {
-      options: [
-        { label: "是", value: true },
-        { label: "否", value: false },
-      ],
+    {
+      prop: "imageCheckRatio",
+      cell: "imageCheckRatio",
+      label: "抽查比例",
+      colSpan: 10,
+    },
+    {
+      prop: "imageCheckOrder",
+      type: "select",
+      attrs: {
+        options: [
+          { value: "DESC", label: "最新扫描批次" },
+          { value: "ASC", label: "批次顺序" },
+        ],
+        disabled: !canEdit.value,
+      },
+      label: "检查顺序",
+      colSpan: 10,
+    },
+    {
+      cell: "label3",
+      label: "实时审核",
+      colSpan: 4,
+      labelWidth: 130,
     },
-  },
-  {
-    cell: "label3",
-    label: "人工绑定锁屏控制",
-    colSpan: 4,
-    labelWidth: 130,
-  },
-  {
-    prop: "scannerAssignedMaxCount",
-    cell: "scannerAssignedMaxCount",
-    label: "锁屏数量",
-    colSpan: 10,
-  },
-  {
-    prop: "scannerAssignedVerifyPassword",
-    type: "password",
-    colSpan: 10,
-    label: "解锁密码",
-  },
-]);
+    {
+      prop: "enableSyncVerify",
+      label: "",
+      colSpan: 20,
+      type: "radio",
+      attrs: {
+        options: [
+          { label: "是", value: true },
+          { label: "否", value: false },
+        ],
+        disabled: !canEdit.value,
+      },
+    },
+    {
+      cell: "label3",
+      label: "人工绑定锁屏控制",
+      colSpan: 4,
+      labelWidth: 130,
+    },
+    {
+      prop: "scannerAssignedMaxCount",
+      cell: "scannerAssignedMaxCount",
+      label: "锁屏数量",
+      colSpan: 10,
+    },
+    {
+      prop: "scannerAssignedVerifyPassword",
+      type: "password",
+      colSpan: 10,
+      label: "解锁密码",
+      attrs: {
+        disabled: !canEdit.value,
+      },
+    },
+  ];
+});
 
 const addTag = () => {
   params.paperTypeBarcodeContent.push(params.paperTypeBarcodeContentItem);
@@ -224,7 +251,7 @@ const save = () => {
     overflow: auto;
     .btns {
       padding-top: 60px;
-      padding-left: 150px;
+      text-align: right;
     }
     .ccbl {
       color: @text-color1;

+ 1 - 0
src/render/views/BaseDataConfig/StuImport.vue

@@ -26,6 +26,7 @@
     <StuImportFileDialog
       v-model="showStuImportFileDialog"
       v-if="showStuImportFileDialog"
+      :curRow="curRow"
     >
     </StuImportFileDialog>
   </div>

+ 11 - 8
src/render/views/BaseDataConfig/StuImportFileDialog.vue

@@ -39,6 +39,7 @@ import { importStu, exportStu, importStuProgress } from "@/ap/baseDataConfig";
 import { VerticalAlignBottomOutlined } from "@ant-design/icons-vue";
 import { useUserStore } from "@/store";
 
+const props = defineProps<{ curRow: any }>();
 const userStore = useUserStore();
 const form = ref();
 const visible = defineModel();
@@ -68,14 +69,16 @@ const getFile = (file: any) => {
 };
 const submitHandle = () => {
   form.value.formRef.validate().then(() => {
-    importStu({ examId: userStore.curExam?.id as number, ...params }).then(
-      (res: any) => {
-        let taskId = res.taskId;
-        showProgressDialog.value = true;
-        // visible.value = false;
-        watchProgress({ taskId });
-      }
-    );
+    importStu({
+      examId: userStore.curExam?.id as number,
+      ...params,
+      subjectCode: props.curRow?.subjectCode as string,
+    }).then((res: any) => {
+      let taskId = res.taskId;
+      showProgressDialog.value = true;
+      // visible.value = false;
+      watchProgress({ taskId });
+    });
   });
 };
 const watchProgress = (obj: { taskId: string }) => {

+ 7 - 2
src/render/views/CurExam/index.vue

@@ -34,7 +34,10 @@
         <div class="none-center text-center">
           <img src="../../assets/imgs/none_data.png" />
           <p>当前没有考试,请新建考试</p>
-          <qm-button :icon="h(PlusCircleOutlined)" type="primary"
+          <qm-button
+            :icon="h(PlusCircleOutlined)"
+            type="primary"
+            @click="addExam"
             >新建</qm-button
           >
         </div>
@@ -123,7 +126,9 @@
             </div>
             <div class="body">
               <div class="option">
-                审核员
+                审核员<span style="color: red; font-weight: normal"
+                  >(人工绑定审核)</span
+                >
                 <p>
                   已完成:{{
                     allData.assignedCheck?.auditorFinishCount

+ 6 - 5
src/render/views/Login/LoginWays.vue

@@ -7,7 +7,7 @@
         <div class="flex items-center justify-between">
           <div
             class="card"
-            @click="activeName = 'scan'"
+            @click="nextStep('scan')"
             :class="{ active: activeName === 'scan' }"
           >
             <img src="../../assets/imgs/scan_login_icon.png" />
@@ -16,7 +16,7 @@
           </div>
           <div
             class="card"
-            @click="activeName = 'admin'"
+            @click="nextStep('admin')"
             :class="{ active: activeName === 'admin' }"
           >
             <img src="../../assets/imgs/admin_login_icon.png" />
@@ -26,11 +26,11 @@
         </div>
       </div>
       <div class="level2">
-        <div class="text-right">
+        <!-- <div class="text-right">
           <qm-button type="primary" @click="nextStep"
             >下一步 <template #icon> <SwapRightOutlined /> </template
           ></qm-button>
-        </div>
+        </div> -->
         <footer-info></footer-info>
       </div>
     </div>
@@ -52,7 +52,8 @@ import { useAppStore } from "@/store";
 const emit = defineEmits(["next", "toIndex"]);
 const activeName = ref("scan");
 
-const nextStep = async () => {
+const nextStep = async (name: string) => {
+  activeName.value = name;
   if (activeName.value === "scan") {
     let deviceName = window.electronApi?.getComputerName?.();
     let device = await window.electronApi?.getUuid?.();

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

@@ -112,6 +112,7 @@ import {
   recognizeCheckBuildTask,
   recognizeCheckTaskStatusSave,
   recognizeConditionsList,
+  recognizeConditionsListAll,
 } from "@/ap/recognizeCheck";
 import { showConfirm } from "@/utils/uiUtils";
 import { useUserStore } from "@/store";
@@ -131,7 +132,7 @@ const route = useRoute();
 const conditionList = ref<RecognizeConditionItem[]>([]);
 const conditionMap = ref<Record<string, string>>({});
 async function getConditionList() {
-  const res = await recognizeConditionsList();
+  const res = await recognizeConditionsListAll();
 
   conditionList.value = res || [];
   conditionList.value.forEach((item) => {
@@ -211,7 +212,12 @@ function checkActionValid(index: number, type: ActionType): boolean {
     return record.stage === "SECOND" && record.unarbitrateCount > 0;
   }
   if (type === "edit") {
-    return record.totalCount === 0;
+    return (
+      record.totalCount === 0 &&
+      !(record.conditions || []).find(
+        (item: any) => item.code === "FILL_SUSPECT"
+      )
+    );
   }
   if (type === "reset") {
     return true;

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

@@ -105,6 +105,7 @@ import {
   getStuCardDetail,
 } from "@/ap/scanManage";
 import { QuestionInfo } from "../DataCheck/types";
+import dayjs from "dayjs";
 
 const imageTypeOptions = enum2Options(IMAGE_TYPE);
 const dataCheckStore = useDataCheckStore();
@@ -115,7 +116,7 @@ function onImageTypeChange() {
   });
 }
 
-const timeArr = ref([]);
+const timeArr = ref([Date.now() - 1000 * 60 * 60, Date.now()]);
 const timeParams = computed<any>(() => {
   return { startTime: timeArr.value[0], endTime: timeArr.value[1] };
 });
@@ -335,16 +336,26 @@ async function onQuestionsChange() {
 
 async function onPrevPage() {
   if (dataCheckStore.curPageIndex <= 0) {
-    window.$message.error("没有上一张了");
-    return;
+    if (activeIndex.value == 0) {
+      window.$message.error("没有上一张了");
+      return;
+    } else {
+      chooseLeft(leftList.value[activeIndex.value - 1], activeIndex.value - 1);
+      return;
+    }
   }
   selectPage(dataCheckStore.curPageIndex - 1);
 }
 
 async function onNextPage() {
   if (dataCheckStore.curPageIndex >= dataList.value.length - 1) {
-    window.$message.error("没有下一张了");
-    return;
+    if (activeIndex.value == leftList.value.length - 1) {
+      window.$message.error("没有下一张了");
+      return;
+    } else {
+      chooseLeft(leftList.value[activeIndex.value + 1], activeIndex.value + 1);
+      return;
+    }
   }
 
   selectPage(dataCheckStore.curPageIndex + 1);

+ 7 - 2
src/render/views/ScanManage/StuInfo.vue

@@ -16,6 +16,11 @@
       :loading="loading"
       :pagination="pagination"
     >
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.dataIndex === 'subjectName'">
+          {{ record.subjectCode + "-" + record[column.dataIndex] }}
+        </template>
+      </template>
     </a-table>
   </div>
 </template>
@@ -131,7 +136,7 @@ const fields = computed(() => {
 const columns: TableColumnsType = [
   {
     title: "科目",
-    dataIndex: "subjectCode",
+    dataIndex: "subjectName",
   },
   {
     title: "考号",
@@ -163,7 +168,7 @@ const columns: TableColumnsType = [
   },
   {
     title: "扫描账号",
-    dataIndex: "device",
+    dataIndex: "deviceName",
   },
 ];
 </script>