77 lines
2.0 KiB
TypeScript
77 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { Alert, Form, Input, Modal } from "antd";
|
|
import { useState } from "react";
|
|
|
|
type ReasonActionModalProps = {
|
|
open: boolean;
|
|
title: string;
|
|
okText: string;
|
|
danger?: boolean;
|
|
confirmLoading?: boolean;
|
|
onCancel: () => void;
|
|
onSubmit: (reason: string) => Promise<void> | void;
|
|
};
|
|
|
|
type ReasonFormValues = {
|
|
reason: string;
|
|
};
|
|
|
|
export function ReasonActionModal({
|
|
open,
|
|
title,
|
|
okText,
|
|
danger = false,
|
|
confirmLoading = false,
|
|
onCancel,
|
|
onSubmit,
|
|
}: ReasonActionModalProps) {
|
|
const [form] = Form.useForm<ReasonFormValues>();
|
|
const [actionLoading, setActionLoading] = useState(false);
|
|
const [actionError, setActionError] = useState<string>();
|
|
|
|
async function submit(values: ReasonFormValues) {
|
|
setActionError(undefined);
|
|
setActionLoading(true);
|
|
try {
|
|
await onSubmit(values.reason.trim());
|
|
} catch (error) {
|
|
setActionError(error instanceof Error ? error.message : "操作失败,请稍后再试");
|
|
} finally {
|
|
setActionLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
open={open}
|
|
title={title}
|
|
okText={okText}
|
|
cancelText="取消"
|
|
okButtonProps={{ danger }}
|
|
confirmLoading={confirmLoading || actionLoading}
|
|
onCancel={onCancel}
|
|
onOk={() => form.submit()}
|
|
afterOpenChange={(visible) => {
|
|
if (visible) form.resetFields();
|
|
setActionError(undefined);
|
|
}}
|
|
destroyOnHidden
|
|
>
|
|
<Form form={form} layout="vertical" preserve={false} onFinish={submit}>
|
|
<Form.Item
|
|
label="操作原因"
|
|
name="reason"
|
|
rules={[
|
|
{ required: true, whitespace: true, message: "请输入操作原因" },
|
|
{ max: 500, message: "操作原因最多 500 字" },
|
|
]}
|
|
>
|
|
<Input.TextArea rows={3} maxLength={500} showCount autoFocus />
|
|
</Form.Item>
|
|
{actionError ? <Alert type="error" showIcon message={actionError} /> : null}
|
|
</Form>
|
|
</Modal>
|
|
);
|
|
}
|