Преглед изворни кода

扫描客户端增加版本号显示 扫描批次删除更新

dengshaobo пре 3 недеља
родитељ
комит
badbc10e33
7 измењених фајлова са 534 додато и 42 уклоњено
  1. 1 1
      .env.development
  2. 46 3
      src/store/user.ts
  3. 9 4
      src/styles/common.scss
  4. 44 0
      src/utils/common.ts
  5. 13 7
      src/utils/scanCommon.ts
  6. 407 25
      src/views/exam/scanList.vue
  7. 14 2
      src/views/login/login.vue

+ 1 - 1
.env.development

@@ -1,4 +1,4 @@
 # //开发环境配置
 # //开发环境配置
 
 
-VITE_API_BASE_URL = 
+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
 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

+ 46 - 3
src/store/user.ts

@@ -1,31 +1,74 @@
 import { defineStore } from 'pinia'
 import { defineStore } from 'pinia'
 import { ref, computed } from 'vue'
 import { ref, computed } from 'vue'
 
 
+// 定义用户信息接口类型
+export interface UserInfo {
+  userId?: string | number;
+  userName?: string;
+  schoolId?: string | number;
+  schoolName?: string;
+  accountName?: string; // 登录账号名称
+  phone?: string;       // 手机号
+  [key: string]: any;   // 允许其他额外字段
+}
 //用户相关的状态管理
 //用户相关的状态管理
 export const useUserStore = defineStore('user', () => {
 export const useUserStore = defineStore('user', () => {
   // State
   // State
   const schoolLogo = ref<string>('')
   const schoolLogo = ref<string>('')
   
   
   // 如果需要管理 userInfo,也可以放在这里,但通常 userInfo 存在 localStorage 中
   // 如果需要管理 userInfo,也可以放在这里,但通常 userInfo 存在 localStorage 中
-  // 这里我们主要解决 header.vue 中用到的 schoolLogo
-  
+  const userInfo = ref<UserInfo>(JSON.parse(localStorage.getItem('userInfo') || '{}'))
   // Getters (可选,如果需要计算属性)
   // Getters (可选,如果需要计算属性)
   const hasLogo = computed(() => !!schoolLogo.value)
   const hasLogo = computed(() => !!schoolLogo.value)
 
 
+  //便捷获取用户具体信息的getters
+  const userId = computed(() => userInfo.value.userId)
+  const userName = computed(() => userInfo.value.userName)
+  const schoolId = computed(() => userInfo.value.schoolId)
+  const schoolName = computed(() => userInfo.value.schoolName)
+  const accountName = computed(() => userInfo.value.accountName)
+  const phone = computed(() => userInfo.value.phone)
+
   // Actions
   // Actions
   function setSchoolLogo(logo: string) {
   function setSchoolLogo(logo: string) {
     schoolLogo.value = logo
     schoolLogo.value = logo
   }
   }
 
 
+  // 新增:设置用户信息
+  function setUserInfo(info: UserInfo) {
+    userInfo.value = info
+    // 同步存储到 localStorage,确保持久化
+    localStorage.setItem('userInfo', JSON.stringify(info))
+  }
+  // 新增:更新部分用户信息
+  function updateUserInfo(partialInfo: Partial<UserInfo>) {
+    userInfo.value = { ...userInfo.value, ...partialInfo }
+    localStorage.setItem('userInfo', JSON.stringify(userInfo.value))
+  }
+
+  // 清除用户状态
   function clearUserState() {
   function clearUserState() {
     schoolLogo.value = ''
     schoolLogo.value = ''
-    // 清除其他用户相关状态
+    userInfo.value = {}
+    // 清除 localStorage 中的用户信息
+    localStorage.removeItem('userInfo')
   }
   }
 
 
   return {
   return {
     schoolLogo,
     schoolLogo,
     hasLogo,
     hasLogo,
     setSchoolLogo,
     setSchoolLogo,
+
+    // 返回新增的用户相关状态和方法
+    userInfo,
+    userId,
+    userName,
+    schoolId,
+    schoolName,
+    accountName,
+    phone,
+    setUserInfo,
+    updateUserInfo,
     clearUserState
     clearUserState
   }
   }
 })
 })

+ 9 - 4
src/styles/common.scss

@@ -1409,10 +1409,10 @@ body {
     
     
   }
   }
 
 
-  .is-active
-  {
-    color:#2e64fa !important;
-  }
+  // .is-active
+  // {
+  //   color:#2e64fa !important;
+  // }
 
 
   .el-tabs__item{
   .el-tabs__item{
     font-size:16px;
     font-size:16px;
@@ -3471,4 +3471,9 @@ body {
   justify-content: center;
   justify-content: center;
   gap: 10px;
   gap: 10px;
 
 
+  .el-icon-loading
+  {
+    color: #2E64FA;
+  }
+
 }
 }

+ 44 - 0
src/utils/common.ts

@@ -25,4 +25,48 @@ export const getCourseBgColor = (courseName: string): string => {
     return COURSE_COLOR_MAP['default']
     return COURSE_COLOR_MAP['default']
   }
   }
   return COURSE_COLOR_MAP[courseName] || COURSE_COLOR_MAP['default']
   return COURSE_COLOR_MAP[courseName] || COURSE_COLOR_MAP['default']
+}
+
+/**
+ * 将时间戳转换为指定格式的时间字符串
+ * @param timestamp 时间戳(秒或毫秒)
+ * @param fmt 格式化字符串,默认为 'YYYY-MM-DD HH:mm:ss'
+ * @returns 格式化后的时间字符串
+ */
+export const formatTimestamp = (timestamp: number | string | null | undefined, fmt: string = 'YYYY-MM-DD HH:mm:ss'): string => {
+  if (!timestamp) return '-';
+  
+  // 统一转换为数字类型
+  let ts = Number(timestamp);
+  
+  // 如果时间戳是10位(秒级),转换为13位(毫秒级)
+  if (ts.toString().length === 10) {
+    ts = ts * 1000;
+  }
+
+  const date = new Date(ts);
+  
+  // 如果日期无效,返回原值或横杠
+  if (isNaN(date.getTime())) return '-';
+
+  const o: Record<string, number> = {
+    'M+': date.getMonth() + 1,                 // 月份
+    'D+': date.getDate(),                      // 日
+    'H+': date.getHours(),                     // 小时
+    'm+': date.getMinutes(),                   // 分
+    's+': date.getSeconds(),                   // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    'S': date.getMilliseconds()                // 毫秒
+  };
+
+  if (/(Y+)/.test(fmt)) {
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
+  }
+
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k] + '') : (('00' + o[k]).substr(('' + o[k]).length)));
+    }
+  }
+  return fmt;
 }
 }

+ 13 - 7
src/utils/scanCommon.ts

@@ -9,11 +9,12 @@ interface ScanMessage {
   action?: ScanAction;
   action?: ScanAction;
   data?: any;
   data?: any;
   message?: string;
   message?: string;
+  param?: any; // 扩展参数,用于传递额外信息
 }
 }
 
 
 // 定义回调函数类型
 // 定义回调函数类型
 type ScanCallback = (data: ScanMessage) => void;
 type ScanCallback = (data: ScanMessage) => void;
-type ConnectionCallback = (isOnline: boolean) => void;
+type ConnectionCallback = (isOnline: boolean,clientVersion: string) => void;
 
 
 class ScanCommonService {
 class ScanCommonService {
   // 扫描客户端WebSocket 服务器地址
   // 扫描客户端WebSocket 服务器地址
@@ -25,6 +26,7 @@ class ScanCommonService {
   // 当前客户端连接状态
   // 当前客户端连接状态
   public isOnline: boolean = false;
   public isOnline: boolean = false;
   public isScanned: boolean = false; // 是否有扫描仪 是否可以扫描
   public isScanned: boolean = false; // 是否有扫描仪 是否可以扫描
+  public clientVersion: string = '1.0.0'; // 客户端版本号
 
 
   // 心跳定时器
   // 心跳定时器
   private timerPing: number | null = null;
   private timerPing: number | null = null;
@@ -100,10 +102,14 @@ class ScanCommonService {
             if (objectData != null) {
             if (objectData != null) {
               if (objectData.code === 200) {
               if (objectData.code === 200) {
                 if (objectData.action === 'connectMessage') {
                 if (objectData.action === 'connectMessage') {
-                  ElMessage({
-                    type: 'success',
-                    message: '客户端连接成功……'
-                  });
+                    
+                    this.clientVersion = objectData.param.appVersion || '';
+                    const version = objectData.param.appVersion || '未知版本';
+
+                    ElMessage({
+                      type: 'success',
+                      message: `客户端连接成功……版本号:${version}`
+                    });
                 } else if (objectData.action === 'getScannerList') {
                 } else if (objectData.action === 'getScannerList') {
                   // 获取扫描仪结果
                   // 获取扫描仪结果
                   if (objectData.data && Array.isArray(objectData.data) && objectData.data.length > 0) {
                   if (objectData.data && Array.isArray(objectData.data) && objectData.data.length > 0) {
@@ -300,9 +306,9 @@ class ScanCommonService {
           };
           };
           this.ws?.send(JSON.stringify(json));
           this.ws?.send(JSON.stringify(json));
         }
         }
-        callback(true); // 告诉网页 客户端打开状态
+        callback(true,this.clientVersion); // 告诉网页 客户端打开状态
       } else {
       } else {
-        callback(false); // 告诉网页 连接已经断开
+        callback(false,this.clientVersion); // 告诉网页 连接已经断开
       }
       }
     };
     };
 
 

+ 407 - 25
src/views/exam/scanList.vue

@@ -8,6 +8,7 @@
                     <i class="iconfont icon_open"></i>已打开</span>
                     <i class="iconfont icon_open"></i>已打开</span>
                 <span class="scan_state_close" v-else>
                 <span class="scan_state_close" v-else>
                     <i class="iconfont icon_close"></i>未打开</span>
                     <i class="iconfont icon_close"></i>未打开</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-select  v-model="params.batchNo"   placeholder="选择批次" @change="GoSearch()" class="select_width" >
                 <el-option label="全部批次" value=""></el-option>
                 <el-option label="全部批次" value=""></el-option>
                 <el-option v-for="item in batchList"
                 <el-option v-for="item in batchList"
@@ -42,30 +43,43 @@
         <div class="content_table">
         <div class="content_table">
             <div class="page_table">
             <div class="page_table">
                 <el-table :data="tableData" style="width: 100%" :height="tableHeight">
                 <el-table :data="tableData" style="width: 100%" :height="tableHeight">
-                    <el-table-column prop="questionName" label="序号" width="100" align="center" >
+                    <!-- <el-table-column prop="questionName" label="序号" width="100" align="center" >
+                    </el-table-column> -->
+                    <el-table-column prop="batchNo" label="批次" width="120" align="center">
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="questionType" label="批次" width="120" align="center">
+                    <el-table-column prop="scannedPaperNum" label="扫描张数"  align="center">
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="questionType" label="考号" width="120" align="center">
+                    <el-table-column prop="uploadNum" label="待上传张数" align="center" >
+                        <template v-slot="scope">
+                            
+                        </template>
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="date" label="客观题" align="center">
+                    <el-table-column prop="uploadNum" label="已上传张数" align="center" >
                         <template v-slot="scope">
                         <template v-slot="scope">
                             
                             
                         </template>
                         </template>
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="fullScore" label="状态" width="120" align="center">
+                    <el-table-column prop="scanUserName" label="扫描人" align="center" >
                         <template v-slot="scope">
                         <template v-slot="scope">
-                            <div class="full_mark_input">
-                                <el-input v-model="scope.row.fullScore" maxlength="3" @input="(val: any) => onScoreInput(scope.row, 'fullScore', val)"></el-input>
-                            </div>
+                            
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="scanUserName" label="扫描时间" align="center">
+                        <template v-slot="scope">
+                            {{formatTimestamp(scope.row.scannedTime)   }}
                         </template>
                         </template>
                     </el-table-column>
                     </el-table-column>
-                    <el-table-column prop="name" label="操作" width="150" align="center">
+                    <el-table-column prop="name" label="操作" width="250" align="center">
                         <template v-slot="scope">
                         <template v-slot="scope">
-                            <div class="ele_button table_row_button">
-                                <span class="btn_editor">编辑</span>
-                                <span class="btn_delete" @click="DeleteSingle(scope.row)">删除</span>
+                            <div class="ele_button table_row_button" v-if="scope.row.uploadStatus==0">
+                                <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_delete" v-if="scope.row.scanUserName==currentUserName " @click="OptionDelete(scope.row)">删除</span>
+                                <span class="editor_disable" v-else>删除</span>
                             </div>
                             </div>
+
                     </template>
                     </template>
                     </el-table-column>
                     </el-table-column>
                 </el-table>
                 </el-table>
@@ -143,16 +157,19 @@
 </template>
 </template>
 <script lang="ts" setup>
 <script lang="ts" setup>
 import { useExamStore } from '@/store/exam'
 import { useExamStore } from '@/store/exam'
+import { useUserStore } from '@/store/user'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 import { onMounted ,ref,computed,onUnmounted,nextTick } from 'vue';
 import { onMounted ,ref,computed,onUnmounted,nextTick } from 'vue';
 import ScanButton from './components/scanButton.vue'
 import ScanButton from './components/scanButton.vue'
 import SelectStudent from './components/selectStudent.vue'
 import SelectStudent from './components/selectStudent.vue'
-import { hasImportStudent,getBatchList } from '@/api/exam'
+import { hasImportStudent,getBatchList,getCurrentBatchNo,deleteBatch } from '@/api/exam'
 import scanCommon from '@/utils/scanCommon';
 import scanCommon from '@/utils/scanCommon';
 import { ElMessageBox, ElMessage } from 'element-plus'
 import { ElMessageBox, ElMessage } from 'element-plus'
-
+import { formatTimestamp } from '@/utils/common';
 // 实例化 Store
 // 实例化 Store
 const examStore = useExamStore()
 const examStore = useExamStore()
+const userStore = useUserStore()
+
 const router = useRouter()
 const router = useRouter()
 
 
 // 考试科目 ID
 // 考试科目 ID
@@ -160,15 +177,33 @@ const examSubjectId = computed(() => {
   return examStore.currentExam?.id
   return examStore.currentExam?.id
 })//计算属性
 })//计算属性
 
 
+//考试科目code
+const examSubjectCode=computed(() => {
+    return examStore.currentExam?.examSubjectCode
+})
+//当前登录人姓名
+const currentUserName=computed(() => {
+    return userStore.userName;
+})
+
+const selectSchoolId=ref(0);//学校ID
+
 const params=ref({
 const params=ref({
     batchNo:'',
     batchNo:'',
     keyWord:''
     keyWord:''
 })
 })
 
 
-const batchList=ref([]);//批次列表
-const listMode=ref('list');
 const scanClientStates=ref(false);//客户端状态
 const scanClientStates=ref(false);//客户端状态
-const tableData=ref([]);
+const scanClientVersion=ref('');//客户端版本
+const isScanning=ref(false);//是否正在扫描 扫描状态
+const hasMakeTemplate=ref(false);//模版是否制作完成
+const currentBatchNo=ref('');//当前批次号
+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 tableData=ref([]);//批次列表
 
 
 const tableHeight=ref(500);
 const tableHeight=ref(500);
 const scanIdentifyList=[                
 const scanIdentifyList=[                
@@ -197,11 +232,75 @@ const OpenReIdentify=() => {
 
 
 //开始扫描
 //开始扫描
 const OpenScan = () => {
 const OpenScan = () => {
+
+    if(isScanning.value)
+    {
+        //正在扫描
+        ElMessage.warning('正在扫描中,请勿重复点击哦!');
+        return;
+    }
+
     //判断客户端是否已经连接
     //判断客户端是否已经连接
     if(scanClientStates.value)
     if(scanClientStates.value)
     {
     {
         //一打开客户端 
         //一打开客户端 
         console.log('客户端已打开,开始扫描');
         console.log('客户端已打开,开始扫描');
+        // isScanning.value=true;//扫描状态
+        const params={
+            examSubjectId:examSubjectId.value,
+            schoolId:selectSchoolId.value,
+        };
+        getCurrentBatchNo(params).then((res:any)=>{
+            console.log("当前获取批次结果",res);
+            if(res.code==200)
+            {
+                currentBatchNo.value=res.data.batchNo;
+                currentBatchNoId.value=res.data.id;
+                let newData={
+                    batchNo:currentBatchNo.value,
+                    id:currentBatchNoId.value,//批次号
+                    batchTypeName:'扫描',
+                    scanUserName:currentUserName.value,
+                    scannedPaperNum:0,//扫描张数
+                    scannedTime:Date.now(),//扫描时间
+                    uploadNum:0,//上传张数
+                    uploadStatus:0,//扫描状态
+                };
+                tableData.value.push(newData);
+                //开始扫描
+                let jsonParam={
+                    examSubjectId:examSubjectId.value,
+                    batchNumber:currentBatchNo.value,
+                    // sensitive: 35,//灵敏度参数 固定35
+                    filter:0,//是否过滤参数 1-是 0-否
+                    // heightRatio:this.heightRatio,//占打分框高度比例
+                    schoolId:selectSchoolId.value//学校id  联校单校都需要学校id
+                };
+                let json = {
+                    "action":"startScan",//交互指令参数
+                    "batchNumber":GetBatchStr(res.data.id,currentBatchNo.value),//批次号  这里传给客户端的批次号需要进行处理 截取批次id后5位拼接batchNumber
+                    "subjectCode":examSubjectCode.value,//科目编号
+                    "paperSchema":1,//this.paperSchema,// 单双面//页面类型  1 单面  2  双面
+                    "token":localStorage.getItem('token'),//token信息
+                    "uploadUrl":uploadUrl, // 图片上传地址
+                    "dpi":150,//dpi参数 设置图片清晰度质量的
+                    "isColor":1,//是否彩色图片 手阅卡扫描彩色图片 0  黑白  1 彩色
+                    "useDriveUI":1,//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)
+                
+            }
+            else
+            {
+                ElMessage.error(res.msg);
+            }
+            
+        });
+
     }
     }
     else
     else
     {
     {
@@ -210,6 +309,80 @@ const OpenScan = () => {
     }
     }
 }
 }
 
 
+//获取批次字符串
+const GetBatchStr=(batchId:any,batChNo:any)=>{
+    const batchNoValue = batchId.substring(batchId.length - 5);
+    const batchNo=String(batChNo).padStart(3, '0');
+    const batchNumber=batchNoValue+batchNo;//传给客户端的图片批次号 id后五位数加上00批次号
+    return batchNumber;
+}
+
+//根据处理后的批次号转换成处理前的批次号
+const GetBatchNumber=(batchNumber:any)=>{
+    if (!batchNumber) {
+        console.warn('批次号为空或未定义');
+        return currentBatchNo.value;//返回当前的批次号
+    }
+    
+    const batchStr = batchNumber.toString();
+    if (batchStr.length < 3) {
+        console.warn('批次号长度小于3位:', batchStr);
+        return currentBatchNo.value;//返回当前的批次号
+    }
+    
+    const lastThree = batchStr.slice(-3);
+    const parsedNumber = parseInt(lastThree, 10);
+    
+    if (isNaN(parsedNumber)) {
+        console.error('无法解析批次号:', lastThree);
+        return currentBatchNo.value;//返回当前的批次号
+    }
+    console.log("打印最后的批次号",parsedNumber);
+    return parsedNumber.toString();
+}
+
+//删除批次
+const OptionDelete=(row: any) => {
+    console.log('删除批次', row);
+    
+    if(row.scanUserName==currentUserName.value)
+    {
+        ElMessageBox.confirm('确定要删除该批次吗?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+        }).then(() => {
+            // 删除操作
+            console.log('删除', row);
+            const params={
+                examSubjectId:examSubjectId.value,
+                batchNo:row.batchNo,
+                schoolId:selectSchoolId.value,
+            };
+            deleteBatch(params).then((res:any)=>{
+                if(res.code==200)
+                {
+                    ElMessage.success("删除成功!")
+                    GetScanBatchList(); 
+                }
+                else
+                {
+                    ElMessage.error(res.msg);
+                }
+            })
+            
+        }).catch(() => {    
+            // 取消操作
+            console.log('取消删除');
+        });
+    }
+    else
+    {
+        ElMessage.warning('只能删除自己上传的记录');
+    }
+}
+
+//跳转到异常详情页
 const GotoDetail = (type: number) => {
 const GotoDetail = (type: number) => {
     //根据类型跳转到不同的详情页
     //根据类型跳转到不同的详情页
     // switch (type) {
     // switch (type) {
@@ -282,7 +455,8 @@ const GetScanBatchList=async()=>{
     const res = await getBatchList(params);
     const res = await getBatchList(params);
     if(res.code==200)
     if(res.code==200)
     {
     {
-        batchList.value=res.data;
+        tableData.value=res.data.scannedWebSocketVO.data;
+        selectSchoolId.value=res.data.schoolId;//获取学校id
     }
     }
 }
 }
 
 
@@ -306,18 +480,227 @@ const CalculateTableHeight = () => {
   });
   });
 }; 
 }; 
 // 处理扫描结果
 // 处理扫描结果
-const HandleScanResult = (data: any) => {
-  console.log('收到扫描数据', data);
+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 = "启动扫描仪中";
+
+    ElMessage.success("正在启动扫描仪,请稍后…");
+    if (targetItem) 
+    {
+      targetItem.uploadStatus = 0; //更新上传状态 0 开始上传
+    }
+  }
+
+  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 + '请勿重复点击');
+    }
+    
+    if (res.code == 509) {
+      console.log("扫描仪正在使用中", res);
+
+      if (res.msg == '未找到指定的扫描仪') {
+        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);
+      }
+    }
+    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 == 'loadImage') {
+    console.log("加载图片loadImage 获取图片数据", res);
+    
+    // 修复:reImageList 必须是 ref
+    if (typeof reImageList !== 'undefined') {
+      reImageList.value = res.data || [];
+      reImageList.value.forEach((item: any) => {
+        item.uploadStatus = -2; 
+        item.uploadProgress = 0; 
+      });
+    }
+    
+    let batchNumber = GetBatchNumber(res.batchNumber);
+    let targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
+    
+    console.log("打印重新上传图片列表", reImageList.value);
+    console.log("打印重新上传的批次Item", targetItem);
+    
+    if (targetItem) {
+      targetItem.uploadStatus = 0; 
+    }
+    
+    if (typeof loadingText !== 'undefined') loadingText.value = '正在上传中';
+    if (typeof uploadDialogTitle !== 'undefined') uploadDialogTitle.value = '未上传的图片';
+    if (typeof showUploadDialog !== 'undefined') showUploadDialog.value = true;
+  }
+
+  if (res.action == 'finish') {
+    console.log("图片重传finish指令", res);
+    
+    let batchNumber = GetBatchNumber(res.batchNumber);
+    let targetItem = tableData.value.find((item: any) => item.batchNo == batchNumber);
+
+    let fileIndex = res.fileIndex.split(",");
+    console.log("打印fileIndex", fileIndex);
+    
+    fileIndex.forEach((index: string) => {
+      // 修复:reImageList.value
+      let reImageItem = reImageList.value.find((item: any) => item.fileIndex == index);
+      if (reImageItem) {
+        let progress = reImageItem.uploadProgress;
+        const interval = setInterval(() => {
+          progress += 5;
+          if (progress >= 100) {
+            progress = 100;
+            clearInterval(interval);
+            // 修复:Vue3 不需要 $set,直接赋值即可触发响应式
+            reImageItem.uploadStatus = res.uploadState;
+          }
+          reImageItem.uploadProgress = progress;
+        }, 100);
+      }
+    });
+
+    // 修复:reImageCount 可能是 ref 或计算属性
+    let currentReImageCount = typeof reImageCount !== 'undefined' ? reImageCount.value : reImageList.value.length;
+    
+    if (currentReImageCount === 0) {
+      if (typeof showUploadDialog !== 'undefined') showUploadDialog.value = false;
+      if (targetItem) {
+        targetItem.uploadStatus = 1; 
+        isScanning.value = false;
+        // 修复:确保 GetBatchList 已定义
+        if (typeof GetBatchList === 'function') GetBatchList();
+      }
+    }
+  }
+
+  if (res.action == 'getFailedImage') {
+    console.log("获取失败图片getFailedImage", res);
+    let failedList = res.data || [];
+    
+    failedList.forEach((item: any) => {
+      let batchNumber = GetBatchNumber(item.batchNumber);
+      let currentItem = tableData.value.find((item1: any) => item1.batchNo == batchNumber);
+      
+      if (currentItem) {
+        if (item.failedNumber == 0) {
+          if (currentItem.scannedPaperNum != currentItem.uploadNum) {
+            currentItem.scannedPaperNum = currentItem.uploadNum;
+            if (typeof UpdateBatchScanNumber === 'function') {
+              UpdateBatchScanNumber(currentItem.id, currentItem.uploadNum);
+            }
+          }
+        } else {
+          let number = currentItem.uploadNum + item.failedNumber;
+          currentItem.scannedPaperNum = number;
+          if (typeof UpdateBatchScanNumber === 'function') {
+            UpdateBatchScanNumber(currentItem.id, number);
+          }
+        }
+      }
+    });
+  }
+
+  if (res.action == 'scanFinishBatch') {
+    if (typeof loadingText !== 'undefined') loadingText.value = '本次扫描结束';
+    console.log("本批次扫描完成scanFinishBatch 更新扫描张数", res);
+  } 
+
 };
 };
 
 
 onMounted(() => {
 onMounted(() => {
-    // 1. 初始化连接
+    // 初始化连接
     scanCommon.init(HandleScanResult);
     scanCommon.init(HandleScanResult);
-    // 2. 监听连接状态(可选)
-    scanCommon.watchConnection((isOnline) => {
+    // 监听连接状态
+    scanCommon.watchConnection((isOnline,clientVersion) => {
         console.log('连接状态:', isOnline ? '在线' : '离线');
         console.log('连接状态:', isOnline ? '在线' : '离线');
         scanClientStates.value=isOnline;
         scanClientStates.value=isOnline;
+        scanClientVersion.value=clientVersion; //客户端版本号
+        console.log("客户端版本",clientVersion);
     });
     });
     if (!examStore.currentExam) {
     if (!examStore.currentExam) {
         console.warn('当前没有选中的考试信息')
         console.warn('当前没有选中的考试信息')
@@ -325,8 +708,7 @@ onMounted(() => {
     }
     }
     HasImportStudent();//查询是否导入了学生名单
     HasImportStudent();//查询是否导入了学生名单
     GetScanBatchList();//获取扫描批次列表
     GetScanBatchList();//获取扫描批次列表
-     // 初始化计算
-    CalculateTableHeight();
+    CalculateTableHeight();//初始化计算表格高度
   
   
     // 监听窗口大小变化
     // 监听窗口大小变化
     window.addEventListener('resize', CalculateTableHeight);
     window.addEventListener('resize', CalculateTableHeight);

+ 14 - 2
src/views/login/login.vue

@@ -86,10 +86,11 @@ import { ref, onMounted } from 'vue'
 import { login, getUserInfo } from '@/api/login'
 import { login, getUserInfo } from '@/api/login'
 import { ElMessage } from 'element-plus' // 
 import { ElMessage } from 'element-plus' // 
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
-
+import { useUserStore } from '@/store/user'
 // 1. 引入封装好的加密方法
 // 1. 引入封装好的加密方法
 import { encryptPassword } from '@/utils/jsencrypt'
 import { encryptPassword } from '@/utils/jsencrypt'
 
 
+const userStore = useUserStore()
 const router = useRouter() // 获取路由实例
 const router = useRouter() // 获取路由实例
 // 响应式数据
 // 响应式数据
 const userName = ref('huijiaoyan')
 const userName = ref('huijiaoyan')
@@ -147,7 +148,18 @@ const SubmitLogin = () => {
     console.log("获取用户信息返回",userInfoRes)
     console.log("获取用户信息返回",userInfoRes)
     if (userInfoRes && userInfoRes.code === 200) {
     if (userInfoRes && userInfoRes.code === 200) {
       // 存储用户信息到 localStorage 或 Pinia
       // 存储用户信息到 localStorage 或 Pinia
-      localStorage.setItem('userInfo', JSON.stringify(userInfoRes.data))
+      // localStorage.setItem('userInfo', JSON.stringify(userInfoRes.data))
+      userStore.setUserInfo({
+        userId: userInfoRes.data.id,
+        userName: userInfoRes.data.userName,
+        schoolId: userInfoRes.data.schoolId,
+        schoolName: userInfoRes.data.schoolName,
+        accountName: userInfoRes.data.loginName,
+        phone: userInfoRes.data.phone,
+        identify: userInfoRes.data.identify,
+        roleNames: userInfoRes.data.roleNames,
+        roleCodes: userInfoRes.data.roleCodes,
+      })
       ElMessage.success('登录成功');
       ElMessage.success('登录成功');
       // 跳转到主页
       // 跳转到主页
       router.push('/main/choice')
       router.push('/main/choice')