feat: 路由改造
This commit is contained in:
168
src/App.js
168
src/App.js
@@ -26,6 +26,7 @@ import PageLoader from "components/Loading/PageLoader";
|
|||||||
import Admin from "layouts/Admin";
|
import Admin from "layouts/Admin";
|
||||||
import Auth from "layouts/Auth";
|
import Auth from "layouts/Auth";
|
||||||
import HomeLayout from "layouts/Home";
|
import HomeLayout from "layouts/Home";
|
||||||
|
import MainLayout from "layouts/MainLayout";
|
||||||
|
|
||||||
// ⚡ 使用 React.lazy() 实现路由懒加载
|
// ⚡ 使用 React.lazy() 实现路由懒加载
|
||||||
// 首屏不需要的组件按需加载,大幅减少初始 JS 包大小
|
// 首屏不需要的组件按需加载,大幅减少初始 JS 包大小
|
||||||
@@ -48,95 +49,113 @@ import { AuthModalProvider } from "contexts/AuthModalContext";
|
|||||||
import ProtectedRoute from "components/ProtectedRoute";
|
import ProtectedRoute from "components/ProtectedRoute";
|
||||||
import ErrorBoundary from "components/ErrorBoundary";
|
import ErrorBoundary from "components/ErrorBoundary";
|
||||||
import AuthModalManager from "components/Auth/AuthModalManager";
|
import AuthModalManager from "components/Auth/AuthModalManager";
|
||||||
|
import ScrollToTop from "components/ScrollToTop";
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh" bg={colorMode === 'dark' ? 'gray.800' : 'white'}>
|
<Box minH="100vh" bg={colorMode === 'dark' ? 'gray.800' : 'white'}>
|
||||||
{/* ⚡ Suspense 边界:懒加载组件加载时显示 Loading */}
|
{/* 路由切换时自动滚动到顶部 */}
|
||||||
<Suspense fallback={<PageLoader message="页面加载中..." />}>
|
<ScrollToTop />
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* 首页路由 */}
|
{/* 带导航栏的主布局 - 所有需要导航栏的页面都在这里 */}
|
||||||
<Route path="home/*" element={<HomeLayout />} />
|
{/* MainLayout 内部有 Suspense,确保导航栏始终可见 */}
|
||||||
|
<Route element={<MainLayout />}>
|
||||||
|
{/* 首页路由 */}
|
||||||
|
<Route path="home/*" element={<HomeLayout />} />
|
||||||
|
|
||||||
{/* Community页面路由 - 需要登录 */}
|
{/* Community页面路由 - 需要登录 */}
|
||||||
<Route
|
<Route
|
||||||
path="community"
|
path="community"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute>
|
<ProtectedRoute>
|
||||||
<Community />
|
<Community />
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="forecast-report" element={<ForecastReport />} />
|
|
||||||
<Route path="Financial" element={<FinancialPanorama />} />
|
|
||||||
<Route path="company" element={<CompanyIndex />} />
|
|
||||||
<Route path="company/:code" element={<CompanyIndex />} />
|
|
||||||
<Route path="market-data" element={<MarketDataView />} />
|
|
||||||
{/* 概念中心路由 - 需要登录 */}
|
|
||||||
<Route
|
|
||||||
path="concepts"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<ConceptCenter />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="concept"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<ConceptCenter />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/* 股票概览页面路由 - 需要登录 */}
|
|
||||||
<Route
|
|
||||||
path="stocks"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<StockOverview />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="stock-overview"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<StockOverview />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/* Limit Analyse页面路由 - 需要登录 */}
|
|
||||||
<Route
|
|
||||||
path="limit-analyse"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<LimitAnalyse />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/* 事件详情独立页面路由(不经 Admin 布局) */}
|
|
||||||
<Route path="event-detail/:eventId" element={<EventDetail />} />
|
|
||||||
|
|
||||||
{/* 模拟盘交易系统路由 - 无需登录 */}
|
{/* 概念中心路由 - 需要登录 */}
|
||||||
<Route
|
<Route
|
||||||
path="trading-simulation"
|
path="concepts"
|
||||||
element={<TradingSimulation />}
|
element={
|
||||||
/>
|
<ProtectedRoute>
|
||||||
|
<ConceptCenter />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="concept"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<ConceptCenter />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 管理后台路由 - 需要登录 */}
|
{/* 股票概览页面路由 - 需要登录 */}
|
||||||
|
<Route
|
||||||
|
path="stocks"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<StockOverview />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="stock-overview"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<StockOverview />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Limit Analyse页面路由 - 需要登录 */}
|
||||||
|
<Route
|
||||||
|
path="limit-analyse"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<LimitAnalyse />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 模拟盘交易系统路由 - 需要登录 */}
|
||||||
|
<Route
|
||||||
|
path="trading-simulation"
|
||||||
|
element={
|
||||||
|
<ProtectedRoute>
|
||||||
|
<TradingSimulation />
|
||||||
|
</ProtectedRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 事件详情独立页面路由 (不经 Admin 布局) */}
|
||||||
|
<Route path="event-detail/:eventId" element={<EventDetail />} />
|
||||||
|
|
||||||
|
{/* 公司相关页面 */}
|
||||||
|
<Route path="forecast-report" element={<ForecastReport />} />
|
||||||
|
<Route path="Financial" element={<FinancialPanorama />} />
|
||||||
|
<Route path="company" element={<CompanyIndex />} />
|
||||||
|
<Route path="company/:code" element={<CompanyIndex />} />
|
||||||
|
<Route path="market-data" element={<MarketDataView />} />
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
{/* 管理后台路由 - 需要登录,不使用 MainLayout */}
|
||||||
|
{/* 这些路由有自己的加载状态处理 */}
|
||||||
<Route
|
<Route
|
||||||
path="admin/*"
|
path="admin/*"
|
||||||
element={
|
element={
|
||||||
<ProtectedRoute>
|
<Suspense fallback={<PageLoader message="加载中..." />}>
|
||||||
<Admin />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<Admin />
|
||||||
|
</ProtectedRoute>
|
||||||
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 认证页面路由 */}
|
{/* 认证页面路由 - 不使用 MainLayout */}
|
||||||
<Route path="auth/*" element={<Auth />} />
|
<Route path="auth/*" element={<Auth />} />
|
||||||
|
|
||||||
{/* 默认重定向到首页 */}
|
{/* 默认重定向到首页 */}
|
||||||
@@ -145,7 +164,6 @@ function AppContent() {
|
|||||||
{/* 404 页面 */}
|
{/* 404 页面 */}
|
||||||
<Route path="*" element={<Navigate to="/home" replace />} />
|
<Route path="*" element={<Navigate to="/home" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Suspense>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { authService } from "../../services/authService";
|
|||||||
import AuthHeader from './AuthHeader';
|
import AuthHeader from './AuthHeader';
|
||||||
import VerificationCodeInput from './VerificationCodeInput';
|
import VerificationCodeInput from './VerificationCodeInput';
|
||||||
import WechatRegister from './WechatRegister';
|
import WechatRegister from './WechatRegister';
|
||||||
|
import { setCurrentUser } from '../../mocks/data/users';
|
||||||
|
|
||||||
// 统一配置对象
|
// 统一配置对象
|
||||||
const AUTH_CONFIG = {
|
const AUTH_CONFIG = {
|
||||||
@@ -261,6 +262,12 @@ export default function AuthFormContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.ok && data.success) {
|
if (response.ok && data.success) {
|
||||||
|
// ⚡ Mock 模式:先在前端侧写入 localStorage,确保时序正确
|
||||||
|
if (process.env.REACT_APP_ENABLE_MOCK === 'true' && data.user) {
|
||||||
|
setCurrentUser(data.user);
|
||||||
|
console.log('[Auth] 前端侧设置当前用户(Mock模式):', data.user);
|
||||||
|
}
|
||||||
|
|
||||||
// 更新session
|
// 更新session
|
||||||
await checkSession();
|
await checkSession();
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,14 @@ const SecondaryNav = () => {
|
|||||||
{ path: '/stocks', label: '个股中心', badges: [{ text: 'HOT', colorScheme: 'green' }] },
|
{ path: '/stocks', label: '个股中心', badges: [{ text: 'HOT', colorScheme: 'green' }] },
|
||||||
{ path: '/trading-simulation', label: '模拟盘', badges: [{ text: 'NEW', colorScheme: 'red' }] }
|
{ path: '/trading-simulation', label: '模拟盘', badges: [{ text: 'NEW', colorScheme: 'red' }] }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
'/trading-simulation': {
|
||||||
|
title: '行情复盘',
|
||||||
|
items: [
|
||||||
|
{ path: '/limit-analyse', label: '涨停分析', badges: [{ text: 'FREE', colorScheme: 'blue' }] },
|
||||||
|
{ path: '/stocks', label: '个股中心', badges: [{ text: 'HOT', colorScheme: 'green' }] },
|
||||||
|
{ path: '/trading-simulation', label: '模拟盘', badges: [{ text: 'NEW', colorScheme: 'red' }] }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -181,9 +189,9 @@ const NavItems = ({ isAuthenticated, user }) => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
// 辅助函数:判断导航项是否激活
|
// 辅助函数:判断导航项是否激活
|
||||||
const isActive = (paths) => {
|
const isActive = useCallback((paths) => {
|
||||||
return paths.some(path => location.pathname.includes(path));
|
return paths.some(path => location.pathname.includes(path));
|
||||||
};
|
}, [location.pathname]);
|
||||||
|
|
||||||
if (isAuthenticated && user) {
|
if (isAuthenticated && user) {
|
||||||
return (
|
return (
|
||||||
@@ -203,45 +211,35 @@ const NavItems = ({ isAuthenticated, user }) => {
|
|||||||
高频跟踪
|
高频跟踪
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList minW="260px" p={2}>
|
<MenuList minW="260px" p={2}>
|
||||||
<VStack spacing={1} align="stretch">
|
<MenuItem
|
||||||
<Link
|
onClick={() => navigate('/community')}
|
||||||
onClick={() => navigate('/community')}
|
borderRadius="md"
|
||||||
py={2}
|
bg={location.pathname.includes('/community') ? 'blue.50' : 'transparent'}
|
||||||
px={3}
|
borderLeft={location.pathname.includes('/community') ? '3px solid' : 'none'}
|
||||||
borderRadius="md"
|
borderColor="blue.600"
|
||||||
_hover={{ bg: 'gray.100' }}
|
fontWeight={location.pathname.includes('/community') ? 'bold' : 'normal'}
|
||||||
cursor="pointer"
|
>
|
||||||
bg={location.pathname.includes('/community') ? 'blue.50' : 'transparent'}
|
<Flex justify="space-between" align="center" w="100%">
|
||||||
borderLeft={location.pathname.includes('/community') ? '3px solid' : 'none'}
|
<Text fontSize="sm">新闻催化分析</Text>
|
||||||
borderColor="blue.600"
|
<HStack spacing={1}>
|
||||||
fontWeight={location.pathname.includes('/community') ? 'bold' : 'normal'}
|
<Badge size="sm" colorScheme="green">HOT</Badge>
|
||||||
>
|
|
||||||
<Flex justify="space-between" align="center">
|
|
||||||
<Text fontSize="sm">新闻催化分析</Text>
|
|
||||||
<HStack spacing={1}>
|
|
||||||
<Badge size="sm" colorScheme="green">HOT</Badge>
|
|
||||||
<Badge size="sm" colorScheme="red">NEW</Badge>
|
|
||||||
</HStack>
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
onClick={() => navigate('/concepts')}
|
|
||||||
py={2}
|
|
||||||
px={3}
|
|
||||||
borderRadius="md"
|
|
||||||
_hover={{ bg: 'gray.100' }}
|
|
||||||
cursor="pointer"
|
|
||||||
bg={location.pathname.includes('/concepts') ? 'blue.50' : 'transparent'}
|
|
||||||
borderLeft={location.pathname.includes('/concepts') ? '3px solid' : 'none'}
|
|
||||||
borderColor="blue.600"
|
|
||||||
fontWeight={location.pathname.includes('/concepts') ? 'bold' : 'normal'}
|
|
||||||
>
|
|
||||||
<Flex justify="space-between" align="center">
|
|
||||||
<Text fontSize="sm">概念中心</Text>
|
|
||||||
<Badge size="sm" colorScheme="red">NEW</Badge>
|
<Badge size="sm" colorScheme="red">NEW</Badge>
|
||||||
</Flex>
|
</HStack>
|
||||||
</Link>
|
</Flex>
|
||||||
</VStack>
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => navigate('/concepts')}
|
||||||
|
borderRadius="md"
|
||||||
|
bg={location.pathname.includes('/concepts') ? 'blue.50' : 'transparent'}
|
||||||
|
borderLeft={location.pathname.includes('/concepts') ? '3px solid' : 'none'}
|
||||||
|
borderColor="blue.600"
|
||||||
|
fontWeight={location.pathname.includes('/concepts') ? 'bold' : 'normal'}
|
||||||
|
>
|
||||||
|
<Flex justify="space-between" align="center" w="100%">
|
||||||
|
<Text fontSize="sm">概念中心</Text>
|
||||||
|
<Badge size="sm" colorScheme="red">NEW</Badge>
|
||||||
|
</Flex>
|
||||||
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
@@ -260,59 +258,45 @@ const NavItems = ({ isAuthenticated, user }) => {
|
|||||||
行情复盘
|
行情复盘
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList minW="260px" p={2}>
|
<MenuList minW="260px" p={2}>
|
||||||
<VStack spacing={1} align="stretch">
|
<MenuItem
|
||||||
<Link
|
onClick={() => navigate('/limit-analyse')}
|
||||||
onClick={() => navigate('/limit-analyse')}
|
borderRadius="md"
|
||||||
py={2}
|
bg={location.pathname.includes('/limit-analyse') ? 'blue.50' : 'transparent'}
|
||||||
px={3}
|
borderLeft={location.pathname.includes('/limit-analyse') ? '3px solid' : 'none'}
|
||||||
borderRadius="md"
|
borderColor="blue.600"
|
||||||
_hover={{ bg: 'gray.100' }}
|
fontWeight={location.pathname.includes('/limit-analyse') ? 'bold' : 'normal'}
|
||||||
cursor="pointer"
|
>
|
||||||
bg={location.pathname.includes('/limit-analyse') ? 'blue.50' : 'transparent'}
|
<Flex justify="space-between" align="center" w="100%">
|
||||||
borderLeft={location.pathname.includes('/limit-analyse') ? '3px solid' : 'none'}
|
<Text fontSize="sm">涨停分析</Text>
|
||||||
borderColor="blue.600"
|
<Badge size="sm" colorScheme="blue">FREE</Badge>
|
||||||
fontWeight={location.pathname.includes('/limit-analyse') ? 'bold' : 'normal'}
|
</Flex>
|
||||||
>
|
</MenuItem>
|
||||||
<Flex justify="space-between" align="center">
|
<MenuItem
|
||||||
<Text fontSize="sm">涨停分析</Text>
|
onClick={() => navigate('/stocks')}
|
||||||
<Badge size="sm" colorScheme="blue">FREE</Badge>
|
borderRadius="md"
|
||||||
</Flex>
|
bg={location.pathname.includes('/stocks') ? 'blue.50' : 'transparent'}
|
||||||
</Link>
|
borderLeft={location.pathname.includes('/stocks') ? '3px solid' : 'none'}
|
||||||
<Link
|
borderColor="blue.600"
|
||||||
onClick={() => navigate('/stocks')}
|
fontWeight={location.pathname.includes('/stocks') ? 'bold' : 'normal'}
|
||||||
py={2}
|
>
|
||||||
px={3}
|
<Flex justify="space-between" align="center" w="100%">
|
||||||
borderRadius="md"
|
<Text fontSize="sm">个股中心</Text>
|
||||||
_hover={{ bg: 'gray.100' }}
|
<Badge size="sm" colorScheme="green">HOT</Badge>
|
||||||
cursor="pointer"
|
</Flex>
|
||||||
bg={location.pathname.includes('/stocks') ? 'blue.50' : 'transparent'}
|
</MenuItem>
|
||||||
borderLeft={location.pathname.includes('/stocks') ? '3px solid' : 'none'}
|
<MenuItem
|
||||||
borderColor="blue.600"
|
onClick={() => navigate('/trading-simulation')}
|
||||||
fontWeight={location.pathname.includes('/stocks') ? 'bold' : 'normal'}
|
borderRadius="md"
|
||||||
>
|
bg={location.pathname.includes('/trading-simulation') ? 'blue.50' : 'transparent'}
|
||||||
<Flex justify="space-between" align="center">
|
borderLeft={location.pathname.includes('/trading-simulation') ? '3px solid' : 'none'}
|
||||||
<Text fontSize="sm">个股中心</Text>
|
borderColor="blue.600"
|
||||||
<Badge size="sm" colorScheme="green">HOT</Badge>
|
fontWeight={location.pathname.includes('/trading-simulation') ? 'bold' : 'normal'}
|
||||||
</Flex>
|
>
|
||||||
</Link>
|
<Flex justify="space-between" align="center" w="100%">
|
||||||
<Link
|
<Text fontSize="sm">模拟盘</Text>
|
||||||
onClick={() => navigate('/trading-simulation')}
|
<Badge size="sm" colorScheme="red">NEW</Badge>
|
||||||
py={2}
|
</Flex>
|
||||||
px={3}
|
</MenuItem>
|
||||||
borderRadius="md"
|
|
||||||
_hover={{ bg: 'gray.100' }}
|
|
||||||
cursor="pointer"
|
|
||||||
bg={location.pathname.includes('/trading-simulation') ? 'blue.50' : 'transparent'}
|
|
||||||
borderLeft={location.pathname.includes('/trading-simulation') ? '3px solid' : 'none'}
|
|
||||||
borderColor="blue.600"
|
|
||||||
fontWeight={location.pathname.includes('/trading-simulation') ? 'bold' : 'normal'}
|
|
||||||
>
|
|
||||||
<Flex justify="space-between" align="center">
|
|
||||||
<Text fontSize="sm">模拟盘</Text>
|
|
||||||
<Badge size="sm" colorScheme="red">NEW</Badge>
|
|
||||||
</Flex>
|
|
||||||
</Link>
|
|
||||||
</VStack>
|
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
@@ -327,30 +311,20 @@ const NavItems = ({ isAuthenticated, user }) => {
|
|||||||
AGENT社群
|
AGENT社群
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList minW="300px" p={4}>
|
<MenuList minW="300px" p={4}>
|
||||||
<VStack spacing={2} align="stretch">
|
<MenuItem
|
||||||
<Link
|
isDisabled
|
||||||
py={2}
|
cursor="not-allowed"
|
||||||
px={3}
|
color="gray.400"
|
||||||
borderRadius="md"
|
>
|
||||||
_hover={{}}
|
<Text fontSize="sm" color="gray.400">今日热议</Text>
|
||||||
cursor="not-allowed"
|
</MenuItem>
|
||||||
color="gray.400"
|
<MenuItem
|
||||||
pointerEvents="none"
|
isDisabled
|
||||||
>
|
cursor="not-allowed"
|
||||||
<Text fontSize="sm" color="gray.400">今日热议</Text>
|
color="gray.400"
|
||||||
</Link>
|
>
|
||||||
<Link
|
<Text fontSize="sm" color="gray.400">个股社区</Text>
|
||||||
py={2}
|
</MenuItem>
|
||||||
px={3}
|
|
||||||
borderRadius="md"
|
|
||||||
_hover={{}}
|
|
||||||
cursor="not-allowed"
|
|
||||||
color="gray.400"
|
|
||||||
pointerEvents="none"
|
|
||||||
>
|
|
||||||
<Text fontSize="sm" color="gray.400">个股社区</Text>
|
|
||||||
</Link>
|
|
||||||
</VStack>
|
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
@@ -373,7 +347,7 @@ const NavItems = ({ isAuthenticated, user }) => {
|
|||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 计算 API 基础地址(移到组件外部,避免每次 render 重新创建)
|
// 计算 API 基础地址(移到组件外部,避免每次 render 重新创建)
|
||||||
const getApiBase = () => (process.env.NODE_ENV === 'production' ? '' : (process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001'));
|
const getApiBase = () => (process.env.NODE_ENV === 'production' ? '' : (process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001'));
|
||||||
|
|||||||
27
src/components/ScrollToTop/index.js
Normal file
27
src/components/ScrollToTop/index.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// src/components/ScrollToTop/index.js
|
||||||
|
// 路由切换时自动滚动到页面顶部
|
||||||
|
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScrollToTop - 路由切换时自动滚动到顶部
|
||||||
|
*
|
||||||
|
* 使用方式:在 App.js 的 Router 内部添加此组件
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <BrowserRouter>
|
||||||
|
* <ScrollToTop />
|
||||||
|
* <Routes>...</Routes>
|
||||||
|
* </BrowserRouter>
|
||||||
|
*/
|
||||||
|
export default function ScrollToTop() {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 路径改变时滚动到顶部
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@ import React from "react";
|
|||||||
import { Routes, Route } from "react-router-dom";
|
import { Routes, Route } from "react-router-dom";
|
||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
|
|
||||||
// 导入导航栏组件 - 使用我们之前修复的版本
|
// 导航栏已由 MainLayout 提供,此处不再导入
|
||||||
import HomeNavbar from "../components/Navbars/HomeNavbar";
|
// import HomeNavbar from "../components/Navbars/HomeNavbar";
|
||||||
|
|
||||||
// 导入页面组件
|
// 导入页面组件
|
||||||
import HomePage from "views/Home/HomePage";
|
import HomePage from "views/Home/HomePage";
|
||||||
@@ -25,8 +25,7 @@ import ProtectedRoute from "../components/ProtectedRoute";
|
|||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh">
|
<Box minH="100vh">
|
||||||
{/* 导航栏 - 这是关键,确保HomeNavbar被正确包含 */}
|
{/* 导航栏已由 MainLayout 提供,此处不再渲染 */}
|
||||||
<HomeNavbar />
|
|
||||||
|
|
||||||
{/* 主要内容区域 */}
|
{/* 主要内容区域 */}
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
31
src/layouts/MainLayout.js
Normal file
31
src/layouts/MainLayout.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// src/layouts/MainLayout.js
|
||||||
|
// 主布局组件 - 为所有带导航栏的页面提供统一布局
|
||||||
|
import React, { Suspense } from "react";
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import { Box } from '@chakra-ui/react';
|
||||||
|
import HomeNavbar from "../components/Navbars/HomeNavbar";
|
||||||
|
import PageLoader from "../components/Loading/PageLoader";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MainLayout - 带导航栏的主布局
|
||||||
|
*
|
||||||
|
* 使用 <Outlet /> 渲染子路由,确保导航栏只渲染一次
|
||||||
|
* 页面切换时只有 Outlet 内的内容会更新,导航栏保持不变
|
||||||
|
* Suspense 边界确保导航栏始终可见,只有内容区域显示 loading
|
||||||
|
*/
|
||||||
|
export default function MainLayout() {
|
||||||
|
return (
|
||||||
|
<Box minH="100vh">
|
||||||
|
{/* 导航栏 - 在所有页面间共享,不会重新渲染 */}
|
||||||
|
<HomeNavbar />
|
||||||
|
|
||||||
|
{/* 页面内容区域 - 通过 Outlet 渲染当前路由对应的组件 */}
|
||||||
|
{/* Suspense 只包裹内容区域,导航栏保持可见 */}
|
||||||
|
<Box>
|
||||||
|
<Suspense fallback={<PageLoader message="页面加载中..." />}>
|
||||||
|
<Outlet />
|
||||||
|
</Suspense>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
172
src/mocks/data/events.js
Normal file
172
src/mocks/data/events.js
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
// Mock 事件相关数据
|
||||||
|
|
||||||
|
// Mock 股票池 - 常见的A股股票
|
||||||
|
const stockPool = [
|
||||||
|
{ stock_code: '600000.SH', stock_name: '浦发银行', industry: '银行' },
|
||||||
|
{ stock_code: '600519.SH', stock_name: '贵州茅台', industry: '白酒' },
|
||||||
|
{ stock_code: '600036.SH', stock_name: '招商银行', industry: '银行' },
|
||||||
|
{ stock_code: '601318.SH', stock_name: '中国平安', industry: '保险' },
|
||||||
|
{ stock_code: '600016.SH', stock_name: '民生银行', industry: '银行' },
|
||||||
|
{ stock_code: '601398.SH', stock_name: '工商银行', industry: '银行' },
|
||||||
|
{ stock_code: '601288.SH', stock_name: '农业银行', industry: '银行' },
|
||||||
|
{ stock_code: '601166.SH', stock_name: '兴业银行', industry: '银行' },
|
||||||
|
{ stock_code: '000001.SZ', stock_name: '平安银行', industry: '银行' },
|
||||||
|
{ stock_code: '000002.SZ', stock_name: '万科A', industry: '房地产' },
|
||||||
|
{ stock_code: '000858.SZ', stock_name: '五粮液', industry: '白酒' },
|
||||||
|
{ stock_code: '000333.SZ', stock_name: '美的集团', industry: '家电' },
|
||||||
|
{ stock_code: '002415.SZ', stock_name: '海康威视', industry: '安防' },
|
||||||
|
{ stock_code: '002594.SZ', stock_name: 'BYD比亚迪', industry: '新能源汽车' },
|
||||||
|
{ stock_code: '300750.SZ', stock_name: '宁德时代', industry: '新能源' },
|
||||||
|
{ stock_code: '300059.SZ', stock_name: '东方财富', industry: '证券' },
|
||||||
|
{ stock_code: '601888.SH', stock_name: '中国中免', industry: '免税' },
|
||||||
|
{ stock_code: '600276.SH', stock_name: '恒瑞医药', industry: '医药' },
|
||||||
|
{ stock_code: '600887.SH', stock_name: '伊利股份', industry: '乳制品' },
|
||||||
|
{ stock_code: '601012.SH', stock_name: '隆基绿能', industry: '光伏' },
|
||||||
|
{ stock_code: '688981.SH', stock_name: '中芯国际', industry: '半导体' },
|
||||||
|
{ stock_code: '600309.SH', stock_name: '万华化学', industry: '化工' },
|
||||||
|
{ stock_code: '603259.SH', stock_name: '药明康德', industry: '医药研发' },
|
||||||
|
{ stock_code: '002475.SZ', stock_name: '立讯精密', industry: '电子' },
|
||||||
|
{ stock_code: '000063.SZ', stock_name: '中兴通讯', industry: '通信设备' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 关联描述模板
|
||||||
|
const relationDescTemplates = [
|
||||||
|
'主营业务直接相关,预计受事件影响较大',
|
||||||
|
'产业链上游企业,间接受益',
|
||||||
|
'产业链下游企业,需求端受影响',
|
||||||
|
'同行业竞争对手,可能受到间接影响',
|
||||||
|
'参股该领域相关企业,有一定关联性',
|
||||||
|
'业务板块涉及相关领域,预计有正面影响',
|
||||||
|
'控股子公司从事相关业务',
|
||||||
|
'近期公告布局该领域,潜在受益标的',
|
||||||
|
'行业龙头企业,市场关注度高',
|
||||||
|
'技术储备充足,有望抢占市场先机',
|
||||||
|
'已有成熟产品线,短期内可能受益',
|
||||||
|
'战略转型方向与事件相关',
|
||||||
|
];
|
||||||
|
|
||||||
|
// 生成随机关联股票数据
|
||||||
|
export function generateRelatedStocks(eventId, count = 5) {
|
||||||
|
// 使用事件ID作为随机种子,确保相同事件ID返回相同的股票列表
|
||||||
|
const seed = parseInt(eventId) || 1;
|
||||||
|
const selectedStocks = [];
|
||||||
|
|
||||||
|
// 伪随机选择股票(基于事件ID)
|
||||||
|
for (let i = 0; i < Math.min(count, stockPool.length); i++) {
|
||||||
|
const index = (seed * 17 + i * 13) % stockPool.length;
|
||||||
|
const stock = stockPool[index];
|
||||||
|
const descIndex = (seed * 7 + i * 11) % relationDescTemplates.length;
|
||||||
|
|
||||||
|
selectedStocks.push({
|
||||||
|
stock_code: stock.stock_code,
|
||||||
|
stock_name: stock.stock_name,
|
||||||
|
relation_desc: relationDescTemplates[descIndex],
|
||||||
|
industry: stock.industry,
|
||||||
|
// 可选字段 - 用于前端显示更多信息
|
||||||
|
relevance_score: Math.max(60, 100 - i * 8), // 相关性评分,递减
|
||||||
|
impact_level: i < 2 ? 'high' : i < 4 ? 'medium' : 'low', // 影响程度
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedStocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock 事件相关股票数据映射
|
||||||
|
// 这里可以为特定事件ID预设特定的股票列表
|
||||||
|
export const mockEventStocks = {
|
||||||
|
// 示例:事件ID为1的预设股票
|
||||||
|
'1': [
|
||||||
|
{
|
||||||
|
stock_code: '600519.SH',
|
||||||
|
stock_name: '贵州茅台',
|
||||||
|
relation_desc: '白酒行业龙头,消费板块受政策影响',
|
||||||
|
industry: '白酒',
|
||||||
|
relevance_score: 95,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '000858.SZ',
|
||||||
|
stock_name: '五粮液',
|
||||||
|
relation_desc: '白酒行业第二梯队,同样受消费政策影响',
|
||||||
|
industry: '白酒',
|
||||||
|
relevance_score: 90,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '600887.SH',
|
||||||
|
stock_name: '伊利股份',
|
||||||
|
relation_desc: '消费品龙头,受益于消费复苏',
|
||||||
|
industry: '乳制品',
|
||||||
|
relevance_score: 75,
|
||||||
|
impact_level: 'medium',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// 事件ID为2的预设股票(科技类)
|
||||||
|
'2': [
|
||||||
|
{
|
||||||
|
stock_code: '002415.SZ',
|
||||||
|
stock_name: '海康威视',
|
||||||
|
relation_desc: 'AI视觉领域龙头,技术创新受政策支持',
|
||||||
|
industry: '安防',
|
||||||
|
relevance_score: 92,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '000063.SZ',
|
||||||
|
stock_name: '中兴通讯',
|
||||||
|
relation_desc: '5G通信设备商,基础设施建设受益',
|
||||||
|
industry: '通信设备',
|
||||||
|
relevance_score: 88,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '688981.SH',
|
||||||
|
stock_name: '中芯国际',
|
||||||
|
relation_desc: '半导体制造龙头,国产替代核心标的',
|
||||||
|
industry: '半导体',
|
||||||
|
relevance_score: 85,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// 事件ID为3的预设股票(新能源类)
|
||||||
|
'3': [
|
||||||
|
{
|
||||||
|
stock_code: '300750.SZ',
|
||||||
|
stock_name: '宁德时代',
|
||||||
|
relation_desc: '动力电池龙头,新能源产业链核心',
|
||||||
|
industry: '新能源',
|
||||||
|
relevance_score: 98,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '002594.SZ',
|
||||||
|
stock_name: 'BYD比亚迪',
|
||||||
|
relation_desc: '新能源汽车龙头,产业链一体化布局',
|
||||||
|
industry: '新能源汽车',
|
||||||
|
relevance_score: 95,
|
||||||
|
impact_level: 'high',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stock_code: '601012.SH',
|
||||||
|
stock_name: '隆基绿能',
|
||||||
|
relation_desc: '光伏组件龙头,清洁能源受政策支持',
|
||||||
|
industry: '光伏',
|
||||||
|
relevance_score: 85,
|
||||||
|
impact_level: 'medium',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取事件相关股票
|
||||||
|
export function getEventRelatedStocks(eventId) {
|
||||||
|
// 优先返回预设的股票列表
|
||||||
|
if (mockEventStocks[eventId]) {
|
||||||
|
return mockEventStocks[eventId];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则生成随机股票列表(3-6只股票)
|
||||||
|
const count = 3 + (parseInt(eventId) % 4);
|
||||||
|
return generateRelatedStocks(eventId, count);
|
||||||
|
}
|
||||||
@@ -86,22 +86,33 @@ export function generateWechatSessionId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 当前登录用户状态管理 ====================
|
// ==================== 当前登录用户状态管理 ====================
|
||||||
// 用于跟踪当前登录的用户(Mock 模式下的全局状态)
|
// Mock 模式下使用 localStorage 持久化登录状态
|
||||||
let currentLoggedInUser = null;
|
|
||||||
|
|
||||||
// 设置当前登录用户
|
// 设置当前登录用户
|
||||||
export function setCurrentUser(user) {
|
export function setCurrentUser(user) {
|
||||||
currentLoggedInUser = user;
|
if (user) {
|
||||||
console.log('[Mock State] 设置当前登录用户:', user);
|
localStorage.setItem('mock_current_user', JSON.stringify(user));
|
||||||
|
console.log('[Mock State] 设置当前登录用户:', user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前登录用户
|
// 获取当前登录用户
|
||||||
export function getCurrentUser() {
|
export function getCurrentUser() {
|
||||||
return currentLoggedInUser;
|
try {
|
||||||
|
const stored = localStorage.getItem('mock_current_user');
|
||||||
|
if (stored) {
|
||||||
|
const user = JSON.parse(stored);
|
||||||
|
console.log('[Mock State] 获取当前登录用户:', user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Mock State] 解析用户数据失败:', error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除当前登录用户
|
// 清除当前登录用户
|
||||||
export function clearCurrentUser() {
|
export function clearCurrentUser() {
|
||||||
currentLoggedInUser = null;
|
localStorage.removeItem('mock_current_user');
|
||||||
console.log('[Mock State] 清除当前登录用户');
|
console.log('[Mock State] 清除当前登录用户');
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/mocks/handlers/event.js
Normal file
39
src/mocks/handlers/event.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// src/mocks/handlers/event.js
|
||||||
|
// 事件相关的 Mock API Handlers
|
||||||
|
|
||||||
|
import { http, HttpResponse } from 'msw';
|
||||||
|
import { getEventRelatedStocks } from '../data/events';
|
||||||
|
|
||||||
|
// 模拟网络延迟
|
||||||
|
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
export const eventHandlers = [
|
||||||
|
// 获取事件相关股票
|
||||||
|
http.get('/api/events/:eventId/stocks', async ({ params }) => {
|
||||||
|
await delay(300);
|
||||||
|
|
||||||
|
const { eventId } = params;
|
||||||
|
|
||||||
|
console.log('[Mock] 获取事件相关股票, eventId:', eventId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const relatedStocks = getEventRelatedStocks(eventId);
|
||||||
|
|
||||||
|
return HttpResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: relatedStocks,
|
||||||
|
message: '获取成功'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Mock] 获取事件相关股票失败:', error);
|
||||||
|
return HttpResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: '获取事件相关股票失败',
|
||||||
|
data: []
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
@@ -4,15 +4,15 @@
|
|||||||
import { authHandlers } from './auth';
|
import { authHandlers } from './auth';
|
||||||
import { accountHandlers } from './account';
|
import { accountHandlers } from './account';
|
||||||
import { simulationHandlers } from './simulation';
|
import { simulationHandlers } from './simulation';
|
||||||
|
import { eventHandlers } from './event';
|
||||||
|
|
||||||
// 可以在这里添加更多的 handlers
|
// 可以在这里添加更多的 handlers
|
||||||
// import { userHandlers } from './user';
|
// import { userHandlers } from './user';
|
||||||
// import { eventHandlers } from './event';
|
|
||||||
|
|
||||||
export const handlers = [
|
export const handlers = [
|
||||||
...authHandlers,
|
...authHandlers,
|
||||||
...accountHandlers,
|
...accountHandlers,
|
||||||
...simulationHandlers,
|
...simulationHandlers,
|
||||||
|
...eventHandlers,
|
||||||
// ...userHandlers,
|
// ...userHandlers,
|
||||||
// ...eventHandlers,
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ import ImportanceLegend from './components/ImportanceLegend';
|
|||||||
import InvestmentCalendar from './components/InvestmentCalendar';
|
import InvestmentCalendar from './components/InvestmentCalendar';
|
||||||
import { eventService } from '../../services/eventService';
|
import { eventService } from '../../services/eventService';
|
||||||
|
|
||||||
// 导入导航栏组件 (如果需要保留原有的导航栏)
|
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||||
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
|
||||||
|
|
||||||
const filterLabelMap = {
|
const filterLabelMap = {
|
||||||
date_range: v => v ? `日期: ${v}` : '',
|
date_range: v => v ? `日期: ${v}` : '',
|
||||||
@@ -266,8 +265,7 @@ const Community = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh" bg={bgColor}>
|
<Box minH="100vh" bg={bgColor}>
|
||||||
{/* 导航栏 - 可以保留原有的或改用Chakra UI版本 */}
|
{/* 导航栏已由 MainLayout 提供 */}
|
||||||
<HomeNavbar />
|
|
||||||
|
|
||||||
{/* Midjourney风格英雄区域 */}
|
{/* Midjourney风格英雄区域 */}
|
||||||
<MidjourneyHeroSection />
|
<MidjourneyHeroSection />
|
||||||
|
|||||||
@@ -83,8 +83,7 @@ import { BsGraphUp, BsLightningFill } from 'react-icons/bs';
|
|||||||
import { keyframes } from '@emotion/react';
|
import { keyframes } from '@emotion/react';
|
||||||
import ConceptTimelineModal from './ConceptTimelineModal';
|
import ConceptTimelineModal from './ConceptTimelineModal';
|
||||||
import ConceptStatsPanel from './components/ConceptStatsPanel';
|
import ConceptStatsPanel from './components/ConceptStatsPanel';
|
||||||
// 导入导航栏组件
|
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||||
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
|
||||||
// 导入订阅权限管理
|
// 导入订阅权限管理
|
||||||
import { useSubscription } from '../../hooks/useSubscription';
|
import { useSubscription } from '../../hooks/useSubscription';
|
||||||
import SubscriptionUpgradeModal from '../../components/SubscriptionUpgradeModal';
|
import SubscriptionUpgradeModal from '../../components/SubscriptionUpgradeModal';
|
||||||
@@ -1080,8 +1079,7 @@ const ConceptCenter = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh" bg="gray.50">
|
<Box minH="100vh" bg="gray.50">
|
||||||
{/* 导航栏 */}
|
{/* 导航栏已由 MainLayout 提供 */}
|
||||||
<HomeNavbar />
|
|
||||||
|
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ import SectorDetails from './components/SectorDetails';
|
|||||||
import { DataAnalysis, StockDetailModal } from './components/DataVisualizationComponents';
|
import { DataAnalysis, StockDetailModal } from './components/DataVisualizationComponents';
|
||||||
import { AdvancedSearch, SearchResultsModal } from './components/SearchComponents';
|
import { AdvancedSearch, SearchResultsModal } from './components/SearchComponents';
|
||||||
|
|
||||||
// 导入导航栏组件
|
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||||
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
|
||||||
|
|
||||||
// 导入高位股统计组件
|
// 导入高位股统计组件
|
||||||
import HighPositionStocks from './components/HighPositionStocks';
|
import HighPositionStocks from './components/HighPositionStocks';
|
||||||
@@ -303,9 +302,8 @@ export default function LimitAnalyse() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh" bg={bgColor}>
|
<Box minH="100vh" bg={bgColor}>
|
||||||
{/* 导航栏 */}
|
{/* 导航栏已由 MainLayout 提供 */}
|
||||||
<HomeNavbar />
|
|
||||||
|
|
||||||
{/* 顶部Header */}
|
{/* 顶部Header */}
|
||||||
<Box bgGradient="linear(to-br, blue.500, purple.600)" color="white" py={8}>
|
<Box bgGradient="linear(to-br, blue.500, purple.600)" color="white" py={8}>
|
||||||
<Container maxW="container.xl">
|
<Container maxW="container.xl">
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ import { FaChartLine, FaFire, FaRocket, FaBrain, FaCalendarAlt, FaChevronRight,
|
|||||||
import { BsGraphUp, BsLightningFill } from 'react-icons/bs';
|
import { BsGraphUp, BsLightningFill } from 'react-icons/bs';
|
||||||
import { keyframes } from '@emotion/react';
|
import { keyframes } from '@emotion/react';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
// Navigation bar now provided by MainLayout
|
||||||
|
// import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
||||||
|
|
||||||
// 动画定义
|
// 动画定义
|
||||||
const pulseAnimation = keyframes`
|
const pulseAnimation = keyframes`
|
||||||
@@ -524,10 +525,9 @@ const StockOverview = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh" bg={bgColor}>
|
<Box minH="100vh" bg={bgColor}>
|
||||||
{/* 导航栏 */}
|
{/* 导航栏已由 MainLayout 提供 */}
|
||||||
<HomeNavbar />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ import MarginTrading from './components/MarginTrading';
|
|||||||
// 导入现有的高质量组件
|
// 导入现有的高质量组件
|
||||||
import LineChart from '../../components/Charts/LineChart';
|
import LineChart from '../../components/Charts/LineChart';
|
||||||
|
|
||||||
// 导入导航栏组件
|
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||||
import HomeNavbar from '../../components/Navbars/HomeNavbar';
|
|
||||||
|
|
||||||
// 模拟盘账户管理 Hook
|
// 模拟盘账户管理 Hook
|
||||||
import { useTradingAccount } from './hooks/useTradingAccount';
|
import { useTradingAccount } from './hooks/useTradingAccount';
|
||||||
@@ -221,9 +220,7 @@ export default function TradingSimulation() {
|
|||||||
// ========== 5. 主要渲染逻辑 ==========
|
// ========== 5. 主要渲染逻辑 ==========
|
||||||
return (
|
return (
|
||||||
<Box minH="100vh" bg={bgColor}>
|
<Box minH="100vh" bg={bgColor}>
|
||||||
{/* 导航栏 */}
|
{/* 导航栏已由 MainLayout 提供 */}
|
||||||
<HomeNavbar />
|
|
||||||
|
|
||||||
<Container maxW="7xl" py={8}>
|
<Container maxW="7xl" py={8}>
|
||||||
{!isAuthenticated ? (
|
{!isAuthenticated ? (
|
||||||
<Alert status="warning">
|
<Alert status="warning">
|
||||||
|
|||||||
Reference in New Issue
Block a user