feat: 登陆注册UI调整,用户协议和隐私政策跳转调整

This commit is contained in:
zdl
2025-10-15 11:03:00 +08:00
parent 29816de72b
commit 4e9acd12c2
18 changed files with 3068 additions and 49 deletions

View File

@@ -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>

View File

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

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

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

View File

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

View File

@@ -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}

View File

@@ -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;

View File

@@ -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}

View File

@@ -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');
// 不再跳转,用户留在当前页面
}
};

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

View File

@@ -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>

View File

@@ -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",

View 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 352732020个人信息是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息本隐私政策中涉及的个人信息包括基本信息包括性别地址地区个人电话号码电子邮箱个人身份信息包括身份证护照相关身份证明等网络身份标识信息包括系统账号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">
QQQQ互联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>
);
}

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