mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-21 08:28:16 +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';
|
import { cloneDeep } from '@ali/lowcode-utils';
|
||||||
|
|
||||||
function getSettingFieldCollectorKey(parent: SettingEntry, config: FieldConfig) {
|
function getSettingFieldCollectorKey(parent: SettingEntry, config: FieldConfig) {
|
||||||
let top = parent;
|
let cur = parent;
|
||||||
const path = [config.name];
|
const path = [config.name];
|
||||||
while (top !== parent.top) {
|
while (cur !== parent.top) {
|
||||||
if (top instanceof SettingField && top.type !== 'group') {
|
if (cur instanceof SettingField && cur.type !== 'group') {
|
||||||
path.unshift(top.name);
|
path.unshift(cur.name);
|
||||||
}
|
}
|
||||||
top = top.parent;
|
cur = cur.parent;
|
||||||
}
|
}
|
||||||
return path.join('.');
|
return path.join('.');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import { untracked, computed, obx, engineConfig } from '@ali/lowcode-editor-core';
|
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 { uniqueId, isPlainObject, hasOwnProperty, compatStage } from '@ali/lowcode-utils';
|
||||||
import { PropStash } from './prop-stash';
|
import { PropStash } from './prop-stash';
|
||||||
import { valueToSource } from './value-to-source';
|
import { valueToSource } from './value-to-source';
|
||||||
import { Props } from './props';
|
import { Props } from './props';
|
||||||
import { SlotNode, Node } from '../node';
|
import { SlotNode, Node } from '../node';
|
||||||
import { TransformStage } from '../transform-stage';
|
import { TransformStage } from '../transform-stage';
|
||||||
|
import { getFocusedElement } from 'medium-editor';
|
||||||
|
|
||||||
export const UNSET = Symbol.for('unset');
|
export const UNSET = Symbol.for('unset');
|
||||||
export type UNSET = typeof UNSET;
|
export type UNSET = typeof UNSET;
|
||||||
@ -14,10 +15,40 @@ export interface IPropParent {
|
|||||||
delete(prop: Prop): void;
|
delete(prop: Prop): void;
|
||||||
readonly props: Props;
|
readonly props: Props;
|
||||||
readonly owner: Node;
|
readonly owner: Node;
|
||||||
|
readonly path: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot';
|
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 {
|
export class Prop implements IPropParent {
|
||||||
readonly isProp = true;
|
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) {
|
if (oldValue !== this._value) {
|
||||||
editor?.emit('node.innerProp.change', {
|
editor?.emit('node.innerProp.change', {
|
||||||
node: this.owner,
|
node: this.owner,
|
||||||
@ -261,7 +307,6 @@ export class Prop implements IPropParent {
|
|||||||
newValue: this._value,
|
newValue: this._value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed getValue(): CompositeValue {
|
@computed getValue(): CompositeValue {
|
||||||
@ -358,7 +403,13 @@ export class Prop implements IPropParent {
|
|||||||
|
|
||||||
@obx.val private _maps: Map<string | number, Prop> | null = null;
|
@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;
|
let _items: any;
|
||||||
untracked(() => {
|
untracked(() => {
|
||||||
_items = this._items;
|
_items = this._items;
|
||||||
|
|||||||
@ -37,6 +37,8 @@ export class Props implements IPropParent {
|
|||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly path = [];
|
||||||
|
|
||||||
get props(): Props {
|
get props(): Props {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { Component, Fragment } from 'react';
|
import { Component, Fragment } from 'react';
|
||||||
import { Icon, Button } from '@alifd/next';
|
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 { createSettingFieldView } from '../settings/settings-pane';
|
||||||
import { PopupContext, PopupPipe } from '../popup';
|
import { PopupContext, PopupPipe } from '../popup';
|
||||||
import { SettingField } from '@ali/lowcode-designer';
|
import { isSettingField, SettingField } from '@ali/lowcode-designer';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
import { Title } from '@ali/lowcode-editor-core';
|
import { Title } from '@ali/lowcode-editor-core';
|
||||||
|
|
||||||
@ -165,11 +165,17 @@ class FormSetter extends Component<FormSetterProps> {
|
|||||||
super(props);
|
super(props);
|
||||||
const { config, field } = props;
|
const { config, field } = props;
|
||||||
const { extraProps } = field;
|
const { extraProps } = field;
|
||||||
this.items = (config?.items || []).map((conf) => field.createField({
|
field.items.forEach((item: SettingField | CustomView) => {
|
||||||
...conf,
|
if (isSettingField(item)) {
|
||||||
setValue: extraProps?.setValue,
|
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
|
// TODO: extraConfig for custom fields
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +187,7 @@ class FormSetter extends Component<FormSetterProps> {
|
|||||||
const { field } = this.props;
|
const { field } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="lc-setter-object lc-block-setter">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,13 +69,13 @@ export class SettingsMain {
|
|||||||
if (!this.designer) {
|
if (!this.designer) {
|
||||||
this.designer = nodes[0].document.designer;
|
this.designer = nodes[0].document.designer;
|
||||||
}
|
}
|
||||||
|
// 当节点只有一个时,复用 node 上挂载的 settingEntry,不会产生平行的两个实例,这样在整个系统中对
|
||||||
let lastSettings = this._settings;
|
// 某个节点操作的 SettingTopEntry 只有一个实例,后续的 getProp() 也会拿到相同的 SettingField 实例
|
||||||
// obx 的一些响应式计算会延迟到下一个时钟周期,导致 prop.parent 获取不到,这里也做一个延迟
|
if (nodes.length === 1) {
|
||||||
executePendingFn(() => {
|
this._settings = nodes[0].settingEntry;
|
||||||
lastSettings?.purge();
|
} else {
|
||||||
}, 2000);
|
this._settings = this.designer.createSettingEntry(nodes);
|
||||||
this._settings = this.designer.createSettingEntry(nodes);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
purge() {
|
purge() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user