Skip to content

Commit

Permalink
Merge pull request baidu#9466 from 2betop/fix-api-control
Browse files Browse the repository at this point in the history
fix: 修复 api 编辑输入太快立马弹出详情时出现弹窗空白问题
  • Loading branch information
hsm-lv committed Jan 18, 2024
2 parents 0c59ce4 + 9b9a6b8 commit 703e0c3
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 131 deletions.
264 changes: 147 additions & 117 deletions packages/amis-core/src/renderers/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -887,11 +887,14 @@ export default class Form extends React.Component<FormProps, object> {
async validate(
forceValidate?: boolean,
throwErrors: boolean = false,
toastErrors: boolean = true
toastErrors: boolean = true,
skipFlush = false
): Promise<boolean> {
const {store, dispatchEvent, data, messages, translate: __} = this.props;

this.flush();
if (!skipFlush) {
await this.flush();
}
const result = await store.validate(
this.hooks['validate'] || [],
forceValidate,
Expand Down Expand Up @@ -930,12 +933,15 @@ export default class Form extends React.Component<FormProps, object> {
store.setValues(value, undefined, replace);
}

submit(
async submit(
fn?: (values: object) => Promise<any>,
throwErrors: boolean = false
throwErrors: boolean = false,
skipFlush = false
): Promise<any> {
const {store, messages, translate: __, dispatchEvent, data} = this.props;
this.flush();
if (!skipFlush) {
await this.flush();
}
const validateErrCb = () => dispatchEvent('validateError', data);
return store.submit(
fn,
Expand All @@ -949,10 +955,20 @@ export default class Form extends React.Component<FormProps, object> {
}

// 如果开启了 lazyChange,需要一个 flush 方法把队列中值应用上。
flush() {
const hooks = this.hooks['flush'] || [];
hooks.forEach(fn => fn());
this.lazyEmitChange.flush();
flushing = false;
async flush() {
try {
if (this.flushing) {
return;
}

this.flushing = true;
const hooks = this.hooks['flush'] || [];
await Promise.all(hooks.map(fn => fn()));
await this.lazyEmitChange.flush();
} finally {
this.flushing = false;
}
}

reset() {
Expand Down Expand Up @@ -1037,14 +1053,19 @@ export default class Form extends React.Component<FormProps, object> {
}

store.clearRestError();
(submit || (submitOnChange && store.inited)) &&
this.handleAction(

if (submit || (submitOnChange && store.inited)) {
await this.handleAction(
undefined,
{
type: 'submit'
type: 'submit',
// 如果这里不跳过,会相互依赖死循环,flush 会 让 emiteChange 立即执行
// handleAction 里面又会调用 flush
skipFormFlush: true
},
store.data
);
}
}

handleBulkChange(values: Object, submit: boolean) {
Expand Down Expand Up @@ -1130,7 +1151,9 @@ export default class Form extends React.Component<FormProps, object> {
} = this.props;

// 做动作之前,先把数据同步一下。
this.flush();
if (!action.skipFormFlush) {
await this.flush();
}

if (trimValues) {
store.trimValues();
Expand Down Expand Up @@ -1190,120 +1213,127 @@ export default class Form extends React.Component<FormProps, object> {
store.clear(this.handleReset(action));
}

return this.submit(async values => {
if (onSubmit) {
const result = await onSubmit(values, action);
if (result === false) {
return Promise.resolve(false);
return this.submit(
async values => {
if (onSubmit) {
const result = await onSubmit(values, action);
if (result === false) {
return Promise.resolve(false);
}
}
}

// 走到这里代表校验成功了
dispatchEvent('validateSucc', this.props.data);

if (target) {
this.submitToTarget(filterTarget(target, values), values);
/** 可能配置页面跳转事件,页面路由变化导致persistKey不一致,无法清除持久化数据,所以提交成功事件之前先清理一下 */
clearPersistDataAfterSubmit && store.clearLocalPersistData();
dispatchEvent('submitSucc', createObject(this.props.data, values));
} else if (action.actionType === 'reload') {
action.target &&
this.reloadTarget(filterTarget(action.target, values), values);
} else if (action.actionType === 'dialog') {
store.openDialog(
data,
undefined,
action.callback,
delegate || (this.context as any)
);
} else if (action.actionType === 'drawer') {
store.openDrawer(data);
} else if (isEffectiveApi(action.api || api, values)) {
let finnalAsyncApi = action.asyncApi || asyncApi;
isEffectiveApi(finnalAsyncApi, store.data) &&
store.updateData({
[finishedField || 'finished']: false
});
// 走到这里代表校验成功了
dispatchEvent('validateSucc', this.props.data);

if (target) {
this.submitToTarget(filterTarget(target, values), values);
/** 可能配置页面跳转事件,页面路由变化导致persistKey不一致,无法清除持久化数据,所以提交成功事件之前先清理一下 */
clearPersistDataAfterSubmit && store.clearLocalPersistData();
dispatchEvent('submitSucc', createObject(this.props.data, values));
} else if (action.actionType === 'reload') {
action.target &&
this.reloadTarget(filterTarget(action.target, values), values);
} else if (action.actionType === 'dialog') {
store.openDialog(
data,
undefined,
action.callback,
delegate || (this.context as any)
);
} else if (action.actionType === 'drawer') {
store.openDrawer(data);
} else if (isEffectiveApi(action.api || api, values)) {
let finnalAsyncApi = action.asyncApi || asyncApi;
isEffectiveApi(finnalAsyncApi, store.data) &&
store.updateData({
[finishedField || 'finished']: false
});

return store
.saveRemote(action.api || (api as Api), values, {
successMessage:
typeof saveSuccess === 'string'
? filter(saveSuccess, store.data)
: undefined,
errorMessage:
typeof saveFailed === 'string'
? filter(saveFailed, store.data)
: undefined,
onSuccess: async (result: Payload) => {
clearPersistDataAfterSubmit && store.clearLocalPersistData();
// result为提交接口返回的内容
const dispatcher = await dispatchEvent(
'submitSucc',
createObject(this.props.data, {result})
);
if (
!isEffectiveApi(finnalAsyncApi, store.data) ||
store.data[finishedField || 'finished']
) {
return store
.saveRemote(action.api || (api as Api), values, {
successMessage:
typeof saveSuccess === 'string'
? filter(saveSuccess, store.data)
: undefined,
errorMessage:
typeof saveFailed === 'string'
? filter(saveFailed, store.data)
: undefined,
onSuccess: async (result: Payload) => {
clearPersistDataAfterSubmit && store.clearLocalPersistData();
// result为提交接口返回的内容
const dispatcher = await dispatchEvent(
'submitSucc',
createObject(this.props.data, {result})
);
if (
!isEffectiveApi(finnalAsyncApi, store.data) ||
store.data[finishedField || 'finished']
) {
return {
cbResult: null,
dispatcher
};
}
const cbResult = until(
() => store.checkRemote(finnalAsyncApi as Api, store.data),
(ret: any) => ret && ret[finishedField || 'finished'],
cancel => (this.asyncCancel = cancel),
checkInterval
).then((value: any) => {
// 派发asyncApiFinished事件
dispatchEvent('asyncApiFinished', store.data);
});
return {
cbResult,
dispatcher
};
},
onFailed: async (result: Payload) => {
const dispatcher = await dispatchEvent(
'submitFail',
createObject(this.props.data, {error: result})
);
return {
cbResult: null,
dispatcher
};
}
const cbResult = until(
() => store.checkRemote(finnalAsyncApi as Api, store.data),
(ret: any) => ret && ret[finishedField || 'finished'],
cancel => (this.asyncCancel = cancel),
checkInterval
).then((value: any) => {
// 派发asyncApiFinished事件
dispatchEvent('asyncApiFinished', store.data);
});
return {
cbResult,
dispatcher
};
},
onFailed: async (result: Payload) => {
const dispatcher = await dispatchEvent(
'submitFail',
createObject(this.props.data, {error: result})
);
return {
dispatcher
};
}
})
.then(async response => {
onSaved && onSaved(values, response);
const feedback = action.feedback || this.props.feedback;

// submit 也支持 feedback
if (feedback && isVisible(feedback, store.data)) {
const confirmed = await this.openFeedback(feedback, store.data);

// 如果 feedback 配置了,取消就跳过原有逻辑。
if (feedback.skipRestOnCancel && !confirmed) {
throw new SkipOperation();
} else if (feedback.skipRestOnConfirm && confirmed) {
throw new SkipOperation();
})
.then(async response => {
onSaved && onSaved(values, response);
const feedback = action.feedback || this.props.feedback;

// submit 也支持 feedback
if (feedback && isVisible(feedback, store.data)) {
const confirmed = await this.openFeedback(
feedback,
store.data
);

// 如果 feedback 配置了,取消就跳过原有逻辑。
if (feedback.skipRestOnCancel && !confirmed) {
throw new SkipOperation();
} else if (feedback.skipRestOnConfirm && confirmed) {
throw new SkipOperation();
}
}
}

return injectObjectChain(store.data, {
__payload: values,
__response: response
return injectObjectChain(store.data, {
__payload: values,
__response: response
});
});
});
} else {
clearPersistDataAfterSubmit && store.clearLocalPersistData();
// type为submit,但是没有配api以及target时,只派发事件
dispatchEvent('submitSucc', createObject(this.props.data, values));
}
} else {
clearPersistDataAfterSubmit && store.clearLocalPersistData();
// type为submit,但是没有配api以及target时,只派发事件
dispatchEvent('submitSucc', createObject(this.props.data, values));
}

return Promise.resolve(null);
}, throwErrors)
return Promise.resolve(null);
},
throwErrors,
true
)
.then(values => {
// 有可能 onSubmit return false 了,那么后面的就不应该再执行了。
if (values === false) {
Expand Down Expand Up @@ -1349,7 +1379,7 @@ export default class Form extends React.Component<FormProps, object> {
store.clear(onReset);
} else if (action.actionType === 'validate') {
store.setCurrentAction(action);
return this.validate(true, throwErrors);
return this.validate(true, throwErrors, true, true);
} else if (action.actionType === 'dialog') {
store.setCurrentAction(action);
store.openDialog(
Expand Down
5 changes: 2 additions & 3 deletions packages/amis-core/src/renderers/wrapControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,7 @@ export function wrapControl<
} = this.props;

// 提交前先把之前的 lazyEmit 执行一下。
this.hook3 = () => {
this.lazyEmitChange.flush();
};
this.hook3 = () => this.lazyEmitChange.flush();
addHook?.(this.hook3, 'flush');

const formItem = this.model as IFormItemStore;
Expand Down Expand Up @@ -691,6 +689,7 @@ export function wrapControl<
if (!this.model) {
return;
}

const model = this.model;
const value = this.model.tmpValue;
const oldValue = model.extraName
Expand Down
2 changes: 2 additions & 0 deletions packages/amis-core/src/store/iRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ export const iRendererStore = StoreNode.named('iRendererStore')

setCurrentAction(action: object) {
self.action = action;
self.dialogData = false;
self.drawerOpen = false;
},

openDialog(
Expand Down
10 changes: 4 additions & 6 deletions packages/amis-editor/src/renderer/APIControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,9 @@ export default class APIControl extends React.Component<
);
}

handleSimpleInputChange = debounce((value: string) => {
this.handleSubmit(value, 'input');
}, 1000);
handleSimpleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.handleSubmit(e.currentTarget.value, 'input');
};

@autobind
handleSubmit(values: SchemaApi, action?: 'input' | 'picker-submit') {
Expand Down Expand Up @@ -1034,9 +1034,7 @@ export default class APIControl extends React.Component<
type="text"
disabled={disabled}
placeholder="http://"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
this.handleSimpleInputChange(e.currentTarget.value)
}
onChange={this.handleSimpleInputChange}
/>
)}
{enablePickerMode ? this.renderPickerSchema() : null}
Expand Down

0 comments on commit 703e0c3

Please sign in to comment.