feat:transfer配置面板

Change-Id: I7f7a0ff3d6c16709ef8eaf912d06f50f00c5c41d
This commit is contained in:
zhou999 2022-07-22 13:29:45 +08:00
parent 530766f87a
commit 5a748d67f9
4 changed files with 662 additions and 289 deletions

View File

@ -155,6 +155,7 @@ import './renderer/DataMappingControl';
import './renderer/DataPickerControl';
import './renderer/event-control/index';
import './renderer/TreeOptionControl';
import './renderer/TransferTableControl';
export * from './component/BaseControl';
export * from './icons/index';

View File

@ -218,6 +218,10 @@ export class TabsTransferPlugin extends BasePlugin {
}
];
notRenderFormZone = true;
panelJustify = true;
panelDefinitions = {
options: {
label: '选项 Options',
@ -269,65 +273,64 @@ export class TabsTransferPlugin extends BasePlugin {
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('switchDefaultValue'),
{
type: 'select',
name: 'value',
label: '默认值',
source: '${options}',
multiple: true,
visibleOn: 'typeof this.value !== "undefined"'
},
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('searchable'),
getSchemaTpl('api', {
label: '检索接口',
name: 'searchApi'
}),
{
label: '查询时勾选展示模式',
name: 'searchResultMode',
type: 'select',
mode: 'inline',
className: 'w-full',
options: [
getSchemaTpl('searchable'),
getSchemaTpl('api', {
label: '检索接口',
name: 'searchApi'
}),
{
label: '列表形式',
value: 'list'
label: '查询时勾选展示模式',
name: 'searchResultMode',
type: 'select',
mode: 'normal',
options: [
{
label: '列表形式',
value: 'list'
},
{
label: '表格形式',
value: 'table'
},
{
label: '树形选择形式',
value: 'tree'
},
{
label: '级联选择形式',
value: 'chained'
}
]
},
{
label: '表格形式',
value: 'table'
},
{
label: '树形选择形式',
value: 'tree'
},
{
label: '级联选择形式',
value: 'chained'
}
getSchemaTpl('sortable'),
getSchemaTpl('formulaControl', {
label: '左侧选项标题',
name: 'selectTitle',
type: 'input-text',
inputClassName: 'is-inline '
}),
getSchemaTpl('formulaControl', {
label: '右侧结果标题',
name: 'resultTitle',
type: 'input-text',
inputClassName: 'is-inline '
})
]
},
getSchemaTpl('sortable'),
{
label: '左侧的标题文字',
name: 'selectTitle',
type: 'input-text'
},
{
label: '右侧结果的标题文字',
name: 'resultTitle',
type: 'input-text'
},
getSchemaTpl('fieldSet', {
title: '选项',
body: [
{
@ -340,7 +343,8 @@ export class TabsTransferPlugin extends BasePlugin {
getSchemaTpl('extractValue'),
getSchemaTpl('autoFill')
]
})
},
getSchemaTpl('status', {isFormItem: true})
])
},
{

View File

@ -7,6 +7,9 @@ import {
RendererPluginEvent
} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {tipedLabel} from '../../component/BaseControl';
export class TransferPlugin extends BasePlugin {
// 关联渲染器名字
rendererName = 'transfer';
@ -26,43 +29,28 @@ export class TransferPlugin extends BasePlugin {
name: 'transfer',
options: [
{
label: '法师',
children: [
{
label: '诸葛亮',
value: 'zhugeliang'
}
]
label: '诸葛亮',
value: 'zhugeliang'
},
{
label: '战士',
children: [
{
label: '曹操',
value: 'caocao'
},
{
label: '钟无艳',
value: 'zhongwuyan'
}
]
label: '曹操',
value: 'caocao'
},
{
label: '打野',
children: [
{
label: '李白',
value: 'libai'
},
{
label: '韩信',
value: 'hanxin'
},
{
label: '云中君',
value: 'yunzhongjun'
}
]
label: '钟无艳',
value: 'zhongwuyan'
},
{
label: '李白',
value: 'libai'
},
{
label: '韩信',
value: 'hanxin'
},
{
label: '云中君',
value: 'yunzhongjun'
}
]
};
@ -181,7 +169,9 @@ export class TransferPlugin extends BasePlugin {
}
};
// notRenderFormZone = true;
notRenderFormZone = true;
panelJustify = true;
panelBodyCreator = (context: BaseEventContext) => {
const renderer: any = context.info.renderer;
@ -190,231 +180,150 @@ export class TransferPlugin extends BasePlugin {
{
title: '属性',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('switchDefaultValue'),
{
type: 'select',
name: 'value',
label: '默认值',
source: '${options}',
visibleOn: '!data.multiple && typeof this.value !== "undefined"'
},
{
type: 'select',
name: 'value',
label: '默认值',
source: '${options}',
multiple: true,
visibleOn: ' data.multiple && typeof this.value !== "undefined"'
},
{
label: '勾选展示模式',
name: 'selectMode',
type: 'select',
mode: 'inline',
className: 'w-full',
options: [
{
label: '列表形式',
value: 'list'
},
{
label: '表格形式',
value: 'table'
},
{
label: '树形选择形式',
value: 'tree'
},
{
label: '级联选择形式',
value: 'chained'
},
{
label: '关联选择形式',
value: 'associated'
}
title: '基本',
body: [
getSchemaTpl('formItemName', {
required: true
}),
getSchemaTpl('label'),
getSchemaTpl('labelRemark'),
getSchemaTpl('remark'),
getSchemaTpl('placeholder'),
getSchemaTpl('description'),
getSchemaTpl('switch', {
label: '统计数据',
name: 'statistics'
})
]
},
{
name: 'columns',
type: 'combo',
multiple: true,
label: false,
strictMode: false,
addButtonText: '新增一列',
draggable: false,
visibleOn: 'data.selectMode === "table"',
items: [
{
type: 'input-text',
name: 'label',
placeholder: '标题'
},
{
type: 'input-text',
name: 'name',
placeholder: '绑定字段名'
},
title: '左侧选项面板',
body: [
{
label: '展示形式',
name: 'selectMode',
type: 'select',
name: 'type',
placeholder: '类型',
value: 'input-text',
options: [
{
value: 'text',
label: '纯文本'
label: '列表形式',
value: 'list'
},
{
value: 'tpl',
label: '模板'
label: '表格形式',
value: 'table'
},
{
value: 'image',
label: '图片'
},
{
value: 'date',
label: '日期'
},
{
value: 'progress',
label: '进度'
},
{
value: 'status',
label: '状态'
},
{
value: 'mapping',
label: '映射'
},
{
value: 'operation',
label: '操作栏'
label: '树形形式',
value: 'tree'
}
]
}
]
},
{
$ref: 'options',
label: '左边的选项集',
name: 'leftOptions',
visibleOn: 'data.selectMode === "associated"'
},
{
label: '左侧选择形式',
name: 'leftMode',
type: 'select',
mode: 'inline',
className: 'w-full',
visibleOn: 'data.selectMode === "associated"',
options: [
{
label: '列表形式',
value: 'list'
],
onChange: (value: any, origin: any, item: any, form: any) => {
form.setValueByName('options', undefined);
}
},
{
label: '树形选择形式',
value: 'tree'
}
]
},
{
label: '右侧选择形式',
name: 'rightMode',
type: 'select',
mode: 'inline',
className: 'w-full',
visibleOn: 'data.selectMode === "associated"',
options: [
getSchemaTpl('optionControl', {
visibleOn: 'data.selectMode === "list"',
multiple: true
}),
{
label: '列表形式',
value: 'list'
type: 'ae-transferTableControl',
name: 'options',
label: '数据',
visibleOn: 'data.selectMode === "table"',
mode: 'normal'
},
{
label: '树形选择形式',
value: 'tree'
}
]
getSchemaTpl('treeOptionControl', {
visibleOn: 'data.selectMode === "tree"',
}),
getSchemaTpl('switch', {
label: '可检索',
name: 'searchable'
}),
getSchemaTpl('menuTpl', {
label: tipedLabel('模板', '左侧选项渲染模板支持JSX变量使用\\${xx}')
}),
getSchemaTpl('formulaControl', {
label: '标题',
name: 'selectTitle',
type: 'input-text',
inputClassName: 'is-inline '
})
],
},
getSchemaTpl('searchable'),
getSchemaTpl('api', {
label: '检索接口',
name: 'searchApi'
}),
{
label: '查询时勾选展示模式',
name: 'searchResultMode',
type: 'select',
mode: 'inline',
className: 'w-full',
options: [
{
label: '列表形式',
value: 'list'
},
{
label: '表格形式',
value: 'table'
},
{
label: '树形选择形式',
value: 'tree'
},
{
label: '级联选择形式',
value: 'chained'
}
]
},
getSchemaTpl('sortable'),
getSchemaTpl('selectFirst'),
getSchemaTpl('switch', {
label: '是否显示统计数据',
name: 'statistics'
}),
{
label: '左侧的标题文字',
name: 'selectTitle',
type: 'input-text'
},
{
label: '右侧结果的标题文字',
name: 'resultTitle',
type: 'input-text'
},
getSchemaTpl('fieldSet', {
title: '选项',
title: '右侧结果面板',
body: [
{
$ref: 'options',
name: 'options'
type: 'button-group-select',
label: '展示形式',
name: 'resultListModeFollowSelect',
inputClassName: 'items-center',
options: [
{label: '列表形式', value: false},
{label: '跟随左侧', value: true},
],
},
getSchemaTpl('source'),
getSchemaTpl('joinValues'),
getSchemaTpl('delimiter'),
getSchemaTpl('extractValue'),
getSchemaTpl('autoFill')
getSchemaTpl('switch', {
label: tipedLabel(
'可检索',
'查询功能目前只支持根据名称或值来模糊匹配查询'
),
name: 'resultSearchable'
}),
getSchemaTpl('sortable', {
label: '支持排序',
mode: 'horizontal',
horizontal: {
justify: true,
left: 8
},
inputClassName: 'is-inline',
visibleOn: 'data.selectMode === "list" && !data.resultListModeFollowSelect',
}),
getSchemaTpl('menuTpl', {
name: 'valueTpl',
label: tipedLabel('模板', '结果选项渲染模板支持JSX变量使用\\${xx}')
}),
getSchemaTpl('formulaControl', {
label: '标题',
name: 'resultTitle',
type: 'input-text',
inputClassName: 'is-inline '
})
]
})
},
getSchemaTpl('status', {isFormItem: true}),
getSchemaTpl('validation', {tag: ValidatorTag.MultiSelect})
])
},
{
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:formItem', renderer),
getSchemaTpl('style:classNames', [
getSchemaTpl('className', {
label: '描述',
name: 'descriptionClassName',
visibleOn: 'this.description'
}),
getSchemaTpl('className', {
name: 'addOn.className',
label: 'AddOn',
visibleOn: 'this.addOn && this.addOn.type === "text"'
})
])
])
},
{

View File

@ -0,0 +1,459 @@
/**
* @file Transfer的表格对应选项
*/
import React from 'react';
import { render as amisRender, FormItem } from 'amis';
import { omit } from 'lodash';
import { SchemaApi } from 'amis/lib/Schema';
import { autobind, getSchemaTpl } from 'amis-editor-core';
import cx from 'classnames';
import { tipedLabel } from '../component/BaseControl';
import type { FormControlProps } from 'amis-core';
import type { Option } from 'amis';
interface OptionControlProps extends FormControlProps {
className?: string;
}
interface OptionControlState {
api: SchemaApi;
labelField: string;
valueField: string;
source: 'custom' | 'api' | 'form';
};
function BaseOptionControl(Cmpt: React.JSXElementConstructor<any>) {
return class extends React.Component<OptionControlProps, OptionControlState> {
$comp: string; // 记录一下路径,不再从外部同步内部,只从内部同步外部
internalProps = ['checked', 'editing'];
constructor(props: OptionControlProps) {
super(props);
this.state = {
api: props.data.source,
labelField: props.data.labelField,
valueField: props.data.valueField,
source: props.data.source ? 'api' : 'custom'
};
this.handleSourceChange = this.handleSourceChange.bind(this);
this.handleAPIChange = this.handleAPIChange.bind(this);
this.handleLableFieldChange = this.handleLableFieldChange.bind(this);
this.handleValueFieldChange = this.handleValueFieldChange.bind(this);
this.onChange = this.onChange.bind(this);
}
/**
* options字段的统一出口
*/
onChange() {
const { source } = this.state;
const { onBulkChange } = this.props;
const data: Partial<OptionControlProps> = {
source: undefined,
options: undefined,
labelField: undefined,
valueField: undefined
};
if (source === 'api') {
const { api, labelField, valueField } = this.state;
data.source = api;
data.labelField = labelField;
data.valueField = valueField;
}
onBulkChange && onBulkChange(data);
return;
}
/**
*
*/
handleSourceChange(source: 'custom' | 'api' | 'form') {
this.setState({ source: source }, this.onChange);
}
handleAPIChange(source: SchemaApi) {
this.setState({ api: source }, this.onChange);
}
handleLableFieldChange(labelField: string) {
this.setState({ labelField }, this.onChange);
}
handleValueFieldChange(valueField: string) {
this.setState({ valueField }, this.onChange);
}
buildBatchAddSchema() {
return {
type: 'action',
actionType: 'dialog',
label: '批量添加',
dialog: {
title: '批量添加选项',
headerClassName: 'font-bold',
closeOnEsc: true,
closeOnOutside: false,
showCloseButton: true,
body: [
{
type: 'alert',
level: 'warning',
body: [
{
type: 'tpl',
tpl: '每个选项单列一行,将所有值不重复的项加为新的选项;<br/>每行可通过空格来分别设置label和value,例:"张三 zhangsan"'
}
],
showIcon: true,
className: 'mb-2.5'
},
{
type: 'form',
wrapWithPanel: false,
mode: 'normal',
wrapperComponent: 'div',
resetAfterSubmit: true,
autoFocus: true,
preventEnterSubmit: true,
horizontal: {
left: 0,
right: 12
},
body: [
{
name: 'batchOption',
type: 'textarea',
label: '',
placeholder: '请输入选项内容',
trimContents: true,
minRows: 10,
maxRows: 50,
required: true
}
]
}
]
}
};
}
renderHeader() {
const { render, label, labelRemark, useMobileUI, env, popOverContainer } =
this.props;
const classPrefix = env?.theme?.classPrefix;
const { source } = this.state;
const optionSourceList = (
[
{
label: '自定义选项',
value: 'custom'
},
{
label: '接口获取',
value: 'api'
}
] as Array<{
label: string;
value: 'custom' | 'api' | 'form';
}>
).map(item => ({
...item,
onClick: () => this.handleSourceChange(item.value)
}));
return (
<header className="ae-OptionControl-header">
<label className={cx(`${classPrefix}Form-label`)}>
{label || ''}
{labelRemark
? render('label-remark', {
type: 'remark',
icon: labelRemark.icon || 'warning-mark',
tooltip: labelRemark,
className: cx(`Form-lableRemark`, labelRemark?.className),
useMobileUI,
container: popOverContainer
? popOverContainer
: env && env.getModalContainer
? env.getModalContainer
: undefined
})
: null}
</label>
<div>
{render(
'validation-control-addBtn',
{
type: 'dropdown-button',
level: 'link',
size: 'sm',
label: '${selected}',
align: 'right',
closeOnClick: true,
closeOnOutside: true,
buttons: optionSourceList
},
{
popOverContainer: null,
data: {
selected: optionSourceList.find(item => item.value === source)!
.label
}
}
)}
</div>
</header>
);
}
renderApiPanel() {
const { render } = this.props;
const { source, api, labelField, valueField } = this.state;
if (source !== 'api') {
return null;
}
return render(
'api',
getSchemaTpl('apiControl', {
label: '接口',
name: 'source',
className: 'ae-ExtendMore',
visibleOn: 'data.autoComplete !== false',
value: api,
onChange: this.handleAPIChange,
footer: [
{
label: tipedLabel(
'显示字段',
'选项文本对应的数据字段,多字段合并请通过模板配置'
),
type: 'input-text',
name: 'labelField',
value: labelField,
placeholder: '选项文本对应的字段',
onChange: this.handleLableFieldChange
},
{
label: '值字段',
type: 'input-text',
name: 'valueField',
value: valueField,
placeholder: '值对应的字段',
onChange: this.handleValueFieldChange
}
]
})
);
}
render() {
const { source, api, labelField, valueField } = this.state;
const { className } = this.props;
const cmptProps = {
...this.props,
data: {
api,
labelField,
valueField,
...this.props?.data
},
}
return (
<div className={cx('ae-OptionControl', className)}>
{this.renderHeader()}
{source === 'custom' ? (
<Cmpt {...cmptProps} />
) : null}
{this.renderApiPanel()}
</div>
);
}
}
}
const renderInput = (
name: string,
placeholder: string,
required: boolean = true
) => {
return {
type: 'input-text',
name,
placeholder: placeholder,
required
}
}
export default class TransferTableOption extends React.Component<OptionControlProps, {}> {
addColumns() {
const { columns = [{ type: 'text' }] } = this.props.data;
return {
type: 'action',
actionType: 'dialog',
label: '添加表格列',
level: 'enhance',
dialog: {
title: '设置表格列选项',
headerClassName: 'font-bold',
closeOnEsc: true,
closeOnOutside: false,
showCloseButton: true,
body: [
{
name: 'columns',
type: 'combo',
multiple: true,
label: false,
strictMode: false,
addButtonText: '新增一列',
draggable: false,
value: columns,
onChange: (value: Array<Option>) => this.handleColumnsChange(value),
items: [
{
type: 'input-text',
name: 'label',
placeholder: '标题'
},
{
type: 'input-text',
name: 'name',
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: 'operation',
label: '操作栏'
}
]
}
]
}
]
}
};
}
addRows() {
const { columns = [], options = [{}] } = this.props.data;
return {
type: 'tooltip-wrapper',
tooltip: '需设置表格列后,才能设置表格行',
tooltipTheme: 'dark',
placement: 'top',
tooltipStyle: {
fontSize: '12px'
},
className: 'ae-formItemControl-label-tip',
body: [{
type: 'action',
actionType: 'dialog',
label: '添加表格行',
level: 'enhance',
disabled: columns && columns.length === 0,
block: true,
dialog: {
title: '设置表格行选项',
headerClassName: 'font-bold',
closeOnEsc: true,
closeOnOutside: false,
showCloseButton: true,
size: columns.length >= 6 ? 'md' : '',
body: [{
name: 'options',
type: 'combo',
multiple: true,
draggable: true,
addButtonText: '新增',
value: options,
onChange: (value: Array<Option>) => this.handleRowsChange(value),
items: [
...columns.map((item: Option) => renderInput(item.name, item.label ?? '')),
renderInput('value', '值', true)
]
}]
}
}]
};
}
@autobind
handleColumnsChange(value: Array<Option>) {
const { data } = this.props;
const { onBulkChange } = this.props;
data.columns = value.map(item => omit(item, 'id'));
onBulkChange && onBulkChange(data);
}
@autobind
handleRowsChange(value: Array<Option>) {
const { data } = this.props;
const { onBulkChange } = this.props;
data.options = value;
onBulkChange && onBulkChange(data);
}
render() {
return (
<div className='ae-OptionControl-footer'>
{amisRender(this.addColumns())}
{amisRender(this.addRows())}
</div>)
}
}
const TransferTableControl = BaseOptionControl(TransferTableOption);
@FormItem({
type: 'ae-transferTableControl',
renderLabel: false
})
export class TransferTableControlRenderer extends TransferTableControl { }