Skip to content

Commit 5359f22

Browse files
committed
feat: props 多人协作同步
1 parent 1b9a740 commit 5359f22

7 files changed

Lines changed: 109 additions & 5 deletions

File tree

packages/canvas/container/src/CanvasContainer.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ export default {
474474
moveDownSharedNode,
475475
moveUpSharedNode,
476476
updateStyleNode,
477+
updatePropsNode,
477478
remoteStates
478479
} = useCollabSchema({
479480
roomId: 'schema-yjs',
@@ -487,6 +488,7 @@ export default {
487488
moveDownSharedNode,
488489
moveUpSharedNode,
489490
updateStyleNode,
491+
updatePropsNode,
490492
remoteStates
491493
})
492494

packages/multi-person-collaboration/src/composables/useCollabSchema.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ export function useCollabSchema(options: UseCollabSchemaOptions) {
7878
schemaModel.updatedNodeCss(styleStr, nodeId, className)
7979
}
8080

81+
// settings, 修改节点属性
82+
const updatePropsNode = (newProps: Record<any, any>, nodeId: string, overwrite: boolean) => {
83+
schemaModel.updatedNodeProps(newProps, nodeId, overwrite)
84+
}
85+
8186
// 用户信息同步 方法
8287
const updateUserSelection = (selectedNode: any) => {
8388
updateLocalStateField('selection', selectedNode)
@@ -111,6 +116,7 @@ export function useCollabSchema(options: UseCollabSchemaOptions) {
111116
deleteSharedNode,
112117
moveUpSharedNode,
113118
moveDownSharedNode,
114-
updateStyleNode
119+
updateStyleNode,
120+
updatePropsNode
115121
}
116122
}

packages/multi-person-collaboration/src/models/NodeSchemaModel.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,12 @@ export class NodeSchemaModel {
7070

7171
// canvas 内,更新节点样式
7272
public updatedNodeCss(strStyle: string, nodeId: string, className: string) {
73-
this.operationHandler.updatedStyle(strStyle, nodeId, className)
73+
this.operationHandler.updatedStyle({ strStyle, nodeId, className })
74+
}
75+
76+
// settings,更新节点属性
77+
public updatedNodeProps(newProps: Record<any, any>, nodeId: string, overwrite: boolean) {
78+
this.operationHandler.updatedProps({ newProps, nodeId, overwrite })
7479
}
7580

7681
// insert 操作

packages/multi-person-collaboration/src/operation/operationHandler .ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import type { DeleteOperation, MoveOperation, Node, NodeOperation, PageSchema } from '../type'
1+
import type {
2+
DeleteOperation,
3+
MoveOperation,
4+
Node,
5+
NodeOperation,
6+
PageSchema,
7+
UpdatePropsOperation,
8+
UpdateStyleOperation
9+
} from '../type'
210
import * as Y from 'yjs'
311
import { toYjs } from '../utils'
412
import { DocManager } from '../services/docManager'
@@ -154,7 +162,8 @@ export class OperationHandler {
154162
}
155163

156164
// 修改节点样式
157-
public updatedStyle(strStyle: string, nodeId: string, className: string) {
165+
public updatedStyle(operation: UpdateStyleOperation) {
166+
const { strStyle, nodeId, className } = operation
158167
// 添加样式
159168
this.yMap.set('css', strStyle)
160169

@@ -165,6 +174,46 @@ export class OperationHandler {
165174
Object.assign(this.rootSchema, { css: strStyle })
166175
}
167176

177+
// 修改节点属性
178+
public updatedProps(operation: UpdatePropsOperation) {
179+
const { newProps, nodeId, overwrite } = operation
180+
let node = this.getYNode(nodeId)
181+
182+
if (!node) {
183+
node = this.yMap
184+
}
185+
186+
const yNewProps = new Y.Map<any>() // 新的 props
187+
const propsMap = node.get('props') as Y.Map<any> // 旧的 props
188+
189+
if (overwrite) {
190+
// 覆盖模式
191+
for (const [k, v] of Object.entries(newProps || {})) {
192+
yNewProps.set(k, v)
193+
}
194+
} else {
195+
// 先复制旧的
196+
if (propsMap) {
197+
propsMap.forEach((val, key) => {
198+
yNewProps.set(key, val)
199+
})
200+
}
201+
202+
// 再合并新的
203+
for (const [k, v] of Object.entries(newProps) || {}) {
204+
yNewProps.set(k, v)
205+
}
206+
}
207+
208+
// 元数据,用于补丁操作
209+
const meta = new Y.Map<any>()
210+
meta.set('nodeId', nodeId)
211+
meta.set('overwrite', overwrite)
212+
213+
yNewProps.set('meta', meta)
214+
node.set('props', yNewProps)
215+
}
216+
168217
// 重建整个映射(刷新后可以手动调用)
169218
public rebuildYNodeMap(rootSchema: PageSchema) {
170219
this.yNodeMap.clear()

packages/multi-person-collaboration/src/services/schemaManager.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type DiffPatch =
2424
}
2525
| { type: 'style-update'; path: (string | number)[]; css: string }
2626
| { type: 'class-add'; path: (string | number)[]; nodeId: string; className: string }
27+
| { type: 'props-update'; path: (string | number)[]; props: Record<any, any>; meta: Record<any, any> }
2728

2829
/**
2930
* SchemaManager 类,负责管理 Yjs 中的 NodeSchema 文档
@@ -278,6 +279,20 @@ export class SchemaManager {
278279
css: newCss
279280
})
280281
}
282+
} else if (key === 'props') {
283+
// Props 属性更新同步逻辑
284+
if (change.action === 'add' || change.action === 'update') {
285+
const newProps = yMapNode.get('props').toJSON()
286+
const meta = newProps.meta // meta 包含操作的 nodeId 和 overwrite
287+
288+
delete newProps.meta // 删除元数据,保持 props 不被污染
289+
patches.push({
290+
type: 'props-update',
291+
path: event.path,
292+
props: newProps,
293+
meta
294+
})
295+
}
281296
}
282297
})
283298
}
@@ -404,6 +419,18 @@ export class SchemaManager {
404419
updateSchema({ css: strStyle })
405420
break
406421
}
422+
case 'props-update': {
423+
const { props: newProps, meta } = patch
424+
const { nodeId, overwrite } = meta
425+
426+
useCanvas().operateNode({
427+
type: 'changeProps',
428+
id: nodeId || '',
429+
value: { props: newProps },
430+
option: { overwrite }
431+
})
432+
break
433+
}
407434
default:
408435
break
409436
}

packages/multi-person-collaboration/src/type.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@ export interface MoveOperation {
9090
direction: 'up' | 'down'
9191
}
9292

93+
export interface UpdateStyleOperation {
94+
strStyle: string
95+
nodeId: string
96+
className: string
97+
}
98+
99+
export interface UpdatePropsOperation {
100+
newProps: Record<string, string>
101+
nodeId: string
102+
overwrite: boolean
103+
}
104+
93105
export interface UserAwareness {
94106
id?: string | number
95107
name: string

packages/settings/props/src/composable/useProperties.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/* metaService: engine.service.properties.useProperties */
1414
import { toRaw, shallowReactive, ref } from 'vue'
1515
import { constants } from '@opentiny/tiny-engine-utils'
16-
import { useCanvas, useMaterial, useTranslate } from '@opentiny/tiny-engine-meta-register'
16+
import { useCanvas, useMaterial, useTranslate, useRealtimeCollab } from '@opentiny/tiny-engine-meta-register'
1717
import type { Property, Schema } from '@opentiny/tiny-engine-plugin-materials'
1818

1919
const { COMPONENT_NAME } = constants
@@ -142,6 +142,9 @@ const setProp = (name: string, value: unknown, type?: unknown) => {
142142
option: { overwrite }
143143
})
144144

145+
// 多人协作,属性同步
146+
useRealtimeCollab().updatePropsNode(newProps, properties.schema.id || '', overwrite)
147+
145148
propsUpdateKey.value++
146149
}
147150

0 commit comments

Comments
 (0)