merge feat-optimize-4

Change-Id: I8974063a944df31cfcd401f416b69859fa53c04d
This commit is contained in:
yangwei9012 2022-07-05 23:06:46 +08:00
parent 29bd6b5e91
commit ec4170017f
79 changed files with 6255 additions and 2357 deletions

View File

@ -4,9 +4,12 @@
import flatten from 'lodash/flatten';
import {getEventControlConfig} from '../util';
import {getSchemaTpl, isObject} from 'amis-editor-core';
import type {BaseEventContext} from 'amis-editor-core';
import {SchemaObject} from 'amis/lib/Schema';
import {
getSchemaTpl,
isObject,
BaseEventContext,
tipedLabel
} from 'amis-editor-core';
// 默认动作
export const BUTTON_DEFAULT_ACTION = {
@ -370,25 +373,6 @@ export const formItemControl: (
];
};
export function tipedLabel(
body: string | Array<SchemaObject>,
tip: string,
style?: React.CSSProperties
) {
return {
type: 'tooltip-wrapper',
tooltip: tip,
tooltipTheme: 'dark',
placement: 'top',
tooltipStyle: {
fontSize: '12px',
...(style || {})
},
className: 'ae-formItemControl-label-tip',
body
};
}
/**
*
*/
@ -400,7 +384,7 @@ export function remarkTpl(config: {
return {
type: 'ae-switch-more',
formType: 'dialog',
className:'ae-switch-more-flex',
className: 'ae-switch-more-flex',
label: config.labelRemark
? tipedLabel(config.label, config.labelRemark)
: config.label,

View File

@ -7,7 +7,7 @@ interface InputComponentNameProps extends RendererProps {
}
export function InputComponentName(props: InputComponentNameProps) {
const {value, onChange, render, name, node} = props;
const {value, onChange, render, name, node, placeholder} = props;
const [options, setOptions] = useState<Array<any>>([]);
useEffect(() => {
@ -37,7 +37,8 @@ export function InputComponentName(props: InputComponentNameProps) {
'inner',
{
type: 'input-text',
name
name,
placeholder
},
{
value,

View File

@ -74,6 +74,7 @@ import './plugin/ButtonGroup';
import './plugin/ButtonToolbar';
import './plugin/Breadcrumb';
import './plugin/Card';
import './plugin/Card2';
import './plugin/Cards';
import './plugin/Carousel';
import './plugin/Chart';
@ -119,6 +120,8 @@ import './plugin/Steps';
import './plugin/Sparkline';
import './plugin/Submit';
import './plugin/Table';
import './plugin/Table-v2';
import './plugin/TableCell-v2';
import './plugin/Tabs';
import './plugin/Tasks';
import './plugin/Time';
@ -131,6 +134,8 @@ import './plugin/TooltipWrapper';
import './plugin/TableView';
import './plugin/CodeView';
import './plugin/WebComponent';
import './plugin/CRUD2';
import './plugin/ColumnToggler';
import {GridPlugin} from './plugin/Grid';

View File

@ -78,7 +78,7 @@ export class AlertPlugin extends BasePlugin {
name: 'showCloseButton'
}),
{
type: 'ae-Switch-More',
type: 'ae-switch-more',
mode: 'normal',
name: 'showIcon',
label: '图标',

View File

@ -4,7 +4,7 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin} from 'amis-editor-core';
import {getSchemaTpl, defaultValue} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
const DefaultSize = 40;
const DefaultBorderRadius = 20;
@ -173,7 +173,7 @@ export class AvatarPlugin extends BasePlugin {
visibleOn: 'data.showtype === "text"'
}
]
},
}
// TODO 临时关闭,目前角标功能还没完善,待完善后再开启
// getSchemaTpl('badge')
]

View File

@ -4,10 +4,11 @@ import {
BasePlugin,
BasicRendererInfo,
PluginInterface,
RendererInfoResolveEventContext
RendererInfoResolveEventContext,
tipedLabel
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {BUTTON_DEFAULT_ACTION, tipedLabel} from '../component/BaseControl';
import {BUTTON_DEFAULT_ACTION} from '../component/BaseControl';
import {getEventControlConfig} from '../util';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {SchemaObject} from 'amis/lib/Schema';

View File

@ -1,12 +1,13 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {
BUTTON_DEFAULT_ACTION,
formItemControl,
tipedLabel
} from '../component/BaseControl';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {RegionWrapper as Region} from 'amis-editor-core';
BasePlugin,
RegionConfig,
BaseEventContext,
tipedLabel,
defaultValue,
getSchemaTpl,
registerEditorPlugin
} from 'amis-editor-core';
import {BUTTON_DEFAULT_ACTION} from '../component/BaseControl';
export class ButtonGroupPlugin extends BasePlugin {
// 关联渲染器名字

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,13 @@ import {registerEditorPlugin} from 'amis-editor-core';
import {
BaseEventContext,
BasePlugin,
BasicRendererInfo,
InsertEventContext,
PluginEvent,
PluginInterface,
RegionConfig,
RendererInfo,
RendererInfoResolveEventContext,
VRendererConfig
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
@ -204,6 +207,8 @@ export class CardPlugin extends BasePlugin {
}*/
fieldWrapperResolve = (dom: HTMLElement) => dom;
overrides = {
renderFeild: function (
this: any,

View File

@ -0,0 +1,104 @@
import {
BaseEventContext,
BasePlugin,
RegionConfig,
defaultValue,
getSchemaTpl,
tipedLabel,
registerEditorPlugin
} from 'amis-editor-core';
export class Card2Plugin extends BasePlugin {
// 关联渲染器名字
rendererName = 'card2';
$schema = '/schemas/Card2Schema.json';
// 组件名称
name = '卡片';
isBaseComponent = true;
description = '展示单个卡片。';
tags = ['展示'];
icon = '';
scaffold = {
type: 'card2',
body: '内容'
};
previewSchema = {
...this.scaffold
};
regions: Array<RegionConfig> = [
{
key: 'body',
label: '内容区',
renderMethod: 'renderBody',
preferTag: '展示'
}
];
panelTitle = '卡片';
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
return [
getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
{
type: 'button-group-select',
label: tipedLabel(
'选择区域',
'点击触发选中或取消选中的区域'
),
name: 'checkOnItemClick',
options: [
{label: '整个', value: true},
{label: '选框', value: false}
],
pipeIn: defaultValue(false)
},
getSchemaTpl('switch', {
label: tipedLabel(
'隐藏选框',
'不再显示选择框,可以通过自定义选中态外观实现选中样式'
),
name: 'hideCheckToggler',
visibleOn: 'this.checkOnItemClick'
})
]
},
getSchemaTpl('status', {isFormItem: false})
])
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:classNames', {
isFormItem: false,
schema: [
getSchemaTpl('className', {
name: 'bodyClassName',
label: '内容区',
visibleOn: 'this.icon'
}),
// TODO
getSchemaTpl('className', {
name: 'selectedClassName',
label: '选中态',
visibleOn: 'this.icon'
})
]
})
])
}
])
];
};
}
registerEditorPlugin(Card2Plugin);

View File

@ -4,18 +4,17 @@ import {registerEditorPlugin} from 'amis-editor-core';
import {
BaseEventContext,
BasePlugin,
BasicPanelItem,
BasicRendererInfo,
BasicToolbarItem,
ContextMenuEventContext,
ContextMenuItem,
PluginInterface,
RegionConfig,
RendererInfo,
RendererInfoResolveEventContext
RendererInfoResolveEventContext,
tipedLabel
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {diff, JSONPipeOut, repeatArray} from 'amis-editor-core';
import {getEventControlConfig} from '../util';
export class CardsPlugin extends BasePlugin {
// 关联渲染器名字
@ -41,26 +40,17 @@ export class CardsPlugin extends BasePlugin {
},
columnsCount: 2,
card: {
type: 'card',
type: 'card2',
className: 'm-b-none',
header: {
title: '标题',
subTitle: '副标题'
},
body: [
{
name: 'a',
label: 'A'
},
{
name: 'b',
label: 'B'
}
],
actions: [
{
label: '详情',
type: 'button'
type: 'container',
body: [
{
type: 'tpl',
tpl: '这是一个模板'
}
]
}
]
}
@ -71,116 +61,119 @@ export class CardsPlugin extends BasePlugin {
};
panelTitle = '卡片集';
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
const isCRUDBody = context.schema.type === 'crud';
return [
getSchemaTpl('tabs', [
{
title: '常规',
body: [
{
children: (
<div className="m-b">
<Button
level="success"
size="sm"
block
onClick={this.editDetail.bind(this, context.id)}
>
</Button>
</div>
)
},
{
type: 'divider'
},
{
name: 'title',
type: 'input-text',
label: '标题'
},
{
name: 'href',
type: 'input-text',
label: '打开外部链接'
},
isCRUDBody
? null
: {
name: 'source',
type: 'input-text',
label: '数据源',
pipeIn: defaultValue('${items}'),
description: '绑定当前环境变量',
test: !isCRUDBody
},
{
name: 'placeholder',
value: '暂无数据',
type: 'input-text',
label: '无数据提示'
}
]
},
{
title: '外观',
body: [
getSchemaTpl('switch', {
name: 'showHeader',
label: '是否显示头部',
pipeIn: defaultValue(true)
}),
getSchemaTpl('switch', {
name: 'showFooter',
label: '是否显示底部',
pipeIn: defaultValue(true)
}),
getSchemaTpl('className', {
label: 'CSS 类名'
}),
getSchemaTpl('className', {
name: 'headerClassName',
label: '头部 CSS 类名'
}),
getSchemaTpl('className', {
name: 'footerClassName',
label: '底部 CSS 类名'
}),
getSchemaTpl('className', {
name: 'itemsClassName',
label: '内容 CSS 类名'
}),
getSchemaTpl('className', {
pipeIn: defaultValue('Grid-col--sm6 Grid-col--md4 Grid-col--lg3'),
name: 'itemClassName',
label: '卡片 CSS 类名'
}),
{
name: 'columnsCount',
type: 'input-range',
visibleOn: '!this.leftFixed',
min: 0,
max: 12,
step: 1,
label: '每行显示个数',
description: '不设置时,由卡片 CSS 类名决定'
},
getSchemaTpl('switch', {
name: 'masonryLayout',
label: '启用瀑布流'
})
]
},
{
title: '显隐',
body: [getSchemaTpl('ref'), getSchemaTpl('visible')]
}
])
];
const isCRUDBody = ['crud', 'crud2'].includes(context.schema.type);
return getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('switch', {
label: '可多选',
name: 'multiple',
visibleOn: `data.selectable`
}),
// getSchemaTpl('switch', {
// label: '可全选',
// name: 'checkAll',
// pipeIn: defaultValue(true),
// visibleOn: `data.selectable && data.multiple`
// }),
{
name: 'placeholder',
value: '暂无数据',
type: 'input-text',
label: '占位内容'
}
]
},
{
title: '数据',
hidden: isCRUDBody,
body: [
{
name: 'source',
type: 'input-text',
label: tipedLabel('数据', '可绑定当前页面数据'),
pipeIn: defaultValue('${items}'),
// visible: !isCRUDBody
},
{
name: 'valueField',
type: 'input-text',
label: '值字段',
// visible: isInForm && !isCRUDBody
},
]
},
getSchemaTpl('status', {
isFormItem: false
})
])
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
{
title: '布局',
body: [
{
type: 'button-group-select',
name: 'masonryLayout',
label: '模式',
pipeIn: defaultValue(false),
options: [
{label: '瀑布', value: true},
{label: '流式', value: false}
]
},
{
name: 'columnsCount',
type: 'input-range',
pipeIn: defaultValue(4),
min: 0,
max: 12,
step: 1,
label: tipedLabel(
'每行显示个数',
'不设置时,由卡片 CSS 类名决定'
)
}
]
},
getSchemaTpl('style:classNames', {
isFormItem: false,
schema: [
getSchemaTpl('className', {
name: 'itemsClassName',
label: '内容'
}),
getSchemaTpl('className', {
pipeIn: defaultValue('Grid-col--sm6 Grid-col--md4 Grid-col--lg3'),
name: 'itemClassName',
label: '卡片'
}),
]
})
])
},
{
title: '事件',
className: 'p-none',
body: [
getSchemaTpl('eventControl', {
name: 'onEvent',
...getEventControlConfig(this.manager, context)
})
]
}
]);
};
editDetail(id: string) {
@ -253,14 +246,16 @@ export class CardsPlugin extends BasePlugin {
...props.defaultData,
...props.data
};
const arr = Array.isArray(props.value)
let value = Array.isArray(props.value)
? props.value
: typeof props.source === 'string'
? resolveVariable(props.source, data)
: resolveVariable('items', data);
if (!Array.isArray(arr) || !arr.length) {
const mockedData: any = {
? resolveVariable(props.source, data)
: resolveVariable('items', data);
value = !Array.isArray(value) ? [] : value;
if (value.length < 5) {
const mockedData: any = value.length ? value[0] : {
id: 666,
title: '假数据',
description: '假数据',
@ -268,20 +263,36 @@ export class CardsPlugin extends BasePlugin {
b: '假数据'
};
props.value = repeatArray(mockedData, 1).map((item, index) => ({
value = value.concat(repeatArray(mockedData, 3).map((item, index) => ({
...item,
id: index + 1
}));
})));
}
const {$schema, ...rest} = props;
value = value.slice(0, 4);
return {
...JSONPipeOut(rest),
$schema
...props,
value
};
}
overrides = {
renderCard(
this: any,
index: number,
card: any,
...rest: any[]
) {
return this.super(
index,
// 使第一个卡片元素可以选择并编辑schema
index > 0 ? JSONPipeOut(card) : card,
...rest
);
}
}
getRendererInfo(
context: RendererInfoResolveEventContext
): BasicRendererInfo | void {
@ -289,7 +300,7 @@ export class CardsPlugin extends BasePlugin {
const {renderer, schema} = context;
if (
!schema.$$id &&
schema.$$editor?.renderer.name === 'crud' &&
['crud', 'crud2'].includes(schema.$$editor?.renderer.name) &&
renderer.name === 'cards'
) {
return {

View File

@ -1,7 +1,7 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
export class CollapsePlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -2,7 +2,7 @@ import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {isObject} from 'amis-editor-core';
export class CollapseGroupPlugin extends BasePlugin {

View File

@ -0,0 +1,129 @@
import {
BaseEventContext,
BasePlugin,
BasicRendererInfo,
PluginInterface,
RendererInfoResolveEventContext,
getSchemaTpl,
registerEditorPlugin
} from 'amis-editor-core';
export class ColumnToggler extends BasePlugin {
// 关联渲染器名字
rendererName = 'column-toggler';
$schema = '/schemas/ColumnToggler.json';
// 组件名称
name = '自定义显示列';
isBaseComponent = true;
description =
'用来展示表格的自定义显示列按钮,你可以配置不同的展示样式。';
tags = ['自定义显示列'];
icon = 'fa fa-square';
panelTitle = '自定义显示列';
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
return getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
{
label: '按钮文字',
type: 'input-text',
name: 'label'
},
{
label: '按钮提示',
type: 'input-text',
name: 'tooltip'
},
getSchemaTpl('switch', {
name: 'defaultIsOpened',
label: '是否默认展开'
}),
getSchemaTpl('icon', {
label: '按钮图标'
})
]
}
])
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('size', {
label: '按钮尺寸'
})
]
},
{
title: 'CSS 类名',
body: [
getSchemaTpl('className', {
name: 'className',
label: '显示列样式'
}),
getSchemaTpl('className', {
name: 'btnClassName',
label: '按钮样式'
}),
]
}
])
}
]);
};
/**
*
*/
filterProps(props: any) {
props.disabled = false;
return props;
}
/**
* rendererName
* @param renderer
*/
getRendererInfo({
renderer,
schema
}: RendererInfoResolveEventContext): BasicRendererInfo | void {
const plugin: PluginInterface = this;
if (
schema.$$id &&
plugin.name &&
plugin.rendererName &&
plugin.rendererName === renderer.name
) {
// 复制部分信息出去
return {
name: schema.label ? schema.label : plugin.name,
regions: plugin.regions,
patchContainers: plugin.patchContainers,
// wrapper: plugin.wrapper,
vRendererConfig: plugin.vRendererConfig,
wrapperProps: plugin.wrapperProps,
wrapperResolve: plugin.wrapperResolve,
filterProps: plugin.filterProps,
$schema: plugin.$schema,
renderRenderer: plugin.renderRenderer
};
}
}
}
registerEditorPlugin(ColumnToggler);

View File

@ -12,11 +12,17 @@ export class ContainerPlugin extends BasePlugin {
isBaseComponent = true;
description = '一个简单的容器,可以将多个渲染器放置在一起。';
tags = ['容器'];
withDataSource = false;
icon = 'fa fa-square-o';
pluginIcon = 'container-plugin';
scaffold = {
type: 'container',
body: '内容'
body: [
{
type: 'tpl',
tpl: '内容'
}
]
};
previewSchema = {
...this.scaffold

View File

@ -1,20 +1,18 @@
import {Button} from 'amis';
import React from 'react';
import {registerEditorPlugin} from 'amis-editor-core';
import {
BaseEventContext,
BasePlugin,
BasicToolbarItem,
ContextMenuEventContext,
ContextMenuItem
ContextMenuItem,
registerEditorPlugin,
tipedLabel,
defaultValue,
getSchemaTpl,
diff
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {diff} from 'amis-editor-core';
import {
BUTTON_DEFAULT_ACTION,
formItemControl,
tipedLabel
} from '../component/BaseControl';
import {BUTTON_DEFAULT_ACTION} from '../component/BaseControl';
export class DropDownButtonPlugin extends BasePlugin {
// 关联渲染器名字
rendererName = 'dropdown-button';

View File

@ -3,10 +3,10 @@ import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {
RendererPluginAction,
RendererPluginEvent
RendererPluginEvent,
tipedLabel
} from 'amis-editor-core';
import {getSchemaTpl, defaultValue} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {getEventControlConfig} from '../../util';
export class ButtonGroupControlPlugin extends BasePlugin {
@ -118,17 +118,15 @@ export class ButtonGroupControlPlugin extends BasePlugin {
rendererSchema: context?.schema,
useSelectMode: true, // 改用 Select 设置模式
visibleOn: 'this.options && this.options.length > 0'
}),
})
]
},
{
title: '按钮管理',
body: [
getSchemaTpl('optionControlV2'),
]
body: [getSchemaTpl('optionControlV2')]
},
getSchemaTpl('status', {
isFormItem: true,
isFormItem: true
})
])
]
@ -172,10 +170,10 @@ export class ButtonGroupControlPlugin extends BasePlugin {
getSchemaTpl('className', {
label: '按钮',
name: 'btnClassName'
}),
})
]
}),
]),
})
])
]
},
{

View File

@ -4,16 +4,15 @@ import {
BaseEventContext,
BasicSubRenderInfo,
RendererEventContext,
SubRendererInfo
} from 'amis-editor-core';
import {getSchemaTpl, defaultValue} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {tipedLabel} from '../../component/BaseControl';
import {getEventControlConfig} from '../../util';
import {
SubRendererInfo,
RendererPluginAction,
RendererPluginEvent
RendererPluginEvent,
tipedLabel,
getSchemaTpl,
defaultValue
} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
export class ChainedSelectControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -135,7 +134,8 @@ export class ChainedSelectControlPlugin extends BasePlugin {
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
{

View File

@ -10,14 +10,12 @@ import {
BasicSubRenderInfo,
RendererEventContext,
SubRendererInfo,
BaseEventContext
BaseEventContext,
tipedLabel
} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {tipedLabel} from '../../component/BaseControl';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {getEventControlConfig} from '../../util';
setSchemaTpl('option', {
@ -110,7 +108,7 @@ export class CheckboxControlPlugin extends BasePlugin {
getSchemaTpl('label'),
getSchemaTpl('option'),
{
type: 'ae-Switch-More',
type: 'ae-switch-more',
hiddenOnDefault: false,
mode: 'normal',
label: '值格式',
@ -152,7 +150,8 @@ export class CheckboxControlPlugin extends BasePlugin {
}),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true}),

View File

@ -168,7 +168,8 @@ export class CheckboxesControlPlugin extends BasePlugin {
getSchemaTpl('extractValue'),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
{

View File

@ -140,7 +140,8 @@ export class CodeEditorControlPlugin extends BasePlugin {
}),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true}),

View File

@ -149,7 +149,8 @@ export class DiffEditorControlPlugin extends BasePlugin {
},
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true}),

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,7 @@ import {
} from 'amis-editor-core';
import {formItemControl} from '../../component/BaseControl';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class CityControlPlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -1,9 +1,8 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {registerEditorPlugin, tipedLabel} from 'amis-editor-core';
import {getSchemaTpl} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {getEventControlConfig} from '../../util';
import {tipedLabel} from '../../component/BaseControl';
import tinyColor from 'tinycolor2';
function convertColor(value: string[], format: string): string[];
@ -171,124 +170,133 @@ export class ColorControlPlugin extends BasePlugin {
return getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
{
type: 'select',
label: '值格式',
name: 'format',
value: 'hex',
options: formatOptions,
onChange: (
format: any,
oldFormat: any,
model: any,
form: any
) => {
const {value, presetColors} = form.data;
if (value) {
form.setValueByName('value', convertColor(value, format));
body: getSchemaTpl(
'collapseGroup',
[
{
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
{
type: 'select',
label: '值格式',
name: 'format',
value: 'hex',
options: formatOptions,
onChange: (
format: any,
oldFormat: any,
model: any,
form: any
) => {
const {value, presetColors} = form.data;
if (value) {
form.setValueByName('value', convertColor(value, format));
}
if (Array.isArray(presetColors)) {
form.setValueByName(
'presetColors',
convertColor(presetColors, format)
);
}
}
if (Array.isArray(presetColors)) {
form.setValueByName(
'presetColors',
convertColor(presetColors, format)
);
}
}
},
// todo: 待优化
[
...formatOptions.map(({value}) =>
this.getConditionalColorPanel(value)
)
],
// {
// label: '默认值',
// name: 'value',
// type: 'input-color',
// format: '${format}'
// },
getSchemaTpl('clearable'),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description')
]
},
{
title: '拾色器',
body: [
getSchemaTpl('switch', {
label: tipedLabel(
'隐藏调色盘',
'开启时,禁止手动输入颜色,只能从备选颜色中选择'
),
name: 'allowCustomColor',
disabledOn:
'Array.isArray(presetColors) && presetColors.length === 0',
pipeIn: (value: any) =>
typeof value === 'undefined' ? false : !value,
pipeOut: (value: boolean) => !value
}),
getSchemaTpl('switch', {
label: tipedLabel('备选色', '拾色器底部的备选颜色'),
name: 'presetColors',
onText: '自定义',
offText: '默认',
pipeIn: (value: any) =>
typeof value === 'undefined' ? false : true,
pipeOut: (
value: any,
originValue: any,
{format = 'hex'}: any
) => {
return !value ? undefined : presetColorsByFormat[format];
},
onChange: (
colors: any,
oldValue: any,
model: any,
form: any
) => {
if (Array.isArray(colors) && colors.length === 0) {
form.setValueByName('allowCustomColor', true);
// todo: 待优化
[
...formatOptions.map(({value}) =>
this.getConditionalColorPanel(value)
)
],
// {
// label: '默认值',
// name: 'value',
// type: 'input-color',
// format: '${format}'
// },
getSchemaTpl('clearable'),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
{
title: '拾色器',
body: [
getSchemaTpl('switch', {
label: tipedLabel(
'隐藏调色盘',
'开启时,禁止手动输入颜色,只能从备选颜色中选择'
),
name: 'allowCustomColor',
disabledOn:
'Array.isArray(presetColors) && presetColors.length === 0',
pipeIn: (value: any) =>
typeof value === 'undefined' ? false : !value,
pipeOut: (value: boolean) => !value
}),
getSchemaTpl('switch', {
label: tipedLabel('备选色', '拾色器底部的备选颜色'),
name: 'presetColors',
onText: '自定义',
offText: '默认',
pipeIn: (value: any) =>
typeof value === 'undefined' ? false : true,
pipeOut: (
value: any,
originValue: any,
{format = 'hex'}: any
) => {
return !value ? undefined : presetColorsByFormat[format];
},
onChange: (
colors: any,
oldValue: any,
model: any,
form: any
) => {
if (Array.isArray(colors) && colors.length === 0) {
form.setValueByName('allowCustomColor', true);
}
}
}
}),
...formatOptions.map(({value}) =>
this.getConditionalColorComb(value)
)
]
},
getSchemaTpl('status', {
isFormItem: true
}),
getSchemaTpl('validation', {
tag: ValidatorTag.MultiSelect
})
], {...context?.schema, configTitle: 'props'})
}),
...formatOptions.map(({value}) =>
this.getConditionalColorComb(value)
)
]
},
getSchemaTpl('status', {
isFormItem: true
}),
getSchemaTpl('validation', {
tag: ValidatorTag.MultiSelect
})
],
{...context?.schema, configTitle: 'props'}
)
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:formItem', {renderer}),
getSchemaTpl('style:classNames', {
schema: [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
})
]
})
], {...context?.schema, configTitle: 'style'})
body: getSchemaTpl(
'collapseGroup',
[
getSchemaTpl('style:formItem', {renderer}),
getSchemaTpl('style:classNames', {
schema: [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
})
]
})
],
{...context?.schema, configTitle: 'style'}
)
},
{
title: '事件',

View File

@ -1,14 +1,10 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {BasePlugin, BaseEventContext, tipedLabel} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
const formatX = [
{
@ -237,157 +233,166 @@ export class DateControlPlugin extends BasePlugin {
return getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('selectDateType', {
value: this.scaffold.type,
onChange: (
value: string,
oldValue: any,
model: any,
form: any
) => {
let type: string = value.split('-')[1];
body: getSchemaTpl(
'collapseGroup',
[
{
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('selectDateType', {
value: this.scaffold.type,
onChange: (
value: string,
oldValue: any,
model: any,
form: any
) => {
let type: string = value.split('-')[1];
form.setValues({
inputFormat: DateType[type]?.format,
placeholder: DateType[type]?.placeholder,
format: type === 'time' ? 'HH:mm' : 'X',
minDate: '',
maxDate: '',
value: ''
});
}
}),
{
type: 'input-text',
name: 'format',
label: tipedLabel(
'值格式',
'提交数据前将根据设定格式化数据,请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('X')
},
{
type: 'input-text',
name: 'inputFormat',
label: tipedLabel(
'显示格式',
'请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('YYYY-MM-DD'),
clearable: true,
onChange: (
value: string,
oldValue: any,
model: any,
form: any
) => {
const type = form.data.type.split('-')[1];
model.setOptions(DateType[type].formatOptions);
// 时间日期类组件 input-time 需要更加关注 timeFormat 和 inputFormat 属性区别
// inputFormat 表示输入框内的显示格式; timeFormat表示选择下拉弹窗中展示"HH、mm、ss"的组合
if (type === 'time') {
const timeFormatObj = DateType[type].formatOptions.find(
item => item.value === value
);
const timeFormat = timeFormatObj
? (timeFormatObj as any).timeFormat
: 'HH:mm:ss';
form.setValues({
timeFormat: timeFormat
inputFormat: DateType[type]?.format,
placeholder: DateType[type]?.placeholder,
format: type === 'time' ? 'HH:mm' : 'X',
minDate: '',
maxDate: '',
value: ''
});
}
}),
{
type: 'input-text',
name: 'format',
label: tipedLabel(
'值格式',
'提交数据前将根据设定格式化数据,请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('X')
},
options:
DateType[this.scaffold.type.split('-')[1]].formatOptions
},
getSchemaTpl('utc'),
getSchemaTpl('clearable', {
pipeIn: defaultValue(true)
}),
getSchemaTpl('valueFormula', {
rendererSchema: context?.schema,
label: tipedLabel(
'默认值',
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
)
}),
getSchemaTpl('valueFormula', {
name: 'minDate',
rendererSchema: {
...context?.schema,
value: context?.schema.minDate
{
type: 'input-text',
name: 'inputFormat',
label: tipedLabel(
'显示格式',
'请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('YYYY-MM-DD'),
clearable: true,
onChange: (
value: string,
oldValue: any,
model: any,
form: any
) => {
const type = form.data.type.split('-')[1];
model.setOptions(DateType[type].formatOptions);
// 时间日期类组件 input-time 需要更加关注 timeFormat 和 inputFormat 属性区别
// inputFormat 表示输入框内的显示格式; timeFormat表示选择下拉弹窗中展示"HH、mm、ss"的组合
if (type === 'time') {
const timeFormatObj = DateType[type].formatOptions.find(
item => item.value === value
);
const timeFormat = timeFormatObj
? (timeFormatObj as any).timeFormat
: 'HH:mm:ss';
form.setValues({
timeFormat: timeFormat
});
}
},
options:
DateType[this.scaffold.type.split('-')[1]].formatOptions
},
needDeleteProps: ['minDate'], // 避免自我限制
label: tipedLabel('最小值', tipedLabelText)
}),
getSchemaTpl('valueFormula', {
name: 'maxDate',
rendererSchema: {
...context?.schema,
value: context?.schema.maxDate
},
needDeleteProps: ['maxDate'], // 避免自我限制
label: tipedLabel('最大值', tipedLabelText)
}),
getSchemaTpl('placeholder', {
pipeIn: defaultValue('请选择日期')
}),
// getSchemaTpl('remark'),
// getSchemaTpl('labelRemark'),
getSchemaTpl('description')
]
},
getSchemaTpl('status', {isFormItem: true}),
getSchemaTpl('validation', {
tag: ValidatorTag.Date
})
], {...context?.schema, configTitle: 'props'})
getSchemaTpl('utc'),
getSchemaTpl('clearable', {
pipeIn: defaultValue(true)
}),
getSchemaTpl('valueFormula', {
rendererSchema: context?.schema,
label: tipedLabel(
'默认值',
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
)
}),
getSchemaTpl('valueFormula', {
name: 'minDate',
rendererSchema: {
...context?.schema,
value: context?.schema.minDate
},
needDeleteProps: ['minDate'], // 避免自我限制
label: tipedLabel('最小值', tipedLabelText)
}),
getSchemaTpl('valueFormula', {
name: 'maxDate',
rendererSchema: {
...context?.schema,
value: context?.schema.maxDate
},
needDeleteProps: ['maxDate'], // 避免自我限制
label: tipedLabel('最大值', tipedLabelText)
}),
getSchemaTpl('placeholder', {
pipeIn: defaultValue('请选择日期')
}),
// getSchemaTpl('remark'),
// getSchemaTpl('labelRemark'),
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true}),
getSchemaTpl('validation', {
tag: ValidatorTag.Date
})
],
{...context?.schema, configTitle: 'props'}
)
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:formItem', renderer),
getSchemaTpl('style:classNames', [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
}),
getSchemaTpl('className', {
name: 'addOn.className',
label: 'AddOn',
visibleOn: 'this.addOn && this.addOn.type === "text"'
})
]),
getSchemaTpl('style:others', [
{
name: 'embed',
type: 'button-group-select',
size: 'md',
label: '模式',
mode: 'row',
value: false,
options: [
{
label: '浮层',
value: false
},
{
label: '内嵌',
value: true
}
]
}
])
], {...context?.schema, configTitle: 'style'})
body: getSchemaTpl(
'collapseGroup',
[
getSchemaTpl('style:formItem', renderer),
getSchemaTpl('style:classNames', [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
}),
getSchemaTpl('className', {
name: 'addOn.className',
label: 'AddOn',
visibleOn: 'this.addOn && this.addOn.type === "text"'
})
]),
getSchemaTpl('style:others', [
{
name: 'embed',
type: 'button-group-select',
size: 'md',
label: '模式',
mode: 'row',
value: false,
options: [
{
label: '浮层',
value: false
},
{
label: '内嵌',
value: true
}
]
}
])
],
{...context?.schema, configTitle: 'style'}
)
},
{
title: '事件',

View File

@ -1,14 +1,10 @@
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
const DateType: {
[key: string]: {
@ -184,72 +180,74 @@ export class DateRangeControlPlugin extends BasePlugin {
return getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('selectDateRangeType', {
value: this.scaffold.type,
onChange: (
value: string,
oldValue: any,
model: any,
form: any
) => {
const type: string = value.split('-')[1];
form.setValues({
inputFormat: DateType[type]?.format,
placeholder: DateType[type]?.placeholder,
format: type === 'time' ? 'HH:mm' : 'X',
minDate: '',
maxDate: '',
value: '',
ranges: DateType[type]?.ranges
});
}
}),
{
type: 'input-text',
name: 'format',
label: tipedLabel(
'值格式',
'提交数据前将根据设定格式化数据,请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('X')
},
{
type: 'input-text',
name: 'inputFormat',
label: tipedLabel(
'显示格式',
'请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('YYYY-MM-DD'),
clearable: true
// onChange: (
// value: string,
// oldValue: any,
// model: any,
// form: any
// ) => {
// model.setOptions(
// DateType[form.data.type.split('-')[1]].formatOptions
// );
// },
// options:
// DateType[this.scaffold.type.split('-')[1]].formatOptions
},
getSchemaTpl('utc'),
getSchemaTpl('clearable', {
pipeIn: defaultValue(true)
}),
body: getSchemaTpl(
'collapseGroup',
[
{
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('selectDateRangeType', {
value: this.scaffold.type,
onChange: (
value: string,
oldValue: any,
model: any,
form: any
) => {
const type: string = value.split('-')[1];
form.setValues({
inputFormat: DateType[type]?.format,
placeholder: DateType[type]?.placeholder,
format: type === 'time' ? 'HH:mm' : 'X',
minDate: '',
maxDate: '',
value: '',
ranges: DateType[type]?.ranges
});
}
}),
{
type: 'input-text',
name: 'format',
label: tipedLabel(
'值格式',
'提交数据前将根据设定格式化数据,请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('X')
},
{
type: 'input-text',
name: 'inputFormat',
label: tipedLabel(
'显示格式',
'请参考 <a href="https://momentjs.com/" target="_blank">moment</a> 中的格式用法。'
),
pipeIn: defaultValue('YYYY-MM-DD'),
clearable: true
// onChange: (
// value: string,
// oldValue: any,
// model: any,
// form: any
// ) => {
// model.setOptions(
// DateType[form.data.type.split('-')[1]].formatOptions
// );
// },
// options:
// DateType[this.scaffold.type.split('-')[1]].formatOptions
},
getSchemaTpl('utc'),
getSchemaTpl('clearable', {
pipeIn: defaultValue(true)
}),
getSchemaTpl('valueFormula', {
/* : amis
getSchemaTpl('valueFormula', {
/* : amis
rendererSchema: {
...context?.schema,
size: 'full', // 备注目前样式还有问题需要在amis端进行优化
@ -257,120 +255,127 @@ export class DateRangeControlPlugin extends BasePlugin {
},
mode: 'vertical',
*/
rendererSchema: {
type: 'input-date'
},
label: tipedLabel(
'默认值',
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
)
}),
getSchemaTpl('valueFormula', {
name: 'minDate',
rendererSchema: {
...context?.schema,
value: context?.schema.minDate,
type: 'input-date'
},
needDeleteProps: ['minDate'], // 避免自我限制
label: tipedLabel('最小值', tipedLabelText)
}),
getSchemaTpl('valueFormula', {
name: 'maxDate',
rendererSchema: {
...context?.schema,
value: context?.schema.maxDate,
type: 'input-date'
},
needDeleteProps: ['maxDate'], // 避免自我限制
label: tipedLabel('最大值', tipedLabelText)
}),
rendererSchema: {
type: 'input-date'
},
label: tipedLabel(
'默认值',
'支持 <code>now、+1day、-2weeks、+1hours、+2years</code>等这种相对值用法'
)
}),
getSchemaTpl('valueFormula', {
name: 'minDate',
rendererSchema: {
...context?.schema,
value: context?.schema.minDate,
type: 'input-date'
},
needDeleteProps: ['minDate'], // 避免自我限制
label: tipedLabel('最小值', tipedLabelText)
}),
getSchemaTpl('valueFormula', {
name: 'maxDate',
rendererSchema: {
...context?.schema,
value: context?.schema.maxDate,
type: 'input-date'
},
needDeleteProps: ['maxDate'], // 避免自我限制
label: tipedLabel('最大值', tipedLabelText)
}),
getSchemaTpl('formulaControl', {
name: 'minDuration',
label: tipedLabel('最小跨度', '例如 2days'),
placeholder: '请输入最小跨度',
inputClassName: 'is-inline'
}),
getSchemaTpl('formulaControl', {
name: 'maxDuration',
label: tipedLabel('最大跨度', '例如 1year'),
placeholder: '请输入最大跨度',
inputClassName: 'is-inline'
}),
getSchemaTpl('dateShortCutControl', {
mode: 'normal',
dropDownOption: {
'yesterday': '昨天',
'thisweek': '这个周',
'prevweek': '上周',
'7daysago': '最近7天',
'thismonth': '这个月',
'prevmonth': '上个月',
'thisquarter': '这个季度',
'prevquarter': '上个季度',
'thisyear': '今年'
}
}),
// getSchemaTpl('remark'),
// getSchemaTpl('labelRemark'),
{
type: 'input-text',
name: 'startPlaceholder',
label: '前占位提示',
pipeIn: defaultValue('开始时间')
},
{
type: 'input-text',
name: 'endPlaceholder',
label: '后占位提示',
pipeIn: defaultValue('结束时间')
}
]
},
getSchemaTpl('status', {isFormItem: true}),
getSchemaTpl('validation', {
tag: ValidatorTag.Date
})
], {...context?.schema, configTitle: 'props'})
getSchemaTpl('formulaControl', {
name: 'minDuration',
label: tipedLabel('最小跨度', '例如 2days'),
placeholder: '请输入最小跨度',
inputClassName: 'is-inline'
}),
getSchemaTpl('formulaControl', {
name: 'maxDuration',
label: tipedLabel('最大跨度', '例如 1year'),
placeholder: '请输入最大跨度',
inputClassName: 'is-inline'
}),
getSchemaTpl('dateShortCutControl', {
mode: 'normal',
dropDownOption: {
'yesterday': '昨天',
'thisweek': '这个周',
'prevweek': '上周',
'7daysago': '最近7天',
'thismonth': '这个月',
'prevmonth': '上个月',
'thisquarter': '这个季度',
'prevquarter': '上个季度',
'thisyear': '今年'
}
}),
// getSchemaTpl('remark'),
// getSchemaTpl('labelRemark'),
{
type: 'input-text',
name: 'startPlaceholder',
label: '前占位提示',
pipeIn: defaultValue('开始时间')
},
{
type: 'input-text',
name: 'endPlaceholder',
label: '后占位提示',
pipeIn: defaultValue('选择结束时间')
},
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true}),
getSchemaTpl('validation', {
tag: ValidatorTag.Date
})
],
{...context?.schema, configTitle: 'props'}
)
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:formItem', renderer),
getSchemaTpl('style:classNames', [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
}),
getSchemaTpl('className', {
name: 'addOn.className',
label: 'AddOn',
visibleOn: 'this.addOn && this.addOn.type === "text"'
})
]),
getSchemaTpl('style:others', [
{
name: 'embed',
type: 'button-group-select',
size: 'md',
label: '模式',
mode: 'row',
value: false,
options: [
{
label: '浮层',
value: false
},
{
label: '内嵌',
value: true
}
]
}
])
], {...context?.schema, configTitle: 'style'})
body: getSchemaTpl(
'collapseGroup',
[
getSchemaTpl('style:formItem', renderer),
getSchemaTpl('style:classNames', [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
}),
getSchemaTpl('className', {
name: 'addOn.className',
label: 'AddOn',
visibleOn: 'this.addOn && this.addOn.type === "text"'
})
]),
getSchemaTpl('style:others', [
{
name: 'embed',
type: 'button-group-select',
size: 'md',
label: '模式',
mode: 'row',
value: false,
options: [
{
label: '浮层',
value: false
},
{
label: '内嵌',
value: true
}
]
}
])
],
{...context?.schema, configTitle: 'style'}
)
},
{
title: '事件',

View File

@ -1,13 +1,9 @@
import {defaultValue, getSchemaTpl, valuePipeOut} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {registerEditorPlugin, tipedLabel} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class FileControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -348,7 +344,8 @@ export class FileControlPlugin extends BasePlugin {
}),
getSchemaTpl('remark'),
getSchemaTpl('labelRemark'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
// getSchemaTpl('autoFill')
]
},

View File

@ -2,10 +2,8 @@ import {getSchemaTpl, valuePipeOut} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {formItemControl} from '../../component/BaseControl';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class ImageControlPlugin extends BasePlugin {
// 关联渲染器名字
rendererName = 'input-image';

View File

@ -1,7 +1,4 @@
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import flatten from 'lodash/flatten';
import {ContainerWrapper} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
@ -13,8 +10,7 @@ import {
SubRendererInfo,
BaseEventContext
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';

View File

@ -1,11 +1,7 @@
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
@ -278,7 +274,8 @@ export class RangeControlPlugin extends BasePlugin {
label: '可重置',
value: false,
visibleOn: '!!data.showInput'
})
}),
getSchemaTpl('autoFillApi')
]
},
{

View File

@ -5,14 +5,10 @@ import {
undefinedPipeOut
} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {BasePlugin, BaseEventContext, tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class RateControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -234,7 +230,8 @@ export class RateControlPlugin extends BasePlugin {
return res;
}
})
}),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true, readonly: true}),

View File

@ -1,4 +1,5 @@
import {Button} from 'amis';
import { SchemaCollection } from 'amis/lib/Schema';
import React from 'react';
import {registerEditorPlugin} from 'amis-editor-core';
import {
@ -94,7 +95,7 @@ export class SubFormControlPlugin extends BasePlugin {
label: '允许最多个数',
type: 'input-number'
}
];
] as SchemaCollection;
};
filterProps(props: any) {

View File

@ -9,10 +9,7 @@ import {
} from 'amis-editor-core';
import {formItemControl} from '../../component/BaseControl';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class TagControlPlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -6,8 +6,7 @@ import {
SubRendererInfo,
BaseEventContext
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl, setSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {defaultValue, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
@ -222,7 +221,7 @@ export class TextControlPlugin extends BasePlugin {
{
name: 'addOn',
label: tipedLabel('AddOn', '输入框左侧或右侧的附加挂件'),
type: 'ae-Switch-More',
type: 'ae-switch-more',
mode: 'normal',
formType: 'extend',
title: 'AddOn',

View File

@ -1,13 +1,10 @@
import React from 'react';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin} from 'amis-editor-core';
import {getEventControlConfig} from '../../util';
import { getArgsWrapper } from '../../renderer/event-control/helper';
import {getArgsWrapper} from '../../renderer/event-control/helper';
export class TreeControlPlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -112,9 +112,15 @@ export class ItemPlugin extends BasePlugin {
ignoreValidator ? null : getSchemaTpl('required'),
getSchemaTpl('description'),
getSchemaTpl('placeholder'),
getSchemaTpl('remark'),
renderer.renderLabel !== false ? getSchemaTpl('labelRemark') : null,
autoFillApi ? getSchemaTpl('autoFillApi') : null
getSchemaTpl('remark', {
mode: 'row'
}),
renderer.renderLabel !== false
? getSchemaTpl('labelRemark', {
mode: 'row'
})
: null,
getSchemaTpl('autoFillApi')
]
},

View File

@ -111,6 +111,8 @@ export class ListControlPlugin extends BasePlugin {
}),
getSchemaTpl('label'),
getSchemaTpl('multiple'),
getSchemaTpl('extractValue'),
getSchemaTpl('valueFormula', {
rendererSchema: context?.schema,
useSelectMode: true, // 改用 Select 设置模式

View File

@ -5,15 +5,12 @@ import {
BasicSubRenderInfo,
RendererEventContext,
SubRendererInfo,
BaseEventContext
BaseEventContext,
tipedLabel
} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class MatrixControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -151,7 +148,8 @@ export class MatrixControlPlugin extends BasePlugin {
}
],
pipeIn: defaultValue('column')
}
},
getSchemaTpl('autoFillApi')
]
},
{

View File

@ -1,8 +1,5 @@
import {relativeValueRe} from 'amis';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {availableLanguages} from 'amis/lib/renderers/Form/Editor';
import {defaultValue, getSchemaTpl, valuePipeOut} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
@ -11,9 +8,9 @@ import {
BasicSubRenderInfo,
RendererEventContext,
SubRendererInfo,
BaseEventContext
BaseEventContext,
tipedLabel
} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
@ -285,7 +282,8 @@ export class NestedSelectControlPlugin extends BasePlugin {
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
{

View File

@ -4,10 +4,7 @@ import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class RadiosControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -122,7 +119,8 @@ export class RadiosControlPlugin extends BasePlugin {
}),
// getSchemaTpl('autoFill')
getSchemaTpl('labelRemark'),
getSchemaTpl('remark')
getSchemaTpl('remark'),
getSchemaTpl('autoFillApi')
]
},
{

View File

@ -1,15 +1,10 @@
import {getSchemaTpl} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import isArray from 'lodash/isArray';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class SelectControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -225,7 +220,8 @@ export class SelectControlPlugin extends BasePlugin {
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
{
@ -263,7 +259,7 @@ export class SelectControlPlugin extends BasePlugin {
}
}),
getSchemaTpl('editable', {
type: 'ae-Switch-More',
type: 'ae-switch-more',
formType: 'extend',
hiddenOnDefault: true,
form: {
@ -271,7 +267,7 @@ export class SelectControlPlugin extends BasePlugin {
}
}),
getSchemaTpl('removable', {
type: 'ae-Switch-More',
type: 'ae-switch-more',
formType: 'extend',
hiddenOnDefault: true,
form: {

View File

@ -4,10 +4,7 @@ import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin} from 'amis-editor-core';
import {EditorNodeType} from 'amis-editor-core';
import {mockValue} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {mockValue, tipedLabel} from 'amis-editor-core';
export class StaticControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -64,7 +61,7 @@ export class StaticControlPlugin extends BasePlugin {
rendererSchema: {
...context?.schema,
type: 'textarea', // 改用多行文本编辑
value: context?.schema.tpl, // 避免默认值丢失
value: context?.schema.tpl // 避免默认值丢失
},
mode: 'vertical', // 改成上下展示模式
name: 'tpl'
@ -294,7 +291,8 @@ export class StaticControlPlugin extends BasePlugin {
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
/*{
children: (
<Button

View File

@ -1,13 +1,9 @@
import {getSchemaTpl, valuePipeOut} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {registerEditorPlugin, tipedLabel} from 'amis-editor-core';
import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import type {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import type {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class SwitchControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -97,7 +93,7 @@ export class SwitchControlPlugin extends BasePlugin {
},
{
type: 'ae-Switch-More',
type: 'ae-switch-more',
bulk: true,
mode: 'normal',
label: '填充文本',
@ -120,7 +116,7 @@ export class SwitchControlPlugin extends BasePlugin {
},
{
type: 'ae-Switch-More',
type: 'ae-switch-more',
bulk: true,
mode: 'normal',
label: tipedLabel(
@ -199,7 +195,8 @@ export class SwitchControlPlugin extends BasePlugin {
}),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {isFormItem: true}),

View File

@ -1,15 +1,11 @@
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin} from 'amis-editor-core';
import {BasePlugin, tipedLabel} from 'amis-editor-core';
import type {BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class TextareaControlPlugin extends BasePlugin {
// 关联渲染器名字
@ -142,7 +138,8 @@ export class TextareaControlPlugin extends BasePlugin {
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description')
getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
]
},
getSchemaTpl('status', {

View File

@ -3,10 +3,10 @@ import {
BasePlugin,
RegionConfig,
RendererInfo,
BaseEventContext
BaseEventContext,
tipedLabel
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl, valuePipeOut} from 'amis-editor-core';
import {formItemControl, tipedLabel} from '../component/BaseControl';
export class IFramePlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -1,13 +1,10 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, RendererInfo} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../validator';
import {getEventControlConfig} from '../util';
import {
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
export class LinkPlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -13,6 +13,8 @@ import {
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {diff, JSONPipeOut, repeatArray} from 'amis-editor-core';
import {getEventControlConfig} from '../util';
import {ListItemSchema} from 'amis/lib/renderers/List';
export class ListPlugin extends BasePlugin {
// 关联渲染器名字
@ -55,118 +57,148 @@ export class ListPlugin extends BasePlugin {
};
panelTitle = '列表';
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
const isCRUDBody = context.schema.type === 'crud';
const isCRUDBody = ['crud', 'crud2'].includes(context.schema.type);
return getSchemaTpl('tabs', [
{
title: '常规',
body: [
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
children: (
<Button
level="danger"
size="sm"
block
onClick={this.editDetail.bind(this, context.id)}
>
</Button>
)
},
{
type: 'divider'
},
{
name: 'title',
type: 'input-text',
label: '标题'
},
isCRUDBody
? null
: {
name: 'source',
title: '基本',
body: [
{
name: 'title',
type: 'input-text',
label: '数据源',
pipeIn: defaultValue('${items}'),
description: '绑定当前环境变量'
label: '标题'
},
{
name: 'placeholder',
pipeIn: defaultValue('没有数据'),
type: 'input-text',
label: '无数据提示'
}
]
isCRUDBody
? null
: {
name: 'source',
type: 'input-text',
label: '数据源',
pipeIn: defaultValue('${items}'),
description: '绑定当前环境变量'
},
{
name: 'placeholder',
pipeIn: defaultValue('没有数据'),
type: 'input-text',
label: '无数据提示'
}
]
},
getSchemaTpl('status', {
isFormItem: false
})
])
},
{
title: '外观',
body: [
getSchemaTpl('switch', {
name: 'showHeader',
label: '是否显示头部',
pipeIn: defaultValue(true)
}),
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('switch', {
name: 'showHeader',
label: '显示头部',
pipeIn: defaultValue(true)
}),
getSchemaTpl('switch', {
name: 'showFooter',
label: '是否显示底部',
pipeIn: defaultValue(true)
}),
getSchemaTpl('className', {
label: 'CSS 类名'
}),
getSchemaTpl('className', {
name: 'listClassName',
label: 'List div CSS 类名'
}),
getSchemaTpl('className', {
name: 'headerClassName',
label: '头部 CSS 类名'
}),
getSchemaTpl('className', {
name: 'footerClassName',
label: '底部 CSS 类名'
getSchemaTpl('switch', {
name: 'showFooter',
label: '显示底部',
pipeIn: defaultValue(true)
})
]
},
getSchemaTpl('style:classNames', {
isFormItem: false,
schema: [
getSchemaTpl('className', {
name: 'listClassName',
label: '列表项'
}),
getSchemaTpl('className', {
name: 'headerClassName',
label: '头部'
}),
getSchemaTpl('className', {
name: 'footerClassName',
label: '底部'
})
]
})
]
])
},
{
title: '显隐',
body: [getSchemaTpl('ref'), getSchemaTpl('visible')]
title: '事件',
className: 'p-none',
body: [
getSchemaTpl('eventControl', {
name: 'onEvent',
...getEventControlConfig(this.manager, context)
})
]
}
]);
};
filterProps(props: any) {
if (props.isSlot) {
props.value = [props.data];
return props;
overrides = {
renderListItem(
this: any,
index: number,
itemTemplace: ListItemSchema | undefined,
...rest: any[]
) {
return this.super(
index,
// 使第一个卡片元素可以选择并编辑schema
index > 0 ? JSONPipeOut(itemTemplace) : itemTemplace,
...rest
);
}
};
filterProps(props: any) {
const data = {
...props.defaultData,
...props.data
};
let arr = Array.isArray(props.value)
let value = Array.isArray(props.value)
? props.value
: typeof props.source === 'string'
? resolveVariable(props.source, data)
: resolveVariable('items', data);
if (!Array.isArray(arr) || !arr.length) {
const mockedData: any = this.buildMockData();
props.value = repeatArray(mockedData, 1).map((item, index) => ({
...item,
id: index + 1
}));
value = !Array.isArray(value) ? [] : value;
if (value.length < 5) {
const mockedData = value.length
? value[0]
: {
id: 666,
title: '假数据',
description: '假数据',
a: '假数据',
b: '假数据'
};
value = value.concat(
repeatArray(mockedData, 3).map((item, index) => ({
...item,
id: index + 1
}))
);
}
const {$schema, ...rest} = props;
value = value.slice(0, 4);
return {
...JSONPipeOut(rest),
$schema
...props,
value
};
}
@ -251,7 +283,7 @@ export class ListPlugin extends BasePlugin {
const {renderer, schema} = context;
if (
!schema.$$id &&
schema.$$editor?.renderer.name === 'crud' &&
['crud', 'crud2'].includes(schema.$$editor?.renderer.name) &&
renderer.name === 'list'
) {
return {

View File

@ -13,6 +13,7 @@ import {
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {VRenderer} from 'amis-editor-core';
import { getEventControlConfig } from '../util';
export class ListItemPlugin extends BasePlugin {
// 关联渲染器名字
@ -37,83 +38,78 @@ export class ListItemPlugin extends BasePlugin {
];
panelTitle = '列表项';
panelBody = getSchemaTpl('tabs', [
{
title: '基本',
body: [
// {
// children: (
// <Button
// size="sm"
// className="m-b-sm"
// level="info"
// block
// onClick={() => {
// this.pickChild('actions', 'actions', undefined, ['button']);
// }}
// >
// 新增按钮
// </Button>
// )
// },
// {
// children: (
// <div>
// <Button
// level="primary"
// size="sm"
// block
// onClick={this.handleAdd}
// >
// 新增内容
// </Button>
// </div>
// )
// },
// {
// type: 'divider'
// },
{
name: 'title',
type: 'input-text',
label: '标题',
descrition: '支持模板语法如: ${xxx}'
},
{
name: 'subTitle',
type: 'input-text',
label: '副标题',
descrition: '支持模板语法如: ${xxx}'
},
{
name: 'avatar',
type: 'input-text',
label: '图片地址',
descrition: '支持模板语法如: ${xxx}'
},
{
name: 'desc',
type: 'textarea',
label: '描述',
descrition: '支持模板语法如: ${xxx}'
}
]
},
{
title: '外观',
body: [
getSchemaTpl('className', {
name: 'avatarClassName',
label: '图片 CSS 类名',
pipeIn: defaultValue('thumb-sm avatar m-r')
}),
getSchemaTpl('className', {
name: 'titleClassName',
label: '标题 CSS 类名'
})
]
}
]);
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
const isCRUDBody = ['crud', 'crud2'].includes(context.schema.type);
return getSchemaTpl('tabs', [
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
{
name: 'title',
type: 'input-text',
label: '标题',
descrition: '支持模板语法如: ${xxx}'
},
{
name: 'subTitle',
type: 'input-text',
label: '副标题',
descrition: '支持模板语法如: ${xxx}'
},
{
name: 'avatar',
type: 'input-text',
label: '图片地址',
descrition: '支持模板语法如: ${xxx}'
},
{
name: 'desc',
type: 'textarea',
label: '描述',
descrition: '支持模板语法如: ${xxx}'
}
]
},
getSchemaTpl('status', {
isFormItem: false
})
])
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:classNames', {
isFormItem: false,
schema: [
getSchemaTpl('className', {
name: 'avatarClassName',
label: '图片'
}),
getSchemaTpl('className', {
name: 'titleClassName',
label: '标题'
})
]
})
])
},
{
title: '事件',
className: 'p-none',
body: [
getSchemaTpl('eventControl', {
name: 'onEvent',
...getEventControlConfig(this.manager, context)
})
]
}
]);
};
getRendererInfo({
renderer,

View File

@ -6,10 +6,11 @@ import {
BasePlugin,
BasicPanelItem,
BasicToolbarItem,
BuildPanelEventContext,
BuildPanelEventContext
} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {diff} from 'amis-editor-core';
import {SchemaCollection} from 'amis/lib/Schema';
export class ActionPlugin extends BasePlugin {
panelTitle = '按钮';
@ -350,7 +351,7 @@ export class ActionPlugin extends BasePlugin {
className: 'p-3',
body: schema
}
];
] as SchemaCollection;
};
buildEditorPanel(

View File

@ -40,11 +40,9 @@ export class TableCellPlugin extends BasePlugin {
type: 'input-text'
},
{
name: 'name',
type: 'input-text',
getSchemaTpl('formItemName', {
label: '绑定字段名'
},
}),
{
name: 'remark',

View File

@ -177,15 +177,15 @@ export class PagePlugin extends BasePlugin {
label: '数据初始化接口',
name: 'initApi',
sampleBuilder: (schema: any) => `{
"status": 0,
"msg": "",
"status": 0,
"msg": "",
data: {
// 示例数据
"id": 1,
"a": "sample"
}
}`
data: {
// 示例数据
"id": 1,
"a": "sample"
}
}`
}),
getSchemaTpl('initFetch'),

View File

@ -1,11 +1,14 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {ValidatorTag} from '../validator';
import {getEventControlConfig} from '../util';
import {RendererPluginEvent} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {
getSchemaTpl,
defaultValue,
tipedLabel,
BasePlugin,
RendererPluginEvent,
RegionConfig,
BaseEventContext,
registerEditorPlugin
} from 'amis-editor-core';
export class PaginationPlugin extends BasePlugin {
// 关联渲染器名字
@ -14,17 +17,16 @@ export class PaginationPlugin extends BasePlugin {
// 组件名称
name = '分页组件';
isBaseComponent = true;
disabledRendererPlugin = true;
isBaseComponent = false;
description = '分页组件,可以对列表进行分页展示,提高页面性能';
tags = ['容器'];
icon = 'fa fa-window-minimize';
// pluginIcon = 'pagination-plugin'; // 暂无新icon
baseLayoutLIst = [
lastLayoutSetting = ['pager'];
layoutOptions = [
{text: '总数', value: 'total', checked: false},
{text: '每页条数', value: 'perPage', checked: false},
{text: '分页', value: 'pager', checked: true},
{text: '跳转', value: 'go', checked: false}
{text: '跳转', value: 'go', checked: false}
];
scaffold = {
type: 'pagination',
@ -66,27 +68,52 @@ export class PaginationPlugin extends BasePlugin {
body: [
{
name: 'mode',
label: '分页类型',
label: '模式',
type: 'button-group-select',
size: 'sm',
pipeIn: defaultValue('normal'),
options: [
{
label: '普通',
label: '默认',
value: 'normal'
},
{
label: '简',
label: '简',
value: 'simple'
}
]
},
// {
// name: 'hasNext',
// label: '是否有下一页',
// mode: 'row',
// inputClassName: 'inline-flex justify-between flex-row-reverse',
// type: 'switch',
// visibleOn: 'data.mode === "simple"'
// },
// {
// name: 'activePage',
// label: tipedLabel('当前页', '支持使用 \\${xxx} 来获取变量'),
// type: 'input-text'
// },
// {
// name: 'lastPage',
// label: tipedLabel('最后页码', '支持使用 \\${xxx} 来获取变量'),
// type: 'input-text',
// visibleOn: 'data.mode === "normal"'
// },
// {
// name: 'total',
// label: tipedLabel('总条数', '支持使用 \\${xxx} 来获取变量'),
// type: 'input-text',
// visibleOn: 'data.mode === "normal"'
// },
getSchemaTpl('combo-container', {
name: 'layout',
type: 'combo',
label: tipedLabel(
'分页布局展示',
'选中表示渲染该项,可以拖拽排序调整显示的顺序'
'启用功能',
'选中表示启用该项,可以拖拽排序调整功能的顺序'
),
visibleOn: 'data.mode === "normal"',
mode: 'normal',
@ -112,52 +139,37 @@ export class PaginationPlugin extends BasePlugin {
}
],
pipeIn: (value: any) => {
let layoutList: string[] = [];
if (Array.isArray(value)) {
layoutList = value;
if (!value) {
value = this.lastLayoutSetting;
} else if (typeof value === 'string') {
layoutList = (value as string).split(',');
value = (value as string).split(',');
}
const layout = this.baseLayoutLIst.map(v => ({
return this.layoutOptions.map(v => ({
...v,
checked: layoutList.includes(v.value)
checked: value.includes(v.value)
}));
return layout;
},
pipeOut: (value: any[]) => {
this.baseLayoutLIst = [...value];
return value.filter(v => v.checked).map(v => v.value);
this.lastLayoutSetting = value
.filter(v => v.checked)
.map(v => v.value);
return this.lastLayoutSetting.concat();
}
}),
{
type: 'ae-formulaControl',
label: '是否有下一页',
name: 'hasNext',
visibleOn: 'data.mode === "simple"'
},
{
type: 'ae-formulaControl',
label: '当前页',
name: 'activePage'
},
{
type: 'ae-formulaControl',
label: '最后页码',
name: 'lastPage',
visibleOn: 'data.mode === "normal"'
},
{
type: 'ae-formulaControl',
label: '总条数',
name: 'total',
visibleOn: 'data.mode === "normal"'
},
// {
// name: 'showPerPage',
// label: '显示每页条数',
// mode: 'row',
// inputClassName: 'inline-flex justify-between flex-row-reverse',
// type: 'switch',
// visibleOn: 'data.mode === "normal"'
// },
getSchemaTpl('combo-container', {
name: 'perPageAvailable',
type: 'combo',
label: '每页条数选项',
visibleOn:
'data.mode === "normal" && data.layout?.includes("perPage")',
'data.mode === "normal" && data.layout && data.layout.includes("perPage")',
mode: 'normal',
multiple: true,
multiLine: false,
@ -167,6 +179,7 @@ export class PaginationPlugin extends BasePlugin {
editable: true,
minLength: 1,
tabsStyle: 'inline',
addButtonClassName: 'm-b-sm',
items: [
{
type: 'input-number',
@ -175,31 +188,35 @@ export class PaginationPlugin extends BasePlugin {
}
],
pipeIn: (value: any[]) => {
return value.map(v => ({value: v}));
return value?.map(v => ({value: v})) || [10];
},
pipeOut: (value: any[]) => {
return value.map(v => v.value);
}
}),
{
name: 'perPage',
type: 'input-text',
label: '默认每页条数',
visibleOn:
'data.mode === "normal" && data.layout?.includes("perPage")'
},
{
name: 'maxButton',
label: tipedLabel(
'最多按钮数',
'最多显示多少个分页按钮最小为5最大值为20'
),
type: 'input-number',
min: 5,
max: 20,
pipeOut: (value: any) => value || 5,
visibleOn: 'data.mode === "normal"'
}
})
// {
// name: 'perPage',
// type: 'input-text',
// label: '默认每页条数',
// visibleOn: 'data.mode === "normal"'
// },
// {
// name: 'maxButton',
// label: tipedLabel('分页按钮数量', '超过此数量,将会隐藏多余按钮'),
// type: 'input-number',
// min: 5,
// max: 20,
// pipeIn: defaultValue(5),
// visibleOn: 'data.mode === "normal"'
// }
// {
// name: 'showPageInput',
// label: '显示页面跳转',
// mode: 'row',
// inputClassName: 'inline-flex justify-between flex-row-reverse',
// type: 'switch',
// visibleOn: 'data.mode === "normal"'
// }
]
},
{

View File

@ -85,7 +85,8 @@ export class PanelPlugin extends BasePlugin {
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
const isForm = /(?:^|\/)form$/.test(context.path) || context?.schema?.type === 'form';
const isForm =
/(?:^|\/)form$/.test(context.path) || context?.schema?.type === 'form';
return [
getSchemaTpl('tabs', [
@ -102,7 +103,7 @@ export class PanelPlugin extends BasePlugin {
name: 'title',
type: 'input-text'
},
isForm
? null
: {
@ -124,7 +125,7 @@ export class PanelPlugin extends BasePlugin {
]
},
getSchemaTpl('status')
]),
])
]
},
{
@ -140,7 +141,7 @@ export class PanelPlugin extends BasePlugin {
label: '固定底部',
value: false
}),
getSchemaTpl('horizontal', {
visibleOn:
'(data.mode || data.$$formMode) == "horizontal" && data.$$mode == "form"'
@ -152,7 +153,9 @@ export class PanelPlugin extends BasePlugin {
title: '内容区域展示',
body: [
getSchemaTpl('subFormItemMode', {label: '表单展示模式'}),
getSchemaTpl('subFormHorizontalMode', {label: '表单水平占比'}),
getSchemaTpl('subFormHorizontalMode', {
label: '表单水平占比'
}),
getSchemaTpl('subFormHorizontal')
]
},
@ -211,28 +214,28 @@ export class PanelPlugin extends BasePlugin {
name: isForm ? 'panelClassName' : 'className',
pipeIn: defaultValue('Panel--default')
}),
getSchemaTpl('className', {
name: 'headerClassName',
label: '头部区域'
}),
getSchemaTpl('className', {
name: 'bodyClassName',
label: '内容区域'
}),
getSchemaTpl('className', {
name: 'footerClassName',
label: '底部区域'
}),
getSchemaTpl('className', {
name: 'actionsClassName',
label: '按钮外层'
})
],
},
]
}
])
]
}
@ -251,9 +254,10 @@ export class PanelPlugin extends BasePlugin {
if (
context.info.renderer.name === 'form' &&
schema.wrapWithPanel !== false &&
!context.selections.length
!context.selections.length &&
false
) {
/** Panel相关的配置融合到From中了 */
panels.push({
key: 'panel',
icon: 'fa fa-list-alt',

View File

@ -1,7 +1,7 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../validator';
import {getEventControlConfig} from '../util';

View File

@ -1,14 +1,19 @@
import {Button} from 'amis';
import React from 'react';
import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin, RegionConfig} from 'amis-editor-core';
import {getSchemaTpl} from 'amis-editor-core';
import {getEventControlConfig} from '../util';
import type {
import {
getSchemaTpl,
EditorManager,
RendererPluginAction,
RendererPluginEvent
RendererPluginEvent,
registerEditorPlugin,
BaseEventContext,
BasePlugin,
RegionConfig,
DSBuilderManager
} from 'amis-editor-core';
import {flattenDeep} from 'lodash';
export class ServicePlugin extends BasePlugin {
// 关联渲染器名字
@ -21,28 +26,30 @@ export class ServicePlugin extends BasePlugin {
description =
'功能性容器,可以用来加载数据或者加载渲染器配置。加载到的数据在容器可以使用。';
docLink = '/amis/zh-CN/components/service';
tags = ['功能'];
tags = ['功能', '数据容器'];
icon = 'fa fa-server';
pluginIcon = 'service-plugin';
scaffold = {
type: 'service',
body: []
};
previewSchema = {
type: 'service',
body: [
{
type: 'tpl',
tpl: '内容',
inline: false
tpl: '内容区域',
inline: false,
className: 'bg-light wrapper'
}
]
};
previewSchema = {
type: 'tpl',
tpl: '功能性组件,用于数据拉取。'
};
regions: Array<RegionConfig> = [
{
key: 'body',
label: '内容区'
label: '内容区域',
placeholder: '内容区域'
}
];
@ -79,7 +86,30 @@ export class ServicePlugin extends BasePlugin {
panelTitle = '服务';
dsBuilderMgr: DSBuilderManager;
constructor(manager: EditorManager) {
super(manager);
this.dsBuilderMgr = new DSBuilderManager('service', 'api');
}
panelBodyCreator = (context: BaseEventContext) => {
const dsTypeSelect = () =>
this.dsBuilderMgr.getDSSwitch({
onChange: (value: any, oldValue: any, model: any, form: any) => {
if (value !== oldValue) {
const data = form.data;
Object.keys(data).forEach(key => {
if (key.endsWith('Fields') || key.toLowerCase().endsWith('api')) {
form.deleteValueByName(key);
}
});
form.deleteValueByName('__fields');
}
return value;
}
});
return getSchemaTpl('tabs', [
{
title: '属性',
@ -89,87 +119,57 @@ export class ServicePlugin extends BasePlugin {
{
title: '基本',
body: [
getSchemaTpl('name'),
{
children: (
<Button
level="info"
size="sm"
className="m-b-sm"
block
onClick={() => {
// this.manager.showInsertPanel('body', context.id);
this.manager.showRendererPanel('');
}}
>
</Button>
)
}
]
},
{
title: '数据接口',
body: [
getSchemaTpl('apiControl', {
name: 'api',
label: '数据接口',
messageDesc:
'设置 service 默认提示信息,当 service 没有返回 msg 信息时有用,如果 service 返回携带了 msg 值,则还是以 service 返回为主'
}),
{
name: 'ws',
type: 'input-text',
label: 'WebSocket 实时更新接口'
},
/** initFetchOn可以通过api的sendOn属性控制 */
getSchemaTpl('switch', {
name: 'initFetch',
label: '数据接口初始加载',
dsTypeSelect(),
...this.dsBuilderMgr.collectFromBuilders(
(builder, builderFlag) => {
return {
type: 'container',
visibleOn: `this.dsType == null || this.dsType === '${builderFlag}'`,
body: flattenDeep([
builder.makeSourceSettingForm({
name: 'api',
label: '数据源',
feat: 'View',
inScaffold: false,
inCrud: false
})
])
};
}
),
getSchemaTpl('initFetch', {
visibleOn: 'this.api'
}),
{
name: 'interval',
label: '定时刷新间隔',
visibleOn: 'this.api',
type: 'input-number',
step: 500,
description: '设置后将自动定时刷新,单位 ms'
},
getSchemaTpl('switch', {
name: 'silentPolling',
label: '静默加载',
visibleOn: '!!data.interval',
description: '设置自动定时刷新是否显示加载动画'
getSchemaTpl('interval', {
visibleOn: 'this.api'
}),
{
name: 'stopAutoRefreshWhen',
label: '停止定时刷新检测',
type: 'input-text',
visibleOn: '!!data.interval',
description:
'定时刷新一旦设置会一直刷新,除非给出表达式,条件满足后则不刷新了。'
}
getSchemaTpl('silentPolling'),
getSchemaTpl('stopAutoRefreshWhen')
]
},
{
title: 'Schema接口',
title: '状态',
body: [getSchemaTpl('visible')]
},
{
title: '高级',
body: [
getSchemaTpl('apiControl', {
name: 'schemaApi',
label: '内容 Schema 接口'
label: 'Schema数据源'
}),
getSchemaTpl('switch', {
getSchemaTpl('initFetch', {
name: 'initFetchSchema',
label: 'Schema接口初始加载',
label: '是否Schema初始加载',
visibleOn: 'this.schemaApi'
})
]
},
{
title: '全局配置',
body: [
}),
{
type: 'divider'
},
getSchemaTpl('data'),
{
type: 'divider'
},
{
type: 'js-editor',
allowFullscreen: true,
@ -178,30 +178,14 @@ export class ServicePlugin extends BasePlugin {
description: '将会传递 data 和 setData 两个参数'
},
{
label: '默认消息信息',
type: 'combo',
name: 'messages',
multiLine: true,
description:
'设置 service 默认提示信息,当 service 没有返回 msg 信息时有用,如果 service 返回携带了 msg 值,则还是以 service 返回为主',
items: [
{
label: '获取成功',
type: 'input-text',
name: 'fetchSuccess'
},
{
label: '获取失败',
type: 'input-text',
name: 'fetchFailed'
}
]
type: 'divider'
},
{
name: 'ws',
type: 'input-text',
label: 'WebSocket 实时更新接口'
}
]
},
{
title: '状态',
body: [getSchemaTpl('ref'), getSchemaTpl('visible')]
}
])
]

View File

@ -0,0 +1,907 @@
import {resolveVariable} from 'amis';
import {setVariable} from 'amis-core';
import {
BasePlugin,
BaseEventContext,
PluginEvent,
RegionConfig,
RendererInfoResolveEventContext,
BasicRendererInfo,
PluginInterface,
InsertEventContext,
ScaffoldForm,
registerEditorPlugin,
defaultValue,
getSchemaTpl,
tipedLabel,
repeatArray,
mockValue,
EditorNodeType,
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {getEventControlConfig} from '../util';
import {SchemaObject} from 'amis/lib/Schema';
import {getArgsWrapper} from '../renderer/event-control/helper';
export class TableV2Plugin extends BasePlugin {
// 关联渲染器名字
rendererName = 'table-v2';
$schema = '/schemas/TableSchema.json';
// 组件名称
name = '表格V2';
isBaseComponent = true;
panelJustify = true;
description =
'用来展示表格数据,可以配置列信息,然后关联数据便能完成展示。支持嵌套、超级表头、列固定、表头固顶、合并单元格等等。当前组件需要配置数据源,不自带数据拉取,请优先使用 「CRUD」 组件。';
docLink = '/amis/zh-CN/components/table-v2';
icon = 'fa fa-table';
scaffold: SchemaObject = {
type: 'table-v2',
columns: [
{
title: '列信息',
key: 'a'
}
],
source: '$item'
};
regions: Array<RegionConfig> = [
{
key: 'columns',
label: '列集合',
renderMethod: 'renderTable',
preferTag: '展示',
dndMode: 'position-h'
}
];
previewSchema: any = {
type: 'table-v2',
className: 'text-left m-b-none',
items: [
{a: 1, b: 2, c: 9},
{a: 3, b: 4, c: 8},
{a: 5, b: 6, c: 7}
],
columns: [
{
title: 'A',
key: 'a'
},
{
title: 'B',
key: 'b'
}
]
};
scaffoldForm: ScaffoldForm = {
title: '快速构建表格',
body: [
{
name: 'columns',
type: 'combo',
multiple: true,
label: false,
addButtonText: '新增一列',
draggable: true,
items: [
{
type: 'input-text',
name: 'title',
placeholder: '标题'
},
{
type: 'input-text',
name: 'key',
placeholder: '绑定字段名'
},
{
type: 'select',
name: 'type',
placeholder: '类型',
value: 'text',
options: [
{
value: 'text',
label: '纯文本'
},
{
value: 'tpl',
label: '模板'
},
{
value: 'image',
label: '图片'
},
{
value: 'date',
label: '日期'
},
{
value: 'progress',
label: '进度'
},
{
value: 'status',
label: '状态'
},
{
value: 'mapping',
label: '映射'
},
{
value: 'container',
label: '容器'
},
{
value: 'operation',
label: '操作栏'
}
]
}
]
}
],
canRebuild: true
};
panelTitle = '表格';
events: RendererPluginEvent[] = [
{
eventName: 'selectedChange',
eventLabel: '选择表格项',
description: '手动选择表格项事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.selectedItems': {
type: 'array',
title: '已选择行'
},
'event.data.unSelectedItems': {
type: 'array',
title: '未选择行'
}
}
}
]
},
{
eventName: 'columnSort',
eventLabel: '列排序',
description: '点击列排序事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.orderBy': {
type: 'string',
title: '列排序列名'
},
'event.data.orderDir': {
type: 'string',
title: '列排序值'
}
}
}
]
},
{
eventName: 'columnFilter',
eventLabel: '列筛选',
description: '点击列筛选事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.filterName': {
type: 'string',
title: '列筛选列名'
},
'event.data.filterValue': {
type: 'string',
title: '列筛选值'
}
}
}
]
},
{
eventName: 'columnSearch',
eventLabel: '列搜索',
description: '点击列搜索事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.searchName': {
type: 'string',
title: '列搜索列名'
},
'event.data.searchValue': {
type: 'object',
title: '列搜索数据'
}
}
}
]
},
{
eventName: 'orderChange',
eventLabel: '行排序',
description: '手动拖拽行排序事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.movedItems': {
type: 'array',
title: '已排序数据'
}
}
}
]
},
{
eventName: 'columnToggled',
eventLabel: '列显示变化',
description: '点击自定义列事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.columns': {
type: 'array',
title: '当前显示的列配置数据'
}
}
}
]
},
{
eventName: 'rowClick',
eventLabel: '行单击',
description: '点击整行事件',
dataSchema: [
{
type: 'object',
properties: {
'event.data.rowItem': {
type: 'object',
title: '行点击数据'
}
}
}
]
}
];
actions: RendererPluginAction[] = [
{
actionType: 'select',
actionLabel: '设置选中项',
description: '设置表格的选中项',
schema: getArgsWrapper([
{
type: 'input-formula',
variables: '${variables}',
evalMode: false,
variableMode: 'tabs',
label: '选中项',
size: 'lg',
name: 'selected',
mode: 'horizontal'
}
])
},
{
actionType: 'selectAll',
actionLabel: '设置全部选中',
description: '设置表格全部项选中'
},
{
actionType: 'clearAll',
actionLabel: '清空选中项',
description: '清空表格所有选中项'
}
];
async buildDataSchemas(node: EditorNodeType, region?: EditorNodeType) {
const itemsSchema: any = {
$id: 'tableRow',
type: 'object',
properties: {}
};
const columns: EditorNodeType = node.children.find(
item => item.isRegion && item.region === 'columns'
);
for (let current of columns.children) {
const schema = current.schema;
if (schema && schema.key) {
itemsSchema.properties[schema.key] = current.info?.plugin
?.buildDataSchemas
? await current.info.plugin.buildDataSchemas(current, region)
: {
type: 'string',
title: schema.label || schema.title,
description: schema.description
};
}
}
const result: any = {
$id: 'table-v2',
type: 'object',
properties: {
items: {
type: 'array',
title: '表格数据',
items: itemsSchema
}
}
};
if (region?.region === 'columns') {
result.properties = {
...itemsSchema.properties,
...result.properties
};
}
return result;
}
async getAvailableContextFields(
scopeNode: EditorNodeType,
node: EditorNodeType,
region?: EditorNodeType
) {
// // 只有表单项组件可以使用表单组件的数据域
// if (
// scopeNode.parent?.type === 'crud2'
// ) {
// return scopeNode.parent.info.plugin.getAvailableContextFields?.(
// scopeNode.parent,
// node,
// region
// );
// }
}
panelBodyCreator = (context: BaseEventContext) => {
const isCRUDBody = ['crud', 'crud2'].includes(context.schema.type);
return getSchemaTpl('tabs', [
{
title: '属性',
body: [
getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
{
name: 'source',
type: 'input-text',
label: tipedLabel('数据源', '绑定当前环境变量。'),
hidden: isCRUDBody,
pipeIn: defaultValue('${items}')
},
getSchemaTpl('switch', {
name: 'title',
label: '显示标题',
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => {
if (value) {
return {
type: 'container',
body: [
{
type: 'tpl',
tpl: '表格标题',
inline: false,
style: {
fontSize: 14
}
}
]
};
}
return null;
}
}),
getSchemaTpl('switch', {
name: 'showHeader',
label: '显示表头',
value: true,
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => !!value
}),
getSchemaTpl('switch', {
visibleOn: 'this.showHeader !== false',
name: 'sticky',
label: '冻结表头',
pipeIn: defaultValue(false)
}),
getSchemaTpl('switch', {
name: 'footer',
label: '显示表尾',
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => {
if (value) {
return {
type: 'container',
body: [
{
type: 'tpl',
tpl: '表格尾部',
inline: false,
style: {
fontSize: 14
}
}
]
};
}
return null;
}
}),
{
name: 'scroll.y',
label: '内容高度',
type: 'button-group-select',
pipeIn: (v: any) => v != null,
pipeOut: (v: any) => (v ? '' : null),
options: [
{
label: '适配内容',
value: false
},
{
label: '固定',
value: true
}
]
},
{
type: 'input-group',
visibleOn: 'data.scroll && data.scroll.y !== null',
label: '高度值',
body: [
{
type: 'input-number',
name: 'scroll.y'
},
{
type: 'tpl',
addOnclassName: 'border-0 bg-none',
tpl: 'px'
}
]
},
{
name: 'scroll.x',
label: tipedLabel(
'内容宽度',
'当列内容过多,超出宽度时,可使用横向滚动方式查看数据。'
),
type: 'button-group-select',
pipeIn: (v: any) => v != null,
pipeOut: (v: any) => (v ? '' : null),
options: [
{
label: '适配内容',
value: false
},
{
label: '固定',
value: true
}
]
},
{
type: 'input-group',
visibleOn: 'data.scroll && data.scroll.x !== null',
name: 'scroll.x',
label: '宽度值',
body: [
{
type: 'input-number',
name: 'scroll.x'
},
{
type: 'tpl',
addOnclassName: 'border-0 bg-none',
tpl: 'px'
}
]
},
{
name: 'placeholder',
pipeIn: defaultValue('暂无数据'),
type: 'input-text',
label: '占位内容'
}
]
},
{
title: '列设置',
body: [
getSchemaTpl('switch', {
name: 'resizable',
label: tipedLabel('可调整列宽', '用户可通过拖拽调整列宽度'),
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => value
}),
isCRUDBody
? null
: {
type: 'ae-Switch-More',
mode: 'normal',
name: 'columnsTogglable',
hiddenOnDefault: true,
formType: 'extend',
label: tipedLabel(
'自定义显示列',
'自动即列数量大于10自动开启。'
),
pipeOut: (value: any) => {
if (value && value.columnsTogglable) {
return {columnsTogglable: {type: 'column-toggler'}};
}
return value;
},
form: {
body: [
{
mode: 'normal',
type: 'ae-columnControl'
}
]
}
}
]
},
{
title: '行设置',
body: [
{
name: 'lineHeight',
label: '行高度',
type: 'select',
placeholder: '请选择高度',
options: [
{label: '跟随内容', value: ''},
{label: '高', value: 'large'},
{label: '中', value: 'middle'}
],
clearable: false,
value: ''
},
isCRUDBody
? {
type: 'ae-Switch-More',
mode: 'normal',
name: 'rowSelection',
label: '可多选',
visibleOn: 'data.selectable',
hiddenOnDefault: true,
formType: 'extend',
form: {
body: [
{
label: '可选区域',
name: 'rowSelection.rowClick',
type: 'button-group-select',
value: false,
options: [
{
label: 'CheckBox',
value: false
},
{
label: '整行',
value: true
}
]
},
{
name: 'rowSelection.disableOn',
type: 'ae-formulaControl',
label: '行禁用条件'
},
{
name: 'rowSelection.selections',
label: '选择菜单项',
type: 'checkboxes',
joinValues: false,
inline: false,
itemClassName: 'text-sm',
options: [
{label: '全选', value: 'all'},
{label: '反选', value: 'invert'},
{label: '取消选择', value: 'none'},
{label: '选择奇数项', value: 'odd'},
{label: '选择偶数项', value: 'even'}
],
pipeIn(v: any) {
if (!v) {
return;
}
return v.map((item: any) => ({
label: item.text,
value: item.key
}));
},
pipeOut(v: any) {
if (!v) {
return;
}
return v.map((item: any) => ({
key: item.value,
text: item.label
}));
}
}
]
}
}
: null,
{
type: 'ae-Switch-More',
mode: 'normal',
name: 'expandable',
label: '可展开',
hiddenOnDefault: true,
formType: 'extend',
form: {
body: [
{
name: 'expandable.expandableOn',
visibleOn: 'data.expandable',
type: 'ae-formulaControl',
label: '行展开条件'
}
]
}
},
getSchemaTpl('switch', {
name: 'childrenColumnName',
label: '可嵌套',
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => (value ? 'children' : '')
}),
getSchemaTpl('switch', {
name: 'draggable',
label: '可拖拽',
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => value
})
]
},
{
title: '状态',
body: [
getSchemaTpl('hidden', {
label: '隐藏'
})
]
},
{
title: '高级',
body: [
getSchemaTpl('apiControl', {
label: '快速保存',
name: 'quickSaveApi'
}),
getSchemaTpl('apiControl', {
label: '快速保存单条',
name: 'quickSaveItemApi'
})
]
}
])
]
},
{
title: '外观',
body: [
getSchemaTpl('collapseGroup', [
{
title: '基本',
body: [
getSchemaTpl('switch', {
name: 'bordered',
label: '边框',
pipeIn: defaultValue(false)
}),
{
name: 'scroll.x',
type: 'input-number',
label: '横向滚动'
},
{
name: 'indentSize',
visibleOn: 'data.childrenColumnName',
type: 'input-number',
unitOptions: [{label: 'px', value: 'px'}],
label: '嵌套缩进'
},
{
name: 'rowSelection.columnWidth',
visibleOn: 'data.rowSelection',
type: 'input-number',
label: '选择列宽度',
description: '固定选择列的宽度'
},
{
name: 'expandable.columnWidth',
visibleOn: 'data.expandable',
type: 'input-number',
label: '展开列宽度',
description: '固定展开列的宽度'
}
]
},
getSchemaTpl('style:classNames', {
isFormItem: true,
schema: [
{
name: 'rowClassNameExpr',
type: 'ae-formulaControl',
label: '自定义行样式'
},
{
name: 'expandable.expandedRowClassNameExpr',
visibleOn: 'data.expandable',
type: 'ae-formulaControl',
label: '展开行样式'
}
]
})
])
]
},
{
title: '事件',
body: [
getSchemaTpl('eventControl', {
name: 'onEvent',
...getEventControlConfig(this.manager, context)
})
]
}
]);
};
filterProps(props: any) {
const arr = Array.isArray(props.value)
? props.value
: typeof props.source === 'string'
? resolveVariable(props.source, props.data)
: resolveVariable('items', props.data);
if (!Array.isArray(arr) || !arr.length) {
const mockedData: any = {};
if (Array.isArray(props.columns)) {
props.columns.forEach((column: any) => {
if (column.key) {
setVariable(mockedData, column.key, mockValue(column));
}
});
}
props.value = repeatArray(mockedData, 10).map((item, index) => ({
...item,
id: index + 1
}));
} else {
// 只取10条预览否则太多卡顿
props.value = arr.slice(0, 10);
}
// 如果设置了可展开 默认把第一行展开
if (props.expandable) {
if (typeof props.expandable === 'boolean') {
props.expandable = {};
}
if (!props.expandable.type) {
props.expandable.type = 'container';
props.expandable.body = [
{
type: 'tpl',
tpl: '展开行内容',
inline: false
}
];
}
props.expandable.keyField = 'id';
props.expandable.expandedRowKeys = [1];
}
return props;
}
// 为了能够自动注入数据。
getRendererInfo(
context: RendererInfoResolveEventContext
): BasicRendererInfo | void {
const plugin: PluginInterface = this;
const {schema, renderer} = context;
if (
!schema.$$id &&
['crud', 'crud2'].includes(schema.$$editor?.renderer.name) &&
renderer.name === 'table-v2'
) {
return {
...({id: schema.$$editor.id} as any),
name: plugin.name!,
regions: plugin.regions,
patchContainers: plugin.patchContainers,
vRendererConfig: plugin.vRendererConfig,
wrapperProps: plugin.wrapperProps,
wrapperResolve: plugin.wrapperResolve,
filterProps: plugin.filterProps,
$schema: plugin.$schema,
renderRenderer: plugin.renderRenderer
};
}
return super.getRendererInfo(context);
}
// 自动插入 label
beforeInsert(event: PluginEvent<InsertEventContext>) {
const context = event.context;
if (
(context.info.plugin === this ||
context.node.sameIdChild?.info.plugin === this) &&
context.region === 'columns'
) {
context.data = {
...context.data,
title: context.data.label ?? context.subRenderer?.name ?? '列名称'
};
}
}
}
registerEditorPlugin(TableV2Plugin);

View File

@ -0,0 +1,588 @@
import {Button, Icon} from 'amis';
import React from 'react';
import {FormItemProps, getVariable} from 'amis-core';
import {
BasePlugin,
BasicRendererInfo,
registerEditorPlugin,
RendererInfoResolveEventContext,
ReplaceEventContext,
PluginEvent,
AfterBuildPanelBody,
defaultValue,
getSchemaTpl,
tipedLabel,
DSField
} from 'amis-editor-core';
import {fromPairs} from 'lodash';
import {TabsSchema} from 'amis/lib/renderers/Tabs';
import {SchemaObject} from 'amis/lib/Schema';
import {remarkTpl} from '../component/BaseControl';
export class TableCellV2Plugin extends BasePlugin {
panelTitle = '列配置';
panelIcon = 'fa fa-columns';
afterBuildPanelBody(event: PluginEvent<AfterBuildPanelBody>) {
const {context, data} = event.context;
if (
!context.node.parent?.parent?.type ||
context.node.parent.parent.type !== 'table-v2'
) {
return;
}
// @ts-ignore
const base: Array<{
sameName?: string;
[propName: string]: any;
}> = [
context.node.info.plugin.withDataSource === false
? false
: {
sameName: context.info.renderer.isFormItem ? 'name' : undefined,
name: 'key',
type: 'ae-DataBindingControl',
label: '列字段',
onBindingChange(
field: DSField,
onBulkChange: (value: any) => void
) {
const schema = field?.resolveColumnSchema?.('List') || {
title: field.label
};
onBulkChange(schema);
}
},
{
sameName: context.info.renderer.isFormItem ? 'label' : undefined,
name: 'title',
label: '列标题',
type: 'input-text'
},
remarkTpl({
name: 'remark',
label: '标题提示',
labelRemark: '在标题旁展示提示'
}),
{
name: 'placeholder',
type: 'input-text',
label: tipedLabel('占位提示', '当没有值时用这个来替代展示。'),
value: '-'
}
].filter(Boolean);
const advanced = [
getSchemaTpl('switch', {
name: 'sorter',
label: tipedLabel(
'可排序',
'开启后可以根据当前列排序,接口类型将增加排序参数。'
)
}),
getSchemaTpl('switch', {
name: 'searchable',
label: '可搜索',
pipeIn: (value: any) => !!value
}),
{
visibleOn: 'data.searchable',
name: 'searchable',
asFormItem: true,
label: false,
children: ({value, onChange, data}: any) => {
if (value === true) {
value = {};
} else if (typeof value === 'undefined') {
value = getVariable(data, 'searchable');
}
const originMode = value.mode;
value = {
...value,
type: 'form',
mode: 'normal',
wrapWithPanel: false,
body: [
{
type: 'input-text',
name: data.key
}
]
};
delete value.mode;
// todo 多个快速编辑表单模式看来只能代码模式编辑了。
return (
<Button
className="w-full flex flex-col items-center"
onClick={() => {
this.manager.openSubEditor({
title: '配置列搜索类型',
value: value,
onChange: value =>
onChange(
{
...value,
mode: originMode
},
'searchable'
)
});
}}
>
<span className="inline-flex items-center">
<Icon icon="edit" className="mr-1 w-3" />
</span>
</Button>
);
}
},
{
name: 'quickEdit',
label: tipedLabel('快速编辑', '输入框左侧或右侧的附加挂件'),
type: 'ae-switch-more',
mode: 'normal',
formType: 'extend',
bulk: true,
defaultData: {
mode: 'popOver'
},
form: {
body: [
{
name: 'quickEdit.mode',
type: 'button-group-select',
label: '模式',
value: 'popOver',
options: [
{
label: '下拉',
value: 'popOver'
},
{
label: '内嵌',
value: 'inline'
}
]
},
getSchemaTpl('switch', {
name: 'quickEdit.saveImmediately',
label: tipedLabel(
'修改立即保存',
'开启后修改即提交,而不是批量提交。'
),
pipeIn: (value: any) => !!value
}),
{
name: 'quickEdit',
asFormItem: true,
label: false,
children: ({value, onBulkChange, name, data}: any) => {
if (value === true) {
value = {};
} else if (typeof value === 'undefined') {
value = getVariable(data, 'quickEdit');
}
const originMode = value?.mode || 'popOver';
value = {
...value,
type: 'form',
mode: 'normal',
wrapWithPanel: false,
body: [
{
type: 'input-text',
name: data.key
}
]
};
if (value.mode) {
delete value.mode;
}
// todo 多个快速编辑表单模式看来只能代码模式编辑了。
return (
<Button
className="w-full flex flex-col items-center"
onClick={() => {
this.manager.openSubEditor({
title: '配置快速编辑类型',
value: value,
onChange: value =>
onBulkChange({
[name]: {
...value,
mode: originMode
}
})
});
}}
>
<span className="inline-flex items-center">
<Icon icon="edit" className="mr-1 w-3" />
</span>
</Button>
);
}
}
]
}
},
{
name: 'popOver',
label: '查看更多',
type: 'ae-switch-more',
mode: 'normal',
formType: 'extend',
bulk: true,
form: {
body: [
{
name: 'popOver.mode',
label: '模式',
type: 'button-group-select',
pipeIn: defaultValue('popOver'),
options: [
{
label: '浮窗',
value: 'popOver'
},
{
label: '弹框',
value: 'dialog'
},
{
label: '抽屉',
value: 'drawer'
}
]
},
{
name: 'popOver.position',
label: '浮窗位置',
type: 'select',
visibleOn: 'data.popOver.mode === "popOver"',
pipeIn: defaultValue('center'),
options: [
{
label: '目标中部',
value: 'center'
},
{
label: '目标左上角',
value: 'left-top'
},
{
label: '目标右上角',
value: 'right-top'
},
{
label: '目标左下角',
value: 'left-bottom'
},
{
label: '目标右下角',
value: 'right-bottom'
},
{
label: '页面左上角',
value: 'fixed-left-top'
},
{
label: '页面右上角',
value: 'fixed-right-top'
},
{
label: '页面左下角',
value: 'fixed-left-bottom'
},
{
label: '页面右下角',
value: 'fixed-right-bottom'
}
]
},
{
name: 'popOver',
asFormItem: true,
label: false,
children: ({value, onBulkChange, name}: any) => {
value = {
type: 'panel',
title: '查看详情',
body: '内容详情',
...value
};
return (
<Button
className="w-full flex flex-col items-center"
onClick={() => {
this.manager.openSubEditor({
title: '配置查看更多展示内容',
value: value,
onChange: value =>
onBulkChange({
[name]: value
})
});
}}
>
<span className="inline-flex items-center">
<Icon icon="edit" className="mr-1 w-3" />
</span>
</Button>
);
}
}
]
}
},
{
name: 'copyable',
label: tipedLabel('复制内容', '默认为当前字段值,可定制。'),
type: 'ae-switch-more',
mode: 'normal',
formType: 'extend',
bulk: true,
defaultData: {},
form: {
body: [
{
name: 'copyable.content',
type: 'textarea',
placehoder: '默认为当前字段的值',
label: '内容模板'
}
]
}
},
{
name: 'rowSpanExpr',
type: 'ae-formulaControl',
label: '合并行'
},
{
name: 'colSpanExpr',
type: 'ae-formulaControl',
label: '合并列'
}
];
const baseStyle = [
getSchemaTpl('withUnit', {
name: 'width',
label: tipedLabel('列宽', '固定列的宽度,不推荐设置。'),
control: {
name: 'width',
type: 'input-number'
},
unit: 'px'
}),
{
name: 'fixed',
type: 'button-group-select',
label: '固定位置',
pipeIn: defaultValue(''),
pipeOut(value: any) {
if (!value) {
return undefined;
}
return value;
},
options: [
{
value: '',
label: '不固定'
},
{
value: 'left',
label: '左侧'
},
{
value: 'right',
label: '右侧'
}
]
},
getSchemaTpl('switch', {
name: 'toggled',
label: '自定义列时默认展示',
pipeIn: defaultValue(true)
}),
getSchemaTpl('switch', {
name: 'className',
label: '内容超出换行',
pipeIn: (value: any) =>
typeof value === 'string' && /\word\-break\b/.test(value),
pipeOut: (value: any, originValue: any) =>
(value ? 'word-break ' : '') +
(originValue || '').replace(/\bword\-break\b/g, '').trim()
})
];
// 之前的面板不是新的组件面板需要添加新的tab不能合并
if (Array.isArray(data)) {
if ((data[0] as SchemaObject).type === 'tabs') {
const body = data[0] as TabsSchema;
body.tabs.forEach((tab: any) => {
if (tab.title === '常规') {
tab.body.unshift(...base.concat(advanced));
}
if (tab.title === '外观') {
tab.body.unshift(...baseStyle);
}
});
} else {
console.error('错误的组件合并对象,面板过老无法处理,除非增加新面板');
}
return;
}
(data as TabsSchema).tabs?.forEach((tab: any) => {
if (tab.title === '属性') {
tab.body[0].body.forEach((collapse: any) => {
if (collapse.title === '基本') {
const appendItems = fromPairs(
base.map(item => [item.sameName ?? item.name, item])
);
const removeIndex: number[] = [];
collapse.body.forEach((item: any, index: number) => {
const key = item.name;
// 重复意义的配置用现在的表达文案替换一下
if (appendItems.hasOwnProperty(key)) {
removeIndex.push(index);
appendItems[key] = {
...item,
...appendItems[key]
};
return;
}
if (item.name === 'labelRemark') {
removeIndex.push(index);
}
});
removeIndex.reverse();
removeIndex.forEach(index => {
collapse.body.splice(index, 1);
});
collapse.body.unshift(...Object.values(appendItems));
}
});
const moreCollapse = getSchemaTpl('collapseGroup', [
{
title: '列',
body: advanced
}
]);
tab.body[0].body.splice(1, 0, ...moreCollapse.body);
// 让折叠器默认都展开
tab.body[0].activeKey.push(...moreCollapse.activeKey);
}
if (tab.title === '外观') {
const moreCollapse = getSchemaTpl('collapseGroup', [
{
title: '列',
body: baseStyle
}
]);
tab.body[0].body.splice(1, 0, ...moreCollapse.body);
// 让折叠器默认都展开
tab.body[0].activeKey.push(...moreCollapse.activeKey);
}
});
}
// filterProps(props: any) {
// props = JSONPipeOut(props, true);
// return props;
// }
getRendererInfo(
context: RendererInfoResolveEventContext
): BasicRendererInfo | void {
const {renderer, schema} = context;
if (renderer.name === 'cell-field') {
return {
name: schema.title ? `<${schema.title}>列` : '匿名列',
$schema: '/schemas/TableColumn.json',
multifactor: true,
wrapperResolve: (dom: HTMLDivElement) => {
// 固定这种结构 amis里改了 这里也得改
const parent = dom.parentElement?.parentElement;
const groupId = parent?.getAttribute('data-group-id');
const wrapper = dom.closest('table')!.parentElement?.parentElement;
return [].slice.call(
wrapper?.querySelectorAll(
`th[data-group-id="${groupId}"],
td[data-group-id="${groupId}"]`
)
);
}
// filterProps: this.filterProps
};
}
}
/*exchangeRenderer(id: string) {
this.manager.showReplacePanel(id, '展示');
}*/
beforeReplace(event: PluginEvent<ReplaceEventContext>) {
const context = event.context;
// 替换字段的时候保留 label 和 name 值。
if (context.info.plugin === this && context.data) {
context.data.title = context.data.title || context.schema.title;
context.data.key = context.data.key || context.schema.key;
}
}
}
registerEditorPlugin(TableCellV2Plugin);

View File

@ -16,10 +16,10 @@ import {VRenderer} from 'amis-editor-core';
import findIndex from 'lodash/findIndex';
import {RegionWrapper as Region} from 'amis-editor-core';
import {Tab} from 'amis';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../validator';
import {getEventControlConfig} from '../util';
import { getArgsWrapper } from '../renderer/event-control/helper';
import {getArgsWrapper} from '../renderer/event-control/helper';
export class TabsPlugin extends BasePlugin {
// 关联渲染器名字

View File

@ -5,7 +5,7 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
export class TooltipWrapperPlugin extends BasePlugin {
rendererName = 'tooltip-wrapper';

View File

@ -1,7 +1,7 @@
import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin} from 'amis-editor-core';
import {defaultValue, getSchemaTpl, setSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {ValidatorTag} from '../validator';
import {getEventControlConfig} from '../util';
@ -146,7 +146,7 @@ export class TplPlugin extends BasePlugin {
),
name: 'inline',
pipeIn: defaultValue(true),
hiddenOn:'data.wrapperComponent !== ""'
hiddenOn: 'data.wrapperComponent !== ""'
}),
getSchemaTpl('tpl:content'),
getSchemaTpl('tpl:rich-text')

View File

@ -5,7 +5,7 @@ import {
getSchemaTpl
} from 'amis-editor-core';
import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
// 需要一个示例,不然默认的没有高度都无法选中
class WebComponentDemo extends HTMLElement {
@ -67,7 +67,7 @@ export class WebComponentPlugin extends BasePlugin {
mode: 'normal',
name: 'props',
label: '属性'
}),
})
]
}
])

View File

@ -1,26 +1,43 @@
import React from 'react';
import merge from 'lodash/merge';
import isEqual from 'lodash/isEqual';
import cloneDeep from 'lodash/cloneDeep';
import cx from 'classnames';
import {FormItem, InputBox, Icon} from 'amis';
import {} from 'amis-ui';
import {PickerContainer} from 'amis';
import {FormItem, Icon} from 'amis';
import {Input, PickerContainer, Spinner} from 'amis-ui';
import {getEnv} from 'mobx-state-tree';
import {normalizeApi, isEffectiveApi, isApiOutdated} from 'amis-core';
import {isObject, autobind, createObject, anyChanged} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {isObject, autobind, createObject, tipedLabel} from 'amis-editor-core';
import type {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
import type {Api} from 'amis/lib/types';
import type {FormControlProps} from 'amis-core';
import type {ActionSchema} from 'amis/lib/renderers/Action';
export type ApiObject = Api & {
messages?: Record<
| 'fetchSuccess'
| 'fetchFailed'
| 'saveOrderSuccess'
| 'saveOrderFailed'
| 'quickSaveSuccess'
| 'quickSaveFailed',
string
>;
};
export interface APIControlProps extends FormControlProps {
name?: string;
label?: string;
value?: any;
/**
* debug模式
*/
debug?: boolean;
/**
*
*/
@ -76,6 +93,36 @@ export interface APIControlProps extends FormControlProps {
*/
pickerHeaderClassName?: string;
/**
* TabsPanel
*/
onlyTabs?: boolean;
/**
*
*/
enableHighlight?: boolean;
/**
* Picker选项的label字段
*/
labelField?: string;
/**
*
*/
searchField?: string;
/**
*
*/
searchType?: string;
/**
* CSS类名
*/
footerClassName?: string;
/**
* Picker面板确认
*/
@ -103,38 +150,52 @@ export interface APIControlState {
apiStr: string;
selectedItem?: any[];
schema?: SchemaCollection;
loading: boolean;
}
export default class APIControl extends React.Component<
APIControlProps,
APIControlState
> {
static defaultProps: Pick<APIControlProps, 'pickerBtnSchema'> = {
input?: HTMLInputElement;
static defaultProps: Pick<
APIControlProps,
'pickerBtnSchema' | 'labelField' | 'searchType'
> = {
pickerBtnSchema: {
type: 'button',
level: 'link',
size: 'sm',
label: '点击选择'
}
size: 'sm'
},
labelField: 'label',
searchType: 'key'
};
constructor(props: APIControlProps) {
super(props);
this.state = {
apiStr: this.transformApi2Str(props.value),
selectedItem: [],
schema: props.pickerSchema
schema: props.pickerSchema,
loading: false
};
}
componentDidMount() {
this.updatePickerOptions();
}
componentDidUpdate(prevProps: APIControlProps) {
const props = this.props;
if (prevProps.value !== props.value) {
if (!isEqual(prevProps?.value, props?.value)) {
this.setState({apiStr: this.transformApi2Str(props.value)});
this.updatePickerOptions();
}
if (anyChanged(['enablePickerMode', 'pickerSchema'], prevProps, props)) {
if (!isEqual(prevProps?.enablePickerMode, props?.enablePickerMode)) {
this.setState({schema: props.pickerSchema});
}
@ -150,23 +211,52 @@ export default class APIControl extends React.Component<
}
}
/**
* API详情list接口是分页的
*/
async updatePickerOptions() {
const apiObj = normalizeApi(this.props.value);
if (apiObj?.url?.startsWith('api://')) {
this.setState({loading: true});
const keyword = apiObj.url.replace('api://', '');
try {
await this.fetchOptions(keyword);
} catch (error) {}
}
this.setState({loading: false});
}
transformApi2Str(value: any) {
const api = normalizeApi(value);
return api.url ? `${api.method ? `${api.method}:` : ''}${api.url}` : '';
return api.url
? `${
api.method &&
api.method.toLowerCase() !==
'get' /** 默认为GET请求直接隐藏掉前缀为了呈现更多信息 */
? `${api.method}:`
: ''
}${api.url}`
: '';
}
async fetchOptions() {
const {value, data, env} = this.props;
async fetchOptions(keyword?: string) {
const {value, data, env, searchField, searchType} = this.props;
let {pickerSource} = this.props;
const apiObj = normalizeApi(value);
const apiKey = apiObj?.url.split('api://')?.[1];
if (!pickerSource) {
if (!pickerSource || !apiObj?.url) {
return;
}
const ctx = createObject(data, {value, op: 'loadOptions'});
const apiKey = apiObj?.url?.split('api://')?.[1];
const ctx = createObject(data, {
value,
op: 'loadOptions',
...(keyword && searchField ? {[searchField]: keyword, searchType} : {})
});
const schemaFilter = getEnv((window as any).editorStore).schemaFilter;
// 基于爱速搭的规则转换一下
@ -177,6 +267,7 @@ export default class APIControl extends React.Component<
if (isEffectiveApi(pickerSource, ctx)) {
const res = await env.fetcher(pickerSource, ctx);
const items: any[] = res.data?.items || res?.data?.rows;
if (items.length) {
const selectedItem = items.find(item => item.key === apiKey);
@ -186,7 +277,40 @@ export default class APIControl extends React.Component<
}
@autobind
handleSubmit(values: SchemaApi, action: any) {
inputRef(ref: any) {
this.input = ref;
}
focus() {
if (!this.input) {
return;
}
this.input.focus();
}
@autobind
clearPickerValue() {
const {onChange} = this.props;
this.setState(
{apiStr: this.transformApi2Str(undefined), selectedItem: []},
() => {
onChange?.(undefined);
this.focus();
}
);
}
@autobind
handleSimpleInputChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.currentTarget.value;
this.handleSubmit(value, 'input');
}
@autobind
handleSubmit(values: SchemaApi, action?: 'input' | 'picker-submit') {
const {onChange, value} = this.props;
let api: Api = values;
@ -241,7 +365,7 @@ export default class APIControl extends React.Component<
}
renderHeader() {
const {render, actions, enablePickerMode} = this.props;
const {render, actions} = this.props;
const actionsDom =
Array.isArray(actions) && actions.length > 0
@ -253,9 +377,9 @@ export default class APIControl extends React.Component<
})
: null;
return actionsDom || enablePickerMode ? (
return actionsDom ? (
<header className="ae-ApiControl-header" key="header">
{enablePickerMode ? this.renderPickerSchema() : actionsDom}
{actionsDom}
</header>
) : null;
}
@ -280,14 +404,19 @@ export default class APIControl extends React.Component<
return (
<PickerContainer
title={pickerTitle}
value={selectedItem}
headerClassName={cx(pickerHeaderClassName, 'font-bold')}
onConfirm={this.handlePickerConfirm}
onCancel={this.handlePickerClose}
size={pickerSize}
bodyRender={({value, onClose, onChange, setState, ...states}) => {
bodyRender={({
onChange,
setState
}: {
onChange: (value: any) => void;
setState: (state: any) => void;
}) => {
return render('api-control-picker', schema!, {
data: {[pickerName]: selectedItem},
value: selectedItem,
onSelect: (items: Array<any>) => {
setState({selectedItem: items});
onChange(this.normalizeValue(items, onPickerSelect));
@ -295,32 +424,50 @@ export default class APIControl extends React.Component<
});
}}
>
{({onClick, isOpened}) =>
render('picker-action', pickerBtnSchema!, {
onClick: async (e: React.MouseEvent<any>) => {
if (!isOpened && enablePickerMode) {
try {
await this.fetchOptions();
} catch {}
}
{({
onClick,
isOpened
}: {
onClick: (e: React.MouseEvent) => void;
isOpened: boolean;
}) =>
render(
'picker-action',
{
icon: (
<Icon icon="picker-icon" className="icon ae-ApiControl-icon" />
),
...pickerBtnSchema!,
className: cx(
'ae-ApiControl-PickerBtn',
pickerBtnSchema?.className
)
},
{
onClick: async (e: React.MouseEvent<any>) => {
if (!isOpened && enablePickerMode) {
try {
await this.fetchOptions();
} catch {}
}
onClick(e);
onClick(e);
}
}
})
)
}
</PickerContainer>
);
}
renderApiDialog() {
const {messageDesc} = this.props;
return {
label: '',
type: 'action',
acitonType: 'dialog',
size: 'sm',
icon: <Icon icon="api" />,
icon: <Icon icon="setting" className="icon ae-ApiControl-icon" />,
className: 'ae-ApiControl-setting-button',
actionType: 'dialog',
dialog: {
title: '高级设置',
@ -331,12 +478,14 @@ export default class APIControl extends React.Component<
closeOnEsc: true,
closeOnOutside: false,
showCloseButton: true,
body: [this.renderApiConfigTabs(messageDesc)]
body: [this.renderApiConfigTabs()]
}
};
}
renderApiConfigTabs(messageDesc?: string, submitOnChange: boolean = false) {
renderApiConfigTabs(submitOnChange: boolean = false) {
const {messageDesc, debug = false} = this.props;
return {
type: 'form',
className: 'ae-ApiControl-form',
@ -344,6 +493,7 @@ export default class APIControl extends React.Component<
submitOnChange,
wrapWithPanel: false,
onSubmit: this.handleSubmit,
debug,
body: [
{
type: 'tabs',
@ -462,75 +612,6 @@ export default class APIControl extends React.Component<
mode: 'horizontal',
description: '默认数据为追加方式,开启后完全替换当前数据'
},
{
label: tipedLabel(
'初始加载',
'当配置初始化接口后,组件初始就会拉取接口数据,可以通过以下配置修改'
),
type: 'group',
visibleOn: 'this.initApi',
mode: 'horizontal',
direction: 'vertical',
// labelRemark: {
// trigger: 'hover',
// rootClose: true,
// content:
// '当配置初始化接口后,组件初始就会拉取接口数据,可以通过以下配置修改',
// placement: 'top'
// },
body: [
{
name: 'initFetch',
type: 'radios',
inline: true,
mode: 'normal',
renderLabel: false,
onChange: () => {
document.getElementsByClassName(
'ae-Settings-content'
)[0].scrollTop = 0;
},
// pipeIn: (value:any) => typeof value === 'boolean' ? value : '1'
options: [
{
label: '是',
value: true
},
{
label: '否',
value: false
},
{
label: '表达式',
value: ''
}
]
},
{
name: 'initFetchOn',
autoComplete: false,
visibleOn: 'typeof this.initFetch !== "boolean"',
type: 'input-text',
mode: 'normal',
size: 'lg',
renderLabel: false,
placeholder: '如this.id 表示有 id 值时初始加载',
className: 'm-t-n-sm'
}
]
},
{
label: '定时刷新',
name: 'interval',
type: 'switch',
mode: 'horizontal',
visibleOn: 'data.initApi',
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => (value ? 3000 : undefined)
},
{
label: '',
name: 'interval',
@ -563,13 +644,6 @@ export default class APIControl extends React.Component<
size: 'lg',
visibleOn: '!!data.interval',
placeholder: '停止定时刷新检测表达式'
// labelRemark: {
// trigger: 'hover',
// rootClose: true,
// content:
// '定时刷新一旦设置会一直刷新,除非给出表达式,条件满足后则停止刷新',
// placement: 'top'
// }
}
]
},
@ -589,13 +663,6 @@ export default class APIControl extends React.Component<
),
name: 'data',
mode: 'row',
// labelRemark: {
// trigger: 'hover',
// rootClose: true,
// content:
// '当没开启数据映射时,发送 API 的时候会发送尽可能多的数据,如果你想自己控制发送的数据,或者需要额外的数据处理,请开启此选项',
// placement: 'top'
// },
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => (value ? {'&': '$$'} : null)
},
@ -656,7 +723,7 @@ export default class APIControl extends React.Component<
{
placeholder: 'Value',
type: 'input-text',
type: 'ae-DataPickerControl',
name: 'value'
}
]
@ -669,13 +736,6 @@ export default class APIControl extends React.Component<
),
name: 'responseData',
mode: 'row',
// labelRemark: {
// trigger: 'hover',
// rootClose: true,
// content:
// '如果需要对返回结果做额外的数据处理,请开启此选项',
// placement: 'top'
// },
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => (value ? {'&': '$$'} : null)
},
@ -772,18 +832,11 @@ export default class APIControl extends React.Component<
type: 'switch',
label: tipedLabel(
'请求头',
'可以配置headers对象添加自定义请求头'
'可以配置<code>headers</code>对象,添加自定义请求头'
),
name: 'headers',
mode: 'row',
className: 'm-b-xs',
// labelRemark: {
// trigger: 'hover',
// rootClose: true,
// content:
// '可以配置<code>headers</code>对象,添加自定义请求头',
// placement: 'top'
// },
pipeIn: (value: any) => !!value,
pipeOut: (value: any) => (value ? {'': ''} : null)
},
@ -915,42 +968,103 @@ export default class APIControl extends React.Component<
}
]
};
// return
}
render() {
const {
render,
className,
footerClassName,
classPrefix,
label,
labelRemark,
value,
footer,
border = false,
messageDesc
onlyTabs = false,
messageDesc,
enablePickerMode,
disabled,
mode,
enableHighlight,
labelField = 'label',
useMobileUI,
popOverContainer,
env
} = this.props;
let {apiStr, selectedItem, loading} = this.state;
selectedItem =
Array.isArray(selectedItem) && selectedItem.length !== 0
? selectedItem
: [];
const highlightLabel = selectedItem?.[0]?.[labelField] ?? '';
return (
<div className={cx('ae-ApiControl', className, {border})}>
{this.renderHeader()}
<>
<div className={cx('ae-ApiControl', className, {border})}>
{onlyTabs ? (
render('api-control-tabs', this.renderApiConfigTabs(true), {
data: normalizeApi(value)
})
) : (
<>
{this.renderHeader()}
<div className="ae-ApiControl-content" key="content">
<InputBox
className="ae-ApiControl-input m-b-none"
value={this.state.apiStr}
clearable={false}
placeholder="http://"
onChange={(value: string) => this.handleSubmit(value, 'input')}
/>
{render('api-control-dialog', this.renderApiDialog(), {
data: normalizeApi(value)
})}
<div className="ae-ApiControl-content" key="content">
<div className={cx('ae-ApiControl-input')}>
{enableHighlight && highlightLabel ? (
<div className={cx('ae-ApiControl-highlight')}>
{loading ? (
<Spinner
show
icon="reload"
size="sm"
spinnerClassName={cx('Select-spinner')}
/>
) : (
<span className={cx('ae-ApiControl-highlight-tag')}>
<span>{highlightLabel}</span>
<a
onClick={this.clearPickerValue}
className={cx('Modal-close')}
>
<Icon
icon="close"
className={cx(
'icon',
'ae-ApiControl-highlight-close'
)}
/>
</a>
</span>
)}
</div>
) : (
<Input
ref={this.inputRef}
value={apiStr}
type="text"
disabled={disabled}
placeholder="http://"
onChange={this.handleSimpleInputChange}
/>
)}
{enablePickerMode ? this.renderPickerSchema() : null}
</div>
{render('api-control-dialog', this.renderApiDialog(), {
data: normalizeApi(value)
})}
</div>
</>
)}
</div>
{Array.isArray(footer) && footer.length !== 0 ? (
<footer className="mt-3" key="footer">
<footer className={cx('mt-3', footerClassName)} key="footer">
{render('api-control-footer', footer)}
</footer>
) : null}
</div>
</>
);
}
}

View File

@ -9,7 +9,7 @@ import {getEnv} from 'mobx-state-tree';
import {normalizeApi, isEffectiveApi, isApiOutdated} from 'amis-core';
import {autobind, isObject, anyChanged, createObject} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import type {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
import type {Api} from 'amis/lib/types';

View File

@ -0,0 +1,73 @@
/**
* @file
*/
import React from 'react';
import cx from 'classnames';
import findIndex from 'lodash/findIndex';
import {
FormControlProps,
FormItem,
TreeSelection
} from 'amis';
export interface ColumnControlProps extends FormControlProps {
className?: string;
}
export interface ColumnsControlState {
columns: Array<any>;
}
export default class ColumnControl extends React.Component<
ColumnControlProps,
ColumnsControlState
> {
constructor(props: any) {
super(props);
this.state = {
columns: this.transformColumns(props)
};
}
transformColumns(props: any) {
const {data} = props;
return data.columns;
}
onChange(value: Array<any>) {
const {onBulkChange} = this.props;
const columns = this.state.columns.map(c => ({
...c,
toggled: findIndex(value, (v: any) => v.value === c.key) > -1
}));
this.setState({columns});
onBulkChange && onBulkChange({columns});
}
render() {
const {columns} = this.state;
const options = columns ? columns.map(c => ({value: c.key, label: c.title})) : [];
const value = columns ? columns.filter(c => c.toggled !== false).map(c => ({value: c.key, label: c.title})) : []
return (
<div className={cx('ae-ColumnControl')}>
<TreeSelection
options={options}
value={value}
onChange={(v: Array<any>) => this.onChange(v)}>
</TreeSelection>
</div>
);
}
}
@FormItem({
type: 'ae-columnControl',
renderLabel: false
})
export class ColumnControlRenderer extends ColumnControl {}

View File

@ -1,37 +1,244 @@
import {Icon, InputBox, SchemaVariableListPicker, FormItem} from 'amis';
import type {FormControlProps} from 'amis-core';
import {
Icon,
InputBox,
SchemaVariableListPicker,
FormItem,
SearchBox,
CollapseGroup,
PickerContainer,
Collapse,
Checkbox,
Spinner
} from 'amis';
import {FormControlProps, generateIcon} from 'amis-core';
import {debounce, remove} from 'lodash';
import React from 'react';
import {autobind} from 'amis-editor-core';
import {
EditorManager,
EditorNodeType,
autobind,
DSField,
DSFieldGroup
} from 'amis-editor-core';
import {matchSorter} from 'match-sorter';
export interface DataBindingProps extends FormControlProps {
node: EditorNodeType;
manager: EditorManager;
samePredicate?: (a: any, b: any) => boolean;
onBindingChange?: (
value: DSField,
onBulkChange: (value: any) => void
) => void;
}
export interface DataBindingState {
filteredFields: DSFieldGroup[];
sourceFields: DSFieldGroup[];
loading: boolean;
hint: string | void;
}
export class DataBindingControl extends React.Component<
DataBindingProps,
DataBindingState
> {
constructor(props: DataBindingProps) {
super(props);
this.handleSearchDebounced = debounce(this.handleSearch, 250, {
trailing: true,
leading: false
});
this.state = {
filteredFields: [],
sourceFields: [],
loading: false,
hint: undefined
};
}
handleSearchDebounced;
export class DataBindingControl extends React.Component<FormControlProps> {
@autobind
handleConfirm(result: {value: string; schema: any}) {
const {manager, data} = this.props;
handleConfirm(result: {label: string; value: string}) {
const {manager, data, onChange, onBulkChange, onBindingChange} = this.props;
if (result?.value) {
this.props.onChange(`${result.value}`);
onChange(result.value);
onBulkChange && onBindingChange?.(result, onBulkChange);
manager.config?.dataBindingChange?.(result.value, data, manager);
}
}
@autobind
async handlePickerOpen() {
handlePickerOpen() {
const {manager, node} = this.props;
const withSuper = manager.config?.withSuperDataSchema ?? false;
const schemas = await manager.getContextSchemas(node.info.id, !withSuper);
return {schemas};
// 如果node没变化就不再重复加载
if (this.state.sourceFields.length) {
return;
}
this.setState({
sourceFields: [],
filteredFields: [],
loading: true
});
manager
.getAvailableContextFields(node)
.then(groupedFields => {
this.setState({
sourceFields: groupedFields || [],
filteredFields: groupedFields || [],
loading: false,
hint: groupedFields ? undefined : '暂无可绑定字段'
});
})
.catch(() => {
this.setState({
loading: false,
hint: '加载可用字段失败,请联系管理员!'
});
});
}
render() {
const {classnames: cx, value, onChange, disabled} = this.props;
@autobind
async handleSearch(keywords: string) {
this.setState({
filteredFields: matchSorter(this.state.sourceFields, keywords, {
keys: ['label', 'value', 'children']
})
});
}
@autobind
handleSelect() {}
render() {
const {
className,
classnames: cx,
value,
onChange,
samePredicate = (a, b) => a.value === b.value,
multiple,
disabled
} = this.props;
const {filteredFields, loading, hint} = this.state;
return (
<SchemaVariableListPicker
<PickerContainer
onPickerOpen={this.handlePickerOpen}
className={className}
title="绑定字段"
bodyRender={({value, isOpened, onChange}) => {
if (!isOpened) {
return null;
}
if (loading) {
return (
<Spinner
show
icon="reload"
spinnerClassName={cx('ae-DataBindingList-spinner')}
/>
);
}
if (hint) {
return <p className={cx('ae-DataBindingList-hint')}>{hint}</p>;
}
return (
<div className={cx('ae-DataBindingList')}>
<div className={cx('ae-DataBindingList-searchBox')}>
<SearchBox
mini={false}
placeholder={'输入名称搜索'}
onSearch={this.handleSearchDebounced}
/>
</div>
<div className={cx('ae-DataBindingList-body')}>
<CollapseGroup
className={cx('ae-DataBindingList-collapseGroup')}
defaultActiveKey={filteredFields.map(item => item.value!)}
expandIcon={
generateIcon(
cx,
'fa fa-chevron-right ae-DataBindingList-expandIcon',
'Icon'
)!
}
expandIconPosition="right"
// accordion={true}
>
{filteredFields.map(item => (
<Collapse
className={cx('ae-DataBindingList-collapse')}
headingClassName={cx('ae-DataBindingList-collapse-title')}
bodyClassName={cx('ae-DataBindingList-collapse-body')}
propKey={item.value}
key={item.value}
header={item.label}
>
{Array.isArray(item.children) &&
item.children.length > 0 ? (
item.children.map((childItem: DSField) => {
if (multiple) {
const checked = !!value.find((i: any) =>
samePredicate(i, childItem)
);
return (
<div
key={childItem.value}
className={cx('ae-DataBindingList-item')}
onClick={() =>
onChange(
checked
? value.concat(childItem)
: remove(value, childItem)
)
}
>
<Checkbox value={checked}>
{childItem.label}
</Checkbox>
</div>
);
}
return (
<div
className={cx('ae-DataBindingList-item', {
'is-active':
value && childItem.value === value.value
})}
onClick={() => onChange(childItem)}
key={childItem.value}
>
{childItem.label}
</div>
);
})
) : (
<p className={cx('ae-DataBindingList-hint')}>
</p>
)}
</Collapse>
))}
</CollapseGroup>
</div>
</div>
);
}}
value={value}
onConfirm={this.handleConfirm}
title="绑定变量"
>
{({onClick, isOpened, setState}) => {
{({onClick}: {onClick: (e: React.MouseEvent) => void}) => {
return (
<InputBox
className="ae-InputVariable"
@ -50,7 +257,7 @@ export class DataBindingControl extends React.Component<FormControlProps> {
</InputBox>
);
}}
</SchemaVariableListPicker>
</PickerContainer>
);
}
}

View File

@ -47,7 +47,9 @@ class DataPickerControl extends React.Component<FormControlProps> {
onChange={() => {}}
header={''}
>
{({onClick, isOpened, setState}) => {
{({onClick}: {
onClick: (e: React.MouseEvent) => void;
}) => {
return (
<InputBox
className="ae-InputVariable"

View File

@ -0,0 +1,268 @@
/**
* @file
*/
import React from 'react';
import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import {FormItem, Button, Icon, FormControlProps, autobind} from 'amis';
import {clone, remove} from 'lodash';
import {GoConfigControl} from './GoConfigControl';
import Sortable from 'sortablejs';
const klass = 'ae-FeatureControl';
export type FeatureOption = {
label: string;
value: any;
remove?: (data: any) => void;
/** 提供该字段表示删除后还可以再新增回来 */
add?: (data: any) => void;
isActive?: (data: any) => boolean;
};
interface FeatureControlProps extends FormControlProps {
className?: string;
removable?: boolean;
addable?: boolean;
addText?: string;
sortable?: boolean;
features: Array<FeatureOption> | ((schema: any) => Array<FeatureOption>);
goFeatureComp?: (item: FeatureOption) => string; // 去子组件
onSort?: (value: FeatureOption[]) => void;
}
interface FeatureControlState {
/**
*
*/
inUseFeat: FeatureOption[];
/**
*
*/
unUseFeat: FeatureOption[];
}
export default class FeatureControl extends React.Component<
FeatureControlProps,
FeatureControlState
> {
constructor(props: FeatureControlProps) {
super(props);
this.state = FeatureControl.initState(props.data, props.features);
}
static getDerivedStateFromProps(
nextProps: FeatureControlProps,
preState: FeatureControlState
) {
return FeatureControl.initState(
nextProps.data,
nextProps.features,
preState.inUseFeat,
preState.unUseFeat
);
}
static initState(
data: any,
features: FeatureOption[] | ((schema: any) => Array<FeatureOption>),
lastInUseFeat?: FeatureOption[],
lastUnUseFeat?: FeatureOption[]
) {
const inUseFeat: FeatureOption[] = [];
const unUseFeat: FeatureOption[] = [];
if (!Array.isArray(features)) {
features = features(data);
}
features.forEach(item => {
if (item.isActive == null || item.isActive?.(data)) {
inUseFeat.push(item);
} else if (item.add) {
unUseFeat.push(item);
}
});
return {
inUseFeat,
unUseFeat
};
}
@autobind
handleRemove(item: FeatureOption, index: number) {
const {removeFeature, data, onBulkChange} = this.props;
const {inUseFeat, unUseFeat} = this.state;
item.remove?.(data);
removeFeature?.(item, data);
onBulkChange?.(data);
remove(inUseFeat, item);
item.add && unUseFeat.push(item);
this.setState({inUseFeat, unUseFeat});
}
@autobind
handleAdd(item: any) {
const {addFeature, data, onBulkChange} = this.props;
const {inUseFeat, unUseFeat} = this.state;
inUseFeat.push(item);
remove(unUseFeat, item);
const schema = clone(data);
item.add?.(schema);
addFeature?.(item, schema);
onBulkChange?.(schema);
}
sortable?: Sortable;
drag?: HTMLElement | null;
@autobind
dragRef(ref: any) {
const {sortable} = this.props;
if (!sortable) {
return;
}
if (!this.drag && ref) {
this.initDragging();
} else if (this.drag && !ref) {
this.destroyDragging();
}
this.drag = ref;
}
/**
*
*/
initDragging() {
const dom = findDOMNode(this) as HTMLElement;
this.sortable = new Sortable(
dom.querySelector(`.${klass}-features`) as HTMLElement,
{
group: 'FeatureControlGroup',
animation: 150,
handle: `.${klass}Item-dragBar`,
ghostClass: `${klass}Item-dragging`,
onEnd: (e: any) => {
// 没有移动
if (e.newIndex === e.oldIndex) {
return;
}
// 换回来
const parent = e.to as HTMLElement;
if (
e.newIndex < e.oldIndex &&
e.oldIndex < parent.childNodes.length - 1
) {
parent.insertBefore(e.item, parent.childNodes[e.oldIndex + 1]);
} else if (e.oldIndex < parent.childNodes.length - 1) {
parent.insertBefore(e.item, parent.childNodes[e.oldIndex]);
} else {
parent.appendChild(e.item);
}
const value = this.state.inUseFeat.concat();
value[e.oldIndex] = value.splice(e.newIndex, 1, value[e.oldIndex])[0];
this.setState({inUseFeat: value}, () => {
this.props.onSort?.(value);
});
}
}
);
}
/**
*
*/
destroyDragging() {
this.sortable && this.sortable.destroy();
}
renderItem(item: FeatureOption, index: number) {
const {sortable, goFeatureComp, node, manager} = this.props;
let content = null;
if (goFeatureComp) {
content = (
// @ts-ignore
<GoConfigControl
className={cx(`${klass}Item-go`)}
label={item.label}
manager={manager}
compId={() => goFeatureComp(item)}
/>
);
} else {
content = <div className={cx(`${klass}Item-label`)}>{item.label}</div>;
}
return (
<li className={klass + 'Item'} key={index}>
{sortable && (
<a className={klass + 'Item-dragBar'}>
<Icon icon="drag-bar" className="icon" />
</a>
)}
{content}
<Button
className={klass + 'Item-action'}
onClick={() => this.handleRemove(item, index)}
>
<Icon icon="delete-btn" className="icon" />
</Button>
</li>
);
}
renderAction() {
const {addable, addText, render} = this.props;
if (!addable) {
return null;
}
return render('action', {
type: 'dropdown-button',
closeOnClick: true,
label: '添加' || addText,
className: `${klass}-action`,
btnClassName: `${klass}-action--btn`,
menuClassName: `${klass}-action--menus`,
buttons: this.state.unUseFeat.map(item => {
return {
label: item.label,
onClick: () => this.handleAdd(item)
};
})
});
}
render() {
const {className} = this.props;
return (
<div className={cx('ae-FeatureControl', className)}>
<ul className={cx('ae-FeatureControl-features')} ref={this.dragRef}>
{this.state.inUseFeat.map((item, index) =>
this.renderItem(item, index)
)}
</ul>
{this.renderAction()}
</div>
);
}
}
@FormItem({
type: 'ae-feature-control'
})
export class FeatureControlRenderer extends FeatureControl {}

View File

@ -248,7 +248,14 @@ export default class FormulaControl extends React.Component<
const {node, manager} = this.props.formProps || this.props;
await manager?.getContextSchemas(node);
const dataPropsAsOptions = manager?.dataSchema?.getDataPropsAsOptions();
return dataPropsAsOptions || [];
if (dataPropsAsOptions) {
return dataPropsAsOptions.map((item: any) => ({
selectMode: 'tree',
...item
}));
}
return [];
}
@autobind
@ -331,9 +338,7 @@ export default class FormulaControl extends React.Component<
@autobind
renderFormulaValue(item: any) {
const html = {__html: item.html};
{
/* bca-disable-next-line */
}
// bca-disable-line
return <span dangerouslySetInnerHTML={html}></span>;
}
@ -452,7 +457,13 @@ export default class FormulaControl extends React.Component<
)}
<PickerContainer
showTitle={false}
bodyRender={({onClose, value, onChange}) => {
bodyRender={({
value,
onChange
}: {
onChange: (value: any) => void;
value: any;
}) => {
return (
<FormulaEditor
{...rest}
@ -470,7 +481,7 @@ export default class FormulaControl extends React.Component<
onConfirm={this.handleConfirm}
size="md"
>
{({onClick, isOpened}) => (
{({onClick}: {onClick: (e: React.MouseEvent) => void}) => (
<Button
size="sm"
tooltip={'点击配置表达式'}

View File

@ -0,0 +1,49 @@
/**
* @file
*/
import React from 'react';
import cx from 'classnames';
import {Renderer, toast} from 'amis';
import {EditorManager} from 'amis-editor-core';
import {autobind, FormControlProps} from 'amis-core';
export interface GoCongigControlProps extends FormControlProps {
label: string;
compId: string | ((data: any) => string);
manager: EditorManager;
}
export class GoConfigControl extends React.PureComponent<
GoCongigControlProps,
any
> {
@autobind
onClick() {
const {data: ctx = {}, compId, manager} = this.props;
const id = typeof compId === 'string' ? compId : compId(ctx);
if (!id) {
toast.error('未找到对应组件');
return;
}
manager.setActiveId(id);
}
render() {
const {className, label, data: ctx = {}} = this.props;
return (
<div className={cx('ae-GoConfig', className)} onClick={this.onClick}>
{label}
<div className={cx('ae-GoConfig-trigger')}></div>
</div>
);
}
}
@Renderer({
type: 'ae-go-config'
})
export class GoConfigControlRenderer extends GoConfigControl {}

View File

@ -20,7 +20,7 @@ import {value2array} from 'amis-ui/lib/components/Select';
import {autobind} from 'amis-editor-core';
import {getSchemaTpl} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import type {Option} from 'amis';
import type {FormControlProps} from 'amis-core';
@ -396,33 +396,29 @@ export default class OptionControl extends React.Component<
}
renderHeader() {
const {
render,
label,
labelRemark,
useMobileUI,
env,
popOverContainer
} = this.props;
const {render, label, labelRemark, useMobileUI, env, popOverContainer} =
this.props;
const classPrefix = env?.theme?.classPrefix;
const {source} = this.state;
const optionSourceList = ([
{
label: '自定义选项',
value: 'custom'
},
{
label: '接口获取',
value: 'api'
}
// {
// label: '表单实体',
// value: 'form'
// }
] as Array<{
label: string;
value: 'custom' | 'api' | 'form';
}>).map(item => ({
const optionSourceList = (
[
{
label: '自定义选项',
value: 'custom'
},
{
label: '接口获取',
value: 'api'
}
// {
// label: '表单实体',
// value: 'form'
// }
] as Array<{
label: string;
value: 'custom' | 'api' | 'form';
}>
).map(item => ({
...item,
onClick: () => this.handleSourceChange(item.value)
}));
@ -586,7 +582,7 @@ export default class OptionControl extends React.Component<
value={label}
placeholder="请输入文本/值"
clearable={false}
onChange={value => this.handleEditLabel(index, value)}
onChange={(value: string) => this.handleEditLabel(index, value)}
/>
{amisRender({
type: 'dropdown-button',
@ -636,8 +632,7 @@ export default class OptionControl extends React.Component<
body: [
{
type: 'tpl',
tpl:
'每个选项单列一行,将所有值不重复的项加为新的选项;<br/>每行可通过空格来分别设置label和value,例:"张三 zhangsan"'
tpl: '每个选项单列一行,将所有值不重复的项加为新的选项;<br/>每行可通过空格来分别设置label和value,例:"张三 zhangsan"'
}
],
showIcon: true,
@ -700,6 +695,7 @@ export default class OptionControl extends React.Component<
getSchemaTpl('apiControl', {
label: '接口',
name: 'source',
mode: 'normal',
className: 'ae-ExtendMore',
visibleOn: 'data.autoComplete !== false',
value: api,

View File

@ -1,277 +0,0 @@
/**
* @file
*/
import React from 'react';
import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import {FormItem, Button, Overlay, PopOver, Icon, Switch} from 'amis';
import {isObject, autobind} from 'amis-editor-core';
import type {Action} from 'amis/lib/types';
import type {IScopedContext} from 'amis-core';
import type {FormControlProps} from 'amis-core';
import type {FormSchema} from 'amis/lib/Schema';
import {Offset} from 'amis-core/lib/components/PopOver';
export interface PopoverEditProps extends FormControlProps {
className?: string;
popOverclassName?: string;
btnLabel?: string;
btnIcon?: string;
iconPosition?: 'right' | 'left';
mode: 'popover' | 'dialog';
form: Omit<FormSchema, 'type'>;
rootClose?: boolean;
placement?: string;
offset?: ((clip: object, offset: object) => Offset) | Offset;
style?: object;
overlay?: boolean;
container?: React.ReactNode | Function;
target?: React.ReactNode | Function;
trueValue?: any;
falseValue?: any;
enableEdit?: boolean;
removable?: boolean;
onClose: (e: React.UIEvent<any> | void) => void;
}
interface PopoverEditState {
/**
*
*/
show: boolean;
/**
*
*/
checked: boolean;
}
export class PopoverEdit extends React.Component<
PopoverEditProps,
PopoverEditState
> {
static defaultProps: Pick<
PopoverEditProps,
| 'btnIcon'
| 'iconPosition'
| 'container'
| 'placement'
| 'overlay'
| 'rootClose'
| 'mode'
| 'trueValue'
| 'falseValue'
| 'enableEdit'
> = {
btnIcon: 'pencil',
iconPosition: 'right',
container: document.body,
placement: 'left',
overlay: true,
rootClose: false,
mode: 'popover',
trueValue: true,
falseValue: false,
enableEdit: true
};
overlay: HTMLElement | null;
constructor(props: PopoverEditProps) {
super(props);
this.state = {
show: false,
checked: !!props.value
};
}
@autobind
overlayRef(ref: any) {
this.overlay = ref ? (findDOMNode(ref) as HTMLElement) : null;
}
@autobind
openPopover() {
this.setState({show: true});
}
@autobind
closePopover() {
this.setState({show: false});
}
@autobind
handleDelete(e: React.UIEvent<any> | void) {
const {onDelete} = this.props;
onDelete && onDelete(e);
}
@autobind
handleSwitchChange(checked: boolean) {
const {onChange, enableEdit} = this.props;
this.setState({checked});
if (!enableEdit) {
onChange && onChange(checked);
} else {
// undefined字段会从schema中删除
!checked && onChange && onChange(undefined);
}
}
@autobind
handleSubmit(values: any, action: any) {
const {onChange} = this.props;
onChange && onChange(values);
}
@autobind
handleAction(
e: React.UIEvent<any> | void,
action: Action,
data: object,
throwErrors: boolean = false,
delegate?: IScopedContext
) {
const {onClose} = this.props;
if (action.actionType === 'close') {
this.setState({show: false});
onClose && onClose(e);
}
}
renderPopover() {
const {
render,
popOverclassName,
overlay,
offset,
target,
container,
placement,
rootClose,
style,
title,
label,
form,
name,
data: ctx
} = this.props;
return (
<Overlay
show
rootClose={rootClose}
placement={placement}
target={target || this.overlay}
container={container}
>
<PopOver
className={cx('ae-PopoverEdit-popover', popOverclassName)}
placement={placement}
overlay={overlay}
offset={offset}
style={style}
>
<header>
<p className="ae-PopoverEdit-title">{title || label}</p>
<a onClick={this.closePopover} className="ae-PopoverEdit-close">
<Icon icon="close" className="icon" />
</a>
</header>
{isObject(form)
? render(
'popover-edit-form',
{
type: 'form',
wrapWithPanel: false,
panelClassName: 'border-none shadow-none mb-0',
bodyClassName: 'p-none',
actionsClassName: 'border-none mt-2.5',
wrapperComponent: 'div',
mode: 'horizontal',
autoFocus: true,
formLazyChange: true,
preventEnterSubmit: true,
submitOnChange: true,
data: ctx && name ? ctx?.[name] : {},
...form
},
{
onSubmit: this.handleSubmit
}
)
: null}
</PopOver>
</Overlay>
);
}
render() {
const {
render,
removable,
btnIcon,
btnLabel,
iconPosition,
disabled,
enableEdit,
className
} = this.props;
const {show, checked} = this.state;
const btnLabelNode = btnLabel ? <span>{btnLabel}</span> : null;
return (
<div className={cx('ae-PopoverEditControl', className)}>
{enableEdit && checked && !disabled ? (
<Button
level="link"
size="sm"
ref={this.overlayRef}
onClick={this.openPopover}
>
{iconPosition === 'right' ? (
<>
{btnLabelNode}
<Icon icon={btnIcon} className="icon" />
</>
) : (
<>
<Icon icon={btnIcon} className="icon" />
{btnLabelNode}
</>
)}
</Button>
) : null}
{removable ? (
<Button level="link" size="sm" onClick={this.handleDelete}>
<Icon icon="delete-btn" className="icon" />
</Button>
) : null}
<Switch
value={checked}
onChange={this.handleSwitchChange}
disabled={disabled}
/>
{show ? this.renderPopover() : null}
</div>
);
}
}
@FormItem({
type: 'popover-edit'
})
export class PopoverEditRenderer extends PopoverEdit {}

View File

@ -169,7 +169,7 @@ export default class SwitchMore extends React.Component<
@autobind
handleSwitchChange(checked: boolean) {
const {onBulkChange, onChange, bulk, defaultData, name} = this.props;
const {onBulkChange, onChange, bulk, defaultData, name, pipeOut} = this.props;
this.setState({checked});
@ -179,19 +179,30 @@ export default class SwitchMore extends React.Component<
if (checked) {
let data = defaultData ? {...defaultData} : {};
name && (data[name] = true);
if (pipeOut) {
data = pipeOut(data);
}
onBulkChange && onBulkChange(data);
}
// 取消选中后,讲所有字段重置
else {
const values = fromPairs(
let values = fromPairs(
this.getFormItemNames().map(i => [i, undefined])
);
name && (values[name] = false);
if (pipeOut) {
values = pipeOut(values);
}
onBulkChange && onBulkChange(values);
}
return;
}
onChange(checked ? defaultData || true : undefined);
let data = checked ? defaultData || true : undefined;
if (pipeOut) {
data = pipeOut(data);
}
onChange(data);
}
@autobind

View File

@ -8,7 +8,7 @@ import {render, Button, Switch} from 'amis';
import {autobind} from 'amis-editor-core';
import {Validator} from '../validator';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {SchemaCollection} from 'amis/lib/Schema';
export type ValidatorData = {

View File

@ -1,9 +1,11 @@
import {setSchemaTpl, getSchemaTpl} from 'amis-editor-core';
import {setSchemaTpl, getSchemaTpl, tipedLabel} from 'amis-editor-core';
import React from 'react';
import {buildApi, Html} from 'amis';
import {get} from 'lodash';
setSchemaTpl('api', (patch: any = {}) => {
const {name, label, value, description, sampleBuilder, ...rest} = patch;
const {name, label, value, description, sampleBuilder, apiDesc, ...rest} =
patch;
return {
type: 'container',
@ -13,21 +15,25 @@ setSchemaTpl('api', (patch: any = {}) => {
label: label || 'API',
labelRemark: sampleBuilder
? {
icon: '',
label: '示例',
label: false,
title: '接口返回示例',
icon: 'fas fa-code',
className: 'm-l-xs ae-ApiSample-icon',
tooltipClassName: 'ae-ApiSample-tooltip',
render: (data: any) => (
<Html
className="ae-ApiSample"
inline={false}
html={`
<pre><code>${sampleBuilder(data)}</code></pre>
`}
/>
),
children: () => {
return (
<Html
className="ae-ApiSample"
inline={false}
html={`<pre><code>${sampleBuilder()}</code></pre>${
apiDesc
? `<span class="ae-ApiSample-desc">${apiDesc}</span>`
: ''
}`}
/>
);
},
trigger: 'click',
className: 'm-l-xs',
rootClose: true,
placement: 'left'
}
@ -166,6 +172,7 @@ setSchemaTpl('api', (patch: any = {}) => {
mode: 'normal',
renderLabel: false,
visibleOn: 'this.data',
valueType: 'ae-DataPickerControl',
descriptionClassName: 'help-block text-xs m-b-none',
description:
'<p>当没开启数据映射时,发送数据自动切成白名单模式,配置啥发送啥,请绑定数据。如:<code>{"a": "\\${a}", "b": 2}</code></p><p>如果希望在默认的基础上定制,请先添加一个 Key 为 `&` Value 为 `\\$$` 作为第一行。</p><div>当值为 <code>__undefined</code>时,表示删除对应的字段,可以结合<code>{"&": "\\$$"}</code>来达到黑名单效果。</div>'
@ -346,55 +353,66 @@ setSchemaTpl('apiString', {
placeholder: 'http://'
});
setSchemaTpl('initFetch', {
type: 'group',
label: '是否初始加载',
visibleOn: 'this.initApi',
direction: 'vertical',
className: 'm-b-none',
labelRemark: {
trigger: 'click',
rootClose: true,
className: 'm-l-xs',
content:
'当配置初始化接口后,组件初始就会拉取接口数据,可以通过以下配置修改。',
placement: 'left'
},
body: [
{
name: 'initFetch',
type: 'radios',
inline: true,
onChange: () => {},
// pipeIn: (value:any) => typeof value === 'boolean' ? value : '1'
options: [
setSchemaTpl(
'initFetch',
(overrides: {visibleOn?: string; name?: string} = {}) => {
const visibleOn = get(overrides, 'visibleOn', 'this.initApi');
const fieldName = get(overrides, 'name', 'initFetch');
const label = get(overrides, 'label', '是否初始加载');
return {
type: 'group',
label: tipedLabel(
label,
'当配置初始化接口后,组件初始就会拉取接口数据,可以通过以下配置修改。'
),
visibleOn,
direction: 'vertical',
body: [
{
label: '是',
value: true
name: fieldName,
type: 'radios',
inline: true,
onChange: () => {},
// pipeIn: (value:any) => typeof value === 'boolean' ? value : '1'
options: [
{
label: '是',
value: true
},
{
label: '否',
value: false
},
{
label: '表达式',
value: ''
}
]
},
{
label: '否',
value: false
},
{
label: '表达式',
value: ''
}
getSchemaTpl('valueFormula', {
label: '',
name: `${fieldName}On`,
autoComplete: false,
visibleOn: `typeof this.${fieldName} !== "boolean"`,
placeholder: '如this.id 表示有 id 值时初始加载',
className: 'm-t-n-sm'
})
// {
// name: `${fieldName}On`,
// autoComplete: false,
// visibleOn: `typeof this.${fieldName} !== "boolean"`,
// type: 'input-text',
// placeholder: '如this.id 表示有 id 值时初始加载',
// className: 'm-t-n-sm'
// }
]
},
{
name: 'initFetchOn',
autoComplete: false,
visibleOn: 'typeof this.initFetch !== "boolean"',
type: 'input-text',
placeholder: '如this.id 表示有 id 值时初始加载',
className: 'm-t-n-sm'
}
]
});
};
}
);
setSchemaTpl('proxy', {
type: 'switch',
@ -409,31 +427,35 @@ setSchemaTpl('proxy', {
});
setSchemaTpl('apiControl', (patch: any = {}) => {
const {name, label, value, description, sampleBuilder, ...rest} = patch;
const {name, label, value, description, sampleBuilder, apiDesc, ...rest} =
patch;
return {
type: 'ae-apiControl',
label,
name,
description,
mode: 'normal',
labelRemark: sampleBuilder
? {
icon: '',
label: '示例',
label: false,
title: '接口返回示例',
icon: 'fas fa-code',
className: 'm-l-xs ae-ApiSample-icon',
tooltipClassName: 'ae-ApiSample-tooltip',
render: (data: any) => (
<Html
className="ae-ApiSample"
inline={false}
html={`
<pre><code>${sampleBuilder(data)}</code></pre>
`}
/>
),
children: () => {
return (
<Html
className="ae-ApiSample"
inline={false}
html={`<pre><code>${sampleBuilder()}</code></pre>${
apiDesc
? `<span class="ae-ApiSample-desc">${apiDesc}</span>`
: ''
}`}
/>
);
},
trigger: 'click',
className: 'm-l-xs',
rootClose: true,
placement: 'left'
}
@ -442,6 +464,50 @@ setSchemaTpl('apiControl', (patch: any = {}) => {
};
});
setSchemaTpl('interval', (more: any = {}) => ({
type: 'ae-switch-more',
label: '定时刷新',
name: 'interval',
formType: 'extend',
bulk: true,
mode: 'normal',
form: {
body: [
getSchemaTpl('withUnit', {
label: '刷新间隔',
name: 'interval',
control: {
type: 'input-number',
name: 'interval',
value: 1000
},
unit: '毫秒'
})
]
},
...more
}));
setSchemaTpl('silentPolling', () =>
getSchemaTpl('switch', {
label: tipedLabel('静默刷新', '设置自动定时刷新时是否显示loading'),
name: 'silentPolling',
visibleOn: '!!this.interval'
})
);
setSchemaTpl('stopAutoRefreshWhen', (extra: any = {}) =>
getSchemaTpl('valueFormula', {
name: 'stopAutoRefreshWhen',
label: tipedLabel(
'定时刷新停止',
'定时刷新一旦设置会一直刷新,除非给出表达式,条件满足后则停止刷新'
),
visibleOn: '!!this.interval',
...extra
})
);
/**
*
*/

View File

@ -2,9 +2,11 @@ import {
setSchemaTpl,
getSchemaTpl,
defaultValue,
isObject
isObject,
tipedLabel,
DSField
} from 'amis-editor-core';
import {remarkTpl, tipedLabel} from '../component/BaseControl';
import {remarkTpl} from '../component/BaseControl';
import {SchemaObject} from 'amis/lib/Schema';
import flatten from 'lodash/flatten';
import {InputComponentName} from '../component/InputComponentName';
@ -22,13 +24,46 @@ setSchemaTpl('switch', {
inputClassName: 'is-inline '
});
/**
* 线
*/
setSchemaTpl('divider', {
type: 'divider',
className: 'mx-0'
});
/**
*
*/
setSchemaTpl(
'withUnit',
(config: {name: string; label: string; control: any; unit: string}) => {
return {
type: 'input-group',
name: config.name,
label: config.label,
body: [
config.control,
{
type: 'tpl',
addOnclassName: 'border-0 bg-none',
tpl: config.unit
}
]
};
}
);
/**
* name
*/
setSchemaTpl('formItemName', {
label: '字段名',
name: 'name',
type: 'ae-DataBindingControl'
type: 'ae-DataBindingControl',
onBindingChange(field: DSField, onBulkChange: (value: any) => void) {
onBulkChange(field.resolveEditSchema?.() || {label: field.label});
}
// validations: {
// matchRegexp: /^[a-z\$][a-z0-0\-_]*$/i
// },
@ -38,37 +73,45 @@ setSchemaTpl('formItemName', {
// validateOnChange: false
});
setSchemaTpl('formItemMode', {
label: '布局',
name: 'mode',
type: 'button-group-select',
option: '继承',
horizontal: {
left: 2,
justify: true
},
// className: 'w-full',
pipeIn: defaultValue(''),
options: [
{
label: '内联',
value: 'inline'
setSchemaTpl(
'formItemMode',
(config: {
// 是不是独立表单,没有可以集成的内容
isForm: boolean;
}) => ({
label: '布局',
name: 'mode',
type: 'button-group-select',
option: '继承',
horizontal: {
left: 2,
justify: true
},
{
label: '水平',
value: 'horizontal'
},
{
label: '垂直',
value: 'normal'
},
{
label: '继承',
value: ''
}
],
pipeOut: (v: string) => (v ? v : undefined)
});
// className: 'w-full',
pipeIn: defaultValue(''),
options: [
{
label: '内联',
value: 'inline'
},
{
label: '水平',
value: 'horizontal'
},
{
label: '垂直',
value: 'normal'
},
config?.isForm
? null
: {
label: '继承',
value: ''
}
].filter(i => i),
pipeOut: (v: string) => (v ? v : undefined)
})
);
setSchemaTpl('formItemInline', {
type: 'switch',
@ -243,37 +286,31 @@ setSchemaTpl(
key: string;
visibleOn: string;
body: Array<any>;
}>,
rendererSchema?: any
}>
) => {
let currentKey = rendererSchema
? `${rendererSchema.$$id}_${rendererSchema.type}_${rendererSchema.configTitle}_collapse`
: `config_collapse`;
currentKey = currentKey.replace(/-/g, '__');
const collapseGroupBody = config
.filter(
item => item && Array.isArray(item?.body) && item?.body.length > 0
)
.map((item, index) => ({
.map(item => ({
type: 'collapse',
collapsed: false,
headingClassName: 'ae-formItemControl-header',
bodyClassName: 'ae-formItemControl-body',
...item,
key: `${currentKey}_${item.key || index.toString()}`,
key: item.title,
body: flatten(item.body)
}));
return {
type: 'collapse-group',
key: currentKey,
activeKey: collapseGroupBody.map(panel => panel.title),
expandIconPosition: 'right',
expandIcon: {
type: 'icon',
icon: 'chevron-right'
},
className: 'ae-formItemControl ae-styleControl',
activeKey: collapseGroupBody.map((group, index) => group.key),
body: collapseGroupBody
};
}
@ -346,7 +383,7 @@ setSchemaTpl(
body: [
{
type: 'ae-formulaControl',
label: config?.label || '默认值',
label: config?.label ?? '默认值',
name: config?.name || 'value',
rendererSchema: curRendererSchema,
rendererWrapper: config?.rendererWrapper,
@ -509,28 +546,29 @@ setSchemaTpl('size', {
});
setSchemaTpl('name', {
label: '名字',
label: tipedLabel(
'名字',
'需要联动时才需要,其他组件可以通过这个名字跟当前组件联动'
),
name: 'name',
type: 'input-text',
description: '需要联动时才需要,其他组件可以通过这个名字跟当前组件联动',
placeholder: '请输入字母或者数字'
});
setSchemaTpl('reload', {
label: '刷新目标组件',
name: 'reload',
asFormItem: true,
// type: 'input-text',
component: InputComponentName,
description:
'可以指定操作完成后刷新目标组件,请填写目标组件的 <code>name</code> 属性,多个组件请用<code>,</code>隔开,如果目标组件为表单项,请先填写表单的名字,再用<code>.</code>连接表单项的名字如:<code>xxForm.xxControl</code>。另外如果刷新目标对象设置为 <code>window</code>,则会刷新整个页面。',
labelRemark: {
trigger: 'click',
className: 'm-l-xs',
rootClose: true,
content:
'设置名字后,当前组件操作完成会触发目标组件(根据设置的名字)的刷新。',
placement: 'left'
label: tipedLabel(
'刷新目标组件',
'可以指定操作完成后刷新目标组件,请填写目标组件的 <code>name</code> 属性,多个组件请用<code>,</code>隔开,如果目标组件为表单项,请先填写表单的名字,再用<code>.</code>连接表单项的名字如:<code>xxForm.xxControl</code>。另外如果刷新目标对象设置为 <code>window</code>,则会刷新整个页面。'
),
placeholder: '请输入组件name',
mode: 'horizontal',
horizontal: {
left: 4,
justify: true
}
});
@ -577,7 +615,7 @@ setSchemaTpl(
? getSchemaTpl('disabled')
: null,
config?.isFormItem ? getSchemaTpl('clearValueOnHidden') : null
]
].filter(Boolean)
};
}
);

View File

@ -1,5 +1,6 @@
import {setSchemaTpl, getSchemaTpl, defaultValue} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {isObject} from 'lodash';
import {tipedLabel} from 'amis-editor-core';
setSchemaTpl('horizontal-align', {
type: 'button-group-select',
@ -57,47 +58,51 @@ setSchemaTpl('leftRate', {
}
});
setSchemaTpl('horizontal', () => {
return [
{
type: 'button-group-select',
label: '标题宽度',
name: 'horizontal',
options: [
{label: '继承', value: 'formHorizontal'},
{label: '固宽', value: 'leftFixed'},
{label: '比例', value: 'leftRate'}
],
pipeIn(v: any) {
if (!v) {
return 'formHorizontal';
}
if (v.leftFixed) {
return 'leftFixed';
}
return 'leftRate';
},
pipeOut(v: any) {
const defaultData = {
formHorizontal: undefined,
leftFixed: {leftFixed: 'normal'},
leftRate: {left: 3, right: 9}
};
setSchemaTpl(
'horizontal',
(config: {visibleOn: string; [propName: string]: any}) => {
return [
{
type: 'button-group-select',
label: '标题宽度',
name: 'horizontal',
options: [
{label: '继承', value: 'formHorizontal'},
{label: '固宽', value: 'leftFixed'},
{label: '比例', value: 'leftRate'}
],
pipeIn(v: any) {
if (!v) {
return 'formHorizontal';
}
if (v.leftFixed) {
return 'leftFixed';
}
return 'leftRate';
},
pipeOut(v: any) {
const defaultData = {
formHorizontal: undefined,
leftFixed: {leftFixed: 'normal'},
leftRate: {left: 3, right: 9}
};
// @ts-ignore
return defaultData[v];
// @ts-ignore
return defaultData[v];
},
visibleOn: 'this.mode == "horizontal" && this.label !== false',
...(isObject(config) ? config : {})
},
visibleOn: 'this.mode == "horizontal" && this.label !== false'
},
{
type: 'container',
className: 'ae-ExtendMore mb-3',
visibleOn:
'this.mode == "horizontal" && this.horizontal && this.label !== false',
body: [getSchemaTpl('leftFixed'), getSchemaTpl('leftRate')]
}
];
});
{
type: 'container',
className: 'ae-ExtendMore mb-3',
visibleOn:
'this.mode == "horizontal" && this.horizontal && this.label !== false',
body: [getSchemaTpl('leftFixed'), getSchemaTpl('leftRate')]
}
];
}
);
setSchemaTpl('subFormItemMode', {
label: '子表单展示模式',

View File

@ -1,5 +1,5 @@
import {setSchemaTpl, getSchemaTpl, defaultValue} from 'amis-editor-core';
import {tipedLabel} from '../component/BaseControl';
import {tipedLabel} from 'amis-editor-core';
import {SchemaObject} from 'amis/lib/Schema';
setSchemaTpl('options', {
@ -114,7 +114,7 @@ setSchemaTpl('tree', {
setSchemaTpl('multiple', (schema: any = {}) => {
return {
type: 'ae-Switch-More',
type: 'ae-switch-more',
mode: 'normal',
name: 'multiple',
label: '可多选',

View File

@ -9,6 +9,6 @@
"../../node_modules/@types"
]
},
"include": ["src/**/*"],
"include": ["src/**/*", "../amis-editor-core/src/builder"],
"references": []
}