mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-15 05:36:39 +00:00
can change props
This commit is contained in:
parent
9c52978702
commit
38f7c6d5cc
@ -147,18 +147,17 @@ export class ComponentType {
|
||||
name: '#props',
|
||||
title: "属性",
|
||||
items: [{
|
||||
name: 'title',
|
||||
title: '标题',
|
||||
name: 'label',
|
||||
title: '标签',
|
||||
setter: 'StringSetter'
|
||||
}, {
|
||||
name: 'description',
|
||||
title: '描述',
|
||||
setter: {
|
||||
componentName: 'StringSetter',
|
||||
props: {
|
||||
multiline: true,
|
||||
}
|
||||
}
|
||||
name: 'name',
|
||||
title: '名称',
|
||||
setter: 'StringSetter'
|
||||
}, {
|
||||
name: 'size',
|
||||
title: '大小',
|
||||
setter: 'StringSetter'
|
||||
}]
|
||||
}, {
|
||||
name: '#styles',
|
||||
@ -188,12 +187,6 @@ export class ComponentType {
|
||||
name: '#data',
|
||||
title: "数据",
|
||||
items: []
|
||||
}, {
|
||||
name: '#a',
|
||||
title: "数据1",
|
||||
}, {
|
||||
name: '#b',
|
||||
title: "数据2",
|
||||
}];
|
||||
}
|
||||
|
||||
@ -201,7 +194,7 @@ export class ComponentType {
|
||||
private childWhitelist?: string[] | null;
|
||||
|
||||
get title() {
|
||||
return this._spec.title;
|
||||
return this._spec.title || this.componentName;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
@ -261,7 +254,6 @@ export class ComponentType {
|
||||
return this._spec;
|
||||
}
|
||||
|
||||
|
||||
checkNestingUp(my: Node | NodeData, parent: NodeParent) {
|
||||
if (this.parentWhitelist) {
|
||||
return this.parentWhitelist.includes(parent.componentName);
|
||||
|
||||
@ -228,15 +228,25 @@ export default class Designer {
|
||||
}
|
||||
|
||||
@obx.val private _componentTypesMap = new Map<string, ComponentType>();
|
||||
private _lostComponentTypesMap = new Map<string, ComponentType>();
|
||||
|
||||
private buildComponentTypesMap(specs: ComponentDescription[]) {
|
||||
specs.forEach(spec => {
|
||||
const key = spec.componentName;
|
||||
const had = this._componentTypesMap.get(key);
|
||||
if (had) {
|
||||
had.spec = spec;
|
||||
let cType = this._componentTypesMap.get(key);
|
||||
if (cType) {
|
||||
cType.spec = spec;
|
||||
} else {
|
||||
this._componentTypesMap.set(key, new ComponentType(spec));
|
||||
cType = this._lostComponentTypesMap.get(key);
|
||||
|
||||
if (cType) {
|
||||
cType.spec = spec;
|
||||
this._lostComponentTypesMap.delete(key);
|
||||
} else {
|
||||
cType = new ComponentType(spec);
|
||||
}
|
||||
|
||||
this._componentTypesMap.set(key, cType);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -246,9 +256,17 @@ export default class Designer {
|
||||
return this._componentTypesMap.get(componentName)!;
|
||||
}
|
||||
|
||||
return new ComponentType({
|
||||
if (this._lostComponentTypesMap.has(componentName)) {
|
||||
return this._lostComponentTypesMap.get(componentName)!;
|
||||
}
|
||||
|
||||
const cType = new ComponentType({
|
||||
componentName,
|
||||
});
|
||||
|
||||
this._lostComponentTypesMap.set(componentName, cType);
|
||||
|
||||
return cType;
|
||||
}
|
||||
|
||||
get componentsMap(): { [key: string]: ComponentDescription } {
|
||||
|
||||
@ -98,7 +98,7 @@ export default class Node {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return this.componentName;
|
||||
return this.componentType.title;
|
||||
}
|
||||
|
||||
get isSlotRoot(): boolean {
|
||||
@ -173,6 +173,17 @@ export default class Node {
|
||||
this.document.selection.select(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 悬停高亮
|
||||
*/
|
||||
hover(flag: boolean = true) {
|
||||
if (flag) {
|
||||
this.document.designer.hovering.hover(this);
|
||||
} else {
|
||||
this.document.designer.hovering.unhover(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点组件类
|
||||
*/
|
||||
|
||||
@ -29,7 +29,6 @@ export default class PropStash implements IPropParent {
|
||||
}
|
||||
}
|
||||
if (pending.length > 0) {
|
||||
debugger;
|
||||
untracked(() => {
|
||||
for (const item of pending) {
|
||||
write(item);
|
||||
|
||||
@ -77,6 +77,22 @@ export default class Prop implements IPropParent {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得表达式值
|
||||
*/
|
||||
@computed get code() {
|
||||
if (isJSExpression(this.value)) {
|
||||
return this.value.value;
|
||||
}
|
||||
if (this.type === 'slot') {
|
||||
return JSON.stringify(this._slotNode!.export(false));
|
||||
}
|
||||
return JSON.stringify(this.value);
|
||||
}
|
||||
set code(val) {
|
||||
|
||||
}
|
||||
|
||||
@computed getAsString(): string {
|
||||
if (this.type === 'literal') {
|
||||
return this._value ? String(this._value) : '';
|
||||
@ -167,6 +183,17 @@ export default class Prop implements IPropParent {
|
||||
return this._type === 'unset';
|
||||
}
|
||||
|
||||
isEqual(otherProp: Prop | null): boolean {
|
||||
if (!otherProp) {
|
||||
return this.isUnset();
|
||||
}
|
||||
if (otherProp.type !== this.type) {
|
||||
return false;
|
||||
}
|
||||
// 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot'
|
||||
return this.code === otherProp.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 值是否是带类型的 JS
|
||||
* 比如 JSExpresion | JSSlot 等值
|
||||
@ -278,6 +305,7 @@ export default class Prop implements IPropParent {
|
||||
get(path: string): Prop;
|
||||
get(path: string, stash = true) {
|
||||
const type = this._type;
|
||||
// todo: support list get
|
||||
if (type !== 'map' && type !== 'unset' && !stash) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -24,6 +24,12 @@ export default class Hovering {
|
||||
this._current = node;
|
||||
}
|
||||
|
||||
unhover(node: Node) {
|
||||
if (this._current === node) {
|
||||
this._current = null;
|
||||
}
|
||||
}
|
||||
|
||||
leave(document: DocumentModel) {
|
||||
if (this.current && this.current.document === document) {
|
||||
this._current = null;
|
||||
|
||||
@ -4,6 +4,7 @@ import { SettingsMain, SettingField, isSettingField } from './main';
|
||||
import './style.less';
|
||||
import Title from './title';
|
||||
import SettingsTab, { registerSetter, createSetterContent, getSetter, createSettingFieldView } from './settings-tab';
|
||||
import Node from '../../designer/src/designer/document/node/node';
|
||||
|
||||
export default class SettingsPane extends Component {
|
||||
private main: SettingsMain;
|
||||
@ -28,20 +29,34 @@ export default class SettingsPane extends Component {
|
||||
if (this.main.isMulti) {
|
||||
return (
|
||||
<div className="lc-settings-navigator">
|
||||
{this.main.componentType ? this.main.componentType.icon : <Icon type="ellipsis" />}
|
||||
<span>多个节点</span>
|
||||
{this.main.componentType!.icon || <Icon type="ellipsis" size="small" />}
|
||||
<span>
|
||||
{this.main.componentType!.title} x {this.main.nodes.length}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let node: Node | null = this.main.nodes[0]!;
|
||||
const items = [];
|
||||
let l = 4;
|
||||
while (l-- > 0 && node) {
|
||||
const props =
|
||||
l === 3
|
||||
? {}
|
||||
: {
|
||||
onMouseOver: hoverNode.bind(null, node, true),
|
||||
onMouseOut: hoverNode.bind(null, node, false),
|
||||
onClick: selectNode.bind(null, node),
|
||||
};
|
||||
items.unshift(<Breadcrumb.Item {...props}>{node.title}</Breadcrumb.Item>);
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lc-settings-navigator">
|
||||
{this.main.componentType ? this.main.componentType.icon : <Icon type="ellipsis" />}
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>页面</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>容器</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>输入框</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
{this.main.componentType!.icon || <Icon type="ellipsis" size="small" />}
|
||||
<Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -49,12 +64,24 @@ export default class SettingsPane extends Component {
|
||||
render() {
|
||||
if (this.main.isNone) {
|
||||
// 未选中节点,提示选中 或者 显示根节点设置
|
||||
return <div className="lc-settings-pane">请选中节点</div>;
|
||||
return (
|
||||
<div className="lc-settings-pane">
|
||||
<div className="lc-settings-notice">
|
||||
<p>请在左侧画布选中节点</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.main.isSame) {
|
||||
// todo: future support 获取设置项交集编辑
|
||||
return <div className="lc-settings-pane">选中同一类型节点编辑</div>;
|
||||
return (
|
||||
<div className="lc-settings-pane">
|
||||
<div className="lc-settings-notice">
|
||||
<p>请选中同一类型节点编辑</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { items } = this.main;
|
||||
@ -79,7 +106,7 @@ export default class SettingsPane extends Component {
|
||||
>
|
||||
{(items as SettingField[]).map(field => (
|
||||
<Tab.Item className="lc-settings-tab-item" title={<Title title={field.title} />} key={field.name}>
|
||||
<SettingsTab target={field} />
|
||||
<SettingsTab target={field} key={field.id} />
|
||||
</Tab.Item>
|
||||
))}
|
||||
</Tab>
|
||||
@ -88,4 +115,11 @@ export default class SettingsPane extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
function hoverNode(node: Node, flag: boolean) {
|
||||
node.hover(flag);
|
||||
}
|
||||
function selectNode(node: Node) {
|
||||
node.select();
|
||||
}
|
||||
|
||||
export { registerSetter, createSetterContent, getSetter, createSettingFieldView };
|
||||
|
||||
@ -43,24 +43,28 @@ export interface SettingTarget {
|
||||
|
||||
readonly designer?: Designer;
|
||||
|
||||
readonly path: string[];
|
||||
|
||||
/**
|
||||
* 响应式自动运行
|
||||
*/
|
||||
onEffect(action: () => void): () => void;
|
||||
|
||||
// 获取属性值
|
||||
getPropValue(propName: string): any;
|
||||
|
||||
// 设置属性值
|
||||
setPropValue(path: string, value: any): void;
|
||||
|
||||
/*
|
||||
// 所有属性值数据
|
||||
readonly props: object;
|
||||
// 获取属性值
|
||||
getPropValue(propName: string): any;
|
||||
// 设置多个属性值,替换原有值
|
||||
setProps(data: object): void;
|
||||
// 设置多个属性值,和原有值合并
|
||||
mergeProps(data: object): void;
|
||||
// 绑定属性值发生变化时
|
||||
onPropsChange(fn: () => void): () => void;
|
||||
// 设置属性值
|
||||
setPropValue(path: string, value: any) {}
|
||||
*/
|
||||
}
|
||||
|
||||
@ -149,8 +153,9 @@ export class SettingField implements SettingTarget {
|
||||
readonly nodes: Node[];
|
||||
readonly componentType: ComponentType | null;
|
||||
readonly designer: Designer;
|
||||
readonly path: string[];
|
||||
|
||||
constructor(readonly parent: SettingTarget, private config: FieldConfig) {
|
||||
constructor(readonly parent: SettingTarget, config: FieldConfig) {
|
||||
const { type, title, name, items, setter, extraProps, ...rest } = config;
|
||||
|
||||
if (type == null) {
|
||||
@ -184,6 +189,10 @@ export class SettingField implements SettingTarget {
|
||||
this.isOne = parent.isOne;
|
||||
this.isNone = parent.isNone;
|
||||
this.designer = parent.designer!;
|
||||
this.path = parent.path.slice();
|
||||
if (this.type === 'field') {
|
||||
this.path.push(this.name);
|
||||
}
|
||||
|
||||
// initial items
|
||||
if (this.type === 'group' && items) {
|
||||
@ -210,36 +219,44 @@ export class SettingField implements SettingTarget {
|
||||
this._items = [];
|
||||
}
|
||||
|
||||
get isSameValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
get prop(): SettingTargetProp | void {
|
||||
if (this.type === 'field') {
|
||||
// ====== 当前属性读写 =====
|
||||
|
||||
}
|
||||
return;
|
||||
get isSameValue(): boolean {
|
||||
// todo:
|
||||
return true;
|
||||
}
|
||||
|
||||
getValue(): any {
|
||||
return null;
|
||||
return this.parent.getPropValue(this.name);
|
||||
}
|
||||
|
||||
setValue(val: any) {
|
||||
this.parent.setPropValue(this.name, val);
|
||||
}
|
||||
|
||||
// 设置属性值
|
||||
setPropValue(propName: string, value: any) {
|
||||
const path = this.type === 'field' ? `${this.name}.${propName}` : propName;
|
||||
this.parent.setPropValue(path, value);
|
||||
}
|
||||
|
||||
// 获取属性值
|
||||
getPropValue(propName: string): any {
|
||||
const path = this.type === 'field' ? `${this.name}.${propName}` : propName;
|
||||
return this.parent.getPropValue(path);
|
||||
}
|
||||
|
||||
// 添加
|
||||
// addItem(config: FieldConfig): SettingField {}
|
||||
// 删除
|
||||
deleteItem() {}
|
||||
// deleteItem() {}
|
||||
// 移动
|
||||
insertItem(item: SettingField, index?: number) {}
|
||||
remove() {}
|
||||
// insertItem(item: SettingField, index?: number) {}
|
||||
// remove() {}
|
||||
|
||||
purge() {
|
||||
this.disposeItems();
|
||||
@ -250,8 +267,6 @@ export function isSettingField(obj: any): obj is SettingField {
|
||||
return obj && obj.isSettingField;
|
||||
}
|
||||
|
||||
export class SettingTargetProp {}
|
||||
|
||||
export class SettingsMain implements SettingTarget {
|
||||
private emitter = new EventEmitter();
|
||||
|
||||
@ -260,6 +275,7 @@ export class SettingsMain implements SettingTarget {
|
||||
private _sessionId = '';
|
||||
private _componentType: ComponentType | null = null;
|
||||
private _isSame: boolean = true;
|
||||
readonly path = [];
|
||||
|
||||
get nodes(): Node[] {
|
||||
return this._nodes;
|
||||
@ -345,6 +361,47 @@ export class SettingsMain implements SettingTarget {
|
||||
return this.onNodesChange(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性值
|
||||
*/
|
||||
getPropValue(propName: string): any {
|
||||
if (!this.isSame) {
|
||||
return null;
|
||||
}
|
||||
const first = this.nodes[0].getProp(propName)!;
|
||||
let l = this.nodes.length;
|
||||
while (l-- > 1) {
|
||||
const next = this.nodes[l].getProp(propName, false);
|
||||
if (!first.isEqual(next)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return first.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性值
|
||||
*/
|
||||
setPropValue(propName: string, value: any) {
|
||||
this.nodes.forEach(node => {
|
||||
node.setPropValue(propName, value);
|
||||
});
|
||||
}
|
||||
|
||||
// 设置多个属性值,替换原有值
|
||||
setProps(data: object) {
|
||||
this.nodes.forEach(node => {
|
||||
node.setProps(data as any);
|
||||
});
|
||||
}
|
||||
|
||||
// 设置多个属性值,和原有值合并
|
||||
mergeProps(data: object) {
|
||||
this.nodes.forEach(node => {
|
||||
node.mergeProps(data as any);
|
||||
});
|
||||
}
|
||||
|
||||
private setup(nodes: Node[]) {
|
||||
this._nodes = nodes;
|
||||
|
||||
@ -394,6 +451,9 @@ export class SettingsMain implements SettingTarget {
|
||||
if (theSame) {
|
||||
this._isSame = true;
|
||||
this._componentType = type;
|
||||
} else {
|
||||
this._isSame = false;
|
||||
this._componentType = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
||||
|
||||
render() {
|
||||
const { field } = this.props;
|
||||
const { setter, title, extraProps, isSameValue } = field;
|
||||
const { setter, title, extraProps } = field;
|
||||
const { defaultValue } = extraProps;
|
||||
const { visible, value } = this.state;
|
||||
// reaction point
|
||||
@ -77,9 +77,11 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
||||
if (defaultValue != null && !('defaultValue' in props)) {
|
||||
props.defaultValue = defaultValue;
|
||||
}
|
||||
/*
|
||||
if (!('placeholder' in props) && !isSameValue) {
|
||||
props.placeholder = '多种值';
|
||||
}
|
||||
*/
|
||||
|
||||
// todo: error handling
|
||||
|
||||
|
||||
@ -34,12 +34,38 @@
|
||||
.lc-settings-pane {
|
||||
position: relative;
|
||||
|
||||
.lc-settings-notice {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica,Arial,sans-serif;
|
||||
color: var(--color-text ,rgba(0,0,0,.6));
|
||||
margin: 50px 15px 0;
|
||||
overflow: hidden;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.lc-settings-navigator {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 5px;
|
||||
border-bottom: 1px solid var(--color-line-normal);
|
||||
.lc-settings-node-breadcrumb {
|
||||
margin-left: 5px;
|
||||
.next-breadcrumb {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
height: 24px;
|
||||
}
|
||||
.next-breadcrumb-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
&:not(:last-child):hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lc-settings-body {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user