207 lines
5.7 KiB
JavaScript
Executable File
207 lines
5.7 KiB
JavaScript
Executable File
import React, { useState } from "react";
|
||
import {
|
||
Box,
|
||
Button,
|
||
FormControl,
|
||
FormLabel,
|
||
Input,
|
||
VStack,
|
||
Heading,
|
||
Text,
|
||
Link,
|
||
useColorMode,
|
||
InputGroup,
|
||
InputRightElement,
|
||
IconButton,
|
||
Spinner,
|
||
} from "@chakra-ui/react";
|
||
import { ViewIcon, ViewOffIcon } from "@chakra-ui/icons";
|
||
import { useNavigate } from "react-router-dom";
|
||
import { useAuth } from "../../../contexts/AuthContext";
|
||
|
||
export default function SignInCentered() {
|
||
const { colorMode } = useColorMode();
|
||
const navigate = useNavigate();
|
||
const { login, isLoading } = useAuth();
|
||
|
||
// 表单状态
|
||
const [formData, setFormData] = useState({
|
||
email: "",
|
||
password: "",
|
||
});
|
||
|
||
// UI状态
|
||
const [showPassword, setShowPassword] = useState(false);
|
||
const [errors, setErrors] = useState({});
|
||
|
||
// 处理输入变化
|
||
const handleInputChange = (e) => {
|
||
const { name, value } = e.target;
|
||
setFormData(prev => ({
|
||
...prev,
|
||
[name]: value
|
||
}));
|
||
// 清除对应字段的错误
|
||
if (errors[name]) {
|
||
setErrors(prev => ({
|
||
...prev,
|
||
[name]: ""
|
||
}));
|
||
}
|
||
};
|
||
|
||
// 表单验证
|
||
const validateForm = () => {
|
||
const newErrors = {};
|
||
|
||
if (!formData.email) {
|
||
newErrors.email = "邮箱是必填项";
|
||
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
|
||
newErrors.email = "请输入有效的邮箱地址";
|
||
}
|
||
|
||
if (!formData.password) {
|
||
newErrors.password = "密码是必填项";
|
||
} else if (formData.password.length < 6) {
|
||
newErrors.password = "密码至少需要6个字符";
|
||
}
|
||
|
||
setErrors(newErrors);
|
||
return Object.keys(newErrors).length === 0;
|
||
};
|
||
|
||
// 处理表单提交
|
||
const handleSubmit = async (e) => {
|
||
e.preventDefault();
|
||
|
||
if (!validateForm()) {
|
||
return;
|
||
}
|
||
|
||
const result = await login(formData.email, formData.password);
|
||
|
||
if (result.success) {
|
||
// 登录成功,跳转到首页
|
||
navigate("/home");
|
||
}
|
||
};
|
||
|
||
return (
|
||
<Box
|
||
minH="100vh"
|
||
display="flex"
|
||
alignItems="center"
|
||
justifyContent="center"
|
||
bg={colorMode === "dark" ? "gray.800" : "gray.50"}
|
||
p={4}
|
||
>
|
||
<Box
|
||
w="full"
|
||
maxW="md"
|
||
p={8}
|
||
bg={colorMode === "dark" ? "gray.700" : "white"}
|
||
borderRadius="lg"
|
||
shadow="xl"
|
||
>
|
||
<VStack spacing={6}>
|
||
<Box textAlign="center">
|
||
<Heading size="lg" mb={2}>欢迎回来1</Heading>
|
||
<Text color="gray.500">请输入您的凭据登录</Text>
|
||
</Box>
|
||
|
||
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
|
||
<VStack spacing={4}>
|
||
<FormControl isInvalid={!!errors.email}>
|
||
<FormLabel>邮箱地址</FormLabel>
|
||
<Input
|
||
name="email"
|
||
type="email"
|
||
placeholder="your@email.com"
|
||
value={formData.email}
|
||
onChange={handleInputChange}
|
||
size="lg"
|
||
/>
|
||
{errors.email && (
|
||
<Text color="red.500" fontSize="sm" mt={1}>
|
||
{errors.email}
|
||
</Text>
|
||
)}
|
||
</FormControl>
|
||
|
||
<FormControl isInvalid={!!errors.password}>
|
||
<FormLabel>密码</FormLabel>
|
||
<InputGroup size="lg">
|
||
<Input
|
||
name="password"
|
||
type={showPassword ? "text" : "password"}
|
||
placeholder="********"
|
||
value={formData.password}
|
||
onChange={handleInputChange}
|
||
/>
|
||
<InputRightElement>
|
||
<IconButton
|
||
aria-label={showPassword ? "隐藏密码" : "显示密码"}
|
||
icon={showPassword ? <ViewOffIcon /> : <ViewIcon />}
|
||
variant="ghost"
|
||
onClick={() => setShowPassword(!showPassword)}
|
||
/>
|
||
</InputRightElement>
|
||
</InputGroup>
|
||
{errors.password && (
|
||
<Text color="red.500" fontSize="sm" mt={1}>
|
||
{errors.password}
|
||
</Text>
|
||
)}
|
||
</FormControl>
|
||
|
||
<Button
|
||
type="submit"
|
||
colorScheme="blue"
|
||
w="full"
|
||
size="lg"
|
||
isLoading={isLoading}
|
||
loadingText="登录中..."
|
||
>
|
||
{isLoading ? <Spinner size="sm" /> : "登录"}
|
||
</Button>
|
||
</VStack>
|
||
</form>
|
||
|
||
<VStack spacing={3}>
|
||
<Text fontSize="sm" textAlign="center">
|
||
还没有账户?{" "}
|
||
<Link
|
||
color="blue.500"
|
||
onClick={() => navigate("/auth/signup")}
|
||
_hover={{ textDecoration: "underline" }}
|
||
>
|
||
立即注册
|
||
</Link>
|
||
</Text>
|
||
|
||
<Box textAlign="center">
|
||
<Link
|
||
color="gray.500"
|
||
fontSize="sm"
|
||
_hover={{ color: "blue.500" }}
|
||
>
|
||
忘记密码?
|
||
</Link>
|
||
<Text color="gray.500" fontSize="sm" mt={2}>
|
||
还没有账户?{" "}
|
||
<Link
|
||
color="blue.500"
|
||
fontWeight="medium"
|
||
_hover={{ textDecoration: "underline" }}
|
||
onClick={() => navigate('/auth/sign-up')}
|
||
>
|
||
立即注册
|
||
</Link>
|
||
</Text>
|
||
</Box>
|
||
</VStack>
|
||
</VStack>
|
||
</Box>
|
||
</Box>
|
||
);
|
||
}
|