|
@@ -144,8 +144,17 @@
|
|
|
<SlideThumbnails v-if="slideThumbnailModelVisible" :turnSlideToIndex="goToSlide"
|
|
<SlideThumbnails v-if="slideThumbnailModelVisible" :turnSlideToIndex="goToSlide"
|
|
|
@close="slideThumbnailModelVisible = false" />
|
|
@close="slideThumbnailModelVisible = false" />
|
|
|
|
|
|
|
|
- <WritingBoardTool :slideWidth="slideWidth" :slideHeight="slideHeight" v-if="writingBoardToolVisible"
|
|
|
|
|
- @close="writingBoardToolVisible = false" />
|
|
|
|
|
|
|
+ <WritingBoardTool
|
|
|
|
|
+ :slideWidth="slideWidth"
|
|
|
|
|
+ :slideHeight="slideHeight"
|
|
|
|
|
+ v-if="writingBoardToolVisible || (props.type == '2' && isFollowModeActive && writingBoardSyncDataURL && writingBoardSyncDataURL.trim() !== '')"
|
|
|
|
|
+ :readonly="props.type == '2'"
|
|
|
|
|
+ :syncDataURL="props.type == '2' ? writingBoardSyncDataURL : null"
|
|
|
|
|
+ :syncBlackboard="props.type == '2' ? writingBoardSyncBlackboard : null"
|
|
|
|
|
+ @close="handleWritingBoardClose"
|
|
|
|
|
+ @drawing-end="handleDrawingEnd"
|
|
|
|
|
+ @blackboard-change="handleBlackboardChange"
|
|
|
|
|
+ />
|
|
|
|
|
|
|
|
<CountdownTimer
|
|
<CountdownTimer
|
|
|
v-if="timerlVisible"
|
|
v-if="timerlVisible"
|
|
@@ -261,7 +270,7 @@
|
|
|
<div class="homework-empty" v-else>
|
|
<div class="homework-empty" v-else>
|
|
|
暂无作业提交
|
|
暂无作业提交
|
|
|
</div>
|
|
</div>
|
|
|
- </div>--!>
|
|
|
|
|
|
|
+ </div>-->
|
|
|
|
|
|
|
|
|
|
|
|
|
<!--<div v-if="unsubmittedStudents && unsubmittedStudents.length > 0" class="homework-title" style="margin-top: 20px;">未提交</div>
|
|
<!--<div v-if="unsubmittedStudents && unsubmittedStudents.length > 0" class="homework-title" style="margin-top: 20px;">未提交</div>
|
|
@@ -428,12 +437,12 @@ let lastLayout: { w: number; h: number } | null = null
|
|
|
// 学生端覆盖层矩形(固定定位)
|
|
// 学生端覆盖层矩形(固定定位)
|
|
|
const laserOverlayRect = ref<{ left: number; top: number; width: number; height: number }>({ left: 0, top: 0, width: 0, height: 0 })
|
|
const laserOverlayRect = ref<{ left: number; top: number; width: number; height: number }>({ left: 0, top: 0, width: 0, height: 0 })
|
|
|
const laserOverlayStyle = computed(() => ({
|
|
const laserOverlayStyle = computed(() => ({
|
|
|
- position: 'fixed',
|
|
|
|
|
|
|
+ position: 'fixed' as const,
|
|
|
left: laserOverlayRect.value.left + 'px',
|
|
left: laserOverlayRect.value.left + 'px',
|
|
|
top: laserOverlayRect.value.top + 'px',
|
|
top: laserOverlayRect.value.top + 'px',
|
|
|
width: laserOverlayRect.value.width + 'px',
|
|
width: laserOverlayRect.value.width + 'px',
|
|
|
height: laserOverlayRect.value.height + 'px',
|
|
height: laserOverlayRect.value.height + 'px',
|
|
|
- pointerEvents: 'auto',
|
|
|
|
|
|
|
+ pointerEvents: 'auto' as const,
|
|
|
zIndex: 1000
|
|
zIndex: 1000
|
|
|
}))
|
|
}))
|
|
|
const refreshLaserOverlayRect = () => {
|
|
const refreshLaserOverlayRect = () => {
|
|
@@ -536,7 +545,11 @@ const docSocket = ref<Y.Doc | null>(null)
|
|
|
const yMessage = ref<any | null>(null)
|
|
const yMessage = ref<any | null>(null)
|
|
|
const yTimerState = ref<any | null>(null)
|
|
const yTimerState = ref<any | null>(null)
|
|
|
const yLaserState = ref<any | null>(null)
|
|
const yLaserState = ref<any | null>(null)
|
|
|
|
|
+const yWritingBoardState = ref<any | null>(null)
|
|
|
const providerSocket = ref<WebsocketProvider | null>(null)
|
|
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 mId = ref<string | null>(null)
|
|
|
|
|
|
|
|
// WebSocket重连相关变量
|
|
// WebSocket重连相关变量
|
|
@@ -817,6 +830,47 @@ const nextSlide = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+// 监听幻灯片切换,清除不匹配的画图数据
|
|
|
|
|
+watch(() => slideIndex.value, () => {
|
|
|
|
|
+ if (props.type == '2' && yWritingBoardState.value && currentSlide.value) {
|
|
|
|
|
+ const snap = yWritingBoardState.value.toJSON()
|
|
|
|
|
+ console.log('📝 幻灯片切换,检查画图数据:', { snap, currentSlideId: currentSlide.value.id })
|
|
|
|
|
+ if (snap && snap.slideId === currentSlide.value.id && snap.dataURL) {
|
|
|
|
|
+ // 当前幻灯片有画图数据,显示
|
|
|
|
|
+ writingBoardSyncDataURL.value = snap.dataURL
|
|
|
|
|
+ writingBoardSyncBlackboard.value = snap.blackboard !== undefined ? snap.blackboard : null
|
|
|
|
|
+ console.log('📝 当前幻灯片有画图数据,显示画图工具,小黑板状态:', writingBoardSyncBlackboard.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ // 当前幻灯片没有画图数据,隐藏
|
|
|
|
|
+ writingBoardSyncDataURL.value = null
|
|
|
|
|
+ writingBoardSyncBlackboard.value = null
|
|
|
|
|
+ console.log('📝 当前幻灯片没有画图数据,隐藏画图工具')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 监听 currentSlide 变化,确保刷新后能获取到画图状态
|
|
|
|
|
+watch(() => currentSlide.value?.id, (newSlideId, oldSlideId) => {
|
|
|
|
|
+ // 只在学生端且跟随模式下检查
|
|
|
|
|
+ if (props.type == '2' && isFollowModeActive.value && yWritingBoardState.value && newSlideId) {
|
|
|
|
|
+ const snap = yWritingBoardState.value.toJSON()
|
|
|
|
|
+ console.log('📝 currentSlide变化,检查画图数据:', { snap, newSlideId, oldSlideId })
|
|
|
|
|
+ if (snap && snap.slideId === newSlideId && snap.dataURL) {
|
|
|
|
|
+ // 当前幻灯片有画图数据,显示
|
|
|
|
|
+ writingBoardSyncDataURL.value = snap.dataURL
|
|
|
|
|
+ writingBoardSyncBlackboard.value = snap.blackboard !== undefined ? snap.blackboard : null
|
|
|
|
|
+ console.log('📝 currentSlide变化后找到画图数据,显示画图工具,小黑板状态:', writingBoardSyncBlackboard.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (snap && snap.slideId !== newSlideId) {
|
|
|
|
|
+ // 当前幻灯片没有画图数据,隐藏
|
|
|
|
|
+ writingBoardSyncDataURL.value = null
|
|
|
|
|
+ writingBoardSyncBlackboard.value = null
|
|
|
|
|
+ console.log('📝 currentSlide变化后没有匹配的画图数据,隐藏画图工具')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}, { immediate: true })
|
|
|
|
|
+
|
|
|
// 监听slideIndex变化,调用getWork
|
|
// 监听slideIndex变化,调用getWork
|
|
|
watch(() => slideIndex.value, (newIndex, oldIndex) => {
|
|
watch(() => slideIndex.value, (newIndex, oldIndex) => {
|
|
|
console.log('slideIndex变化,调用getWork', { newIndex, oldIndex })
|
|
console.log('slideIndex变化,调用getWork', { newIndex, oldIndex })
|
|
@@ -922,6 +976,113 @@ const getWorkId = () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 处理画图关闭事件
|
|
|
|
|
+const handleWritingBoardClose = () => {
|
|
|
|
|
+ // 学生端只读模式下,不应该响应关闭事件(因为关闭按钮已隐藏)
|
|
|
|
|
+ // 只有老师端可以关闭
|
|
|
|
|
+ if (props.type == '2') {
|
|
|
|
|
+ console.log('📝 学生端收到关闭事件,但只读模式下不应该关闭,忽略')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ writingBoardToolVisible.value = false
|
|
|
|
|
+ // 老师端关闭时,清空共享状态并通知学生端
|
|
|
|
|
+ if (props.type == '1' && isFollowModeActive.value && isCreator.value) {
|
|
|
|
|
+ clearWritingBoardState()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 清空画图共享状态(仅创建人)
|
|
|
|
|
+const clearWritingBoardState = () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (props.type == '1' && isCreator.value && yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.clear()
|
|
|
|
|
+ })
|
|
|
|
|
+ sendMessage({
|
|
|
|
|
+ type: 'writing_board_close',
|
|
|
|
|
+ courseid: props.courseid
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (e) {
|
|
|
|
|
+ console.warn('清空画图状态失败', e)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理小黑板状态变化(老师端)
|
|
|
|
|
+const handleBlackboardChange = (blackboard: boolean) => {
|
|
|
|
|
+ if (props.type == '1' && isFollowModeActive.value && isCreator.value) {
|
|
|
|
|
+ // 同步到共享 Map
|
|
|
|
|
+ if (yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.set('blackboard', blackboard)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ // 广播消息
|
|
|
|
|
+ sendMessage({
|
|
|
|
|
+ type: 'writing_board_blackboard',
|
|
|
|
|
+ blackboard: blackboard,
|
|
|
|
|
+ courseid: props.courseid
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 处理画图结束事件(老师端)
|
|
|
|
|
+const handleDrawingEnd = (dataURL: string) => {
|
|
|
|
|
+ if (props.type == '1' && isFollowModeActive.value && isCreator.value) {
|
|
|
|
|
+ // 同步到共享 Map
|
|
|
|
|
+ if (yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.set('slideId', currentSlide.value.id)
|
|
|
|
|
+ yWritingBoardState.value.set('dataURL', dataURL)
|
|
|
|
|
+ // 保持小黑板状态
|
|
|
|
|
+ const currentBlackboard = yWritingBoardState.value.get('blackboard')
|
|
|
|
|
+ if (currentBlackboard !== undefined) {
|
|
|
|
|
+ yWritingBoardState.value.set('blackboard', currentBlackboard)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ // 广播消息(包含当前小黑板状态)
|
|
|
|
|
+ const currentBlackboard = yWritingBoardState.value?.get('blackboard') || false
|
|
|
|
|
+ sendMessage({
|
|
|
|
|
+ type: 'writing_board_update',
|
|
|
|
|
+ slideId: currentSlide.value.id,
|
|
|
|
|
+ dataURL: dataURL,
|
|
|
|
|
+ blackboard: currentBlackboard,
|
|
|
|
|
+ courseid: props.courseid
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 应用画图共享状态(任意端)
|
|
|
|
|
+const applyWritingBoardStateSnapshot = (snap: any) => {
|
|
|
|
|
+ console.log('📝 应用画图状态快照:', snap, '当前幻灯片ID:', currentSlide.value?.id, '跟随模式:', isFollowModeActive.value, '用户类型:', props.type)
|
|
|
|
|
+ if (!snap || !snap.dataURL || typeof snap.dataURL !== 'string' || snap.dataURL.trim() === '') {
|
|
|
|
|
+ writingBoardSyncDataURL.value = null
|
|
|
|
|
+ writingBoardSyncBlackboard.value = null
|
|
|
|
|
+ console.log('📝 画图状态为空,隐藏画图工具')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const slideId = snap.slideId
|
|
|
|
|
+ const dataURL = snap.dataURL
|
|
|
|
|
+ const blackboardState = snap.blackboard !== undefined ? snap.blackboard : null
|
|
|
|
|
+ // 只有当前幻灯片匹配时才显示
|
|
|
|
|
+ if (slideId && currentSlide.value && slideId === currentSlide.value.id) {
|
|
|
|
|
+ writingBoardSyncDataURL.value = dataURL
|
|
|
|
|
+ writingBoardSyncBlackboard.value = blackboardState
|
|
|
|
|
+ console.log('📝 画图数据匹配,显示画图工具,数据长度:', dataURL.length, '小黑板状态:', blackboardState, '显示条件:', {
|
|
|
|
|
+ type: props.type,
|
|
|
|
|
+ isFollowModeActive: isFollowModeActive.value,
|
|
|
|
|
+ hasData: !!writingBoardSyncDataURL.value
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ writingBoardSyncDataURL.value = null
|
|
|
|
|
+ writingBoardSyncBlackboard.value = null
|
|
|
|
|
+ console.log('📝 画图数据不匹配,隐藏画图工具', { slideId, currentSlideId: currentSlide.value?.id })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 切换激光笔模式
|
|
// 切换激光笔模式
|
|
|
const toggleLaserPen = () => {
|
|
const toggleLaserPen = () => {
|
|
|
laserPen.value = !laserPen.value
|
|
laserPen.value = !laserPen.value
|
|
@@ -2214,6 +2375,43 @@ const messageInit = () => {
|
|
|
applyLaserStateSnapshot(s)
|
|
applyLaserStateSnapshot(s)
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+ // 初始化画图状态 Map 并监听
|
|
|
|
|
+ if (docSocket.value && !yWritingBoardState.value) {
|
|
|
|
|
+ yWritingBoardState.value = docSocket.value.getMap('writingBoardState')
|
|
|
|
|
+ const wsnap = yWritingBoardState.value.toJSON()
|
|
|
|
|
+ console.log('📝 初始化画图状态Map,快照:', wsnap, '当前幻灯片:', currentSlide.value?.id)
|
|
|
|
|
+ // 延迟应用,确保 currentSlide 已初始化
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ // 如果 currentSlide 还没准备好,再等一帧
|
|
|
|
|
+ if (currentSlide.value && currentSlide.value.id) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(wsnap)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ // 如果还没准备好,等待 currentSlide 变化(最多等待3秒)
|
|
|
|
|
+ let timeoutId: any = null
|
|
|
|
|
+ const unwatch = watch(() => currentSlide.value?.id, (slideId) => {
|
|
|
|
|
+ if (slideId) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(wsnap)
|
|
|
|
|
+ unwatch()
|
|
|
|
|
+ if (timeoutId) clearTimeout(timeoutId)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, { immediate: true })
|
|
|
|
|
+ // 3秒后如果还没准备好,强制应用一次
|
|
|
|
|
+ timeoutId = setTimeout(() => {
|
|
|
|
|
+ if (currentSlide.value && currentSlide.value.id) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(wsnap)
|
|
|
|
|
+ }
|
|
|
|
|
+ unwatch()
|
|
|
|
|
+ }, 3000)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ yWritingBoardState.value.observe(() => {
|
|
|
|
|
+ const s = yWritingBoardState.value.toJSON()
|
|
|
|
|
+ if (currentSlide.value && currentSlide.value.id) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(s)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -2298,6 +2496,65 @@ const getMessages = (msgObj: any) => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 画图:老师广播的画图数据
|
|
|
|
|
+ if (props.type == '2' && msgObj.type === 'writing_board_update' && msgObj.courseid === props.courseid) {
|
|
|
|
|
+ console.log('📝 学生端收到画图更新消息:', { slideId: msgObj.slideId, currentSlideId: currentSlide.value?.id, hasData: !!msgObj.dataURL })
|
|
|
|
|
+ if (currentSlide.value && msgObj.slideId === currentSlide.value.id) {
|
|
|
|
|
+ writingBoardSyncDataURL.value = msgObj.dataURL || null
|
|
|
|
|
+ // 如果消息中包含小黑板状态,也更新
|
|
|
|
|
+ if (msgObj.blackboard !== undefined) {
|
|
|
|
|
+ writingBoardSyncBlackboard.value = msgObj.blackboard
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log('📝 画图数据匹配当前幻灯片,显示画图工具')
|
|
|
|
|
+ // 同步到共享 Map
|
|
|
|
|
+ if (yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.set('slideId', msgObj.slideId)
|
|
|
|
|
+ yWritingBoardState.value.set('dataURL', msgObj.dataURL)
|
|
|
|
|
+ if (msgObj.blackboard !== undefined) {
|
|
|
|
|
+ yWritingBoardState.value.set('blackboard', msgObj.blackboard)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ // 不是当前幻灯片,但也要更新到 Map(供后续切换时使用)
|
|
|
|
|
+ if (yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.set('slideId', msgObj.slideId)
|
|
|
|
|
+ yWritingBoardState.value.set('dataURL', msgObj.dataURL)
|
|
|
|
|
+ if (msgObj.blackboard !== undefined) {
|
|
|
|
|
+ yWritingBoardState.value.set('blackboard', msgObj.blackboard)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log('📝 画图数据不匹配当前幻灯片,已保存到Map供后续使用')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 画图:老师关闭画图工具
|
|
|
|
|
+ if (props.type == '2' && msgObj.type === 'writing_board_close' && msgObj.courseid === props.courseid) {
|
|
|
|
|
+ writingBoardSyncDataURL.value = null
|
|
|
|
|
+ writingBoardSyncBlackboard.value = null
|
|
|
|
|
+ // 清空共享 Map
|
|
|
|
|
+ if (yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.clear()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 画图:老师切换小黑板状态
|
|
|
|
|
+ if (props.type == '2' && msgObj.type === 'writing_board_blackboard' && msgObj.courseid === props.courseid) {
|
|
|
|
|
+ writingBoardSyncBlackboard.value = msgObj.blackboard || false
|
|
|
|
|
+ // 同步到共享 Map
|
|
|
|
|
+ if (yWritingBoardState.value) {
|
|
|
|
|
+ docSocket.value?.transact(() => {
|
|
|
|
|
+ yWritingBoardState.value.set('blackboard', msgObj.blackboard)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2312,12 +2569,13 @@ const openChoiceQuestionDetail = (index:number) => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handlePageUnload = () => {
|
|
const handlePageUnload = () => {
|
|
|
- if (isCreator.value && timerIndicator.value.visible) {
|
|
|
|
|
|
|
+ if (isCreator.value && timerIndicator.value.visible && props.type === '1') {
|
|
|
sendMessage({ type: 'timer_stop', courseid: props.courseid })
|
|
sendMessage({ type: 'timer_stop', courseid: props.courseid })
|
|
|
}
|
|
}
|
|
|
- // 创建老师刷新/关闭页面时,清空激光笔共享状态
|
|
|
|
|
- if (isCreator.value) {
|
|
|
|
|
|
|
+ // 创建老师刷新/关闭页面时,清空激光笔和画图共享状态
|
|
|
|
|
+ if (isCreator.value && props.type === '1') {
|
|
|
clearLaserState()
|
|
clearLaserState()
|
|
|
|
|
+ clearWritingBoardState()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2406,8 +2664,9 @@ onMounted(() => {
|
|
|
// visibilitychange 事件(适用于 iframe 嵌套场景,当外层页面返回时触发)
|
|
// visibilitychange 事件(适用于 iframe 嵌套场景,当外层页面返回时触发)
|
|
|
const handleVisibilityChange = () => {
|
|
const handleVisibilityChange = () => {
|
|
|
if (document.hidden && isCreator.value) {
|
|
if (document.hidden && isCreator.value) {
|
|
|
- // 页面被隐藏时,清空激光笔状态
|
|
|
|
|
|
|
+ // 页面被隐藏时,清空激光笔和画图状态
|
|
|
clearLaserState()
|
|
clearLaserState()
|
|
|
|
|
+ clearWritingBoardState()
|
|
|
if (timerIndicator.value.visible) {
|
|
if (timerIndicator.value.visible) {
|
|
|
sendMessage({ type: 'timer_stop', courseid: props.courseid })
|
|
sendMessage({ type: 'timer_stop', courseid: props.courseid })
|
|
|
}
|
|
}
|
|
@@ -2531,6 +2790,41 @@ const createWebSocketConnection = () => {
|
|
|
const s = yLaserState.value.toJSON()
|
|
const s = yLaserState.value.toJSON()
|
|
|
applyLaserStateSnapshot(s)
|
|
applyLaserStateSnapshot(s)
|
|
|
})
|
|
})
|
|
|
|
|
+ // 画图 map
|
|
|
|
|
+ yWritingBoardState.value = docSocket.value.getMap('writingBoardState')
|
|
|
|
|
+ const ws = yWritingBoardState.value.toJSON()
|
|
|
|
|
+ console.log('📝 WebSocket连接成功,读取画图状态:', ws, '当前幻灯片:', currentSlide.value?.id)
|
|
|
|
|
+ // 延迟应用,确保 currentSlide 已初始化
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ // 如果 currentSlide 还没准备好,再等一帧
|
|
|
|
|
+ if (currentSlide.value && currentSlide.value.id) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(ws)
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ // 如果还没准备好,等待 currentSlide 变化(最多等待3秒)
|
|
|
|
|
+ let timeoutId: any = null
|
|
|
|
|
+ const unwatch = watch(() => currentSlide.value?.id, (slideId) => {
|
|
|
|
|
+ if (slideId) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(ws)
|
|
|
|
|
+ unwatch()
|
|
|
|
|
+ if (timeoutId) clearTimeout(timeoutId)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, { immediate: true })
|
|
|
|
|
+ // 3秒后如果还没准备好,强制应用一次
|
|
|
|
|
+ timeoutId = setTimeout(() => {
|
|
|
|
|
+ if (currentSlide.value && currentSlide.value.id) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(ws)
|
|
|
|
|
+ }
|
|
|
|
|
+ unwatch()
|
|
|
|
|
+ }, 3000)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ yWritingBoardState.value.observe(() => {
|
|
|
|
|
+ const s = yWritingBoardState.value.toJSON()
|
|
|
|
|
+ if (currentSlide.value && currentSlide.value.id) {
|
|
|
|
|
+ applyWritingBoardStateSnapshot(s)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else if (event.status === 'disconnected') {
|
|
else if (event.status === 'disconnected') {
|