Parcourir la source

feat: 图片检查

zhangjie il y a 10 mois
Parent
commit
a8e38dada7

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

@@ -117,6 +117,7 @@ const toLogout = () => {
       overflow: auto;
       height: 100%;
       background-color: #fff;
+      border: 1px solid #e5e6eb;
     }
   }
 }

+ 42 - 0
src/render/ap/imageCheck.ts

@@ -0,0 +1,42 @@
+import { AxiosResponse } from "axios";
+import { request } from "@/utils/request";
+
+import {
+  ExamParams,
+  ExamPageParams,
+  RequestActionResult,
+} from "./types/common";
+import {
+  ImageCheckListPageResult,
+  ImageCheckFailedListPageResult,
+  ImageCheckFailedParams,
+} from "./types/imageCheck";
+
+// 图片检查
+export const imageCheckList = (
+  data: ExamPageParams
+): Promise<ImageCheckListPageResult> =>
+  request({
+    url: "/api/admin/subject/image-check/list",
+    method: "post",
+    data,
+  });
+
+export const imageCheckFailedList = (
+  data: ImageCheckFailedParams
+): Promise<ImageCheckFailedListPageResult> =>
+  request({
+    url: "/api/admin/subject/image-check/failed/page",
+    method: "post",
+    data,
+  });
+
+export const imageCheckFailedExport = (
+  data: ImageCheckFailedParams
+): Promise<AxiosResponse<Blob>> =>
+  request({
+    url: "/api/admin/subject/image-check/failed/export",
+    method: "post",
+    data,
+    responseType: "blob",
+  });

+ 14 - 2
src/render/ap/resultExport.ts

@@ -4,6 +4,7 @@ import { request } from "@/utils/request";
 import {
   ExamParams,
   ExamPageParams,
+  ExamSubjectParams,
   RequestActionResult,
 } from "./types/common";
 import {
@@ -12,7 +13,6 @@ import {
   MarkSiteListPageResult,
   MarkSiteSaveParams,
   markSiteSetParams,
-  dbfExportParams,
   dbfExportProgressResult,
 } from "./types/resultExport";
 
@@ -96,7 +96,19 @@ export const markSiteCodeInfo = (
   });
 
 // DBF导出
-export const dbfExport = (data: dbfExportParams): Promise<{ taskId: string }> =>
+// 导出扫描答案DBF
+export const dbfAnswerExport = (
+  data: ExamSubjectParams
+): Promise<{ taskId: string }> =>
+  request({
+    url: "/api/admin/subject/answer-data/export",
+    method: "post",
+    data,
+  });
+// 导出打包DBF
+export const dbfPackageExport = (
+  data: ExamSubjectParams
+): Promise<{ taskId: string }> =>
   request({
     url: "/api/admin/subject/slice-data/export",
     method: "post",

+ 6 - 0
src/render/ap/types/common.ts

@@ -14,6 +14,12 @@ export interface ExamParams {
 
 export type ExamPageParams = PageParams<ExamParams>;
 
+export interface ExamSubjectParams {
+  examId: string;
+  subjectCode: string;
+}
+export type ExamSubjectPageParams = PageParams<ExamSubjectParams>;
+
 export interface RequestActionResult {
   updateTime: number;
 }

+ 26 - 0
src/render/ap/types/imageCheck.ts

@@ -0,0 +1,26 @@
+import { PageResult, PageParams, ExamSubjectParams } from "./common";
+
+// 违纪导入
+export interface ImageCheckListItem {
+  subjectCode: string;
+  subjectName: string;
+  imageCheckProgress: number;
+  failedCount: number;
+}
+export type ImageCheckListPageResult = PageResult<ImageCheckListItem>;
+
+export interface ImageCheckFailedListItem {
+  subjectCode: string;
+  subjectName: string;
+  campusCode: string;
+  campusName: string;
+  examSite: string;
+  examSiteName: string;
+  examRoom: string;
+  failed: boolean;
+}
+
+export type ImageCheckFailedListPageResult =
+  PageResult<ImageCheckFailedListItem>;
+
+export type ImageCheckFailedParams = PageParams<ExamSubjectParams>;

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

@@ -9,6 +9,8 @@ declare module 'vue' {
   export interface GlobalComponents {
     AAlert: typeof import('@qmth/ui')['Alert']
     AButton: typeof import('@qmth/ui')['Button']
+    ACard: typeof import('@qmth/ui')['Card']
+    ADivider: typeof import('@qmth/ui')['Divider']
     AForm: typeof import('@qmth/ui')['Form']
     AFormItem: typeof import('@qmth/ui')['FormItem']
     AInput: typeof import('@qmth/ui')['Input']

+ 1 - 0
src/render/hooks/useTable.ts

@@ -58,6 +58,7 @@ export default function useTable<T extends Record<string, any>>(
     pageSize,
     showTotal: (totalCount: number) => `共${totalCount}页`,
     showQuickJumper: true,
+    showSizeChanger: false,
     onChange: toPage,
   });
 

+ 20 - 0
src/render/router/routes.ts

@@ -38,6 +38,26 @@ const routes: RouteRecordRaw[] = [
           title: "扫描管理",
         },
       },
+      // 图片检查
+      {
+        path: "image-check",
+        name: "ImageCheck",
+        component: () => import("@/views/ImageCheck/index.vue"),
+        meta: {
+          title: "图片检查",
+        },
+        children: [
+          {
+            path: "image-failed/:examId/:subjectCode",
+            name: "ImageFailed",
+            component: () => import("@/views/ImageCheck/ImageFailed.vue"),
+            meta: {
+              title: "图片异常",
+            },
+          },
+        ],
+      },
+      // 结果导出
       {
         path: "result-export",
         name: "ResultExport",

+ 13 - 0
src/render/styles/index.less

@@ -16,3 +16,16 @@ body {
   height: 30px;
   line-height: 28px;
 }
+
+// color
+.color-error {
+  color: @error-color;
+}
+.color-gray {
+  color: @text-color3;
+}
+
+// base
+.page-box {
+  padding: 20px;
+}

+ 1 - 0
src/render/views/CurExam/index.vue

@@ -199,6 +199,7 @@
               <div
                 class="flex items-center cursor-pointer"
                 :style="{ color: token.colorPrimary }"
+                @click="toPage('ImageCheck')"
               >
                 <span>进入</span>
                 <RightOutlined />

+ 130 - 0
src/render/views/ImageCheck/ImageFailed.vue

@@ -0,0 +1,130 @@
+<template>
+  <a-card class="image-fialed" :bordered="false">
+    <template #title>
+      <a-button @click="goback">
+        <template #icon><SwapLeftOutlined /> </template>后退
+      </a-button>
+      <a-divider type="vertical" />
+      <a-button type="primary" :loading="downloading" @click="onExport">
+        <template #icon><ExportOutlined /></template>导出
+      </a-button>
+    </template>
+    <a-table
+      :columns="columns"
+      :row-key="(record) => record.subjectCode"
+      :data-source="dataList"
+      :pagination="pagination"
+      :loading="loading"
+      size="middle"
+      bordered
+    >
+      <template #bodyCell="{ column, text }">
+        <template v-if="column.dataIndex === 'failed'">
+          <span :class="text ? 'color-danger' : 'color-gray'">{{
+            text ? "是" : "否"
+          }}</span>
+        </template>
+      </template>
+    </a-table>
+  </a-card>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import type { TableProps } from "ant-design-vue";
+import { message } from "ant-design-vue";
+import { SwapLeftOutlined, ExportOutlined } from "@ant-design/icons-vue";
+
+import useTable from "@/hooks/useTable";
+import { ImageCheckFailedListItem } from "@/ap/types/imageCheck";
+import { imageCheckFailedList, imageCheckFailedExport } from "@/ap/imageCheck";
+import { downloadByApi } from "@/utils/download";
+import useLoading from "@/hooks/useLoading";
+
+defineOptions({
+  name: "ImageFailed",
+});
+
+const router = useRouter();
+const route = useRoute();
+
+const columns: TableProps["columns"] = [
+  {
+    title: "科目代码",
+    dataIndex: "subjectCode",
+    width: "140px",
+  },
+  {
+    title: "考点",
+    dataIndex: "examSiteName",
+  },
+  {
+    title: "校区",
+    dataIndex: "campusName",
+    width: "",
+  },
+  {
+    title: "考场号",
+    dataIndex: "examRoom",
+    width: "",
+  },
+  {
+    title: "有图片异常",
+    dataIndex: "failed",
+    width: "100px",
+  },
+];
+
+const searchModel = reactive({
+  examId: route.params.examId,
+  subjectCode: route.params.subjectCode,
+});
+
+const { dataList, pagination, loading } = useTable<ImageCheckFailedListItem>(
+  imageCheckFailedList,
+  searchModel,
+  false
+);
+
+const { loading: downloading, setLoading } = useLoading();
+async function onExport() {
+  if (downloading.value) return;
+
+  setLoading(true);
+  const res = await downloadByApi(() =>
+    imageCheckFailedExport(searchModel)
+  ).catch((e: Error) => {
+    message.error(e.message || "下载失败,请重新尝试!");
+  });
+  setLoading(false);
+
+  if (!res) return;
+  message.success("下载成功!");
+}
+
+function goback() {
+  router.back();
+}
+
+onMounted(() => {
+  dataList.value = [
+    {
+      subjectCode: "8956145235",
+      subjectName: "语法基础",
+      campusCode: "string",
+      campusName: "string",
+      examSite: "string",
+      examSiteName: "string",
+      examRoom: "string",
+      failed: false,
+    },
+  ];
+});
+</script>
+
+<style lang="less" scoped>
+.image-fialed {
+  box-shadow: none !important;
+}
+</style>

+ 89 - 0
src/render/views/ImageCheck/index.vue

@@ -0,0 +1,89 @@
+<template>
+  <div v-show="route.name === 'ImageCheck'" class="page-box">
+    <a-table
+      :columns="columns"
+      :row-key="(record) => record.subjectCode"
+      :data-source="dataList"
+      :pagination="false"
+      :loading="loading"
+      bordered
+    >
+      <template #bodyCell="{ column, index }">
+        <template v-if="column.dataIndex === 'operation'">
+          <qm-button type="link" @click="onViewFailed(index)"
+            >查看异常</qm-button
+          >
+        </template>
+      </template>
+    </a-table>
+  </div>
+
+  <router-view />
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import type { TableProps } from "ant-design-vue";
+
+import { ImageCheckListItem } from "@/ap/types/imageCheck";
+import { imageCheckList } from "@/ap/imageCheck";
+
+defineOptions({
+  name: "ImageCheck",
+});
+
+const router = useRouter();
+const route = useRoute();
+
+const columns: TableProps["columns"] = [
+  {
+    title: "科目代码",
+    dataIndex: "subjectCode",
+  },
+  {
+    title: "科目名称",
+    dataIndex: "subjectName",
+  },
+  {
+    title: "异常数量",
+    dataIndex: "failedCount",
+    width: "180px",
+  },
+  {
+    title: "操作",
+    dataIndex: "operation",
+    width: "100px",
+  },
+];
+const loading = ref(false);
+const dataList = ref<ImageCheckListItem[]>([]);
+
+async function getData() {
+  const res = await imageCheckList({ examId: "" });
+  dataList.value = res || [];
+}
+
+function onViewFailed(index: number) {
+  const record = dataList.value[index];
+  router.push({
+    name: "ImageFailed",
+    params: {
+      examId: "1",
+      subjectCode: record.subjectCode,
+    },
+  });
+}
+
+onMounted(() => {
+  dataList.value = [
+    {
+      subjectCode: "8956145235",
+      subjectName: "语法基础",
+      imageCheckProgress: 89,
+      failedCount: 10,
+    },
+  ];
+  // getData()
+});
+</script>