Browse Source

feat: 实现老师响应学生的计时器查询并重发状态

新增老师端响应学生计时器查询的逻辑,当老师是创建者且处于计时状态时,根据计时器类型(倒计时/秒表)重发当前准确状态给学生,同时优化消息判断逻辑,删除冗余条件分支。
lsc 1 week ago
parent
commit
1313316516
1 changed files with 33 additions and 10 deletions
  1. 33 10
      src/views/Student/index.vue

+ 33 - 10
src/views/Student/index.vue

@@ -3909,6 +3909,31 @@ const getMessages = (msgObj: any) => {
     sendMessage({ slideIndex: curIndex, courseid: props.courseid, type: 'slideIndex' })
     console.log('🔗 老师响应 class_begin,回发 slideIndex:', curIndex)
   }
+
+  // ★ 老师收到学生的计时器查询后,若本地计时器仍在显示,按当前真实进度重发状态
+  //   (学生刷新后服务器持久化不可靠,老师内存状态才是权威数据源)
+  if (msgObj.type === 'timer_query' && isCreator.value && props.type === '1') {
+    if (timerIndicator.value.visible) {
+      const now = Date.now()
+      let payload: { isCountdown: boolean; startAt: string; durationSec?: number }
+      if (timerIndicator.value.isCountdown) {
+        // 倒计时:以"现在"为锚点、当前剩余为时长重发,学生端按锚点重算即得正确剩余
+        const remain = Math.max(Number(timerIndicator.value.remainingSec || 0), 0)
+        payload = { isCountdown: true, startAt: new Date(now).toISOString(), durationSec: remain }
+      }
+      else {
+        // 秒表:锚点回推已计时长,学生端从正确进度继续
+        const elapsed = Math.max(Number(timerIndicator.value.elapsedSec || 0), 0)
+        payload = { isCountdown: false, startAt: new Date(now - elapsed * 1000).toISOString() }
+      }
+      sendMessage({ type: 'timer_start', courseid: props.courseid, payload: JSON.stringify(payload) })
+      // 本地处于暂停态(tick 未运行且未结束)→ 补发 pause,让学生端停在同一剩余值
+      if (!timerInterval.value && !timerIndicator.value.finished) {
+        sendMessage({ type: 'timer_pause', courseid: props.courseid })
+      }
+      console.log('⏱️ 老师响应 timer_query,重发计时器状态:', payload)
+    }
+  }
 }
 
 
@@ -4154,6 +4179,8 @@ onMounted(() => {
         '10. provider synced': providerSocket.value?.synced,
         '11. ws.readyState': providerSocket.value?.ws?.readyState,
         '12. messageV3?.get("slideIndex")': msgV3Type?.get?.('slideIndex'),
+        '13. yTimerMesssage': doc.getArray('timerMessages'),
+        '14. timerState':docSocket.value.getMap('timerState')
       }
       console.log('════════ Yjs 诊断 ════════')
       for (const [k, v] of Object.entries(report)) {
@@ -4542,6 +4569,11 @@ const createWebSocketConnection = async (type = 1) => {
             getMessages(slideIndex)
           }
         }
+        // ★ 学生 sync 完成后主动向老师查询计时器状态
+        //   (服务器持久化不可靠时,由老师内存状态重发,保证刷新后接着跑)
+        if (props.type === '2') {
+          sendMessage({ type: 'timer_query', courseid: props.courseid })
+        }
       }
       catch (e) {
         console.warn('sync 兜底恢复失败', e)
@@ -4577,16 +4609,7 @@ const createWebSocketConnection = async (type = 1) => {
         console.log('📨 解析业务消息:', obj)
         // 简化判断逻辑:writingBoard 类型永远跳过 getMessages;
         // timer 类型仅在非创建人时才走 getMessages;其余类型一律走 getMessages
-        const isWritingBoard = writingBoardTypes.includes(obj.type)
-        const isTimer = timerTypes.includes(obj.type)
-        if (isTimer && !isCreator.value) {
-          getMessages(obj)
-          return
-        }
-        if (!isWritingBoard) {
-          getMessages(obj)
-          return
-        }
+        getMessages(obj)
       }
 
       // ★ 消息处理后检查特殊消息数组长度,超出则清理(替代 observe 里的清理逻辑)