diff --git a/packages/designer/src/designer/setting/setting-field.ts b/packages/designer/src/designer/setting/setting-field.ts index 2267fcef9..0bedf77c3 100644 --- a/packages/designer/src/designer/setting/setting-field.ts +++ b/packages/designer/src/designer/setting/setting-field.ts @@ -6,13 +6,13 @@ import { computed, obx } from '@ali/lowcode-editor-core'; import { cloneDeep } from '@ali/lowcode-utils'; function getSettingFieldCollectorKey(parent: SettingEntry, config: FieldConfig) { - let top = parent; + let cur = parent; const path = [config.name]; - while (top !== parent.top) { - if (top instanceof SettingField && top.type !== 'group') { - path.unshift(top.name); + while (cur !== parent.top) { + if (cur instanceof SettingField && cur.type !== 'group') { + path.unshift(cur.name); } - top = top.parent; + cur = cur.parent; } return path.join('.'); } diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index 1622bcfac..148654962 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -1,11 +1,12 @@ import { untracked, computed, obx, engineConfig } from '@ali/lowcode-editor-core'; -import { CompositeValue, isJSExpression, isJSSlot, JSSlot, SlotSchema } from '@ali/lowcode-types'; +import { CompositeValue, FieldConfig, isJSExpression, isJSSlot, JSSlot, SlotSchema } from '@ali/lowcode-types'; import { uniqueId, isPlainObject, hasOwnProperty, compatStage } from '@ali/lowcode-utils'; import { PropStash } from './prop-stash'; import { valueToSource } from './value-to-source'; import { Props } from './props'; import { SlotNode, Node } from '../node'; import { TransformStage } from '../transform-stage'; +import { getFocusedElement } from 'medium-editor'; export const UNSET = Symbol.for('unset'); export type UNSET = typeof UNSET; @@ -14,10 +15,40 @@ export interface IPropParent { delete(prop: Prop): void; readonly props: Props; readonly owner: Node; + readonly path: string[]; } export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot'; +function hasItemsInMetadata(prop: Prop) { + const path = prop.path; + const { configure } = prop.getNode().componentMeta.getMetadata(); + if (!path || path.length === 0) return false; + let props = configure?.props; + while (path.length > 0) { + let name = path.shift(); + let matchedProp = getMatchedProp(props, name!); + if (!matchedProp) return false; + if (path.length === 0) return !!matchedProp?.items; + props = matchedProp.items; + } +} + +function getMatchedProp(props: FieldConfig[] | undefined, name: string): FieldConfig | null { + let found = null; + if (!props) return null; + for (const prop of props) { + if (prop.name === name) { + found = prop; + break; + } else if (prop.type === 'group' && prop.items) { + found = getMatchedProp(prop.items, name); + if (found) return found; + } + } + return found; +} + export class Prop implements IPropParent { readonly isProp = true; @@ -253,6 +284,21 @@ export class Prop implements IPropParent { }; } + this.dispose(); + + // 将父属性设置成一个对象,得同时把子属性都设值,同时清空其他子属性 + if (this._type === 'map' && isPlainObject(val)) { + this.items?.forEach((item) => { + // @ts-ignore + if ([item.key] in val) { + // @ts-ignore + item.setValue(val[item.key]); + } else { + this.clearPropValue(item.key!); + } + }); + } + if (oldValue !== this._value) { editor?.emit('node.innerProp.change', { node: this.owner, @@ -261,7 +307,6 @@ export class Prop implements IPropParent { newValue: this._value, }); } - this.dispose(); } @computed getValue(): CompositeValue { @@ -358,7 +403,13 @@ export class Prop implements IPropParent { @obx.val private _maps: Map | null = null; - @computed private get items(): Prop[] | null { + get path(): string[] { + return (this.parent.path || []).concat(this.key as string); + } + + private get items(): Prop[] | null { + // 不在元数据配置项中的,不产生 items + if (!hasItemsInMetadata(this)) return null; let _items: any; untracked(() => { _items = this._items; diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index 3c40d17fd..028d190a0 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -37,6 +37,8 @@ export class Props implements IPropParent { return maps; } + readonly path = []; + get props(): Props { return this; } diff --git a/packages/editor-skeleton/src/components/object-setter/index.tsx b/packages/editor-skeleton/src/components/object-setter/index.tsx index 1dcb7e25f..ec309ff2c 100644 --- a/packages/editor-skeleton/src/components/object-setter/index.tsx +++ b/packages/editor-skeleton/src/components/object-setter/index.tsx @@ -1,9 +1,9 @@ import { Component, Fragment } from 'react'; import { Icon, Button } from '@alifd/next'; -import { SetterType, FieldConfig } from '@ali/lowcode-types'; +import { SetterType, FieldConfig, CustomView } from '@ali/lowcode-types'; import { createSettingFieldView } from '../settings/settings-pane'; import { PopupContext, PopupPipe } from '../popup'; -import { SettingField } from '@ali/lowcode-designer'; +import { isSettingField, SettingField } from '@ali/lowcode-designer'; import './style.less'; import { Title } from '@ali/lowcode-editor-core'; @@ -165,11 +165,17 @@ class FormSetter extends Component { super(props); const { config, field } = props; const { extraProps } = field; - this.items = (config?.items || []).map((conf) => field.createField({ - ...conf, - setValue: extraProps?.setValue, - })); - + field.items.forEach((item: SettingField | CustomView) => { + if (isSettingField(item)) { + const originalSetValue = item.extraProps.setValue; + item.extraProps.setValue = (...args) => { + // 调用子字段本身的 setValue + originalSetValue?.apply(null, args); + // 调用父字段本身的 setValue + extraProps.setValue?.apply(null, args); + }; + } + }); // TODO: extraConfig for custom fields } @@ -181,7 +187,7 @@ class FormSetter extends Component { const { field } = this.props; return (
- {this.items.map((item, index) => createSettingFieldView(item, field, index))} + {field.items.map((item, index) => createSettingFieldView(item, field, index))}
); } diff --git a/packages/editor-skeleton/src/components/settings/main.ts b/packages/editor-skeleton/src/components/settings/main.ts index 60a20c2bf..66e78bc3d 100644 --- a/packages/editor-skeleton/src/components/settings/main.ts +++ b/packages/editor-skeleton/src/components/settings/main.ts @@ -69,13 +69,13 @@ export class SettingsMain { if (!this.designer) { this.designer = nodes[0].document.designer; } - - let lastSettings = this._settings; - // obx 的一些响应式计算会延迟到下一个时钟周期,导致 prop.parent 获取不到,这里也做一个延迟 - executePendingFn(() => { - lastSettings?.purge(); - }, 2000); - this._settings = this.designer.createSettingEntry(nodes); + // 当节点只有一个时,复用 node 上挂载的 settingEntry,不会产生平行的两个实例,这样在整个系统中对 + // 某个节点操作的 SettingTopEntry 只有一个实例,后续的 getProp() 也会拿到相同的 SettingField 实例 + if (nodes.length === 1) { + this._settings = nodes[0].settingEntry; + } else { + this._settings = this.designer.createSettingEntry(nodes); + } } purge() {