Files
vf_react/src/views/Authentication/SignIn/SignInCentered.js
2025-10-11 16:16:02 +08:00

207 lines
5.7 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}