diff --git a/packages/designer/src/designer/setting/setting-prop-entry.ts b/packages/designer/src/designer/setting/setting-prop-entry.ts index f764e01d8..eec80fffa 100644 --- a/packages/designer/src/designer/setting/setting-prop-entry.ts +++ b/packages/designer/src/designer/setting/setting-prop-entry.ts @@ -126,6 +126,9 @@ export class SettingPropEntry implements SettingEntry { const { getValue } = this.extraProps; return getValue ? (getValue(this, undefined) === undefined ? 0 : 1) : 0; } + if (this.nodes.length === 1) { + return 2; + } const propName = this.path.join('.'); const first = this.nodes[0].getProp(propName)!; let l = this.nodes.length; diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 76124a1df..19097b08f 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -214,6 +214,7 @@ export class DocumentModel { /** * 根据 schema 创建一个节点 */ + @action createNode(data: GetDataType, checkId: boolean = true): T { let schema: any; if (isDOMText(data) || isJSExpression(data)) { diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index ad6172b96..262557433 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, wrapWithEventSwitch } from '@ali/lowcode-editor-core'; +import { obx, computed, autorun, makeObservable, runInAction, wrapWithEventSwitch, action } from '@ali/lowcode-editor-core'; import { isDOMText, isJSExpression, @@ -198,6 +198,7 @@ export class Node { /** * 节点初始化期间就把内置的一些 prop 初始化好,避免后续不断构造实例导致 reaction 执行多次 */ + @action private initBuiltinProps() { this.props.has(getConvertedExtraKey('hidden')) || this.props.add(false, getConvertedExtraKey('hidden')); this.props.has(getConvertedExtraKey('title')) || this.props.add('', getConvertedExtraKey('title')); @@ -207,10 +208,12 @@ export class Node { this.props.has(getConvertedExtraKey('loop')) || this.props.add(undefined, getConvertedExtraKey('loop')); } + @action private initProps(props: any): any { return this.document.designer.transformProps(props, this, TransformStage.Init); } + @action private upgradeProps(props: any): any { return this.document.designer.transformProps(props, this, TransformStage.Upgrade); } diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index df8cb1b56..d6e86792d 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -1,4 +1,4 @@ -import { untracked, computed, obx, engineConfig, action, makeObservable, mobx } from '@ali/lowcode-editor-core'; +import { untracked, computed, obx, engineConfig, action, makeObservable, mobx, runInAction } from '@ali/lowcode-editor-core'; import { CompositeValue, GlobalEvent, isJSExpression, isJSSlot, JSSlot, SlotSchema } from '@ali/lowcode-types'; import { uniqueId, isPlainObject, hasOwnProperty, compatStage } from '@ali/lowcode-utils'; import { valueToSource } from './value-to-source'; @@ -59,7 +59,7 @@ export class Prop implements IPropParent { // TODO: 先用调用方式触发子 prop 的初始化,后续须重构 @action - private setupItems() { + setupItems() { return this.items; } @@ -234,6 +234,7 @@ export class Prop implements IPropParent { @action setValue(val: CompositeValue) { if (val === this._value) return; + this.dispose(); const editor = this.owner.document?.designer.editor; const oldValue = this._value; this._value = val; @@ -262,8 +263,6 @@ export class Prop implements IPropParent { }; } - this.dispose(); - if (oldValue !== this._value) { const propsInfo = { key: this.key, @@ -377,29 +376,31 @@ export class Prop implements IPropParent { @computed private get items(): Prop[] | null { if (this._items) return this._items; - let items: Prop[] | null = []; - if (this._type === 'list') { - const data = this._value; - for (const item of data) { - items.push(new Prop(this, item)); + return runInAction(() => { + let items: Prop[] | null = []; + if (this._type === 'list') { + const data = this._value; + for (const item of data) { + items.push(new Prop(this, item)); + } + this._maps = null; + } else if (this._type === 'map') { + const data = this._value; + const maps = new Map(); + const keys = Object.keys(data); + for (const key of keys) { + const prop = new Prop(this, data[key], key); + items.push(prop); + maps.set(key, prop); + } + this._maps = maps; + } else { + items = null; + this._maps = null; } - this._maps = null; - } else if (this._type === 'map') { - const data = this._value; - const maps = new Map(); - const keys = Object.keys(data); - for (const key of keys) { - const prop = new Prop(this, data[key], key); - items.push(prop); - maps.set(key, prop); - } - this._maps = maps; - } else { - items = null; - this._maps = null; - } - this._items = items; - return this._items; + this._items = items; + return this._items; + }); } @computed private get maps(): Map | null { diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index b2cadab4c..b0c7a082d 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -94,10 +94,12 @@ export class Props implements IPropParent { merge(value: PropsMap, extras?: PropsMap) { Object.keys(value).forEach(key => { this.query(key, true)!.setValue(value[key]); + this.query(key, true)!.setupItems(); }); if (extras) { Object.keys(extras).forEach(key => { this.query(getConvertedExtraKey(key), true)!.setValue(extras[key]); + this.query(getConvertedExtraKey(key), true)!.setupItems(); }); } } diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index 6d5a09cf8..aaada91c8 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -192,6 +192,7 @@ export class Project { return this.documents.find(doc => doc.id === id) || null; } + @action createDocument(data?: RootSchema): DocumentModel { const doc = new DocumentModel(this, data || this?.data?.componentsTree?.[0]); this.documents.push(doc); diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 0552799b7..d1fd3c21f 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -1,5 +1,5 @@ import { Component, MouseEvent, Fragment } from 'react'; -import { shallowIntl, createSetterContent, observer, obx, engineConfig } from '@ali/lowcode-editor-core'; +import { shallowIntl, createSetterContent, observer, obx, engineConfig, runInAction } from '@ali/lowcode-editor-core'; import { createContent } from '@ali/lowcode-utils'; import { createField } from '../field'; import PopupService, { PopupPipe } from '../popup'; @@ -22,14 +22,44 @@ function isStandardComponent(componentMeta: ComponentMeta | null) { return prototype == null; } +type SettingFieldViewProps = { field: SettingField }; @observer class SettingFieldView extends Component<{ field: SettingField }> { static contextType = SkeletonContext; + stageName: string | undefined; + + constructor(props: SettingFieldViewProps) { + super(props); + + const { field } = this.props; + const { extraProps } = field; + const { display } = extraProps; + + const { stages } = field.editor.get('skeleton') as Skeleton; + let stageName; + if (display === 'entry') { + runInAction(() => { + stageName = `${field.getNode().id }_${field.name.toString()}`; + // 清除原 stage,不然 content 引用的一直是老的 field,导致数据无法得到更新 + stages.container.remove(stageName); + const stage = stages.add({ + type: 'Widget', + name: stageName, + content: {field.items.map((item, index) => createSettingFieldView(item, field, index))}, + props: { + title: field.title, + }, + }); + }); + } + this.stageName = stageName; + } + render() { const { field } = this.props; const { extraProps, componentMeta } = field; - const { condition, defaultValue, display } = extraProps; + const { condition, defaultValue } = extraProps; let visible; try { visible = typeof condition === 'function' ? condition(field) !== false : true; @@ -102,24 +132,8 @@ class SettingFieldView extends Component<{ field: SettingField }> { value = field.getValue(); } - const skeleton = this.context as Skeleton; - const { stages } = skeleton; - let _onChange = extraProps?.onChange; - let stageName; - if (display === 'entry') { - stageName = `${field.getNode().id }_${field.name.toString()}`; - // 清除原 stage,不然 content 引用的一直是老的 field,导致数据无法得到更新 - stages.container.remove(stageName); - const stage = stages.add({ - type: 'Widget', - name: stageName, - content: {field.items.map((item, index) => createSettingFieldView(item, field, index))}, - props: { - title: field.title, - }, - }); - } + let stageName = this.stageName; return createField( { @@ -175,10 +189,41 @@ class SettingFieldView extends Component<{ field: SettingField }> { } } +type SettingGroupViewProps = SettingFieldViewProps; @observer -class SettingGroupView extends Component<{ field: SettingField }> { +class SettingGroupView extends Component { static contextType = SkeletonContext; + stageName: string | undefined; + + constructor(props: SettingGroupViewProps) { + super(props); + const { field } = this.props; + const { extraProps } = field; + const { condition, display } = extraProps; + + const { stages } = field.editor.get('skeleton') as Skeleton; + // const items = field.items; + + let stageName; + if (display === 'entry') { + runInAction(() => { + stageName = `${field.getNode().id }_${field.name.toString()}`; + // 清除原 stage,不然 content 引用的一直是老的 field,导致数据无法得到更新 + stages.container.remove(stageName); + stages.add({ + type: 'Widget', + name: stageName, + content: {field.items.map((item, index) => createSettingFieldView(item, field, index))}, + props: { + title: field.title, + }, + }); + }); + } + this.stageName = stageName; + } + render() { const { field } = this.props; const { extraProps } = field; @@ -189,24 +234,6 @@ class SettingGroupView extends Component<{ field: SettingField }> { return null; } - const skeleton = this.context as Skeleton; - const { stages } = skeleton; - - let stageName; - if (display === 'entry') { - stageName = `${field.getNode().id }_${field.name.toString()}`; - // 清除原 stage,不然 content 引用的一直是老的 field,导致数据无法得到更新 - stages.container.remove(stageName); - stages.add({ - type: 'Widget', - name: stageName, - content: {field.items.map((item, index) => createSettingFieldView(item, field, index))}, - props: { - title: field.title, - }, - }); - } - // todo: split collapsed state | field.items for optimize return createField( { @@ -217,7 +244,7 @@ class SettingGroupView extends Component<{ field: SettingField }> { onExpandChange: (expandState) => field.setExpanded(expandState), // field: field, // stages, - stageName, + stageName: this.stageName, }, field.items.map((item, index) => createSettingFieldView(item, field, index)), display, diff --git a/packages/plugin-outline-pane/src/tree-node.ts b/packages/plugin-outline-pane/src/tree-node.ts index 77c1132d4..07a2debd2 100644 --- a/packages/plugin-outline-pane/src/tree-node.ts +++ b/packages/plugin-outline-pane/src/tree-node.ts @@ -1,5 +1,5 @@ import { TitleContent, isI18nData } from '@ali/lowcode-types'; -import { computed, obx, intl, makeObservable } from '@ali/lowcode-editor-core'; +import { computed, obx, intl, makeObservable, action } from '@ali/lowcode-editor-core'; import { Node, DocumentModel, isLocationChildrenDetail, LocationChildrenDetail, Designer } from '@ali/lowcode-designer'; import { Tree } from './tree'; @@ -225,6 +225,7 @@ export default class TreeNode { this._node = node; } + @action setNode(node: Node) { if (this._node !== node) { this._node = node;