This commit is contained in:
2025-10-05 01:34:22 +08:00
parent 7b139d404e
commit aa8fcccf7d
4 changed files with 48 additions and 6 deletions

View File

@@ -11,6 +11,9 @@
- [API 文档](#api-文档) - [API 文档](#api-文档)
- [使用示例](#使用示例) - [使用示例](#使用示例)
- [故障排查](#故障排查) - [故障排查](#故障排查)
- [使用限制](#使用限制)
- [安全考虑](#安全考虑)
- [配置](#配置)
## 概述 ## 概述
@@ -25,6 +28,7 @@
- ✅ 权限控制(必须有一个超管) - ✅ 权限控制(必须有一个超管)
- ✅ 房间人数限制(最多 2 人) - ✅ 房间人数限制(最多 2 人)
- ✅ 离线检测和自动清理 - ✅ 离线检测和自动清理
- 🖥️ 仅支持桌面端(移动端不可用)
## 技术栈 ## 技术栈
@@ -617,6 +621,41 @@ setTimeout(() => {
}, AWARENESS_SYNC_DELAY) }, AWARENESS_SYNC_DELAY)
``` ```
## 使用限制
### 平台支持
协同编辑功能**仅支持桌面端**,移动端不可用。
**原因:**
1. **屏幕空间限制**
- 移动端屏幕较小,难以同时显示光标位置和多用户状态
- 协同编辑的用户信息标签需要足够的显示空间
2. **交互体验**
- 移动端触摸输入与协同编辑的光标跟随机制不够友好
- 桌面端的键盘和鼠标操作更适合代码编辑协作
3. **性能考虑**
- WebRTC 连接在移动设备上可能不够稳定
- 移动端的网络切换WiFi/4G/5G可能影响连接质量
**检测逻辑:**
```vue
<!-- Form.vue -->
<IconButton
v-if="isDesktop && userStore.isAuthed"
:icon="isSynced ? '...' : '...'"
@click="toggleSync"
/>
<template v-if="isDesktop && props.isSynced">
<n-tag>同步状态</n-tag>
</template>
```
## 安全考虑 ## 安全考虑
### 1. 权限控制 ### 1. 权限控制

View File

@@ -175,7 +175,7 @@ defineExpose({
@click="toggleSync" @click="toggleSync"
/> />
<template v-if="props.isSynced"> <template v-if="isDesktop && props.isSynced">
<n-tag v-if="otherUserInfo" type="info"> <n-tag v-if="otherUserInfo" type="info">
{{ otherUserInfo.name }} 同步中 {{ otherUserInfo.name }} 同步中
</n-tag> </n-tag>

View File

@@ -8,6 +8,7 @@ import { LANGUAGE } from "~/utils/types"
import { oneDark } from "../themes/oneDark" import { oneDark } from "../themes/oneDark"
import { smoothy } from "../themes/smoothy" import { smoothy } from "../themes/smoothy"
import { useCodeSync } from "../composables/sync" import { useCodeSync } from "../composables/sync"
import { isDesktop } from "../composables/breakpoints"
interface EditorReadyPayload { interface EditorReadyPayload {
view: EditorView view: EditorView
@@ -59,7 +60,7 @@ const lang = computed((): Extension => {
return ["Python2", "Python3"].includes(props.language) ? python() : cpp() return ["Python2", "Python3"].includes(props.language) ? python() : cpp()
}) })
const extensions = computed((): Extension[] => [ const extensions = computed(() => [
styleTheme, styleTheme,
lang.value, lang.value,
isDark.value ? oneDark : smoothy, isDark.value ? oneDark : smoothy,
@@ -79,7 +80,7 @@ const cleanupSyncResources = () => {
} }
const initSync = async () => { const initSync = async () => {
if (!editorView.value || !props.problem) return if (!editorView.value || !props.problem || !isDesktop.value) return
cleanupSyncResources() cleanupSyncResources()
@@ -97,7 +98,7 @@ const initSync = async () => {
const handleEditorReady = (payload: EditorReadyPayload) => { const handleEditorReady = (payload: EditorReadyPayload) => {
editorView.value = payload.view as EditorView editorView.value = payload.view as EditorView
if (props.sync && props.problem) { if (props.sync) {
initSync() initSync()
} }
} }
@@ -105,7 +106,7 @@ const handleEditorReady = (payload: EditorReadyPayload) => {
watch( watch(
() => props.sync, () => props.sync,
(shouldSync) => { (shouldSync) => {
if (shouldSync && props.problem && editorView.value) { if (shouldSync) {
initSync() initSync()
} else { } else {
cleanupSyncResources() cleanupSyncResources()
@@ -116,7 +117,7 @@ watch(
watch( watch(
() => props.problem, () => props.problem,
(newProblem, oldProblem) => { (newProblem, oldProblem) => {
if (newProblem !== oldProblem && props.sync && editorView.value) { if (newProblem !== oldProblem && props.sync) {
initSync() initSync()
} }
}, },

View File

@@ -234,6 +234,8 @@ export function useCodeSync() {
import("y-codemirror.next"), import("y-codemirror.next"),
]) ])
console.log("Yjs 相关模块导入完成")
// 初始化文档和提供者 // 初始化文档和提供者
ydoc = new Y.Doc() ydoc = new Y.Doc()
ytext = ydoc.getText("codemirror") ytext = ydoc.getText("codemirror")