feat: 10.10线上最新代码提交

This commit is contained in:
zdl
2025-10-11 16:16:02 +08:00
parent 4d0dc109bc
commit 495ad758ea
3338 changed files with 460147 additions and 152745 deletions

0
src/views/Authentication/Lock/LockBasic.js Normal file → Executable file
View File

0
src/views/Authentication/Lock/LockCover.js Normal file → Executable file
View File

0
src/views/Authentication/Lock/LockIllustration.js Normal file → Executable file
View File

0
src/views/Authentication/Reset/ResetBasic.js Normal file → Executable file
View File

0
src/views/Authentication/Reset/ResetCover.js Normal file → Executable file
View File

0
src/views/Authentication/Reset/ResetIllustration.js Normal file → Executable file
View File

2
src/views/Authentication/SignIn/SignInBasic.js Normal file → Executable file
View File

@@ -83,7 +83,7 @@ export default function SignInBasic() {
<Flex minH="100vh" align="center" justify="center" bg={useColorModeValue("gray.50", "gray.900")}>
<Stack spacing={8} mx="auto" maxW="lg" py={12} px={6}>
<Stack align="center">
<Heading fontSize="4xl" color="blue.600">
<Heading style={{minWidth: '140px'}} fontSize="4xl" color="blue.600">
价小前投研
</Heading>
<Text fontSize="lg" color="gray.600">

2
src/views/Authentication/SignIn/SignInCentered.js Normal file → Executable file
View File

@@ -105,7 +105,7 @@ export default function SignInCentered() {
>
<VStack spacing={6}>
<Box textAlign="center">
<Heading size="lg" mb={2}>欢迎回来</Heading>
<Heading size="lg" mb={2}>欢迎回来1</Heading>
<Text color="gray.500">请输入您的凭据登录</Text>
</Box>

0
src/views/Authentication/SignIn/SignInCover.js Normal file → Executable file
View File

1157
src/views/Authentication/SignIn/SignInIllustration.js Normal file → Executable file

File diff suppressed because it is too large Load Diff

0
src/views/Authentication/SignUp/SignUpBasic.js Normal file → Executable file
View File

0
src/views/Authentication/SignUp/SignUpCentered.js Normal file → Executable file
View File

0
src/views/Authentication/SignUp/SignUpCover.js Normal file → Executable file
View File

350
src/views/Authentication/SignUp/SignUpIllustration.js Normal file → Executable file
View File

@@ -25,19 +25,23 @@ import {
Spinner,
FormLabel,
FormErrorMessage,
Divider
Divider,
useDisclosure,
Checkbox
} from "@chakra-ui/react";
import { ViewIcon, ViewOffIcon } from "@chakra-ui/icons";
import { FaWeixin, FaMobile, FaEnvelope, FaUser, FaLock } from "react-icons/fa";
import { useNavigate, Link } from "react-router-dom";
import axios from "axios";
import { useAuth } from '../../../contexts/AuthContext'; // 假设AuthContext在这个路径
import PrivacyPolicyModal from '../../../components/PrivacyPolicyModal';
import UserAgreementModal from '../../../components/UserAgreementModal';
const isProduction = process.env.NODE_ENV === 'production';
const API_BASE_URL = isProduction ? "" : process.env.REACT_APP_API_URL;
export default function SignUpPage() {
const [registerType, setRegisterType] = useState(0); // 0: 微信, 1: 手机号, 2: 邮箱
const [registerType, setRegisterType] = useState(0); // 0: 微信, 1: 手机号
const [showPassword, setShowPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [countdown, setCountdown] = useState(0);
@@ -46,6 +50,7 @@ export default function SignUpPage() {
const [wechatStatus, setWechatStatus] = useState("waiting");
const [errors, setErrors] = useState({});
const [checkInterval, setCheckInterval] = useState(null);
const [agreeToTerms, setAgreeToTerms] = useState(false);
const [formData, setFormData] = useState({
username: "",
@@ -56,6 +61,20 @@ export default function SignUpPage() {
verificationCode: ""
});
// 隐私政策弹窗状态
const {
isOpen: isPrivacyModalOpen,
onOpen: onPrivacyModalOpen,
onClose: onPrivacyModalClose
} = useDisclosure();
// 用户协议弹窗状态
const {
isOpen: isUserAgreementModalOpen,
onOpen: onUserAgreementModalOpen,
onClose: onUserAgreementModalClose
} = useDisclosure();
const navigate = useNavigate();
const toast = useToast();
const { loginWithWechat } = useAuth(); // 使用认证上下文
@@ -316,28 +335,27 @@ export default function SignUpPage() {
// 初始化时如果选择了微信登录获取授权URL
useEffect(() => {
if (registerType === 0) {
if (registerType === 0 && agreeToTerms) {
getWechatQRCode();
}
}, []);
}, [registerType, agreeToTerms]);
// 发送验证码
const sendVerificationCode = async () => {
const isPhone = registerType === 1;
const contact = isPhone ? formData.phone : formData.email;
const endpoint = isPhone ? "send-sms-code" : "send-email-code";
const fieldName = isPhone ? "phone" : "email";
const contact = formData.phone;
const endpoint = "send-sms-code";
const fieldName = "phone";
if (!contact) {
toast({
title: `请输入${isPhone ? "手机号" : "邮箱"}`,
title: "请输入手机号",
status: "warning",
duration: 2000,
});
return;
}
if (isPhone && !/^1[3-9]\d{9}$/.test(contact)) {
if (!/^1[3-9]\d{9}$/.test(contact)) {
toast({
title: "请输入正确的手机号",
status: "warning",
@@ -346,15 +364,6 @@ export default function SignUpPage() {
return;
}
if (!isPhone && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(contact)) {
toast({
title: "请输入正确的邮箱格式",
status: "warning",
duration: 2000,
});
return;
}
try {
setIsLoading(true);
await axios.post(`${API_BASE_URL}/api/auth/${endpoint}`, {
@@ -363,7 +372,7 @@ export default function SignUpPage() {
toast({
title: "验证码已发送",
description: `请查收${isPhone ? "短信" : "邮件"}`,
description: "请查收短信",
status: "success",
duration: 3000,
});
@@ -415,20 +424,12 @@ export default function SignUpPage() {
} else if (!/^1[3-9]\d{9}$/.test(formData.phone)) {
newErrors.phone = "请输入正确的手机号";
}
}
if (registerType === 2) {
if (!formData.email) {
newErrors.email = "请输入邮箱";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = "请输入正确的邮箱格式";
if (!formData.verificationCode) {
newErrors.verificationCode = "请输入验证码";
}
}
if ((registerType === 1 || registerType === 2) && !formData.verificationCode) {
newErrors.verificationCode = "请输入验证码";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
@@ -437,6 +438,17 @@ export default function SignUpPage() {
const handleSubmit = async (e) => {
e.preventDefault();
if (!agreeToTerms) {
toast({
title: "请先同意协议",
description: "请勾选同意用户协议和隐私政策后再注册",
status: "warning",
duration: 3000,
isClosable: true,
});
return;
}
if (!validateForm()) {
return;
}
@@ -444,25 +456,13 @@ export default function SignUpPage() {
setIsLoading(true);
try {
let endpoint, data;
if (registerType === 1) {
endpoint = "/api/auth/register/phone";
data = {
phone: formData.phone,
code: formData.verificationCode,
username: formData.username,
password: formData.password
};
} else {
endpoint = "/api/auth/register/email";
data = {
email: formData.email,
code: formData.verificationCode,
username: formData.username,
password: formData.password
};
}
const endpoint = "/api/auth/register/phone";
const data = {
phone: formData.phone,
code: formData.verificationCode,
username: formData.username,
password: formData.password
};
await axios.post(`${API_BASE_URL}${endpoint}`, data);
@@ -505,6 +505,7 @@ export default function SignUpPage() {
setRegisterType(newType);
setErrors({});
setAgreeToTerms(false); // 切换注册方式时重置协议同意状态
setFormData({
username: "",
email: "",
@@ -518,7 +519,7 @@ export default function SignUpPage() {
setWechatStatus("waiting");
setWechatAuthUrl("");
setWechatSessionId("");
getWechatQRCode();
// 不自动获取二维码,等用户同意协议后再获取
}
};
@@ -627,9 +628,6 @@ export default function SignUpPage() {
<Tab borderRadius="lg" _selected={{ bg: "linear-gradient(135deg, #ff8c00 0%, #ff6347 100%)", color: "white", transform: "scale(1.02)" }} transition="all 0.2s">
<Icon as={FaMobile} mr={2} /><Text fontSize="sm">手机号</Text>
</Tab>
<Tab borderRadius="lg" _selected={{ bg: "linear-gradient(135deg, #ff8c00 0%, #ff6347 100%)", color: "white", transform: "scale(1.02)" }} transition="all 0.2s">
<Icon as={FaEnvelope} mr={2} /><Text fontSize="sm">邮箱</Text>
</Tab>
</TabList>
</Tabs>
</Box>
@@ -640,49 +638,112 @@ export default function SignUpPage() {
{/* 微信注册 */}
{registerType === 0 && (
<>
{/* 协议同意勾选框 - 微信注册 */}
<Box width="100%" mb={4}>
<Checkbox
isChecked={agreeToTerms}
onChange={(e) => setAgreeToTerms(e.target.checked)}
colorScheme="orange"
size="sm"
>
<Text fontSize="sm" color="gray.600">
我已阅读并同意{" "}
<ChakraLink
color="orange.500"
fontSize="sm"
onClick={onUserAgreementModalOpen}
textDecoration="underline"
_hover={{ color: "orange.600" }}
>
用户协议
</ChakraLink>
{" "}{" "}
<ChakraLink
color="orange.500"
fontSize="sm"
onClick={onPrivacyModalOpen}
textDecoration="underline"
_hover={{ color: "orange.600" }}
>
隐私政策
</ChakraLink>
</Text>
</Checkbox>
{!agreeToTerms && (
<Text fontSize="xs" color="red.500" mt={1} ml={6}>
请先同意用户协议和隐私政策
</Text>
)}
</Box>
<Center width="100%" height="420px" bg="gray.50" borderRadius="lg" p={4} mb={4}>
{wechatAuthUrl && wechatStatus !== "expired" ? (
<Box position="relative" width="100%" height="100%">
<iframe
src={wechatAuthUrl}
width="100%"
height="100%"
frameBorder="0"
scrolling="no"
style={{ borderRadius: '8px' }}
/>
{(wechatStatus === "login_success" || wechatStatus === "register_success") && (
<Box position="absolute" top={0} left={0} right={0} bottom={0}
bg="rgba(0,0,0,0.7)" display="flex" alignItems="center"
justifyContent="center" borderRadius="lg">
<VStack>
<Spinner color="white" />
<Text color="white" fontWeight="bold">
{wechatStatus === "login_success" ? "正在登录..." : "正在创建账号..."}
</Text>
</VStack>
</Box>
)}
</Box>
) : wechatStatus === "expired" ? (
<VStack>
<Text color="gray.500" fontWeight="bold" mb={4}>授权已过期</Text>
<Button colorScheme="orange" size="sm" onClick={getWechatQRCode}
isLoading={isLoading}>重新获取</Button>
</VStack>
{agreeToTerms ? (
wechatAuthUrl && wechatStatus !== "expired" ? (
<Box position="relative" width="100%" height="100%">
<iframe
src={wechatAuthUrl}
width="100%"
height="100%"
frameBorder="0"
scrolling="no"
style={{ borderRadius: '8px' }}
/>
{(wechatStatus === "login_success" || wechatStatus === "register_success") && (
<Box position="absolute" top={0} left={0} right={0} bottom={0}
bg="rgba(0,0,0,0.7)" display="flex" alignItems="center"
justifyContent="center" borderRadius="lg">
<VStack>
<Spinner color="white" />
<Text color="white" fontWeight="bold">
{wechatStatus === "login_success" ? "正在登录..." : "正在创建账号..."}
</Text>
</VStack>
</Box>
)}
</Box>
) : wechatStatus === "expired" ? (
<VStack>
<Text color="gray.500" fontWeight="bold" mb={4}>授权已过期</Text>
<Button colorScheme="orange" size="sm" onClick={getWechatQRCode}
isLoading={isLoading}>重新获取</Button>
</VStack>
) : (
<VStack>
<Spinner size="xl" color="orange.500" />
<Text color="gray.500" fontSize="sm">加载中...</Text>
</VStack>
)
) : (
<VStack>
<Spinner size="xl" color="orange.500" />
<Text color="gray.500" fontSize="sm">加载中...</Text>
<VStack spacing={4}>
<Icon as={FaWeixin} w={20} h={20} color="gray.400" />
<VStack spacing={2}>
<Text fontSize="lg" fontWeight="bold" color="gray.400">
微信扫码注册
</Text>
<Text fontSize="sm" color="gray.400" textAlign="center">
请先同意用户协议和隐私政策
</Text>
</VStack>
<Button
colorScheme="orange"
variant="outline"
size="lg"
isDisabled
opacity={0.6}
>
同意协议后显示二维码
</Button>
</VStack>
)}
</Center>
<Text textAlign="center" color={getWechatStatusColor()} fontSize="sm"
fontWeight={wechatStatus === "login_success" || wechatStatus === "register_success" ? "bold" : "normal"}>
{getWechatStatusText()}
{agreeToTerms ? getWechatStatusText() : "请先同意用户协议和隐私政策"}
</Text>
<Text fontSize="xs" color="gray.500" textAlign="center">
扫码即表示同意创建账号系统将使用您的微信昵称作为初始用户名
{agreeToTerms
? "扫码即表示同意创建账号,系统将使用您的微信昵称作为初始用户名"
: "同意协议后即可使用微信快速注册"}
</Text>
</>
)}
@@ -710,48 +771,71 @@ export default function SignUpPage() {
</FormControl>
<Divider my={2} />
{commonAuthFields}
{/* 协议同意勾选框 - 手机号注册 */}
<Box width="100%" py={3}>
<Checkbox
isChecked={agreeToTerms}
onChange={(e) => setAgreeToTerms(e.target.checked)}
colorScheme="orange"
size="sm"
>
<Text fontSize="sm" color="gray.600">
我已阅读并同意{" "}
<ChakraLink
color="orange.500"
fontSize="sm"
onClick={onUserAgreementModalOpen}
textDecoration="underline"
_hover={{ color: "orange.600" }}
>
用户协议
</ChakraLink>
{" "}{" "}
<ChakraLink
color="orange.500"
fontSize="sm"
onClick={onPrivacyModalOpen}
textDecoration="underline"
_hover={{ color: "orange.600" }}
>
隐私政策
</ChakraLink>
</Text>
</Checkbox>
{!agreeToTerms && (
<Text fontSize="xs" color="red.500" mt={1} ml={6}>
请先同意用户协议和隐私政策
</Text>
)}
</Box>
</>
)}
{/* 邮箱注册 */}
{registerType === 2 && (
<>
<FormControl isRequired isInvalid={!!errors.email}>
<FormLabel fontSize="sm">邮箱</FormLabel>
<InputGroup>
<InputRightElement pointerEvents="none"><Icon as={FaEnvelope} color="gray.400" /></InputRightElement>
<Input name="email" type="email" value={formData.email} onChange={handleInputChange} placeholder="请输入邮箱地址" pr="2.5rem" />
</InputGroup>
<FormErrorMessage>{errors.email}</FormErrorMessage>
</FormControl>
<FormControl isRequired isInvalid={!!errors.verificationCode}>
<FormLabel fontSize="sm">验证码</FormLabel>
<HStack>
<Input name="verificationCode" value={formData.verificationCode} onChange={handleInputChange} placeholder="请输入6位验证码" />
<Button colorScheme="orange" onClick={sendVerificationCode} isDisabled={countdown > 0 || isLoading} isLoading={isLoading && countdown === 0} minW="120px">
{countdown > 0 ? `${countdown}秒后重试` : "获取验证码"}
</Button>
</HStack>
<FormErrorMessage>{errors.verificationCode}</FormErrorMessage>
</FormControl>
<Divider my={2} />
{commonAuthFields}
</>
)}
{registerType !== 0 && (
<Button
type="submit"
width="100%"
size="lg"
background="linear-gradient(135deg, #ff8c00 0%, #ff6347 100%)"
background={agreeToTerms
? "linear-gradient(135deg, #ff8c00 0%, #ff6347 100%)"
: "gray.300"
}
color="white"
borderRadius="lg"
_hover={{ transform: "translateY(-2px)", boxShadow: "lg" }}
_active={{ transform: "translateY(0)" }}
_hover={agreeToTerms ? {
transform: "translateY(-2px)",
boxShadow: "lg"
} : {}}
_active={agreeToTerms ? {
transform: "translateY(0)"
} : {}}
isLoading={isLoading}
loadingText="注册中..."
fontWeight="bold"
isDisabled={!agreeToTerms}
cursor={agreeToTerms ? "pointer" : "not-allowed"}
>
完成注册
</Button>
@@ -770,14 +854,38 @@ export default function SignUpPage() {
<Box position="absolute" bottom={8} left="50%" transform="translateX(-50%)" zIndex={1}>
<HStack spacing={6}>
<ChakraLink href="#" color="white" fontSize="sm" opacity={0.8} _hover={{ opacity: 1 }}>Company</ChakraLink>
<ChakraLink href="#" color="white" fontSize="sm" opacity={0.8} _hover={{ opacity: 1 }}>About Us</ChakraLink>
<ChakraLink href="#" color="white" fontSize="sm" opacity={0.8} _hover={{ opacity: 1 }}>Team</ChakraLink>
<ChakraLink href="#" color="white" fontSize="sm" opacity={0.8} _hover={{ opacity: 1 }}>Products</ChakraLink>
<ChakraLink href="#" color="white" fontSize="sm" opacity={0.8} _hover={{ opacity: 1 }}>Blog</ChakraLink>
<ChakraLink href="#" color="white" fontSize="sm" opacity={0.8} _hover={{ opacity: 1 }}>Pricing</ChakraLink>
<ChakraLink
color="white"
fontSize="sm"
opacity={0.8}
_hover={{ opacity: 1 }}
onClick={onUserAgreementModalOpen}
>
用户协议
</ChakraLink>
<ChakraLink
color="white"
fontSize="sm"
opacity={0.8}
_hover={{ opacity: 1 }}
onClick={onPrivacyModalOpen}
>
隐私政策
</ChakraLink>
</HStack>
</Box>
{/* 隐私政策弹窗 */}
<PrivacyPolicyModal
isOpen={isPrivacyModalOpen}
onClose={onPrivacyModalClose}
/>
{/* 用户协议弹窗 */}
<UserAgreementModal
isOpen={isUserAgreementModalOpen}
onClose={onUserAgreementModalClose}
/>
</Flex>
);
}

View File

View File

View File