|
@@ -259,6 +259,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>
|
|
@@ -399,6 +410,13 @@ 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 = () => {
|
|
@@ -1810,6 +1828,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
|
|
@@ -1903,7 +1926,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)
|
|
|
}
|
|
|
|
|
@@ -1986,28 +2009,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()
|
|
|
}
|
|
|
})
|
|
|
|
|
@@ -2023,7 +2037,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) {
|
|
@@ -2031,6 +2054,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>
|
|
@@ -2867,4 +2979,58 @@ onUnmounted(() => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// 在适当位置添加连接状态指示器
|
|
|
+.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>
|