mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-09 22:19:51 +00:00
143 lines
3.3 KiB
TypeScript
143 lines
3.3 KiB
TypeScript
import { useControllableValue } from "ahooks";
|
|
import { Form, type FormProps, Modal, type ModalProps } from "antd";
|
|
|
|
import { useAntdForm, useTriggerElement } from "@/hooks";
|
|
|
|
export interface ModalFormProps<T extends NonNullable<unknown> = any> extends Omit<FormProps<T>, "title" | "onFinish"> {
|
|
className?: string;
|
|
style?: React.CSSProperties;
|
|
children?: React.ReactNode;
|
|
cancelButtonProps?: ModalProps["cancelButtonProps"];
|
|
cancelText?: ModalProps["cancelText"];
|
|
defaultOpen?: boolean;
|
|
modalProps?: Omit<
|
|
ModalProps,
|
|
| "cancelButtonProps"
|
|
| "cancelText"
|
|
| "confirmLoading"
|
|
| "defaultOpen"
|
|
| "forceRender"
|
|
| "okButtonProps"
|
|
| "okText"
|
|
| "okType"
|
|
| "open"
|
|
| "title"
|
|
| "width"
|
|
| "onCancel"
|
|
| "onOk"
|
|
| "onOpenChange"
|
|
>;
|
|
okButtonProps?: ModalProps["okButtonProps"];
|
|
okText?: ModalProps["okText"];
|
|
open?: boolean;
|
|
title?: ModalProps["title"];
|
|
trigger?: React.ReactNode;
|
|
width?: ModalProps["width"];
|
|
onClose?: (e: React.MouseEvent | React.KeyboardEvent) => void | Promise<unknown>;
|
|
onFinish?: (values: T) => unknown | Promise<unknown>;
|
|
onOpenChange?: (open: boolean) => void;
|
|
}
|
|
|
|
const ModalForm = <T extends NonNullable<unknown> = any>({
|
|
className,
|
|
style,
|
|
children,
|
|
cancelButtonProps,
|
|
cancelText,
|
|
form,
|
|
modalProps,
|
|
okButtonProps,
|
|
okText,
|
|
title,
|
|
trigger,
|
|
width,
|
|
onFinish,
|
|
...props
|
|
}: ModalFormProps<T>) => {
|
|
const [open, setOpen] = useControllableValue<boolean>(props, {
|
|
valuePropName: "open",
|
|
defaultValuePropName: "defaultOpen",
|
|
trigger: "onOpenChange",
|
|
});
|
|
|
|
const triggerEl = useTriggerElement(trigger, { onClick: () => setOpen(true) });
|
|
|
|
const {
|
|
form: formInst,
|
|
formPending,
|
|
formProps,
|
|
submit: submitForm,
|
|
} = useAntdForm({
|
|
form,
|
|
onSubmit: (values) => {
|
|
return onFinish?.(values);
|
|
},
|
|
});
|
|
|
|
const mergedFormProps: FormProps = {
|
|
clearOnDestroy: modalProps?.destroyOnClose ? true : undefined,
|
|
...formProps,
|
|
...props,
|
|
};
|
|
|
|
const mergedModalProps: ModalProps = {
|
|
...modalProps,
|
|
afterClose: () => {
|
|
if (!mergedFormProps.preserve) {
|
|
formInst.resetFields();
|
|
}
|
|
|
|
modalProps?.afterClose?.();
|
|
},
|
|
onClose: async (e) => {
|
|
if (formPending) return;
|
|
|
|
// 关闭 Modal 时 Promise.reject 阻止关闭
|
|
await modalProps?.onClose?.(e as React.MouseEvent | React.KeyboardEvent);
|
|
setOpen(false);
|
|
},
|
|
};
|
|
|
|
const handleOkClick = async () => {
|
|
// 提交表单返回 Promise.reject 时不关闭 Modal
|
|
await submitForm();
|
|
setOpen(false);
|
|
};
|
|
|
|
const handleCancelClick = () => {
|
|
if (formPending) return;
|
|
|
|
setOpen(false);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{triggerEl}
|
|
|
|
<Modal
|
|
{...mergedModalProps}
|
|
cancelButtonProps={cancelButtonProps}
|
|
cancelText={cancelText}
|
|
confirmLoading={formPending}
|
|
forceRender
|
|
okButtonProps={okButtonProps}
|
|
okText={okText}
|
|
okType="primary"
|
|
open={open}
|
|
title={title}
|
|
width={width}
|
|
onOk={handleOkClick}
|
|
onCancel={handleCancelClick}
|
|
>
|
|
<div className="pb-2 pt-4">
|
|
<Form className={className} style={style} {...mergedFormProps} form={formInst}>
|
|
{children}
|
|
</Form>
|
|
</div>
|
|
</Modal>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ModalForm;
|