From 2a673859e7904d95c41946e0b1bf04916b0b36ee Mon Sep 17 00:00:00 2001 From: lvxiaojiao Date: Fri, 5 May 2023 14:01:27 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=E7=BB=84=E4=BB=B6=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugin/Form/ListSelect.tsx | 6 +-- packages/amis-editor/src/plugin/Images.tsx | 13 +++-- packages/amis-editor/src/plugin/List.tsx | 10 ++-- packages/amis-editor/src/plugin/Table.tsx | 10 ++-- packages/amis-editor/src/plugin/Tabs.tsx | 6 +-- .../src/renderer/MapSourceControl.tsx | 30 +++++++++-- .../src/renderer/OptionControl.tsx | 47 ++++++++++++----- .../src/renderer/TransferTableControl.tsx | 39 +++++++++++--- .../src/renderer/TreeOptionControl.tsx | 51 ++++++++++++++----- packages/amis-editor/src/tpl/common.tsx | 11 ++++ packages/amis-ui/src/components/Tag.tsx | 2 + 11 files changed, 155 insertions(+), 70 deletions(-) diff --git a/packages/amis-editor/src/plugin/Form/ListSelect.tsx b/packages/amis-editor/src/plugin/Form/ListSelect.tsx index 4c66ade46..1b4023d8a 100644 --- a/packages/amis-editor/src/plugin/Form/ListSelect.tsx +++ b/packages/amis-editor/src/plugin/Form/ListSelect.tsx @@ -118,11 +118,7 @@ export class ListControlPlugin extends BasePlugin { ] }, option: { - body: [ - getSchemaTpl('optionControlV2', { - description: '设置选项后,输入时会下拉这些选项供用户参考。' - }) - ] + body: [getSchemaTpl('optionControlV2')] }, status: {} }, diff --git a/packages/amis-editor/src/plugin/Images.tsx b/packages/amis-editor/src/plugin/Images.tsx index 70642ec1a..0469ec688 100644 --- a/packages/amis-editor/src/plugin/Images.tsx +++ b/packages/amis-editor/src/plugin/Images.tsx @@ -85,14 +85,13 @@ export class ImagesPlugin extends BasePlugin { } } }, - { - name: 'source', - type: 'input-text', - label: '关联数据', - description: - '比如:\\${listVar},用来关联作用域中的已有数据。', + getSchemaTpl('sourceBindControl', { + label: tipedLabel( + '关联数据', + '比如:\\${listVar},用来关联作用域中的已有数据' + ), visibleOn: 'this.__mode == 1' - }, + }), { type: 'combo', name: 'options', diff --git a/packages/amis-editor/src/plugin/List.tsx b/packages/amis-editor/src/plugin/List.tsx index 3ec2c2d89..aa0738f52 100644 --- a/packages/amis-editor/src/plugin/List.tsx +++ b/packages/amis-editor/src/plugin/List.tsx @@ -90,13 +90,9 @@ export class ListPlugin extends BasePlugin { }, isCRUDBody ? null - : { - name: 'source', - type: 'input-text', - label: '数据源', - pipeIn: defaultValue('${items}'), - description: '绑定当前环境变量' - }, + : getSchemaTpl('sourceBindControl', { + label: '数据源' + }), { name: 'placeholder', pipeIn: defaultValue('没有数据'), diff --git a/packages/amis-editor/src/plugin/Table.tsx b/packages/amis-editor/src/plugin/Table.tsx index 15a256832..df8961894 100644 --- a/packages/amis-editor/src/plugin/Table.tsx +++ b/packages/amis-editor/src/plugin/Table.tsx @@ -360,13 +360,9 @@ export class TablePlugin extends BasePlugin { isCRUDBody ? null - : { - name: 'source', - type: 'input-text', - label: '数据源', - pipeIn: defaultValue('${items}'), - description: '绑定当前环境变量' - }, + : getSchemaTpl('sourceBindControl', { + label: '数据源' + }), { name: 'combineNum', diff --git a/packages/amis-editor/src/plugin/Tabs.tsx b/packages/amis-editor/src/plugin/Tabs.tsx index f6a11e72f..4a91d7cbc 100644 --- a/packages/amis-editor/src/plugin/Tabs.tsx +++ b/packages/amis-editor/src/plugin/Tabs.tsx @@ -189,13 +189,11 @@ export class TabsPlugin extends BasePlugin { { title: '高级', body: [ - getSchemaTpl('expressionFormulaControl', { - evalMode: true, + getSchemaTpl('sourceBindControl', { label: tipedLabel( '关联数据', '根据该数据来动态重复渲染所配置的选项卡' - ), - name: 'source' + ) }), getSchemaTpl('switch', { name: 'mountOnEnter', diff --git a/packages/amis-editor/src/renderer/MapSourceControl.tsx b/packages/amis-editor/src/renderer/MapSourceControl.tsx index 46bea0221..0d42b5553 100644 --- a/packages/amis-editor/src/renderer/MapSourceControl.tsx +++ b/packages/amis-editor/src/renderer/MapSourceControl.tsx @@ -12,7 +12,8 @@ import debounce from 'lodash/debounce'; enum MapType { CUSTOM = 'custom', - API = 'api' + API = 'api', + VARIABLE = 'variable' } export interface MapSourceControlProps extends FormControlProps { @@ -38,7 +39,9 @@ export default class MapSourceControl extends React.Component< let mapType: MapType = MapType.CUSTOM; if (props.data.hasOwnProperty('source') && props.data.source) { - mapType = MapType.API; + mapType = /\$\{(.*?)\}/g.test(props.data.source) + ? MapType.VARIABLE + : MapType.API; } this.state = { @@ -86,7 +89,7 @@ export default class MapSourceControl extends React.Component< return; } - if (mapType === MapType.API) { + if ([MapType.API, MapType.VARIABLE].includes(mapType)) { onBulkChange && onBulkChange({ source, @@ -127,6 +130,10 @@ export default class MapSourceControl extends React.Component< { label: '外部接口', value: MapType.API + }, + { + label: '上下文变量', + value: MapType.VARIABLE } ] as Array<{ label: string; @@ -387,12 +394,25 @@ export default class MapSourceControl extends React.Component< render() { const {mapType} = this.state; - const {className} = this.props; + const {className, render} = this.props; return (
{this.renderHeader()} - {mapType === MapType.CUSTOM ? this.renderMap() : this.renderApiPanel()} + {mapType === MapType.CUSTOM ? this.renderMap() : null} + {mapType === MapType.API ? this.renderApiPanel() : null} + {mapType === MapType.VARIABLE + ? render( + 'variable', + getSchemaTpl('sourceBindControl', { + label: false, + className: 'ae-ExtendMore' + }), + { + onChange: this.handleAPIChange + } + ) + : null}
); } diff --git a/packages/amis-editor/src/renderer/OptionControl.tsx b/packages/amis-editor/src/renderer/OptionControl.tsx index 941667ded..0db1e7cc8 100644 --- a/packages/amis-editor/src/renderer/OptionControl.tsx +++ b/packages/amis-editor/src/renderer/OptionControl.tsx @@ -25,7 +25,6 @@ import {tipedLabel} from 'amis-editor-core'; import type {Option} from 'amis'; import {createObject, FormControlProps} from 'amis-core'; -import type {TextControlSchema} from 'amis/lib/renderers/Form/inputText'; import type {OptionValue} from 'amis-core'; import type {SchemaApi} from 'amis/lib/Schema'; @@ -43,12 +42,14 @@ export interface OptionControlProps extends FormControlProps { className?: string; } +export type SourceType = 'custom' | 'api' | 'apicenter' | 'variable'; + export interface OptionControlState { options: Array; api: SchemaApi; labelField: string; valueField: string; - source: 'custom' | 'api' | 'apicenter'; + source: SourceType; } export default class OptionControl extends React.Component< @@ -65,7 +66,7 @@ export default class OptionControl extends React.Component< constructor(props: OptionControlProps) { super(props); - let source: 'custom' | 'api' | 'apicenter' = 'custom'; + let source: SourceType = 'custom'; if (props.data.hasOwnProperty('source') && props.data.source) { const api = props.data.source; @@ -76,7 +77,11 @@ export default class OptionControl extends React.Component< ? api.url || '' : ''; - source = !url.indexOf('api://') ? 'apicenter' : 'api'; + source = /\$\{(.*?)\}/g.test(props.data.source) + ? 'variable' + : !url.indexOf('api://') + ? 'apicenter' + : 'api'; } this.state = { @@ -251,7 +256,7 @@ export default class OptionControl extends React.Component< data.value = defaultValue; } - if (source === 'api' || source === 'apicenter') { + if (source === 'api' || source === 'apicenter' || source === 'variable') { const {api, labelField, valueField} = this.state; data.source = api; data.labelField = labelField || undefined; @@ -338,8 +343,8 @@ export default class OptionControl extends React.Component< * 切换选项类型 */ @autobind - handleSourceChange(source: 'custom' | 'api' | 'apicenter') { - this.setState({source: source}, this.onChange); + handleSourceChange(source: SourceType) { + this.setState({api: '', source: source}, this.onChange); } /** @@ -479,14 +484,18 @@ export default class OptionControl extends React.Component< label: '外部接口', value: 'api' }, - ...(hasApiCenter ? [{label: 'API中心', value: 'apicenter'}] : []) + ...(hasApiCenter ? [{label: 'API中心', value: 'apicenter'}] : []), + { + label: '上下文变量', + value: 'variable' + } // { // label: '表单实体', // value: 'form' // } ] as Array<{ label: string; - value: 'custom' | 'api' | 'apicenter'; + value: SourceType; }> ).map(item => ({ ...item, @@ -814,9 +823,6 @@ export default class OptionControl extends React.Component< renderApiPanel() { const {render} = this.props; const {source, api, labelField, valueField} = this.state; - if (source === 'custom') { - return null; - } return render( 'api', @@ -889,7 +895,22 @@ export default class OptionControl extends React.Component< ) : null} - {this.renderApiPanel()} + {source === 'api' || source === 'apicenter' + ? this.renderApiPanel() + : null} + + {source === 'variable' + ? render( + 'variable', + getSchemaTpl('sourceBindControl', { + label: false, + className: 'ae-ExtendMore' + }), + { + onChange: this.handleAPIChange + } + ) + : null} ); } diff --git a/packages/amis-editor/src/renderer/TransferTableControl.tsx b/packages/amis-editor/src/renderer/TransferTableControl.tsx index 3c2d35973..bdf964449 100644 --- a/packages/amis-editor/src/renderer/TransferTableControl.tsx +++ b/packages/amis-editor/src/renderer/TransferTableControl.tsx @@ -17,11 +17,13 @@ interface OptionControlProps extends FormControlProps { className?: string; } +type SourceType = 'custom' | 'api' | 'form' | 'variable'; + interface OptionControlState { api: SchemaApi; labelField: string; valueField: string; - source: 'custom' | 'api' | 'form'; + source: SourceType; } function BaseOptionControl(Cmpt: React.JSXElementConstructor) { @@ -37,7 +39,11 @@ function BaseOptionControl(Cmpt: React.JSXElementConstructor) { api: props.data.source, labelField: props.data.labelField, valueField: props.data.valueField, - source: props.data.source ? 'api' : 'custom' + source: props.data.source + ? /\$\{(.*?)\}/g.test(props.data.source) + ? 'variable' + : 'api' + : 'custom' }; this.handleSourceChange = this.handleSourceChange.bind(this); @@ -61,7 +67,7 @@ function BaseOptionControl(Cmpt: React.JSXElementConstructor) { valueField: undefined }; - if (source === 'api') { + if (['api', 'variable'].includes(source)) { const {api, labelField, valueField} = this.state; data.source = api; data.labelField = labelField || undefined; @@ -75,8 +81,8 @@ function BaseOptionControl(Cmpt: React.JSXElementConstructor) { /** * 切换选项类型 */ - handleSourceChange(source: 'custom' | 'api' | 'form') { - this.setState({source: source}, this.onChange); + handleSourceChange(source: SourceType) { + this.setState({api: '', source: source}, this.onChange); } handleAPIChange(source: SchemaApi) { @@ -159,10 +165,14 @@ function BaseOptionControl(Cmpt: React.JSXElementConstructor) { { label: '接口获取', value: 'api' + }, + { + label: '上下文变量', + value: 'variable' } ] as Array<{ label: string; - value: 'custom' | 'api' | 'form'; + value: SourceType; }> ).map(item => ({ ...item, @@ -258,7 +268,7 @@ function BaseOptionControl(Cmpt: React.JSXElementConstructor) { render() { const {source, api, labelField, valueField} = this.state; - const {className} = this.props; + const {className, render} = this.props; const cmptProps = { ...this.props, data: { @@ -275,7 +285,20 @@ function BaseOptionControl(Cmpt: React.JSXElementConstructor) { {source === 'custom' ? : null} - {this.renderApiPanel()} + {source === 'api' ? this.renderApiPanel() : null} + + {source === 'variable' + ? render( + 'variable', + getSchemaTpl('sourceBindControl', { + label: false, + className: 'ae-ExtendMore' + }), + { + onChange: this.handleAPIChange + } + ) + : null} ); } diff --git a/packages/amis-editor/src/renderer/TreeOptionControl.tsx b/packages/amis-editor/src/renderer/TreeOptionControl.tsx index 181e8d23f..3834d972f 100644 --- a/packages/amis-editor/src/renderer/TreeOptionControl.tsx +++ b/packages/amis-editor/src/renderer/TreeOptionControl.tsx @@ -32,13 +32,15 @@ export interface OptionControlProps extends FormControlProps { showIconField?: boolean; // 是否有图标字段 } +export type SourceType = 'custom' | 'api' | 'apicenter' | 'variable'; + export interface OptionControlState { options: Array; api: SchemaApi; labelField: string; valueField: string; iconField: string; - source: 'custom' | 'api' | 'apicenter'; + source: SourceType; modalVisible: boolean; } @@ -66,9 +68,14 @@ export default class TreeOptionControl extends React.Component< labelField: labelField, valueField: valueField, iconField: showIconField ? iconField : undefined, - source: source ? 'api' : 'custom', + source: source + ? /\$\{(.*?)\}/g.test(source) + ? 'variable' + : 'api' + : 'custom', modalVisible: false }; + this.sortables = []; } @@ -105,7 +112,7 @@ export default class TreeOptionControl extends React.Component< * 更新options字段的统一出口 */ onChange() { - const {source} = this.state; + const {source, api, labelField, valueField, iconField} = this.state; const {onBulkChange} = this.props; const data: Partial = { source: undefined, @@ -119,13 +126,13 @@ export default class TreeOptionControl extends React.Component< data.options = this.pretreatOptions(options); } - if (source === 'api' || source === 'apicenter') { - const {api, labelField, valueField, iconField} = this.state; + if (source === 'api' || source === 'apicenter' || source === 'variable') { data.source = api; data.labelField = labelField || undefined; data.valueField = valueField || undefined; data.iconField = iconField; } + onBulkChange && onBulkChange(data); return; } @@ -134,8 +141,8 @@ export default class TreeOptionControl extends React.Component< * 切换选项类型 */ @autobind - handleSourceChange(source: 'custom' | 'api' | 'apicenter') { - this.setState({source: source}, this.onChange); + handleSourceChange(source: SourceType) { + this.setState({api: '', source: source}, this.onChange); } renderHeader() { @@ -160,10 +167,14 @@ export default class TreeOptionControl extends React.Component< label: '外部接口', value: 'api' }, - ...(hasApiCenter ? [{label: 'API中心', value: 'apicenter'}] : []) + ...(hasApiCenter ? [{label: 'API中心', value: 'apicenter'}] : []), + { + label: '上下文变量', + value: 'variable' + } ] as Array<{ label: string; - value: 'custom' | 'api' | 'apicenter'; + value: SourceType; }> ).map(item => ({ ...item, @@ -574,9 +585,6 @@ export default class TreeOptionControl extends React.Component< renderApiPanel() { const {render, showIconField = false} = this.props; const {source, api, labelField, valueField, iconField} = this.state; - if (source === 'custom') { - return null; - } return render( 'api', @@ -624,7 +632,7 @@ export default class TreeOptionControl extends React.Component< render() { const {source} = this.state; - const {className} = this.props; + const {className, render} = this.props; return (
@@ -648,7 +656,22 @@ export default class TreeOptionControl extends React.Component<
) : null} - {this.renderApiPanel()} + {source === 'api' || source === 'apicenter' + ? this.renderApiPanel() + : null} + + {source === 'variable' + ? render( + 'variable', + getSchemaTpl('sourceBindControl', { + label: false, + className: 'ae-ExtendMore' + }), + { + onChange: this.handleAPIChange + } + ) + : null} ); } diff --git a/packages/amis-editor/src/tpl/common.tsx b/packages/amis-editor/src/tpl/common.tsx index 7c1b21a64..9fb8dda0a 100644 --- a/packages/amis-editor/src/tpl/common.tsx +++ b/packages/amis-editor/src/tpl/common.tsx @@ -624,6 +624,17 @@ setSchemaTpl( } ); +setSchemaTpl('sourceBindControl', (schema: object = {}) => ({ + type: 'ae-formulaControl', + name: 'source', + label: '数据', + variableMode: 'tabs', + inputMode: 'input-group', + placeholder: '请输入表达式', + requiredDataPropsVariables: true, + ...schema +})); + setSchemaTpl('menuTpl', () => { return getSchemaTpl('textareaFormulaControl', { mode: 'normal', diff --git a/packages/amis-ui/src/components/Tag.tsx b/packages/amis-ui/src/components/Tag.tsx index 08b557dfc..6a59af04d 100644 --- a/packages/amis-ui/src/components/Tag.tsx +++ b/packages/amis-ui/src/components/Tag.tsx @@ -162,6 +162,8 @@ export class Tag extends React.Component { })} style={tagStyle} onClick={this.handleClick} + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} > {prevIcon} From 1b53e6ba1787b7bdf08252de2ff343a116eedac0 Mon Sep 17 00:00:00 2001 From: lvxiaojiao Date: Fri, 5 May 2023 15:34:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=E7=BB=84=E4=BB=B6=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/amis-editor/src/tpl/common.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/amis-editor/src/tpl/common.tsx b/packages/amis-editor/src/tpl/common.tsx index 9fb8dda0a..3933f819d 100644 --- a/packages/amis-editor/src/tpl/common.tsx +++ b/packages/amis-editor/src/tpl/common.tsx @@ -624,6 +624,9 @@ setSchemaTpl( } ); +/** + * 数据源绑定 + */ setSchemaTpl('sourceBindControl', (schema: object = {}) => ({ type: 'ae-formulaControl', name: 'source',