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/AnchorNav.tsx b/packages/amis-editor/src/plugin/AnchorNav.tsx index d6c1422ba..3ce94d665 100644 --- a/packages/amis-editor/src/plugin/AnchorNav.tsx +++ b/packages/amis-editor/src/plugin/AnchorNav.tsx @@ -84,6 +84,9 @@ export class AnchorNavPlugin extends BasePlugin { draggable: true, minLength: 1, addButtonText: '添加锚点', + deleteBtn: { + icon: 'fa fa-trash' + }, items: [ { type: 'input-text', @@ -150,7 +153,8 @@ export class AnchorNavPlugin extends BasePlugin { value: '1' } ] - } + }, + getSchemaTpl('status') ]) }, { diff --git a/packages/amis-editor/src/plugin/Dialog.tsx b/packages/amis-editor/src/plugin/Dialog.tsx index 610904f56..6af6e616c 100644 --- a/packages/amis-editor/src/plugin/Dialog.tsx +++ b/packages/amis-editor/src/plugin/Dialog.tsx @@ -1,14 +1,16 @@ import React from 'react'; -import {registerEditorPlugin} from 'amis-editor-core'; import { + registerEditorPlugin, BaseEventContext, BasePlugin, RegionConfig, - RendererInfo + RendererInfo, + getSchemaTpl, + noop } from 'amis-editor-core'; -import {defaultValue, getSchemaTpl} from 'amis-editor-core'; -import {noop} from 'amis-editor-core'; +import {cloneDeep, assign} from 'lodash'; import {getEventControlConfig} from '../renderer/event-control/helper'; +import {tipedLabel} from '../component/BaseControl'; export class DialogPlugin extends BasePlugin { // 关联渲染器名字 @@ -83,108 +85,152 @@ export class DialogPlugin extends BasePlugin { ]; panelTitle = '弹框'; + panelJustify = true; panelBodyCreator = (context: BaseEventContext) => { return getSchemaTpl('tabs', [ { - title: '常规', - body: [ + title: '属性', + body: getSchemaTpl('collapseGroup', [ { - label: '标题', - type: 'input-text', - name: 'title' - }, - - getSchemaTpl('switch', { - label: '数据映射', - name: 'data', - className: 'block m-b-xs', - pipeIn: (value: any) => !!value, - pipeOut: (value: any) => (value ? {'&': '$$'} : null) - }), - - { - type: 'tpl', - visibleOn: '!this.data', - tpl: - '

当没开启数据映射时,弹框中默认会拥有触发打开弹框按钮所在环境的所有数据。

' - }, - - { - type: 'input-kv', - syncDefaultValue: false, - name: 'data', - visibleOn: 'this.data', - descriptionClassName: 'help-block text-xs m-b-none', - description: - '

当开启数据映射时,弹框中的数据只会包含设置的部分,请绑定数据。如:{"a": "\\${a}", "b": 2}

如果希望在默认的基础上定制,请先添加一个 Key 为 `&` Value 为 `\\$$` 作为第一行。

当值为 __undefined时,表示删除对应的字段,可以结合{"&": "\\$$"}来达到黑名单效果。
', - messages: { - validateFailed: '数据映射中存在错误,请仔细检查' - } - }, - - getSchemaTpl('switch', { - label: '按 Esc 关闭弹框', - name: 'closeOnEsc', - value: false - }), - - getSchemaTpl('switch', { - label: '点击弹框外区域关闭弹框', - name: 'closeOnOutside', - value: false - }) - ] + title: '基本', + body: [ + { + label: '标题', + type: 'input-text', + name: 'title' + }, + { + label: '尺寸', + type: 'button-group-select', + value: 'md', + name: 'size', + size: 'sm', + options: [ + { + label: '小', + value: 'sm' + }, + { + label: '中', + value: 'md' + }, + { + label: '大', + value: 'lg' + }, + { + label: '超大', + value: 'xl' + } + ] + }, + getSchemaTpl('switch', { + label: '展示关闭按钮', + name: 'showCloseButton', + value: true + }), + getSchemaTpl('switch', { + label: '可按 Esc 关闭', + name: 'closeOnEsc', + value: false + }), + getSchemaTpl('switch', { + label: '左下角展示报错消息', + name: 'showErrorMsg', + value: true + }), + getSchemaTpl('switch', { + label: '左下角展示loading动画', + name: 'showLoading', + value: true + }), + getSchemaTpl('switch', { + label: tipedLabel( + '数据映射', + '
当开启数据映射时,弹框中的数据只会包含设置的部分,请绑定数据。如:{"a": "${a}", "b": 2}。
' + + '
当值为 __undefined时,表示删除对应的字段,可以结合{"&": "$$"}来达到黑名单效果。
' + ), + name: 'dataMapSwitch', + value: false, + className: 'm-b-xs', + onChange: (value: any, oldValue: any, model: any, form: any) => { + const newDataValue = value ? {} : null; + form.setValues({ + __dataMap: newDataValue, + data: newDataValue + }); + } + }), + { + type: 'alert', + level: 'info', + visibleOn: 'this.dataMapSwitch', + className: 'relative', + body: [ + { + type: 'tpl', + tpl: '${data["&"] ? "已开启定制参数功能,可点击关闭该功能。" : "如果需要在默认数据的基础上定制参数,请配置开启参数定制再定义key和value。"}' + }, + { + type: 'button', + label: '${data["&"] ? "立即关闭" : "立即开启"}', + level: 'link', + className: 'absolute bottom-3 right-10', + onClick: (e: any, props: any) => { + const newData = props.data.data?.['&'] === '$$' ? {} : {'&': '$$'}; + // 用onBulkChange保证代码视图和编辑区域数据保持同步 + props.onBulkChange({ + data: newData, + __dataMap: {} + }); + } + } + ], + showCloseButton: true + }, + getSchemaTpl('combo-container', { + type: 'input-kv', + syncDefaultValue: false, + name: '__dataMap', + value: null, + visibleOn: 'this.dataMapSwitch', + className: 'block -mt-5', + deleteBtn: { + icon: 'fa fa-trash' + }, + onChange: (value: any, oldValue: any, model: any, form: any) => { + // 用assign保证'&'第一个被遍历到 + const newDataMap = form.data.data?.['&'] ? + assign({'&': '$$'}, value) : cloneDeep(value); + form.setValues({ + data: newDataMap + }); + form.setValues({ + data: newDataMap + }); + } + }), + ] + } + ]) }, { title: '外观', - body: [ + body: getSchemaTpl('collapseGroup', [ { - label: '尺寸', - type: 'button-group-select', - name: 'size', - size: 'sm', - className: 'block', - pipeIn: defaultValue(''), - options: [ - { - label: '小', - value: 'sm' - }, - { - label: '默认', - value: '' - }, - { - label: '中', - value: 'md' - }, - { - label: '大', - value: 'lg' - }, - { - label: '超大', - value: 'xl' - } + title: 'CSS类名', + body: [ + getSchemaTpl('className', { + name: 'className', + label: '外层' + }), + getSchemaTpl('className', { + name: 'bodyClassName', + label: '内容区域' + }) ] - }, - - getSchemaTpl('switch', { - label: '是否显示关闭按钮', - name: 'showCloseButton', - value: true - }), - - getSchemaTpl('className', { - name: 'headerClassName', - label: '顶部 CSS 类名' - }), - - getSchemaTpl('className', { - name: 'bodyClassName', - label: '内容 CSS 类名' - }) - ] + } + ]) }, { title: '事件', diff --git a/packages/amis-editor/src/plugin/Drawer.tsx b/packages/amis-editor/src/plugin/Drawer.tsx index 785a52165..342bd5872 100644 --- a/packages/amis-editor/src/plugin/Drawer.tsx +++ b/packages/amis-editor/src/plugin/Drawer.tsx @@ -1,14 +1,17 @@ -import {registerEditorPlugin} from 'amis-editor-core'; import { + registerEditorPlugin, BaseEventContext, BasePlugin, RegionConfig, - RendererInfo + RendererInfo, + defaultValue, + getSchemaTpl, + noop } from 'amis-editor-core'; -import {defaultValue, getSchemaTpl} from 'amis-editor-core'; -import {noop} from 'amis-editor-core'; +import {assign, cloneDeep} from 'lodash'; import {getEventControlConfig} from '../renderer/event-control/helper'; import {InlineModal} from './Dialog'; +import {tipedLabel} from '../component/BaseControl'; export class DrawerPlugin extends BasePlugin { // 关联渲染器名字 @@ -83,153 +86,213 @@ export class DrawerPlugin extends BasePlugin { ]; panelTitle = '弹框'; + panelJustify = true; panelBodyCreator = (context: BaseEventContext) => { return getSchemaTpl('tabs', [ { - title: '常规', - body: [ + title: '属性', + body: getSchemaTpl('collapseGroup', [ { - label: '标题', - type: 'input-text', - name: 'title' - }, - - // { - // children: ( - // - // ) - // }, - - { - type: 'divider' - }, - - { - label: '位置', - type: 'button-group-select', - name: 'position', - value: 'right', - size: 'sm', - mode: 'inline', - className: 'block', - options: [ + title: '基本', + body: [ { - label: '左', - value: 'left' + label: '标题', + type: 'input-text', + name: 'title' }, { - label: '上', - value: 'top' + type: 'button-group-select', + name: 'position', + label: '位置', + value: 'right', + mode: 'horizontal', + options: [ + { + label: '左', + value: 'left' + }, + { + label: '上', + value: 'top' + }, + { + label: '右', + value: 'right' + }, + { + label: '下', + value: 'bottom' + } + ] }, { - label: '右', - value: 'right' + type: 'button-group-select', + name: 'size', + label: '尺寸', + value: 'md', + size: 'sm', + mode: 'horizontal', + options: [ + { + label: '超小', + value: 'xs' + }, + { + label: '小', + value: 'sm' + }, + { + label: '中', + value: 'md' + }, + { + label: '大', + value: 'lg' + }, + { + label: '超大', + value: 'xl' + } + ] }, + getSchemaTpl('switch', { + name: 'overlay', + label: '显示蒙层', + pipeIn: defaultValue(true) + }), + getSchemaTpl('switch', { + name: 'showCloseButton', + label: '展示关闭按钮', + pipeIn: defaultValue(true) + }), + getSchemaTpl('switch', { + name: 'closeOnOutside', + label: '点击外部关闭' + }), + getSchemaTpl('switch', { + label: '可按 Esc 关闭', + name: 'closeOnEsc' + }), + getSchemaTpl('switch', { + name: 'resizable', + label: '可拖拽抽屉大小', + value: false + }), + getSchemaTpl('switch', { + label: tipedLabel( + '数据映射', + '
当开启数据映射时,弹框中的数据只会包含设置的部分,请绑定数据。如:{"a": "${a}", "b": 2}。
' + + '
当值为 __undefined时,表示删除对应的字段,可以结合{"&": "$$"}来达到黑名单效果。
' + ), + name: 'dataMapSwitch', + value: false, + className: 'm-b-xs', + onChange: (value: any, oldValue: any, model: any, form: any) => { + const newDataValue = value ? {} : null; + form.setValues({ + __dataMap: newDataValue, + data: newDataValue + }); + } + }), { - label: '下', - value: 'bottom' - } - ], - description: '定义弹框从什么位置呼出' - }, - - getSchemaTpl('switch', { - label: '数据映射', - name: 'data', - className: 'm-b-xs', - pipeIn: (value: any) => !!value, - pipeOut: (value: any) => (value ? {'&': '$$'} : null) - }), - - { - type: 'tpl', - visibleOn: '!this.data', - tpl: - '

当没开启数据映射时,弹框中默认会拥有触发打开弹框按钮所在环境的所有数据。

' - }, - - { - type: 'input-kv', - syncDefaultValue: false, - name: 'data', - visibleOn: 'this.data', - descriptionClassName: 'help-block text-xs m-b-none', - description: - '

当开启数据映射时,弹框中的数据只会包含设置的部分,请绑定数据。如:{"a": "\\${a}", "b": 2}

如果希望在默认的基础上定制,请先添加一个 Key 为 `&` Value 为 `\\$$` 作为第一行。

当值为 __undefined时,表示删除对应的字段,可以结合{"&": "\\$$"}来达到黑名单效果。
' - }, - - getSchemaTpl('switch', { - name: 'closeOnOutside', - label: '点击外部关闭弹框' - }), - - getSchemaTpl('switch', { - label: '按 Esc 可关闭', - name: 'closeOnEsc' - }) - ] + type: 'alert', + level: 'info', + visibleOn: 'this.dataMapSwitch', + className: 'relative', + body: [ + { + type: 'tpl', + tpl: '${data["&"] ? "已开启定制参数功能,可点击关闭该功能。" : "如果需要在默认数据的基础上定制参数,请配置开启参数定制再定义key和value。"}' + }, + { + type: 'button', + label: '${data["&"] ? "立即关闭" : "立即开启"}', + level: 'link', + className: 'absolute bottom-3 right-10', + onClick: (e: any, props: any) => { + const newData = props.data.data?.['&'] === '$$' ? {} : {'&': '$$'}; + // 用onBulkChange保证代码视图和编辑区域数据保持同步 + props.onBulkChange({ + data: newData, + __dataMap: {} + }); + } + } + ], + showCloseButton: true + }, + getSchemaTpl('combo-container', { + type: 'input-kv', + syncDefaultValue: false, + name: '__dataMap', + value: null, + visibleOn: 'this.dataMapSwitch', + className: 'block -mt-5', + deleteBtn: { + icon: 'fa fa-trash' + }, + onChange: (value: any, oldValue: any, model: any, form: any) => { + // 用assign保证'&'第一个被遍历到 + const newDataMap = form.data.data?.['&'] ? + assign({'&': '$$'}, value) : cloneDeep(value); + form.setValues({ + data: newDataMap + }); + } + }) + ] + } + ]) }, { title: '外观', - body: [ + body: getSchemaTpl('collapseGroup', [ { - label: '尺寸', - type: 'button-group-select', - name: 'size', - size: 'sm', - mode: 'inline', - className: 'block', - options: [ + title: '基本', + body: [ { - label: '超小', - value: 'xs' + type: 'input-text', + name: 'width', + label: tipedLabel( + '宽度', + '位置为 "左" 或 "右" 时生效。 默认宽度为"尺寸"字段配置的宽度,值单位默认为 px,也支持百分比等单位 ,如:100%' + ), + disabledOn: 'this.position === "top" || this.position === "bottom"', + tooltip: '位置为 为 "左" 或 "右" 时生效' }, { - label: '小', - value: 'sm' - }, - { - label: '中', - value: 'md' - }, - { - label: '大', - value: 'lg' - }, - { - label: '超大', - value: 'xl' + type: 'input-text', + name: 'height', + label: tipedLabel( + '高度', + '位置为 "上" 或 "下" 时生效。 默认宽度为"尺寸"字段配置的高度,值单位默认为 px,也支持百分比等单位 ,如:100%' + ), + disabledOn: 'this.position === "left" || this.position === "right"' } ] }, - - getSchemaTpl('switch', { - name: 'overlay', - label: '是否显示蒙层', - pipeIn: defaultValue(true) - }), - - getSchemaTpl('switch', { - name: 'resizable', - label: '可拉拽', - description: '定义弹框是否可拉拽调整大小', - pipeIn: defaultValue(false) - }), - - getSchemaTpl('className'), - getSchemaTpl('className', { - label: 'bodyClassName 类名', - name: 'bodyClassName' - }) - ] + { + title: 'CSS类名', + body: [ + getSchemaTpl('className', { + label: '外层' + }), + getSchemaTpl('className', { + label: '标题区域', + name: 'headClassName' + }), + getSchemaTpl('className', { + label: '内容区域', + name: 'bodyClassName' + }), + getSchemaTpl('className', { + label: '页脚区域', + name: 'footClassName' + }) + ] + } + ]) }, { title: '事件', diff --git a/packages/amis-editor/src/plugin/Form/ButtonToolbar.tsx b/packages/amis-editor/src/plugin/Form/ButtonToolbar.tsx index 03483b56c..5ccf27e06 100644 --- a/packages/amis-editor/src/plugin/Form/ButtonToolbar.tsx +++ b/packages/amis-editor/src/plugin/Form/ButtonToolbar.tsx @@ -59,58 +59,90 @@ export class ButtonToolbarControlPlugin extends BasePlugin { panelTitle = '工具栏'; + panelJustify = true; + panelBodyCreator = (context: BaseEventContext) => { - return formItemControl( + return getSchemaTpl('tabs', [ { - common: { - replace: true, - body: [ - getSchemaTpl('formItemName', { - required: true - }), - getSchemaTpl('label'), - getSchemaTpl('remark'), - getSchemaTpl('labelRemark'), - getSchemaTpl('description') - ] - }, - option: { - title: '按钮管理', - replace: true, - body: [ + title: '属性', + body: [ + getSchemaTpl('collapseGroup', [ { - name: 'buttons', - type: 'combo', - label: '', - multiple: true, - addable: true, - minLength: 1, - draggable: true, - draggableTip: '', - editable: false, - visibleOn: 'this.buttons && this.buttons.length', - items: [ - { - type: 'tpl', - inline: false, - tpl: - '<% if (data.type === "button-group") { %> 按钮组 <% } else { %><%= data.label %><% if (data.icon) { %><% }%><% } %>' - } - ], - addButtonText: '新增按钮', - scaffold: { - type: 'button', - label: '按钮' - } - } - ] - }, - event: { - hidden: false - } + title: '基本', + body: [ + getSchemaTpl('formItemName', { + required: true + }), + getSchemaTpl('label'), + getSchemaTpl('valueFormula', { + rendererSchema: { + type: 'input-text' + } + }), + getSchemaTpl('labelRemark'), + getSchemaTpl('remark'), + getSchemaTpl('description'), + getSchemaTpl('combo-container', { + type: 'combo', + label: '按钮管理', + name: 'buttons', + mode: 'normal', + multiple: true, + addable: true, + minLength: 1, + draggable: true, + editable: false, + items: [ + { + type: 'tpl', + inline: false, + className: 'p-t-xs', + tpl: '<% if (data.type === "button-group") { %> 按钮组 <% } else { %><%= data.label %><% if (data.icon) { %><% }%><% } %>' + } + ], + addButtonText: '新增按钮', + scaffold: { + type: 'button', + label: '按钮' + } + }), + ] + }, + getSchemaTpl('status', { + isFormItem: true, + }) + ]), + ] }, - context - ); + { + title: '外观', + body: [ + getSchemaTpl('collapseGroup', [ + { + title: '基本', + body: [ + getSchemaTpl('formItemMode'), + getSchemaTpl('horizontal', { + label: '', + visibleOn: + 'data.mode == "horizontal" && data.label !== false && data.horizontal' + }), + ] + }, + getSchemaTpl('style:classNames', { + isFormItem: true, + schema: [ + getSchemaTpl('className', { + label: '描述', + name: 'descriptionClassName', + visibleOn: "this.description" + }) + ] + }), + ]) + ] + } + ]); }; } 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