Bladeren bron

更新扫描批次详情页面

dengshaobo 3 weken geleden
bovenliggende
commit
55a0247e53
6 gewijzigde bestanden met toevoegingen van 203 en 371 verwijderingen
  1. 17 0
      src/api/exam.ts
  2. 11 0
      src/styles/common.scss
  3. 27 32
      src/utils/scanCommon.ts
  4. 0 198
      src/views/exam/components/scanButton copy.vue
  5. 97 101
      src/views/exam/scanDetail.vue
  6. 51 40
      src/views/exam/scanList.vue

+ 17 - 0
src/api/exam.ts

@@ -217,6 +217,23 @@ export const updateScanCount= (data:any):Promise<ApiResponse> => {
 }
 
 
+//扫描学生 查看批次详情 列表模式 分页接口
+export const getBatchDetailList= (data:any):Promise<ApiResponse> => {
+  return request({
+    url: '/api/v1/ai_exam_scan/find_scanned_batch_detail',
+    method: 'get',
+    params: data 
+  })
+}
+
+//扫描学生 查看批次详情  图片模式 分页接口 
+export const getBatchDetailImage= (data:any):Promise<ApiResponse> => {
+  return request({
+    url: '/api/v1/ai_exam_scan/find_scanned_batch_pics',
+    method: 'get',
+    params: data 
+  })
+}
 
 
 

+ 11 - 0
src/styles/common.scss

@@ -520,6 +520,17 @@ body {
           .header_left
           {
 
+
+            .scan_button_header
+            {
+              .el-button
+              {
+                padding: 8px 10px;
+                i{
+                  margin-right:5px;
+                }
+              }
+            }
           }
           .header_right
           {

+ 27 - 32
src/utils/scanCommon.ts

@@ -98,43 +98,38 @@ class ScanCommonService {
           try {
             const objectData: ScanMessage = JSON.parse(evt.data);
             console.log("处理扫描结果的objectData:", objectData);
-
-            if (objectData != null) {
-              if (objectData.code === 200) {
-                if (objectData.action === 'connectMessage') {
-                    
-                    this.clientVersion = objectData.param.appVersion || '';
-                    const version = objectData.param.appVersion || '未知版本';
-
-                    ElMessage({
-                      type: 'success',
-                      message: `客户端连接成功……版本号:${version}`
-                    });
-                } else if (objectData.action === 'getScannerList') {
-                  // 获取扫描仪结果
-                  if (objectData.data && Array.isArray(objectData.data) && objectData.data.length > 0) {
-                    // 有扫描仪连接 
-                    this.isScanned = true;
-                    ElMessage({
-                      type: 'success',
-                      message: '扫描仪连接成功……'
-                    });
-                  } else {
-                    // 没有扫描仪连接
-                    this.isScanned = false;
-                  }
+            if (objectData.code === 200) {
+              if (objectData.action === 'connectMessage') {
+                  
+                  this.clientVersion = objectData.param.appVersion || '';
+                  const version = objectData.param.appVersion || '未知版本';
+
+                  ElMessage({
+                    type: 'success',
+                    message: `客户端连接成功……版本号:${version}`
+                  });
+              } else if (objectData.action === 'getScannerList') {
+                // 获取扫描仪结果
+                if (objectData.data && Array.isArray(objectData.data) && objectData.data.length > 0) {
+                  // 有扫描仪连接 
+                  this.isScanned = true;
+                  ElMessage({
+                    type: 'success',
+                    message: '扫描仪连接成功……'
+                  });
                 } else {
-                  // 其他业务数据回调
-                  if (callback) callback(objectData);
+                  // 没有扫描仪连接
+                  this.isScanned = false;
                 }
-              } else if (objectData.code === 509) {
-                // 扫描仪未连接
-                if (callback) callback(objectData);
-              } else if (objectData.code === 502) {
-                // 未检测到纸张或卡纸
+              } else {
+                // 其他业务数据回调
                 if (callback) callback(objectData);
               }
+            } else {
+              // 未检测到纸张或卡纸
+              if (callback) callback(objectData);
             }
+            
           } catch (error) {
             console.error("解析消息失败:", error);
           }

+ 0 - 198
src/views/exam/components/scanButton copy.vue

@@ -1,198 +0,0 @@
-<template>
-  <div class="canvas_button">
-    <!-- 使用 ref 绑定 canvas 元素 -->
-    <canvas ref="canvasRef" height="180" width="180"></canvas>
-    <div class="canvas_cr"></div>
-    <img src="../../../assets/icon/scan_button_bg.png" v-if="showButton" alt="scan button bg" />
-  </div>
-</template>
-
-<script setup lang="ts">
-import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
-
-// 定义 Props
-interface Props {
-  process?: number;
-  quekao?: number;
-  yichang?: number;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-  process: 10,
-  quekao: 20,
-  yichang: 40,
-});
-
-// 响应式数据
-const canvasRef = ref<HTMLCanvasElement | null>(null);
-const showButton = ref(true);
-let animationFrameId: number | null = null;
-
-// 格式化进度显示
-const formatProcess = (process: number): string | number => {
-  if (process % 1 === 0) {
-    return Math.floor(process);
-  } else {
-    return process.toFixed(2);
-  }
-};
-
-// 绘制 Canvas 核心逻辑
-const drawCanvas = () => {
-  const canvas = canvasRef.value;
-  if (!canvas) return;
-
-  const ctx = canvas.getContext('2d');
-  if (!ctx) return;
-
-  // 清除画布
-  ctx.clearRect(0, 0, canvas.width, canvas.height);
-
-  // 保存状态
-  ctx.save();
-
-  // 1. 绘制白色圆底
-  ctx.beginPath();
-  ctx.arc(90, 90, 75, 0, 2 * Math.PI);
-  ctx.shadowColor = "#4D2EFA";
-  ctx.shadowBlur = 10;
-  ctx.fillStyle = '#ffffff';
-  ctx.fill();
-  ctx.closePath();
-  ctx.restore(); // 恢复阴影状态,避免影响后续绘制
-
-  // 2. 绘制进度环轨道 (灰色背景环)
-  ctx.save();
-  ctx.beginPath();
-  ctx.lineWidth = 10;
-  ctx.strokeStyle = '#EAF0FF';
-  ctx.arc(90, 90, 75, 0, 2 * Math.PI);
-  ctx.stroke();
-  ctx.closePath();
-  ctx.restore();
-
-  // 计算角度辅助函数 (将百分比转换为弧度,起始点为 -90度/12点钟方向)
-  const getEndAngle = (percent: number) => {
-    return ((percent / 100) * 360 - 90) * Math.PI / 180;
-  };
-
-  const startAngle = -90 * Math.PI / 180;
-  const processEnd = getEndAngle(props.process);
-  const yichangEnd = getEndAngle(props.yichang + props.process);
-  const quekaoEnd = getEndAngle(props.quekao + props.yichang + props.process);
-
-  // 3. 绿色进度环 (正常扫描)
-  ctx.save();
-  ctx.beginPath();
-  ctx.lineWidth = 40;
-  ctx.strokeStyle = '#2BC644';
-  ctx.arc(90, 90, 65, startAngle, processEnd);
-  ctx.stroke();
-  ctx.closePath();
-  ctx.restore();
-
-  // 4. 红色进度环 (异常)
-  ctx.save();
-  ctx.beginPath();
-  ctx.strokeStyle = '#F56C6C';
-  ctx.arc(90, 90, 75, processEnd, yichangEnd);
-  ctx.stroke();
-  ctx.closePath();
-  ctx.restore();
-
-  // 5. 橙色进度环 (缺考)
-  ctx.save();
-  ctx.beginPath();
-  ctx.lineWidth = 40;
-  ctx.strokeStyle = '#FB9F34';
-  ctx.arc(90, 90, 75, yichangEnd, quekaoEnd);
-  ctx.stroke();
-  ctx.closePath();
-  ctx.restore();
-
-  // 6. 绘制文字 "开始扫描"
-  ctx.save();
-  ctx.font = "bold 16px Arial"; // 建议指定字体族
-  ctx.textAlign = 'center';
-  ctx.textBaseline = 'middle'; // 优化垂直居中
-  ctx.fillStyle = '#333333';
-  ctx.fillText('开始扫描', 90, 120);
-  ctx.restore();
-
-  // 7. 绘制百分比
-  ctx.save();
-  ctx.font = "bold 32px Arial";
-  ctx.textAlign = 'center';
-  ctx.textBaseline = 'middle';
-  ctx.fillStyle = '#2E64FA';
-  ctx.fillText(`${formatProcess(props.process)}%`, 90, 90);
-  ctx.restore();
-
-  // 请求下一帧动画
-  animationFrameId = requestAnimationFrame(drawCanvas);
-};
-
-// 监听 Props 变化(可选,如果需要在数据变化时执行特定逻辑)
-watch(() => props.yichang, (newVal) => {
-  // console.log("异常数值变化了", newVal);
-});
-
-onMounted(() => {
-  // 启动动画循环
-  drawCanvas();
-});
-
-onBeforeUnmount(() => {
-  // 取消动画帧,防止内存泄漏
-  if (animationFrameId !== null) {
-    cancelAnimationFrame(animationFrameId);
-  }
-});
-</script>
-
-<style lang="scss" scoped>
-.canvas_button {
-  width: 250px;
-  height: 250px;
-  background-color: transparent;
-  position: relative;
-  display: flex; /* 修复:原本缺少 display:flex 导致 justify-content/align-items 无效 */
-  justify-content: center;
-  align-items: center;
-
-  img {
-    width: 100%;
-    height: 100%;
-    object-fit: contain; /* 保持图片比例 */
-  }
-
-  canvas {
-    position: absolute;
-    left: 35px;
-    top: 35px;
-    z-index: 999;
-  }
-
-  .canvas_cr {
-    width: 128px;
-    height: 128px;
-    border-radius: 50%;
-    border: 1px dashed #4D2EFA;
-    position: absolute;
-    left: 55px;
-    top: 55px;
-    z-index: 1000;
-    animation: identifier 10s linear infinite;
-    pointer-events: none; /* 防止遮挡点击事件 */
-  }
-
-  @keyframes identifier {
-    from {
-      transform: rotate(0deg);
-    }
-    to {
-      transform: rotate(360deg);
-    }
-  }
-}
-</style>

+ 97 - 101
src/views/exam/scanDetail.vue

@@ -6,13 +6,11 @@
         <div class="content_left">
             <el-select  v-model="params.batchNo"   placeholder="选择批次" @change="GoSearch()" class="select_width" >
                 <el-option label="全部批次" value=""></el-option>
-                <el-option v-for="item in batchList"
-                :key="item.value"
-                :label="item.label" :value="item.value"></el-option>
+                <el-option v-for="item in batchList" :key="item.value"  :label="'批次'+item.label" :value="item.value"></el-option>
             </el-select>
-            <el-select  v-model="params.batchNo"   placeholder="选择状态" @change="GoSearch()" class="select_width" >
+            <el-select  v-model="params.statusStr"   placeholder="选择状态" @change="GoSearch()" class="select_width" >
                 <el-option label="全部状态" value=""></el-option>
-                <el-option v-for="item in batchList"
+                <el-option v-for="item in stateList"
                 :key="item.value"
                 :label="item.label" :value="item.value"></el-option>
             </el-select>
@@ -26,16 +24,12 @@
             </el-button>
             
 
-            
-            <!-- <el-button class="delete_item" type="text" @click="OpenDeleteAllDialog"  v-if="tableData.length>0">删除所有</el-button> -->
-            <el-button @click="OpenReIdentify" >重新识别</el-button>
-            <el-button @click="OpenEditExamList()" type="primary" v-if="isImportStudent">编辑考场名单</el-button>
-            <el-button @click="OpenImportStudent()" type="primary" v-else>导入考场名单</el-button>
+            <el-button @click="GotoScanHome()" type="primary">扫描页面</el-button>
         </div>
     </div>
     <div class="page_jg_20"></div>
     <div class="page_content" >
-        <div class="content_table">
+        <div class="content_table" style="width: 100%">
             <div class="table_header">
                 <div class="header_left">
                     <div class="scan_button_header">
@@ -45,21 +39,36 @@
     
                 </div>
                 <div class="header_right">
-                    客户端状态:
-                    <span class="scan_state_open" v-if="scanClientStates">
-                        <i class="iconfont icon_open"></i>已打开</span>
-                    <span class="scan_state_close" v-else>
-                        <i class="iconfont icon_close"></i>未打开</span>
+                   
                 </div>
             </div>
             <div class="page_jg_20"></div>
             <div class="page_table">
                 <el-table :data="tableData" style="width: 100%" :height="tableHeight">
-                    <el-table-column prop="questionName" label="序号" width="100" align="center" >
+                    <el-table-column type="index" label="序号" width="100" align="center" >
                     </el-table-column>
-                    <el-table-column prop="questionType" label="批次" width="120" align="center">
+                    <el-table-column prop="batchNo" label="批次" width="120" align="center">
+                        <template v-slot="scope">
+                            批次{{ scope.row.batchNo}}
+                        </template>
                     </el-table-column>
-                    <el-table-column prop="questionType" label="考号" width="120" align="center">
+                    <el-table-column prop="cardNumber" label="考号" width="120" align="center">
+                        <template v-slot="scope">
+                            <span v-if="scope.row.cardNumber">{{ scope.row.cardNumber}}</span>
+                            <span v-else>-</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="studentName" label="姓名" width="120" align="center">
+                        <template v-slot="scope">
+                            <span v-if="scope.row.studentName">{{ scope.row.studentName}}</span>
+                            <span v-else>-</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="studentName" label="页数" width="120" align="center">
+                        <template v-slot="scope">
+                            <span v-if="scope.row.studentName">{{ scope.row.studentName}}</span>
+                            <span v-else>-</span>
+                        </template>
                     </el-table-column>
                     <el-table-column prop="date" label="客观题" align="center">
                         <template v-slot="scope">
@@ -84,73 +93,6 @@
                 </el-table>
             </div>
         </div>
-        <div class="content_right">
-            <div class="right_header">
-               <span> 扫描 设置</span>
-               <span>
-                识别号:
-                <el-select v-model="params.batchNo" placeholder="请选择" @change="GoSearch()" style="width: 120px;">
-                    <el-option label="全部状态" value=""></el-option>
-                    <el-option v-for="item in batchList" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                </el-select>
-               </span>
-            </div>
-            <div class="right_center">
-                <div class="scan_buttons" @click="OpenScan">
-                    <ScanButton></ScanButton>
-                </div>
-                <div class="scan_list">
-                    <div class="list_item no_scan" >
-                        <div class="list_item_info " @click="GotoDetail(0)">
-                            <div class="item_info_title">
-                                未扫描
-                            </div>
-                            <div class="item_info_number">
-                                <span class="number_no_scan">{{}}人</span>
-                                <!-- <span class="number_no_icon"><img src="../../assets/icon/no_scan_icon.png"></span> -->
-                            </div>
-                        </div>
-                    </div>
-                    <div class="list_item no_exam" >
-                        <div class="list_item_info " @click="GotoDetail(2)">
-                            <div class="item_info_title">
-                                缺考
-                            </div>
-                            <div class="item_info_number">
-                                <span class="number_no_exam">{{}}人</span>
-                                <!-- <span class="number_no_icon"><img src="../../assets/icon/miss_exam.png"></span> -->
-                            </div>
-                        </div>
-                    </div>
-                    <div class="list_item annormal_icon" >
-                        <div class="list_item_info " @click="GotoDetail(3)">
-                            <div class="item_info_title">
-                                异常
-                            </div>
-                            <div class="item_info_number">
-                                <span class="number_abnormal">{{}}份</span>
-                                <!-- <span class="number_no_icon"><img src="../../assets/icon/abnormal_icon.png"></span> -->
-                            </div>
-                        </div>
-                    </div>
-                    <div class="list_item sucess_upload" >
-                        <div class="list_item_info " @click="GotoDetail(1)">
-                            <div class="item_info_title">
-                                已上传
-                            </div>
-                            <div class="item_info_number">
-                                <span class="number_uploaded">{{}}人</span>
-                                <!-- <span class="number_no_icon"><img src="../../assets/icon/sucess_upload.png"></span> -->
-
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="right_button">
-                <el-button type="primary" @click="GoSearch()" style="width:calc(100% - 40px);">扫描完成</el-button>
-            </div>
-        </div>
     </div>
     <SelectStudent v-model="showSelectStudent"  @success="StudentSuccess"></SelectStudent>
   </div>
@@ -159,9 +101,16 @@
 import { useExamStore } from '@/store/exam'
 import { useRouter } from 'vue-router'
 import { onMounted ,ref,computed} from 'vue';
-import ScanButton from './components/scanButton.vue'
+import { ElMessageBox, ElMessage } from 'element-plus'
 import SelectStudent from './components/selectStudent.vue'
-import { hasImportStudent, } from '@/api/exam'
+import { getBatchDetailList } from '@/api/exam'
+
+// 定义选项接口类型
+interface OptionItem {
+  label: string
+  value: string | number
+  sort: number
+}
 // 实例化 Store
 const examStore = useExamStore()
 const router = useRouter()
@@ -173,12 +122,16 @@ const examSubjectId = computed(() => {
 
 const params=ref({
     batchNo:'',
+    statusStr:'',//状态
     keyWord:''
 })
 
-const batchList=[];
+const batchList = ref<OptionItem[]>([])
+const stateList = ref<OptionItem[]>([])
+
+
 const listMode=ref('list');
-const scanClientStates=ref(false);//客户端状态
+
 const tableData=ref([]);
 
 const tableHeight=ref(500);
@@ -193,9 +146,9 @@ const OpenImportStudent = () => {
 }
 
 // 打开编辑考试名单
-const OpenEditExamList = () => {
+const GotoScanHome = () => {
     router.push({
-        path: '/exam/examList',
+        path: '/exam/scanList',
         query: {
             examSubjectId: examSubjectId.value,
         },
@@ -203,39 +156,82 @@ const OpenEditExamList = () => {
 }
 
 
+//切换模式
+const ChangeMode = (mode: string) => {
+    listMode.value = mode;
+}
+
+//加载数据
+const LoadData = () => {
+    if(listMode.value=='list')
+    {
+        GetScanDetailList();
+    }
+    else if(listMode.value=='pic')
+    {   
+        GetScanDetailList();
+    }
+    
+}
+
+
+
+
 //学生名单导入成功
 const StudentSuccess = () => {
 
-    HasImportStudent();
+
 }
 
 
 
-//查询是否导入了学生名单
-const HasImportStudent = async () => { 
+
+
+//搜索
+const GoSearch = () => { 
+
+}
+
+//刷新
+const Refresh=() => {
+    GetScanDetailList();
+}
+
+
+//获取扫描详情 列表模式
+const GetScanDetailList = async () => {
     const params = {
       examSubjectId: examSubjectId.value,
+      batchNo:'',//批次号
+      statusStr:'',//状态
+      nameCode:'',//搜索条件 姓名 考号
       schoolId: 0,//单校 0 
+      pageNum:1,//当前页码
+      pageSize:10,//每页条数
     };
-    const res = await hasImportStudent(params);
-    console.log("打印是否导入了学生名单",res);
+    const res = await getBatchDetailList(params);
+    console.log("打印批次详情列表",res);
     if(res.code==200)
     {
-        isImportStudent.value=res.data;
+       batchList.value=res.data.batchSelects;
+       stateList.value=res.data.statusSelects;
+       tableData.value=res.data.pageResult.records;
     }
-    else{
-        isImportStudent.value=false;
+    else
+    {
+        ElMessage.error(res.msg);
     }
 }
 
 
+
 onMounted(() => {
   
     if (!examStore.currentExam) {
         console.warn('当前没有选中的考试信息')
         // 可选:如果没有数据,可以重定向回列表页或提示用户
     }
-    HasImportStudent();
+    LoadData();//加载数据
   
 })
 </script>

+ 51 - 40
src/views/exam/scanList.vue

@@ -75,7 +75,7 @@
                                 <i class="el-icon-loading"></i>{{loadingText}}……
                             </div>
                             <div class="ele_button table_row_button" v-else>
-                                <span class="btn_editor">详情</span>
+                                <span class="btn_editor" @click="GotoDetail(-1)">详情</span>
                                 <span class="btn_delete" v-if="scope.row.scanUserName==currentUserName " @click="OptionDelete(scope.row)">删除</span>
                                 <span class="editor_disable" v-else>删除</span>
                             </div>
@@ -258,7 +258,8 @@ const scanYichang=computed(() => {
 });
 //刷新
 const Refresh = () => {
-
+    tableData.value=[];
+    GetScanBatchList();
 }
 
 //重新识别弹窗
@@ -420,25 +421,34 @@ const OptionDelete=(row: any) => {
 
 //跳转到异常详情页
 const GotoDetail = (type: number) => {
-    //根据类型跳转到不同的详情页
-    // switch (type) {
-    //     case 0:
-    //         router.push({
-    //             path: '/exam/noScanList',
-    //             query: {
-    //                 examSubjectId: examSubjectId.value,
-    //             },
-    //         });
-    //         break;
-    //     case 1:
-    //         router.push({
-    //             path: '/exam/uploadedList',
-    //             query: {
-    //                 examSubjectId: examSubjectId.value,
-    //             },
-    //         })
-    //         break;
-    //     }
+
+    // 根据类型跳转到不同的详情页
+    switch (type) {
+        case 0:
+            router.push({
+                path: '/exam/noScanList',
+                query: {
+                    examSubjectId: examSubjectId.value,
+                },
+            });
+            break;
+        case 1:
+            router.push({
+                path: '/exam/uploadedList',
+                query: {
+                    examSubjectId: examSubjectId.value,
+                },
+            })
+            break;
+        case -1:
+            router.push({
+                path: '/exam/scanDetail',
+                query: {
+                    examSubjectId: examSubjectId.value,
+                },
+            })
+            break;
+        }
 }
 
 // 打开导入学生名单
@@ -539,24 +549,24 @@ const UpdateBatchScanNumber = (batchId: any, scanNumber: Number) => {
 // 处理扫描结果
 const HandleScanResult = (res: any) => {
    console.log('收到扫描数据', res);
-   // 业务逻辑...
-   if (res.action == 'uploading') 
-   {
-        let batchNumber: any = '';
-        if (res.batchNumber) 
-        {
-        batchNumber = GetBatchNumber(res.batchNumber || res.data?.batchNumber);
-        }
-        const targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
-        
-        if (typeof loadingText !== 'undefined') loadingText.value = "启动扫描仪中";
+    // 业务逻辑...
+    if (res.action == 'uploading') 
+    {
+            let batchNumber: any = '';
+            if (res.batchNumber) 
+            {
+            batchNumber = GetBatchNumber(res.batchNumber || res.data?.batchNumber);
+            }
+            const targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
+            
+            if (typeof loadingText !== 'undefined') loadingText.value = "启动扫描仪中";
 
-        ElMessage.success("正在启动扫描仪,请稍后…");
-        if (targetItem) 
-        {
-            targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
-        }
-   }
+            ElMessage.success("正在启动扫描仪,请稍后…");
+            if (targetItem) 
+            {
+                targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
+            }
+    }
     // 开始扫描指令
     if (res.action == 'startScan') 
     {
@@ -566,9 +576,10 @@ const HandleScanResult = (res: any) => {
         console.log("打印currentItem", currentItem);
         
         //开始扫描指令
-        if (res.code == 200) {
+        if (res.code == 200) 
+        {
             ElMessage.success("扫描仪启动成功,开始扫描…");
-            if (typeof loadingText !== 'undefined') loadingText.value = "正在扫描中";
+            if(typeof loadingText !== 'undefined') loadingText.value = "正在扫描中";
         }
         
         if (res.code == 502)