diff --git a/packages/amis-core/src/SchemaRenderer.tsx b/packages/amis-core/src/SchemaRenderer.tsx index 0a081a0e807..d287cd9a1a5 100644 --- a/packages/amis-core/src/SchemaRenderer.tsx +++ b/packages/amis-core/src/SchemaRenderer.tsx @@ -5,6 +5,7 @@ import LazyComponent from './components/LazyComponent'; import { filterSchema, loadRenderer, + RendererComponent, RendererConfig, RendererEnv, RendererProps, @@ -17,7 +18,12 @@ import {DebugWrapper} from './utils/debug'; import getExprProperties from './utils/filter-schema'; import {anyChanged, chainEvents, autobind} from './utils/helper'; import {SimpleMap} from './utils/SimpleMap'; -import {bindEvent, dispatchEvent, RendererEvent} from './utils/renderer-event'; +import { + bindEvent, + checkCircular, + dispatchEvent, + RendererEvent +} from './utils/renderer-event'; import {isAlive} from 'mobx-state-tree'; import {reaction} from 'mobx'; import {resolveVariableAndFilter} from './utils/tpl-builtin'; @@ -221,8 +227,18 @@ export class SchemaRenderer extends React.Component { async dispatchEvent( e: React.MouseEvent, data: any, - renderer?: React.Component // for didmount + renderer?: React.Component // for didmount ): Promise | void> { + // 检查是否存在循环调用 + const eventName = typeof e === 'string' ? e : e.type; + const action = (renderer?.constructor as RendererComponent) + ?.circularEventAction?.[eventName]; + + if (action && !checkCircular(eventName, action, renderer?.props)) { + this.props?.env.notify('warning', '警告:事件动作配置存在循环调用!'); + return; + } + return await dispatchEvent( e, this.cRef || renderer, diff --git a/packages/amis-core/src/factory.tsx b/packages/amis-core/src/factory.tsx index 059d07a4608..442191392ea 100644 --- a/packages/amis-core/src/factory.tsx +++ b/packages/amis-core/src/factory.tsx @@ -87,6 +87,7 @@ export interface RendererProps export type RendererComponent = React.ComponentType & { propsList?: Array; + circularEventAction?: {[eventName: string]: string}; }; export interface RendererConfig extends RendererBasicConfig { diff --git a/packages/amis-core/src/renderers/Form.tsx b/packages/amis-core/src/renderers/Form.tsx index 531fa11e166..ac186ba515f 100644 --- a/packages/amis-core/src/renderers/Form.tsx +++ b/packages/amis-core/src/renderers/Form.tsx @@ -455,6 +455,15 @@ export default class Form extends React.Component { 'multiple' ]; + static circularEventAction: {[eventName: string]: string} = { + inited: 'reload', + submit: 'submit', + submitSucc: 'submit', + submitFail: 'submit', + validateSucc: 'validate', + validateError: 'validate' + }; + hooks: { [propName: string]: Array<() => Promise>; } = {}; @@ -672,7 +681,8 @@ export default class Form extends React.Component { responseData: value?.data ?? {}, responseStatus: store.error ? 1 : 0, responseMsg: store.msg - }) + }), + this ); return value; @@ -832,9 +842,9 @@ export default class Form extends React.Component { .validate(this.hooks['validate'] || [], forceValidate) .then((result: boolean) => { if (result) { - dispatchEvent('validateSucc', data); + dispatchEvent('validateSucc', data, this); } else { - dispatchEvent('validateError', data); + dispatchEvent('validateError', data, this); } return result; }); @@ -866,7 +876,7 @@ export default class Form extends React.Component { submit(fn?: (values: object) => Promise): Promise { const {store, messages, translate: __, dispatchEvent, data} = this.props; this.flush(); - const validateErrCb = () => dispatchEvent('validateError', data); + const validateErrCb = () => dispatchEvent('validateError', data, this); return store.submit( fn, this.hooks['validate'] || [], @@ -1082,7 +1092,8 @@ export default class Form extends React.Component { if (!validationRes) { const dispatcher = await dispatchEvent( 'validateError', - this.props.data + this.props.data, + this ); if (!dispatcher?.prevented) { env.notify('error', __('Form.validateFailed')); @@ -1105,7 +1116,7 @@ export default class Form extends React.Component { // 配了submit事件的表示将提交逻辑全部托管给事件 const {dispatchEvent, onEvent} = this.props; const submitEvent = onEvent?.submit?.actions?.length; - const dispatcher = await dispatchEvent('submit', this.props.data); + const dispatcher = await dispatchEvent('submit', this.props.data, this); if (dispatcher?.prevented || submitEvent) { return; } @@ -1123,11 +1134,15 @@ export default class Form extends React.Component { return Promise.resolve(false); } // 走到这里代表校验成功了 - dispatchEvent('validateSucc', this.props.data); + dispatchEvent('validateSucc', this.props.data, this); if (target) { this.submitToTarget(filterTarget(target, values), values); - dispatchEvent('submitSucc', createObject(this.props.data, values)); + dispatchEvent( + 'submitSucc', + createObject(this.props.data, values), + this + ); } else if (action.actionType === 'reload') { action.target && this.reloadTarget(filterTarget(action.target, values), values); @@ -1150,7 +1165,8 @@ export default class Form extends React.Component { // result为提交接口返回的内容 const dispatcher = await dispatchEvent( 'submitSucc', - createObject(this.props.data, {result}) + createObject(this.props.data, {result}), + this ); if ( !isEffectiveApi(finnalAsyncApi, store.data) || @@ -1168,7 +1184,7 @@ export default class Form extends React.Component { checkInterval ).then((value: any) => { // 派发asyncApiFinished事件 - dispatchEvent('asyncApiFinished', store.data); + dispatchEvent('asyncApiFinished', store.data, this); }); return { cbResult, @@ -1178,7 +1194,8 @@ export default class Form extends React.Component { onFailed: async (result: Payload) => { const dispatcher = await dispatchEvent( 'submitFail', - createObject(this.props.data, {error: result}) + createObject(this.props.data, {error: result}), + this ); return { dispatcher @@ -1205,7 +1222,11 @@ export default class Form extends React.Component { }); } else { // type为submit,但是没有配api以及target时,只派发事件 - dispatchEvent('submitSucc', createObject(this.props.data, values)); + dispatchEvent( + 'submitSucc', + createObject(this.props.data, values), + this + ); } return Promise.resolve(null); diff --git a/packages/amis-core/src/utils/renderer-event.ts b/packages/amis-core/src/utils/renderer-event.ts index 1a226d4451a..7e42b207148 100644 --- a/packages/amis-core/src/utils/renderer-event.ts +++ b/packages/amis-core/src/utils/renderer-event.ts @@ -275,4 +275,18 @@ export const resolveEventData = ( ); }; +export const checkCircular = ( + eventName: string, + actionType: string, + props: any +) => { + const isCircular = props?.onEvent?.[eventName]?.actions?.some( + (item: any) => + item.actionType === actionType && + (item.componentId === props?.id || item.componentName === props?.name) + ); + + return !isCircular; +}; + export default {}; diff --git a/packages/amis/src/renderers/Page.tsx b/packages/amis/src/renderers/Page.tsx index 4ad499f87b6..bfd5d967ccc 100644 --- a/packages/amis/src/renderers/Page.tsx +++ b/packages/amis/src/renderers/Page.tsx @@ -1,16 +1,7 @@ import React from 'react'; -import PropTypes from 'prop-types'; import {Renderer, RendererProps, filterTarget} from 'amis-core'; -import {observer} from 'mobx-react'; import {ServiceStore, IServiceStore} from 'amis-core'; -import { - Api, - SchemaNode, - ActionObject, - Location, - ApiObject, - FunctionPropertyNames -} from 'amis-core'; +import {Api, ActionObject, Location} from 'amis-core'; import {filter, evalExpression} from 'amis-core'; import { isVisible, @@ -34,9 +25,6 @@ import { SchemaMessage } from '../Schema'; import {SchemaRemark} from './Remark'; -import {onAction} from 'mobx-state-tree'; -import mapValues from 'lodash/mapValues'; -import {resolveVariable} from 'amis-core'; import {buildStyle} from 'amis-core'; import {PullRefresh} from 'amis-ui'; import {scrollPosition, isMobile} from 'amis-core'; @@ -271,6 +259,10 @@ export default class Page extends React.Component { 'showErrorMsg' ]; + static circularEventAction: {[eventName: string]: string} = { + inited: 'reload' + }; + constructor(props: PageProps) { super(props); @@ -698,7 +690,8 @@ export default class Page extends React.Component { responseStatus: value?.status === undefined ? (store?.error ? 1 : 0) : value?.status, responseMsg: value?.msg || store?.msg - }) + }), + this ); interval && diff --git a/packages/amis/src/renderers/Service.tsx b/packages/amis/src/renderers/Service.tsx index ce19ad7a6e4..08d461875c1 100644 --- a/packages/amis/src/renderers/Service.tsx +++ b/packages/amis/src/renderers/Service.tsx @@ -174,6 +174,11 @@ export default class Service extends React.Component { static propsList: Array = []; + static circularEventAction: {[eventName: string]: string} = { + fetchInited: 'reload', + fetchSchemaInited: 'reload' + }; + constructor(props: ServiceProps) { super(props); @@ -524,7 +529,8 @@ export default class Service extends React.Component { responseStatus: result?.status === undefined ? (store.error ? 1 : 0) : result?.status, responseMsg: store.msg - }) + }), + this ); if (!isEmpty(data) && onBulkChange) { @@ -537,14 +543,18 @@ export default class Service extends React.Component { afterSchemaFetch(schema: any) { const {onBulkChange, formStore, dispatchEvent, store} = this.props; - dispatchEvent?.('fetchSchemaInited', { - ...schema, - __response: {msg: store.msg, error: store.error}, // 保留,兼容历史 - responseData: schema, - responseStatus: - schema?.status === undefined ? (store.error ? 1 : 0) : schema?.status, - responseMsg: store.msg - }); + dispatchEvent?.( + 'fetchSchemaInited', + { + ...schema, + __response: {msg: store.msg, error: store.error}, // 保留,兼容历史 + responseData: schema, + responseStatus: + schema?.status === undefined ? (store.error ? 1 : 0) : schema?.status, + responseMsg: store.msg + }, + this + ); if (formStore && schema?.data && onBulkChange) { onBulkChange && onBulkChange(schema.data); diff --git a/packages/amis/src/renderers/Wizard.tsx b/packages/amis/src/renderers/Wizard.tsx index 1a75ec7d8e0..5dd2b7cce50 100644 --- a/packages/amis/src/renderers/Wizard.tsx +++ b/packages/amis/src/renderers/Wizard.tsx @@ -237,6 +237,10 @@ export default class Wizard extends React.Component { 'startStep' ]; + static circularEventAction: {[eventName: string]: string} = { + inited: 'reload' + }; + dom: any; form: any; asyncCancel: () => void; @@ -382,7 +386,8 @@ export default class Wizard extends React.Component { const rendererEvent = await dispatchEvent( action, - value ? createObject(data, value) : data + value ? createObject(data, value) : data, + this ); return rendererEvent?.prevented ?? false;