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 flatten from 'lodash/flatten';
import {getEventControlConfig} from '../util'; import {getEventControlConfig} from '../util';
import {getSchemaTpl, isObject} from 'amis-editor-core'; import {
import type {BaseEventContext} from 'amis-editor-core'; getSchemaTpl,
import {SchemaObject} from 'amis/lib/Schema'; isObject,
BaseEventContext,
tipedLabel
} from 'amis-editor-core';
// 默认动作 // 默认动作
export const BUTTON_DEFAULT_ACTION = { 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 { return {
type: 'ae-switch-more', type: 'ae-switch-more',
formType: 'dialog', formType: 'dialog',
className:'ae-switch-more-flex', className: 'ae-switch-more-flex',
label: config.labelRemark label: config.labelRemark
? tipedLabel(config.label, config.labelRemark) ? tipedLabel(config.label, config.labelRemark)
: config.label, : config.label,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import {registerEditorPlugin} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core'; import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} 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 { 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 {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} 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'; import {isObject} from 'amis-editor-core';
export class CollapseGroupPlugin extends BasePlugin { 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; isBaseComponent = true;
description = '一个简单的容器,可以将多个渲染器放置在一起。'; description = '一个简单的容器,可以将多个渲染器放置在一起。';
tags = ['容器']; tags = ['容器'];
withDataSource = false;
icon = 'fa fa-square-o'; icon = 'fa fa-square-o';
pluginIcon = 'container-plugin'; pluginIcon = 'container-plugin';
scaffold = { scaffold = {
type: 'container', type: 'container',
body: '内容' body: [
{
type: 'tpl',
tpl: '内容'
}
]
}; };
previewSchema = { previewSchema = {
...this.scaffold ...this.scaffold

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -149,7 +149,8 @@ export class DiffEditorControlPlugin extends BasePlugin {
}, },
getSchemaTpl('labelRemark'), getSchemaTpl('labelRemark'),
getSchemaTpl('remark'), getSchemaTpl('remark'),
getSchemaTpl('description') getSchemaTpl('description'),
getSchemaTpl('autoFillApi')
] ]
}, },
getSchemaTpl('status', {isFormItem: true}), 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'; } from 'amis-editor-core';
import {formItemControl} from '../../component/BaseControl'; import {formItemControl} from '../../component/BaseControl';
import { import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
export class CityControlPlugin extends BasePlugin { 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 {getSchemaTpl} from 'amis-editor-core';
import {ValidatorTag} from '../../validator'; import {ValidatorTag} from '../../validator';
import {BasePlugin, BaseEventContext} from 'amis-editor-core'; import {BasePlugin, BaseEventContext} from 'amis-editor-core';
import {getEventControlConfig} from '../../util'; import {getEventControlConfig} from '../../util';
import {tipedLabel} from '../../component/BaseControl';
import tinyColor from 'tinycolor2'; import tinyColor from 'tinycolor2';
function convertColor(value: string[], format: string): string[]; function convertColor(value: string[], format: string): string[];
@ -171,124 +170,133 @@ export class ColorControlPlugin extends BasePlugin {
return getSchemaTpl('tabs', [ return getSchemaTpl('tabs', [
{ {
title: '属性', title: '属性',
body: getSchemaTpl('collapseGroup', [ body: getSchemaTpl(
{ 'collapseGroup',
title: '基本', [
body: [ {
getSchemaTpl('formItemName', { title: '基本',
required: true body: [
}), getSchemaTpl('formItemName', {
getSchemaTpl('label'), required: true
{ }),
type: 'select', getSchemaTpl('label'),
label: '值格式', {
name: 'format', type: 'select',
value: 'hex', label: '值格式',
options: formatOptions, name: 'format',
onChange: ( value: 'hex',
format: any, options: formatOptions,
oldFormat: any, onChange: (
model: any, format: any,
form: any oldFormat: any,
) => { model: any,
const {value, presetColors} = form.data; form: any
if (value) { ) => {
form.setValueByName('value', convertColor(value, format)); 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: ( // todo: 待优化
colors: any, [
oldValue: any, ...formatOptions.map(({value}) =>
model: any, this.getConditionalColorPanel(value)
form: any )
) => { ],
if (Array.isArray(colors) && colors.length === 0) { // {
form.setValueByName('allowCustomColor', true); // 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}) =>
...formatOptions.map(({value}) => this.getConditionalColorComb(value)
this.getConditionalColorComb(value) )
) ]
] },
}, getSchemaTpl('status', {
getSchemaTpl('status', { isFormItem: true
isFormItem: true }),
}), getSchemaTpl('validation', {
getSchemaTpl('validation', { tag: ValidatorTag.MultiSelect
tag: ValidatorTag.MultiSelect })
}) ],
], {...context?.schema, configTitle: 'props'}) {...context?.schema, configTitle: 'props'}
)
}, },
{ {
title: '外观', title: '外观',
body: getSchemaTpl('collapseGroup', [ body: getSchemaTpl(
getSchemaTpl('style:formItem', {renderer}), 'collapseGroup',
getSchemaTpl('style:classNames', { [
schema: [ getSchemaTpl('style:formItem', {renderer}),
getSchemaTpl('className', { getSchemaTpl('style:classNames', {
label: '描述', schema: [
name: 'descriptionClassName', getSchemaTpl('className', {
visibleOn: 'this.description' label: '描述',
}) name: 'descriptionClassName',
] visibleOn: 'this.description'
}) })
], {...context?.schema, configTitle: 'style'}) ]
})
],
{...context?.schema, configTitle: 'style'}
)
}, },
{ {
title: '事件', title: '事件',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,5 @@
import {relativeValueRe} from 'amis'; import {relativeValueRe} from 'amis';
import { import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
import {availableLanguages} from 'amis/lib/renderers/Form/Editor'; import {availableLanguages} from 'amis/lib/renderers/Form/Editor';
import {defaultValue, getSchemaTpl, valuePipeOut} from 'amis-editor-core'; import {defaultValue, getSchemaTpl, valuePipeOut} from 'amis-editor-core';
import {registerEditorPlugin} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core';
@ -11,9 +8,9 @@ import {
BasicSubRenderInfo, BasicSubRenderInfo,
RendererEventContext, RendererEventContext,
SubRendererInfo, SubRendererInfo,
BaseEventContext BaseEventContext,
tipedLabel
} from 'amis-editor-core'; } from 'amis-editor-core';
import {tipedLabel} from '../../component/BaseControl';
import {ValidatorTag} from '../../validator'; import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util'; import {getEventControlConfig} from '../../util';
@ -285,7 +282,8 @@ export class NestedSelectControlPlugin extends BasePlugin {
getSchemaTpl('labelRemark'), getSchemaTpl('labelRemark'),
getSchemaTpl('remark'), getSchemaTpl('remark'),
getSchemaTpl('placeholder'), 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 {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../util'; import {getEventControlConfig} from '../../util';
import { import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
RendererPluginAction,
RendererPluginEvent
} from 'amis-editor-core';
export class RadiosControlPlugin extends BasePlugin { export class RadiosControlPlugin extends BasePlugin {
// 关联渲染器名字 // 关联渲染器名字
@ -122,7 +119,8 @@ export class RadiosControlPlugin extends BasePlugin {
}), }),
// getSchemaTpl('autoFill') // getSchemaTpl('autoFill')
getSchemaTpl('labelRemark'), getSchemaTpl('labelRemark'),
getSchemaTpl('remark') getSchemaTpl('remark'),
getSchemaTpl('autoFillApi')
] ]
}, },
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -177,15 +177,15 @@ export class PagePlugin extends BasePlugin {
label: '数据初始化接口', label: '数据初始化接口',
name: 'initApi', name: 'initApi',
sampleBuilder: (schema: any) => `{ sampleBuilder: (schema: any) => `{
"status": 0, "status": 0,
"msg": "", "msg": "",
data: { data: {
// 示例数据 // 示例数据
"id": 1, "id": 1,
"a": "sample" "a": "sample"
} }
}` }`
}), }),
getSchemaTpl('initFetch'), 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 {getEventControlConfig} from '../util';
import {RendererPluginEvent} from 'amis-editor-core'; import {
getSchemaTpl,
import {defaultValue, getSchemaTpl} from 'amis-editor-core'; defaultValue,
tipedLabel,
BasePlugin,
RendererPluginEvent,
RegionConfig,
BaseEventContext,
registerEditorPlugin
} from 'amis-editor-core';
export class PaginationPlugin extends BasePlugin { export class PaginationPlugin extends BasePlugin {
// 关联渲染器名字 // 关联渲染器名字
@ -14,17 +17,16 @@ export class PaginationPlugin extends BasePlugin {
// 组件名称 // 组件名称
name = '分页组件'; name = '分页组件';
isBaseComponent = true; isBaseComponent = false;
disabledRendererPlugin = true;
description = '分页组件,可以对列表进行分页展示,提高页面性能'; description = '分页组件,可以对列表进行分页展示,提高页面性能';
tags = ['容器']; tags = ['容器'];
icon = 'fa fa-window-minimize'; icon = 'fa fa-window-minimize';
// pluginIcon = 'pagination-plugin'; // 暂无新icon lastLayoutSetting = ['pager'];
baseLayoutLIst = [ layoutOptions = [
{text: '总数', value: 'total', checked: false}, {text: '总数', value: 'total', checked: false},
{text: '每页条数', value: 'perPage', checked: false}, {text: '每页条数', value: 'perPage', checked: false},
{text: '分页', value: 'pager', checked: true}, {text: '分页', value: 'pager', checked: true},
{text: '跳转', value: 'go', checked: false} {text: '跳转', value: 'go', checked: false}
]; ];
scaffold = { scaffold = {
type: 'pagination', type: 'pagination',
@ -66,27 +68,52 @@ export class PaginationPlugin extends BasePlugin {
body: [ body: [
{ {
name: 'mode', name: 'mode',
label: '分页类型', label: '模式',
type: 'button-group-select', type: 'button-group-select',
size: 'sm', size: 'sm',
pipeIn: defaultValue('normal'), pipeIn: defaultValue('normal'),
options: [ options: [
{ {
label: '普通', label: '默认',
value: 'normal' value: 'normal'
}, },
{ {
label: '简', label: '简',
value: 'simple' 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', { getSchemaTpl('combo-container', {
name: 'layout', name: 'layout',
type: 'combo', type: 'combo',
label: tipedLabel( label: tipedLabel(
'分页布局展示', '启用功能',
'选中表示渲染该项,可以拖拽排序调整显示的顺序' '选中表示启用该项,可以拖拽排序调整功能的顺序'
), ),
visibleOn: 'data.mode === "normal"', visibleOn: 'data.mode === "normal"',
mode: 'normal', mode: 'normal',
@ -112,52 +139,37 @@ export class PaginationPlugin extends BasePlugin {
} }
], ],
pipeIn: (value: any) => { pipeIn: (value: any) => {
let layoutList: string[] = []; if (!value) {
if (Array.isArray(value)) { value = this.lastLayoutSetting;
layoutList = value;
} else if (typeof value === 'string') { } 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, ...v,
checked: layoutList.includes(v.value) checked: value.includes(v.value)
})); }));
return layout;
}, },
pipeOut: (value: any[]) => { pipeOut: (value: any[]) => {
this.baseLayoutLIst = [...value]; this.lastLayoutSetting = value
return value.filter(v => v.checked).map(v => v.value); .filter(v => v.checked)
.map(v => v.value);
return this.lastLayoutSetting.concat();
} }
}), }),
{ // {
type: 'ae-formulaControl', // name: 'showPerPage',
label: '是否有下一页', // label: '显示每页条数',
name: 'hasNext', // mode: 'row',
visibleOn: 'data.mode === "simple"' // inputClassName: 'inline-flex justify-between flex-row-reverse',
}, // type: 'switch',
{ // visibleOn: 'data.mode === "normal"'
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"'
},
getSchemaTpl('combo-container', { getSchemaTpl('combo-container', {
name: 'perPageAvailable', name: 'perPageAvailable',
type: 'combo', type: 'combo',
label: '每页条数选项', label: '每页条数选项',
visibleOn: visibleOn:
'data.mode === "normal" && data.layout?.includes("perPage")', 'data.mode === "normal" && data.layout && data.layout.includes("perPage")',
mode: 'normal', mode: 'normal',
multiple: true, multiple: true,
multiLine: false, multiLine: false,
@ -167,6 +179,7 @@ export class PaginationPlugin extends BasePlugin {
editable: true, editable: true,
minLength: 1, minLength: 1,
tabsStyle: 'inline', tabsStyle: 'inline',
addButtonClassName: 'm-b-sm',
items: [ items: [
{ {
type: 'input-number', type: 'input-number',
@ -175,31 +188,35 @@ export class PaginationPlugin extends BasePlugin {
} }
], ],
pipeIn: (value: any[]) => { pipeIn: (value: any[]) => {
return value.map(v => ({value: v})); return value?.map(v => ({value: v})) || [10];
}, },
pipeOut: (value: any[]) => { pipeOut: (value: any[]) => {
return value.map(v => v.value); return value.map(v => v.value);
} }
}), })
{ // {
name: 'perPage', // name: 'perPage',
type: 'input-text', // type: 'input-text',
label: '默认每页条数', // label: '默认每页条数',
visibleOn: // visibleOn: 'data.mode === "normal"'
'data.mode === "normal" && data.layout?.includes("perPage")' // },
}, // {
{ // name: 'maxButton',
name: 'maxButton', // label: tipedLabel('分页按钮数量', '超过此数量,将会隐藏多余按钮'),
label: tipedLabel( // type: 'input-number',
'最多按钮数', // min: 5,
'最多显示多少个分页按钮最小为5最大值为20' // max: 20,
), // pipeIn: defaultValue(5),
type: 'input-number', // visibleOn: 'data.mode === "normal"'
min: 5, // }
max: 20, // {
pipeOut: (value: any) => value || 5, // name: 'showPageInput',
visibleOn: 'data.mode === "normal"' // 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; panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => { panelBodyCreator = (context: BaseEventContext) => {
const isForm = /(?:^|\/)form$/.test(context.path) || context?.schema?.type === 'form'; const isForm =
/(?:^|\/)form$/.test(context.path) || context?.schema?.type === 'form';
return [ return [
getSchemaTpl('tabs', [ getSchemaTpl('tabs', [
@ -102,7 +103,7 @@ export class PanelPlugin extends BasePlugin {
name: 'title', name: 'title',
type: 'input-text' type: 'input-text'
}, },
isForm isForm
? null ? null
: { : {
@ -124,7 +125,7 @@ export class PanelPlugin extends BasePlugin {
] ]
}, },
getSchemaTpl('status') getSchemaTpl('status')
]), ])
] ]
}, },
{ {
@ -140,7 +141,7 @@ export class PanelPlugin extends BasePlugin {
label: '固定底部', label: '固定底部',
value: false value: false
}), }),
getSchemaTpl('horizontal', { getSchemaTpl('horizontal', {
visibleOn: visibleOn:
'(data.mode || data.$$formMode) == "horizontal" && data.$$mode == "form"' '(data.mode || data.$$formMode) == "horizontal" && data.$$mode == "form"'
@ -152,7 +153,9 @@ export class PanelPlugin extends BasePlugin {
title: '内容区域展示', title: '内容区域展示',
body: [ body: [
getSchemaTpl('subFormItemMode', {label: '表单展示模式'}), getSchemaTpl('subFormItemMode', {label: '表单展示模式'}),
getSchemaTpl('subFormHorizontalMode', {label: '表单水平占比'}), getSchemaTpl('subFormHorizontalMode', {
label: '表单水平占比'
}),
getSchemaTpl('subFormHorizontal') getSchemaTpl('subFormHorizontal')
] ]
}, },
@ -211,28 +214,28 @@ export class PanelPlugin extends BasePlugin {
name: isForm ? 'panelClassName' : 'className', name: isForm ? 'panelClassName' : 'className',
pipeIn: defaultValue('Panel--default') pipeIn: defaultValue('Panel--default')
}), }),
getSchemaTpl('className', { getSchemaTpl('className', {
name: 'headerClassName', name: 'headerClassName',
label: '头部区域' label: '头部区域'
}), }),
getSchemaTpl('className', { getSchemaTpl('className', {
name: 'bodyClassName', name: 'bodyClassName',
label: '内容区域' label: '内容区域'
}), }),
getSchemaTpl('className', { getSchemaTpl('className', {
name: 'footerClassName', name: 'footerClassName',
label: '底部区域' label: '底部区域'
}), }),
getSchemaTpl('className', { getSchemaTpl('className', {
name: 'actionsClassName', name: 'actionsClassName',
label: '按钮外层' label: '按钮外层'
}) })
], ]
}, }
]) ])
] ]
} }
@ -251,9 +254,10 @@ export class PanelPlugin extends BasePlugin {
if ( if (
context.info.renderer.name === 'form' && context.info.renderer.name === 'form' &&
schema.wrapWithPanel !== false && schema.wrapWithPanel !== false &&
!context.selections.length !context.selections.length &&
false
) { ) {
/** Panel相关的配置融合到From中了 */
panels.push({ panels.push({
key: 'panel', key: 'panel',
icon: 'fa fa-list-alt', icon: 'fa fa-list-alt',

View File

@ -1,7 +1,7 @@
import {registerEditorPlugin} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core';
import {BaseEventContext, BasePlugin} from 'amis-editor-core'; import {BaseEventContext, BasePlugin} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} 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 {ValidatorTag} from '../validator';
import {getEventControlConfig} from '../util'; import {getEventControlConfig} from '../util';

View File

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

View File

@ -5,7 +5,7 @@
import {registerEditorPlugin} from 'amis-editor-core'; import {registerEditorPlugin} from 'amis-editor-core';
import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core'; import {BasePlugin, RegionConfig, BaseEventContext} from 'amis-editor-core';
import {defaultValue, getSchemaTpl} 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 { export class TooltipWrapperPlugin extends BasePlugin {
rendererName = 'tooltip-wrapper'; rendererName = 'tooltip-wrapper';

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import {getEnv} from 'mobx-state-tree';
import {normalizeApi, isEffectiveApi, isApiOutdated} from 'amis-core'; import {normalizeApi, isEffectiveApi, isApiOutdated} from 'amis-core';
import {autobind, isObject, anyChanged, createObject} from 'amis-editor-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 {SchemaObject, SchemaCollection, SchemaApi} from 'amis/lib/Schema';
import type {Api} from 'amis/lib/types'; 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 {
import type {FormControlProps} from 'amis-core'; 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 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 @autobind
handleConfirm(result: {value: string; schema: any}) { handleConfirm(result: {label: string; value: string}) {
const {manager, data} = this.props; const {manager, data, onChange, onBulkChange, onBindingChange} = this.props;
if (result?.value) { if (result?.value) {
this.props.onChange(`${result.value}`); onChange(result.value);
onBulkChange && onBindingChange?.(result, onBulkChange);
manager.config?.dataBindingChange?.(result.value, data, manager); manager.config?.dataBindingChange?.(result.value, data, manager);
} }
} }
@autobind @autobind
async handlePickerOpen() { handlePickerOpen() {
const {manager, node} = this.props; const {manager, node} = this.props;
const withSuper = manager.config?.withSuperDataSchema ?? false;
const schemas = await manager.getContextSchemas(node.info.id, !withSuper); // 如果node没变化就不再重复加载
return {schemas}; 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() { @autobind
const {classnames: cx, value, onChange, disabled} = this.props; 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 ( return (
<SchemaVariableListPicker <PickerContainer
onPickerOpen={this.handlePickerOpen} 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} onConfirm={this.handleConfirm}
title="绑定变量"
> >
{({onClick, isOpened, setState}) => { {({onClick}: {onClick: (e: React.MouseEvent) => void}) => {
return ( return (
<InputBox <InputBox
className="ae-InputVariable" className="ae-InputVariable"
@ -50,7 +257,7 @@ export class DataBindingControl extends React.Component<FormControlProps> {
</InputBox> </InputBox>
); );
}} }}
</SchemaVariableListPicker> </PickerContainer>
); );
} }
} }

View File

@ -47,7 +47,9 @@ class DataPickerControl extends React.Component<FormControlProps> {
onChange={() => {}} onChange={() => {}}
header={''} header={''}
> >
{({onClick, isOpened, setState}) => { {({onClick}: {
onClick: (e: React.MouseEvent) => void;
}) => {
return ( return (
<InputBox <InputBox
className="ae-InputVariable" 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; const {node, manager} = this.props.formProps || this.props;
await manager?.getContextSchemas(node); await manager?.getContextSchemas(node);
const dataPropsAsOptions = manager?.dataSchema?.getDataPropsAsOptions(); const dataPropsAsOptions = manager?.dataSchema?.getDataPropsAsOptions();
return dataPropsAsOptions || [];
if (dataPropsAsOptions) {
return dataPropsAsOptions.map((item: any) => ({
selectMode: 'tree',
...item
}));
}
return [];
} }
@autobind @autobind
@ -331,9 +338,7 @@ export default class FormulaControl extends React.Component<
@autobind @autobind
renderFormulaValue(item: any) { renderFormulaValue(item: any) {
const html = {__html: item.html}; const html = {__html: item.html};
{ // bca-disable-line
/* bca-disable-next-line */
}
return <span dangerouslySetInnerHTML={html}></span>; return <span dangerouslySetInnerHTML={html}></span>;
} }
@ -452,7 +457,13 @@ export default class FormulaControl extends React.Component<
)} )}
<PickerContainer <PickerContainer
showTitle={false} showTitle={false}
bodyRender={({onClose, value, onChange}) => { bodyRender={({
value,
onChange
}: {
onChange: (value: any) => void;
value: any;
}) => {
return ( return (
<FormulaEditor <FormulaEditor
{...rest} {...rest}
@ -470,7 +481,7 @@ export default class FormulaControl extends React.Component<
onConfirm={this.handleConfirm} onConfirm={this.handleConfirm}
size="md" size="md"
> >
{({onClick, isOpened}) => ( {({onClick}: {onClick: (e: React.MouseEvent) => void}) => (
<Button <Button
size="sm" size="sm"
tooltip={'点击配置表达式'} 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 {autobind} from 'amis-editor-core';
import {getSchemaTpl} 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 {Option} from 'amis';
import type {FormControlProps} from 'amis-core'; import type {FormControlProps} from 'amis-core';
@ -396,33 +396,29 @@ export default class OptionControl extends React.Component<
} }
renderHeader() { renderHeader() {
const { const {render, label, labelRemark, useMobileUI, env, popOverContainer} =
render, this.props;
label,
labelRemark,
useMobileUI,
env,
popOverContainer
} = this.props;
const classPrefix = env?.theme?.classPrefix; const classPrefix = env?.theme?.classPrefix;
const {source} = this.state; const {source} = this.state;
const optionSourceList = ([ const optionSourceList = (
{ [
label: '自定义选项', {
value: 'custom' label: '自定义选项',
}, value: 'custom'
{ },
label: '接口获取', {
value: 'api' label: '接口获取',
} value: 'api'
// { }
// label: '表单实体', // {
// value: 'form' // label: '表单实体',
// } // value: 'form'
] as Array<{ // }
label: string; ] as Array<{
value: 'custom' | 'api' | 'form'; label: string;
}>).map(item => ({ value: 'custom' | 'api' | 'form';
}>
).map(item => ({
...item, ...item,
onClick: () => this.handleSourceChange(item.value) onClick: () => this.handleSourceChange(item.value)
})); }));
@ -586,7 +582,7 @@ export default class OptionControl extends React.Component<
value={label} value={label}
placeholder="请输入文本/值" placeholder="请输入文本/值"
clearable={false} clearable={false}
onChange={value => this.handleEditLabel(index, value)} onChange={(value: string) => this.handleEditLabel(index, value)}
/> />
{amisRender({ {amisRender({
type: 'dropdown-button', type: 'dropdown-button',
@ -636,8 +632,7 @@ export default class OptionControl extends React.Component<
body: [ body: [
{ {
type: 'tpl', type: 'tpl',
tpl: tpl: '每个选项单列一行,将所有值不重复的项加为新的选项;<br/>每行可通过空格来分别设置label和value,例:"张三 zhangsan"'
'每个选项单列一行,将所有值不重复的项加为新的选项;<br/>每行可通过空格来分别设置label和value,例:"张三 zhangsan"'
} }
], ],
showIcon: true, showIcon: true,
@ -700,6 +695,7 @@ export default class OptionControl extends React.Component<
getSchemaTpl('apiControl', { getSchemaTpl('apiControl', {
label: '接口', label: '接口',
name: 'source', name: 'source',
mode: 'normal',
className: 'ae-ExtendMore', className: 'ae-ExtendMore',
visibleOn: 'data.autoComplete !== false', visibleOn: 'data.autoComplete !== false',
value: api, 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 @autobind
handleSwitchChange(checked: boolean) { handleSwitchChange(checked: boolean) {
const {onBulkChange, onChange, bulk, defaultData, name} = this.props; const {onBulkChange, onChange, bulk, defaultData, name, pipeOut} = this.props;
this.setState({checked}); this.setState({checked});
@ -179,19 +179,30 @@ export default class SwitchMore extends React.Component<
if (checked) { if (checked) {
let data = defaultData ? {...defaultData} : {}; let data = defaultData ? {...defaultData} : {};
name && (data[name] = true); name && (data[name] = true);
if (pipeOut) {
data = pipeOut(data);
}
onBulkChange && onBulkChange(data); onBulkChange && onBulkChange(data);
} }
// 取消选中后,讲所有字段重置 // 取消选中后,讲所有字段重置
else { else {
const values = fromPairs( let values = fromPairs(
this.getFormItemNames().map(i => [i, undefined]) this.getFormItemNames().map(i => [i, undefined])
); );
name && (values[name] = false); name && (values[name] = false);
if (pipeOut) {
values = pipeOut(values);
}
onBulkChange && onBulkChange(values); onBulkChange && onBulkChange(values);
} }
return; return;
} }
onChange(checked ? defaultData || true : undefined);
let data = checked ? defaultData || true : undefined;
if (pipeOut) {
data = pipeOut(data);
}
onChange(data);
} }
@autobind @autobind

View File

@ -8,7 +8,7 @@ import {render, Button, Switch} from 'amis';
import {autobind} from 'amis-editor-core'; import {autobind} from 'amis-editor-core';
import {Validator} from '../validator'; import {Validator} from '../validator';
import {tipedLabel} from '../component/BaseControl'; import {tipedLabel} from 'amis-editor-core';
import {SchemaCollection} from 'amis/lib/Schema'; import {SchemaCollection} from 'amis/lib/Schema';
export type ValidatorData = { 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 React from 'react';
import {buildApi, Html} from 'amis'; import {buildApi, Html} from 'amis';
import {get} from 'lodash';
setSchemaTpl('api', (patch: any = {}) => { setSchemaTpl('api', (patch: any = {}) => {
const {name, label, value, description, sampleBuilder, ...rest} = patch; const {name, label, value, description, sampleBuilder, apiDesc, ...rest} =
patch;
return { return {
type: 'container', type: 'container',
@ -13,21 +15,25 @@ setSchemaTpl('api', (patch: any = {}) => {
label: label || 'API', label: label || 'API',
labelRemark: sampleBuilder labelRemark: sampleBuilder
? { ? {
icon: '', label: false,
label: '示例',
title: '接口返回示例', title: '接口返回示例',
icon: 'fas fa-code',
className: 'm-l-xs ae-ApiSample-icon',
tooltipClassName: 'ae-ApiSample-tooltip', tooltipClassName: 'ae-ApiSample-tooltip',
render: (data: any) => ( children: () => {
<Html return (
className="ae-ApiSample" <Html
inline={false} className="ae-ApiSample"
html={` inline={false}
<pre><code>${sampleBuilder(data)}</code></pre> html={`<pre><code>${sampleBuilder()}</code></pre>${
`} apiDesc
/> ? `<span class="ae-ApiSample-desc">${apiDesc}</span>`
), : ''
}`}
/>
);
},
trigger: 'click', trigger: 'click',
className: 'm-l-xs',
rootClose: true, rootClose: true,
placement: 'left' placement: 'left'
} }
@ -166,6 +172,7 @@ setSchemaTpl('api', (patch: any = {}) => {
mode: 'normal', mode: 'normal',
renderLabel: false, renderLabel: false,
visibleOn: 'this.data', visibleOn: 'this.data',
valueType: 'ae-DataPickerControl',
descriptionClassName: 'help-block text-xs m-b-none', descriptionClassName: 'help-block text-xs m-b-none',
description: description:
'<p>当没开启数据映射时,发送数据自动切成白名单模式,配置啥发送啥,请绑定数据。如:<code>{"a": "\\${a}", "b": 2}</code></p><p>如果希望在默认的基础上定制,请先添加一个 Key 为 `&` Value 为 `\\$$` 作为第一行。</p><div>当值为 <code>__undefined</code>时,表示删除对应的字段,可以结合<code>{"&": "\\$$"}</code>来达到黑名单效果。</div>' '<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://' placeholder: 'http://'
}); });
setSchemaTpl('initFetch', { setSchemaTpl(
type: 'group', 'initFetch',
label: '是否初始加载', (overrides: {visibleOn?: string; name?: string} = {}) => {
visibleOn: 'this.initApi', const visibleOn = get(overrides, 'visibleOn', 'this.initApi');
direction: 'vertical', const fieldName = get(overrides, 'name', 'initFetch');
className: 'm-b-none', const label = get(overrides, 'label', '是否初始加载');
labelRemark: {
trigger: 'click', return {
rootClose: true, type: 'group',
className: 'm-l-xs', label: tipedLabel(
content: label,
'当配置初始化接口后,组件初始就会拉取接口数据,可以通过以下配置修改。', '当配置初始化接口后,组件初始就会拉取接口数据,可以通过以下配置修改。'
placement: 'left' ),
}, visibleOn,
body: [ direction: 'vertical',
{ body: [
name: 'initFetch',
type: 'radios',
inline: true,
onChange: () => {},
// pipeIn: (value:any) => typeof value === 'boolean' ? value : '1'
options: [
{ {
label: '是', name: fieldName,
value: true type: 'radios',
inline: true,
onChange: () => {},
// pipeIn: (value:any) => typeof value === 'boolean' ? value : '1'
options: [
{
label: '是',
value: true
},
{
label: '否',
value: false
},
{
label: '表达式',
value: ''
}
]
}, },
{ getSchemaTpl('valueFormula', {
label: '否', label: '',
value: false name: `${fieldName}On`,
}, autoComplete: false,
visibleOn: `typeof this.${fieldName} !== "boolean"`,
{ placeholder: '如this.id 表示有 id 值时初始加载',
label: '表达式', className: 'm-t-n-sm'
value: '' })
} // {
// 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', { setSchemaTpl('proxy', {
type: 'switch', type: 'switch',
@ -409,31 +427,35 @@ setSchemaTpl('proxy', {
}); });
setSchemaTpl('apiControl', (patch: any = {}) => { setSchemaTpl('apiControl', (patch: any = {}) => {
const {name, label, value, description, sampleBuilder, ...rest} = patch; const {name, label, value, description, sampleBuilder, apiDesc, ...rest} =
patch;
return { return {
type: 'ae-apiControl', type: 'ae-apiControl',
label, label,
name, name,
description, description,
mode: 'normal',
labelRemark: sampleBuilder labelRemark: sampleBuilder
? { ? {
icon: '', label: false,
label: '示例',
title: '接口返回示例', title: '接口返回示例',
icon: 'fas fa-code',
className: 'm-l-xs ae-ApiSample-icon',
tooltipClassName: 'ae-ApiSample-tooltip', tooltipClassName: 'ae-ApiSample-tooltip',
render: (data: any) => ( children: () => {
<Html return (
className="ae-ApiSample" <Html
inline={false} className="ae-ApiSample"
html={` inline={false}
<pre><code>${sampleBuilder(data)}</code></pre> html={`<pre><code>${sampleBuilder()}</code></pre>${
`} apiDesc
/> ? `<span class="ae-ApiSample-desc">${apiDesc}</span>`
), : ''
}`}
/>
);
},
trigger: 'click', trigger: 'click',
className: 'm-l-xs',
rootClose: true, rootClose: true,
placement: 'left' 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, setSchemaTpl,
getSchemaTpl, getSchemaTpl,
defaultValue, defaultValue,
isObject isObject,
tipedLabel,
DSField
} from 'amis-editor-core'; } from 'amis-editor-core';
import {remarkTpl, tipedLabel} from '../component/BaseControl'; import {remarkTpl} from '../component/BaseControl';
import {SchemaObject} from 'amis/lib/Schema'; import {SchemaObject} from 'amis/lib/Schema';
import flatten from 'lodash/flatten'; import flatten from 'lodash/flatten';
import {InputComponentName} from '../component/InputComponentName'; import {InputComponentName} from '../component/InputComponentName';
@ -22,13 +24,46 @@ setSchemaTpl('switch', {
inputClassName: 'is-inline ' 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 * name
*/ */
setSchemaTpl('formItemName', { setSchemaTpl('formItemName', {
label: '字段名', label: '字段名',
name: 'name', name: 'name',
type: 'ae-DataBindingControl' type: 'ae-DataBindingControl',
onBindingChange(field: DSField, onBulkChange: (value: any) => void) {
onBulkChange(field.resolveEditSchema?.() || {label: field.label});
}
// validations: { // validations: {
// matchRegexp: /^[a-z\$][a-z0-0\-_]*$/i // matchRegexp: /^[a-z\$][a-z0-0\-_]*$/i
// }, // },
@ -38,37 +73,45 @@ setSchemaTpl('formItemName', {
// validateOnChange: false // validateOnChange: false
}); });
setSchemaTpl('formItemMode', { setSchemaTpl(
label: '布局', 'formItemMode',
name: 'mode', (config: {
type: 'button-group-select', // 是不是独立表单,没有可以集成的内容
option: '继承', isForm: boolean;
horizontal: { }) => ({
left: 2, label: '布局',
justify: true name: 'mode',
}, type: 'button-group-select',
// className: 'w-full', option: '继承',
pipeIn: defaultValue(''), horizontal: {
options: [ left: 2,
{ justify: true
label: '内联',
value: 'inline'
}, },
{ // className: 'w-full',
label: '水平', pipeIn: defaultValue(''),
value: 'horizontal' options: [
}, {
{ label: '内联',
label: '垂直', value: 'inline'
value: 'normal' },
}, {
{ label: '水平',
label: '继承', value: 'horizontal'
value: '' },
} {
], label: '垂直',
pipeOut: (v: string) => (v ? v : undefined) value: 'normal'
}); },
config?.isForm
? null
: {
label: '继承',
value: ''
}
].filter(i => i),
pipeOut: (v: string) => (v ? v : undefined)
})
);
setSchemaTpl('formItemInline', { setSchemaTpl('formItemInline', {
type: 'switch', type: 'switch',
@ -243,37 +286,31 @@ setSchemaTpl(
key: string; key: string;
visibleOn: string; visibleOn: string;
body: Array<any>; body: Array<any>;
}>, }>
rendererSchema?: any
) => { ) => {
let currentKey = rendererSchema
? `${rendererSchema.$$id}_${rendererSchema.type}_${rendererSchema.configTitle}_collapse`
: `config_collapse`;
currentKey = currentKey.replace(/-/g, '__');
const collapseGroupBody = config const collapseGroupBody = config
.filter( .filter(
item => item && Array.isArray(item?.body) && item?.body.length > 0 item => item && Array.isArray(item?.body) && item?.body.length > 0
) )
.map((item, index) => ({ .map(item => ({
type: 'collapse', type: 'collapse',
collapsed: false,
headingClassName: 'ae-formItemControl-header', headingClassName: 'ae-formItemControl-header',
bodyClassName: 'ae-formItemControl-body', bodyClassName: 'ae-formItemControl-body',
...item, ...item,
key: `${currentKey}_${item.key || index.toString()}`, key: item.title,
body: flatten(item.body) body: flatten(item.body)
})); }));
return { return {
type: 'collapse-group', type: 'collapse-group',
key: currentKey, activeKey: collapseGroupBody.map(panel => panel.title),
expandIconPosition: 'right', expandIconPosition: 'right',
expandIcon: { expandIcon: {
type: 'icon', type: 'icon',
icon: 'chevron-right' icon: 'chevron-right'
}, },
className: 'ae-formItemControl ae-styleControl', className: 'ae-formItemControl ae-styleControl',
activeKey: collapseGroupBody.map((group, index) => group.key),
body: collapseGroupBody body: collapseGroupBody
}; };
} }
@ -346,7 +383,7 @@ setSchemaTpl(
body: [ body: [
{ {
type: 'ae-formulaControl', type: 'ae-formulaControl',
label: config?.label || '默认值', label: config?.label ?? '默认值',
name: config?.name || 'value', name: config?.name || 'value',
rendererSchema: curRendererSchema, rendererSchema: curRendererSchema,
rendererWrapper: config?.rendererWrapper, rendererWrapper: config?.rendererWrapper,
@ -509,28 +546,29 @@ setSchemaTpl('size', {
}); });
setSchemaTpl('name', { setSchemaTpl('name', {
label: '名字', label: tipedLabel(
'名字',
'需要联动时才需要,其他组件可以通过这个名字跟当前组件联动'
),
name: 'name', name: 'name',
type: 'input-text', type: 'input-text',
description: '需要联动时才需要,其他组件可以通过这个名字跟当前组件联动',
placeholder: '请输入字母或者数字' placeholder: '请输入字母或者数字'
}); });
setSchemaTpl('reload', { setSchemaTpl('reload', {
label: '刷新目标组件',
name: 'reload', name: 'reload',
asFormItem: true, asFormItem: true,
// type: 'input-text', // type: 'input-text',
component: InputComponentName, component: InputComponentName,
description: label: tipedLabel(
'可以指定操作完成后刷新目标组件,请填写目标组件的 <code>name</code> 属性,多个组件请用<code>,</code>隔开,如果目标组件为表单项,请先填写表单的名字,再用<code>.</code>连接表单项的名字如:<code>xxForm.xxControl</code>。另外如果刷新目标对象设置为 <code>window</code>,则会刷新整个页面。', '刷新目标组件',
labelRemark: { '可以指定操作完成后刷新目标组件,请填写目标组件的 <code>name</code> 属性,多个组件请用<code>,</code>隔开,如果目标组件为表单项,请先填写表单的名字,再用<code>.</code>连接表单项的名字如:<code>xxForm.xxControl</code>。另外如果刷新目标对象设置为 <code>window</code>,则会刷新整个页面。'
trigger: 'click', ),
className: 'm-l-xs', placeholder: '请输入组件name',
rootClose: true, mode: 'horizontal',
content: horizontal: {
'设置名字后,当前组件操作完成会触发目标组件(根据设置的名字)的刷新。', left: 4,
placement: 'left' justify: true
} }
}); });
@ -577,7 +615,7 @@ setSchemaTpl(
? getSchemaTpl('disabled') ? getSchemaTpl('disabled')
: null, : null,
config?.isFormItem ? getSchemaTpl('clearValueOnHidden') : null config?.isFormItem ? getSchemaTpl('clearValueOnHidden') : null
] ].filter(Boolean)
}; };
} }
); );

View File

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

View File

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

View File

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