mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 17:48:13 +00:00
refactor(props): 将底层 prop 的层级调整成跟元数据描述一致, 支持通过 API 方式设置 prop 分支节点
This commit is contained in:
parent
0cdd1060b7
commit
6176608ac7
@ -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('.');
|
||||
}
|
||||
|
||||
@ -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<string | number, Prop> | 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;
|
||||
|
||||
@ -37,6 +37,8 @@ export class Props implements IPropParent {
|
||||
return maps;
|
||||
}
|
||||
|
||||
readonly path = [];
|
||||
|
||||
get props(): Props {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -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<FormSetterProps> {
|
||||
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<FormSetterProps> {
|
||||
const { field } = this.props;
|
||||
return (
|
||||
<div className="lc-setter-object lc-block-setter">
|
||||
{this.items.map((item, index) => createSettingFieldView(item, field, index))}
|
||||
{field.items.map((item, index) => createSettingFieldView(item, field, index))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user