feat: 事件动作类型增强, 能够根据actionType自动类型提示和校验, 同时修复AjaxAction里会将adaptor/requestAdaptor/responseAdaptor转换成对象的问题 #4189

This commit is contained in:
allanyu 2022-04-29 15:09:01 +08:00
parent 92a2208c82
commit f7fc6d011f
17 changed files with 196 additions and 132 deletions

View File

@ -1,11 +1,25 @@
import {RendererProps} from '../factory'; import { RendererProps } from '../factory';
import {extendObject} from '../utils/helper'; import { extendObject } from '../utils/helper';
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import {evalExpression} from '../utils/tpl'; import { evalExpression } from '../utils/tpl';
import {dataMapping} from '../utils/tpl-builtin'; import { dataMapping } from '../utils/tpl-builtin';
import { IAjaxAction } from './AjaxAction';
import { IBreakAction } from './BreakAction';
import { IBroadcastAction } from './BroadcastAction';
import { ICmptAction } from './CmptAction';
import { IContinueAction } from './ContinueAction';
import { ICopyAction } from './CopyAction';
import { ICustomAction } from './CustomAction';
import { IAlertAction, IConfirmAction, IDialogAction, ICloseDialogAction } from './DialogAction';
import { IDrawerAction, ICloseDrawerAction } from './DrawerAction';
import { IEmailAction } from './EmailAction';
import { ILinkAction } from './LinkAction';
import { ILoopAction } from './LoopAction';
import { IPageGoAction } from './PageAction';
import { IParallelAction } from './ParallelAction';
import { ISwitchAction } from './SwitchAction';
import { IToastAction } from './ToastAction';
// 逻辑动作类型支持并行、排他switch、循环支持continue和break
type LogicActionType = 'parallel' | 'switch' | 'loop' | 'continue' | 'break';
// 循环动作执行状态 // 循环动作执行状态
export enum LoopStatus { export enum LoopStatus {
@ -15,21 +29,48 @@ export enum LoopStatus {
} }
// 监听器动作定义 // 监听器动作定义
export interface ListenerAction { export interface IListenerAction {
actionType: 'broadcast' | LogicActionType | 'custom' | string; // 动作类型 逻辑动作|自定义(脚本支撑)|reload|url|ajax|dialog|drawer 其他扩充的组件动作 actionType: string; // 动作类型 逻辑动作|自定义(脚本支撑)|reload|url|ajax|dialog|drawer 其他扩充的组件动作
description?: string; // 事件描述actionType: broadcast description?: string; // 事件描述actionType: broadcast
componentId?: string; // 组件ID用于直接执行指定组件的动作 componentId?: string; // 组件ID用于直接执行指定组件的动作
args?: any; // 参数,可以配置数据映射 args?: Record<string, any>; // 参数,可以配置数据映射
outputVar?: any; // 输出数据变量名 outputVar?: string; // 输出数据变量名
preventDefault?: boolean; // 阻止原有组件的动作行为 preventDefault?: boolean; // 阻止原有组件的动作行为
stopPropagation?: boolean; // 阻止后续的事件处理器执行 stopPropagation?: boolean; // 阻止后续的事件处理器执行
expression?: string; // 执行条件 expression?: string; // 执行条件
} }
export interface LogicAction extends ListenerAction { // 监听器动作联合类型
export type ListenerAction =
| IToastAction
| IBroadcastAction
| IAjaxAction
| ICmptAction
| ICopyAction
| ICustomAction
| IAlertAction
| IConfirmAction
| IDialogAction
| ICloseDialogAction
| IDrawerAction
| ICloseDrawerAction
| IEmailAction
| ILinkAction
| LogicAction
| IPageGoAction;
export interface ILogicAction extends IListenerAction {
children?: ListenerAction[]; // 子动作 children?: ListenerAction[]; // 子动作
} }
// 逻辑动作类型支持并行、排他switch、循环支持continue和break
export type LogicAction =
| IParallelAction
| ISwitchAction
| ILoopAction
| IContinueAction
| IBreakAction;
export interface ListenerContext extends React.Component<RendererProps> { export interface ListenerContext extends React.Component<RendererProps> {
[propName: string]: any; [propName: string]: any;
} }
@ -46,7 +87,7 @@ export interface RendererAction {
} }
// 存储 Action 和类型的映射关系,用于后续查找 // 存储 Action 和类型的映射关系,用于后续查找
const ActionTypeMap: {[key: string]: RendererAction} = {}; const ActionTypeMap: { [key: string]: RendererAction } = {};
// 注册 Action // 注册 Action
export const registerAction = (type: string, action: RendererAction) => { export const registerAction = (type: string, action: RendererAction) => {
@ -104,7 +145,7 @@ export const runAction = async (
event: any event: any
) => { ) => {
// 用户可能需要用到事件数据和当前域的数据因此merge事件数据和当前渲染器数据 // 用户可能需要用到事件数据和当前域的数据因此merge事件数据和当前渲染器数据
// 需要保持渲染器数据链完整 // 需要保持渲染器数据链完整
const mergeData = extendObject(renderer.props.data, { const mergeData = extendObject(renderer.props.data, {
event event
}); });
@ -120,7 +161,7 @@ export const runAction = async (
let args = event.data; let args = event.data;
if (actionConfig.args) { if (actionConfig.args) {
args = dataMapping(actionConfig.args, mergeData); args = dataMapping(actionConfig.args, mergeData, (key) => ['adaptor', 'responseAdaptor', 'requestAdaptor'].includes(key));
} }
await actionInstrance.run( await actionInstrance.run(

View File

@ -1,24 +1,25 @@
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import {Api} from '../types'; import { Api } from '../types';
import {normalizeApiResponseData} from '../utils/api'; import { normalizeApiResponseData } from '../utils/api';
import {ServerError} from '../utils/errors'; import { ServerError } from '../utils/errors';
import {createObject, isEmpty} from '../utils/helper'; import { createObject, isEmpty } from '../utils/helper';
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction, IListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction
} from './Action'; } from './Action';
export interface IAjaxAction extends ListenerAction { export interface IAjaxAction extends IListenerAction {
action: 'ajax';
args: { args: {
api: Api; api: Api;
messages: { messages?: {
success: string; success: string;
failed: string; failed: string;
}; };
options: object; options?: Record<string, any>
[propName: string]: any; [propName: string]: any;
}; };
} }
@ -56,8 +57,8 @@ export class AjaxAction implements RendererAction {
event.data, event.data,
action.outputVar action.outputVar
? { ? {
[`${action.outputVar}`]: responseData [`${action.outputVar}`]: responseData
} }
: responseData : responseData
) )
); );
@ -76,9 +77,9 @@ export class AjaxAction implements RendererAction {
msg, msg,
result.msgTimeout !== undefined result.msgTimeout !== undefined
? { ? {
closeButton: true, closeButton: true,
timeout: result.msgTimeout timeout: result.msgTimeout
} }
: undefined : undefined
); );
} }
@ -92,9 +93,9 @@ export class AjaxAction implements RendererAction {
e.message, e.message,
result.msgTimeout !== undefined result.msgTimeout !== undefined
? { ? {
closeButton: true, closeButton: true,
timeout: result.msgTimeout timeout: result.msgTimeout
} }
: undefined : undefined
); );
} else { } else {

View File

@ -1,12 +1,16 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
LoopStatus, LoopStatus,
registerAction registerAction,
ILogicAction
} from './Action'; } from './Action';
export interface IBreakAction extends ILogicAction {
actionType: 'break';
}
/** /**
* breach * breach
* *
@ -16,7 +20,7 @@ import {
*/ */
export class BreakAction implements RendererAction { export class BreakAction implements RendererAction {
async run( async run(
action: ListenerAction, action: IBreakAction,
renderer: ListenerContext, renderer: ListenerContext,
event: RendererEvent<any> event: RendererEvent<any>
) { ) {

View File

@ -1,14 +1,15 @@
import {RendererProps} from '../factory'; import { RendererProps } from '../factory';
import {createObject} from '../utils/helper'; import { createObject } from '../utils/helper';
import {RendererEvent, dispatchEvent} from '../utils/renderer-event'; import { RendererEvent, dispatchEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction, IListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction
} from './Action'; } from './Action';
export interface IBroadcastAction extends ListenerAction { export interface IBroadcastAction extends IListenerAction {
actionType: 'broadcast';
eventName: string; // 事件名称actionType: broadcast eventName: string; // 事件名称actionType: broadcast
} }

View File

@ -1,15 +1,16 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import {dataMapping} from '../utils/tpl-builtin';
import { import {
RendererAction, RendererAction,
ListenerAction, IListenerAction,
ListenerContext, ListenerContext,
LoopStatus,
registerAction registerAction
} from './Action'; } from './Action';
export interface ICmptAction extends ListenerAction { export interface ICmptAction extends IListenerAction {
value?: string | {[key: string]: string}; actionType: 'setValue' | 'show' | 'hidden' | 'enabled' | 'disabled' | 'reload';
args: {
value?: string | { [key: string]: string };
}
} }
/** /**

View File

@ -1,12 +1,16 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
LoopStatus, LoopStatus,
registerAction registerAction,
ILogicAction
} from './Action'; } from './Action';
export interface IContinueAction extends ILogicAction {
actionType: 'continue';
}
/** /**
* continue * continue
* *
@ -16,7 +20,7 @@ import {
*/ */
export class ContinueAction implements RendererAction { export class ContinueAction implements RendererAction {
async run( async run(
action: ListenerAction, action: IContinueAction,
renderer: ListenerContext, renderer: ListenerContext,
event: RendererEvent<any> event: RendererEvent<any>
) { ) {

View File

@ -1,13 +1,13 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import {filter} from '../utils/tpl';
import { import {
RendererAction, RendererAction,
ListenerAction, IListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction
} from './Action'; } from './Action';
export interface ICopyAction extends ListenerAction { export interface ICopyAction extends IListenerAction {
actionType: 'copy';
args: { args: {
content: string; content: string;
copyFormat?: string; copyFormat?: string;

View File

@ -1,14 +1,21 @@
import {RendererEvent} from '../utils/renderer-event'; import { Action } from '../types';
import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction, IListenerAction,
ListenerContext, ListenerContext,
LoopStatus, registerAction,
registerAction ListenerAction
} from './Action'; } from './Action';
export interface ICustomAction extends ListenerAction { export interface ICustomAction extends IListenerAction {
script: string; // 自定义JSactionType: custom actionType: 'custom';
script: string | ((
renderer: any,
doAction: (action: Action, data: Record<string, any>) => void,
event: RendererEvent<any>,
action: ListenerAction,
) => void); // 自定义JSactionType: custom
} }
/** /**
@ -42,7 +49,7 @@ export class CustomAction implements RendererAction {
null, null,
renderer, renderer,
renderer.props.onAction?.bind(renderer, event.context.nativeEvent) || renderer.props.onAction?.bind(renderer, event.context.nativeEvent) ||
renderer.doAction?.bind(renderer), renderer.doAction?.bind(renderer),
event, event,
action action
); );

View File

@ -1,20 +1,23 @@
import {SchemaNode} from '../types'; import { SchemaNode } from '../types';
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction, ListenerAction,
IListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction
} from './Action'; } from './Action';
export interface IAlertAction extends ListenerAction { export interface IAlertAction extends IListenerAction {
actionType: 'alert';
args: { args: {
msg: string; msg: string;
[propName: string]: any; [propName: string]: any;
}; };
} }
export interface IConfirmAction extends ListenerAction { export interface IConfirmAction extends IListenerAction {
actionType: 'confirm';
args: { args: {
title: string; title: string;
msg: string; msg: string;
@ -22,7 +25,8 @@ export interface IConfirmAction extends ListenerAction {
}; };
} }
export interface IDialogAction extends ListenerAction { export interface IDialogAction extends IListenerAction {
actionType: 'dialog';
dialog: SchemaNode; dialog: SchemaNode;
} }
@ -43,6 +47,10 @@ export class DialogAction implements RendererAction {
} }
} }
export interface ICloseDialogAction extends IListenerAction {
actionType: 'closeDialog';
}
/** /**
* *
* *

View File

@ -1,13 +1,15 @@
import {SchemaNode} from '../types'; import { SchemaNode } from '../types';
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
IListenerAction,
ListenerAction, ListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction
} from './Action'; } from './Action';
export interface IDrawerAction extends ListenerAction { export interface IDrawerAction extends IListenerAction {
actionType: 'drawer';
drawer: SchemaNode; drawer: SchemaNode;
} }
@ -28,6 +30,11 @@ export class DrawerAction implements RendererAction {
} }
} }
export interface ICloseDrawerAction extends IListenerAction {
actionType: 'closeDrawer';
}
/** /**
* *
* *

View File

@ -1,16 +1,15 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import {filter} from '../utils/tpl';
import pick from 'lodash/pick'; import pick from 'lodash/pick';
import mapValues from 'lodash/mapValues';
import qs from 'qs'; import qs from 'qs';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction,
IListenerAction
} from './Action'; } from './Action';
export interface IEmailAction extends ListenerAction { export interface IEmailAction extends IListenerAction {
actionType: 'email';
args: { args: {
to: string; to: string;
cc: string; cc: string;

View File

@ -1,19 +1,17 @@
import {Action} from '../types'; import { buildApi } from '../utils/api';
import {buildApi} from '../utils/api'; import { RendererEvent } from '../utils/renderer-event';
import {isEmpty, isObject, qsstringify} from '../utils/helper';
import {RendererEvent} from '../utils/renderer-event';
import {filter} from '../utils/tpl';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction,
IListenerAction
} from './Action'; } from './Action';
export interface ILinkAction extends ListenerAction { export interface ILinkAction extends IListenerAction {
actionType: 'link' | 'url' | 'jump';
args: { args: {
link: string; link?: string;
url?: never; url?: never;
blank?: boolean; blank?: boolean;
params?: { params?: {
@ -23,18 +21,6 @@ export interface ILinkAction extends ListenerAction {
}; };
} }
export interface IUrlAction extends ListenerAction {
args: {
url: string;
link?: never;
blank?: boolean;
params?: {
[key: string]: string;
};
[propName: string]: any;
};
}
/** /**
* *
* *
@ -44,7 +30,7 @@ export interface IUrlAction extends ListenerAction {
*/ */
export class LinkAction implements RendererAction { export class LinkAction implements RendererAction {
async run( async run(
action: ListenerAction, action: ILinkAction,
renderer: ListenerContext, renderer: ListenerContext,
event: RendererEvent<any> event: RendererEvent<any>
) { ) {
@ -71,6 +57,7 @@ export class LinkAction implements RendererAction {
urlObj.url, urlObj.url,
{ {
actionType: action.actionType, actionType: action.actionType,
type: 'button',
...action.args ...action.args
}, },
action.args action.args

View File

@ -1,18 +1,17 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import {createObject} from '../utils/helper'; import { createObject } from '../utils/helper';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
LogicAction,
LoopStatus, LoopStatus,
registerAction, registerAction,
runAction, runActions,
runActions ILogicAction
} from './Action'; } from './Action';
import {resolveVariable} from '../utils/tpl-builtin'; import { resolveVariable } from '../utils/tpl-builtin';
export interface ILoopAction extends ListenerAction, LogicAction { export interface ILoopAction extends ILogicAction {
actionType: 'loop';
args: { args: {
loopName: string; loopName: string;
[propName: string]: any; [propName: string]: any;

View File

@ -1,12 +1,13 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction, IListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction
} from './Action'; } from './Action';
export interface IPageGoAction extends ListenerAction { export interface IPageGoAction extends IListenerAction {
actionType: 'goBack' | 'refresh' | 'goPage';
args: { args: {
delta?: number; delta?: number;
[propName: string]: any; [propName: string]: any;
@ -22,7 +23,7 @@ export interface IPageGoAction extends ListenerAction {
*/ */
export class PageGoBackAction implements RendererAction { export class PageGoBackAction implements RendererAction {
async run( async run(
action: ListenerAction, action: IPageGoAction,
renderer: ListenerContext, renderer: ListenerContext,
event: RendererEvent<any> event: RendererEvent<any>
) { ) {
@ -56,7 +57,7 @@ export class PageGoAction implements RendererAction {
*/ */
export class PageRefreshAction implements RendererAction { export class PageRefreshAction implements RendererAction {
async run( async run(
action: ListenerAction, action: IPageGoAction,
renderer: ListenerContext, renderer: ListenerContext,
event: RendererEvent<any> event: RendererEvent<any>
) { ) {

View File

@ -1,14 +1,16 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
ILogicAction,
LogicAction, LogicAction,
registerAction, registerAction,
runActions runActions
} from './Action'; } from './Action';
export interface IParallelAction extends ListenerAction, LogicAction {} export interface IParallelAction extends ILogicAction {
actionType: 'parallel';
}
export class ParallelAction implements RendererAction { export class ParallelAction implements RendererAction {
async run( async run(

View File

@ -1,16 +1,16 @@
import {inflate} from 'zlib'; import { RendererEvent } from '../utils/renderer-event';
import {RendererEvent} from '../utils/renderer-event'; import { evalExpression } from '../utils/tpl';
import {evalExpression} from '../utils/tpl';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
LogicAction,
registerAction, registerAction,
runActions runActions,
ILogicAction
} from './Action'; } from './Action';
export interface ISwitchAction extends ListenerAction, LogicAction {} export interface ISwitchAction extends ILogicAction {
actionType: 'switch';
}
/** /**
* *

View File

@ -1,16 +1,17 @@
import {RendererEvent} from '../utils/renderer-event'; import { RendererEvent } from '../utils/renderer-event';
import { import {
RendererAction, RendererAction,
ListenerAction,
ListenerContext, ListenerContext,
registerAction registerAction,
IListenerAction
} from './Action'; } from './Action';
import {resolveVariableAndFilter} from '../utils/tpl-builtin';
export interface IToastAction extends ListenerAction { export interface IToastAction extends IListenerAction {
msg: string; actionType: 'toast';
msgType?: string; args: {
position?: msg: string;
msgType?: string;
position?:
| 'top-right' | 'top-right'
| 'top-center' | 'top-center'
| 'top-left' | 'top-left'
@ -18,9 +19,10 @@ export interface IToastAction extends ListenerAction {
| 'bottom-left' | 'bottom-left'
| 'bottom-right' | 'bottom-right'
| 'center'; | 'center';
closeButton?: boolean; closeButton?: boolean;
showIcon?: boolean; showIcon?: boolean;
timeout?: number; timeout?: number;
}
} }
/** /**