mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-14 21:12:53 +00:00
528 lines
13 KiB
TypeScript
528 lines
13 KiB
TypeScript
import {
|
|
PropConfig,
|
|
PropType,
|
|
Shape,
|
|
OneOf,
|
|
ObjectOf,
|
|
ArrayOf,
|
|
OneOfType,
|
|
} from '../../designer/src/designer/prop-config';
|
|
import { SetterType, FieldConfig, SettingField } from './main';
|
|
import { registerMetadataTransducer } from '../../designer/src/designer/component-meta';
|
|
|
|
export function propConfigToFieldConfig(propConfig: PropConfig): FieldConfig {
|
|
return {
|
|
...propConfig,
|
|
setter: propTypeToSetter(propConfig.propType),
|
|
};
|
|
}
|
|
|
|
export function propTypeToSetter(propType: PropType): SetterType {
|
|
let typeName: string;
|
|
let isRequired: boolean | undefined = false;
|
|
if (typeof propType === 'string') {
|
|
typeName = propType;
|
|
} else {
|
|
typeName = propType.type;
|
|
isRequired = propType.isRequired;
|
|
}
|
|
// TODO: use mixinSetter wrapper
|
|
switch (typeName) {
|
|
case 'string':
|
|
return {
|
|
componentName: 'StringSetter',
|
|
isRequired,
|
|
initialValue: '',
|
|
};
|
|
|
|
case 'number':
|
|
return {
|
|
componentName: 'NumberSetter',
|
|
isRequired,
|
|
initialValue: 0,
|
|
};
|
|
case 'bool':
|
|
return {
|
|
componentName: 'NumberSetter',
|
|
isRequired,
|
|
initialValue: false,
|
|
};
|
|
case 'oneOf':
|
|
const dataSource = ((propType as OneOf).value || []).map((value, index) => {
|
|
const t = typeof value;
|
|
return {
|
|
label: t === 'string' || t === 'number' || t === 'boolean' ? String(value) : `value ${index}`,
|
|
value,
|
|
};
|
|
});
|
|
const componentName = dataSource.length > 4 ? 'SelectSetter' : 'RadioSetter';
|
|
return {
|
|
componentName,
|
|
props: { dataSource },
|
|
isRequired,
|
|
initialValue: dataSource[0] ? dataSource[0].value : null,
|
|
};
|
|
|
|
case 'element':
|
|
case 'node':
|
|
return {
|
|
// slotSetter
|
|
componentName: 'NodeSetter',
|
|
props: {
|
|
mode: typeName,
|
|
},
|
|
isRequired,
|
|
initialValue: {
|
|
type: 'JSSlot',
|
|
value: '',
|
|
},
|
|
};
|
|
case 'shape':
|
|
case 'exact':
|
|
const items = (propType as Shape).value.map(item => propConfigToFieldConfig(item));
|
|
return {
|
|
componentName: 'ObjectSetter',
|
|
props: {
|
|
config: {
|
|
items,
|
|
extraSetter: typeName === 'shape' ? propTypeToSetter('any') : null,
|
|
},
|
|
},
|
|
isRequired,
|
|
initialValue: (field: any) => {
|
|
const data: any = {};
|
|
items.forEach(item => {
|
|
let initial = item.defaultValue;
|
|
if (initial == null && item.setter && typeof item.setter === 'object') {
|
|
initial = (item.setter as any).initialValue;
|
|
}
|
|
data[item.name] = initial ? (typeof initial === 'function' ? initial(field) : initial) : null;
|
|
});
|
|
return data;
|
|
},
|
|
};
|
|
case 'object':
|
|
case 'objectOf':
|
|
return {
|
|
componentName: 'ObjectSetter',
|
|
props: {
|
|
config: {
|
|
extraSetter: propTypeToSetter(typeName === 'objectOf' ? (propType as ObjectOf).value : 'any'),
|
|
},
|
|
},
|
|
isRequired,
|
|
};
|
|
case 'array':
|
|
case 'arrayOf':
|
|
return {
|
|
componentName: 'ArraySetter',
|
|
props: {
|
|
itemSetter: propTypeToSetter(typeName === 'arrayOf' ? (propType as ArrayOf).value : 'any'),
|
|
},
|
|
isRequired,
|
|
initialValue: [],
|
|
};
|
|
case 'func':
|
|
return {
|
|
componentName: 'FunctionSetter',
|
|
isRequired,
|
|
initialValue: {
|
|
type: 'JSFunction',
|
|
value: 'function(){}',
|
|
},
|
|
};
|
|
case 'oneOfType':
|
|
return {
|
|
componentName: 'MixinSetter',
|
|
props: {
|
|
setters: (propType as OneOfType).value.map(item => propTypeToSetter(item)),
|
|
},
|
|
isRequired,
|
|
};
|
|
}
|
|
|
|
return {
|
|
componentName: 'MixinSetter',
|
|
isRequired,
|
|
};
|
|
}
|
|
|
|
const EVENT_RE = /^on[A-Z][\w]*$/;
|
|
|
|
registerMetadataTransducer(metadata => {
|
|
if (metadata.configure) {
|
|
if (Array.isArray(metadata.configure)) {
|
|
return {
|
|
...metadata,
|
|
configure: {
|
|
props: metadata.configure,
|
|
},
|
|
};
|
|
}
|
|
if (metadata.configure.props) {
|
|
return metadata as any;
|
|
}
|
|
}
|
|
if (!metadata.props) {
|
|
return {
|
|
...metadata,
|
|
configure: {
|
|
props: metadata.configure && Array.isArray(metadata.configure) ? metadata.configure : [],
|
|
},
|
|
};
|
|
}
|
|
|
|
const { configure = {} } = metadata;
|
|
const { props = [], component = {}, events = {}, styles = {} } = configure;
|
|
const supportEvents: string[] | null = (events as any).supportEvents ? null : [];
|
|
|
|
metadata.props.forEach(prop => {
|
|
const { name, propType } = prop;
|
|
if (
|
|
name === 'children' &&
|
|
(component.isContainer || propType === 'node' || propType === 'element' || propType === 'any')
|
|
) {
|
|
if (component.isContainer !== false) {
|
|
component.isContainer = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (EVENT_RE.test(name) && (propType === 'func' || propType === 'any')) {
|
|
if (supportEvents) {
|
|
supportEvents.push(name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (name === 'className' && (propType === 'string' || propType === 'any')) {
|
|
if ((styles as any).supportClassName == null) {
|
|
(styles as any).supportClassName = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (name === 'style' && (propType === 'object' || propType === 'any')) {
|
|
if ((styles as any).supportInlineStyle == null) {
|
|
(styles as any).supportInlineStyle = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
props.push(propConfigToFieldConfig(prop));
|
|
});
|
|
|
|
return {
|
|
...metadata,
|
|
configure: {
|
|
...configure,
|
|
props,
|
|
events,
|
|
styles,
|
|
component,
|
|
},
|
|
};
|
|
});
|
|
|
|
registerMetadataTransducer(metadata => {
|
|
const { configure = {}, componentName } = metadata;
|
|
const { component = {} } = configure as any;
|
|
if (!component.nestingRule) {
|
|
let m;
|
|
// uri match xx.Group set subcontrolling: true, childWhiteList
|
|
if ((m = /^(.+)\.Group$/.exec(componentName))) {
|
|
// component.subControlling = true;
|
|
if (!component.nestingRule) {
|
|
component.nestingRule = {
|
|
childWhitelist: [`${m[1]}`],
|
|
};
|
|
}
|
|
}
|
|
// uri match xx.Node set selfControlled: false, parentWhiteList
|
|
else if ((m = /^(.+)\.Node$/.exec(componentName))) {
|
|
// component.selfControlled = false;
|
|
component.nestingRule = {
|
|
parentWhitelist: [`${m[1]}`, componentName],
|
|
};
|
|
}
|
|
// uri match .Item .Node .Option set parentWhiteList
|
|
else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) {
|
|
component.nestingRule = {
|
|
parentWhitelist: [`${m[1]}`],
|
|
};
|
|
}
|
|
}
|
|
if (component.isModal == null && /Dialog/.test(componentName)) {
|
|
component.isModal = true;
|
|
}
|
|
return {
|
|
...metadata,
|
|
configure: {
|
|
...configure,
|
|
component,
|
|
},
|
|
};
|
|
});
|
|
|
|
registerMetadataTransducer(metadata => {
|
|
const { componentName, configure = {} } = metadata;
|
|
if (componentName === 'Leaf') {
|
|
return {
|
|
...metadata,
|
|
configure: {
|
|
...configure,
|
|
combined: [
|
|
{
|
|
name: 'children',
|
|
title: '内容设置',
|
|
setter: {
|
|
componentName: 'MixinSetter',
|
|
props: {
|
|
setters: [
|
|
{
|
|
componentName: 'StringSetter',
|
|
props: {
|
|
// todo:
|
|
multiline: true,
|
|
},
|
|
initialValue: '',
|
|
},
|
|
{
|
|
componentName: 'ExpressionSetter',
|
|
initialValue: {
|
|
type: 'JSExpression',
|
|
value: '',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
const { props, events, styles } = configure as any;
|
|
let eventsDefinition: any;
|
|
let isRoot: boolean = false;
|
|
if (componentName === 'Page' || componentName === 'Component') {
|
|
isRoot = true;
|
|
// 平台配置的,一般只有根节点才会配置
|
|
eventsDefinition = [{
|
|
type: 'lifeCycleEvent',
|
|
title: '生命周期',
|
|
list: [
|
|
{
|
|
description: '初始化时',
|
|
name: 'constructor',
|
|
},
|
|
{
|
|
description: '装载后',
|
|
name: 'componentDidMount',
|
|
},
|
|
{
|
|
description: '更新时',
|
|
name: 'componentDidMount',
|
|
},
|
|
{
|
|
description: '卸载时',
|
|
name: 'componentWillUnmount',
|
|
},
|
|
]
|
|
}];
|
|
} else {
|
|
eventsDefinition = [{
|
|
type: 'events',
|
|
title: '事件',
|
|
list: (events?.supportEvents || []).map((event: any) => {
|
|
return typeof event === 'string'
|
|
? {
|
|
name: event,
|
|
}
|
|
: event;
|
|
}),
|
|
|
|
// mock data
|
|
list:[
|
|
{
|
|
name:'onClick',
|
|
description:'点击回调'
|
|
},
|
|
{
|
|
name:'onChange',
|
|
description:'变更回调'
|
|
},
|
|
{
|
|
name:'onSubmit'
|
|
}
|
|
]
|
|
}];
|
|
}
|
|
// 通用设置
|
|
const propsGroup = props || [];
|
|
propsGroup.push({
|
|
name: '#generals',
|
|
title: '通用',
|
|
items: [
|
|
{
|
|
name: 'id',
|
|
title: 'ID',
|
|
setter: 'StringSetter',
|
|
},
|
|
{
|
|
name: 'key',
|
|
title: 'Key',
|
|
// todo: use Mixin
|
|
setter: 'StringSetter',
|
|
},
|
|
{
|
|
name: 'ref',
|
|
title: 'Ref',
|
|
setter: 'StringSetter',
|
|
},
|
|
{
|
|
name: '!more',
|
|
title: '更多',
|
|
setter: 'PropertiesSetter',
|
|
},
|
|
],
|
|
});
|
|
const combined = [
|
|
{
|
|
title: '属性',
|
|
name: '#props',
|
|
items: propsGroup,
|
|
},
|
|
];
|
|
const stylesGroup = [];
|
|
if (styles?.supportClassName) {
|
|
stylesGroup.push({
|
|
name: 'className',
|
|
title: '类名绑定',
|
|
setter: 'ClassNameSetter',
|
|
});
|
|
}
|
|
if (styles?.supportInlineStyle) {
|
|
stylesGroup.push({
|
|
name: 'style',
|
|
title: '行内样式',
|
|
setter: 'StyleSetter',
|
|
});
|
|
}
|
|
if (stylesGroup.length > 0) {
|
|
combined.push({
|
|
name: '#styles',
|
|
title: '样式',
|
|
items: stylesGroup,
|
|
});
|
|
}
|
|
|
|
if (eventsDefinition) {
|
|
combined.push({
|
|
name: '#events',
|
|
title: '事件',
|
|
items: [
|
|
{
|
|
name: '!events',
|
|
title: '事件设置',
|
|
setter: {
|
|
componentName: 'EventsSetter',
|
|
props: {
|
|
definition: eventsDefinition,
|
|
},
|
|
},
|
|
|
|
getValue(field: SettingField) {
|
|
let data = field.getPropValue('eventDataList');
|
|
return data;
|
|
},
|
|
setValue(field: SettingField, eventDataList: any[]) {
|
|
field.setPropValue('eventDataList', eventDataList);
|
|
}
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
if (isRoot) {
|
|
// todo...
|
|
combined.push({
|
|
name: '#advanced',
|
|
title: '高级',
|
|
items: [],
|
|
});
|
|
} else {
|
|
combined.push({
|
|
name: '#advanced',
|
|
title: '高级',
|
|
items: [
|
|
{
|
|
name: '__condition',
|
|
title: '条件显示',
|
|
setter: 'ExpressionSetter',
|
|
},
|
|
{
|
|
name: '#loop',
|
|
title: '循环',
|
|
items: [
|
|
{
|
|
name: '__loop',
|
|
title: '循环数据',
|
|
setter: {
|
|
componentName: 'MixinSetter',
|
|
props: {
|
|
setters: [
|
|
{
|
|
componentName: 'JSONSetter',
|
|
props: {
|
|
mode: 'popup',
|
|
placeholder: '编辑数据',
|
|
},
|
|
},
|
|
{
|
|
componentName: 'ExpressionSetter',
|
|
props: {
|
|
placeholder: '绑定数据',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: '__loopArgs.0',
|
|
title: '迭代变量名',
|
|
setter: {
|
|
componentName: 'StringSetter',
|
|
placeholder: '默认为 item',
|
|
},
|
|
},
|
|
{
|
|
name: '__loopArgs.1',
|
|
title: '索引变量名',
|
|
setter: {
|
|
componentName: 'StringSetter',
|
|
placeholder: '默认为 index',
|
|
},
|
|
},
|
|
{
|
|
name: 'key',
|
|
title: 'Key',
|
|
setter: 'ExpressionSetter',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
return {
|
|
...metadata,
|
|
configure: {
|
|
...configure,
|
|
combined,
|
|
},
|
|
};
|
|
});
|