lsc 2 周之前
父節點
當前提交
9418fa211c
共有 1 個文件被更改,包括 234 次插入19 次删除
  1. 234 19
      src/views/Student/index.vue

+ 234 - 19
src/views/Student/index.vue

@@ -223,6 +223,17 @@
   <QAWorkModal v-model:visible="visibleQA" :work="selectedWork" />
   <ChoiceWorkModal v-model:visible="visibleChoice" :work="selectedWork" />
   <AIWorkModal v-model:visible="visibleAI" :work="selectedWork" />
+
+  <!-- 在适当位置添加连接状态指示器 -->
+  <div class="connection-status" v-if="connectionStatus !== 'connected'">
+    <div class="status-indicator" :class="connectionStatus">
+      <span v-if="connectionStatus === 'connecting'">连接中...</span>
+      <span v-else-if="connectionStatus === 'disconnected'">连接断开</span>
+    </div>
+    <button v-if="connectionStatus === 'disconnected'" @click="manualReconnect" class="reconnect-btn">
+      重新连接
+    </button>
+  </div>
 </template>
 
 <script lang="ts" setup>
@@ -361,6 +372,14 @@ const yMessage = ref<any | null>(null)
 const providerSocket = ref<WebsocketProvider | null>(null)
 const mId = ref<string | null>(null)
 
+// WebSocket重连相关变量
+const reconnectAttempts = ref(0)
+const maxReconnectAttempts = ref(5) // 最大重连次数
+const reconnectInterval = ref(5000) // 重连间隔(毫秒)
+const reconnectTimer = ref<NodeJS.Timeout | null>(null)
+const isConnecting = ref(false)
+const connectionStatus = ref<'disconnected' | 'connecting' | 'connected'>('disconnected')
+
 
 // 切换到作业区
 const switchToHomework = () => {
@@ -1742,6 +1761,11 @@ const selectCourseSLook = async () => {
       goToSlide(Number(res[0][0].followC))
     }
     isFollowModeActive.value = true
+    if (props.userid == courseDetail.value.userid && props.type == '1') {
+      await api.updateCourseFollowC(slideIndex.value, props.courseid as string)
+      sendMessage({slideIndex: slideIndex.value, courseid: props.courseid, type: 'slideIndex'})
+      console.log('设置当前幻灯片为跟随目标:', slideIndex.value)
+    }
   }
   else {
     isFollowModeActive.value = false
@@ -1835,7 +1859,7 @@ const getMessages = (msgObj: any) => {
   console.log('message', msgObj)
   
   // 处理幻灯片切换消息
-  if (props.type == '2' && msgObj.slideIndex && msgObj.type === 'slideIndex') {
+  if (props.type == '2' && msgObj.type === 'slideIndex') {
     goToSlide(msgObj.slideIndex)
   }
   
@@ -1918,28 +1942,19 @@ onMounted(() => {
     // 添加URL参数到全局对象中
     courseid: props.courseid,
     type: props.type,
-    successSubmit
+    successSubmit,
+    toggleFollowMode,
+    // 添加重连功能
+    manualReconnect,
+    connectionStatus: computed(() => connectionStatus.value)
   }
 
   console.log('PPTist Student View 已加载,可通过 window.PPTistStudent 访问功能')
   console.log('URL参数:', { courseid: props.courseid, type: props.type })
 
-  if (api.yweb_socket && !docSocket.value) {
-			
-    docSocket.value = new Y.Doc()
-    providerSocket.value = new WebsocketProvider(api.yweb_socket,
-      'PPT' + props.courseid,
-      docSocket.value
-    )
-
-    providerSocket.value.on('status', (event: any) => {
-      console.log('👉', event.status)
-      if (event.status === 'connected') {
-        console.log('👉连接成功websocket(teachingMode)')
-        mId.value = Math.random().toString(36).substr(2, 9)
-        messageInit()
-      }
-    })
+  // 初始化WebSocket连接
+  if (api.yweb_socket) {
+    createWebSocketConnection()
   }
 })
 
@@ -1955,7 +1970,16 @@ onUnmounted(() => {
   // 移除视口尺寸更新事件监听器
   window.removeEventListener('viewportSizeUpdated', handleViewportSizeUpdated)
 
-  // 移除定时器清理代码,已改用socket监听
+  // 清理WebSocket连接
+  if (reconnectTimer.value) {
+    clearTimeout(reconnectTimer.value)
+    reconnectTimer.value = null
+  }
+  
+  if (providerSocket.value) {
+    providerSocket.value.destroy()
+    providerSocket.value = null
+  }
 
   // 清理window上的引用
   if ((window as any).PPTistStudent) {
@@ -1963,6 +1987,95 @@ onUnmounted(() => {
     console.log('PPTist Student View 已卸载,window.PPTistStudent 已清理')
   }
 })
+
+// 手动重连
+const manualReconnect = () => {
+  if (isConnecting.value) return
+  
+  reconnectAttempts.value = 0 // 重置重连次数
+  createWebSocketConnection()
+}
+
+// 创建WebSocket连接
+const createWebSocketConnection = () => {
+  if (!api.yweb_socket || isConnecting.value) return
+  
+  isConnecting.value = true
+  connectionStatus.value = 'connecting'
+  
+  try {
+    // 清理之前的连接
+    if (providerSocket.value) {
+      providerSocket.value.destroy()
+      providerSocket.value = null
+    }
+    
+    docSocket.value = new Y.Doc()
+    providerSocket.value = new WebsocketProvider(
+      api.yweb_socket,
+      'PPT' + props.courseid,
+      docSocket.value
+    )
+
+    providerSocket.value.on('status', (event: any) => {
+      console.log('👉 WebSocket状态:', event.status)
+      
+      if (event.status === 'connected') {
+        console.log('👉连接成功websocket(teachingMode)')
+        connectionStatus.value = 'connected'
+        isConnecting.value = false
+        reconnectAttempts.value = 0 // 重置重连次数
+        
+        // 清理重连定时器
+        if (reconnectTimer.value) {
+          clearTimeout(reconnectTimer.value)
+          reconnectTimer.value = null
+        }
+        
+        mId.value = Math.random().toString(36).substr(2, 9)
+        messageInit()
+      }
+      else if (event.status === 'disconnected') {
+        console.log('👉 WebSocket连接断开')
+        connectionStatus.value = 'disconnected'
+        isConnecting.value = false
+        handleDisconnection()
+      }
+    })
+    
+    // 监听连接错误
+    providerSocket.value.on('connection-error', (error: any) => {
+      console.error('👉 WebSocket连接错误:', error)
+      connectionStatus.value = 'disconnected'
+      isConnecting.value = false
+      handleDisconnection()
+    })
+    
+  }
+  catch (error) {
+    console.error('👉 创建WebSocket连接失败:', error)
+    connectionStatus.value = 'disconnected'
+    isConnecting.value = false
+    handleDisconnection()
+  }
+}
+
+// 处理连接断开
+const handleDisconnection = () => {
+  if (reconnectAttempts.value < maxReconnectAttempts.value) {
+    reconnectAttempts.value++
+    console.log(`👉 尝试重连 (${reconnectAttempts.value}/${maxReconnectAttempts.value})`)
+    
+    reconnectTimer.value = setTimeout(() => {
+      createWebSocketConnection()
+    }, reconnectInterval.value)
+  }
+  else {
+    console.error('👉 WebSocket重连次数已达上限,停止重连')
+    // 可以在这里显示用户提示
+    message.error('网络连接异常,请检查网络后刷新页面')
+  }
+}
 </script>
 
 <style lang="scss" scoped>
@@ -2725,4 +2838,106 @@ onUnmounted(() => {
     0% { transform: rotate(0deg); }
     100% { transform: rotate(360deg); }
 }
+
+/* 标签页切换器样式 */
+.tab-switcher {
+  display: flex;
+  flex: 1;
+  margin-right: 12px;
+  border-bottom: 1px solid #e0e0e0;
+  padding-bottom: 0;
+  height: 100%;
+  justify-content: center;
+  gap: 20px;
+}
+
+.tab-btn {
+  // flex: 1;
+  // padding: 12px 16px;
+  border: none;
+  background: transparent;
+  color: #666;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  font-size: 14px;
+  font-weight: 500;
+  text-align: center;
+  white-space: nowrap;
+  position: relative;
+  border-radius: 0;
+  
+  &:hover {
+    color: #333;
+  }
+  
+  &.active {
+    color: #333;
+    font-weight: 600;
+    
+    &::after {
+      content: '';
+      position: absolute;
+      bottom: -1px;
+      left: 0;
+      right: 0;
+      height: 2px;
+      background: #333;
+      border-radius: 1px;
+    }
+  }
+}
+
+// 在适当位置添加连接状态指示器
+.connection-status {
+  position: fixed;
+  top: 10px;
+  right: 10px;
+  background-color: rgba(255, 255, 255, 0.8);
+  border-radius: 5px;
+  padding: 5px 10px;
+  display: flex;
+  align-items: center;
+  z-index: 1000;
+
+  .status-indicator {
+    width: 100px;
+    height: 20px;
+    border-radius: 5px;
+    margin-right: 10px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    &.connected {
+      background-color: #52c41a;
+    }
+
+    &.connecting {
+      background-color: #1890ff;
+    }
+
+    &.disconnected {
+      background-color: #ff4d4f;
+    }
+
+    span {
+      color: #fff;
+      font-size: 12px;
+    }
+  }
+
+  .reconnect-btn {
+    background-color: #1890ff;
+    color: #fff;
+    border: none;
+    border-radius: 5px;
+    padding: 5px 10px;
+    cursor: pointer;
+    transition: background-color 0.3s;
+
+    &:hover {
+      background-color: #40a9ff;
+    }
+  }
+}
 </style>