From 5a748d67f9b740384bf416e232b142c2a2ea13b9 Mon Sep 17 00:00:00 2001 From: zhou999 Date: Fri, 22 Jul 2022 13:29:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:transfer=E9=85=8D=E7=BD=AE=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7f7a0ff3d6c16709ef8eaf912d06f50f00c5c41d --- packages/amis-editor/src/index.tsx | 1 + .../src/plugin/Form/TabsTransfer.tsx | 110 +++-- .../amis-editor/src/plugin/Form/Transfer.tsx | 381 ++++++--------- .../src/renderer/TransferTableControl.tsx | 459 ++++++++++++++++++ 4 files changed, 662 insertions(+), 289 deletions(-) create mode 100644 packages/amis-editor/src/renderer/TransferTableControl.tsx diff --git a/packages/amis-editor/src/index.tsx b/packages/amis-editor/src/index.tsx index 2e59af6d8..8f3a1f02d 100644 --- a/packages/amis-editor/src/index.tsx +++ b/packages/amis-editor/src/index.tsx @@ -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'; diff --git a/packages/amis-editor/src/plugin/Form/TabsTransfer.tsx b/packages/amis-editor/src/plugin/Form/TabsTransfer.tsx index 586e4fefd..39fccede9 100644 --- a/packages/amis-editor/src/plugin/Form/TabsTransfer.tsx +++ b/packages/amis-editor/src/plugin/Form/TabsTransfer.tsx @@ -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}) ]) }, { diff --git a/packages/amis-editor/src/plugin/Form/Transfer.tsx b/packages/amis-editor/src/plugin/Form/Transfer.tsx index 8190e1779..bdc2004ac 100644 --- a/packages/amis-editor/src/plugin/Form/Transfer.tsx +++ b/packages/amis-editor/src/plugin/Form/Transfer.tsx @@ -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"' + }) + ]) ]) }, { diff --git a/packages/amis-editor/src/renderer/TransferTableControl.tsx b/packages/amis-editor/src/renderer/TransferTableControl.tsx new file mode 100644 index 000000000..5eb7acba1 --- /dev/null +++ b/packages/amis-editor/src/renderer/TransferTableControl.tsx @@ -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) { + return class extends React.Component { + + $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 = { + 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: '每个选项单列一行,将所有值不重复的项加为新的选项;
每行可通过空格来分别设置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 ( +
+ +
+ {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 + } + } + )} +
+
+ ); + } + + 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 ( +
+ {this.renderHeader()} + + {source === 'custom' ? ( + + ) : null} + + {this.renderApiPanel()} +
+ ); + } + } +} + +const renderInput = ( + name: string, + placeholder: string, + required: boolean = true +) => { + return { + type: 'input-text', + name, + placeholder: placeholder, + required + } +} + +export default class TransferTableOption extends React.Component { + + 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