lsc 3 週間 前
コミット
78a364d97c
1 ファイル変更188 行追加18 行削除
  1. 188 18
      src/views/Student/index.vue

+ 188 - 18
src/views/Student/index.vue

@@ -551,6 +551,9 @@ const isCreator = ref(false) // 是否为创建人
 const isFollowModeActive = ref(false) // 跟随模式是否开启
 const isFirstEnter = ref(true) // 是否首次进入
 
+// 用户信息
+const userJson = ref<any>(null)
+
 // 计算未提交作业的学生
 const unsubmittedStudents = computed(() => {
   if (!studentArray.value || !workArray.value) return []
@@ -572,6 +575,8 @@ const providerSocket = ref<WebsocketProvider | null>(null)
 const writingBoardSyncDataURL = ref<string | null>(null)
 const writingBoardSyncBlackboard = ref<boolean | null>(null)
 const mId = ref<string | null>(null)
+// 画图延迟发送定时器
+const drawingDelayTimer = ref<NodeJS.Timeout | null>(null)
 
 // WebSocket重连相关变量
 const reconnectAttempts = ref(0)
@@ -1073,15 +1078,23 @@ const handleDrawingEnd = (dataURL: string) => {
         }
       })
     }
-    // 广播消息(包含当前小黑板状态)
-    const currentBlackboard = yWritingBoardState.value?.get('blackboard') || false
-    sendMessage({ 
-      type: 'writing_board_update', 
-      slideId: currentSlide.value.id,
-      dataURL: dataURL,
-      blackboard: currentBlackboard,
-      courseid: props.courseid 
-    })
+
+    // 延迟5秒后广播消息,避免频繁发送
+    if (drawingDelayTimer.value) {
+      clearTimeout(drawingDelayTimer.value)
+    }
+
+    drawingDelayTimer.value = setTimeout(() => {
+      const currentBlackboard = yWritingBoardState.value?.get('blackboard') || false
+      sendMessage({
+        type: 'writing_board_update',
+        slideId: currentSlide.value.id,
+        dataURL: dataURL,
+        blackboard: currentBlackboard,
+        courseid: props.courseid
+      })
+      drawingDelayTimer.value = null
+    }, 5000) // 延迟5秒发送
   }
 }
 
@@ -1187,6 +1200,40 @@ const clearLaserState = () => {
   }
 }
 
+// 清空所有同步状态(仅创建人)
+const clearAllSyncStates = () => {
+  try {
+    if (props.type == '1' && isCreator.value && docSocket.value) {
+      console.log('🧹 创建老师退出,清空所有同步状态')
+      docSocket.value.transact(() => {
+        // 清空消息
+        const messageArray = docSocket.value?.getArray?.('message')
+        if (messageArray) {
+          messageArray.delete(0, messageArray.length)
+        }
+        // 清空计时器状态
+        const timerStateMap = docSocket.value?.getMap?.('timerState')
+        if (timerStateMap) {
+          timerStateMap.clear()
+        }
+        // 清空激光笔状态
+        const laserStateMap = docSocket.value?.getMap?.('laserState')
+        if (laserStateMap) {
+          laserStateMap.clear()
+        }
+        // 清空画图状态
+        const writingBoardStateMap = docSocket.value?.getMap?.('writingBoardState')
+        if (writingBoardStateMap) {
+          writingBoardStateMap.clear()
+        }
+      })
+    }
+  }
+  catch (e) {
+    console.warn('清空所有同步状态失败', e)
+  }
+}
+
 // 获取导入导出功能
 const { readJSON, exportJSON2, getFile } = useImport()
 
@@ -1546,6 +1593,8 @@ const handleHomeworkSubmit = async () => {
   }
 
   isSubmitting.value = true
+  let homeworkContent: string = '作业提交' // 默认作业内容
+  let hasSubmitWork = false // 标记是否成功提交作业
 
   try {
     // 获取所有iframe元素
@@ -1557,8 +1606,6 @@ const handleHomeworkSubmit = async () => {
       return
     }
 
-    let hasSubmitWork = false
-
     for (let i = 0; i < iframes.length; i++) {
       const iframe = iframes[i] as HTMLIFrameElement
       const iframeSrc = iframe.src
@@ -1579,6 +1626,16 @@ const handleHomeworkSubmit = async () => {
             // 支持同步和异步submitWork
             const result = await iframeWindow.submitWork(...submitArgs)
             console.log('submitWork同步执行完成')
+            // 尝试从结果中获取作业内容
+            if (result && typeof result === 'object') {
+              homeworkContent = JSON.stringify(result)
+            }
+            else if (result) {
+              homeworkContent = String(result)
+            }
+            else {
+              homeworkContent = 'workPage作业提交'
+            }
             message.success('作业提交成功')
             hasSubmitWork = true
             
@@ -1616,6 +1673,7 @@ const handleHomeworkSubmit = async () => {
             const file = new File([blob], `ai_work_${Date.now()}.json`, { type: 'application/json' })
             const fileUrl = await uploadFile(file)
             console.log('文件上传成功,链接:', fileUrl)
+            homeworkContent = fileUrl // 保存AI作业内容
             
             // 使用上传后的链接提交作业
             await submitWork(iframeSlideIndex, '72', fileUrl, '20')
@@ -1820,6 +1878,7 @@ const handleHomeworkSubmit = async () => {
           
           const imageFile = base64ToFile(imageData, `screenshot_${Date.now()}.png`)
           const imageUrl = await uploadFile(imageFile)
+          homeworkContent = imageUrl // 保存截图URL作为作业内容
           // 提交截图
           await submitWork(slideIndex.value, '73', imageUrl, '1') // 73表示截图工具,21表示图片类型
           message.success('页面截图提交成功')
@@ -1918,10 +1977,17 @@ const handleHomeworkSubmit = async () => {
     console.error('作业提交过程中出错:', error)
     message.error('作业提交失败')
     isSubmitting.value = false
+    addOp3(1, new Date().getTime(), { courseid: props.courseid, homeworkContent }, 'error')
   }
   finally {
     // isSubmitting.value = false
     getWork(true)
+    if (hasSubmitWork) {
+      addOp3(1, new Date().getTime(), { courseid: props.courseid, homeworkContent }, 'success')
+    }
+    else {
+      addOp3(1, new Date().getTime(), { courseid: props.courseid, homeworkContent: '未找到可用的作业提交功能' }, 'error')
+    }
   }
 }
 
@@ -2752,13 +2818,112 @@ const handlePageUnload = () => {
   if (isCreator.value && timerIndicator.value.visible && props.type === '1') {
     sendMessage({ type: 'timer_stop', courseid: props.courseid })
   }
-  // 创建老师刷新/关闭页面时,清空激光笔和画图共享状态
+  // 创建老师刷新/关闭页面时,清空所有同步状态
   if (isCreator.value && props.type === '1') {
-    clearLaserState()
-    clearWritingBoardState()
+    clearAllSyncStates()
+  }
+
+  // 清理画图延迟发送定时器
+  if (drawingDelayTimer.value) {
+    clearTimeout(drawingDelayTimer.value)
+    drawingDelayTimer.value = null
   }
 }
 
+// 检测浏览器类型
+const detectBrowser = () => {
+  const ua = navigator.userAgent
+
+  // 按优先级顺序检测
+  if (ua.includes('Edg/') || ua.includes('Edge/')) {
+    return 'Microsoft Edge'
+  }
+  if (ua.includes('Firefox')) {
+    return 'Mozilla Firefox'
+  }
+  if (ua.includes('Trident') || ua.includes('MSIE')) {
+    return 'Internet Explorer'
+  }
+  if (ua.includes('360EE')) {
+    return '360 Browser (极速模式)'
+  }
+  if (ua.includes('360SE')) {
+    return '360 Browser (安全模式)'
+  }
+  if (ua.includes('SLBrowser')) {
+    return 'QQ Browser'
+  }
+  if (ua.includes('UCBrowser')) {
+    return 'UC Browser'
+  }
+  if (ua.includes('Opera') || ua.includes('OPR/')) {
+    return 'Opera'
+  }
+  if (ua.includes('Chrome') && !ua.includes('Edg/')) {
+    return 'Google Chrome'
+  }
+  if (ua.includes('Safari/') && !ua.includes('Chrome')) {
+    return 'Safari'
+  }
+  return 'Other Browser'
+}
+
+// 用户数据上报功能
+const addOp3 = async (userTime: any, loadTime: any, object: any, status: any) => {
+  if (!props.userid) return
+
+  try {
+    if (!userJson.value || !userJson.value.accountNumber) {
+      const res = await axios.get('https://pbl.cocorobo.cn/api/pbl/selectUser', {
+        params: { userid: props.userid }
+      })
+      userJson.value = res[0][0]
+      console.log(userJson.value)
+      console.log(res[0][0])
+    }
+  }
+  catch (e) {
+    console.log(e)
+    return addOp3(userTime, loadTime, object, status)
+  }
+
+  const _time = new Date()
+    .toLocaleString('zh-CN', { hour12: false, timeZone: 'Asia/Shanghai' })
+    .replace(/\//g, '-')
+
+  const browser = detectBrowser()
+  const params = {
+    userid: props.userid,
+    username: userJson.value.username,
+    accountNumber: userJson.value.accountNumber,
+    org: userJson.value.orgName,
+    school: userJson.value.schoolName,
+    role: userJson.value.type === '1' ? '老师' : '学生',
+    browser,
+    userTime: userTime === '1' ? _time : userTime, // 使用时间 1次的就1 其次传秒
+    loadTime, // load的时间没有就''
+    object: JSON.stringify(object), // 执行信息传json
+    status // 成功返回success。失败返回error的信息
+  }
+
+  console.log('params', params)
+
+  axios
+    .post('https://pbl.cocorobo.cn/api/mongo/updateUserData2', [params])
+    .then(res => {
+      if (res.status === 1) {
+        console.log('保存成功')
+      }
+      else {
+        console.log('保存失败')
+      }
+    })
+    .catch(e => {
+      console.log('保存失败')
+      console.log(e)
+    })
+}
+
 onMounted(() => {
   document.addEventListener('keydown', handleKeydown)
 
@@ -2844,9 +3009,8 @@ onMounted(() => {
   // visibilitychange 事件(适用于 iframe 嵌套场景,当外层页面返回时触发)
   const handleVisibilityChange = () => {
     if (document.hidden && isCreator.value) {
-      // 页面被隐藏时,清空激光笔和画图状态
-      clearLaserState()
-      clearWritingBoardState()
+      // 页面被隐藏时,清空所有同步状态
+      clearAllSyncStates()
       if (timerIndicator.value.visible) {
         sendMessage({ type: 'timer_stop', courseid: props.courseid })
       }
@@ -2882,12 +3046,18 @@ onUnmounted(() => {
     clearTimeout(reconnectTimer.value)
     reconnectTimer.value = null
   }
-  
+
   if (providerSocket.value) {
     providerSocket.value.destroy()
     providerSocket.value = null
   }
 
+  // 清理画图延迟发送定时器
+  if (drawingDelayTimer.value) {
+    clearTimeout(drawingDelayTimer.value)
+    drawingDelayTimer.value = null
+  }
+
   // 清理页面卸载相关的事件监听器
   if ((window as any).__pptistStudentUnloadHandlers) {
     const handlers = (window as any).__pptistStudentUnloadHandlers