mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 03:58:07 +08:00
Merge pull request #7855 from hsm-lv/chore-eventaction
chore:动作执行支持ignoreError,用于忽略执行错误继续执行动作列表
This commit is contained in:
commit
8c93fe7631
@ -3194,7 +3194,7 @@ http 请求动作执行结束后,后面的动作可以通过 `${responseResult
|
||||
| id | 组件 ID,即组件的 id 属性的值 |
|
||||
| path | 数据路径,即数据变量的路径 |
|
||||
|
||||
# 事件动作干预
|
||||
# 干预动作执行
|
||||
|
||||
事件动作干预是指执行完当前动作后,干预所监听事件默认处理逻辑和后续其他动作的执行。通过`preventDefault`、`stopPropagation`分别阻止监听事件默认行为和停止下一个动作执行。
|
||||
|
||||
@ -3305,6 +3305,65 @@ http 请求动作执行结束后,后面的动作可以通过 `${responseResult
|
||||
}
|
||||
```
|
||||
|
||||
## 忽略动作报错继续执行
|
||||
|
||||
> `3.3.1` 及以上版本
|
||||
|
||||
默认情况下,尝试执行一个不存在的目标组件动作、JS 脚本执行错误等程序错误都会导致动作执行终止,可以通过`ignoreError: true`来忽略动作报错继续执行后面的动作。
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"title": "第一个按钮发生错误直接阻塞执行,第二个按钮发生错误后仍然执行",
|
||||
"body": [
|
||||
{
|
||||
type: 'button',
|
||||
label: '无法弹出提示',
|
||||
level: 'primary',
|
||||
className: 'mr-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'reload',
|
||||
componentId: 'notfound'
|
||||
},
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msg: 'okk'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
label: '可以弹出提示',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'reload',
|
||||
componentId: 'notfound',
|
||||
ignoreError: true
|
||||
},
|
||||
{
|
||||
actionType: 'toast',
|
||||
args: {
|
||||
msg: 'okk'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# 自定义组件接入事件动作
|
||||
|
||||
需求场景主要是想要自定义组件的内部事件暴露出去,能够通过对事件的监听来执行所需动作,并希望自定义组件自身的动作能够被其他组件调用。接入方法是通过`props.dispatchEvent`派发自身的各种事件,使其具备更灵活的交互设计能力;通过实现`doAction`方法实现其他组件对其专属动作的调用,需要注意的是,此处依赖内部的 `Scoped Context`来实现自身的注册,可以参考 [组件间通信](../../docs/extend/custom-react#组件间通信)。
|
||||
@ -3321,3 +3380,4 @@ http 请求动作执行结束后,后面的动作可以通过 `${responseResult
|
||||
| stopPropagation | `boolean`\|[表达式](../concepts/expression)\|[ConditionBuilder](../../components/form/condition-builder) | false | 停止后续动作执行,`> 1.10.0 及以上版本支持表达式,> 2.9.0 及以上版本支持ConditionBuilder` |
|
||||
| expression | `boolean`\|[表达式](../concepts/expression)\|[ConditionBuilder](../../components/form/condition-builder) | - | 执行条件,不设置表示默认执行,`> 1.10.0 及以上版本支持表达式,> 2.9.0 及以上版本支持ConditionBuilder` |
|
||||
| outputVar | `string` | - | 输出数据变量名 |
|
||||
| ignoreError | `boolean` | false | 当动作执行出错后,是否忽略错误继续执行。`3.3.1 及以上版本支持` |
|
||||
|
@ -24,6 +24,7 @@ export interface ListenerAction {
|
||||
description?: string; // 事件描述,actionType: broadcast
|
||||
componentId?: string; // 组件ID,用于直接执行指定组件的动作,指定多个组件时使用英文逗号分隔
|
||||
componentName?: string; // 组件Name,用于直接执行指定组件的动作,指定多个组件时使用英文逗号分隔
|
||||
ignoreError?: boolean; // 当执行动作发生错误时,是否忽略并继续执行
|
||||
args?: Record<string, any>; // 动作配置,可以配置数据映射。注意:存在schema配置的动作都不能放在args里面,避免数据域不同导致的解析错误问题
|
||||
data?: Record<string, any> | null; // 动作数据参数,可以配置数据映射
|
||||
dataMergeMode?: 'merge' | 'override'; // 参数模式,合并或者覆盖
|
||||
@ -181,8 +182,19 @@ export const runActions = async (
|
||||
actionInstrance = getActionByType('component');
|
||||
}
|
||||
|
||||
// 这些节点的子节点运行逻辑由节点内部实现
|
||||
await runAction(actionInstrance, actionConfig, renderer, event);
|
||||
try {
|
||||
// 这些节点的子节点运行逻辑由节点内部实现
|
||||
await runAction(actionInstrance, actionConfig, renderer, event);
|
||||
} catch (e) {
|
||||
const ignore = actionConfig.ignoreError ?? false;
|
||||
if (!ignore) {
|
||||
throw Error(
|
||||
`${actionConfig.actionType} 动作执行失败,原因:${
|
||||
e.message || '未知'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.stoped) {
|
||||
break;
|
||||
|
@ -43,7 +43,9 @@ export class CmptAction implements RendererAction {
|
||||
: null;
|
||||
|
||||
if (key && !component) {
|
||||
throw Error('目标组件没有找到,请检查componentId或componentName是否正确');
|
||||
throw Error(
|
||||
'尝试执行一个不存在的目标组件动作,请检查目标组件非隐藏状态,且正确指定了componentId或componentName'
|
||||
);
|
||||
}
|
||||
|
||||
if (action.actionType === 'setValue') {
|
||||
|
@ -97,3 +97,70 @@ test('EventAction:prevent', async () => {
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(fetcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('EventAction:ignoreError', async () => {
|
||||
const notify = jest.fn();
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
age: 18
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const {getByText, container}: any = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '按钮',
|
||||
level: 'primary',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'reload',
|
||||
componentId: 'notfound',
|
||||
ignoreError: true
|
||||
},
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: '/api/test3'
|
||||
},
|
||||
{
|
||||
actionType: 'custom',
|
||||
script:
|
||||
"const myMsg = '我是自定义JS';\nsdfsdfsdf();\ndoAction({\n actionType: 'toast',\n args: {\n msg: myMsg\n }\n});\n",
|
||||
ignoreError: true
|
||||
},
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: '/api/test4'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
getModalContainer: () => container,
|
||||
notify,
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('按钮'));
|
||||
await waitFor(() => {
|
||||
expect(fetcher).toHaveBeenCalledTimes(2);
|
||||
expect(fetcher.mock.calls[0][0].url).toEqual('/api/test3');
|
||||
expect(fetcher.mock.calls[1][0].url).toEqual('/api/test4');
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import type {IColumn, ITableStore} from 'amis-core/lib/store/table';
|
||||
import type {IColumn, ITableStore} from 'amis-core';
|
||||
import {observer} from 'mobx-react';
|
||||
|
||||
export function ColGroup({
|
||||
|
Loading…
Reference in New Issue
Block a user