feat: 登陆注册UI调整,用户协议和隐私政策跳转调整
This commit is contained in:
@@ -42,10 +42,12 @@ const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
|
||||
|
||||
// Contexts
|
||||
import { AuthProvider } from "contexts/AuthContext";
|
||||
import { AuthModalProvider } from "contexts/AuthModalContext";
|
||||
|
||||
// Components
|
||||
import ProtectedRoute from "components/ProtectedRoute";
|
||||
import ErrorBoundary from "components/ErrorBoundary";
|
||||
import AuthModalManager from "components/Auth/AuthModalManager";
|
||||
|
||||
function AppContent() {
|
||||
const { colorMode } = useColorMode();
|
||||
@@ -180,7 +182,10 @@ export default function App() {
|
||||
<ChakraProvider theme={theme}>
|
||||
<ErrorBoundary>
|
||||
<AuthProvider>
|
||||
<AppContent />
|
||||
<AuthModalProvider>
|
||||
<AppContent />
|
||||
<AuthModalManager />
|
||||
</AuthModalProvider>
|
||||
</AuthProvider>
|
||||
</ErrorBoundary>
|
||||
</ChakraProvider>
|
||||
|
||||
@@ -5,12 +5,17 @@ import { Link } from "react-router-dom";
|
||||
/**
|
||||
* 认证页面底部组件
|
||||
* 包含页面切换链接和登录方式切换链接
|
||||
*
|
||||
* 支持两种模式:
|
||||
* 1. 页面模式:使用 linkTo 进行路由跳转
|
||||
* 2. 弹窗模式:使用 onClick 进行弹窗切换
|
||||
*/
|
||||
export default function AuthFooter({
|
||||
// 左侧链接配置
|
||||
linkText, // 提示文本,如 "还没有账号," 或 "已有账号?"
|
||||
linkLabel, // 链接文本,如 "去注册" 或 "去登录"
|
||||
linkTo, // 链接路径,如 "/auth/sign-up" 或 "/auth/sign-in"
|
||||
linkTo, // 链接路径,如 "/auth/sign-up" 或 "/auth/sign-in"(页面模式)
|
||||
onClick, // 点击回调函数(弹窗模式,优先级高于 linkTo)
|
||||
|
||||
// 右侧切换配置
|
||||
useVerificationCode, // 当前是否使用验证码登录
|
||||
@@ -19,24 +24,35 @@ export default function AuthFooter({
|
||||
return (
|
||||
<HStack justify="space-between" width="100%">
|
||||
{/* 左侧:页面切换链接(去注册/去登录) */}
|
||||
<HStack spacing={1} as={Link} to={linkTo}>
|
||||
<Text fontSize="sm" color="gray.600">{linkText}</Text>
|
||||
<Text fontSize="sm" color="blue.500" fontWeight="bold">{linkLabel}</Text>
|
||||
</HStack>
|
||||
{onClick ? (
|
||||
// 弹窗模式:使用 onClick
|
||||
<HStack spacing={1} cursor="pointer" onClick={onClick}>
|
||||
<Text fontSize="sm" color="gray.600">{linkText}</Text>
|
||||
<Text fontSize="sm" color="blue.500" fontWeight="bold">{linkLabel}</Text>
|
||||
</HStack>
|
||||
) : (
|
||||
// 页面模式:使用 Link 组件跳转
|
||||
<HStack spacing={1} as={Link} to={linkTo}>
|
||||
<Text fontSize="sm" color="gray.600">{linkText}</Text>
|
||||
<Text fontSize="sm" color="blue.500" fontWeight="bold">{linkLabel}</Text>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{/* 右侧:登录方式切换链接 */}
|
||||
<ChakraLink
|
||||
href="#"
|
||||
fontSize="sm"
|
||||
color="blue.500"
|
||||
fontWeight="bold"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onSwitchMethod();
|
||||
}}
|
||||
>
|
||||
{useVerificationCode ? '密码登陆' : '验证码登陆'}
|
||||
</ChakraLink>
|
||||
{/* 右侧:登录方式切换链接(仅在提供了切换方法时显示) */}
|
||||
{onSwitchMethod && (
|
||||
<ChakraLink
|
||||
href="#"
|
||||
fontSize="sm"
|
||||
color="blue.500"
|
||||
fontWeight="bold"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onSwitchMethod();
|
||||
}}
|
||||
>
|
||||
{useVerificationCode ? '密码登陆' : '验证码登陆'}
|
||||
</ChakraLink>
|
||||
)}
|
||||
</HStack>
|
||||
);
|
||||
}
|
||||
|
||||
379
src/components/Auth/AuthFormContent.js
Normal file
379
src/components/Auth/AuthFormContent.js
Normal file
@@ -0,0 +1,379 @@
|
||||
// src/components/Auth/AuthFormContent.js
|
||||
// 统一的认证表单组件
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
Heading,
|
||||
VStack,
|
||||
HStack,
|
||||
useToast,
|
||||
Icon,
|
||||
FormErrorMessage,
|
||||
Center,
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogContent,
|
||||
AlertDialogOverlay,
|
||||
Text,
|
||||
Link as ChakraLink,
|
||||
} from "@chakra-ui/react";
|
||||
import { FaLock } from "react-icons/fa";
|
||||
import { useAuth } from "../../contexts/AuthContext";
|
||||
import { useAuthModal } from "../../contexts/AuthModalContext";
|
||||
import AuthHeader from './AuthHeader';
|
||||
import VerificationCodeInput from './VerificationCodeInput';
|
||||
import WechatRegister from './WechatRegister';
|
||||
|
||||
// API配置
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5000";
|
||||
|
||||
// 统一配置对象
|
||||
const AUTH_CONFIG = {
|
||||
// UI文本
|
||||
title: "欢迎使用价值前沿",
|
||||
subtitle: "开启您的投资之旅",
|
||||
formTitle: "手机号验证",
|
||||
buttonText: "登录/注册",
|
||||
loadingText: "验证中...",
|
||||
successTitle: "验证成功",
|
||||
successDescription: "欢迎!",
|
||||
errorTitle: "验证失败",
|
||||
|
||||
// API配置
|
||||
api: {
|
||||
endpoint: '/api/auth/register-with-code',
|
||||
purpose: 'register',
|
||||
},
|
||||
|
||||
// 功能开关
|
||||
features: {
|
||||
successDelay: 1000, // 延迟1秒显示成功提示
|
||||
}
|
||||
};
|
||||
|
||||
export default function AuthFormContent() {
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const { checkSession } = useAuth();
|
||||
const { handleLoginSuccess } = useAuthModal();
|
||||
|
||||
// 使用统一配置
|
||||
const config = AUTH_CONFIG;
|
||||
|
||||
// 追踪组件挂载状态,防止内存泄漏
|
||||
const isMountedRef = useRef(true);
|
||||
const cancelRef = useRef(); // AlertDialog 需要的 ref
|
||||
|
||||
// 页面状态
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
// 昵称设置引导对话框
|
||||
const [showNicknamePrompt, setShowNicknamePrompt] = useState(false);
|
||||
const [currentPhone, setCurrentPhone] = useState("");
|
||||
|
||||
|
||||
// 表单数据
|
||||
const [formData, setFormData] = useState({
|
||||
phone: "",
|
||||
verificationCode: "",
|
||||
});
|
||||
|
||||
// 验证码状态
|
||||
const [verificationCodeSent, setVerificationCodeSent] = useState(false);
|
||||
const [sendingCode, setSendingCode] = useState(false);
|
||||
const [countdown, setCountdown] = useState(0);
|
||||
|
||||
// 输入框变化处理
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
// 倒计时逻辑
|
||||
useEffect(() => {
|
||||
let timer;
|
||||
let isMounted = true;
|
||||
|
||||
if (countdown > 0) {
|
||||
timer = setInterval(() => {
|
||||
if (isMounted) {
|
||||
setCountdown(prev => prev - 1);
|
||||
}
|
||||
}, 1000);
|
||||
} else if (countdown === 0 && isMounted) {
|
||||
setVerificationCodeSent(false);
|
||||
}
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
if (timer) clearInterval(timer);
|
||||
};
|
||||
}, [countdown]);
|
||||
|
||||
// 发送验证码
|
||||
const sendVerificationCode = async () => {
|
||||
const credential = formData.phone;
|
||||
|
||||
if (!credential) {
|
||||
toast({
|
||||
title: "请先输入手机号",
|
||||
status: "warning",
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(credential)) {
|
||||
toast({
|
||||
title: "请输入有效的手机号",
|
||||
status: "warning",
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setSendingCode(true);
|
||||
const response = await fetch(`${API_BASE_URL}/api/auth/send-verification-code`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
credential,
|
||||
type: 'phone',
|
||||
purpose: config.api.purpose // 根据模式使用不同的purpose
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
throw new Error('网络请求失败,请检查网络连接');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!isMountedRef.current) return;
|
||||
|
||||
if (!data) {
|
||||
throw new Error('服务器响应为空');
|
||||
}
|
||||
|
||||
if (response.ok && data.success) {
|
||||
toast({
|
||||
title: "验证码已发送",
|
||||
description: "验证码已发送到您的手机号",
|
||||
status: "success",
|
||||
duration: 3000,
|
||||
});
|
||||
setVerificationCodeSent(true);
|
||||
setCountdown(60);
|
||||
} else {
|
||||
throw new Error(data.error || '发送验证码失败');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isMountedRef.current) {
|
||||
toast({
|
||||
title: "发送验证码失败",
|
||||
description: error.message || "请稍后重试",
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setSendingCode(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 提交处理(登录或注册)
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const { phone, verificationCode, nickname } = formData;
|
||||
|
||||
// 表单验证
|
||||
if (!phone || !verificationCode) {
|
||||
toast({
|
||||
title: "请填写完整信息",
|
||||
description: "手机号和验证码不能为空",
|
||||
status: "warning",
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
toast({
|
||||
title: "请输入有效的手机号",
|
||||
status: "warning",
|
||||
duration: 3000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
const requestBody = {
|
||||
credential: phone,
|
||||
verification_code: verificationCode,
|
||||
register_type: 'phone',
|
||||
};
|
||||
|
||||
// 调用API(根据模式选择不同的endpoint)
|
||||
const response = await fetch(`${API_BASE_URL}${config.api.endpoint}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
throw new Error('网络请求失败,请检查网络连接');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!isMountedRef.current) return;
|
||||
|
||||
if (!data) {
|
||||
throw new Error('服务器响应为空');
|
||||
}
|
||||
|
||||
if (response.ok && data.success) {
|
||||
// 更新session
|
||||
await checkSession();
|
||||
|
||||
toast({
|
||||
title: config.successTitle,
|
||||
description: config.successDescription,
|
||||
status: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
// 检查是否为新注册用户
|
||||
if (data.isNewUser) {
|
||||
// 新注册用户,延迟后显示昵称设置引导
|
||||
setTimeout(() => {
|
||||
setCurrentPhone(phone);
|
||||
setShowNicknamePrompt(true);
|
||||
}, config.features.successDelay);
|
||||
} else {
|
||||
// 已有用户,直接登录成功
|
||||
setTimeout(() => {
|
||||
handleLoginSuccess({ phone });
|
||||
}, config.features.successDelay);
|
||||
}
|
||||
} else {
|
||||
throw new Error(data.error || `${config.errorTitle}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth error:', error);
|
||||
if (isMountedRef.current) {
|
||||
toast({
|
||||
title: config.errorTitle,
|
||||
description: error.message || "请稍后重试",
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
if (isMountedRef.current) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 组件卸载时清理
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
|
||||
return () => {
|
||||
isMountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box width="100%">
|
||||
<AuthHeader title={config.title} subtitle={config.subtitle} />
|
||||
<HStack spacing={8} align="stretch">
|
||||
<Box flex="4">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<VStack spacing={4}>
|
||||
<Heading size="md" color="gray.700" alignSelf="flex-start">{config.formTitle}</Heading>
|
||||
<FormControl isRequired isInvalid={!!errors.phone}>
|
||||
<Input name="phone" value={formData.phone} onChange={handleInputChange} placeholder="请输入11位手机号" />
|
||||
<FormErrorMessage>{errors.phone}</FormErrorMessage>
|
||||
</FormControl>
|
||||
<VerificationCodeInput value={formData.verificationCode} onChange={handleInputChange} onSendCode={sendVerificationCode} countdown={countdown} isLoading={isLoading} isSending={sendingCode} error={errors.verificationCode} colorScheme="green" />
|
||||
<Button type="submit" width="100%" size="lg" colorScheme="green" color="white" borderRadius="lg" isLoading={isLoading} loadingText={config.loadingText} fontWeight="bold"><Icon as={FaLock} mr={2} />{config.buttonText}</Button>
|
||||
|
||||
{/* 隐私声明 */}
|
||||
<Text fontSize="xs" color="gray.500" textAlign="center" mt={2}>
|
||||
登录即表示你同意价值前沿{" "}
|
||||
<ChakraLink
|
||||
as="a"
|
||||
href="/home/user-agreement"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="blue.500"
|
||||
textDecoration="underline"
|
||||
_hover={{ color: "blue.600" }}
|
||||
>
|
||||
《用户协议》
|
||||
</ChakraLink>
|
||||
{" "}和{" "}
|
||||
<ChakraLink
|
||||
as="a"
|
||||
href="/home/privacy-policy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="blue.500"
|
||||
textDecoration="underline"
|
||||
_hover={{ color: "blue.600" }}
|
||||
>
|
||||
《隐私政策》
|
||||
</ChakraLink>
|
||||
</Text>
|
||||
</VStack>
|
||||
</form>
|
||||
</Box>
|
||||
<Box flex="1">
|
||||
<Center width="100%" bg="gray.50" borderRadius="lg" p={8}><WechatRegister /></Center>
|
||||
</Box>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{/* 只在需要时才渲染 AlertDialog,避免创建不必要的 Portal */}
|
||||
{showNicknamePrompt && (
|
||||
<AlertDialog isOpen={showNicknamePrompt} leastDestructiveRef={cancelRef} onClose={() => { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); }} isCentered closeOnEsc={true} closeOnOverlayClick={false}>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">完善个人信息</AlertDialogHeader>
|
||||
<AlertDialogBody>您已成功注册!是否前往个人中心设置昵称和其他信息?</AlertDialogBody>
|
||||
<AlertDialogFooter>
|
||||
<Button ref={cancelRef} onClick={() => { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); }}>稍后再说</Button>
|
||||
<Button colorScheme="green" onClick={() => { setShowNicknamePrompt(false); handleLoginSuccess({ phone: currentPhone }); setTimeout(() => { navigate('/admin/profile'); }, 300); }} ml={3}>去设置</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
84
src/components/Auth/AuthModalManager.js
Normal file
84
src/components/Auth/AuthModalManager.js
Normal file
@@ -0,0 +1,84 @@
|
||||
// src/components/Auth/AuthModalManager.js
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
useBreakpointValue
|
||||
} from '@chakra-ui/react';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import AuthFormContent from './AuthFormContent';
|
||||
|
||||
/**
|
||||
* 全局认证弹窗管理器
|
||||
* 统一的登录/注册弹窗
|
||||
*/
|
||||
export default function AuthModalManager() {
|
||||
const {
|
||||
isAuthModalOpen,
|
||||
closeModal
|
||||
} = useAuthModal();
|
||||
|
||||
// 响应式尺寸配置
|
||||
const modalSize = useBreakpointValue({
|
||||
base: "full", // 移动端:全屏
|
||||
sm: "xl", // 小屏:xl
|
||||
md: "2xl", // 中屏:2xl
|
||||
lg: "4xl" // 大屏:4xl
|
||||
});
|
||||
|
||||
// 条件渲染:只在打开时才渲染 Modal,避免创建不必要的 Portal
|
||||
if (!isAuthModalOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isAuthModalOpen}
|
||||
onClose={closeModal}
|
||||
size={modalSize}
|
||||
isCentered
|
||||
closeOnOverlayClick={false} // 防止误点击背景关闭
|
||||
closeOnEsc={true} // 允许ESC键关闭
|
||||
scrollBehavior="inside" // 内容滚动
|
||||
zIndex={999} // 低于导航栏(1000),不覆盖导航
|
||||
>
|
||||
{/* 半透明背景 + 模糊效果 */}
|
||||
<ModalOverlay
|
||||
bg="blackAlpha.700"
|
||||
backdropFilter="blur(10px)"
|
||||
/>
|
||||
|
||||
{/* 弹窗内容容器 */}
|
||||
<ModalContent
|
||||
bg="white"
|
||||
boxShadow="2xl"
|
||||
borderRadius="2xl"
|
||||
maxW={modalSize === "full" ? "100%" : "900px"}
|
||||
my={modalSize === "full" ? 0 : 8}
|
||||
position="relative"
|
||||
>
|
||||
{/* 关闭按钮 */}
|
||||
<ModalCloseButton
|
||||
position="absolute"
|
||||
right={4}
|
||||
top={4}
|
||||
zIndex={9999}
|
||||
color="gray.500"
|
||||
bg="transparent"
|
||||
_hover={{ bg: "gray.100" }}
|
||||
borderRadius="full"
|
||||
size="lg"
|
||||
onClick={closeModal}
|
||||
/>
|
||||
|
||||
{/* 弹窗主体内容 */}
|
||||
<ModalBody p={8}>
|
||||
<AuthFormContent />
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import { ChevronDownIcon, HamburgerIcon, SunIcon, MoonIcon } from '@chakra-ui/ic
|
||||
import { FiStar, FiCalendar } from 'react-icons/fi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
|
||||
/** 桌面端导航 - 完全按照原网站
|
||||
* @TODO 添加逻辑 不展示导航case
|
||||
@@ -200,6 +201,7 @@ export default function HomeNavbar() {
|
||||
const navigate = useNavigate();
|
||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||
const { user, isAuthenticated, logout, isLoading } = useAuth();
|
||||
const { openAuthModal } = useAuthModal();
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
const navbarBg = useColorModeValue('white', 'gray.800');
|
||||
const navbarBorder = useColorModeValue('gray.200', 'gray.700');
|
||||
@@ -231,10 +233,6 @@ export default function HomeNavbar() {
|
||||
}
|
||||
};
|
||||
|
||||
// 处理登录按钮点击
|
||||
const handleLoginClick = () => {
|
||||
navigate('/auth/signin');
|
||||
};
|
||||
|
||||
// 检查是否为禁用的链接(没有NEW标签的链接)
|
||||
// const isDisabledLink = true;
|
||||
@@ -733,13 +731,13 @@ export default function HomeNavbar() {
|
||||
</Menu>
|
||||
</HStack>
|
||||
) : (
|
||||
// 未登录状态
|
||||
// 未登录状态 - 单一按钮
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
borderRadius="full"
|
||||
onClick={handleLoginClick}
|
||||
onClick={() => openAuthModal()}
|
||||
_hover={{
|
||||
transform: "translateY(-1px)",
|
||||
boxShadow: "md"
|
||||
@@ -960,7 +958,7 @@ export default function HomeNavbar() {
|
||||
colorScheme="blue"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
handleLoginClick();
|
||||
openAuthModal();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -18,6 +18,11 @@ const PrivacyPolicyModal = ({ isOpen, onClose }) => {
|
||||
const headingColor = useColorModeValue("gray.800", "white");
|
||||
const textColor = useColorModeValue("gray.600", "gray.300");
|
||||
|
||||
// Conditional rendering: only render Modal when open
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
// src/components/ProtectedRoute.js - Session版本
|
||||
import React from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
// src/components/ProtectedRoute.js - 弹窗拦截版本
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box, VStack, Spinner, Text } from '@chakra-ui/react';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
const ProtectedRoute = ({ children }) => {
|
||||
const { isAuthenticated, isLoading, user } = useAuth();
|
||||
const { openAuthModal, isAuthModalOpen } = useAuthModal();
|
||||
|
||||
// 记录当前路径,登录成功后可以跳转回来
|
||||
const currentPath = window.location.pathname + window.location.search;
|
||||
|
||||
// 未登录时自动弹出认证窗口
|
||||
useEffect(() => {
|
||||
if (!isLoading && !isAuthenticated && !user && !isAuthModalOpen) {
|
||||
openAuthModal(currentPath);
|
||||
}
|
||||
}, [isAuthenticated, user, isLoading, isAuthModalOpen, currentPath, openAuthModal]);
|
||||
|
||||
// 显示加载状态
|
||||
if (isLoading) {
|
||||
@@ -25,26 +36,26 @@ const ProtectedRoute = ({ children }) => {
|
||||
);
|
||||
}
|
||||
|
||||
// 记录当前路径,登录后可以回到这里
|
||||
let currentPath = window.location.pathname + window.location.search;
|
||||
let redirectUrl = `/auth/signin?redirect=${encodeURIComponent(currentPath)}`;
|
||||
|
||||
// 检查是否已登录
|
||||
// 未登录时显示占位符(弹窗会自动打开)
|
||||
if (!isAuthenticated || !user) {
|
||||
return <Navigate to={redirectUrl} replace />;
|
||||
return (
|
||||
<Box
|
||||
height="100vh"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
bg="gray.50"
|
||||
>
|
||||
<VStack spacing={4}>
|
||||
<Spinner size="xl" color="blue.500" thickness="4px" />
|
||||
<Text fontSize="lg" color="gray.600">请先登录...</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// 已登录,渲染子组件
|
||||
// return children;
|
||||
|
||||
// 更新逻辑 如果 currentPath 是首页 登陆成功后跳转到个人中心
|
||||
if (currentPath === '/' || currentPath === '/home') {
|
||||
currentPath = '/profile';
|
||||
redirectUrl = `/auth/signin?redirect=${encodeURIComponent(currentPath)}`;
|
||||
return <Navigate to={redirectUrl} replace />;
|
||||
} else { // 否则正常渲染
|
||||
return children;
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
||||
export default ProtectedRoute;
|
||||
export default ProtectedRoute;
|
||||
|
||||
@@ -18,6 +18,11 @@ const UserAgreementModal = ({ isOpen, onClose }) => {
|
||||
const headingColor = useColorModeValue("gray.800", "white");
|
||||
const textColor = useColorModeValue("gray.600", "gray.300");
|
||||
|
||||
// Conditional rendering: only render Modal when open
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
|
||||
@@ -455,15 +455,14 @@ export const AuthProvider = ({ children }) => {
|
||||
isClosable: true,
|
||||
});
|
||||
|
||||
// 跳转到登录页面
|
||||
navigate('/auth/signin');
|
||||
// 不再跳转,用户留在当前页面
|
||||
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
// 即使API调用失败也清除本地状态
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
navigate('/auth/signin');
|
||||
// 不再跳转,用户留在当前页面
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
100
src/contexts/AuthModalContext.js
Normal file
100
src/contexts/AuthModalContext.js
Normal file
@@ -0,0 +1,100 @@
|
||||
// src/contexts/AuthModalContext.js
|
||||
import { createContext, useContext, useState, useCallback } from 'react';
|
||||
|
||||
const AuthModalContext = createContext();
|
||||
|
||||
/**
|
||||
* 自定义Hook:获取弹窗上下文
|
||||
*/
|
||||
export const useAuthModal = () => {
|
||||
const context = useContext(AuthModalContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuthModal must be used within AuthModalProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
/**
|
||||
* 认证弹窗提供者组件
|
||||
* 管理统一的认证弹窗状态(登录/注册合并)
|
||||
*/
|
||||
export const AuthModalProvider = ({ children }) => {
|
||||
// 弹窗状态(统一的认证弹窗)
|
||||
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
|
||||
|
||||
// 重定向URL(认证成功后跳转)
|
||||
const [redirectUrl, setRedirectUrl] = useState(null);
|
||||
|
||||
// 成功回调函数
|
||||
const [onSuccessCallback, setOnSuccessCallback] = useState(null);
|
||||
|
||||
/**
|
||||
* 打开认证弹窗(统一的登录/注册入口)
|
||||
* @param {string} url - 认证成功后的重定向URL(可选)
|
||||
* @param {function} callback - 认证成功后的回调函数(可选)
|
||||
*/
|
||||
const openAuthModal = useCallback((url = null, callback = null) => {
|
||||
setRedirectUrl(url);
|
||||
setOnSuccessCallback(() => callback);
|
||||
setIsAuthModalOpen(true);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 关闭认证弹窗
|
||||
*/
|
||||
const closeModal = useCallback(() => {
|
||||
setIsAuthModalOpen(false);
|
||||
setRedirectUrl(null);
|
||||
setOnSuccessCallback(null);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 登录/注册成功处理
|
||||
* @param {object} user - 用户信息
|
||||
*/
|
||||
const handleLoginSuccess = useCallback((user) => {
|
||||
// 执行自定义回调(如果有)
|
||||
if (onSuccessCallback) {
|
||||
try {
|
||||
onSuccessCallback(user);
|
||||
} catch (error) {
|
||||
console.error('Success callback error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有重定向URL,则跳转
|
||||
if (redirectUrl) {
|
||||
// 使用 window.location.href 确保完整刷新页面状态
|
||||
setTimeout(() => {
|
||||
window.location.href = redirectUrl;
|
||||
}, 500); // 延迟500ms,让用户看到成功提示
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
closeModal();
|
||||
}, [onSuccessCallback, redirectUrl, closeModal]);
|
||||
|
||||
/**
|
||||
* 提供给子组件的上下文值
|
||||
*/
|
||||
const value = {
|
||||
// 状态
|
||||
isAuthModalOpen,
|
||||
redirectUrl,
|
||||
|
||||
// 打开弹窗方法
|
||||
openAuthModal,
|
||||
|
||||
// 关闭弹窗方法
|
||||
closeModal,
|
||||
|
||||
// 成功处理方法
|
||||
handleLoginSuccess,
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthModalContext.Provider value={value}>
|
||||
{children}
|
||||
</AuthModalContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -13,6 +13,10 @@ import SettingsPage from "views/Settings/SettingsPage";
|
||||
import CenterDashboard from "views/Dashboard/Center";
|
||||
import Subscription from "views/Pages/Account/Subscription";
|
||||
|
||||
// 懒加载隐私政策和用户协议页面
|
||||
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
|
||||
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
|
||||
|
||||
// 导入保护路由组件
|
||||
import ProtectedRoute from "../components/ProtectedRoute";
|
||||
|
||||
@@ -66,6 +70,12 @@ export default function Home() {
|
||||
}
|
||||
/>
|
||||
|
||||
{/* 隐私政策页面 - 无需登录 */}
|
||||
<Route path="/privacy-policy" element={<PrivacyPolicy />} />
|
||||
|
||||
{/* 用户协议页面 - 无需登录 */}
|
||||
<Route path="/user-agreement" element={<UserAgreement />} />
|
||||
|
||||
{/* 其他可能的路由 */}
|
||||
<Route path="*" element={<HomePage />} />
|
||||
</Routes>
|
||||
|
||||
@@ -83,6 +83,8 @@ const CompanyIndex = React.lazy(() => import("views/Company"));
|
||||
const MarketDataView = React.lazy(() => import("views/Company/MarketDataView"));
|
||||
const StockOverview = React.lazy(() => import("views/StockOverview"));
|
||||
const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
|
||||
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
|
||||
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
|
||||
const dashRoutes = [
|
||||
{
|
||||
name: "Dashboard",
|
||||
@@ -211,6 +213,22 @@ const dashRoutes = [
|
||||
layout: "/admin",
|
||||
invisible: true, // 不在侧边栏显示
|
||||
},
|
||||
{
|
||||
name: "隐私政策",
|
||||
path: "/privacy-policy",
|
||||
icon: <DocumentIcon color="inherit" />,
|
||||
component: PrivacyPolicy,
|
||||
layout: "/home",
|
||||
invisible: true, // 不在侧边栏显示
|
||||
},
|
||||
{
|
||||
name: "用户协议",
|
||||
path: "/user-agreement",
|
||||
icon: <DocumentIcon color="inherit" />,
|
||||
component: UserAgreement,
|
||||
layout: "/home",
|
||||
invisible: true, // 不在侧边栏显示
|
||||
},
|
||||
{
|
||||
name: "PAGES",
|
||||
category: "pages",
|
||||
|
||||
238
src/views/Pages/PrivacyPolicy.js
Normal file
238
src/views/Pages/PrivacyPolicy.js
Normal file
@@ -0,0 +1,238 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Text,
|
||||
VStack,
|
||||
Divider,
|
||||
useColorModeValue,
|
||||
Heading
|
||||
} from "@chakra-ui/react";
|
||||
|
||||
export default function PrivacyPolicy() {
|
||||
const headingColor = useColorModeValue("gray.800", "white");
|
||||
const textColor = useColorModeValue("gray.600", "gray.300");
|
||||
|
||||
return (
|
||||
<Box minH="100vh" bg={useColorModeValue("gray.50", "gray.900")} py={12}>
|
||||
<Container maxW="container.lg">
|
||||
<VStack spacing={8} align="stretch">
|
||||
<Heading as="h1" size="2xl" color={headingColor} textAlign="center">
|
||||
隐私政策
|
||||
</Heading>
|
||||
|
||||
<Box bg="blue.50" p={6} borderRadius="lg" border="1px solid" borderColor="blue.100">
|
||||
<Text fontSize="md" color="blue.600" mb={3} fontWeight="semibold">
|
||||
生效日期:2025年1月20日
|
||||
</Text>
|
||||
<Text fontSize="lg" color={textColor} lineHeight="1.8" mb={4}>
|
||||
【北京价值前沿科技有限公司】(以下简称"我们")深知个人信息对您的重要性,并会尽全力保护您的个人信息安全可靠。我们致力于维持您对我们的信任,恪守以下原则,保护您的个人信息:权责一致原则、目的明确原则、选择同意原则、最少够用原则、确保安全原则、主体参与原则、公开透明原则等。同时,我们承诺,我们将按业界成熟的安全标准,采取相应的安全保护措施来保护您的个人信息。
|
||||
</Text>
|
||||
<Text fontSize="lg" color={textColor} lineHeight="1.8" fontWeight="medium">
|
||||
请在使用我们的产品(或服务)前,仔细阅读并了解本《隐私政策》。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
一、我们如何收集和使用您的个人信息
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
根据《信息安全技术个人信息安全规范》(GB/T 35273—2020),个人信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。本隐私政策中涉及的个人信息包括:基本信息(包括性别、地址、地区、个人电话号码、电子邮箱);个人身份信息(包括身份证、护照、相关身份证明等);网络身份标识信息(包括系统账号、IP地址、口令);个人上网记录(包括登录记录、浏览记录);个人常用设备信息(包括硬件型号、操作系统类型、应用安装列表、运行中进程信息、设备MAC地址、软件列表设备识别码如IMEI/android ID/IDFA/IMSI 在内的描述个人常用设备基本情况的信息);个人位置信息(包括精准定位信息、经纬度等);
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
个人敏感信息是指一旦泄露、非法提供或滥用可能危害人身和财产安全,极易导致个人名誉、身心健康受到损害或歧视性待遇等的个人信息,本隐私政策中涉及的个人敏感信息包括:个人身份信息(包括身份证、护照、相关身份证明等);网络身份识别信息(包括账户名、账户昵称、用户头像、与前述有关的密码);其他信息(包括个人电话号码、浏览记录、精准定位信息)。对于个人敏感信息,我们将在本政策中进行显著标识,请您仔细阅读。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="green.300" bg="green.50" p={4} borderRadius="md">
|
||||
<Heading as="h3" size="md" color="green.700" mb={2}>
|
||||
(一)手机号注册/登录
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
当您使用手机号注册/登录服务时,我们会收集您的手机号码、验证码匹配结果、手机系统平台等信息,用于保存您的登录信息,使您在使用不同设备登录时能够同步您的数据。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="purple.300" bg="purple.50" p={4} borderRadius="md">
|
||||
<Heading as="h3" size="md" color="purple.700" mb={2}>
|
||||
(二)第三方登录
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
当您使用微信/QQ等第三方登录时,我们会收集您第三方的唯一标识、头像、昵称,用于保存您的登录信息,使您在使用不同设备登录时能够同步您的数据。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
当您使用微信,微博,QQ 进行三方分享的时候,我们的产品可能会集成第三方的SDK或其他类似的应用程序,用于三方登录以及分享内容到三方平台。您可以登陆以下网址了解相关隐私政策:
|
||||
</Text>
|
||||
<VStack align="start" spacing={2} pl={4}>
|
||||
<Text fontSize="sm" color="blue.600" lineHeight="1.6">
|
||||
【新浪微博】微博个人信息保护政策:https://m.weibo.cn/c/privacy
|
||||
</Text>
|
||||
<Text fontSize="sm" color="blue.600" lineHeight="1.6">
|
||||
【微信】微信开放平台开发者服务协议:https://open.weixin.qq.com/cgi-bin/frame?t=news/protocol_developer_tmpl
|
||||
</Text>
|
||||
<Text fontSize="sm" color="blue.600" lineHeight="1.6">
|
||||
【QQ】QQ互联SDK隐私保护声明:https://wiki.connect.qq.com/qq互联sdk隐私保护声明
|
||||
</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="orange.300" bg="orange.50" p={4} borderRadius="md">
|
||||
<Heading as="h3" size="md" color="orange.700" mb={2}>
|
||||
(三)第三方支付
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
当您使用 微信 支付宝 华为 进行三方支付的时候,我们的产品可能会集成第三方的SDK或其他类似的应用程序,帮助用户在应用内使用三方支付:
|
||||
</Text>
|
||||
<VStack align="start" spacing={2} pl={4}>
|
||||
<Text fontSize="sm" color="blue.600" lineHeight="1.6">
|
||||
【支付宝】客户端 SDK 隐私说明:https://opendocs.alipay.com/open/01g6qm
|
||||
</Text>
|
||||
<Text fontSize="sm" color="blue.600" lineHeight="1.6">
|
||||
【微信支付】微信支付服务协议:https://pay.weixin.qq.com/index.php/public/apply_sign/protocol_v2
|
||||
</Text>
|
||||
<Text fontSize="sm" color="blue.600" lineHeight="1.6">
|
||||
【华为支付】SDK数据安全说明:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/sdk-data-security-0000001050044906
|
||||
</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
二、我们如何使用 Cookie 和同类技术
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
为确保网站正常运转,我们会在您的计算机或移动设备上存储名为 Cookie 的小数据文件。Cookie 通常包含标识符、站点名称以及一些号码和字符。借助于 Cookie,网站能够存储您的偏好或购物篮内的商品等数据。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
三、我们如何共享、转让、公开披露您的个人信息
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
我们不会向其他任何公司、组织和个人分享您的个人信息,但以下情况除外:
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={2}>
|
||||
1、在获取明确同意的情况下共享:获得您的明确同意后,我们会与其他方共享您的个人信息。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={2}>
|
||||
2、我们可能会根据法律法规规定,或按政府主管部门的强制性要求,对外共享您的个人信息。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
3、与我们的关联公司共享:您的个人信息可能会与我们关联公司共享。我们只会共享必要的个人信息,且受本隐私政策中所声明目的的约束。关联公司如要改变个人信息的处理目的,将再次征求您的授权同意。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
我们的关联公司包括:北京价值经纬咨询有限责任公司等。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
四、我们如何保护您的个人信息
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
我们已使用符合业界标准的安全防护措施保护您提供的个人信息,防止数据遭到未经授权访问、公开披露、使用、修改、损坏或丢失。我们会采取一切合理可行的措施,保护您的个人信息。例如,在您的浏览器与"服务"之间交换数据(如信用卡信息)时受 SSL 加密保护;我们同时对我们网站提供 https 安全浏览方式;我们会使用加密技术确保数据的保密性;我们会使用受信赖的保护机制防止数据遭到恶意攻击;我们会部署访问控制机制,确保只有授权人员才可访问个人信息;以及我们会举办安全和隐私保护培训课程,加强员工对于保护个人信息重要性的认识。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
五、您的权利
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
按照中国相关的法律、法规、标准,以及其他国家、地区的通行做法,我们保障您对自己的个人信息行使以下权利:
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={2}>
|
||||
(一)访问您的个人信息
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={2}>
|
||||
(二)更正您的个人信息
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={2}>
|
||||
(三)删除您的个人信息
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
(四)约束信息系统自动决策
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
如果您无法通过上述链接更正这些个人信息,您可以随时使用我们的 Web 表单联系,或发送电子邮件至admin@valuefrontier.cn。我们将在30天内回复您的更正请求。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
六、我们如何处理儿童的个人信息
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
我们的产品、网站和服务主要面向成人。如果没有父母或监护人的同意,儿童不得创建自己的用户账户。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
对于经父母同意而收集儿童个人信息的情况,我们只会在受到法律允许、父母或监护人明确同意或者保护儿童所必要的情况下使用或公开披露此信息。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
尽管当地法律和习俗对儿童的定义不同,但我们将不满 14 周岁的任何人均视为儿童。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
如果我们发现自己在未事先获得可证实的父母同意的情况下收集了儿童的个人信息,则会设法尽快删除相关数据。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
七、本隐私政策如何更新
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
我们可能适时会对本隐私政策进行调整或变更,本隐私政策的任何更新将在用户启动应用时以弹窗形式提醒用户更新内容并提示查看最新的隐私政策,提醒用户重新确认是否同意隐私政策条款,除法律法规或监管规定另有强制性规定外,经调整或变更的内容一经用户确认后将即时生效。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
八、如何联系我们
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
如果您对本隐私政策有任何疑问、意见或建议,通过以下方式与我们联系:
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
邮箱:admin@valuefrontier.cn
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
九、未成年人保护方面
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
1、若您是未满18周岁的未成年人,您应在您的监护人监护、指导下并获得监护人同意的情况下,认真阅读并同意本协议后,方可使用价值前沿app及相关服务。若您未取得监护人的同意,监护人可以通过联系价值前沿官方公布的客服联系方式通知价值前沿处理相关账号,价值前沿有权对相关账号的功能、使用进行限制,包括但不限于浏览、发布信息、互动交流等功能。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
2、价值前沿重视对未成年人个人信息的保护,未成年用户在填写个人信息时,请加强个人保护意识并谨慎对待,并应在取得监护人的同意以及在监护人指导下正确使用价值前沿app及相关服务。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.6" mb={4}>
|
||||
3、未成年人用户及其监护人理解并确认,如您违反法律法规、本协议内容,则您及您的监护人应依照法律规定承担因此而可能导致的全部法律责任。
|
||||
</Text>
|
||||
</Box>
|
||||
</VStack>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
236
src/views/Pages/UserAgreement.js
Normal file
236
src/views/Pages/UserAgreement.js
Normal file
@@ -0,0 +1,236 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Text,
|
||||
VStack,
|
||||
Divider,
|
||||
useColorModeValue,
|
||||
Heading
|
||||
} from "@chakra-ui/react";
|
||||
|
||||
export default function UserAgreement() {
|
||||
const headingColor = useColorModeValue("gray.800", "white");
|
||||
const textColor = useColorModeValue("gray.600", "gray.300");
|
||||
|
||||
return (
|
||||
<Box minH="100vh" bg={useColorModeValue("gray.50", "gray.900")} py={12}>
|
||||
<Container maxW="container.lg">
|
||||
<VStack spacing={8} align="stretch">
|
||||
<Heading as="h1" size="2xl" color={headingColor} textAlign="center">
|
||||
价值前沿用户协议
|
||||
</Heading>
|
||||
|
||||
<Box bg="orange.50" p={6} borderRadius="lg" border="1px solid" borderColor="orange.100">
|
||||
<Heading as="h2" size="lg" color="orange.700" mb={6}>
|
||||
欢迎你使用价值前沿及服务!
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={5}>
|
||||
为使用价值前沿(以下简称"本软件")及服务,你应当阅读并遵守《价值前沿用户协议》(以下简称"本协议")。请你务必审慎阅读、充分理解各条款内容,特别是免除或者限制责任的条款,以及开通或使用某项服务的单独协议,并选择接受或不接受。限制、免责条款可能以加粗形式提示你注意。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={5}>
|
||||
除非你已阅读并接受本协议所有条款,否则你无权下载、安装或使用本软件及相关服务。你的下载、安装、使用、获取价值前沿帐号、登录等行为即视为你已阅读并同意上述协议的约束。
|
||||
</Text>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" fontWeight="medium">
|
||||
如果你未满18周岁,请在法定监护人的陪同下阅读本协议及其他上述协议,并特别注意未成年人使用条款。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={6}>
|
||||
一、协议的范围
|
||||
</Heading>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="blue.300" bg="blue.50" p={4} borderRadius="md" mb={4}>
|
||||
<Heading as="h3" size="md" color="blue.700" mb={2}>
|
||||
1.1 协议适用主体范围
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8">
|
||||
本协议是你与北京价值前沿科技有限公司之间关于你下载、安装、使用、复制本软件,以及使用价值前沿相关服务所订立的协议。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="green.300" bg="green.50" p={4} borderRadius="md" mb={4}>
|
||||
<Heading as="h3" size="md" color="green.700" mb={2}>
|
||||
1.2 协议关系及冲突条款
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8">
|
||||
本协议内容同时包括北京价值前沿科技有限公司可能不断发布的关于本服务的相关协议、业务规则等内容。上述内容一经正式发布,即为本协议不可分割的组成部分,你同样应当遵守。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="purple.300" bg="purple.50" p={4} borderRadius="md" mb={4}>
|
||||
<Heading as="h3" size="md" color="purple.700" mb={2}>
|
||||
1.3 许可范围
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8">
|
||||
明确标识免费产品的,用户可以进行自用的、非商业性、无限制数量地下载、安装及使用,但不得复制、分发。其他收费类产品或者信息,除遵守本协议规定之外,还须遵守专门协议的规定。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="red.300" bg="red.50" p={4} borderRadius="md">
|
||||
<Heading as="h3" size="md" color="red.700" mb={2}>
|
||||
1.4 权利限制
|
||||
</Heading>
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8">
|
||||
禁止反向工程、反向编译和反向汇编:用户不得对价值前沿软件类产品进行反向工程、反向编译或反向汇编,同时不得改动编译程序文件内部的任何资源。除法律、法规明文规定允许上述活动外,用户必须遵守此协议限制。
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
二、关于本服务
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
本服务内容是指北京价值前沿科技有限公司向用户提供的跨平台的社交资讯工具(以下简称"价值前沿"),支持单人、多人参与,在发布图片和文字等内容服务的基础上,同时为用户提供包括但不限于社交关系拓展、便捷工具等功能或内容的软件服务(以下简称"本服务")。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
三、免责条款
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
北京价值前沿科技有限公司作为面向全球投资人提供信息和服务的商家,对以下情况不承担相关责任:
|
||||
</Text>
|
||||
|
||||
<VStack align="stretch" spacing={3}>
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="yellow.400" bg="yellow.50" p={3} borderRadius="md">
|
||||
<Heading as="h3" size="sm" color="yellow.700" mb={2}>
|
||||
1)不可抗力
|
||||
</Heading>
|
||||
<Text fontSize="sm" color={textColor} lineHeight="1.6">
|
||||
如因发生自然灾害、战争、第三方侵害等不可控因素而发生的信息、服务中断,价值前沿不承担相应责任。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="yellow.400" bg="yellow.50" p={3} borderRadius="md">
|
||||
<Heading as="h3" size="sm" color="yellow.700" mb={2}>
|
||||
2)信息网络传播
|
||||
</Heading>
|
||||
<Text fontSize="sm" color={textColor} lineHeight="1.6">
|
||||
如因信息网络传播中的拥塞、断续、病毒木马、黑客窃取侦听等网络通道上的因素,而造成信息缺失、丢失、延迟、被篡改等,价值前沿不对此承担相应责任。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="yellow.400" bg="yellow.50" p={3} borderRadius="md">
|
||||
<Heading as="h3" size="sm" color="yellow.700" mb={2}>
|
||||
3)第三方信息的收集整理
|
||||
</Heading>
|
||||
<Text fontSize="sm" color={textColor} lineHeight="1.6">
|
||||
价值前沿为了更好地服务投资者,便于用户分析研判投资环境,尽可能多地收集整理来自第三方的所有信息,分门别类地提供给用户参考,并明确标识为来自第三方的信息,而对内容的真实性、合理性、完整性、合法性等并不承担判断责任,也不承担用户因信息而造成的损失责任。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="yellow.400" bg="yellow.50" p={3} borderRadius="md">
|
||||
<Heading as="h3" size="sm" color="yellow.700" mb={2}>
|
||||
4)证券信息汇总
|
||||
</Heading>
|
||||
<Text fontSize="sm" color={textColor} lineHeight="1.6">
|
||||
价值前沿仅提供证券信息汇总及证券投资品种历史数据统计功能,不针对用户提供任何情况判断、投资参考、品种操作建议等等,不属于荐股软件。用户按照自身对于市场环境的分析研判而做出的评论参考,用户可以结合自身需求予以借鉴,并自行作出判断,风险和收益都由用户自行承担。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="yellow.400" bg="yellow.50" p={3} borderRadius="md">
|
||||
<Heading as="h3" size="sm" color="yellow.700" mb={2}>
|
||||
5)信息储存
|
||||
</Heading>
|
||||
<Text fontSize="sm" color={textColor} lineHeight="1.6">
|
||||
用户在使用价值前沿系统时,会因信息注册、产品购买、软件使用过程中的某些需求,而留存于系统中的账户、密码、真实身份、联系方式、用户网络信息等个人信息,价值前沿将按照国家相关规定进行必要的保护。
|
||||
</Text>
|
||||
</Box>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
四、用户个人信息保护
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
保护用户个人信息是北京价值前沿科技有限公司的一项基本原则,北京价值前沿科技有限公司将会采取合理的措施保护用户的个人信息。除法律法规规定的情形外,未经用户许可北京价值前沿科技有限公司不会向第三方公开、透露用户个人信息。
|
||||
</Text>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
你在注册帐号或使用本服务的过程中,需要提供一些必要的信息,例如:为向你提供帐号注册服务或进行用户身份识别,需要你填写手机号码;手机通讯录匹配功能需要你授权访问手机通讯录等。若国家法律法规或政策有特殊规定的,你需要提供真实的身份信息。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
五、用户行为规范
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
你理解并同意,价值前沿一直致力于为用户提供文明健康、规范有序的网络环境,你不得利用价值前沿帐号或本软件及服务制作、复制、发布、传播干扰正常运营,以及侵犯其他用户或第三方合法权益的内容。
|
||||
</Text>
|
||||
|
||||
<Box pl={4} borderLeft="3px solid" borderColor="red.400" bg="red.50" p={4} borderRadius="md">
|
||||
<Heading as="h3" size="md" color="red.700" mb={2}>
|
||||
禁止内容包括但不限于:
|
||||
</Heading>
|
||||
<VStack align="start" spacing={1} pl={4}>
|
||||
<Text fontSize="sm" color={textColor}>• 违反宪法确定的基本原则的内容</Text>
|
||||
<Text fontSize="sm" color={textColor}>• 危害国家安全,泄露国家秘密的内容</Text>
|
||||
<Text fontSize="sm" color={textColor}>• 损害国家荣誉和利益的内容</Text>
|
||||
<Text fontSize="sm" color={textColor}>• 散布谣言,扰乱社会秩序的内容</Text>
|
||||
<Text fontSize="sm" color={textColor}>• 散布淫秽、色情、赌博、暴力、恐怖的内容</Text>
|
||||
<Text fontSize="sm" color={textColor}>• 侮辱或者诽谤他人,侵害他人合法权益的内容</Text>
|
||||
<Text fontSize="sm" color={textColor}>• 其他违反法律法规的内容</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
六、知识产权声明
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
北京价值前沿科技有限公司是本软件的知识产权权利人。本软件的一切著作权、商标权、专利权、商业秘密等知识产权,以及与本软件相关的所有信息内容(包括但不限于文字、图片、音频、视频、图表、界面设计、版面框架、有关数据或电子文档等)均受中华人民共和国法律法规和相应的国际条约保护。
|
||||
</Text>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
未经北京价值前沿科技有限公司或相关权利人书面同意,你不得为任何商业或非商业目的自行或许可任何第三方实施、利用、转让上述知识产权。
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Heading as="h2" size="lg" color={headingColor} mb={4}>
|
||||
七、其他条款
|
||||
</Heading>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
你使用本软件即视为你已阅读并同意受本协议的约束。北京价值前沿科技有限公司有权在必要时修改本协议条款。你可以在本软件的最新版本中查阅相关协议条款。
|
||||
</Text>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8" mb={4}>
|
||||
本协议签订地为中华人民共和国北京市海淀区。本协议的成立、生效、履行、解释及纠纷解决,适用中华人民共和国大陆地区法律(不包括冲突法)。
|
||||
</Text>
|
||||
|
||||
<Text fontSize="md" color={textColor} lineHeight="1.8">
|
||||
若你和北京价值前沿科技有限公司之间发生任何纠纷或争议,首先应友好协商解决;协商不成的,你同意将纠纷或争议提交本协议签订地有管辖权的人民法院管辖。
|
||||
</Text>
|
||||
</Box>
|
||||
</VStack>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user