刘洋 8 luni în urmă
părinte
comite
e2c8ea7c56

+ 4 - 1
src/render/components/ImportButton/index.vue

@@ -5,8 +5,9 @@
     :custom-request="customRequest"
     :disabled="disabled || loading"
     :before-upload="beforeUpload"
-    :file-list="fileList"
+    v-model:file-list="fileList"
     @change="fileChange"
+    :accept="accept"
   >
     <qm-button :loading="loading" :icon="h(ImportOutlined)">
       {{ btnText }}
@@ -29,6 +30,7 @@ const props = withDefaults(
       disabled?: boolean;
       fileParamsName?: string;
       btnText?: string;
+      accept?: string;
       uploadParams?: Record<string, any> | Ref | Reactive<any>;
     } & UploadProps
   >(),
@@ -37,6 +39,7 @@ const props = withDefaults(
     fileParamsName: "file",
     disabled: false,
     btnText: "点击导入",
+    accept: ".*",
     uploadParams: () => ({}),
   }
 );

+ 11 - 10
src/render/store/modules/user/index.ts

@@ -22,7 +22,7 @@ interface UserInfo {
 interface UserState {
   heatBeatWorker: Worker | null;
   heatBeatIsActive: boolean;
-  curExam: Exam;
+  curExam: Exam | null;
   imageCheckLoopTime: number;
   userInfo: UserInfo | null;
   recogFillSet: RecogFillSetType;
@@ -43,14 +43,15 @@ export const useUserStore = defineStore("user", {
     heatBeatWorker: null,
     heatBeatIsActive: false,
     userInfo: null,
-    curExam: {
-      enable: true,
-      id: 0,
-      mode: "",
-      name: "",
-      schoolName: "",
-      updateTime: 0,
-    },
+    // curExam: {
+    //   enable: true,
+    //   id: 0,
+    //   mode: "",
+    //   name: "",
+    //   schoolName: "",
+    //   updateTime: 0,
+    // },
+    curExam: null,
     imageCheckLoopTime: 0,
     recogFillSet: {
       fillColor: "#f53f3f ",
@@ -83,7 +84,7 @@ export const useUserStore = defineStore("user", {
         this.heatBeatWorker?.postMessage(true);
       }
     },
-    setCurExam(exam: Exam) {
+    setCurExam(exam: Exam | null) {
       this.curExam = exam;
     },
     setImageCheckLoopTime(imageCheckLoopTime: number) {

+ 31 - 4
src/render/views/BaseDataConfig/ScanParams.vue

@@ -72,14 +72,14 @@
           :icon="h(EditOutlined)"
           class="mr-10px"
           v-if="!canEdit"
-          @click="canEdit = !canEdit"
+          @click="willEdit"
           >编辑</qm-button
         >
         <qm-button
           :icon="h(EditOutlined)"
           class="mr-10px"
           v-else
-          @click="canEdit = !canEdit"
+          @click="cancelEdit"
           >取消编辑</qm-button
         >
         <qm-button type="primary" :icon="h(SaveOutlined)" @click="save"
@@ -114,6 +114,7 @@ const getData = () => {
 onMounted(() => {
   getData();
 });
+let oldTags: string[] = [];
 const params = reactive<any>({
   paperTypeBarcodeContentItem: "",
   paperTypeBarcodeContent: [],
@@ -226,8 +227,26 @@ const rules = {
       },
     },
   ],
-  imageCheckRatio: [{ required: true, message: "请输入抽查比例" }],
-  scannerAssignedMaxCount: [{ required: true, message: "请输入锁屏数量" }],
+  imageCheckRatio: [
+    { required: true, message: "请输入抽查比例" },
+    {
+      validator: async () => {
+        if (params.imageCheckRatio == 0) {
+          return Promise.reject("抽查比例不能为0");
+        }
+      },
+    },
+  ],
+  scannerAssignedMaxCount: [
+    { required: true, message: "请输入锁屏数量" },
+    {
+      validator: async () => {
+        if (params.scannerAssignedMaxCount == 0) {
+          return Promise.reject("锁屏数量不能为0");
+        }
+      },
+    },
+  ],
   scannerAssignedVerifyPassword: [
     { required: true, message: "请输入解锁密码" },
   ],
@@ -242,6 +261,14 @@ const save = () => {
     });
   });
 };
+const willEdit = () => {
+  oldTags = JSON.parse(JSON.stringify(params.paperTypeBarcodeContent));
+  canEdit.value = !canEdit.value;
+};
+const cancelEdit = () => {
+  params.paperTypeBarcodeContent = oldTags;
+  canEdit.value = !canEdit.value;
+};
 </script>
 <style lang="less" scoped>
 .scan-params {

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

@@ -7,7 +7,13 @@
         </div>
       </template>
     </qm-low-form>
-    <a-table :data-source="dataList" :columns="columns" size="middle" bordered>
+    <a-table
+      :data-source="dataList"
+      :columns="columns"
+      size="middle"
+      bordered
+      :pagination="false"
+    >
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'operation'">
           <qm-button type="link" @click="openImportDialog(record)"
@@ -27,6 +33,7 @@
       v-model="showStuImportFileDialog"
       v-if="showStuImportFileDialog"
       :curRow="curRow"
+      @success="search"
     >
     </StuImportFileDialog>
   </div>

+ 6 - 1
src/render/views/BaseDataConfig/StuImportFileDialog.vue

@@ -13,7 +13,11 @@
       ref="form"
     >
       <template #file>
-        <ImportButton @fileChange="getFile" :maxSize="200 * 1024 * 1024" />
+        <ImportButton
+          @fileChange="getFile"
+          :maxSize="200 * 1024 * 1024"
+          accept=".txt"
+        />
       </template>
       <template #download>
         <qm-button :icon="h(VerticalAlignBottomOutlined)" @click="downloadTpl"
@@ -89,6 +93,7 @@ const watchProgress = (obj: { taskId: string }) => {
       progressStatus.value = "success";
       errMsg.value = "";
       window.$message.success("导入成功");
+      emit("success");
     } else if (res?.status === "FAILED") {
       progressStatus.value = "exception";
       errMsg.value = res?.errMsg || "";

+ 3 - 0
src/render/views/CurExam/AddExamDialog.vue

@@ -21,6 +21,7 @@ import { setValueFromObj } from "@/utils/tool";
 import { addOrEditExam } from "@/ap/baseDataConfig";
 import { useUserStore } from "@/store";
 
+const emit = defineEmits(["addSuccess"]);
 const userStore = useUserStore();
 const form = ref();
 const IS_EDIT = computed(() => !!props.curRow?.id);
@@ -51,6 +52,8 @@ const handleOk = () => {
             obj.name = params.value.name;
             userStore.setCurExam(obj);
           }
+        } else {
+          emit("addSuccess");
         }
       }
     );

+ 26 - 13
src/render/views/CurExam/index.vue

@@ -94,15 +94,15 @@
                 <p>
                   已扫描:{{ allData.scan?.scannedCount }},未扫描:{{
                     allData.scan?.unexistCount
-                  }},完成比:{{ allData.scan?.scannedRate }}
+                  }},完成比:{{ allData.scan?.scannedRate }}%
                 </p>
               </div>
               <div class="option">
                 图片审核:
                 <p>
-                  已扫描:{{ allData.scan?.imageCheckCount }},未扫描:{{
+                  已完成:{{ allData.scan?.imageCheckCount }},未完成:{{
                     allData.scan?.imageCheckTodoCount
-                  }},完成比:{{ allData.scan?.imageCheckRate }}
+                  }},完成比:{{ allData.scan?.imageCheckRate }}%
                 </p>
               </div>
             </div>
@@ -134,7 +134,7 @@
                     allData.assignedCheck?.auditorFinishCount
                   }},待完成:{{
                     allData.assignedCheck?.auditorTodoCount
-                  }},完成比:{{ allData.assignedCheck?.auditorFinishRate }}
+                  }},完成比:{{ allData.assignedCheck?.auditorFinishRate }}%
                 </p>
               </div>
               <div class="option">
@@ -144,7 +144,7 @@
                     allData.assignedCheck?.adminFinishCount
                   }},待完成:{{
                     allData.assignedCheck?.adminTodoCount
-                  }},完成比:{{ allData.assignedCheck?.adminFinishRate }}
+                  }},完成比:{{ allData.assignedCheck?.adminFinishRate }}%
                 </p>
               </div>
             </div>
@@ -172,7 +172,7 @@
                 <p>
                   待处理:{{ allData.omr?.suspectTodoCount }},已处理:{{
                     allData.omr?.suspectFinishCount
-                  }},完成比:{{ allData.omr?.suspectFinishRate }}
+                  }},完成比:{{ allData.omr?.suspectFinishRate }}%
                 </p>
               </div>
               <div class="option">
@@ -180,7 +180,7 @@
                 <p>
                   待处理:{{ allData.omr?.customizeTodoCount }},已处理:{{
                     allData.omr?.customizeFinishCount
-                  }},完成比:{{ allData.omr?.customizeFinishRate }}
+                  }},完成比:{{ allData.omr?.customizeFinishRate }}%
                 </p>
                 <p>待生成:</p>
               </div>
@@ -280,6 +280,7 @@
       v-if="showExamListModal"
       title="切换考试"
       @ok="chooseExamHandler"
+      :width="450"
     >
       <a-radio-group v-model:value="choosedExamId" class="exam-modal-body">
         <a-radio
@@ -289,7 +290,7 @@
           :value="item.id"
         >
           <div class="flex-1 flex items-center justify-between">
-            <span class="sub1">{{ item?.id }}</span>
+            <span class="sub1">No.{{ item?.id }}</span>
             <span class="sub2">{{ item?.name }}</span>
             <span class="sub3">{{ item?.mode }}</span>
           </div>
@@ -300,6 +301,7 @@
       v-model="showAddDialog"
       v-if="showAddDialog"
       :curRow="curRow"
+      @addSuccess="addSuccess"
     ></AddExamDialog>
   </div>
 </template>
@@ -330,6 +332,7 @@ const radioStyle = reactive({
   display: "flex",
   alignItems: "center",
   justifyContent: "between",
+  marginBottom: "8px",
 });
 
 const userStore = useUserStore();
@@ -339,7 +342,7 @@ const curExam = computed(() => {
 const choosedExamId = ref();
 const allData = ref<any>({});
 const getAllCardData = () => {
-  getExamOverview({ examId: choosedExamId.value }).then((res: any) => {
+  getExamOverview({ examId: curExam.value.id }).then((res: any) => {
     allData.value = res || {};
   });
 };
@@ -349,11 +352,9 @@ if (curExam.value?.id) {
 }
 watch(curExam, (exam: Exam | null) => {
   choosedExamId.value = exam?.id;
-});
-
-watch(choosedExamId, (val: number | string) => {
   getAllCardData();
 });
+
 const examList = ref<Exam[]>([]);
 const showExamListModal = ref(false);
 const _getExamList = () => {
@@ -363,6 +364,7 @@ const _getExamList = () => {
         examList.value = res.result || [];
       } else {
         examList.value = [];
+        userStore.setCurExam(null);
       }
       if (!userStore.curExam && examList.value.length === 1) {
         userStore.setCurExam(examList.value[0]);
@@ -370,7 +372,15 @@ const _getExamList = () => {
     }
   );
 };
-
+const addSuccess = () => {
+  getExamList({ enable: true, pageNumber: 1, pageSize: 10000 }).then(
+    (res: any) => {
+      if (res?.result?.length) {
+        userStore.setCurExam(res.result[res.result.length - 1]);
+      }
+    }
+  );
+};
 const chooseExamHandler = () => {
   let exam = examList.value.find((item: any) => item.id == choosedExamId.value);
   !!exam && userStore.setCurExam(exam);
@@ -391,6 +401,9 @@ onMounted(() => {
 watch(showExamListModal, (val: boolean) => {
   if (val) {
     _getExamList();
+    if (curExam.value?.id) {
+      choosedExamId.value = curExam.value.id;
+    }
   }
 });
 </script>

+ 52 - 13
src/render/views/Login/IpSet.vue

@@ -5,24 +5,52 @@
         <div class="title">设置</div>
         <div class="tip">输入服务器IP地址</div>
         <div class="flex items-center">
-          <a-input v-model:value="ipData.ip1" class="ip-num" />
+          <a-input
+            v-model:value="ipData.ip1"
+            class="ip-num"
+            @input="changeFocus(ipData.ip1, input2)"
+          />
           <span class="dian">●</span>
-          <a-input v-model:value="ipData.ip2" class="ip-num" />
+          <a-input
+            v-model:value="ipData.ip2"
+            class="ip-num"
+            @input="changeFocus(ipData.ip2, input3)"
+            ref="input2"
+          />
           <span class="dian">●</span>
-          <a-input v-model:value="ipData.ip3" class="ip-num" />
+          <a-input
+            v-model:value="ipData.ip3"
+            class="ip-num"
+            @input="changeFocus(ipData.ip3, input4)"
+            ref="input3"
+          />
           <span class="dian">●</span>
-          <a-input v-model:value="ipData.ip4" class="ip-num" />
+          <a-input
+            v-model:value="ipData.ip4"
+            class="ip-num"
+            @input="changeFocus(ipData.ip4, input5)"
+            ref="input4"
+          />
           <img class="maohao" src="../../assets/imgs/maohao.png" />
-          <a-input v-model:value="ipData.port" class="ip-num" />
+          <a-input v-model:value="ipData.port" class="ip-num" ref="input5" />
         </div>
         <div class="pt-20px flex items-center">
-          <!-- <a-tag color="success" class="tag check-tag" v-if="showCheckResult">
+          <qm-button @click="checkClicked = true">验证</qm-button>
+          <a-tag
+            color="success"
+            class="tag check-tag"
+            v-if="checkClicked && IPV4Single()"
+          >
             <template #icon>
               <check-circle-filled />
             </template>
             验证通过
-          </a-tag> -->
-          <a-tag color="error" class="tag check-tag" v-if="showCheckResult">
+          </a-tag>
+          <a-tag
+            color="error"
+            class="tag check-tag"
+            v-if="checkClicked && !IPV4Single()"
+          >
             <template #icon>
               <close-circle-filled />
             </template>
@@ -32,7 +60,10 @@
       </div>
       <div class="level2">
         <div class="text-right">
-          <qm-button type="primary" @click="nextStep"
+          <qm-button
+            type="primary"
+            :disabled="!(checkClicked && IPV4Single())"
+            @click="nextStep"
             >下一步 <template #icon> <SwapRightOutlined /> </template
           ></qm-button>
         </div>
@@ -51,6 +82,10 @@ import {
 import FooterInfo from "@/components/FooterInfo/index.vue";
 import { local } from "@/utils/tool";
 
+const input2 = ref();
+const input3 = ref();
+const input4 = ref();
+const input5 = ref();
 const emit = defineEmits(["next"]);
 const ipData = reactive({
   ip1: "",
@@ -59,6 +94,11 @@ const ipData = reactive({
   ip4: "",
   port: "",
 });
+const changeFocus = (val: string, el: any) => {
+  if (val?.length == 3) {
+    el.focus();
+  }
+};
 const checkClicked = ref(false);
 
 const IPV4Single = () => {
@@ -68,12 +108,11 @@ const IPV4Single = () => {
   return IPV4.test(ipStr);
 };
 
-const showCheckResult = computed(() => {
-  return checkClicked.value && !IPV4Single();
-});
+// const showCheckResult = computed(() => {
+//   return checkClicked.value && !IPV4Single();
+// });
 
 const nextStep = () => {
-  checkClicked.value = true;
   if (IPV4Single()) {
     local.set(
       "baseUrl",