fix(collab): 动态 import 竞态、切走页面的语义、演示模式的身份错位

- collabDoc.start 要 await 六个动态 import,房间可能在这期间就关了(机房首次
  加载 y* 那几个 chunk 正是最慢的时候)。stop() 先跑完面对的是 doc === null,
  什么也拆不到;import 回来之后 start 的后半段照样建文档、装 binaryHandler、
  挂 yCollab、发 SyncStep1——给一个不存在的房间。加会话代号,过期就整个放弃。

- SyncCodeEditor 卸载(切走题目页、把语言切成流程图)原来只是悄悄 stop(),
  房间和 active 都还留着,老师那边模态框照开、字照敲,一个也到不了;watch
  没有 immediate,学生切回来也不会重建。改成明确 leave() 结束协作。
  另外 @ready 也作为起点之一:学生排队时切走、老师这期间接了单,切回来能接上。

- 演示模式下 isTeacherOrAbove 被强制 false,服务端却按库里的 adminType 照样
  把他算作在线老师:学生因此拿到 pending 而不是 no_teacher,排队等一个顶栏里
  根本没有求助列表的人;他自己看到的求助按钮点下去,服务端回「教师不能发起求助」。
  两头都不对,索性对演示模式整个关掉这个功能。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016DHxhKNxXfG89JnVzHbvgj
This commit is contained in:
2026-08-30 09:38:04 -06:00
parent 66a564710f
commit 4abaf9c7e4
4 changed files with 48 additions and 22 deletions

View File

@@ -68,8 +68,17 @@ interface EditorReadyPayload {
// 项目里旧的 sync.ts 用的是裸变量,同一个道理,这里换成 shallowRef 规避。
const editorView = shallowRef<EditorView | null>(null)
const bind = (view: EditorView) => {
if (collabStore.isTeacher || !collabStore.room) return
// 学生端:当前编辑器内容就是内容源
start({ editorView: view, seedContent: view.state.doc.toString() })
}
const handleEditorReady = (payload: EditorReadyPayload) => {
editorView.value = payload.view
// 也从这里起:学生排队时切去看提交记录、老师在这期间接了单,
// 等他切回来时 room 早就非空了,只靠下面的 watch 是等不到的
bind(payload.view)
}
// 房间开了才建文档。学生点求助时什么都不做 —— 老师没来之前不该动他的编辑器。
@@ -82,19 +91,19 @@ const handleEditorReady = (payload: EditorReadyPayload) => {
watch(
() => collabStore.room,
(room) => {
if (room && !collabStore.isTeacher && editorView.value) {
// 学生端:当前编辑器内容就是内容源
start({
editorView: editorView.value,
seedContent: editorView.value.state.doc.toString(),
})
} else {
stop()
}
if (room && !collabStore.isTeacher && editorView.value) bind(editorView.value)
else stop()
},
)
onUnmounted(stop)
onUnmounted(() => {
stop()
// 这个组件卸载意味着编辑器没了(切走页面、或者把语言切成流程图),
// CRDT 会话没法接着用:回来时只能新建 Y.Doc再拿编辑器内容当种子就会和
// 老师那份合并成重复文本。所以不是"悄悄把绑定拆了",而是明确结束协作 ——
// 否则老师那边模态框照开、字照敲,一个也到不了学生那里
if (collabStore.room && !collabStore.isTeacher) collabStore.leave()
})
</script>
<template>