143 lines
4.7 KiB
TypeScript
143 lines
4.7 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { Form, Input, Button, Card, Space, Typography, Alert } from 'antd';
|
||
import { UserOutlined, LockOutlined, CheckSquareOutlined, MailOutlined } from '@ant-design/icons';
|
||
import { api } from '../api/axios';
|
||
|
||
const { Title, Text, Link } = Typography;
|
||
|
||
function Register() {
|
||
const [loading, setLoading] = useState(false);
|
||
const [error, setError] = useState<string | null>(null);
|
||
const navigate = useNavigate();
|
||
|
||
const onFinish = async (values: any) => {
|
||
setLoading(true);
|
||
setError(null);
|
||
try {
|
||
await api.post('/auth/register', {
|
||
username: values.username,
|
||
email: values.email,
|
||
password: values.password,
|
||
});
|
||
navigate('/login');
|
||
} catch (err: any) {
|
||
const errorMessage = err.response?.data?.message || '注册失败,用户名可能已被占用或服务器错误。';
|
||
setError(errorMessage);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="flex items-center justify-center min-h-screen bg-gray-100" style={{ background: '#f0f2f5',height: '100vh',display: 'flex',alignItems: 'center',justifyContent: 'center' }}>
|
||
<Card
|
||
className="w-full max-w-sm"
|
||
variant="borderless"
|
||
style={{
|
||
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
|
||
padding: '40px 24px 24px',
|
||
textAlign: 'center',
|
||
width: '500px',
|
||
margin: '0 auto 200px',
|
||
}}
|
||
>
|
||
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 24 }}>
|
||
<CheckSquareOutlined style={{ fontSize: '48px', color: '#1890ff' }} />
|
||
<Title level={2} style={{ margin: '8px 0 0' }}>Todo List</Title>
|
||
<Text type="secondary">轻松管理您的任务</Text>
|
||
</div>
|
||
|
||
{error && (
|
||
<Alert
|
||
message="注册失败"
|
||
description={error}
|
||
type="error"
|
||
showIcon
|
||
closable
|
||
onClose={() => setError(null)}
|
||
style={{ marginBottom: 24 }}
|
||
/>
|
||
)}
|
||
|
||
<Form
|
||
name="register"
|
||
initialValues={{ remember: true }}
|
||
onFinish={onFinish}
|
||
autoComplete="off"
|
||
layout="vertical"
|
||
style={{ width: '100%' }}
|
||
>
|
||
<Form.Item
|
||
name="username"
|
||
rules={[{ required: true, message: '请输入用户名!' }]}
|
||
>
|
||
<Input
|
||
prefix={<UserOutlined className="site-form-item-icon" />}
|
||
placeholder="用户名"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
name="email"
|
||
rules={[{ required: true, message: '请输入邮箱!' }, { type: 'email', message: '请输入有效的邮箱地址!' }]}
|
||
>
|
||
<Input
|
||
prefix={<MailOutlined className="site-form-item-icon" />}
|
||
placeholder="邮箱"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
name="password"
|
||
rules={[{ required: true, message: '请输入密码!' }]}
|
||
>
|
||
<Input.Password
|
||
prefix={<LockOutlined className="site-form-item-icon" />}
|
||
placeholder="密码"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
name="confirm"
|
||
dependencies={['password']}
|
||
hasFeedback
|
||
rules={[
|
||
{ required: true, message: '请确认您的密码!' },
|
||
({ getFieldValue }) => ({
|
||
validator(_, value) {
|
||
if (!value || getFieldValue('password') === value) {
|
||
return Promise.resolve();
|
||
}
|
||
return Promise.reject(new Error('两次输入的密码不一致!'));
|
||
},
|
||
}),
|
||
]}
|
||
>
|
||
<Input.Password
|
||
prefix={<LockOutlined className="site-form-item-icon" />}
|
||
placeholder="确认密码"
|
||
size="large"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item>
|
||
<Button type="primary" htmlType="submit" className="w-full" size="large" style={{width: '100%',}} loading={loading}>
|
||
注册
|
||
</Button>
|
||
</Form.Item>
|
||
<div style={{ textAlign: 'right' }}>
|
||
<Link href="/login">已有账号?去登录</Link>
|
||
</div>
|
||
</Form>
|
||
</Space>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default Register;
|