LoginForm.tsx
2.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { useEffect } from 'react';
import { Form, Input, Select, Button, Alert } from 'antd';
import type { LoginReq } from '../../api/auth';
import { COMPANY_OPTIONS } from './loginConstants';
export interface LoginFormFieldErrors {
username?: string;
password?: string;
companyCode?: string;
}
interface Props {
onSubmit: (req: LoginReq) => Promise<void>;
loading: boolean;
errorMessage: string | null;
fieldErrors: LoginFormFieldErrors;
/** 锁定状态下 submit 强制 disabled(无视 loading) */
submitDisabled?: boolean;
}
export default function LoginForm({
onSubmit,
loading,
errorMessage,
fieldErrors,
submitDisabled = false,
}: Props) {
const [form] = Form.useForm<LoginReq>();
useEffect(() => {
// 字段级错误同步到 AntD Form 实例
const errs: Array<{ name: keyof LoginFormFieldErrors; errors: string[] }> = [];
(['username', 'password', 'companyCode'] as const).forEach((k) => {
if (fieldErrors[k]) errs.push({ name: k, errors: [fieldErrors[k]!] });
});
if (errs.length > 0) {
form.setFields(errs as any);
}
}, [fieldErrors, form]);
const handleFinish = async (values: LoginReq) => {
await onSubmit(values);
};
return (
<Form
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={{ companyCode: 'HQ' }}
data-testid="login-form"
>
{errorMessage && (
<Alert
type="error"
message={errorMessage}
showIcon
style={{ marginBottom: 16 }}
data-testid="login-error-alert"
/>
)}
<Form.Item
label="用户名"
name="username"
rules={[{ required: true, message: '请输入用户名' }]}
>
<Input
placeholder="请输入你的用户名"
disabled={loading}
autoComplete="username"
/>
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input.Password
placeholder="请输入你的密码"
disabled={loading}
autoComplete="current-password"
/>
</Form.Item>
<Form.Item
label="公司"
name="companyCode"
rules={[{ required: true, message: '请选择公司' }]}
>
<Select
options={COMPANY_OPTIONS}
disabled={loading}
data-testid="company-select"
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
block
loading={loading}
disabled={submitDisabled}
data-testid="login-submit"
>
{loading ? '登录中...' : '登 录'}
</Button>
</Form.Item>
</Form>
);
}