/** * InvestmentPlanningCenter - 投资规划中心主组件 (TypeScript 重构版) * * 性能优化: * - 使用 React.lazy() 懒加载子面板,减少初始加载时间 * - 使用 TypeScript 提供类型安全 * * 组件架构: * - InvestmentPlanningCenter (主组件) * - CalendarPanel (日历面板,懒加载) * - EventPanel (通用事件面板,用于计划和复盘) * - PlanningContext (数据共享层) */ import React, { useState, useEffect, useCallback, useMemo, Suspense, lazy } from 'react'; import { Box, Card, CardHeader, CardBody, Heading, HStack, Flex, Icon, useColorModeValue, useToast, Tabs, TabList, TabPanels, Tab, TabPanel, Spinner, Center, Button, ButtonGroup, } from '@chakra-ui/react'; import { FiCalendar, FiTarget, FiFileText, FiList, FiPlus, } 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.less'; // 懒加载子面板组件(实现代码分割) const CalendarPanel = lazy(() => import('./CalendarPanel').then(module => ({ default: module.CalendarPanel })) ); const EventPanel = lazy(() => import('./EventPanel').then(module => ({ default: module.EventPanel })) ); /** * 面板加载占位符 */ 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 [viewMode, setViewMode] = useState<'calendar' | 'list'>('list'); const [listTab, setListTab] = useState(0); // 0: 我的计划, 1: 我的复盘 const [openPlanModalTrigger, setOpenPlanModalTrigger] = useState(0); const [openReviewModalTrigger, setOpenReviewModalTrigger] = 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 值(使用 useMemo 缓存,避免子组件不必要的重渲染) const contextValue: PlanningContextValue = useMemo( () => ({ allEvents, setAllEvents, loadAllData, loading, setLoading, openPlanModalTrigger, openReviewModalTrigger, toast, borderColor, textColor, secondaryText, cardBg, setViewMode, setListTab, }), [ allEvents, loadAllData, loading, openPlanModalTrigger, openReviewModalTrigger, toast, borderColor, textColor, secondaryText, cardBg, ] ); // 计算各类型事件数量(使用 useMemo 缓存,避免每次渲染重复遍历数组) const { planCount, reviewCount } = useMemo( () => ({ planCount: allEvents.filter(e => e.type === 'plan').length, reviewCount: allEvents.filter(e => e.type === 'review').length, }), [allEvents] ); return ( 投资规划中心 {/* 视图切换按钮组 - H5隐藏 */} {viewMode === 'calendar' ? ( /* 日历视图 */ }> ) : ( /* 列表视图:我的计划 / 我的复盘 切换 */ 我的计划 ({planCount}) 我的复盘 ({reviewCount}) {/* 计划列表面板 */} }> {/* 复盘列表面板 */} }> )} ); }; export default InvestmentPlanningCenter;