diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 7a7666d63..58d17a67f 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -267,14 +267,16 @@ export class DocumentModel { /** * 内部方法,请勿调用 */ - internalRemoveAndPurgeNode(node: Node) { + internalRemoveAndPurgeNode(node: Node, useMutator = false) { if (!this.nodes.has(node)) { return; } - this._nodesMap.delete(node.id); + node.remove(useMutator); + } + + unlinkNode(node: Node) { this.nodes.delete(node); - this.selection.remove(node.id); - node.remove(); + this._nodesMap.delete(node.id); } @obx.ref private _dropLocation: DropLocation | null = null; @@ -318,20 +320,20 @@ export class DocumentModel { * 导出 schema 数据 */ get schema(): RootSchema { - return this.rootNode.schema as any; + return this.rootNode?.schema as any; } import(schema: RootSchema, checkId = false) { - // TODO: do purge this.nodes.forEach(node => { + this.internalRemoveAndPurgeNode(node, true); this.destroyNode(node); }); - this.rootNode.import(schema as any, checkId); + this.rootNode?.import(schema as any, checkId); // todo: select added and active track added } export(stage: TransformStage = TransformStage.Serilize) { - return this.rootNode.export(stage); + return this.rootNode?.export(stage); } /** diff --git a/packages/designer/src/document/node/node-children.ts b/packages/designer/src/document/node/node-children.ts index 4ca63e4a0..527b72c61 100644 --- a/packages/designer/src/document/node/node-children.ts +++ b/packages/designer/src/document/node/node-children.ts @@ -16,7 +16,7 @@ export class NodeChildren { } - interalInitParent() { + internalInitParent() { this.children.forEach(child => child.internalSetParent(this.owner)); } @@ -56,7 +56,7 @@ export class NodeChildren { } this.children = children; - this.interalInitParent(); + this.internalInitParent(); if (!shallowEqual(children, originChildren)) { this.emitter.emit('change'); } @@ -92,20 +92,53 @@ export class NodeChildren { return this.children.length; } + private purged = false; /** - * 删除一个节点 + * 回收销毁 */ - delete(node: Node, purge = false, useMutator = true): boolean { + purge(useMutator = true) { + if (this.purged) { + return; + } + this.purged = true; + this.children.forEach((child) => { + child.purge(useMutator); + }); + } + + unlinkChild(node: Node) { const i = this.children.indexOf(node); if (i < 0) { return false; } - const deleted = this.children.splice(i, 1)[0]; + this.children.splice(i, 1); + } + /** + * 删除一个节点 + */ + delete(node: Node, purge = false, useMutator = true): boolean { + if (node.isParental()) { + node.children.forEach(subNode => { + subNode.remove(useMutator, purge); + }); + } if (purge) { // should set parent null - deleted.internalSetParent(null, useMutator); - deleted.purge(useMutator); + node.internalSetParent(null, useMutator); + try { + node.purge(useMutator); + } catch(err) { + console.error(err); + } } + const i = this.children.indexOf(node); + if (i < 0) { + return false; + } + this.children.splice(i, 1); + const document = node.document; + document.unlinkNode(node); + document.selection.remove(node.id); this.emitter.emit('change'); if (useMutator) { this.reportModified(node, this.owner, {type: 'remove', removeIndex: i, removeNode: node}); @@ -294,18 +327,6 @@ export class NodeChildren { }; } - private purged = false; - /** - * 回收销毁 - */ - purge(useMutator = true) { - if (this.purged) { - return; - } - this.purged = true; - this.children.forEach((child) => child.purge(useMutator)); - } - get [Symbol.toStringTag]() { // 保证向前兼容性 return 'Array'; @@ -331,7 +352,7 @@ export class NodeChildren { try { callbacks?.onSubtreeModified.call(node, owner, options); } catch (e) { - console.error('error when excute experimental.callbacks.onNodeAdd', e); + console.error('error when excute experimental.callbacks.onSubtreeModified', e); } } diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index c69f780d5..5b11fc097 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -155,9 +155,12 @@ export class Node { children: isDOMText(children) || isJSExpression(children) ? children : '', }); } else { + // 这里 props 被初始化两次,一次 new,一次 import,new 的实例需要给 propsReducer 的钩子去使用, + // import 是为了使用钩子返回的值,并非完全幂等的操作,部分行为执行两次会有 bug, + // 所以在 props 里会对 new / import 做一些区别化的解析 this.props = new Props(this, props, extras); this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children)); - this._children.interalInitParent(); + this._children.internalInitParent(); this.props.import(this.upgradeProps(this.initProps(props || {})), this.upgradeProps(extras || {})); this.setupAutoruns(); } @@ -258,14 +261,15 @@ export class Node { return; } + // 解除老的父子关系,但不需要真的删除节点 if (this._parent) { if (this.isSlot()) { - this._parent.removeSlot(this, false); + this._parent.unlinkSlot(this); } else { - this._parent.children.delete(this, false, useMutator); + this._parent.children.unlinkChild(this); } } - + // 建立新的父子关系 this._parent = parent; if (parent) { this.document.removeWillPurge(this); @@ -298,12 +302,12 @@ export class Node { /** * 移除当前节点 */ - remove(useMutator = true) { + remove(useMutator = true, purge = true) { if (this.parent) { if (this.isSlot()) { - this.parent.removeSlot(this, true); + this.parent.removeSlot(this, purge); } else { - this.parent.children.delete(this, true, useMutator); + this.parent.children.delete(this, purge, useMutator); } } } @@ -645,6 +649,14 @@ export class Node { return comparePosition(this, otherNode); } + unlinkSlot(slotNode: Node) { + const i = this._slots.indexOf(slotNode); + if (i < 0) { + return false; + } + this._slots.splice(i, 1); + } + /** * 删除一个Slot节点 */ @@ -653,12 +665,14 @@ export class Node { if (i < 0) { return false; } - const deleted = this._slots.splice(i, 1)[0]; if (purge) { // should set parent null - deleted.internalSetParent(null); - deleted.purge(); + slotNode.internalSetParent(null, false); + slotNode.purge(); } + this.document.unlinkNode(slotNode); + this.document.selection.remove(slotNode.id); + this._slots.splice(i, 1); return false; } @@ -699,20 +713,10 @@ export class Node { if (this.purged) { return; } - // if (this._parent) { - // // should remove thisNode before purge - // this.remove(useMutator); - // return; - // } this.purged = true; - if (this.isParental()) { - this.children.purge(useMutator); - } this.autoruns?.forEach((dispose) => dispose()); this.props.purge(); - this.document.internalRemoveAndPurgeNode(this); this.document.destroyNode(this); - this.remove(useMutator); } /** diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index b27b6e60b..5acfcc757 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -22,6 +22,37 @@ export class Prop implements IPropParent { readonly isProp = true; readonly owner: Node; + private stash: PropStash | undefined; + + /** + * 键值 + */ + @obx key: string | number | undefined; + /** + * 扩展值 + */ + @obx spread: boolean; + + readonly props: Props; + readonly options: any; + + constructor( + public parent: IPropParent, + value: CompositeValue | UNSET = UNSET, + key?: string | number, + spread = false, + options = {}, + ) { + this.owner = parent.owner; + this.props = parent.props; + this.key = key; + this.spread = spread; + this.options = options; + if (value !== UNSET) { + this.setValue(value); + } + } + /** * @see SettingTarget */ @@ -197,7 +228,7 @@ export class Prop implements IPropParent { } else if (Array.isArray(val)) { this._type = 'list'; } else if (isPlainObject(val)) { - if (isJSSlot(val)) { + if (isJSSlot(val) && this.options.propsMode !== 'init') { this.setAsSlot(val); return; } @@ -347,34 +378,6 @@ export class Prop implements IPropParent { return this._maps; } - private stash: PropStash | undefined; - - /** - * 键值 - */ - @obx key: string | number | undefined; - /** - * 扩展值 - */ - @obx spread: boolean; - - readonly props: Props; - - constructor( - public parent: IPropParent, - value: CompositeValue | UNSET = UNSET, - key?: string | number, - spread = false, - ) { - this.owner = parent.owner; - this.props = parent.props; - if (value !== UNSET) { - this.setValue(value); - } - this.key = key; - this.spread = spread; - } - /** * 获取某个属性 * @param stash 如果不存在,临时获取一个待写入 diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index 02852816f..e9c73ccd3 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -57,9 +57,9 @@ export class Props implements IPropParent { constructor(readonly owner: Node, value?: PropsMap | PropsList | null, extras?: object) { if (Array.isArray(value)) { this.type = 'list'; - this.items = value.map(item => new Prop(this, item.value, item.name, item.spread)); + this.items = value.map(item => new Prop(this, item.value, item.name, item.spread, { propsMode: 'init' })); } else if (value != null) { - this.items = Object.keys(value).map(key => new Prop(this, value[key], key)); + this.items = Object.keys(value).map(key => new Prop(this, value[key], key, false, { propsMode: 'init' })); } if (extras) { Object.keys(extras).forEach(key => { diff --git a/packages/editor-preset-vision/src/editor.ts b/packages/editor-preset-vision/src/editor.ts index a45b2f30b..86188d288 100644 --- a/packages/editor-preset-vision/src/editor.ts +++ b/packages/editor-preset-vision/src/editor.ts @@ -26,21 +26,10 @@ export const designer = new Designer({ editor: editor }); editor.set(Designer, designer); editor.set('designer', designer); -let nodeCache: any = {}; designer.project.onCurrentDocumentChange((doc) => { - nodeCache = {}; - doc.nodesMap.forEach((node) => { - nodeCache[node.id] = node; - }); doc.onRendererReady(() => { bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY); }); - doc.onNodeCreate((node) => { - nodeCache[node.id] = node; - }); - doc.onNodeDestroy((node) => { - delete nodeCache[node.id]; - }); }); interface Variable { @@ -89,6 +78,18 @@ function upgradePropsReducer(props: any) { // 升级 Props designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade); +function getCurrentFieldIds() { + const fieldIds: any = []; + const nodesMap = designer?.currentDocument?.nodesMap || new Map(); + nodesMap.forEach((curNode: any) => { + const fieldId = nodesMap?.get(curNode.id)?.getPropValue('fieldId'); + if (fieldId) { + fieldIds.push(fieldId); + } + }); + return fieldIds; +} + // 节点 props 初始化 designer.addPropsReducer((props, node) => { // run initials @@ -96,16 +97,8 @@ designer.addPropsReducer((props, node) => { ...props, }; if (newProps.fieldId) { - const fieldIds: any = []; - Object.keys(nodeCache).forEach(nodeId => { - if (nodeId === node.id) { - return; - } - const fieldId = nodeCache[nodeId].getPropValue('fieldId'); - if (fieldId) { - fieldIds.push(fieldId); - } - }); + const fieldIds = getCurrentFieldIds(); + // 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现 if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) { newProps.fieldId = undefined;