lsc 2 months ago
parent
commit
b39b41dd2c
1 changed files with 151 additions and 47 deletions
  1. 151 47
      src/views/Student/index.vue

+ 151 - 47
src/views/Student/index.vue

@@ -1178,14 +1178,8 @@ const handleLaserMove = (e: MouseEvent) => {
   sendRafPending = true
   sendRafPending = true
   requestAnimationFrame(() => {
   requestAnimationFrame(() => {
     sendRafPending = false
     sendRafPending = false
+    // sendMessage 中已经直接更新 Map,不需要重复更新
     sendMessage({ type: 'laser_move', x: lastSent.x, y: lastSent.y, courseid: props.courseid })
     sendMessage({ type: 'laser_move', x: lastSent.x, y: lastSent.y, courseid: props.courseid })
-    // 更新共享 Map 的位置(节流后每帧最多一次)
-    if (yLaserState.value) {
-      docSocket.value?.transact(() => {
-        yLaserState.value.set('x', lastSent.x)
-        yLaserState.value.set('y', lastSent.y)
-      })
-    }
   })
   })
 }
 }
 
 
@@ -2542,6 +2536,8 @@ const messageInit = () => {
     yMessage.value = docSocket.value.getArray('message')
     yMessage.value = docSocket.value.getArray('message')
     yMessage.value.observe((e: any) => {
     yMessage.value.observe((e: any) => {
       e.changes.added.forEach((i: any) => {
       e.changes.added.forEach((i: any) => {
+        console.log('yMessage', yMessage.value.length)
+        console.log('yMessage', yMessage.value)
         const message = i.content.getContent()[0]
         const message = i.content.getContent()[0]
         console.log('yMessage', message)
         console.log('yMessage', message)
         if (message.mId !== mId.value) {
         if (message.mId !== mId.value) {
@@ -2555,14 +2551,24 @@ const messageInit = () => {
   if (!yTimerMessages.value) {
   if (!yTimerMessages.value) {
     console.log('初始化计时器消息数组')
     console.log('初始化计时器消息数组')
     yTimerMessages.value = docSocket.value.getArray('timerMessages')
     yTimerMessages.value = docSocket.value.getArray('timerMessages')
+    
+    // 初始化时检查一次是否需要清理
+    if (yTimerMessages.value && yTimerMessages.value.length > 500 && isCreator.value && docSocket.value) {
+      smartCleanupMessages(yTimerMessages.value, 500, 'timer', docSocket.value)
+    }
+    
     yTimerMessages.value.observe((e: any) => {
     yTimerMessages.value.observe((e: any) => {
-      // 检查数组长度,如果超过2000条,清理旧数据
-      if (yTimerMessages.value && yTimerMessages.value.length > 2000 && isCreator.value && docSocket.value) {
-        const excessCount = yTimerMessages.value.length - 2000
-        docSocket.value.transact(() => {
-          yTimerMessages.value.delete(0, excessCount)
-        })
-        console.log(`🧹 清理了 ${excessCount} 条计时器消息,保留最新2000条`)
+      console.log('yTimerMessages', yTimerMessages.value.length)
+      console.log('yTimerMessages', yTimerMessages.value)
+      
+      // 每次有新消息添加后,检查是否需要清理
+      if (yTimerMessages.value && yTimerMessages.value.length > 500 && isCreator.value && docSocket.value) {
+        // 使用 setTimeout 确保在 observe 回调执行完成后再清理
+        setTimeout(() => {
+          if (docSocket.value && yTimerMessages.value && yTimerMessages.value.length > 500) {
+            smartCleanupMessages(yTimerMessages.value, 500, 'timer', docSocket.value)
+          }
+        }, 0)
       }
       }
       
       
       e.changes.added.forEach((i: any) => {
       e.changes.added.forEach((i: any) => {
@@ -2578,19 +2584,30 @@ const messageInit = () => {
   if (!yLaserMessages.value) {
   if (!yLaserMessages.value) {
     console.log('初始化激光笔消息数组')
     console.log('初始化激光笔消息数组')
     yLaserMessages.value = docSocket.value.getArray('laserMessages')
     yLaserMessages.value = docSocket.value.getArray('laserMessages')
+    
+    // 初始化时检查一次是否需要清理
+    if (yLaserMessages.value && yLaserMessages.value.length > 500 && isCreator.value && docSocket.value) {
+      smartCleanupMessages(yLaserMessages.value, 500, 'laser', docSocket.value)
+    }
+    
     yLaserMessages.value.observe((e: any) => {
     yLaserMessages.value.observe((e: any) => {
-      // 检查数组长度,如果超过2000条,清理旧数据
-      if (yLaserMessages.value && yLaserMessages.value.length > 2000 && isCreator.value && docSocket.value) {
-        const excessCount = yLaserMessages.value.length - 2000
-        docSocket.value.transact(() => {
-          yLaserMessages.value.delete(0, excessCount)
-        })
-        console.log(`🧹 清理了 ${excessCount} 条激光笔消息,保留最新2000条`)
+      console.log('yLaserMessages', yLaserMessages.value.length)
+      console.log('yLaserMessages', yLaserMessages.value)
+      
+      // 每次有新消息添加后,检查是否需要清理
+      if (yLaserMessages.value && yLaserMessages.value.length > 500 && isCreator.value && docSocket.value) {
+        // 使用 setTimeout 确保在 observe 回调执行完成后再清理
+        setTimeout(() => {
+          if (docSocket.value && yLaserMessages.value && yLaserMessages.value.length > 500) {
+            smartCleanupMessages(yLaserMessages.value, 500, 'laser', docSocket.value)
+          }
+        }, 0)
       }
       }
       
       
       e.changes.added.forEach((i: any) => {
       e.changes.added.forEach((i: any) => {
         const message = i.content.getContent()[0]
         const message = i.content.getContent()[0]
-        if (message.mId !== mId.value) {
+        // 只处理开关消息,位置消息直接通过 Map 同步
+        if (message.type === 'laser_toggle' && message.mId !== mId.value) {
           getMessages(message)
           getMessages(message)
         }
         }
       })
       })
@@ -2601,14 +2618,24 @@ const messageInit = () => {
   if (!yWritingBoardMessages.value) {
   if (!yWritingBoardMessages.value) {
     console.log('初始化画图消息数组')
     console.log('初始化画图消息数组')
     yWritingBoardMessages.value = docSocket.value.getArray('writingBoardMessages')
     yWritingBoardMessages.value = docSocket.value.getArray('writingBoardMessages')
+    
+    // 初始化时检查一次是否需要清理
+    if (yWritingBoardMessages.value && yWritingBoardMessages.value.length > 500 && isCreator.value && docSocket.value) {
+      smartCleanupMessages(yWritingBoardMessages.value, 500, 'writingBoard', docSocket.value)
+    }
+    
     yWritingBoardMessages.value.observe((e: any) => {
     yWritingBoardMessages.value.observe((e: any) => {
-      // 检查数组长度,如果超过2000条,清理旧数据
-      if (yWritingBoardMessages.value && yWritingBoardMessages.value.length > 2000 && isCreator.value && docSocket.value) {
-        const excessCount = yWritingBoardMessages.value.length - 2000
-        docSocket.value.transact(() => {
-          yWritingBoardMessages.value.delete(0, excessCount)
-        })
-        console.log(`🧹 清理了 ${excessCount} 条画图消息,保留最新2000条`)
+      console.log('yWritingBoardMessages', yWritingBoardMessages.value.length)
+      console.log('yWritingBoardMessages', yWritingBoardMessages.value)
+      
+      // 每次有新消息添加后,检查是否需要清理
+      if (yWritingBoardMessages.value && yWritingBoardMessages.value.length > 500 && isCreator.value && docSocket.value) {
+        // 使用 setTimeout 确保在 observe 回调执行完成后再清理
+        setTimeout(() => {
+          if (docSocket.value && yWritingBoardMessages.value && yWritingBoardMessages.value.length > 500) {
+            smartCleanupMessages(yWritingBoardMessages.value, 500, 'writingBoard', docSocket.value)
+          }
+        }, 0)
       }
       }
       
       
       e.changes.added.forEach((i: any) => {
       e.changes.added.forEach((i: any) => {
@@ -2726,20 +2753,89 @@ const messageInit = () => {
 
 
 /**
 /**
  * 判断是否为特殊类型的消息(计时器、激光笔、画图)
  * 判断是否为特殊类型的消息(计时器、激光笔、画图)
+ * 注意:laser_move 不通过消息数组,直接通过 Map 同步,所以不包含在这里
  */
  */
 const isSpecialMessageType = (type: string): boolean => {
 const isSpecialMessageType = (type: string): boolean => {
   const timerTypes = ['timer_start', 'timer_pause', 'timer_reset', 'timer_stop', 'timer_finish', 'timer_update']
   const timerTypes = ['timer_start', 'timer_pause', 'timer_reset', 'timer_stop', 'timer_finish', 'timer_update']
-  const laserTypes = ['laser_toggle', 'laser_move']
+  const laserTypes = ['laser_toggle'] // laser_move 直接通过 Map,不通过消息数组
   const writingBoardTypes = ['writing_board_update', 'writing_board_close', 'writing_board_blackboard']
   const writingBoardTypes = ['writing_board_update', 'writing_board_close', 'writing_board_blackboard']
   return timerTypes.includes(type) || laserTypes.includes(type) || writingBoardTypes.includes(type)
   return timerTypes.includes(type) || laserTypes.includes(type) || writingBoardTypes.includes(type)
 }
 }
 
 
+/**
+ * 判断是否为开关类型的消息(需要保留的状态消息)
+ */
+const isToggleMessageType = (type: string, category: 'timer' | 'laser' | 'writingBoard'): boolean => {
+  if (category === 'laser') {
+    return type === 'laser_toggle'
+  }
+  if (category === 'writingBoard') {
+    return type === 'writing_board_close'
+  }
+  if (category === 'timer') {
+    return ['timer_start', 'timer_stop', 'timer_reset'].includes(type)
+  }
+  return false
+}
+
+/**
+ * 智能清理消息数组,保留最新的开关消息
+ */
+const smartCleanupMessages = (messageArray: any, maxLength: number, category: 'timer' | 'laser' | 'writingBoard', docSocket: Y.Doc) => {
+  if (!messageArray || messageArray.length <= maxLength) return
+  
+  const allMessages = messageArray.toArray()
+  
+  // 找到所有开关类型的消息及其索引
+  const toggleMessages: Array<{ index: number; message: any }> = []
+  for (let i = 0; i < allMessages.length; i++) {
+    const msg = allMessages[i]
+    if (msg && typeof msg === 'object' && msg.type && isToggleMessageType(msg.type, category)) {
+      toggleMessages.push({ index: i, message: msg })
+    }
+  }
+  
+  // 如果有关键的开关消息,需要确保它们被保留
+  if (toggleMessages.length > 0) {
+    // 找到最新的开关消息索引
+    const latestToggleIndex = toggleMessages[toggleMessages.length - 1].index
+    
+    // 如果最新的开关消息在最后500条内,直接保留最后500条
+    if (latestToggleIndex >= allMessages.length - maxLength) {
+      const excessCount = allMessages.length - maxLength
+      docSocket.transact(() => {
+        messageArray.delete(0, excessCount)
+      })
+      console.log(`🧹 清理了 ${excessCount} 条${category}消息,保留最新${maxLength}条(包含最新开关状态)`)
+    }
+    else {
+      // 如果最新的开关消息不在最后500条内,需要特殊处理
+      // 保留从最新开关消息开始到末尾的所有消息
+      const keepFromIndex = Math.max(0, latestToggleIndex)
+      const excessCount = keepFromIndex
+      docSocket.transact(() => {
+        messageArray.delete(0, excessCount)
+      })
+      console.log(`🧹 清理了 ${excessCount} 条${category}消息,保留从最新开关状态开始的所有消息`)
+    }
+  }
+  else {
+    // 没有开关消息,直接保留最后500条
+    const excessCount = allMessages.length - maxLength
+    docSocket.transact(() => {
+      messageArray.delete(0, excessCount)
+    })
+    console.log(`🧹 清理了 ${excessCount} 条${category}消息,保留最新${maxLength}条`)
+  }
+}
+
 /**
 /**
  * 获取消息类型对应的数组
  * 获取消息类型对应的数组
+ * 注意:laser_move 不通过消息数组,直接通过 Map 同步
  */
  */
 const getMessageArrayByType = (type: string): any | null => {
 const getMessageArrayByType = (type: string): any | null => {
   const timerTypes = ['timer_start', 'timer_pause', 'timer_reset', 'timer_stop', 'timer_finish', 'timer_update']
   const timerTypes = ['timer_start', 'timer_pause', 'timer_reset', 'timer_stop', 'timer_finish', 'timer_update']
-  const laserTypes = ['laser_toggle', 'laser_move']
+  const laserTypes = ['laser_toggle'] // laser_move 直接通过 Map,不通过消息数组
   const writingBoardTypes = ['writing_board_update', 'writing_board_close', 'writing_board_blackboard']
   const writingBoardTypes = ['writing_board_update', 'writing_board_close', 'writing_board_blackboard']
   
   
   if (timerTypes.includes(type)) {
   if (timerTypes.includes(type)) {
@@ -2765,6 +2861,18 @@ const sendMessage = (obj: any) => {
   message.mId = mId.value
   message.mId = mId.value
   
   
   const messageType = message.type
   const messageType = message.type
+  
+  // laser_move 消息直接通过 Map 同步,不通过消息数组(减少延迟)
+  if (messageType === 'laser_move') {
+    if (yLaserState.value) {
+      docSocket.value.transact(() => {
+        yLaserState.value.set('x', message.x)
+        yLaserState.value.set('y', message.y)
+      })
+    }
+    return // 不存储到消息数组
+  }
+  
   const isSpecial = isSpecialMessageType(messageType)
   const isSpecial = isSpecialMessageType(messageType)
   
   
   docSocket.value.transact(() => {
   docSocket.value.transact(() => {
@@ -2847,15 +2955,7 @@ const getMessages = (msgObj: any) => {
       laserMoveRafId = requestAnimationFrame(updateLaserDotPosition)
       laserMoveRafId = requestAnimationFrame(updateLaserDotPosition)
     }
     }
   }
   }
-  // 激光笔:老师广播的位置
-  if (props.type == '2' && msgObj.type === 'laser_move' && msgObj.courseid === props.courseid) {
-    if (laserPenOverlay.value.visible) {
-      laserPenOverlay.value.xPct = Number(msgObj.x || 0)
-      laserPenOverlay.value.yPct = Number(msgObj.y || 0)
-      if (laserMoveRafId) cancelAnimationFrame(laserMoveRafId)
-      laserMoveRafId = requestAnimationFrame(updateLaserDotPosition)
-    }
-  }
+  // 注意:laser_move 现在直接通过 Map 同步,不通过消息数组,所以这里不需要处理
 
 
   // 画图:老师广播的画图数据
   // 画图:老师广播的画图数据
   if (props.type == '2' && msgObj.type === 'writing_board_update' && msgObj.courseid === props.courseid) {
   if (props.type == '2' && msgObj.type === 'writing_board_update' && msgObj.courseid === props.courseid) {
@@ -3613,17 +3713,21 @@ const applyTimerUpdate = (payload: { durationSec: number; startAt?: string }) =>
 // 应用激光笔共享状态(任意端)
 // 应用激光笔共享状态(任意端)
 const applyLaserStateSnapshot = (snap: any) => {
 const applyLaserStateSnapshot = (snap: any) => {
   if (!snap) return
   if (!snap) return
-  const enabled = !!snap.enabled
+  const enabled = snap.enabled !== undefined ? !!snap.enabled : laserPenOverlay.value.visible
   const x = typeof snap.x === 'number' ? snap.x : null
   const x = typeof snap.x === 'number' ? snap.x : null
   const y = typeof snap.y === 'number' ? snap.y : null
   const y = typeof snap.y === 'number' ? snap.y : null
+  
   if (props.type == '2') {
   if (props.type == '2') {
-    laserPenOverlay.value.visible = enabled
-    if (enabled) {
+    // 更新开关状态
+    if (snap.enabled !== undefined) {
+      laserPenOverlay.value.visible = enabled
+    }
+    
+    // 如果激光笔是开启状态,更新位置
+    if (laserPenOverlay.value.visible && x != null && y != null) {
+      laserPenOverlay.value.xPct = x
+      laserPenOverlay.value.yPct = y
       refreshLaserOverlayRect()
       refreshLaserOverlayRect()
-      if (x != null && y != null) {
-        laserPenOverlay.value.xPct = x
-        laserPenOverlay.value.yPct = y
-      }
       if (laserMoveRafId) cancelAnimationFrame(laserMoveRafId)
       if (laserMoveRafId) cancelAnimationFrame(laserMoveRafId)
       laserMoveRafId = requestAnimationFrame(updateLaserDotPosition)
       laserMoveRafId = requestAnimationFrame(updateLaserDotPosition)
     }
     }