Explorar el Código

扫描进度组件 扫描进度数据更新

dengshaobo hace 3 semanas
padre
commit
9864d28ac9

+ 9 - 0
src/api/exam.ts

@@ -207,6 +207,15 @@ export const deleteBatch= (data:any):Promise<ApiResponse> => {
   })
 }
 
+//扫描学生 更新扫描张数 
+export const updateScanCount= (data:any):Promise<ApiResponse> => {
+  return request({
+    url: '/api/v1/ai_exam_scan/record_batch_scan_number',
+    method: 'post',
+    data
+  })
+}
+
 
 
 

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

@@ -0,0 +1,198 @@
+<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>

+ 275 - 187
src/views/exam/components/scanButton.vue

@@ -1,197 +1,285 @@
 <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>
+     <div class="canvas_button">
+        <canvas id="canvasProcess" height="180" width="180"></canvas>
+        <div class="canvas_cr"></div>
+        <img src="../../../assets/icon/scan_button_bg.png" v-if="showButton">
+     </div>
 </template>
 
-<script setup lang="ts">
-import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
+<script>
 
-// 定义 Props
-interface Props {
-  process?: number;
-  quekao?: number;
-  yichang?: number;
-}
+export default {
+    name: 'scanButton',
 
-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 = 10;
-  ctx.strokeStyle = '#2BC644';
-  ctx.arc(90, 90, 75, 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.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>
+    props: {
+        process: {
+            type: Number,
+            default: 10
+        },
+        quekao: {
+            type: Number,
+            default: 20
+        },
+        yichang: {
+            type: Number,
+            default: 40
+        }
+    },
+    watch: {
+        yichang(){
+            // console.log("异常数值变化了",this.yichang);
+            // console.log("打印进度值",this.process,this.quekao,this.yichang);
+        },
+    },
+    data () {
+        return {
+            canvas: null,
+            scale: 1,
+            circle:0,
+            id: '',
+
+            animationSpeed:1,
+            showButton:true
+        }
+    },
+    mounted() {
+        // this.canvas = document.getElementById('canvasScanButton')
+        this.DrawCanvas();//画布开始
+        // this.animations()
+    },
+    beforeDestroy () {
+        cancelAnimationFrame(this.id);//取消注册请求动画id 通过取消动画帧请求,可以停止正在进行的动画,防止不必要的计算和渲染,从而节省资源和提高性能。
+    },
+    methods: {
+
+        handleAnimation: function(anim) {
+            this.anim = anim;
+            this.anim.setSpeed(this.animationSpeed);
+            // 可以在这里添加更多的动画控制逻辑
+        },
+
+        //画一个环行进度条圆圈
+        DrawCanvas()
+        {
+            this.canvas=document.getElementById('canvasProcess');
+            let ctx=this.canvas.getContext('2d');
+            //创建一个圆锥渐变对象
+            let g = ctx.createConicGradient(0, 55, 55);
+            //添加颜色停止点
+            g.addColorStop(0,'blue')
+            g.addColorStop(1,'white')
+            ctx.clearRect(0,0,this.canvas.width,this.canvas.height);//清除指定矩形区域内的所有像素
+            ctx.save();//保存当前绘图状态
+            ctx.translate(this.canvas.width / 2, this.canvas.height / 2);//将原点移动到画布的中心
+
+             //抖动
+            // this.scale = this.scale + 0.002
+            // ctx.scale(this.scale, this.scale)
+            // if (this.scale >= 1.05) {
+            //     this.scale = 0.9998
+            // }
+            // // 圆底
+            ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2)//将原点移动到画布的中心
+            ctx.beginPath();//开始路径
+            ctx.save();//保存当前绘图状态
+            ctx.arc(90,90,75,0,2 * Math.PI);//绘制一个圆形,圆心在90,90原点上 半径为80像素
+            ctx.shadowColor = "#4D2EFA";//描边颜色
+            ctx.shadowBlur = 10;//描边粗细
+            ctx.fillStyle = '#ffffff';//底部背景色
+            ctx.fill();//填充
+            ctx.restore();//恢复之前保存的绘图状态
+            ctx.closePath();//闭合路径
+
+            // 绘制进度环轨道
+            ctx.beginPath();//开始路径
+            ctx.lineWidth = 10;//进度环宽度
+            ctx.strokeStyle = '#EAF0FF';//进度环轨道颜色值
+            ctx.arc(90,90,75,0,2*Math.PI);//绘制进度环圆形 圆心在90 90上半径为75像素
+            ctx.stroke();//描边
+            ctx.closePath();//闭合路径
+
+            // 进度环
+            //绿色进度环
+            ctx.beginPath()
+            ctx.lineWidth = 10;//进度条宽度
+            ctx.strokeStyle = '#2BC644';//进度条颜色值
+            ctx.arc(90,90,75, -90 * Math.PI / 180,((this.process.toFixed(0) / 100)*360 - 90)*Math.PI/180);//绘制进度条弧度 圆心在90 90 半径为75像素
+            ctx.stroke()
+            ctx.closePath();//闭合路径
+
+            //异常进度环
+            ctx.beginPath()
+            ctx.strokeStyle = '#F56C6C';//异常进度条颜色值
+            ctx.arc(90,90,75,((this.process.toFixed(0) / 100)*360 - 90)*Math.PI/180,((this.yichang.toFixed(0) / 100)*360 + ((this.process.toFixed(0) / 100)*360 - 90))*Math.PI/180)
+            ctx.stroke()
+            ctx.closePath()
+
+            //缺考进度环
+            ctx.beginPath()
+            ctx.strokeStyle = '#FB9F34'//缺考颜色值
+            ctx.arc(90,90,75,
+            ((this.yichang.toFixed(0) / 100)*360 + ((this.process.toFixed(0) / 100)*360 - 90))*Math.PI/180,
+            ((this.quekao.toFixed(0) / 100)*360 + (this.yichang.toFixed(0) / 100)*360 + ((this.process.toFixed(0) / 100)*360 - 90))*Math.PI/180)
+            ctx.stroke();
+            ctx.closePath();
 
-<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: 138px;
-    height: 138px;
-    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);
+            // 扫描文字
+            ctx.beginPath()
+            ctx.font = "20px bold"
+            ctx.textAlign = 'center'
+            ctx.fillStyle = '#333333';//文字颜色值
+            ctx.fillText('开始扫描',90,120);//扫描的位置
+            ctx.closePath()
+            // 扫描百分比
+            ctx.beginPath()
+            ctx.font = "32px bold";
+            ctx.textAlign = 'center'
+            ctx.fillStyle = '#2E64FA';//扫描百分比颜色值
+            ctx.fillText(this.FormatProcess(this.process) + '%',90,90)
+            ctx.closePath()
+            ctx.restore()
+            // console.log("打印进度值",this.process,this.quekao,this.yichang);
+            // 请求下一帧
+            this.id = requestAnimationFrame(this.DrawCanvas);
+            
+        },
+
+        // 格式化进度显示:如果小数部分为0则不显示,否则显示一位小数
+        FormatProcess(process) {
+            if (process % 1 === 0) {
+                // 如果是整数,直接返回整数部分
+                return Math.floor(process);
+            } else {
+                // 如果有小数部分,保留一位小数
+                return process.toFixed(2);
+            }
+        },
+        animations () {
+            this.canvas=document.getElementById('canvasProcess');
+            let ctx = this.canvas.getContext('2d')
+            //创建一个圆锥渐变对象
+            let g = ctx.createConicGradient(0, 90, 90);
+            //添加颜色停止点
+            g.addColorStop(0,'blue')
+            g.addColorStop(1,'white')
+            ctx.clearRect(0,0,this.canvas.width,this.canvas.height);//清除指定矩形区域内的所有像素
+            ctx.save();//保存当前绘图状态
+            ctx.translate(this.canvas.width / 2, this.canvas.height / 2);//将原点移动到画布的中心
+
+            //抖动
+            // this.scale = this.scale + 0.002
+            // ctx.scale(this.scale, this.scale)
+            // if (this.scale >= 1.05) {
+            //     this.scale = 0.9998
+            // }
+            // 圆底
+            ctx.translate(-this.canvas.width / 2, -this.canvas.height / 2)
+            ctx.beginPath()
+            ctx.save()
+            ctx.arc(125,125,100,0,360 * Math.PI / 180)
+            ctx.shadowColor = "#4D2EFA";
+            ctx.shadowBlur = 15;
+            ctx.fillStyle = '#ffffff';//底部背景色
+            ctx.fill()
+            ctx.restore()
+            ctx.closePath()
+            // 进度环底边
+            ctx.beginPath()
+            ctx.lineWidth = 20
+            ctx.strokeStyle = '#D1E0FF';//进度环轨道颜色值
+            ctx.arc(125,125,85,0,360*Math.PI/180)
+            ctx.stroke()
+            ctx.closePath()
+            // 进度环
+            ctx.beginPath()
+            ctx.lineWidth = 20
+            ctx.strokeStyle = '#2BC644';//进度条颜色值
+            ctx.arc(125,125,85,-90 * Math.PI / 180,((this.process.toFixed(0) / 100)*360 - 90)*Math.PI/180)
+            ctx.stroke()
+            ctx.closePath()
+            ctx.beginPath()
+            ctx.strokeStyle = 'red'
+            ctx.arc(125,125,85,((this.process.toFixed(0) / 100)*360 - 90)*Math.PI/180,((this.yichang.toFixed(0) / 100)*360 + ((this.process.toFixed(0) / 100)*360 - 90))*Math.PI/180)
+            ctx.stroke()
+            ctx.beginPath()
+            ctx.strokeStyle = '#FB9334'//缺考颜色值
+            ctx.arc(125,125,85,
+            ((this.yichang.toFixed(0) / 100)*360 + ((this.process.toFixed(0) / 100)*360 - 90))*Math.PI/180,
+            ((this.quekao.toFixed(0) / 100)*360 + (this.yichang.toFixed(0) / 100)*360 + ((this.process.toFixed(0) / 100)*360 - 90))*Math.PI/180)
+            ctx.stroke()
+            ctx.closePath()
+            // 扫描文字
+            ctx.beginPath()
+            ctx.font = "20px bold"
+            ctx.textAlign = 'center'
+            ctx.fillStyle = '#333333';//文字颜色值
+            ctx.fillText('开始扫描',125,155);//扫描的位置
+            ctx.closePath()
+            // 扫描百分比
+            ctx.beginPath()
+            ctx.font = "32px bold";
+            ctx.textAlign = 'center'
+            ctx.fillStyle = '#2E64FA';//扫描百分比颜色值
+            ctx.fillText(this.process.toFixed(0) + '%',125,125)
+            ctx.closePath()
+            ctx.restore()
+            this.id = requestAnimationFrame(this.animations)
+        }
     }
-    to {
-      transform: rotate(360deg);
+}
+</script>
+
+<style lang="scss" scoped> 
+.canvas_button{
+   width: 250px;
+   height: 250px;
+   background-color: transparent;
+   position: relative;
+   text-align: center;
+   justify-content:center;
+   align-items: center;
+    img
+    {
+        width: 100%;
+        height: 100%;
     }
-  }
+
+   #canvasProcess
+   {
+        position: absolute;
+        left: 35px;
+        top:35px;
+        z-index: 999;
+   }
+   .canvas_cr
+   {
+        width: 138px;
+        height: 138px;
+        
+        // box-shadow: 4px 4px 16px 0px rgba(46,100,250,0.4);
+        
+        // border-image: radial-gradient(circle, rgba(83, 46, 250, 0), rgba(77, 46, 250, 0.53), rgba(46, 100, 250, 1)) 1;;
+        // border: 2px solid red;
+        border-radius: 50%;
+        border:1px dashed #4D2EFA;
+
+  
+        position: absolute;
+        left: 55px;
+        top: 55px;
+        z-index: 1000;
+
+        animation:identifier 10s  linear infinite;
+   }
+
+   @keyframes identifier {
+        from {
+            transform: rotate(0deg);
+        }
+        to {
+            transform: rotate(360deg);
+        }
+        
+   }
 }
 </style>

+ 166 - 108
src/views/exam/scanList.vue

@@ -71,7 +71,7 @@
                     </el-table-column>
                     <el-table-column prop="name" label="操作" width="250" align="center">
                         <template v-slot="scope">
-                            <div class="ele_button table_row_button" v-if="scope.row.uploadStatus==0">
+                            <div class="ele_button table_row_button" v-if="scope.row.uploadStatus==0" style="color:#2E64FA;">
                                 <i class="el-icon-loading"></i>{{loadingText}}……
                             </div>
                             <div class="ele_button table_row_button" v-else>
@@ -97,16 +97,16 @@
             </div>
             <div class="right_center">
                 <div class="scan_buttons">
-                    <ScanButton  @click="OpenScan()"></ScanButton>
+                    <ScanButton :process="scanProcess" :quekao="scanQuekao" :yichang="scanYichang" @click="OpenScan()"></ScanButton>
                 </div>
                 <div class="scan_list">
                     <div class="list_item no_scan" >
-                        <div class="list_item_info " @click="GotoDetail(0)">
+                        <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_scan">{{scanDataInfo.unScanned}}人</span>
                                 <!-- <span class="number_no_icon"><img src="../../assets/icon/no_scan_icon.png"></span> -->
                             </div>
                         </div>
@@ -117,7 +117,7 @@
                                 缺考
                             </div>
                             <div class="item_info_number">
-                                <span class="number_no_exam">{{}}人</span>
+                                <span class="number_no_exam">{{scanDataInfo.examMissNum}}人</span>
                                 <!-- <span class="number_no_icon"><img src="../../assets/icon/miss_exam.png"></span> -->
                             </div>
                         </div>
@@ -128,7 +128,7 @@
                                 异常
                             </div>
                             <div class="item_info_number">
-                                <span class="number_abnormal">{{}}份</span>
+                                <span class="number_abnormal">{{scanDataInfo.abnormalNum}}份</span>
                                 <!-- <span class="number_no_icon"><img src="../../assets/icon/abnormal_icon.png"></span> -->
                             </div>
                         </div>
@@ -139,7 +139,7 @@
                                 已上传
                             </div>
                             <div class="item_info_number">
-                                <span class="number_uploaded">{{}}人</span>
+                                <span class="number_uploaded">{{scanDataInfo.scannedNum}}人</span>
                                 <!-- <span class="number_no_icon"><img src="../../assets/icon/sucess_upload.png"></span> -->
 
                             </div>
@@ -162,7 +162,7 @@ import { useRouter } from 'vue-router'
 import { onMounted ,ref,computed,onUnmounted,nextTick } from 'vue';
 import ScanButton from './components/scanButton.vue'
 import SelectStudent from './components/selectStudent.vue'
-import { hasImportStudent,getBatchList,getCurrentBatchNo,deleteBatch } from '@/api/exam'
+import { hasImportStudent,getBatchList,getCurrentBatchNo,deleteBatch,updateScanCount } from '@/api/exam'
 import scanCommon from '@/utils/scanCommon';
 import { ElMessageBox, ElMessage } from 'element-plus'
 import { formatTimestamp } from '@/utils/common';
@@ -203,8 +203,21 @@ const loadingText=ref('');//加载文本
 const baseUrl=import.meta.env.VITE_API_BASE_URL;
 const uploadUrl=`https://dev3.k12100.net/teaching/api/v1/ai_exam_scan/upload_multi_img`;//图片上传地址
 
-const tableData=ref([]);//批次列表
 
+//  定义数据类型接口
+interface BatchItem {
+  id?: string | number;
+  batchNo: string;
+  batchTypeName?: string;
+  scanUserName?: string;
+  scannedPaperNum?: number;
+  scannedTime?: number | string;
+  uploadNum?: number;
+  uploadStatus?: number; // 确保包含此属性
+  failedNumber?: number;
+  [key: string]: any; // 允许其他动态属性
+}
+const tableData=ref<BatchItem[]>([]); //批次列表
 const tableHeight=ref(500);
 const scanIdentifyList=[                
     {
@@ -219,7 +232,30 @@ const scanIdentifyList=[
 
 const isImportStudent=ref(false);//是否导入了学生名单
 const showSelectStudent=ref(false);//是否显示选择学生名单弹窗
+const scanDataInfo=ref({
+    abnormalNum:0,//异常数量
+    examMissNum:0,//缺考数量
+    unScanned:0,//未扫描数量
+    scannedNum:0,//已上传数量
+    examTotal:0,//考试总人数
+
+});//扫描返回的数据信息
+
+//扫描进度  
+const scanProcess=computed(() => {
+
+    return Math.floor((scanDataInfo.value.scannedNum+scanDataInfo.value.examMissNum)/scanDataInfo.value.examTotal)*100;
+});
 
+//缺考进度
+const scanQuekao=computed(() => {
+    return Math.floor((scanDataInfo.value.examMissNum)/scanDataInfo.value.examTotal)*100;
+});
+//异常进度
+const scanYichang=computed(() => {
+    
+    return Math.floor((scanDataInfo.value.abnormalNum)/scanDataInfo.value.examTotal)*100;
+});
 //刷新
 const Refresh = () => {
 
@@ -457,6 +493,11 @@ const GetScanBatchList=async()=>{
     {
         tableData.value=res.data.scannedWebSocketVO.data;
         selectSchoolId.value=res.data.schoolId;//获取学校id
+        scanDataInfo.value.abnormalNum=res.data.scannedWebSocketVO.abnormalNum;//异常数量
+        scanDataInfo.value.examMissNum=res.data.scannedWebSocketVO.examMissNum;//缺考数量
+        scanDataInfo.value.scannedNum=res.data.scannedWebSocketVO.scannedNum;//已上传数量
+        scanDataInfo.value.unScanned=res.data.scannedWebSocketVO.unScanned;//未扫描数量
+        scanDataInfo.value.examTotal=res.data.scannedWebSocketVO.examTotal;//总人数
     }
 }
 
@@ -479,116 +520,132 @@ const CalculateTableHeight = () => {
     tableHeight.value = computedHeight;
   });
 }; 
+
+
+//更新扫描张数
+const UpdateBatchScanNumber = (batchId: any, scanNumber: Number) => {
+    const params={
+        id:batchId,
+        recordNumber: scanNumber,
+    };
+    updateScanCount(params).then((res:any)=>{
+        if(res.code==200)
+        {
+           console.log("第"+batchId+"批次更新扫描张数"+scanNumber+"成功", res);
+        }
+
+    })
+};
 // 处理扫描结果
 const HandleScanResult = (res: any) => {
-  console.log('收到扫描数据', res);
-  // 业务逻辑...
-   if (res.action == 'uploading') {
-    let batchNumber: any = '';
-    if (res.batchNumber) 
+   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 = "启动扫描仪中";
+
+        ElMessage.success("正在启动扫描仪,请稍后…");
+        if (targetItem) 
+        {
+            targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
+        }
+   }
+    // 开始扫描指令
+    if (res.action == 'startScan') 
     {
-      batchNumber = GetBatchNumber(res.batchNumber || res.data?.batchNumber);
-    }
-    const targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
     
-    if (typeof loadingText !== 'undefined') loadingText.value = "启动扫描仪中";
+        let batchNumber = GetBatchNumber(res?.batchNumber || res.data?.batchNumber);
+        let currentItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
+        console.log("打印currentItem", currentItem);
+        
+        //开始扫描指令
+        if (res.code == 200) {
+            ElMessage.success("扫描仪启动成功,开始扫描…");
+            if (typeof loadingText !== 'undefined') loadingText.value = "正在扫描中";
+        }
+        
+        if (res.code == 502) 
+        {
+            console.log("开始扫描指令502错误  未检测到纸张或者卡纸", res);
+            console.log("打印currentItem", currentItem);
+            if (currentItem) {
+                currentItem.uploadStatus = 1; //更新上传状态
+            }
+            if (typeof loadingText !== 'undefined') loadingText.value = ""; //清空上传提示
+            isScanning.value = false; //重置扫描状态
+            ElMessage.error(res.msg);
+        }
+        
+        if (res.code == 510) 
+        {
+            console.log("启动扫描失败,上传正在进行中", res);
+            isScanning.value = true; //重置扫描状态
+            ElMessage.warning(res.msg + '请勿重复点击');
+        }
+        
+        if (res.code == 509) {
+            console.log("扫描仪正在使用中", res);
 
-    ElMessage.success("正在启动扫描仪,请稍后…");
-    if (targetItem) 
-    {
-      targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
-    }
-  }
+            if (res.msg == '未找到指定的扫描仪') {
+                isScanning.value = false; //重置扫描状态
+            }
 
-  if (res.action == 'startScan') {
-    // 修复:直接调用 GetBatchNumber,去掉 this
-    let batchNumber = GetBatchNumber(res?.batchNumber || res.data?.batchNumber);
-    // 修复:tableData.value
-    let currentItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
-    console.log("打印currentItem", currentItem);
-    
-    //开始扫描指令
-    if (res.code == 200) {
-      ElMessage.success("扫描仪启动成功,开始扫描…");
-      if (typeof loadingText !== 'undefined') loadingText.value = "正在扫描中";
-    }
-    
-    if (res.code == 502) 
-    {
-      console.log("开始扫描指令502错误  未检测到纸张或者卡纸", res);
-      console.log("打印currentItem", currentItem);
-      if (currentItem) {
-        currentItem.uploadStatus = 1; //更新上传状态
-      }
-      if (typeof loadingText !== 'undefined') loadingText.value = ""; //清空上传提示
-      isScanning.value = false; //重置扫描状态
-      ElMessage.error(res.msg);
-    }
-    
-    if (res.code == 510) 
-    {
-      console.log("启动扫描失败,上传正在进行中", res);
-      isScanning.value = true; //重置扫描状态
-      ElMessage.warning(res.msg + '请勿重复点击');
+            ElMessage.warning(res.msg + ',请稍后再试!');
+            if (currentItem) {
+                currentItem.uploadStatus = 1; //更新上传状态
+            }
+        }
     }
-    
-    if (res.code == 509) {
-      console.log("扫描仪正在使用中", res);
-
-      if (res.msg == '未找到指定的扫描仪') {
+    // 扫描完成指令
+    if (res.action == 'uploadFinish') {
+        console.log("上传完成uploadFinish 更新上传动画状态", res);
+        
+        // 修复:直接调用 GetBatchNumber
+        let batchNumber = GetBatchNumber(res?.batchNumber || res.data?.batchNumber);
+        let targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
+        
+        if (typeof loadingText !== 'undefined') loadingText.value = '正在上传中';
+        
+        if (targetItem) {
+        targetItem.scannedPaperNum = res.scanNumber; //更新扫描张数
+        targetItem.failedNumber = res.failedNumber; //更新失败张数
+        
+        if (typeof UpdateBatchScanNumber === 'function') {
+            UpdateBatchScanNumber(targetItem.id, Number(targetItem.scannedPaperNum));
+        }
+        }
         isScanning.value = false; //重置扫描状态
-      }
-
-      ElMessage.warning(res.msg + ',请稍后再试!');
-      if (currentItem) {
-        currentItem.uploadStatus = 1; //更新上传状态
-      }
     }
-  }
 
-  if (res.action == 'uploadFinish') {
-    console.log("上传完成uploadFinish 更新上传动画状态", res);
-    
-    // 修复:直接调用 GetBatchNumber
-    let batchNumber = GetBatchNumber(res?.batchNumber || res.data?.batchNumber);
-    let targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
-    
-    if (typeof loadingText !== 'undefined') loadingText.value = '正在上传中';
-    
-    if (targetItem) {
-      targetItem.scannedPaperNum = res.scanNumber; //更新扫描张数
-      targetItem.failedNumber = res.failedNumber; //更新失败张数
-      
-      // 修复:确保 UpdateBatchScanNumber 已定义并直接调用
-      if (typeof UpdateBatchScanNumber === 'function') {
-        UpdateBatchScanNumber(targetItem.id, targetItem.scannedPaperNum);
-      }
+    if (res.action == 'uploadNumber') {
+        console.log("上传完成uploadNumber 更新上传张数", res); 
+        // 逻辑已注释,保持原样
     }
-    isScanning.value = false; //重置扫描状态
-  }
-
-  if (res.action == 'uploadNumber') {
-    console.log("上传完成uploadNumber 更新上传张数", res); 
-    // 逻辑已注释,保持原样
-  }
 
-  if (res.action == 'scanNumber') {
-    console.log("扫描张数scanNumber 更新扫描张数");
-    // 修复:直接调用 GetBatchNumber
-    let batchNumber = GetBatchNumber(res.batchNumber || res.data?.batchNumber);
-    let targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
-    console.log("打印targetItem", targetItem);
-    
-    if (targetItem) {
-      targetItem.scannedPaperNum = res.number; //更新上传张数
-      targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
-    }
-    
-    // 修复:确保 UpdateBatchScanNumber 已定义
-    if (targetItem && typeof UpdateBatchScanNumber === 'function') {
-      UpdateBatchScanNumber(targetItem.id, targetItem.scannedPaperNum);
+    if (res.action == 'scanNumber') {
+        console.log("扫描张数scanNumber 更新扫描张数");
+        // 修复:直接调用 GetBatchNumber
+        let batchNumber = GetBatchNumber(res.batchNumber || res.data?.batchNumber);
+        let targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
+        console.log("打印targetItem", targetItem);
+        
+        if (targetItem) {
+        targetItem.scannedPaperNum = res.number; //更新上传张数
+        targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
+        }
+        
+        // 修复:确保 UpdateBatchScanNumber 已定义
+        if (targetItem && typeof UpdateBatchScanNumber === 'function') {
+            UpdateBatchScanNumber(targetItem.id, Number(targetItem.scannedPaperNum));
+        }
     }
-  }
 
   if (res.action == 'loadImage') {
     console.log("加载图片loadImage 获取图片数据", res);
@@ -688,6 +745,7 @@ const HandleScanResult = (res: any) => {
   if (res.action == 'scanFinishBatch') {
     if (typeof loadingText !== 'undefined') loadingText.value = '本次扫描结束';
     console.log("本批次扫描完成scanFinishBatch 更新扫描张数", res);
+
   } 
 
 };
@@ -697,10 +755,10 @@ onMounted(() => {
     scanCommon.init(HandleScanResult);
     // 监听连接状态
     scanCommon.watchConnection((isOnline,clientVersion) => {
-        console.log('连接状态:', isOnline ? '在线' : '离线');
+        
         scanClientStates.value=isOnline;
         scanClientVersion.value=clientVersion; //客户端版本号
-        console.log("客户端版本",clientVersion);
+        // console.log("客户端版本",clientVersion);
     });
     if (!examStore.currentExam) {
         console.warn('当前没有选中的考试信息')