refactor(Center): 大幅简化,移除侧边栏逻辑
- 移除 WatchSidebar 相关代码(已移至全局 GlobalSidebar) - 移除数据加载逻辑(由 GlobalSidebarContext 统一管理) - 移除 useAuth、useLocation、useNavigate 等依赖 - 保留核心功能:MarketDashboard、ForumCenter、InvestmentPlanningCenter - 代码从 ~260 行精简至 ~40 行 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,223 +5,24 @@
|
||||
* 功能:自选股监控、关注事件、投资规划等
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { logger } from '@/utils/logger';
|
||||
import { getApiBase } from '@/utils/apiConfig';
|
||||
import { useDashboardEvents } from '@/hooks/useDashboardEvents';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Text,
|
||||
VStack,
|
||||
useToast,
|
||||
Spinner,
|
||||
Center,
|
||||
} from '@chakra-ui/react';
|
||||
import { useCenterColors } from './hooks';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import InvestmentPlanningCenter from './components/InvestmentPlanningCenter';
|
||||
import { getEventDetailUrl } from '@/utils/idEncoder';
|
||||
import MarketDashboard from '@views/Profile/components/MarketDashboard';
|
||||
import ForumCenter from '@views/Profile/components/ForumCenter';
|
||||
import WatchSidebar from '@views/Profile/components/WatchSidebar';
|
||||
import { THEME } from '@views/Profile/components/MarketDashboard/constants';
|
||||
|
||||
import type {
|
||||
WatchlistItem,
|
||||
RealtimeQuotesMap,
|
||||
FollowingEvent,
|
||||
EventComment,
|
||||
WatchlistApiResponse,
|
||||
RealtimeQuotesApiResponse,
|
||||
FollowingEventsApiResponse,
|
||||
EventCommentsApiResponse,
|
||||
DashboardEventsResult,
|
||||
} from '@/types';
|
||||
|
||||
/**
|
||||
* CenterDashboard 组件
|
||||
* 个人中心仪表板主页面
|
||||
*
|
||||
* 注意:右侧 WatchSidebar 已移至全局 GlobalSidebar(在 MainLayout 中渲染)
|
||||
*/
|
||||
const CenterDashboard: React.FC = () => {
|
||||
const { user } = useAuth();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
|
||||
// 提取 userId 为独立变量(优化依赖项)
|
||||
const userId = user?.id;
|
||||
|
||||
// 初始化 Dashboard 埋点 Hook(类型断言为 DashboardEventsResult)
|
||||
const dashboardEvents = useDashboardEvents({
|
||||
pageType: 'center',
|
||||
navigate
|
||||
}) as DashboardEventsResult;
|
||||
|
||||
// 颜色主题(使用 useCenterColors 封装,避免 7 次 useColorModeValue 调用)
|
||||
const { secondaryText } = useCenterColors();
|
||||
|
||||
// 数据状态
|
||||
const [watchlist, setWatchlist] = useState<WatchlistItem[]>([]);
|
||||
const [realtimeQuotes, setRealtimeQuotes] = useState<RealtimeQuotesMap>({});
|
||||
const [followingEvents, setFollowingEvents] = useState<FollowingEvent[]>([]);
|
||||
const [eventComments, setEventComments] = useState<EventComment[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [quotesLoading, setQuotesLoading] = useState<boolean>(false);
|
||||
|
||||
// 使用 ref 跟踪是否已经加载过数据(首次加载标记)
|
||||
const hasLoadedRef = useRef<boolean>(false);
|
||||
|
||||
/**
|
||||
* 加载实时行情
|
||||
*/
|
||||
const loadRealtimeQuotes = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
setQuotesLoading(true);
|
||||
const base = getApiBase();
|
||||
const response = await fetch(base + '/api/account/watchlist/realtime', {
|
||||
credentials: 'include',
|
||||
cache: 'no-store'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data: RealtimeQuotesApiResponse = await response.json();
|
||||
if (data.success) {
|
||||
const quotesMap: RealtimeQuotesMap = {};
|
||||
data.data.forEach(item => {
|
||||
quotesMap[item.stock_code] = item;
|
||||
});
|
||||
setRealtimeQuotes(quotesMap);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Center', 'loadRealtimeQuotes', error, {
|
||||
userId,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} finally {
|
||||
setQuotesLoading(false);
|
||||
}
|
||||
}, [userId]);
|
||||
|
||||
/**
|
||||
* 加载所有数据(自选股、关注事件、评论)
|
||||
*/
|
||||
const loadData = useCallback(async (): Promise<void> => {
|
||||
try {
|
||||
const base = getApiBase();
|
||||
const ts = Date.now();
|
||||
|
||||
const [w, e, c] = await Promise.all([
|
||||
fetch(base + `/api/account/watchlist?_=${ts}`, { credentials: 'include', cache: 'no-store' }),
|
||||
fetch(base + `/api/account/events/following?_=${ts}`, { credentials: 'include', cache: 'no-store' }),
|
||||
fetch(base + `/api/account/events/posts?_=${ts}`, { credentials: 'include', cache: 'no-store' }),
|
||||
]);
|
||||
|
||||
const jw: WatchlistApiResponse = await w.json();
|
||||
const je: FollowingEventsApiResponse = await e.json();
|
||||
const jc: EventCommentsApiResponse = await c.json();
|
||||
|
||||
if (jw.success) {
|
||||
const watchlistData = Array.isArray(jw.data) ? jw.data : [];
|
||||
setWatchlist(watchlistData);
|
||||
|
||||
// 追踪自选股列表查看
|
||||
if (watchlistData.length > 0) {
|
||||
dashboardEvents.trackWatchlistViewed(watchlistData.length, true);
|
||||
}
|
||||
|
||||
// 加载实时行情
|
||||
if (jw.data && jw.data.length > 0) {
|
||||
loadRealtimeQuotes();
|
||||
}
|
||||
}
|
||||
|
||||
if (je.success) {
|
||||
const eventsData = Array.isArray(je.data) ? je.data : [];
|
||||
setFollowingEvents(eventsData);
|
||||
|
||||
// 追踪关注的事件列表查看
|
||||
dashboardEvents.trackFollowingEventsViewed(eventsData.length);
|
||||
}
|
||||
|
||||
if (jc.success) {
|
||||
const commentsData = Array.isArray(jc.data) ? jc.data : [];
|
||||
setEventComments(commentsData);
|
||||
|
||||
// 追踪评论列表查看
|
||||
dashboardEvents.trackCommentsViewed(commentsData.length);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Center', 'loadData', err, {
|
||||
userId,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [userId, loadRealtimeQuotes, dashboardEvents]);
|
||||
|
||||
// 首次加载和页面可见性变化时加载数据
|
||||
useEffect(() => {
|
||||
const isOnCenterPage = location.pathname.includes('/home/center');
|
||||
|
||||
// 首次进入页面且有用户时加载数据
|
||||
if (user && isOnCenterPage && !hasLoadedRef.current) {
|
||||
console.log('[Center] 🚀 首次加载数据');
|
||||
hasLoadedRef.current = true;
|
||||
loadData();
|
||||
}
|
||||
|
||||
const onVis = (): void => {
|
||||
if (document.visibilityState === 'visible' && location.pathname.includes('/home/center')) {
|
||||
console.log('[Center] 👁️ visibilitychange 触发 loadData');
|
||||
loadData();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', onVis);
|
||||
return () => document.removeEventListener('visibilitychange', onVis);
|
||||
}, [userId, location.pathname, loadData, user]);
|
||||
|
||||
// 当用户登出再登入(userId 变化)时,重置加载标记
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
hasLoadedRef.current = false;
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
// 定时刷新实时行情(每分钟一次)
|
||||
useEffect(() => {
|
||||
if (watchlist.length > 0) {
|
||||
const interval = setInterval(() => {
|
||||
loadRealtimeQuotes();
|
||||
}, 60000); // 60秒刷新一次
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [watchlist.length, loadRealtimeQuotes]);
|
||||
|
||||
// 渲染加载状态
|
||||
if (loading) {
|
||||
return (
|
||||
<Center h="60vh">
|
||||
<VStack spacing={4}>
|
||||
<Spinner size="xl" color="blue.500" thickness="4px" />
|
||||
<Text color={secondaryText}>加载个人中心数据...</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box bg={THEME.bg.primary} minH="100vh" overflowX="hidden">
|
||||
<Box px={{ base: 3, md: 4 }} py={{ base: 4, md: 6 }} maxW="container.xl" mx="auto">
|
||||
{/* 左右布局:左侧自适应,右侧固定200px */}
|
||||
<Flex gap={4}>
|
||||
{/* 左侧主内容区 */}
|
||||
<Box flex={1} minW={0}>
|
||||
{/* 市场概览仪表盘 */}
|
||||
<Box mb={4}>
|
||||
<MarketDashboard />
|
||||
@@ -237,30 +38,6 @@ const CenterDashboard: React.FC = () => {
|
||||
<InvestmentPlanningCenter />
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* 右侧固定宽度侧边栏 */}
|
||||
<Box
|
||||
w={{ base: '100%', md: '300px' }}
|
||||
flexShrink={0}
|
||||
display={{ base: 'none', md: 'block' }}
|
||||
position="sticky"
|
||||
top="80px"
|
||||
alignSelf="flex-start"
|
||||
>
|
||||
<WatchSidebar
|
||||
watchlist={watchlist}
|
||||
realtimeQuotes={realtimeQuotes}
|
||||
followingEvents={followingEvents}
|
||||
eventComments={eventComments}
|
||||
onStockClick={(stock: WatchlistItem) => navigate(`/company/${stock.stock_code}`)}
|
||||
onEventClick={(event: FollowingEvent) => navigate(getEventDetailUrl(event.id))}
|
||||
onCommentClick={(comment: EventComment) => navigate(getEventDetailUrl(comment.event_id))}
|
||||
onAddStock={() => navigate('/stocks')}
|
||||
onAddEvent={() => navigate('/community')}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user