fix: anti double submit in form

This commit is contained in:
jialin
2026-06-11 20:02:27 +08:00
committed by jialin
parent 1218c6d096
commit a7b33a928b
23 changed files with 246 additions and 93 deletions
@@ -1,5 +1,6 @@
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import useSubmitLock from '@/hooks/use-submit-lock';
import { useQueryClusterList } from '@/pages/cluster-management/services/use-query-cluster-list';
import Separator from '@/pages/llmodels/components/separator';
import { SearchOutlined } from '@ant-design/icons';
@@ -89,7 +90,7 @@ const AddModal: React.FC<AddModalProps> = ({
const [templateId, setTemplateId] = useState<number | undefined>();
const [instanceKeyword, setInstanceKeyword] = useState('');
const [templateKeyword, setTemplateKeyword] = useState('');
const [loading, setLoading] = useState(false);
const { loading, guard, run, release } = useSubmitLock();
const initializedRef = useRef(false);
const {
@@ -385,7 +386,7 @@ const AddModal: React.FC<AddModalProps> = ({
});
const handleSubmit = () => {
form.current?.submit();
guard(() => form.current?.submit());
};
const handleCancel = () => {
@@ -394,15 +395,12 @@ const AddModal: React.FC<AddModalProps> = ({
};
const onFinish = async (values: FormData) => {
setLoading(true);
try {
await run(async () => {
await onOk({
...values
});
console.log('submit form values', values);
} finally {
setLoading(false);
}
});
};
const handleInstanceTypeChange = (item: InstanceTypeItem) => {
@@ -576,6 +574,7 @@ const AddModal: React.FC<AddModalProps> = ({
currentData={data}
disabled={readonly}
onFinish={onFinish}
onFinishFailed={release}
onScopeChange={handleScopeChange}
open={open}
instanceTypeList={ownedInstanceTypes}
@@ -70,6 +70,7 @@ interface InstanceFormProps {
// tenant-scoped instance-type / template offerings.
onScopeChange?: (orgId: number | null | undefined) => void;
onFinish: (values: FormData) => Promise<void>;
onFinishFailed?: (errorInfo: any) => void;
}
const TABKeysMap = {
@@ -113,7 +114,8 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
instanceTypeList = [],
noAvailableInstanceTypes,
onScopeChange,
onFinish
onFinish,
onFinishFailed
} = props;
const intl = useIntl();
const { getRuleMessage } = useAppUtils();
@@ -370,6 +372,7 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
]
}));
rawHandleOnFinishFailed({ ...errorInfo, errorFields });
onFinishFailed?.(errorInfo);
};
const detectMode = (volume?: FormData['spec']['volume']) => {
if (volume?.persistent?.name || volume?.persistentTemplate?.name) {
@@ -1,4 +1,5 @@
import { PageActionType } from '@/config/types';
import useSubmitLock from '@/hooks/use-submit-lock';
import { ModalFooter } from '@gpustack/core-ui';
import { useRef } from 'react';
import FormDrawer from '../../../_components/form-drawer';
@@ -23,9 +24,10 @@ const AddPublicKeyModal: React.FC<AddPublicKeyModalProps> = ({
onCancel
}) => {
const form = useRef<any>(null);
const { loading, guard, run, release } = useSubmitLock();
const handleSubmit = () => {
form.current?.submit();
guard(() => form.current?.submit());
};
const handleCancel = () => {
@@ -34,7 +36,7 @@ const AddPublicKeyModal: React.FC<AddPublicKeyModalProps> = ({
};
const onFinish = async (values: FormData) => {
onOk({ ...values });
await run(() => onOk({ ...values }));
};
return (
@@ -48,6 +50,7 @@ const AddPublicKeyModal: React.FC<AddPublicKeyModalProps> = ({
<ModalFooter
onOk={handleSubmit}
onCancel={handleCancel}
loading={loading}
style={{
padding: '16px 24px 8px',
display: 'flex',
@@ -61,6 +64,7 @@ const AddPublicKeyModal: React.FC<AddPublicKeyModalProps> = ({
action={action}
currentData={data}
onFinish={onFinish}
onFinishFailed={release}
open={open}
/>
</FormDrawer>
@@ -11,11 +11,12 @@ interface PublicKeyFormProps {
action: PageActionType;
currentData?: ListItem | null;
onFinish: (values: FormData) => Promise<void>;
onFinishFailed?: (errorInfo: any) => void;
}
const GPUServicePublicKeyForm: React.FC<PublicKeyFormProps> = forwardRef(
(props, ref) => {
const { action, currentData, open, onFinish } = props;
const { action, currentData, open, onFinish, onFinishFailed } = props;
const [form] = Form.useForm<FormData>();
useEffect(() => {
@@ -50,6 +51,7 @@ const GPUServicePublicKeyForm: React.FC<PublicKeyFormProps> = forwardRef(
name="gpuServicePublicKeyForm"
form={form}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
preserve={false}
initialValues={{}}
>
@@ -1,4 +1,5 @@
import { PageActionType } from '@/config/types';
import useSubmitLock from '@/hooks/use-submit-lock';
import { ModalFooter } from '@gpustack/core-ui';
import { useRef } from 'react';
import FormDrawer from '../../../_components/form-drawer';
@@ -23,9 +24,10 @@ const AddStorageTypeModal: React.FC<AddStorageTypeModalProps> = ({
onCancel
}) => {
const form = useRef<any>(null);
const { loading, guard, run, release } = useSubmitLock();
const handleSubmit = () => {
form.current?.submit();
guard(() => form.current?.submit());
};
const handleCancel = () => {
@@ -34,7 +36,7 @@ const AddStorageTypeModal: React.FC<AddStorageTypeModalProps> = ({
};
const onFinish = async (values: FormData) => {
onOk({ ...values });
await run(() => onOk({ ...values }));
};
return (
@@ -48,6 +50,7 @@ const AddStorageTypeModal: React.FC<AddStorageTypeModalProps> = ({
<ModalFooter
onOk={handleSubmit}
onCancel={handleCancel}
loading={loading}
style={{
padding: '16px 24px 8px',
display: 'flex',
@@ -61,6 +64,7 @@ const AddStorageTypeModal: React.FC<AddStorageTypeModalProps> = ({
action={action}
currentData={data}
onFinish={onFinish}
onFinishFailed={release}
open={open}
/>
</FormDrawer>
@@ -18,6 +18,7 @@ interface StorageTypeFormProps {
action: PageActionType;
currentData?: ListItem | null;
onFinish: (values: FormData) => Promise<void>;
onFinishFailed?: (errorInfo: any) => void;
}
const detectKind = (item?: ListItem | null): StorageTypeKind => {
@@ -27,7 +28,7 @@ const detectKind = (item?: ListItem | null): StorageTypeKind => {
const GPUServiceStorageTypeForm: React.FC<StorageTypeFormProps> = forwardRef(
(props, ref) => {
const { action, currentData, open, onFinish } = props;
const { action, currentData, open, onFinish, onFinishFailed } = props;
const [form] = Form.useForm<FormData>();
const kind = Form.useWatch('type', form);
@@ -115,6 +116,7 @@ const GPUServiceStorageTypeForm: React.FC<StorageTypeFormProps> = forwardRef(
name="gpuServiceStorageTypeForm"
form={form}
onFinish={handleFinish}
onFinishFailed={onFinishFailed}
preserve={false}
initialValues={{}}
>
@@ -1,4 +1,5 @@
import { PageActionType } from '@/config/types';
import useSubmitLock from '@/hooks/use-submit-lock';
import { ModalFooter } from '@gpustack/core-ui';
import { useRef } from 'react';
import FormDrawer from '../../../_components/form-drawer';
@@ -26,9 +27,10 @@ const AddModal: React.FC<AddModalProps> = ({
storageClassList
}) => {
const form = useRef<any>(null);
const { loading, guard, run, release } = useSubmitLock();
const handleSubmit = () => {
form.current?.submit();
guard(() => form.current?.submit());
};
const handleCancel = () => {
@@ -37,9 +39,11 @@ const AddModal: React.FC<AddModalProps> = ({
};
const onFinish = async (values: FormData) => {
onOk({
...values
});
await run(() =>
onOk({
...values
})
);
};
return (
@@ -53,6 +57,7 @@ const AddModal: React.FC<AddModalProps> = ({
<ModalFooter
onOk={handleSubmit}
onCancel={handleCancel}
loading={loading}
style={{
padding: '16px 24px 8px',
display: 'flex',
@@ -67,6 +72,7 @@ const AddModal: React.FC<AddModalProps> = ({
action={action}
currentData={data}
onFinish={onFinish}
onFinishFailed={release}
open={open}
/>
</FormContext.Provider>
@@ -11,11 +11,12 @@ interface StorageFormProps {
action: PageActionType;
currentData?: ListItem | null;
onFinish: (values: FormData) => Promise<void>;
onFinishFailed?: (errorInfo: any) => void;
}
const GPUServiceStorageForm: React.FC<StorageFormProps> = forwardRef(
(props, ref) => {
const { action, currentData, open, onFinish } = props;
const { action, currentData, open, onFinish, onFinishFailed } = props;
const [form] = Form.useForm<FormData>();
useEffect(() => {
@@ -51,6 +52,7 @@ const GPUServiceStorageForm: React.FC<StorageFormProps> = forwardRef(
name="gpuServiceStorageForm"
form={form}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
preserve={false}
initialValues={{}}
>
@@ -1,4 +1,5 @@
import { PageActionType } from '@/config/types';
import useSubmitLock from '@/hooks/use-submit-lock';
import { ModalFooter } from '@gpustack/core-ui';
import { useRef } from 'react';
import FormDrawer from '../../../_components/form-drawer';
@@ -23,9 +24,10 @@ const AddModal: React.FC<AddModalProps> = ({
onCancel
}) => {
const form = useRef<any>(null);
const { loading, guard, run, release } = useSubmitLock();
const handleSubmit = () => {
form.current?.submit();
guard(() => form.current?.submit());
};
const handleCancel = () => {
@@ -34,9 +36,11 @@ const AddModal: React.FC<AddModalProps> = ({
};
const onFinish = async (values: FormData) => {
onOk({
...values
});
await run(() =>
onOk({
...values
})
);
};
return (
@@ -50,6 +54,7 @@ const AddModal: React.FC<AddModalProps> = ({
<ModalFooter
onOk={handleSubmit}
onCancel={handleCancel}
loading={loading}
style={{
padding: '16px 24px 8px',
display: 'flex',
@@ -63,6 +68,7 @@ const AddModal: React.FC<AddModalProps> = ({
action={action}
currentData={currentData}
onFinish={onFinish}
onFinishFailed={release}
open={open}
/>
</FormDrawer>
@@ -12,11 +12,12 @@ interface TemplateFormProps {
action: PageActionType;
currentData?: ListItem | null;
onFinish: (values: FormData) => Promise<void>;
onFinishFailed?: (errorInfo: any) => void;
}
const GPUServiceTemplateForm: React.FC<TemplateFormProps> = forwardRef(
(props, ref) => {
const { action, currentData, open, onFinish } = props;
const { action, currentData, open, onFinish, onFinishFailed } = props;
const [form] = Form.useForm<FormData>();
useEffect(() => {
@@ -58,6 +59,7 @@ const GPUServiceTemplateForm: React.FC<TemplateFormProps> = forwardRef(
name="gpuServiceTemplateForm"
form={form}
onFinish={handleFinish}
onFinishFailed={onFinishFailed}
preserve={false}
scrollToFirstError
initialValues={{