Jelajahi Sumber

扫描完成联调

dengshaobo 1 bulan lalu
induk
melakukan
eb581678d1
4 mengubah file dengan 236 tambahan dan 46 penghapusan
  1. 19 1
      src/api/exam.ts
  2. 42 16
      src/views/exam/abnormal/abnormal.vue
  3. 8 6
      src/views/exam/question.vue
  4. 167 23
      src/views/exam/scanList.vue

+ 19 - 1
src/api/exam.ts

@@ -322,8 +322,26 @@ export const correctObjectiveQuestion= (data:any):Promise<ApiResponse> => {
 // 35. 扫描学生  定位异常 重新识别
 export const reRecognizePosition= (data:any):Promise<ApiResponse> => {
   return request({
-    url: '/api/v1/ai_exam_scan/delete_objective_question',
+    url: '/api/v1/ai_exam_scan/ai_hand_recognize_again',
     method: 'post',
     data
   })
+}
+
+// 36. 扫描学生  定位异常 获取模板详细信息
+export const getTemplateDetail= (data:any):Promise<ApiResponse> => {
+  return request({
+    url: '/api/v1/smart_template/find_ai_template_information',
+    method: 'get',
+    params: data ,
+  })
+}
+
+// 37. 扫描学生  扫描完成  
+export const finishScanned= (data:any):Promise<ApiResponse> => {
+  return request({
+    url: '/api/v1/ai_exam_scan/finish_ai_scanned',
+    method: 'post',
+    data ,
+  })
 }

+ 42 - 16
src/views/exam/abnormal/abnormal.vue

@@ -327,7 +327,7 @@ import { onMounted,onUnmounted, ref, computed, nextTick, watch } from 'vue';
 import { ElMessage,ElMessageBox } from 'element-plus'
 
 // 引入可能需要的 API (请根据实际路径调整)
-import { getExamAbnormalList,getSearchScanStudents,claimAnswerCard, correctObjectiveQuestion,markPositionAbnormal, deletePaper ,reRecognizePosition} from '@/api/exam'
+import { getExamAbnormalList,getSearchScanStudents,claimAnswerCard, correctObjectiveQuestion,markPositionAbnormal, deletePaper ,reRecognizePosition,getTemplateDetail} from '@/api/exam'
 
 // 引入子组件 (确保路径正确)
 import PositionCanvas from '@/components/PositionCanvas.vue' // 假设路径
@@ -344,6 +344,10 @@ const examSubjectId = computed(() => {
   return examStore.currentExam?.id
 })//计算属性
 
+const paperSchema=computed(() => {
+    return 1;
+})//1-单面 2-双面
+
 const schoolId = computed(() => {
   return 0;
 })//学校id
@@ -405,7 +409,7 @@ const currentPageInfo = ref<any>({})    // 当前页面信息
 
 
 // 定位异常相关
-const usedCardType = ref(1) // 1-模板 2-学生
+const usedCardType = ref(2) // 1 系统卡  2 三方卡  默认2
 const sourcePoints = ref<any[]>([]) // 模板点位
 const targetPoints = ref<any[]>([]) // 学生点位
 const currentTemplateList = ref<any[]>([]) // 模板页码列表
@@ -455,7 +459,7 @@ const choiceHeader = ref([])
 const PaperCanvas = ref()
 const ActionCanvas = ref()
 const objectQuestionTable = ref<HTMLElement | null>(null);
-const numberPendingTable=ref<HTMLElement | null>(null);//考号异常待处理表格高度
+const numberPendingTable=ref<any>(null);//考号异常待处理表格高度
 
 // ==================== 计算属性 ====================
 
@@ -734,11 +738,11 @@ const GetPositionSelNumber = (row: any) => {
 const ReCutPaper = async () => {
     
     
-    // if(currentPaperList.value.length==0)
-    // {
-        // ElMessage.error('没有定位异常哦!');
+    if(currentPaperList.value.length==0)
+    {
+        ElMessage.error('没有定位异常哦!');
         return
-    // }
+    }
 
     // 先判断是否图片都做过校正对应处理
     const unsavedItems = currentPaperList.value.filter(item => !item.isSave);
@@ -752,8 +756,13 @@ const ReCutPaper = async () => {
         ElMessage.error(`请给第${indices}张图片设置对应的模板页码!`);
         return;
     }
-
-    let pictures=[];
+    let pictures: Array<{
+        picId: any;
+        pageNo: any;
+        rotate: any;
+        sourcePoints: number[][];
+        targetPoints: number[][];
+    }> = [];
     let templatePageList=[];
     console.log("打印当前卡类型",usedCardType.value);
     console.log("打印当前的卡类型",currentPaperList.value);
@@ -781,6 +790,7 @@ const ReCutPaper = async () => {
             console.log("打印三方卡的定位坐标",sourcePoints,targetPoints);
             if(item.pageNo==-1)
             {
+                //空白页
                 sourcePoints=[[0,0],[0,0]];
                 targetPoints=[[0,0],[0,0]];
             }
@@ -821,7 +831,7 @@ const ReCutPaper = async () => {
     
     let param={
         examSubjectId:examSubjectId.value,
-        schema:paperSchema.value,//双面扫描 1-单面 2-双面
+        // schema:paperSchema.value,//双面扫描 1-单面 2-双面
         pictures:pictures,//图片列表集合 图片id picId  图片页码 pageNo
     }; 
     
@@ -829,8 +839,7 @@ const ReCutPaper = async () => {
     reCutText.value='识别中,请稍候...';
     
     try {
-        // 注意:需要在 API 文件中添加 reRecognizePosition 方法
-        // import { reRecognizePosition } from '@/api/exam'
+
         const res = await reRecognizePosition(param);
         
         reCutLoading.value=false;//重新识别loading
@@ -1330,8 +1339,26 @@ const GetAbnormalList = async () => {
     }
 
 }
+ 
+//获取模板详情
+const GetTemplateDetail = async () => { 
+    const params={
+        examSubjectId: examSubjectId.value,//考试id
+    };
+    const res = await getTemplateDetail(params);
+    console.log("打印模板数据",res);
+    if(res.code == 200)
+    {
        
 
+
+    }
+    else
+    {
+
+    }
+}
+
 // ==================== 生命周期 ====================
 
 onMounted(() => {
@@ -1343,6 +1370,7 @@ onMounted(() => {
         // LoadAbnormalList(abnormalType.value)
     }
     GetAbnormalList();
+    GetTemplateDetail();//获取模板数据
     // 监听窗口变化调整高度
     window.addEventListener('resize', CalculateTableHeight)
     CalculateTableHeight()
@@ -1367,16 +1395,14 @@ const CalculateTableHeight = () => {
     nextTick(() => {
 
         tableHeightRight.value = window.innerHeight - 220;//定位表格的高度
-        if(abnormalType.value==2)
+        if(abnormalType.value==3)
         {
             //考号异常表格高度计算
-            const tableContainerEl = numberPendingTable.value;
+            const tableContainerEl = numberPendingTable.value?.$el || numberPendingTable.value;
             const numberRect = tableContainerEl && 
                             typeof tableContainerEl.getBoundingClientRect === 'function' ? 
                             tableContainerEl.getBoundingClientRect() : null;
             const containerHeight = numberRect ? numberRect.height : 200;
-            console.log("打印表格高度",containerHeight);
-
             numberTableHeight.value=window.innerHeight - containerHeight - 345;
 
         }

+ 8 - 6
src/views/exam/question.vue

@@ -3,11 +3,13 @@
     <div class="question_header">
         <div class="item_title">
             <div class="item_step">1</div>
-            扫描标准答案:<el-button type="primary"  style="width: 88px;">开始扫描</el-button>  <span class="preview_btn">预览</span>
-            <el-button style="margin-left: 10px;width: 116px;">引用试题结构</el-button>
+            扫描标准答案:<el-button type="primary"  style="width: 88px;">开始扫描</el-button>  
+            <!-- <span class="preview_btn">预览</span> -->
+            <!-- <el-button style="margin-left: 10px;width: 116px;">引用试题结构</el-button> -->
         </div>
         <div class="choose_template">
-            当前模板:{{templateName}}   <span class="choose_btn" @click="OpenChooseTemplate()">更换</span>
+            当前模板:{{templateName}}  
+             <!-- <span class="choose_btn" @click="OpenChooseTemplate()">更换</span> -->
         </div>
     </div>
     <div class="page_jg_20"></div>
@@ -26,18 +28,18 @@
                 </el-select>
             </div>
             <div class="item_search">
-                <div class="search_name">题目类型:</div>
+                <div class="search_name">题目类型</div>
                 <el-select  v-model="queryParams.questionType"   placeholder="请选择"  class="select_width" >
                     <el-option label="单选题" :value="1"></el-option>
                     <el-option label="多选题" :value="2"></el-option>
                 </el-select>
             </div>
             <div class="item_search">
-                <div class="search_name">每题满分:</div>
+                <div class="search_name">每题满分</div>
                 <el-input v-model="queryParams.fullMark" placeholder="请输入" maxlength="3" @input="(val: any) => onScoreInput(queryParams, 'fullMark', val)"   class="select_width" ></el-input>
             </div>
             <div class="item_search">
-                <div class="search_name">分组:</div>
+                <div class="search_name">分组</div>
                 <el-input v-model="queryParams.groupName" placeholder="请输入"  class="select_width" ></el-input>
             </div>
             <div class="item_search">

+ 167 - 23
src/views/exam/scanList.vue

@@ -3,12 +3,12 @@
     
     <div class="search_content">
         <div class="content_left">
-                <span class="scan_state_title">客户端状态:</span> 
+                <span class="scan_state_title">客户端状态</span> 
                 <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>
-                <span v-if="scanClientStates" class="scan_state_title" style="color: #999;">(当前客户端版本:{{scanClientVersion}})</span>
+                <span v-if="scanClientStates" class="scan_state_title" style="color: #999;">(当前客户端版本{{scanClientVersion}})</span>
             <!-- <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"
@@ -93,7 +93,7 @@
             <div class="right_header">
                <span> 扫描 设置</span>
                <span>
-                识别号:
+                识别号
                 <el-select v-model="params.batchNo" placeholder="请选择" @change="GoSearch()" style="width: 120px;">
                     <el-option v-for="item in scanIdentifyList" :key="item.value" :label="item.label" :value="item.value"></el-option>
                 </el-select>
@@ -152,7 +152,7 @@
                 </div>
             </div>
             <div class="right_button">
-                <el-button type="primary" @click="GoSearch()" style="width:calc(100% - 40px);">扫描完成</el-button>
+                <el-button type="primary" @click="FinishScanned()" style="width:calc(100% - 40px);">扫描完成</el-button>
             </div>
         </div>
     </div>
@@ -167,7 +167,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,updateScanCount } from '@/api/exam'
+import { finishScanned,hasImportStudent,getBatchList,getCurrentBatchNo,deleteBatch,updateScanCount } from '@/api/exam'
 import scanCommon from '@/utils/scanCommon';
 import { ElMessageBox, ElMessage } from 'element-plus'
 import { formatTimestamp } from '@/utils/common';
@@ -184,6 +184,14 @@ const examSubjectId = computed(() => {
   return examStore.currentExam?.id
 })//计算属性
 
+const userInfo=computed(() => {
+    return userStore.userInfo;
+})//用户信息
+
+const schoolType=computed(() => {
+    return ''
+})
+
 //考试科目code
 const examSubjectCode=computed(() => {
     return examStore.currentExam?.examSubjectCode
@@ -211,10 +219,10 @@ const currentBatchNoId=ref('');//当前批次号id
 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 updateCountUrl=`https://dev3.k12100.net/teaching/api/v1/ai_exam_scan/record_batch_scan_number`;
 
 const webSocket=ref<InstanceType<typeof WebSocketManager> | null>(null);//websocket
-const websocketUrl=import.meta.env.VITE_WEB_SOCKET_URL;//websocket链接地址
+let websocketUrl=import.meta.env.VITE_WEB_SOCKET_URL;//websocket链接地址
 //  定义数据类型接口
 interface BatchItem {
   id?: string | number;
@@ -273,14 +281,58 @@ const Refresh = () => {
     GetScanBatchList();
 }
 
+//扫描完成
+const FinishScanned= async () =>{
+    console.log("打印完成")
+    ElMessageBox.confirm('确认完成吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+        customClass:'page_dialog'
+    }).then(async () => {
+        
+        let param={
+            id:examSubjectId.value
+        };
+
+        const res = await finishScanned(param)
+        if(res.code == 200) 
+        {
+            ElMessage.success("完成成功!")
+        }
+        else
+        {
+            ElMessage.error(res.msg);
+        
+        }
+    })
+}
+
+// 根据环境配置请求地址
+const ConfigRequestUrlByEnv= () =>{
+    console.log("打印环境", import.meta.env.MODE);
+    if (import.meta.env.MODE === 'development') 
+    {
+        //开发环境
+        console.log("打印import.meta.env.VITE_API_BASE_URL", import.meta.env.VITE_API_BASE_URL);
+        websocketUrl = 'wss://dev3.k12100.net/teaching/api/v1/teaching/websocket/'
+        console.log("设置webSocket地址",websocketUrl);
+
+    } else if (import.meta.env.MODE === 'production') 
+    {
+        websocketUrl = import.meta.env.VITE_WEB_SOCKET_URL || '';
+        console.log("打印websocket地址", websocketUrl);
+        console.log("打印上传图片地址", uploadUrl);
+
+    }
+          
+}
+
 //获取未上传图片的数量
 const GetNoUploadNumber=(item:BatchItem)=>
 {
     const scannedPaperNum = item.scannedPaperNum ?? 0;
     const uploadNum = item.uploadNum ?? 0;
-    
-    console.log("打印item", item);
-    
     const result = scannedPaperNum - uploadNum;
     
     return result > 0 ? result : 0;
@@ -295,23 +347,99 @@ const WebSocketLink = () => {
     let userCourseId='';
     let schoolIdValue = userInfo.value.schoolId; // 这里逻辑是 单校都取登录用户的学校ID 如果是联校账号 给单校扫描 传单校的学校id
 
-    if(schoolType.value == 1) {
-        //联校 盟主学校
-        schoolIdValue = selectSchoolId.value;
-    } else {
-        schoolIdValue = userInfo.value.schoolId;
-    }
+    // if(schoolType.value == 1) {
+    //     //联校 盟主学校
+    //     schoolIdValue = selectSchoolId.value;
+    // } else {
+    //     schoolIdValue = userInfo.value.schoolId;
+    // }
     
     console.log("打印用户信息", userInfo.value);
-    userCourseId = `${userInfo.value.id}_${examSubjectId.value}_${schoolIdValue}_${currentBatchNo.value}`; //用户 科目 批次号
-    
-    const url = `${websocketUrl.value}${userCourseId}`;
+    userCourseId = `${userInfo.value.userId}_${examSubjectId.value}_${schoolIdValue}_${currentBatchNo.value}`; //用户 科目 批次号
+    console.log("websocketUrl",websocketUrl);
+    const url = `${websocketUrl}${userCourseId}`;
 
     console.log("打印websocket链接地址url", url);
     webSocket.value = new WebSocketManager(url, MessageBack);
     webSocket.value.start();
 }
 
+//websocket消息返回
+const MessageBack = (data:any) => { 
+
+    // console.log("打印websocket返回数据", data);
+    const msg=JSON.parse(data);
+    console.log("Websocket消息返回处理",msg);
+    //code 0 建立成功  200 业务成功  400 业务失败 异常 需要提示信息
+    if(msg.code==0)
+    {
+        //建立成功
+        console.log("和后端的websocket建立成功");
+    }
+    else if(msg.code==200)
+    {
+        //业务成功
+        // console.log("打印当前tableData的数据",this.tableData);
+        //正常扫描
+        let currentData=msg.data[0];
+        let targetItem = tableData.value.find(item => item.batchNo == currentData.batchNo);
+        console.log("识别成功+1",targetItem);
+        if(msg.abnormalNum==-1)
+        {
+            scanDataInfo.value.unScanned =  msg.unScanned;
+            scanDataInfo.value.examMissNum = msg.examMissNum;
+            scanDataInfo.value.abnormalNum = msg.abnormalNum;
+            scanDataInfo.value.scannedNum = msg.scannedNum;
+            if (targetItem) {
+                if(currentData.uploadNum==-1)
+                {
+                    targetItem.uploadNum++;
+                }
+            }
+        }
+        else
+        {
+            if (targetItem) {
+                targetItem.uploadNum = msg.data[0].uploadNum;
+            }
+            scanDataInfo.value.unScanned =  msg.unScanned;
+            scanDataInfo.value.examMissNum = msg.examMissNum;
+            scanDataInfo.value.abnormalNum = msg.abnormalNum;
+            scanDataInfo.value.scannedNum = msg.scannedNum;
+
+            let process = (Number(scanDataInfo.value.scannedNum) + Number(scanDataInfo.value.examMissNum)) / Number(scanDataInfo.value.examTotal) * 100
+            if (isNaN(process)) {
+                process = 0
+            }
+
+            
+        }
+        if(targetItem)
+        {
+            if(Number(targetItem.scannedPaperNum) === Number(targetItem.uploadNum))
+            {
+                targetItem.uploadStatus=1;
+                loadingText.value='';
+                isScanning.value=false;
+                // removeBeforeUnloadListener();// 移除页面离开阻止监听器
+            }
+            else
+            {
+            }
+        }
+        
+        
+        
+    }
+    else if(msg.code==400)
+    {
+        //业务失败
+        GetScanBatchList();
+        ElMessage.error(msg.message);
+    }
+}
+
+
 //重新识别弹窗
 const OpenReIdentify=() => {
 
@@ -370,16 +498,20 @@ const OpenScan = () => {
                     "paperSchema":1,//this.paperSchema,// 单双面//页面类型  1 单面  2  双面
                     "token":localStorage.getItem('token'),//token信息
                     "uploadUrl":uploadUrl, // 图片上传地址
+                    "updateCountUrl":updateCountUrl,//更新扫描纸张数量接口  接口参数 批次id post 请求 id
+                    "batchId":currentBatchNoId.value,//批次号id
+
                     "dpi":150,//dpi参数 设置图片清晰度质量的
                     "isColor":1,//是否彩色图片 手阅卡扫描彩色图片 0  黑白  1 彩色
-                    "useDriveUI":1,//startScan和scanTemplate增加useDriveUI参数,=1时会弹框,其他值或不写则不弹
+                    "useDriveUI":0,//startScan和scanTemplate增加useDriveUI参数,=1时会弹框,其他值或不写则不弹
                     "jsonParam":JSON.stringify(jsonParam),// 后端使用的参数 json字符串 后端接口固定三个参数  1:jsonParam  2:seqNumber(这个批次的图片序号:从1开始)   3:file 图片文件
                 };//正式版参数 
                 console.log("打印发送的数据",JSON.stringify(json));
                 setTimeout(() => {
                     scanCommon.send(JSON.stringify(json))
                 },100)
-                
+                // 建立与后端的websocket连接
+                WebSocketLink();
             }
             else
             {
@@ -807,8 +939,10 @@ const HandleScanResult = (res: any) => {
     });
   }
 
-  if (res.action == 'scanFinishBatch') {
+  if (res.action == 'scanFinishBatch') 
+  {
     if (typeof loadingText !== 'undefined') loadingText.value = '本次扫描结束';
+
     console.log("本批次扫描完成scanFinishBatch 更新扫描张数", res);
 
   } 
@@ -832,7 +966,17 @@ onMounted(() => {
     HasImportStudent();//查询是否导入了学生名单
     GetScanBatchList();//获取扫描批次列表
     CalculateTableHeight();//初始化计算表格高度
-  
+    console.log("打印当前的配置",import.meta.env.VUE_APP_BASE);
+    //环境判断
+    if (import.meta.env.VUE_APP_BASE === 'https://www.k12100.com') 
+    {
+
+    }
+    else
+    {
+        ConfigRequestUrlByEnv();///连接本地时候使用
+    }
+
     // 监听窗口大小变化
     window.addEventListener('resize', CalculateTableHeight);
 });