Quellcode durchsuchen

扫描异常处理增加新增学生弹窗

dengshaobo vor 1 Monat
Ursprung
Commit
83f1eaae8f

+ 1 - 1
.env.development

@@ -3,4 +3,4 @@
 VITE_API_BASE_URL =
 VITE_RSA_PUBLIC_KEY = MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDmkP5hUxr7x3t76s9kiVrP1acGVp3JFyPsSoZzV+FvFSc78rfLSwEhj0WkUC0O+entDZ/hnvX1pSPsY8VwXtc20n5pSuo/G1HzrFSUCmFD9HWX3RRrFTHGn+G01CQswO/2JZokJQzbTsNDUnlK/w//NYQvXhxOhF9fM/S1L73CkYPIabHmIM1Rc8uik66XlTXrb9+gDQ8Uon+QZTGo3BzxWceONYYnVZZXdFQtzvaHLI5av8LSU5MLp03faX8fYlfB5FwyBL+dQZRVyTP09pbESn30487tsa0/+0pRZ1D+4Tsc2cjIpuXEg1IKGLrs0TSX78T1fROt/JcZ/lt3Tn9xAgMBAAECggEADEAD4/PgaSQuIWVWY4cQth4p46JSe86o7/L9tb8jkR1UmlDJBxoTE09jadmAq10H2rpwljI16zk88WBTqya+1IDWio2aaIPxFLtBOyRaCpxAazMp1I6puF3iRhNHYMFXfoJ88BKv3i8PHNKS8zMeDHcxcLrVUi6iSpKeG8pPkLjEqY0eccYjTUOyh2Q8YmLLwO3+lOOYrD9TcUZ1NqsZCqOI3pzLNONYatqaPprqiuZLUcloiO7D1OJ52LtTzlP2z7/jXM4fwUZK1/9U79zMYmjJifIORY9UiildkGaKzYs3G1ZAKiLKBehf7tGzorU0+tSIF99voyjhed286fMeqQKBgQDn7wH6oadnfZL5NknEaioua4GJ0LhBeZCiaLReYs116554zzBubb4Y752DTE4ka8LKsSIcnCqNbc/b6q0Kw+yThJJ5F/nFgaCzKHfhImqtslolqW5K1UVwUZ9RdFUeeqt8w+CtcnrJs/jIswv7XOnfHm6VKd0gSlpbnAPEVua75wKBgQD+farEb4PCNsOTaLQYcy0dSDh68lBskc5k+kMYmDsUloaOQel2S9ufEEYxZgy4Y2b0U9Y8nEJByFBoKZW+Mplp+hH9OWaK/QpMylh1uwoN44jUyRAKhR211kBZMXcgbIjr49PxG261OydxIyu9ntcJApVpHK30qzR+DBRD7H8+5wKBgQDGz/hQUZXgfqIoAkNFnQO/euQ1sLbhWUWEEmDar7MTq//R6zjG0EettGi/Df/F9KGrgh+NishnJ4SQLSBcJAp9gZzVNJoklbOdH8lzMT9k2Yew1QX4G81ENJNvDVuRnvG1J2tHAuUCVcWitOhGdiT732hHcPVeIp5F/Py1pxBubQKBgQDlVvJxm90tRJTzXsQN1J2vacocYgpADRXmwfF9VJLJdu1DffqadLoymkPneIO2Fz5MqNDERj0fcxmjBPbBNHA0pPtZLEVQs8B4e1FEp43j/kztFVSzZkrj93R97KniOm0Zx3LUMViPUgO1XXCprV8z63QiCYpql27yuIf6vkHduQKBgEe/BLwVHxAxmGzt/k0t9TZYn4MMQd5nx1UWlFzI+mNkNQqNC5cLGWarazeHC2YdcaYAsSMfi11NTvnVY79EbXYomxSBoN3Y63ML7W6Cj9fInVtUF6j002sdRyfey+e2BQtYiUHb9xW6A7sf4LyLZJ64LGYIh1uqWQc2iUeUsSWQ
 # webSocket环境地址配置
-VUE_APP_WEB_SOCKET_URL= wss://dev3.k12100.net/teaching/api/v1/teaching/websocket/
+VITE_WEB_SOCKET_URL= wss://dev3.k12100.net/teaching/api/v1/teaching/websocket/

+ 31 - 5
src/views/exam/abnormal/abnormal.vue

@@ -318,6 +318,10 @@
 
 
     </div>
+    <div class="page_dialog">
+        <AddStudent ref="addStudentRef"  v-model="showAddStudent" :selectSchoolId="schoolId"  />
+        <PaperPreview v-model="showPaperPreview" :imageList="previewImageList" :index="previewImageIndex" :showTool="true"></PaperPreview>
+    </div>
   </div>
 </template>
 <script lang="ts" setup>
@@ -325,14 +329,13 @@ import { useExamStore } from '@/store/exam'
 import { useRouter } from 'vue-router'
 import { onMounted,onUnmounted, ref, computed, nextTick, watch } from 'vue';
 import { ElMessage,ElMessageBox } from 'element-plus'
-
-// 引入可能需要的 API (请根据实际路径调整)
 import { getExamAbnormalList,getSearchScanStudents,claimAnswerCard, correctObjectiveQuestion,markPositionAbnormal, deletePaper ,reRecognizePosition,getTemplateDetail} from '@/api/exam'
 
 // 引入子组件 (确保路径正确)
-import PositionCanvas from '@/components/PositionCanvas.vue' // 假设路径
+import PositionCanvas from '@/components/PositionCanvas.vue' 
 import ActionImage from '@/components/ActionImage.vue'// 画布图片组件
-// import MarkScore from './components/MarkScore.vue'           // 假设路径
+import PaperPreview from '../components/ImagePreview.vue'//图片预览组件
+import AddStudent from '../components/addStudent.vue' // 新增学生弹窗
 
 // 实例化 Store
 const examStore = useExamStore()
@@ -405,7 +408,12 @@ const currentDrawData= ref<any[]>([]);//画框数据
 const currentPaperList = ref<any[]>([]) // 底部缩略图列表
 const currentPageInfo = ref<any>({})    // 当前页面信息
 
-
+//图片预览相关
+const showPaperPreview=ref(false);//图片预览
+const previewImageList= ref<any[]>([]);//图片预览图片列表
+const previewAnswerData= ref<any>({});//图片预览答案数据
+const previewImageIndex= ref(0);//图片预览图片索引
+const showAddStudent=ref(false);//新增学生弹窗
 
 
 // 定位异常相关
@@ -911,6 +919,12 @@ const PendingListRowClick = (row: any, column: any, event: Event) => {
     console.log('点击行:', row)
     if(abnormalType.value==2)
     {
+
+        //定位异常处理
+        currentItem.value=row;
+        currentPaperList.value=currentItem.value.scanPictureVOS || [];//试卷图片列表
+        currentPaperUrl.value=currentPaperList.value[0].recognizeUrl;//当前图片地址
+        currentImagePageNo.value=currentPaperList.value[0].pageNo;//当前图片的页码
         
     }
     //考号异常
@@ -1232,12 +1246,24 @@ const GetSearchScanStudentList = async () => {
 const OpenAddStudentDialog = () => {
     // TODO: 打开新增弹窗
     console.log('新增考生')
+    showAddStudent.value=true;
 }
 
 // 20. 考号异常 - 查看试卷
 const OpenViewPaper = (row: any) => {
     // TODO: 预览该学生的试卷
     console.log('查看试卷:', row)
+
+    previewImageList.value = [];
+    row.scanPictureVOS.forEach((image: any) => {
+        console.log("image", image);
+        let obj = {
+            url: image.recognizeUrl,
+            pageNo: image.pageNo,
+        };
+        previewImageList.value.push(obj);
+    });
+    showPaperPreview.value = true;
 }
 
 // 21. 考号异常 - 认领

+ 1 - 1
src/views/exam/abnormal/tableList.vue

@@ -122,7 +122,7 @@
                     @current-change="ChangePage"
                     background
                     
-                    :page-sizes="[10,50,100]"
+                    :page-sizes="[20,50,100]"
                     layout="sizes,prev, pager, next"
                     @size-change="ChangePageSize"
                     :current-page="pageInfo.pageNum"

+ 1 - 1
src/views/exam/abnormalDetail.vue

@@ -21,7 +21,7 @@
                     </div>
                 </div>
             </div>
-            <div class="detail_content">
+            <div class="detail_content" v-if="showDialog">
                 <!-- 异常 -->
                 <Abnormal v-if="scanState == 3"></Abnormal>
                 <!-- 状态  0  未扫描  1 已上传 2  缺考  3 异常 -->

+ 215 - 0
src/views/exam/components/ImagePreview.vue

@@ -0,0 +1,215 @@
+<template>
+  <el-dialog 
+    title="" 
+    :model-value="showDialog" 
+    @update:model-value="showDialog = $event"
+    class="preview_dialog" 
+    :modal-append-to-body="false"
+    append-to-body
+    fullscreen 
+    height="100%"
+  >
+    <div class="preview_image">
+      <img 
+        :src="currenImageUrl" 
+        alt="" 
+        :style="{ transform: `scale(${zoomLevel}) rotate(${rotateAngle}deg)` }"
+      />
+      
+      <div class="preview_close" @click="CloseDialog()">
+        <el-icon><Close /></el-icon>
+      </div>
+      <div 
+        class="preview_last" 
+        :class="currentIndex==0?'button_diasble':''" 
+        v-if="imageList.length>1" 
+        @click="LastImage()"
+      >
+        <el-icon><ArrowLeft /></el-icon>
+      </div>
+      <div 
+        class="preview_next" 
+        :class="currentIndex==imageList.length-1?'button_diasble':''"  
+        v-if="imageList.length>1" 
+        @click="NextImage()"
+      >
+        <el-icon><ArrowRight /></el-icon>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch, computed } from 'vue';
+
+
+interface ImageItem {
+  url: string;
+  rotateAngle?: number;
+  pageNo?: number;
+}
+
+interface Props {
+  modelValue: boolean;
+  imageList: ImageItem[];
+  index?: number;
+  showTool?: boolean;
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: boolean): void;
+  (e: 'GetRotateAngle', data: { currentIndex: number; rotateAngle: number }): void;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  imageList: () => [],
+  index: 0,
+  showTool: false,
+});
+
+const emit = defineEmits<Emits>();
+
+const currenImageUrl = ref('');
+const currentIndex = ref(0);
+const zoomLevel = ref(1);
+const maxZoom = ref(3);
+const minZoom = ref(0.5);
+const zoomStep = ref(0.1);
+const rotateAngle = ref(0);
+
+const toolList = [
+  {
+    title: '放大',
+    icon: 'icon_fangda',
+    value: '1',
+  },
+  {
+    title: '缩小',
+    icon: 'icon_suoxiao',
+    value: '2',
+  },
+  {
+    title: '逆时针90°',
+    icon: 'icon_nishizhen90',
+    value: '5',
+  },
+  {
+    title: '顺时针90°',
+    icon: 'icon_shunshizhen90',
+    value: '6',
+  },
+];
+
+const showDialog = computed({
+  get() {
+    return props.modelValue;
+  },
+  set(val) {
+    emit('update:modelValue', val);
+  },
+});
+
+watch(
+  () => props.modelValue,
+  (newVal) => {
+    if (newVal && props.imageList.length > 0) {
+      const targetIndex = props.index || 0;
+      currenImageUrl.value = props.imageList[targetIndex].url;
+      rotateAngle.value = props.imageList[targetIndex].rotateAngle || 0;
+      currentIndex.value = targetIndex;
+    }
+  }
+);
+
+const CloseDialog = () => {
+  showDialog.value = false;
+  let obj = {
+    currentIndex: currentIndex.value,
+    rotateAngle: rotateAngle.value,
+  };
+  emit('GetRotateAngle', obj);
+};
+
+const GetReturn = () => {
+  let obj = {
+    currentIndex: currentIndex.value,
+    rotateAngle: rotateAngle.value,
+  };
+  emit('GetRotateAngle', obj);
+};
+
+const LastImage = () => {
+  if (currentIndex.value > 0) {
+    currentIndex.value--;
+  }
+  currenImageUrl.value = props.imageList[currentIndex.value].url;
+  rotateAngle.value = props.imageList[currentIndex.value].rotateAngle || 0;
+};
+
+const NextImage = () => {
+  if (currentIndex.value < props.imageList.length - 1) {
+    currentIndex.value++;
+  }
+  currenImageUrl.value = props.imageList[currentIndex.value].url;
+  rotateAngle.value = props.imageList[currentIndex.value].rotateAngle || 0;
+};
+
+const ToolClick = (item: any) => {
+  if (item.value === '1') {
+    ZoomIn();
+  }
+  if (item.value === '2') {
+    ZoomOut();
+  }
+  if (item.value === '5') {
+    RotateCounterClockwise90();
+  }
+  if (item.value === '6') {
+    RotateClockwise90();
+  }
+};
+
+const ZoomIn = () => {
+  if (zoomLevel.value < maxZoom.value) {
+    zoomLevel.value += zoomStep.value;
+  }
+};
+
+const ZoomOut = () => {
+  if (zoomLevel.value > minZoom.value) {
+    zoomLevel.value -= zoomStep.value;
+  }
+};
+
+const RotateCounterClockwise90 = () => {
+  rotateAngle.value -= 90;
+  GetReturn();
+};
+
+const RotateClockwise90 = () => {
+  rotateAngle.value += 90;
+  GetReturn();
+};
+</script>
+
+<style lang="scss" scoped>
+.preview_canvas {
+  width: 100%;
+  height: 100%;
+  .main_container {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    background: transparent;
+    user-select: none;
+    overflow: hidden;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    .paper_template {
+      position: absolute;
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/exam/components/ReIdentify.vue

@@ -141,7 +141,7 @@ const reIdentifyText = ref('开始识别');     // 重新识别按钮文案
 const reIdentifyLoading = ref(false);       // 重新识别按钮loading状态
 
 // --- WebSocket 相关 ---
-const websocketUrl = ref(import.meta.env.VUE_APP_WEB_SOCKET_URL || '');  // WebSocket链接地址
+const websocketUrl = ref(import.meta.env.VITE_WEB_SOCKET_URL || '');  // WebSocket链接地址
 const socket = ref<any>(null);                                       // WebSocket实例对象
 
 // --- 进度条配置 ---
@@ -287,7 +287,7 @@ const ReIdentifyWebSocketLink = () => {
         websocketUrl.value = 'wss://dev3.k12100.net/teaching/api/v1/teaching/websocket/';
     } else if (import.meta.env.MODE === 'production') {
         // 生产环境使用环境变量
-        websocketUrl.value = import.meta.env.VUE_APP_WEB_SOCKET_URL || '';
+        websocketUrl.value = import.meta.env.VITE_WEB_SOCKET_URL || '';
     }
 
     // 拼接完整的 WebSocket URL

+ 21 - 7
src/views/exam/examList.vue

@@ -154,8 +154,8 @@ import CircleProcess from '@/views/exam/components/CircleProcess.vue'
 const examStore = useExamStore()
 const router = useRouter()
 
-const examRoomList=ref([]);//考场列表
-const examStudentList=ref([]);//考场学生列表
+const examRoomList=ref<any[]>([]);//考场列表
+const examStudentList=ref<any[]>([]);//考场学生列表
 const selectSchoolId=ref(0);
 const searchContent=ref('');//搜索内容
 const selectExamRoomCode=ref('');//选择的考场code
@@ -210,10 +210,7 @@ const BackToScan = () => {
 
 // 监听选择的考场代码变化,自动更新考场名称
 watch(selectExamRoomCode, (newCode) => {
-  if (!newCode || !examRoomList.value || examRoomList.value.length === 0) {
-    selectExamRoomName.value = '';
-    return;
-  }
+
 
   // 在考场列表中查找匹配的项
   const selectedRoom = examRoomList.value.find((room: any) => room.examRoomCode === newCode);
@@ -365,7 +362,24 @@ const GetExamRoomList = async () => {
         const res = await getExamRoomList(params);
         if (res.code === 200) {
             examRoomList.value = res.data.examRoomTblVOS;
-            selectExamRoomCode.value = examRoomList.value[0]?.examRoomCode;
+            console.log('打印考场列表',res);
+            let allExamRoomItem={
+                abnormalNum: 0,//异常数据
+                examRoomCode:'',//考场编号
+                examRoomName:'全部',//考场名称
+                examRoomSort:'',//考场排序
+
+                missExamNum:res.data.totalMissExamNum,//缺考学生数量
+                totalStudentNum:res.data.totalStudentNum,//总学生数量
+                uploadedNum:res.data.totalUploadedNum,//已上传学生数量
+                uploadedRate:res.data.totalUploadRate,//上传进度
+
+            };//第一条全部数据
+            examRoomList.value.unshift(allExamRoomItem);//添加第一条数据
+            selectExamRoomCode.value=examRoomList.value[0].examRoomCode;
+            selectExamRoomName.value=examRoomList.value[0].examRoomName;
+            // selectExamRoomNum.value=examRoomList[0].totalStudentNum;
+
 
             examInfo.value={
                 classCount:res.data.examRoomNum,//班级数

+ 1 - 3
src/views/exam/scanList.vue

@@ -509,16 +509,14 @@ const ConfigRequestUrlByEnv= () =>{
     if (import.meta.env.MODE === 'development') 
     {
         //开发环境
-        console.log("打印import.meta.env.VITE_API_BASE_URL", import.meta.env.VITE_API_BASE_URL);
+        console.log("打印import.meta.env.VITE_API_BASE_URL", import.meta.env.VITE_WEB_SOCKET_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);
-
     }
           
 }

+ 4 - 0
vite.config.ts

@@ -14,6 +14,10 @@ export default defineConfig({
   server: {
     host: '0.0.0.0',//允许所有ip访问
     port: 8088,//默认端口
+    // 添加缓存控制
+    headers: {
+      'Cache-Control': 'no-store',
+    },
     proxy:{
      
       '/api':{