update: 优化动作配置部分

Change-Id: Ie01a37a1a50cec51efc9dfd178daff5208c3f4b3
This commit is contained in:
hsm-lv 2022-06-23 14:11:33 +08:00
parent 463eb2df08
commit a82d0e0566
4 changed files with 546 additions and 549 deletions

View File

@ -6,6 +6,7 @@ import {
} from 'amis-editor-core';
import React from 'react';
import {
FORMITEM_CMPTS,
getArgsWrapper,
IS_DATA_CONTAINER,
renderCmptActionSelect,
@ -29,8 +30,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '跳转链接',
actionType: 'url',
description: '跳转至指定链接的页面',
config: ['url', 'params', 'blank'],
desc: (info: any) => {
innerArgs: ['url', 'params', 'blank'],
descDetail: (info: any) => {
return (
<div>
<span className="variable-left">{info?.args?.url}</span>
@ -98,8 +99,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '打开页面',
actionType: 'link',
description: '打开指定页面',
config: ['link', 'params'],
desc: (info: any) => {
innerArgs: ['link', 'params'],
descDetail: (info: any) => {
return (
<div>
@ -127,7 +128,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '回退页面',
actionType: 'goBack',
description: '浏览器回退',
desc: (info: any) => <div></div>
descDetail: (info: any) => <div></div>
}
]
},
@ -272,7 +273,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '消息提醒',
actionType: 'toast',
description: '弹出消息提醒',
config: [
innerArgs: [
'title',
'msgType',
'msg',
@ -281,7 +282,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
'closeButton',
'showIcon'
],
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">
@ -407,8 +408,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '发送请求',
actionType: 'ajax',
description: '配置并发送API请求',
config: ['api'],
desc: (info: any) => {
innerArgs: ['api'],
descDetail: (info: any) => {
return (
<div>
@ -436,7 +437,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '下载文件',
actionType: 'download',
description: '触发下载文件',
config: ['api'],
innerArgs: ['api'],
schema: {
type: 'wrapper',
style: {padding: '0 0 0 32px'},
@ -462,7 +463,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actions: [
{
actionType: 'show',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
@ -476,7 +477,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
},
{
actionType: 'hidden',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
@ -489,6 +490,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
}
}
],
supportComponents: '*',
schema: [
...renderCmptSelect('选择组件', true),
{
@ -519,7 +521,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actions: [
{
actionType: 'enabled',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
@ -533,7 +535,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
},
{
actionType: 'disabled',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
@ -546,6 +548,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
}
}
],
supportComponents: ['form', ...FORMITEM_CMPTS],
schema: [
...renderCmptSelect('选择组件', true),
{
@ -573,7 +576,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '刷新组件',
actionType: 'reload',
description: '请求并重新加载所选组件的数据',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
@ -590,8 +593,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '设置组件数据',
actionType: 'setValue',
description: '设置数据容器或表单项的数据',
config: ['value', 'valueInput'],
desc: (info: any) => {
innerArgs: ['value', 'valueInput'],
descDetail: (info: any) => {
return (
<div>
@ -708,7 +711,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '提交表单',
actionType: 'submit',
description: '提交表单数据至数据源',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -716,13 +719,14 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
</div>
);
},
supportComponents: 'form',
schema: renderCmptSelect('选择组件', true)
},
{
actionLabel: '清空表单',
actionType: 'clear',
description: '清空表单数据',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -730,13 +734,14 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
</div>
);
},
supportComponents: 'form',
schema: renderCmptSelect('选择组件', true)
},
{
actionLabel: '重置表单',
actionType: 'reset',
description: '重置表单数据',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -744,13 +749,14 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
</div>
);
},
supportComponents: 'form',
schema: renderCmptSelect('选择组件', true)
},
{
actionLabel: '校验表单',
actionType: 'validate',
description: '校验表单数据',
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -758,6 +764,7 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
</div>
);
},
supportComponents: 'form',
schema: renderCmptSelect('选择组件', true)
},
{
@ -777,8 +784,8 @@ const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
actionLabel: '复制内容',
actionType: 'copy',
description: '复制文本内容至粘贴板',
config: ['content', 'copyFormat'],
desc: (info: any) => {
innerArgs: ['content', 'copyFormat'],
descDetail: (info: any) => {
return (
<div>

View File

@ -5,7 +5,8 @@ import React from 'react';
import {
PluginActions,
RendererPluginAction,
RendererPluginEvent
RendererPluginEvent,
SubRendererPluginAction
} from 'amis-editor-core';
import {ActionConfig, ComponentInfo, ContextVariables} from './types';
import {DataSchema, findTree} from 'amis-core';
@ -28,361 +29,6 @@ export const IS_DATA_CONTAINER = `${JSON.stringify(
DATA_CONTAINER
)}.includes(__rendererName)`;
export const getArgsWrapper = (items: any, multiple: boolean = false) => ({
type: 'combo',
name: 'args',
// label: '动作参数',
multiple,
strictMode: false,
items: Array.isArray(items) ? items : [items]
});
// 获取动作树中指定的动作
export const findActionNode = (
actions: RendererPluginAction[],
actionType: string
) => findTree(actions, node => node.actionType === actionType);
// 获取包含指定子动作的动作
export const findHasSubActionNode = (
actions: RendererPluginAction[],
actionType: string
) =>
findTree(actions, node =>
node.actions?.find(item => item.actionType === actionType)
);
// 获取真实的动作类型
export const getActionType = (
action: ActionConfig,
hasSubActionNode: RendererPluginAction | null
) =>
action.__isCmptAction
? 'component'
: hasSubActionNode
? hasSubActionNode.actionType
: action.actionType;
// 获取事件Label文案
export const getEventLabel = (events: RendererPluginEvent[], name: string) =>
events.find(item => item.eventName === name)?.eventLabel;
// 获取事件描述文案
export const getEventDesc = (events: RendererPluginEvent[], name: string) =>
events.find(item => item.eventName === name)?.description;
// 获取动作Label文案
export const getActionLabel = (events: RendererPluginAction[], name: string) =>
findTree(events, item => item.actionType === name)?.actionLabel;
// 根据动作类型获取过滤后的组件树
export const getComponentTreeSource = (
actionType: string,
pluginActions: PluginActions,
getComponents: () => ComponentInfo[],
commonActions?: {[propName: string]: RendererPluginAction}
) => {
if (!actionType) {
return [];
}
const commonActionConfig = {
...COMMON_ACTION_SCHEMA_MAP,
...commonActions
};
// 如果组件树结构没有传,则重新获取
const components = getComponents ? getComponents() || [] : [];
return getComponentTreeByType(
components,
actionType,
pluginActions,
commonActionConfig
);
};
/**
*
* @param componentsTree
* @param actionType
* @returns
*/
export const getComponentTreeByType = (
componentsTree: ComponentInfo[] = [],
actionType: string = '',
pluginActions: {
[key: string]: any;
} = {},
actionConfigItems: {[propName: string]: RendererPluginAction}
) => {
const hasActionType = (actions?: RendererPluginAction[]) => {
if (!Array.isArray(actions)) {
return false;
}
return !!actions?.find(item =>
[item.actionType, 'component'].includes(actionType)
);
};
const loopChildren = (nodes: ComponentInfo[]) => {
const temp: ComponentInfo[] = [];
for (let node of nodes) {
const actions = pluginActions[node.type];
if (
['visibility'].includes(actionType) ||
(['usability'].includes(actionType) &&
['form', ...FORMITEM_CMPTS].includes(node.type)) ||
(!['submit', 'clear', 'reset', 'validate'].includes(actionType) &&
node.type &&
hasActionType(actions)) ||
(['submit', 'clear', 'reset', 'validate'].includes(actionType) &&
node.type === 'form') ||
((actionType === 'component' ||
actionConfigItems[actionType]?.withComponentId) &&
actionConfigItems[actionType]?.supportComponents?.includes(node.type))
) {
// 组件特性动作,如果当前组件没有动作,则禁用
const disabled =
actionType === 'component' && (!actions || !actions.length);
const newNode: ComponentInfo = {
...node,
disabled: disabled || node.disabled,
children: []
};
if (node.children?.length) {
// 检查子项
newNode.children?.push(...loopChildren(node.children));
}
temp.push(newNode);
} else if (node.children?.length) {
const childNodes = loopChildren(node.children);
if (childNodes.length) {
temp.push(...childNodes);
}
}
}
return temp;
};
return loopChildren(componentsTree);
};
// 获取动作配置
export const getAcionConfig = (
action: ActionConfig,
actionTree: RendererPluginAction[],
pluginActions: PluginActions,
commonActions?: {[propName: string]: RendererPluginAction}
): RendererPluginAction | undefined => {
const commonActionConfig = {
...COMMON_ACTION_SCHEMA_MAP,
...commonActions
};
const actionNode = findActionNode(actionTree, action.actionType);
if (actionNode) {
return actionNode;
}
const hasSubActionNode = findHasSubActionNode(actionTree, action.actionType);
let actionConfig: RendererPluginAction | undefined =
hasSubActionNode?.actions?.find(
item => item.actionType === action.actionType
) ?? commonActionConfig[action.actionType];
if (!actionConfig && action.componentId) {
// 尝试从actions中获取desc
actionConfig = pluginActions[action.__rendererName]?.find(
(item: RendererPluginAction) => item.actionType === action.actionType
);
}
return actionConfig;
};
// 格式化初始化时的动作配置
export const formatActionInitConfig = (
action: ActionConfig,
actionTree: RendererPluginAction[],
pluginActions: PluginActions,
getComponents: () => ComponentInfo[],
commonActions?: {[propName: string]: RendererPluginAction}
) => {
let config = {...action};
if (['setValue', 'url', 'link'].includes(action.actionType) && action.args) {
const prop = action.actionType === 'setValue' ? 'value' : 'params';
!config.args && (config.args = {});
if (Array.isArray(action.args[prop])) {
config.args[prop] = action.args[prop].reduce(
(arr: any, valueItem: any, index: number) => {
if (!arr[index]) {
arr[index] = {};
}
arr[index].item = Object.entries(valueItem).map(([key, val]) => ({
key,
val
}));
return arr;
},
[]
);
} else if (typeof action.args[prop] === 'object') {
config.args[prop] = Object.keys(action.args[prop]).map(key => ({
key,
val: action.args?.[prop][key]
}));
} else if (
action.actionType === 'setValue' &&
typeof action.args[prop] === 'string'
) {
config.args['valueInput'] = config.args['value'];
delete config.args?.value;
}
}
// 获取动作配置
const actionConfig: any = getAcionConfig(
action,
actionTree,
pluginActions,
commonActions
);
// 还原args为可视化配置结构(args + addOnArgs)
if (config.args) {
if (actionConfig?.config) {
let tmpArgs = {};
config.addOnArgs = [];
Object.keys(config.args).forEach(key => {
// 筛选出附加配置参数
if (!actionConfig?.config.includes(key)) {
config.addOnArgs = [
...config.addOnArgs,
{
key: key,
val: config.args?.[key]
}
];
} else {
tmpArgs = {
...tmpArgs,
[key]: config.args?.[key]
};
}
});
config.args = tmpArgs;
}
}
// 获取左侧命中的动作节点
const hasSubActionNode = findHasSubActionNode(actionTree, action.actionType);
const actionType = getActionType(action, hasSubActionNode);
return {
...config,
actionType,
__cmptTreeSource: getComponentTreeSource(
actionType!,
pluginActions,
getComponents,
commonActions
),
__cmptActionType:
hasSubActionNode || action.componentId ? action.actionType : '',
__actionDesc: action.__actionDesc ?? hasSubActionNode?.desc ?? actionConfig.schema, // 树节点描述
__actionSchema: action.__actionSchema ?? hasSubActionNode?.schema ?? actionConfig.schema, // 树节点schema
__subActions: hasSubActionNode?.actions // 树节点子动作
// broadcastId: action.actionType === 'broadcast' ? action.eventName : ''
};
};
// 渲染组件选择配置项
export function renderCmptSelect(
componentLabel: string,
required: boolean,
onChange?: (value: string, oldVal: any, data: any, form: any) => void
) {
return [
{
type: 'tree-select',
name: 'componentId',
label: componentLabel,
showIcon: false,
searchable: true,
required,
selfDisabledAffectChildren: false,
size: 'lg',
source: '${__cmptTreeSource}',
mode: 'horizontal',
autoFill: {
__rendererLabel: '${label}',
__rendererName: '${type}',
__nodeId: '${id}',
__nodeSchema: '${schema}'
},
onChange: async (value: string, oldVal: any, data: any, form: any) => {
onChange?.(value, oldVal, data, form);
}
}
];
}
// 渲染组件特性动作配置项
export function renderCmptActionSelect(
componentLabel: string,
required: boolean,
onChange?: (value: string, oldVal: any, data: any, form: any) => void
) {
return [
...renderCmptSelect(
'选择组件',
true,
async (value: string, oldVal: any, data: any, form: any) => {
// 获取组件上下文
if (form.data.__nodeId) {
const dataSchema: any = await form.data.getContextSchemas?.(
form.data.__nodeId,
true
);
const dataSchemaIns = new DataSchema(dataSchema || []);
const variables = dataSchemaIns?.getDataPropsAsOptions() || [];
form.setValueByName('__cmptDataSchema', dataSchema);
form.setValueByName('__cmptVariables', variables); // 组件上下文(不含父级)
form.setValueByName('__cmptVariablesWithSys', [
// 组件上下文+页面+系统
{
label: `${form.data.__rendererLabel}变量`,
children: variables
},
...form.data.rawVariables.filter((item: ContextVariables) =>
['页面变量', '系统变量'].includes(item.label)
)
]);
}
if (form.data.actionType === 'setValue') {
// todo:这里会闪一下需要从amis查下问题
form.setValueByName('args.value', undefined);
form.setValueByName('args.valueInput', undefined);
}
form.setValueByName('__cmptActionType', '');
onChange?.(value, oldVal, data, form);
}
),
{
asFormItem: true,
label: '组件动作',
name: '__cmptActionType',
mode: 'horizontal',
required: true,
visibleOn: 'data.actionType === "component"',
component: CmptActionSelect,
description: '${__cmptActionDesc}'
}
];
}
// 表单项组件
export const FORMITEM_CMPTS = [
'button-group-select',
@ -449,13 +95,22 @@ export const FORMITEM_CMPTS = [
'uuid'
];
export const getArgsWrapper = (items: any, multiple: boolean = false) => ({
type: 'combo',
name: 'args',
// label: '动作参数',
multiple,
strictMode: false,
items: Array.isArray(items) ? items : [items]
});
// 动作配置项schema map
export const COMMON_ACTION_SCHEMA_MAP: {
[propName: string]: RendererPluginAction;
} = {
setValue: {
config: ['value', 'valueInput'],
desc: (info: any) => {
innerArgs: ['value', 'valueInput'],
descDetail: (info: any) => {
return (
<div>
@ -566,7 +221,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
})
},
reload: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
@ -579,7 +234,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
clear: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -589,7 +244,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
reset: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -599,7 +254,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
submit: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -610,7 +265,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
validate: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -620,7 +275,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
prev: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -631,7 +286,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
next: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -642,7 +297,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
collapse: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -652,7 +307,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
selectAll: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -662,7 +317,7 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
focus: {
desc: (info: any) => {
descDetail: (info: any) => {
return (
<div>
<span className="variable-right">{info?.__rendererLabel}</span>
@ -672,12 +327,192 @@ export const COMMON_ACTION_SCHEMA_MAP: {
}
},
refresh: {
desc: (info: any) => <div></div>
descDetail: (info: any) => <div></div>
},
alert: {
desc: (info: any) => <div></div>
descDetail: (info: any) => <div></div>
},
confirm: {
desc: (info: any) => <div></div>
descDetail: (info: any) => <div></div>
}
};
// 获取动作树中指定的动作
export const findActionNode = (
actions: RendererPluginAction[],
actionType: string
) => findTree(actions, node => node.actionType === actionType);
// 获取包含指定子动作的动作
export const findSubActionNode = (
actions: RendererPluginAction[],
actionType: string
) =>
findTree(actions, node =>
node.actions?.find(
(item: SubRendererPluginAction) => item.actionType === actionType
)
);
// 获取真实的动作类型
export const getActionType = (
action: ActionConfig,
hasSubActionNode: RendererPluginAction | null
) =>
action.__isCmptAction
? 'component'
: hasSubActionNode
? hasSubActionNode.actionType
: action.actionType;
// 获取事件Label文案
export const getEventLabel = (events: RendererPluginEvent[], name: string) =>
events.find(item => item.eventName === name)?.eventLabel;
// 获取事件描述文案
export const getEventDesc = (events: RendererPluginEvent[], name: string) =>
events.find(item => item.eventName === name)?.description;
// 判断插件动作中是否存在指定动作
export const hasActionType = (actionType: string, actions?: RendererPluginAction[]) => {
if (!Array.isArray(actions)) {
return false;
}
return !!actions?.find(item =>
[item.actionType, 'component'].includes(actionType)
);
};
// 获取动作配置主要是为了获取config和descschema强制捆绑在动作树节点动作配置可能在插件动作中 > 树节点 or 子动作)
export const getPropOfAcion = (
action: ActionConfig,
propName: string,
actionTree: RendererPluginAction[],
pluginActions: PluginActions,
commonActions?: {[propName: string]: RendererPluginAction}
): any => {
let prop: any = null;
if (action.componentId) {
// 优先从组件特性动作中找
pluginActions[action.__rendererName]?.find(
(item: RendererPluginAction) => item.actionType === action.actionType
)?.[propName as keyof RendererPluginAction];
}
if (!prop) {
prop = findActionNode(actionTree, action.actionType)?.[
propName as keyof RendererPluginAction
];
}
if (!prop) {
const commonActionConfig = {
...COMMON_ACTION_SCHEMA_MAP,
...commonActions
};
const hasSubActionNode = findSubActionNode(actionTree, action.actionType);
if (propName === 'actionLabel') {
prop = hasSubActionNode?.actionLabel;
}
else {
prop =
hasSubActionNode?.actions?.find(
(item: SubRendererPluginAction) => item.actionType === action.actionType
)?.[propName as keyof SubRendererPluginAction] ??
commonActionConfig[action.actionType]?.[
propName as keyof RendererPluginAction
];
}
}
return prop;
};
// 渲染组件选择配置项
export function renderCmptSelect(
componentLabel: string,
required: boolean,
onChange?: (value: string, oldVal: any, data: any, form: any) => void
) {
return [
{
type: 'tree-select',
name: 'componentId',
label: componentLabel,
showIcon: false,
searchable: true,
required,
selfDisabledAffectChildren: false,
size: 'lg',
source: '${__cmptTreeSource}',
mode: 'horizontal',
autoFill: {
__rendererLabel: '${label}',
__rendererName: '${type}',
__nodeId: '${id}',
__nodeSchema: '${schema}'
},
onChange: async (value: string, oldVal: any, data: any, form: any) => {
onChange?.(value, oldVal, data, form);
}
}
];
}
// 渲染组件特性动作配置项
export function renderCmptActionSelect(
componentLabel: string,
required: boolean,
onChange?: (value: string, oldVal: any, data: any, form: any) => void
) {
return [
...renderCmptSelect(
'选择组件',
true,
async (value: string, oldVal: any, data: any, form: any) => {
// 获取组件上下文
if (form.data.__nodeId) {
const dataSchema: any = await form.data.getContextSchemas?.(
form.data.__nodeId,
true
);
const dataSchemaIns = new DataSchema(dataSchema || []);
const variables = dataSchemaIns?.getDataPropsAsOptions() || [];
form.setValueByName('__cmptDataSchema', dataSchema);
form.setValueByName('__cmptVariables', variables); // 组件上下文(不含父级)
form.setValueByName('__cmptVariablesWithSys', [
// 组件上下文+页面+系统
{
label: `${form.data.__rendererLabel}变量`,
children: variables
},
...form.data.rawVariables.filter((item: ContextVariables) =>
['页面变量', '系统变量'].includes(item.label)
)
]);
}
if (form.data.actionType === 'setValue') {
// todo:这里会闪一下需要从amis查下问题
form.setValueByName('args.value', undefined);
form.setValueByName('args.valueInput', undefined);
}
form.setValueByName('__cmptActionType', '');
onChange?.(value, oldVal, data, form);
}
),
{
asFormItem: true,
label: '组件动作',
name: '__cmptActionType',
mode: 'horizontal',
required: true,
visibleOn: 'data.actionType === "component"',
component: CmptActionSelect,
description: '${__cmptActionDesc}'
}
];
}

View File

@ -3,22 +3,16 @@ import {findDOMNode} from 'react-dom';
import cx from 'classnames';
import Sortable from 'sortablejs';
import {DataSchema, FormItem, Icon, TooltipWrapper} from 'amis';
import {
FormControlProps,
autobind,
render as amisRender
} from 'amis-core';
import {FormControlProps, autobind, render as amisRender} from 'amis-core';
import {BASE_ACTION_PROPS} from './comp-action-select';
import ActionConfigPanel from './action-config-panel';
import {
findHasSubActionNode,
formatActionInitConfig,
getAcionConfig,
getActionLabel,
getComponentTreeSource,
findActionNode,
findSubActionNode,
getEventDesc,
getEventLabel
getEventLabel,
getPropOfAcion
} from './helper';
import {
ActionConfig,
@ -30,7 +24,8 @@ import {
PluginActions,
PluginEvents,
RendererPluginAction,
RendererPluginEvent
RendererPluginEvent,
SubRendererPluginAction
} from 'amis-editor-core';
interface EventControlProps extends FormControlProps {
@ -46,8 +41,10 @@ interface EventControlProps extends FormControlProps {
) => void;
addBroadcast?: (event: RendererPluginEvent) => void;
removeBroadcast?: (eventName: string) => void;
getComponents: () => ComponentInfo[]; // 当前页面组件树
getComponents: (action: RendererPluginAction) => ComponentInfo[]; // 当前页面组件树
getContextSchemas?: (id?: string, withoutSuper?: boolean) => DataSchema; // 获取上下文
actionConfigInitFormatter?: (actionConfig: ActionConfig) => ActionConfig; // 动作配置初始化时格式化
actionConfigSubmitFormatter?: (actionConfig: ActionConfig) => ActionConfig; // 动作配置提交时格式化
owner?: string; // 组件标识
}
@ -67,6 +64,11 @@ interface EventControlState {
pluginActions: PluginActions;
getContextSchemas?: (id?: string, withoutSuper?: boolean) => DataSchema;
rawVariables: ContextVariables[];
__cmptActionType?: string;
__actionDesc?: string;
__cmptTreeSource?: ComponentInfo[],
__actionSchema?: any;
__subActions?: SubRendererPluginAction[]
}
| undefined;
type: 'update' | 'add';
@ -425,9 +427,9 @@ export class EventControl extends React.Component<
const {
actions: pluginActions,
getContextSchemas,
actionTree,
commonActions,
getComponents
actionConfigInitFormatter,
getComponents,
actionTree
} = this.props;
const {events, rawVariables} = this.state;
@ -454,6 +456,10 @@ export class EventControl extends React.Component<
// 编辑操作,需要格式化动作配置
if (data.type === 'update') {
const action = data.actionData!.action!;
const actionConfig = actionConfigInitFormatter?.(action);
const actionNode = findActionNode(actionTree, actionConfig?.actionType!);
const hasSubActionNode = findSubActionNode(actionTree, action.actionType);
data.actionData = {
eventKey: data.actionData!.eventKey,
actionIndex: data.actionData!.actionIndex,
@ -461,13 +467,14 @@ export class EventControl extends React.Component<
pluginActions,
getContextSchemas,
rawVariables,
...formatActionInitConfig(
data.actionData!.action!,
actionTree,
pluginActions,
getComponents,
commonActions
)
...actionConfig,
__cmptTreeSource: getComponents?.(actionNode!) ?? [],
__cmptActionType:
hasSubActionNode || action.componentId ? action.actionType : '',
__actionDesc: actionNode!.description!, // 树节点描述
__actionSchema: actionNode!.schema, // 树节点schema
__subActions: hasSubActionNode?.actions // 树节点子动作
// broadcastId: action.actionType === 'broadcast' ? action.eventName : ''
};
} else {
data.actionData = {
@ -484,17 +491,14 @@ export class EventControl extends React.Component<
// 渲染描述信息
renderDesc(action: ActionConfig) {
const {
actions: pluginActions,
actionTree,
commonActions
} = this.props;
const desc = getAcionConfig(
const {actions: pluginActions, actionTree, commonActions} = this.props;
const desc = getPropOfAcion(
action,
'descDetail',
actionTree,
pluginActions,
commonActions
)?.desc;
);
return typeof desc === 'function' ? (
<div className="action-control-content">{desc?.(action) || '-'}</div>
@ -591,19 +595,16 @@ export class EventControl extends React.Component<
__cmptActionType = 'enabled';
}
const action = data.selectedOptions[0];
form.setValues({
...removeKeys,
__cmptTreeSource: getComponentTreeSource(
value,
pluginActions,
getComponents,
commonActions
),
__cmptActionType,
componentId: form.data.componentId ? '' : undefined,
__actionDesc: data.selectedOptions[0].description,
__actionSchema: data.selectedOptions[0].schema,
__subActions: data.selectedOptions[0].actions
__cmptTreeSource: getComponents?.(action) ?? [],
__cmptActionType,
__actionDesc: action.description,
__actionSchema: action.schema,
__subActions: action.actions
});
}
}
@ -681,78 +682,9 @@ export class EventControl extends React.Component<
}
submitConfig(type: string, config: any) {
const {actionTree} = this.props;
const {actionConfigSubmitFormatter} = this.props;
const action = actionConfigSubmitFormatter?.(config) ?? config;
let action = {...config};
// 修正动作名称
if (config.actionType === 'component') {
// 标记一下组件特性动作
action.__isCmptAction = true;
action.actionType = config.__cmptActionType;
}
const hasSubActionNode = findHasSubActionNode(
actionTree,
config.__cmptActionType
);
if (hasSubActionNode) {
// 修正动作
action.actionType = config.__cmptActionType;
}
action.__label = getActionLabel(actionTree, config.actionType);
// 合并附加的动作参数
if (config.addOnArgs) {
config.addOnArgs.forEach((args: any) => {
action.args = action.args ?? {};
action.args = {
...action.args,
[args.key]: args.val
};
});
delete action.addOnArgs;
}
// 转换下格式
if (['setValue', 'url', 'link'].includes(action.actionType)) {
const prop = action.actionType === 'setValue' ? 'value' : 'params';
if (Array.isArray(config.args[prop])) {
action.args = action.args ?? {};
if (action.__rendererName === 'combo') {
// combo特殊处理
let tempArr: any = [];
config.args?.[prop].forEach((valueItem: any, index: number) => {
valueItem.item.forEach((item: any) => {
if (!tempArr[index]) {
tempArr[index] = {};
}
tempArr[index][item.key] = item.val;
});
});
action.args = {
...action.args,
[prop]: tempArr
};
} else {
let tmpObj: any = {};
config.args[prop].forEach((item: any) => {
tmpObj[item.key] = item.val;
});
action.args = {
...action.args,
[prop]: tmpObj
};
}
} else if (action.actionType === 'setValue') {
// 处理变量赋值非数组的情况
action.args = {
...action.args,
value: config.args['valueInput']
};
}
}
delete action.config;
if (type === 'add') {
this.addAction(config.eventKey, action);
} else if (type === 'update') {
@ -763,6 +695,7 @@ export class EventControl extends React.Component<
}
render() {
const {actionTree, actions: pluginActions, commonActions} = this.props;
const {
onEvent,
events,
@ -868,7 +801,7 @@ export class EventControl extends React.Component<
/>
</div>
<div className="action-item-actiontype">
{action.__label || action.actionType}
{getPropOfAcion(action, 'actionLabel', actionTree, pluginActions, commonActions) || action.actionType}
</div>
</div>
<div className="action-control-header-right">

View File

@ -1,6 +1,20 @@
import {BaseEventContext, EditorManager} from 'amis-editor-core';
import {mapTree} from 'amis';
import {
BaseEventContext,
EditorManager,
RendererPluginAction
} from 'amis-editor-core';
import {filterTree, mapTree} from 'amis';
import ACTION_TYPE_TREE from './renderer/event-control/actions';
import {ActionConfig, ComponentInfo} from './renderer/event-control/types';
import {
COMMON_ACTION_SCHEMA_MAP,
findActionNode,
findSubActionNode,
FORMITEM_CMPTS,
getActionType,
getPropOfAcion,
hasActionType
} from './renderer/event-control/helper';
/**
*
@ -8,9 +22,26 @@ import ACTION_TYPE_TREE from './renderer/event-control/actions';
export const getEventControlConfig = (
manager: EditorManager,
context: BaseEventContext
) => ({
) => {
// 通用动作配置
const commonActions =
manager?.config.actionOptions?.customActionGetter?.(manager);
// 动作树
const actionTree = manager?.config.actionOptions?.actionTreeGetter
? manager?.config.actionOptions?.actionTreeGetter(ACTION_TYPE_TREE(manager))
: ACTION_TYPE_TREE(manager);
const commonActionConfig = {
...COMMON_ACTION_SCHEMA_MAP,
...commonActions
};
return {
actions: manager?.pluginActions,
events: manager?.pluginEvents,
actionTree,
commonActions,
owner: '',
addBroadcast: manager?.addBroadcast,
removeBroadcast: manager?.removeBroadcast,
getContextSchemas: async (id?: string, withoutSuper?: boolean) => {
const dataSchema = await manager.getContextSchemas(
id ?? context!.id,
@ -22,8 +53,8 @@ export const getEventControlConfig = (
}
return manager.dataSchema;
},
getComponents: () =>
mapTree(
getComponents: (action: RendererPluginAction) => {
const components = mapTree(
manager?.store?.outline ?? [],
(item: any) => {
const schema = manager?.store?.getSchema(item.id);
@ -39,14 +70,205 @@ export const getEventControlConfig = (
},
1,
true
),
actionTree: manager?.config.actionOptions?.actionTreeGetter
? manager?.config.actionOptions?.actionTreeGetter(ACTION_TYPE_TREE(manager))
: ACTION_TYPE_TREE(manager),
commonActions: {
...manager?.config.actionOptions?.customActionGetter?.(manager)
);
const actionType = action.actionType!;
const loopChildren = (nodes: ComponentInfo[]) => {
const temp: ComponentInfo[] = [];
for (let node of nodes) {
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);
}
else {
isSupport = hasActionType(actionType, actions);
}
if (isSupport) {
// 组件特性动作,如果当前组件没有动作,则禁用
const disabled =
actionType === 'component' && (!actions || !actions.length);
const newNode: ComponentInfo = {
...node,
disabled: disabled || node.disabled,
children: []
};
if (node.children?.length) {
// 检查子项
newNode.children?.push(...loopChildren(node.children));
}
temp.push(newNode);
} else if (node.children?.length) {
const childNodes = loopChildren(node.children);
if (childNodes.length) {
temp.push(...childNodes);
}
}
}
return temp;
};
return loopChildren(components);
},
owner: '',
addBroadcast: manager?.addBroadcast,
removeBroadcast: manager?.removeBroadcast
actionConfigInitFormatter: (action: ActionConfig) => {
let config = {...action};
if (
['setValue', 'url', 'link'].includes(action.actionType) &&
action.args
) {
const prop = action.actionType === 'setValue' ? 'value' : 'params';
!config.args && (config.args = {});
if (Array.isArray(action.args[prop])) {
config.args[prop] = action.args[prop].reduce(
(arr: any, valueItem: any, index: number) => {
if (!arr[index]) {
arr[index] = {};
}
arr[index].item = Object.entries(valueItem).map(([key, val]) => ({
key,
val
}));
return arr;
},
[]
);
} else if (typeof action.args[prop] === 'object') {
config.args[prop] = Object.keys(action.args[prop]).map(key => ({
key,
val: action.args?.[prop][key]
}));
} else if (
action.actionType === 'setValue' &&
typeof action.args[prop] === 'string'
) {
config.args['valueInput'] = config.args['value'];
delete config.args?.value;
}
}
// 获取动作专有配置参数
const innerArgs: any = getPropOfAcion(
action,
'innerArgs',
actionTree,
manager.pluginActions,
commonActions
);
// 还原args为可视化配置结构(args + addOnArgs)
if (config.args) {
if (innerArgs?.config) {
let tmpArgs = {};
config.addOnArgs = [];
Object.keys(config.args).forEach(key => {
// 筛选出附加配置参数
if (!innerArgs?.config.includes(key)) {
config.addOnArgs = [
...config.addOnArgs,
{
key: key,
val: config.args?.[key]
}
];
} else {
tmpArgs = {
...tmpArgs,
[key]: config.args?.[key]
};
}
});
config.args = tmpArgs;
}
}
// 获取左侧命中的动作节点
const hasSubActionNode = findSubActionNode(actionTree, action.actionType);
return {
...config,
actionType: getActionType(action, hasSubActionNode)
};
},
actionConfigSubmitFormatter: (config: ActionConfig) => {
let action = {...config};
action.__title = findActionNode(
actionTree,
config.actionType
)?.actionLabel;
// 修正动作名称
if (config.actionType === 'component') {
// 标记一下组件特性动作
action.__isCmptAction = true;
action.actionType = config.__cmptActionType;
}
const hasSubActionNode = findSubActionNode(
actionTree,
config.__cmptActionType
);
if (hasSubActionNode) {
// 修正动作
action.actionType = config.__cmptActionType;
}
// 合并附加的动作参数
if (config.addOnArgs) {
config.addOnArgs.forEach((args: any) => {
action.args = action.args ?? {};
action.args = {
...action.args,
[args.key]: args.val
};
});
delete action.addOnArgs;
}
// 转换下格式
if (['setValue', 'url', 'link'].includes(action.actionType)) {
const propName = action.actionType === 'setValue' ? 'value' : 'params';
if (Array.isArray(config.args?.[propName])) {
action.args = action.args ?? {};
if (action.__rendererName === 'combo') {
// combo特殊处理
let tempArr: any = [];
config.args?.[propName].forEach((valueItem: any, index: number) => {
valueItem.item.forEach((item: any) => {
if (!tempArr[index]) {
tempArr[index] = {};
}
tempArr[index][item.key] = item.val;
});
});
action.args = {
...action.args,
[propName]: tempArr
};
} else {
let tmpObj: any = {};
config.args?.[propName].forEach((item: any) => {
tmpObj[item.key] = item.val;
});
action.args = {
...action.args,
[propName]: tmpObj
};
}
} else if (action.actionType === 'setValue') {
// 处理变量赋值非数组的情况
action.args = {
...action.args,
value: config.args?.['valueInput']
};
}
}
delete action.config;
return action;
}
};
};