diff --git a/src/views/Dashboard/components/InvestmentPlanningCenter.tsx b/src/views/Dashboard/components/InvestmentPlanningCenter.tsx new file mode 100644 index 00000000..f6fcb756 --- /dev/null +++ b/src/views/Dashboard/components/InvestmentPlanningCenter.tsx @@ -0,0 +1,203 @@ +/** + * InvestmentPlanningCenter - 投资规划中心主组件 (TypeScript 重构版) + * + * 性能优化: + * - 使用 React.lazy() 懒加载子面板,减少初始加载时间 + * - 从 1421 行拆分为 5 个独立模块,提升可维护性 + * - 使用 TypeScript 提供类型安全 + * + * 组件架构: + * - InvestmentPlanningCenter (主组件,~200 行) + * - CalendarPanel (日历面板,懒加载) + * - PlansPanel (计划面板,懒加载) + * - ReviewsPanel (复盘面板,懒加载) + * - PlanningContext (数据共享层) + */ + +import React, { useState, useEffect, useCallback, Suspense, lazy } from 'react'; +import { + Box, + Card, + CardHeader, + CardBody, + Heading, + HStack, + Flex, + Icon, + useColorModeValue, + useToast, + Tabs, + TabList, + TabPanels, + Tab, + TabPanel, + Spinner, + Center, +} from '@chakra-ui/react'; +import { + FiCalendar, + FiTarget, + FiFileText, +} from 'react-icons/fi'; + +import { PlanningDataProvider } from './PlanningContext'; +import type { InvestmentEvent, PlanningContextValue } from '@/types'; +import { logger } from '@/utils/logger'; +import { getApiBase } from '@/utils/apiConfig'; +import './InvestmentCalendar.css'; + +// 懒加载子面板组件(实现代码分割) +const CalendarPanel = lazy(() => + import('./CalendarPanel').then(module => ({ default: module.CalendarPanel })) +); +const PlansPanel = lazy(() => + import('./PlansPanel').then(module => ({ default: module.PlansPanel })) +); +const ReviewsPanel = lazy(() => + import('./ReviewsPanel').then(module => ({ default: module.ReviewsPanel })) +); + +/** + * 面板加载占位符 + */ +const PanelLoadingFallback: React.FC = () => ( +
+ +
+); + +/** + * InvestmentPlanningCenter 主组件 + */ +const InvestmentPlanningCenter: React.FC = () => { + const toast = useToast(); + + // 颜色主题 + const bgColor = useColorModeValue('white', 'gray.800'); + const borderColor = useColorModeValue('gray.200', 'gray.600'); + const textColor = useColorModeValue('gray.700', 'white'); + const secondaryText = useColorModeValue('gray.600', 'gray.400'); + const cardBg = useColorModeValue('gray.50', 'gray.700'); + + // 全局数据状态 + const [allEvents, setAllEvents] = useState([]); + const [loading, setLoading] = useState(false); + const [activeTab, setActiveTab] = useState(0); + + /** + * 加载所有事件数据(日历事件 + 计划 + 复盘) + */ + const loadAllData = useCallback(async (): Promise => { + try { + setLoading(true); + const base = getApiBase(); + + const response = await fetch(base + '/api/account/calendar/events', { + credentials: 'include' + }); + + if (response.ok) { + const data = await response.json(); + if (data.success) { + setAllEvents(data.data || []); + logger.debug('InvestmentPlanningCenter', '数据加载成功', { + count: data.data?.length || 0 + }); + } + } + } catch (error) { + logger.error('InvestmentPlanningCenter', 'loadAllData', error); + } finally { + setLoading(false); + } + }, []); + + // 组件挂载时加载数据 + useEffect(() => { + loadAllData(); + }, [loadAllData]); + + // 提供给子组件的 Context 值 + const contextValue: PlanningContextValue = { + allEvents, + setAllEvents, + loadAllData, + loading, + setLoading, + activeTab, + setActiveTab, + toast, + bgColor, + borderColor, + textColor, + secondaryText, + cardBg, + }; + + // 计算各类型事件数量 + const planCount = allEvents.filter(e => e.type === 'plan').length; + const reviewCount = allEvents.filter(e => e.type === 'review').length; + + return ( + + + + + + + 投资规划中心 + + + + + + + + + 日历视图 + + + + 我的计划 ({planCount}) + + + + 我的复盘 ({reviewCount}) + + + + + {/* 日历视图面板 */} + + }> + + + + + {/* 计划列表面板 */} + + }> + + + + + {/* 复盘列表面板 */} + + }> + + + + + + + + + ); +}; + +export default InvestmentPlanningCenter;