zhangjie 1 mese fa
parent
commit
4cd68108f9

+ 11 - 0
src/render/styles/pages.less

@@ -835,6 +835,17 @@
     background-color: @background-color;
   }
 }
+.exam-number-check {
+  .check-menu {
+    &-body {
+      li {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+      }
+    }
+  }
+}
 
 // recog-edit-dialog
 .recog-edit-dialog {

+ 22 - 1
src/render/views/ExamNumberCheck/CheckAction.vue

@@ -7,6 +7,22 @@
     >
       <a-collapse-panel key="1">
         <template #header><FilterFilled />搜索条件 </template>
+        <a-form :label-col="{ style: { width: '83px' } }">
+          <a-form-item label="图片名称">
+            <a-input
+              v-model:value="searchModel.imageName"
+              placeholder="请输入图片名称"
+              allow-clear
+            ></a-input>
+          </a-form-item>
+          <a-form-item label="是否异常">
+            <SelectBoolean
+              v-model:value="searchModel.isCheck"
+              style="width: 85px"
+              :allow-clear="false"
+            />
+          </a-form-item>
+        </a-form>
         <div>
           <a-button class="m-r-8px" type="primary" @click="onSearch"
             >查询</a-button
@@ -66,9 +82,14 @@ const dataCheckStore = useDataCheckStore();
 const { save } = useUpload();
 
 const panelKey = ref(["1", "2"]);
+const searchModel = reactive<AllCheckFilter>({ imageName: "", isCheck: true });
 
 function onSearch() {
-  emit("search");
+  // if (!searchModel.isCheck && !searchModel.imageName) {
+  //   message.warning("请输入图片名称");
+  //   return;
+  // }
+  emit("search", searchModel);
 }
 
 // question panel

+ 1 - 1
src/render/views/ExamNumberCheck/EditExamNumberDialog.vue

@@ -1,5 +1,5 @@
 <template>
-  <a-drawer v-model:open="visible" :width="396" title="输入准考证号">
+  <a-drawer v-model:open="visible" :width="380" title="输入准考证号">
     <a-form ref="formRef" :label-col="{ style: { width: '80px' } }">
       <a-form-item label="准考证号">
         <div class="exam-number" style="margin-bottom: 10px">

+ 36 - 2
src/render/views/ExamNumberCheck/QuestionPanel.vue

@@ -2,7 +2,11 @@
   <div class="question-panel">
     <a-form :label-col="{ style: { width: '80px' } }">
       <a-form-item label="准考证号">
-        <a-button class="ant-gray m-r-4px">{{ info.examNumber }}</a-button>
+        <a-button
+          class="ant-gray m-r-4px"
+          :danger="!checkExamNumber(info.examNumber)"
+          >{{ info.examNumber }}</a-button
+        >
         <a-button @click="onEditPaperType">
           <template #icon><SwapOutlined /></template>
         </a-button>
@@ -23,7 +27,11 @@
       <div
         v-for="(item, index) in questionList"
         :key="index"
-        :class="['question-item', `question-item-${index}`]"
+        :class="[
+          'question-item',
+          `question-item-${index}`,
+          { 'is-error': checkQuestionError(item) },
+        ]"
       >
         <span>{{ getQuestionNo(index) }}:</span>
         <a-button
@@ -124,6 +132,17 @@ function getQuestionNo(index: number) {
   const no = index + 1;
   return no < 10 ? `0${no}` : `${no}`;
 }
+function checkQuestionError(cont: string) {
+  if (!cont) return true;
+  if (cont.length > 1) return true;
+  return false;
+}
+
+function checkExamNumber(examNumber: string) {
+  if (!examNumber) return false;
+  if (examNumber.includes(".")) return false;
+  return true;
+}
 
 function getQuesionCont(cont: string) {
   if (!cont) return "#";
@@ -220,6 +239,11 @@ onMounted(() => {});
 
 <style lang="less" scoped>
 .question-panel {
+  .ant-btn.ant-btn-dangerous {
+    color: @error-color;
+    border-color: @error-color;
+    background-color: #fff2f0;
+  }
   .panel-body {
     margin: 15px -14px 0;
     padding: 0 14px;
@@ -246,6 +270,16 @@ onMounted(() => {});
       font-size: 14px;
       margin-bottom: 12px;
 
+      &.is-error {
+        color: @error-color;
+
+        .ant-btn.ant-gray {
+          border-color: @error-color;
+          color: @error-color;
+          background-color: #fff2f0;
+        }
+      }
+
       > span {
         display: inline-block;
         width: 30px;

+ 53 - 3
src/render/views/ExamNumberCheck/api.ts

@@ -6,9 +6,21 @@ import Papa from "papaparse";
 
 const appStore = useAppStore();
 
-export const allCheckList = async (): Promise<DataCheckListResult> => {
-  const url = `${appStore.serverUrl}/check.csv?${randomCode()}`;
-  const students = await fetchAndParseData(url);
+export const allCheckList = async (
+  params: AllCheckFilter
+): Promise<DataCheckListResult> => {
+  // if (!params.isCheck && !params.imageName) {
+  //   return Promise.resolve([]);
+  // }
+  console.log(params);
+
+  const fname = params.isCheck ? "check" : "success";
+  const url = `${appStore.serverUrl}/${fname}.csv?${randomCode()}`;
+  let students = await fetchAndParseData(url);
+  if (params.imageName) {
+    students = students.filter((item) => item.imageName === params.imageName);
+  }
+  const cacheList = await fetchCacheList();
   const data = (students || []).map((item: any) => {
     const studentId = `${item.imageName}_${randomCode(8)}`;
     const questions = (item.smda ? item.smda.split("|") : []).map((item) =>
@@ -18,6 +30,7 @@ export const allCheckList = async (): Promise<DataCheckListResult> => {
       ...item,
       id: studentId,
       examNumber: item.zkzh,
+      checked: cacheList.includes(item.imageName),
       papers: [
         {
           id: `${studentId}_1`,
@@ -45,6 +58,43 @@ export const allCheckList = async (): Promise<DataCheckListResult> => {
   return Promise.resolve(data);
 };
 
+export const getExamNumberInfo = async (
+  imageName: string
+): Promise<DataCheckListResult> => {
+  const url = `${appStore.serverUrl}/cache/${imageName}.csv?${randomCode()}`;
+  const students = await fetchAndParseData(url).catch(() => []);
+  if (!students || students.length === 0) {
+    return Promise.resolve(null);
+  }
+  const item = students[0];
+  const questions = (item.smda ? item.smda.split("|") : []).map((item) =>
+    item.trim().replace(/[\.\?]/g, "")
+  );
+  return Promise.resolve({ ...students[0], questions });
+};
+
+export const fetchCacheList = async () => {
+  try {
+    const res = await axios.get(`${appStore.serverUrl}/cache/`);
+    const parser = new DOMParser();
+    const doc = parser.parseFromString(res.data, "text/html");
+    const links = Array.from(doc.querySelectorAll("a"))
+      .map((a) => a.getAttribute("href"))
+      .filter((href) => href && /\/cache\/.*\.csv$/.test(href))
+      .map((href) => {
+        // Extract filename without extension
+        const parts = href.split("/");
+        const filenameWithExtension = parts[parts.length - 1];
+        return filenameWithExtension.replace(".csv", "");
+      });
+
+    // console.log("缓存文件列表:", links);
+    return links; // Return the extracted list
+  } catch (err) {
+    console.error("获取失败:", err);
+  }
+};
+
 export const saveCheck = async (data: string[][], filename): Promise<any> => {
   const url = `${appStore.serverUrl}/upload?path=/cache`;
   return uploadCsvData(url, data, filename);

+ 38 - 21
src/render/views/ExamNumberCheck/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="data-check">
+  <div class="data-check exam-number-check">
     <div class="check-menu">
       <div class="check-menu-body">
         <ul>
@@ -9,7 +9,10 @@
             :class="{ 'is-active': dataCheckStore.curStudent?.id === item.id }"
             @click="onSelectStudent(index)"
           >
-            {{ item.imageName }}
+            <span>{{ item.imageName }}</span>
+            <span v-if="item.checked" class="color-success"
+              ><CheckCircleFilled
+            /></span>
           </li>
         </ul>
       </div>
@@ -39,11 +42,15 @@
 <script setup lang="ts">
 import { ref, reactive, onMounted, computed, onBeforeUnmount } from "vue";
 import { message } from "ant-design-vue";
-import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons-vue";
+import {
+  CaretLeftOutlined,
+  CaretRightOutlined,
+  CheckCircleFilled,
+} from "@ant-design/icons-vue";
 
 import { DataCheckListFilter, DataCheckListItem } from "@/ap/types/dataCheck";
-import { allCheckList, fetchAndParseData } from "./api";
-import { StudentPage } from "./types";
+import { allCheckList, fetchAndParseData, getExamNumberInfo } from "./api";
+import { StudentPage, AllCheckFilter } from "./types";
 import { useDataCheckStore, useAppStore } from "@/store";
 
 import SimplePagination from "@/components/SimplePagination/index.vue";
@@ -68,9 +75,9 @@ const studentList = ref<DataCheckListItem[]>([]);
 const dataList = ref<StudentPage[]>([]);
 const loading = ref(false);
 
-async function getAllStudents() {
+async function getAllStudents(data: AllCheckFilter) {
   loading.value = true;
-  const res = await allCheckList().catch(() => null);
+  const res = await allCheckList(data).catch(() => null);
   loading.value = false;
   if (!res) return;
 
@@ -126,21 +133,31 @@ function onChangeListPage(index: number) {
   getList();
   selectPage(0);
 }
-async function onSearch() {
+async function onSearch(data: AllCheckFilter) {
   pageNumber.value = 1;
-  await getAllStudents();
-  onChangeListPage(1);
+  await getAllStudents(data);
+  getList();
+  await onSelectStudent(0);
 }
 
 // student
-function onSelectStudent(index: number) {
+async function onSelectStudent(index: number) {
   const student = studentList.value[index];
+  if (!student) return;
+  if (student.checked) {
+    const info = await getExamNumberInfo(student.imageName);
+    if (info) {
+      student.examNumber = info.zkzh;
+      student.papers[0].pages[0].question.result = info.questions;
+    }
+  }
+
   const pageIndex = dataList.value.findIndex(
     (item) => item.studentId === student.id
   );
   if (pageIndex === -1) return;
 
-  selectPage(pageIndex);
+  await selectPage(pageIndex);
 }
 
 async function onPrevStudent() {
@@ -152,11 +169,11 @@ async function onPrevStudent() {
 
     pageNumber.value--;
     await getList();
-    onSelectStudent(studentList.value.length - 1);
+    await onSelectStudent(studentList.value.length - 1);
     return;
   }
 
-  onSelectStudent(dataCheckStore.curStudentIndex - 1);
+  await onSelectStudent(dataCheckStore.curStudentIndex - 1);
 }
 
 async function onNextStudent() {
@@ -168,11 +185,11 @@ async function onNextStudent() {
 
     pageNumber.value++;
     await getList();
-    onSelectStudent(0);
+    await onSelectStudent(0);
     return;
   }
 
-  onSelectStudent(dataCheckStore.curStudentIndex + 1);
+  await onSelectStudent(dataCheckStore.curStudentIndex + 1);
 }
 
 function getImgDpi(imgWidth: number): number {
@@ -233,11 +250,11 @@ async function onPrevPage() {
 
     pageNumber.value--;
     await getList();
-    selectPage(dataList.value.length - 1);
+    await selectPage(dataList.value.length - 1);
     return;
   }
 
-  selectPage(dataCheckStore.curPageIndex - 1);
+  await selectPage(dataCheckStore.curPageIndex - 1);
 }
 
 async function onNextPage() {
@@ -249,11 +266,11 @@ async function onNextPage() {
 
     pageNumber.value++;
     await getList();
-    selectPage(0);
+    await selectPage(0);
     return;
   }
 
-  selectPage(dataCheckStore.curPageIndex + 1);
+  await selectPage(dataCheckStore.curPageIndex + 1);
 }
 
 // shortcut
@@ -297,7 +314,7 @@ onMounted(async () => {
   }
   appStore.setState({ serverUrl: appConfig.base.serverUrl });
   registShortcut();
-  onSearch();
+  onSearch({ imageName: "", isCheck: true });
 });
 onBeforeUnmount(() => {
   removeShortcut();

+ 5 - 0
src/render/views/ExamNumberCheck/types.ts

@@ -20,3 +20,8 @@ export interface QuestionInfo {
   examStatus: string;
   packageCode?: string;
 }
+
+export interface AllCheckFilter {
+  imageName: string;
+  isCheck: boolean;
+}