From 0f34f2702dc9e22dc200853d76647e6ac437cf9e Mon Sep 17 00:00:00 2001 From: "lihao.ylh" Date: Tue, 28 Sep 2021 12:38:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20doc=20import=20=E6=97=B6=E4=B8=B4?= =?UTF-8?q?=E6=97=B6=E5=85=B3=E9=97=AD=20Node=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 4 +- .../src/designer/setting/setting-top-entry.ts | 8 +- .../designer/src/document/document-model.ts | 34 +++++---- packages/designer/src/document/node/node.ts | 15 ++-- .../__snapshots__/document-model.test.ts.snap | 16 ++-- packages/editor-core/src/utils/control.ts | 30 ++++++++ packages/editor-core/src/utils/index.ts | 1 + packages/renderer-core/src/hoc/leaf.tsx | 73 +++++++++++++------ 8 files changed, 123 insertions(+), 58 deletions(-) create mode 100644 packages/editor-core/src/utils/control.ts diff --git a/.eslintrc.js b/.eslintrc.js index 77e001b13..3f11e47e4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,8 +28,6 @@ module.exports = { 'eol-last': 0, 'react/no-find-dom-node': 0, 'no-case-declarations': 0, - '@typescript-eslint/indent': 0, - "indent": "off", - "@typescript-eslint/indent": ["error", 2] + '@typescript-eslint/indent': 0 } }; diff --git a/packages/designer/src/designer/setting/setting-top-entry.ts b/packages/designer/src/designer/setting/setting-top-entry.ts index ff65ad4b3..b63fd79d9 100644 --- a/packages/designer/src/designer/setting/setting-top-entry.ts +++ b/packages/designer/src/designer/setting/setting-top-entry.ts @@ -71,6 +71,8 @@ export class SettingTopEntry implements SettingEntry { readonly designer: Designer; + disposeFunctions: any[] = []; + constructor(readonly editor: IEditor, readonly nodes: Node[]) { if (!Array.isArray(nodes) || nodes.length < 1) { throw new ReferenceError('nodes should not be empty'); @@ -85,7 +87,7 @@ export class SettingTopEntry implements SettingEntry { // clear fields this.setupItems(); - this.setupEvents(); + this.disposeFunctions.push(this.setupEvents()); } private setupComponentMeta() { @@ -127,7 +129,7 @@ export class SettingTopEntry implements SettingEntry { } private setupEvents() { - this.componentMeta?.onMetadataChange(() => { + return this.componentMeta?.onMetadataChange(() => { this.setupItems(); }); } @@ -219,6 +221,8 @@ export class SettingTopEntry implements SettingEntry { this.disposeItems(); this._settingFieldMap = {}; this.emitter.removeAllListeners(); + this.disposeFunctions.forEach(f => f()); + this.disposeFunctions = []; } diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 3090662e9..6b9e70f13 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -1,4 +1,4 @@ -import { computed, makeObservable, obx, action } from '@ali/lowcode-editor-core'; +import { computed, makeObservable, obx, action, runWithGlobalEventOff, wrapWithEventSwitch } from '@ali/lowcode-editor-core'; import { NodeData, isJSExpression, isDOMText, NodeSchema, isNodeSchema, RootSchema, PageSchema } from '@ali/lowcode-types'; import { EventEmitter } from 'events'; import { Project } from '../project'; @@ -361,17 +361,19 @@ export class DocumentModel { @action import(schema: RootSchema, checkId = false) { const drillDownNodeId = this._drillDownNode?.id; - // TODO: 暂时用饱和式删除,原因是 Slot 节点并不是树节点,无法正常递归删除 - this.nodes.forEach(node => { - if (node.isRoot()) return; - this.internalRemoveAndPurgeNode(node, true); - }); - this.rootNode?.import(schema as any, checkId); + runWithGlobalEventOff(() => { + // TODO: 暂时用饱和式删除,原因是 Slot 节点并不是树节点,无法正常递归删除 + this.nodes.forEach(node => { + if (node.isRoot()) return; + this.internalRemoveAndPurgeNode(node, true); + }); + this.rootNode?.import(schema as any, checkId); - // todo: select added and active track added - if (drillDownNodeId) { - this.drillDown(this.getNode(drillDownNodeId)); - } + // todo: select added and active track added + if (drillDownNodeId) { + this.drillDown(this.getNode(drillDownNodeId)); + } + }); } export(stage: TransformStage = TransformStage.Serilize) { @@ -699,16 +701,18 @@ export class DocumentModel { } onNodeCreate(func: (node: Node) => void) { - this.emitter.on('nodecreate', func); + const wrappedFunc = wrapWithEventSwitch(func); + this.emitter.on('nodecreate', wrappedFunc); return () => { - this.emitter.removeListener('nodecreate', func); + this.emitter.removeListener('nodecreate', wrappedFunc); }; } onNodeDestroy(func: (node: Node) => void) { - this.emitter.on('nodedestroy', func); + const wrappedFunc = wrapWithEventSwitch(func); + this.emitter.on('nodedestroy', wrappedFunc); return () => { - this.emitter.removeListener('nodedestroy', func); + this.emitter.removeListener('nodedestroy', wrappedFunc); }; } diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index af9593d86..86bfda345 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -1,6 +1,6 @@ import { ReactElement } from 'react'; import { EventEmitter } from 'events'; -import { obx, computed, autorun, makeObservable, runInAction, getObserverTree } from '@ali/lowcode-editor-core'; +import { obx, computed, autorun, makeObservable, runInAction, wrapWithEventSwitch } from '@ali/lowcode-editor-core'; import { isDOMText, isJSExpression, @@ -560,9 +560,10 @@ export class Node { } onVisibleChange(func: (flag: boolean) => any): () => void { - this.emitter.on('visibleChange', func); + const wrappedFunc = wrapWithEventSwitch(func); + this.emitter.on('visibleChange', wrappedFunc); return () => { - this.emitter.removeListener('visibleChange', func); + this.emitter.removeListener('visibleChange', wrappedFunc); }; } @@ -920,7 +921,8 @@ export class Node { } onChildrenChange(fn: (param?: { type: string, node: Node }) => void): (() => void) | undefined { - return this.children?.onChange(fn); + const wrappedFunc = wrapWithEventSwitch(fn); + return this.children?.onChange(wrappedFunc); } mergeChildren( @@ -1118,9 +1120,10 @@ export class Node { } onPropChange(func: (info: PropChangeOptions) => void): Function { - this.emitter.on('propChange', func); + const wrappedFunc = wrapWithEventSwitch(func); + this.emitter.on('propChange', wrappedFunc); return () => { - this.emitter.removeListener('propChange', func); + this.emitter.removeListener('propChange', wrappedFunc); }; } } diff --git a/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap b/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap index dd31db7de..779fdd751 100644 --- a/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap +++ b/packages/designer/tests/document/document-model/__snapshots__/document-model.test.ts.snap @@ -180,7 +180,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -259,7 +259,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -334,7 +334,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -426,7 +426,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -517,7 +517,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -684,7 +684,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -763,7 +763,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", @@ -855,7 +855,7 @@ Object { "labelTextAlign": "right", "labelTipsIcon": "", "labelTipsText": Object { - "en_US": "", + "en_US": null, "type": "i18n", "use": "zh_CN", "zh_CN": "", diff --git a/packages/editor-core/src/utils/control.ts b/packages/editor-core/src/utils/control.ts new file mode 100644 index 000000000..664ca938b --- /dev/null +++ b/packages/editor-core/src/utils/control.ts @@ -0,0 +1,30 @@ +let globalEventOn = true; + +export function setGlobalEventFlag(flag: boolean) { + globalEventOn = flag; +} + +export function switchGlobalEventOn() { + setGlobalEventFlag(true); +} + +export function switchGlobalEventOff() { + setGlobalEventFlag(false); +} + +export function isGlobalEventOn() { + return globalEventOn; +} + +export function runWithGlobalEventOff(fn: Function) { + switchGlobalEventOff(); + fn(); + switchGlobalEventOn(); +} + +type ListenerFunc = (...args: any[]) => void; +export function wrapWithEventSwitch(fn: ListenerFunc): ListenerFunc { + return (...args: any[]) => { + if (isGlobalEventOn()) fn(...args); + }; +} \ No newline at end of file diff --git a/packages/editor-core/src/utils/index.ts b/packages/editor-core/src/utils/index.ts index 8f37753ac..add9b05f6 100644 --- a/packages/editor-core/src/utils/index.ts +++ b/packages/editor-core/src/utils/index.ts @@ -3,3 +3,4 @@ export * from './monitor'; export * from './obx'; export * from './request'; export * from './focus-tracker'; +export * from './control'; diff --git a/packages/renderer-core/src/hoc/leaf.tsx b/packages/renderer-core/src/hoc/leaf.tsx index 06636a16b..b5bd93045 100644 --- a/packages/renderer-core/src/hoc/leaf.tsx +++ b/packages/renderer-core/src/hoc/leaf.tsx @@ -90,7 +90,6 @@ export function leafWrapper(Comp: types.IBaseRenderer, { type?: string; node?: Node; } = {}; - static displayName = schema.componentName; disposeFunctions: ((() => void) | Function)[] = []; @@ -140,19 +139,44 @@ export function leafWrapper(Comp: types.IBaseRenderer, { return whitelist.includes(schema.componentName); } - static getDerivedStateFromProps(props: any, state: any) { - if (props.__tag === state.__tag) { + componentWillReceiveProps(nextProps: any) { + if (nextProps.__tag === this.state.__tag) { return null; } + const { _leaf, __tag, children, ...rest } = nextProps; + if (_leaf && this.leaf && _leaf !== this.leaf) { + this.disposeFunctions.forEach(fn => fn()); + this.disposeFunctions = []; + this.initOnChildrenChangeEvent(_leaf); + this.initOnPropsChangeEvent(_leaf); + this.initOnVisibleChangeEvent(_leaf); + } - return { - nodeChildren: props.children, - nodeProps: props.nodeProps, + this.setState({ + nodeChildren: children, + nodeProps: rest, childrenInState: true, - __tag: props.__tag, - }; + __tag, + }); } + // static getDerivedStateFromProps(props: any, state: any) { + // if (props.__tag === state.__tag) { + // return null; + // } + // if (props._leaf && this.state.leaf && props._leaf !== this.state.leaf) { + // this.disposeFunctions.forEach(fn => fn()); + // } + + // return { + // nodeChildren: props.children, + // nodeProps: props.nodeProps, + // childrenInState: true, + // __tag: props.__tag, + // _leaf: props._leaf, + // }; + // } + shouldComponentUpdate() { if (this.isInWhitelist) { __debug(`${schema.componentName} is in leaf Hoc whitelist`); @@ -164,12 +188,12 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } /** 监听参数变化 */ - initOnPropsChangeEvent(): void { - const dispose = this.leaf?.onPropChange?.((propChangeInfo: PropChangeOptions) => { + initOnPropsChangeEvent(leaf = this.leaf): void { + const dispose = leaf?.onPropChange?.((propChangeInfo: PropChangeOptions) => { const { key, } = propChangeInfo; - const node = this.leaf; + const node = leaf; // 如果循坏条件变化,从根节点重新渲染 // 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决 @@ -180,7 +204,7 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } this.beforeRender(RerenderType.PropsChanged); - __debug(`${this.leaf?.componentName} component trigger onPropsChange event`); + __debug(`${leaf?.componentName} component trigger onPropsChange event`); const nextProps = getProps(node?.export?.(TransformStage.Render) as types.ISchema, Comp, componentInfo); this.setState(nextProps.children ? { nodeChildren: nextProps.children, @@ -196,13 +220,13 @@ export function leafWrapper(Comp: types.IBaseRenderer, { /** * 监听显隐变化 */ - initOnVisibleChangeEvent() { - const dispose = this.leaf?.onVisibleChange?.((flag: boolean) => { + initOnVisibleChangeEvent(leaf = this.leaf) { + const dispose = leaf?.onVisibleChange?.((flag: boolean) => { if (this.state.visible === flag) { return; } - __debug(`${this.leaf?.componentName} component trigger onVisibleChange event`); + __debug(`${leaf?.componentName} component trigger onVisibleChange event`); this.beforeRender(RerenderType.VisibleChanged); this.setState({ visible: flag, @@ -215,15 +239,15 @@ export function leafWrapper(Comp: types.IBaseRenderer, { /** * 监听子元素变化(拖拽,删除...) */ - initOnChildrenChangeEvent() { - const dispose = this.leaf?.onChildrenChange?.((param): void => { + initOnChildrenChangeEvent(leaf = this.leaf) { + const dispose = leaf?.onChildrenChange?.((param): void => { const { type, node, } = param || {}; this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node); - __debug(`${this.leaf} component trigger onChildrenChange event`); - const nextChild = getChildren(this.leaf?.export?.(TransformStage.Render) as types.ISchema, Comp); + __debug(`${leaf} component trigger onChildrenChange event`); + const nextChild = getChildren(leaf?.export?.(TransformStage.Render) as types.ISchema, Comp); this.setState({ nodeChildren: nextChild, childrenInState: true, @@ -238,15 +262,16 @@ export function leafWrapper(Comp: types.IBaseRenderer, { } get hasChildren(): boolean { + let { children } = this.props; if (this.state.childrenInState) { - return !!this.state.nodeChildren?.length; + children = this.state.nodeChildren; } - if (Array.isArray(this.props.children)) { - return Boolean(this.props.children && this.props.children.length); + if (Array.isArray(children)) { + return Boolean(children && children.length); } - return Boolean(this.props.children); + return Boolean(children); } get children(): any { @@ -305,4 +330,4 @@ export function leafWrapper(Comp: types.IBaseRenderer, { LeafWrapper.displayName = (Comp as any).displayName; return LeafWrapper; -} +} \ No newline at end of file