From 21ef86a5a9a1092df7252ad146d13f683328b7f7 Mon Sep 17 00:00:00 2001 From: pianruijie Date: Thu, 21 Jul 2022 16:56:01 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=85=BC=E5=AE=B9api=20string=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=20&&=20tooltip=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I05b1a5e03060ee73affd203b1308fb3ddb8b126b --- .../amis-editor/src/renderer/APIControl.tsx | 3 - .../src/renderer/event-control/actions.tsx | 9 +- .../src/renderer/event-control/helper.tsx | 594 +++++++++--------- 3 files changed, 309 insertions(+), 297 deletions(-) diff --git a/packages/amis-editor/src/renderer/APIControl.tsx b/packages/amis-editor/src/renderer/APIControl.tsx index b9f72be74..90cc9ac7e 100644 --- a/packages/amis-editor/src/renderer/APIControl.tsx +++ b/packages/amis-editor/src/renderer/APIControl.tsx @@ -129,11 +129,9 @@ export default class APIControl extends React.Component< componentDidUpdate(prevProps: APIControlProps) { const props = this.props; - if (prevProps.value !== props.value) { this.setState({apiStr: this.transformApi2Str(props.value)}); } - if (anyChanged(['enablePickerMode', 'pickerSchema'], prevProps, props)) { this.setState({schema: props.pickerSchema}); } @@ -198,7 +196,6 @@ export default class APIControl extends React.Component< if (typeof value !== 'string' || typeof values !== 'string') { api = merge({}, normalizeApi(values)); } - onChange?.(api); } diff --git a/packages/amis-editor/src/renderer/event-control/actions.tsx b/packages/amis-editor/src/renderer/event-control/actions.tsx index bbfbe36b9..422f62ca4 100644 --- a/packages/amis-editor/src/renderer/event-control/actions.tsx +++ b/packages/amis-editor/src/renderer/event-control/actions.tsx @@ -5,6 +5,7 @@ import { RendererPluginAction } from 'amis-editor-core'; import React from 'react'; +import {normalizeApi} from 'amis-core'; import { FORMITEM_CMPTS, getArgsWrapper, @@ -409,14 +410,18 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => { description: '配置并发送API请求', innerArgs: ['api'], descDetail: (info: any) => { + let apiInfo = info?.args?.api; + if (typeof apiInfo === 'string'){ + apiInfo = normalizeApi(apiInfo); + } return (
发送 - {info?.args?.api?.method} + {apiInfo?.method} 请求: - {info?.args?.api?.url} + {apiInfo?.url}
); }, diff --git a/packages/amis-editor/src/renderer/event-control/helper.tsx b/packages/amis-editor/src/renderer/event-control/helper.tsx index 144883a7e..3a52be717 100644 --- a/packages/amis-editor/src/renderer/event-control/helper.tsx +++ b/packages/amis-editor/src/renderer/event-control/helper.tsx @@ -13,7 +13,7 @@ import { SubRendererPluginAction } from 'amis-editor-core'; import {ActionConfig, ContextVariables} from './types'; -import {DataSchema, filterTree, findTree, mapTree} from 'amis-core'; +import {DataSchema, filterTree, findTree, mapTree, normalizeApi} from 'amis-core'; import CmptActionSelect from './comp-action-select'; import {Button} from 'amis'; import ACTION_TYPE_TREE from './actions'; @@ -591,345 +591,353 @@ export const getOldActionSchema = ( ) => { const isInDialog = /(?:\/|^)dialog\/.+$/.test(context.path); return { - type: 'button', - label: '配置动作(旧版)', - className: 'block old-action-btn', - tooltip: + type: 'tooltip-wrapper', + content: '温馨提示:添加下方事件动作后,下方事件动作将先于旧版动作执行,建议统一迁移至事件动作机制,帮助您实现更灵活的交互设计', - tooltipPlacement: 'left', - actionType: 'dialog', - dialog: { - type: 'dialog', - title: '动作', - body: { - type: 'form', - body: [ - { - label: '按钮行为', - type: 'select', - name: 'actionType', - pipeIn: defaultValue(''), - options: [ + inline: true, + tooltipTheme: "dark", + body: [ + { + type: 'button', + label: '配置动作(旧版)', + className: 'block old-action-btn', + actionType: 'dialog', + dialog: { + type: 'dialog', + title: '动作', + body: { + type: 'form', + body: [ { - label: '默认', - value: '' - }, - { - label: '弹框', - value: 'dialog' + label: '按钮行为', + type: 'select', + name: 'actionType', + pipeIn: defaultValue(''), + options: [ + { + label: '默认', + value: '' + }, + { + label: '弹框', + value: 'dialog' + }, + + { + label: '抽出式弹框(Drawer)', + value: 'drawer' + }, + + { + label: '发送请求', + value: 'ajax' + }, + + { + label: '下载文件', + value: 'download' + }, + + { + label: '页面跳转(单页模式)', + value: 'link' + }, + + { + label: '页面跳转', + value: 'url' + }, + + { + label: '刷新目标', + value: 'reload' + }, + + { + label: '复制内容', + value: 'copy' + }, + + { + label: '提交', + value: 'submit' + }, + + { + label: '重置', + value: 'reset' + }, + + { + label: '重置并提交', + value: 'reset-and-submit' + }, + + { + label: '确认', + value: 'confirm' + }, + + { + label: '取消', + value: 'cancel' + }, + + { + label: '跳转下一条', + value: 'next' + }, + + { + label: '跳转上一条', + value: 'prev' + } + ] }, { - label: '抽出式弹框(Drawer)', - value: 'drawer' + type: 'input-text', + name: 'content', + visibleOn: 'data.actionType == "copy"', + label: '复制内容模板' }, { - label: '发送请求', - value: 'ajax' + type: 'select', + name: 'copyFormat', + options: [ + { + label: '纯文本', + value: 'text/plain' + }, + { + label: '富文本', + value: 'text/html' + } + ], + visibleOn: 'data.actionType == "copy"', + label: '复制格式' }, { - label: '下载文件', - value: 'download' + type: 'input-text', + name: 'target', + visibleOn: 'data.actionType == "reload"', + label: '指定刷新目标', + required: true }, { - label: '页面跳转(单页模式)', - value: 'link' + name: 'dialog', + pipeIn: defaultValue({ + title: '弹框标题', + body: '

对,你刚刚点击了

' + }), + asFormItem: true, + children: ({value, onChange, data}: any) => + data.actionType === 'dialog' ? ( + + ) : null }, { - label: '页面跳转', - value: 'url' + visibleOn: 'data.actionType == "drawer"', + name: 'drawer', + pipeIn: defaultValue({ + title: '弹框标题', + body: '

对,你刚刚点击了

' + }), + asFormItem: true, + children: ({value, onChange, data}: any) => + data.actionType == 'drawer' ? ( + + ) : null + }, + + getSchemaTpl('api', { + label: '目标API', + visibleOn: 'data.actionType == "ajax"' + }), + + { + name: 'feedback', + pipeIn: defaultValue({ + title: '弹框标题', + body: '

内容

' + }), + asFormItem: true, + children: ({onChange, value, data}: any) => + data.actionType == 'ajax' ? ( +
+ + + {value ? ( + + ) : null} +
+ ) : null }, { - label: '刷新目标', - value: 'reload' + name: 'feedback.visibleOn', + label: '是否弹出表达式', + type: 'input-text', + visibleOn: 'this.feedback', + autoComplete: false, + description: '请使用 JS 表达式如:`this.xxx == 1`' }, { - label: '复制内容', - value: 'copy' + name: 'feedback.skipRestOnCancel', + label: '弹框取消是否中断后续操作', + type: 'switch', + mode: 'inline', + className: 'block', + visibleOn: 'this.feedback' }, { - label: '提交', - value: 'submit' + name: 'feedback.skipRestOnConfirm', + label: '弹框确认是否中断后续操作', + type: 'switch', + mode: 'inline', + className: 'block', + visibleOn: 'this.feedback' }, { - label: '重置', - value: 'reset' + type: 'input-text', + label: '目标地址', + name: 'link', + visibleOn: 'data.actionType == "link"' }, { - label: '重置并提交', - value: 'reset-and-submit' + type: 'input-text', + label: '目标地址', + name: 'url', + visibleOn: 'data.actionType == "url"', + placeholder: 'http://' }, { - label: '确认', - value: 'confirm' + type: 'switch', + name: 'blank', + visibleOn: 'data.actionType == "url"', + mode: 'inline', + className: 'w-full', + label: '是否用新窗口打开', + pipeIn: defaultValue(true) + }, + + isInDialog + ? { + visibleOn: + 'data.actionType == "submit" || data.type == "submit"', + name: 'close', + type: 'switch', + mode: 'inline', + className: 'w-full', + pipeIn: defaultValue(true), + label: '是否关闭当前弹框' + } + : {}, + + { + name: 'confirmText', + type: 'textarea', + label: '确认文案', + description: + '点击后会弹出此内容,等用户确认后才进行相应的操作。' }, { - label: '取消', - value: 'cancel' + type: 'input-text', + name: 'reload', + label: '刷新目标组件', + visibleOn: + 'data.actionType != "link" && data.actionType != "url"', + description: + '当前动作完成后,指定目标组件刷新。支持传递数据如:xxx?a=\\${a}&b=\\${b},多个目标请用英文逗号隔开。' }, { - label: '跳转下一条', - value: 'next' + type: 'input-text', + name: 'target', + visibleOn: 'data.actionType != "reload"', + label: '指定响应组件', + description: + '指定动作执行者,默认为当前组件所在的功能性性组件,如果指定则转交给目标组件来处理。' }, { - label: '跳转上一条', - value: 'prev' + type: 'js-editor', + allowFullscreen: true, + name: 'onClick', + label: '自定义点击事件', + description: '将会传递 event 和 props 两个参数' + }, + + { + type: 'input-text', + name: 'hotKey', + label: '键盘快捷键' } ] }, - - { - type: 'input-text', - name: 'content', - visibleOn: 'data.actionType == "copy"', - label: '复制内容模板' - }, - - { - type: 'select', - name: 'copyFormat', - options: [ - { - label: '纯文本', - value: 'text/plain' - }, - { - label: '富文本', - value: 'text/html' - } - ], - visibleOn: 'data.actionType == "copy"', - label: '复制格式' - }, - - { - type: 'input-text', - name: 'target', - visibleOn: 'data.actionType == "reload"', - label: '指定刷新目标', - required: true - }, - - { - name: 'dialog', - pipeIn: defaultValue({ - title: '弹框标题', - body: '

对,你刚刚点击了

' - }), - asFormItem: true, - children: ({value, onChange, data}: any) => - data.actionType === 'dialog' ? ( - - ) : null - }, - - { - visibleOn: 'data.actionType == "drawer"', - name: 'drawer', - pipeIn: defaultValue({ - title: '弹框标题', - body: '

对,你刚刚点击了

' - }), - asFormItem: true, - children: ({value, onChange, data}: any) => - data.actionType == 'drawer' ? ( - - ) : null - }, - - getSchemaTpl('api', { - label: '目标API', - visibleOn: 'data.actionType == "ajax"' - }), - - { - name: 'feedback', - pipeIn: defaultValue({ - title: '弹框标题', - body: '

内容

' - }), - asFormItem: true, - children: ({onChange, value, data}: any) => - data.actionType == 'ajax' ? ( -
- - - {value ? ( - - ) : null} -
- ) : null - }, - - { - name: 'feedback.visibleOn', - label: '是否弹出表达式', - type: 'input-text', - visibleOn: 'this.feedback', - autoComplete: false, - description: '请使用 JS 表达式如:`this.xxx == 1`' - }, - - { - name: 'feedback.skipRestOnCancel', - label: '弹框取消是否中断后续操作', - type: 'switch', - mode: 'inline', - className: 'block', - visibleOn: 'this.feedback' - }, - - { - name: 'feedback.skipRestOnConfirm', - label: '弹框确认是否中断后续操作', - type: 'switch', - mode: 'inline', - className: 'block', - visibleOn: 'this.feedback' - }, - - { - type: 'input-text', - label: '目标地址', - name: 'link', - visibleOn: 'data.actionType == "link"' - }, - - { - type: 'input-text', - label: '目标地址', - name: 'url', - visibleOn: 'data.actionType == "url"', - placeholder: 'http://' - }, - - { - type: 'switch', - name: 'blank', - visibleOn: 'data.actionType == "url"', - mode: 'inline', - className: 'w-full', - label: '是否用新窗口打开', - pipeIn: defaultValue(true) - }, - - isInDialog - ? { - visibleOn: - 'data.actionType == "submit" || data.type == "submit"', - name: 'close', - type: 'switch', - mode: 'inline', - className: 'w-full', - pipeIn: defaultValue(true), - label: '是否关闭当前弹框' - } - : {}, - - { - name: 'confirmText', - type: 'textarea', - label: '确认文案', - description: '点击后会弹出此内容,等用户确认后才进行相应的操作。' - }, - - { - type: 'input-text', - name: 'reload', - label: '刷新目标组件', - visibleOn: 'data.actionType != "link" && data.actionType != "url"', - description: - '当前动作完成后,指定目标组件刷新。支持传递数据如:xxx?a=\\${a}&b=\\${b},多个目标请用英文逗号隔开。' - }, - - { - type: 'input-text', - name: 'target', - visibleOn: 'data.actionType != "reload"', - label: '指定响应组件', - description: - '指定动作执行者,默认为当前组件所在的功能性性组件,如果指定则转交给目标组件来处理。' - }, - - { - type: 'js-editor', - allowFullscreen: true, - name: 'onClick', - label: '自定义点击事件', - description: '将会传递 event 和 props 两个参数' - }, - - { - type: 'input-text', - name: 'hotKey', - label: '键盘快捷键' + onConfirm: (values: any[]) => { + manager.panelChangeValue(values[0]); } - ] - }, - onConfirm: (values: any[]) => { - manager.panelChangeValue(values[0]); + } } - } + ] }; }; @@ -1064,7 +1072,9 @@ export const getEventControlConfig = ( delete config.args?.value; } } - + if (action.actionType === 'ajax' && typeof action?.args?.api === 'string') { + action.args.api = normalizeApi(action?.args?.api); + } // 获取动作专有配置参数 const innerArgs: any = getPropOfAcion( action, From b399a5900488e1fbea08185cd6fd32baddbf7f53 Mon Sep 17 00:00:00 2001 From: pianruijie Date: Tue, 26 Jul 2022 21:18:49 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=E5=A4=84=E7=90=86=E6=89=93=E5=BC=80?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=BD=B1=E5=93=8D=E5=8F=98=E9=87=8F=E8=B5=8B?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5a2ccbd9dc7f0750c5ef6a223b66d3176143522f --- packages/amis-editor/src/renderer/event-control/actions.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/amis-editor/src/renderer/event-control/actions.tsx b/packages/amis-editor/src/renderer/event-control/actions.tsx index 2763d2c2e..22d03899f 100644 --- a/packages/amis-editor/src/renderer/event-control/actions.tsx +++ b/packages/amis-editor/src/renderer/event-control/actions.tsx @@ -57,7 +57,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => { placeholder: 'http://', mode: 'horizontal', size: 'lg', - required: true + required: true, + visibleOn: 'data.actionType === "url"' }, { type: 'combo', From a059875c23646e006f9c62a181133a34720c76ba Mon Sep 17 00:00:00 2001 From: pianruijie Date: Tue, 19 Jul 2022 21:22:58 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=BC=B9=E6=A1=86=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=A4=96=E9=83=A8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib8f64cedad9502f3c35169e782dc4df25450366d --- .../event-control/action-config-dialog.tsx | 2 - .../src/renderer/event-control/actions.tsx | 1 + .../src/renderer/event-control/helper.tsx | 106 ++++++++++-------- .../src/renderer/event-control/index.tsx | 13 ++- 4 files changed, 68 insertions(+), 54 deletions(-) diff --git a/packages/amis-editor/src/renderer/event-control/action-config-dialog.tsx b/packages/amis-editor/src/renderer/event-control/action-config-dialog.tsx index bc42d1cdd..6e024e010 100644 --- a/packages/amis-editor/src/renderer/event-control/action-config-dialog.tsx +++ b/packages/amis-editor/src/renderer/event-control/action-config-dialog.tsx @@ -116,7 +116,6 @@ export default class ActionDialog extends React.Component { } const actionNode = findActionNode(actionTree, value); - form.setValues({ ...removeKeys, __keywords: form.data.__keywords, @@ -158,7 +157,6 @@ export default class ActionDialog extends React.Component { commonActions, onClose } = this.props; - return amisRender( { type: 'dialog', diff --git a/packages/amis-editor/src/renderer/event-control/actions.tsx b/packages/amis-editor/src/renderer/event-control/actions.tsx index 22d03899f..33590cef1 100644 --- a/packages/amis-editor/src/renderer/event-control/actions.tsx +++ b/packages/amis-editor/src/renderer/event-control/actions.tsx @@ -192,6 +192,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => { manager.openSubEditor({ title: '配置弹框内容', value: {type: 'dialog', ...value}, + data, onChange: (value: any) => onChange(value) }) } diff --git a/packages/amis-editor/src/renderer/event-control/helper.tsx b/packages/amis-editor/src/renderer/event-control/helper.tsx index 3a52be717..2a77d06d8 100644 --- a/packages/amis-editor/src/renderer/event-control/helper.tsx +++ b/packages/amis-editor/src/renderer/event-control/helper.tsx @@ -17,6 +17,7 @@ import {DataSchema, filterTree, findTree, mapTree, normalizeApi} from 'amis-core import CmptActionSelect from './comp-action-select'; import {Button} from 'amis'; import ACTION_TYPE_TREE from './actions'; +import { stores } from 'amis-core/lib/factory'; // 数据容器范围 export const DATA_CONTAINER = [ @@ -948,6 +949,7 @@ export const getEventControlConfig = ( manager: EditorManager, context: BaseEventContext ) => { + const isSubEditor = manager.store.isSubEditor; // 通用动作配置 const commonActions = manager?.config.actionOptions?.customActionGetter?.(manager); @@ -955,7 +957,49 @@ export const getEventControlConfig = ( const actionTree = manager?.config.actionOptions?.actionTreeGetter ? manager?.config.actionOptions?.actionTreeGetter(ACTION_TYPE_TREE(manager)) : ACTION_TYPE_TREE(manager); - + const allComponents = mapTree( + manager?.store?.outline ?? [], + (item: any) => { + const schema = manager?.store?.getSchema(item.id); + return { + id: item.id, + label: item.label, + value: schema?.id ?? item.id, + type: schema?.type ?? item.type, + schema, + disabled: !!item.region, + children: item?.children + }; + }, + 1, + true + ); + const checkComponent = (node: any, action: RendererPluginAction) => { + const actionType = action.actionType; + const actions = manager?.pluginActions[node.type]; + const haveChild = !!node.children?.length; + let isSupport = false; + if (typeof action.supportComponents === 'string') { + isSupport = + action.supportComponents === '*' || + action.supportComponents === node.type; + } else if (Array.isArray(action.supportComponents)) { + isSupport = action.supportComponents.includes(node.type); + } + if (['reload', 'setValue'].includes(actionType)) { + isSupport = hasActionType(actionType, actions); + } + if (actionType === 'component' && !actions?.length) { + node.disabled = true; + } + if (isSupport) { + return true; + } else if (haveChild) { + node.disabled = true; + return true; + } + return false; + } return { showOldEntry: manager?.config.actionOptions?.showOldEntry !== false && (!!context.schema.actionType || @@ -967,6 +1011,7 @@ export const getEventControlConfig = ( owner: '', addBroadcast: manager?.addBroadcast, removeBroadcast: manager?.removeBroadcast, + allComponents: allComponents, getContextSchemas: async (id?: string, withoutSuper?: boolean) => { const dataSchema = await manager.getContextSchemas( id ?? context!.id, @@ -979,56 +1024,23 @@ export const getEventControlConfig = ( return manager.dataSchema; }, getComponents: (action: RendererPluginAction) => { - const actionType = action.actionType!; - const components = filterTree( - mapTree( - manager?.store?.outline ?? [], - (item: any) => { - const schema = manager?.store?.getSchema(item.id); - return { - id: item.id, - label: item.label, - value: schema?.id ?? item.id, - type: schema?.type ?? item.type, - schema, - disabled: !!item.region, - children: item?.children - }; - }, - 1, - true - ), - node => { - const actions = manager?.pluginActions[node.type]; - let isSupport = false; - if (typeof action.supportComponents === 'string') { - isSupport = - action.supportComponents === '*' || - action.supportComponents === node.type; - } else if (Array.isArray(action.supportComponents)) { - isSupport = action.supportComponents.includes(node.type); + let components = allComponents; + if (isSubEditor) { + let superTree = manager.store.getSuperEditorData; + while(superTree) { + if (superTree.__superCmptTreeSource) { + components = components.concat(superTree.__superCmptTreeSource); } - if (['reload', 'setValue'].includes(actionType)) { - isSupport = hasActionType(actionType, actions); - } - - if (actionType === 'component' && !actions?.length) { - node.disabled = true; - } - - if (isSupport) { - return true; - } else if (!isSupport && !!node.children?.length) { - node.disabled = true; - return true; - } - return false; - }, + superTree = superTree.__super; + } + } + const result = filterTree( + components, + (node) => checkComponent(node, action), 1, true ); - - return components; + return result; }, actionConfigInitFormatter: (action: ActionConfig) => { let config = {...action}; diff --git a/packages/amis-editor/src/renderer/event-control/index.tsx b/packages/amis-editor/src/renderer/event-control/index.tsx index 30667229f..ee4c9ce33 100644 --- a/packages/amis-editor/src/renderer/event-control/index.tsx +++ b/packages/amis-editor/src/renderer/event-control/index.tsx @@ -73,6 +73,7 @@ interface EventControlState { groupType?: string; __actionDesc?: string; __cmptTreeSource?: ComponentInfo[]; + __superCmptTreeSource?: ComponentInfo[]; __actionSchema?: any; __subActions?: SubRendererPluginAction[]; __setValueDs?: any[]; @@ -492,13 +493,13 @@ export class EventControl extends React.Component< getContextSchemas, actionConfigInitFormatter, getComponents, - actionTree + actionTree, + allComponents } = this.props; const {rawVariables} = this.state; // 收集事件变量 const eventVariables = this.getEventVariables(data); const variables = [...eventVariables, ...rawVariables]; - // 编辑操作,需要格式化动作配置 if (data.type === 'update') { const action = data.actionData!.action!; @@ -521,7 +522,7 @@ export class EventControl extends React.Component< item => item.value !== '$$id' ); } - } + }; data.actionData = { eventKey: data.actionData!.eventKey, actionIndex: data.actionData!.actionIndex, @@ -537,6 +538,8 @@ export class EventControl extends React.Component< __cmptTreeSource: actionConfig?.componentId ? getComponents?.(actionNode!) ?? [] : [], + __superCmptTreeSource: allComponents, + // __supersCmptTreeSource: '', __setValueDs: setValueDs // broadcastId: action.actionType === 'broadcast' ? action.eventName : '' }; @@ -554,10 +557,10 @@ export class EventControl extends React.Component< variables, pluginActions, getContextSchemas, - rawVariables + rawVariables, + __superCmptTreeSource: allComponents }; } - this.setState(data); }