From 0f7a3c0cc92492258c09937e935f425f568a0a07 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Fri, 5 Dec 2025 14:44:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20CalendarPanel=20=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=8C=E7=BB=9F=E4=B8=80=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dashboard/components/CalendarPanel.tsx | 228 ++++++------------ 1 file changed, 78 insertions(+), 150 deletions(-) diff --git a/src/views/Dashboard/components/CalendarPanel.tsx b/src/views/Dashboard/components/CalendarPanel.tsx index daf424c7..e329b106 100644 --- a/src/views/Dashboard/components/CalendarPanel.tsx +++ b/src/views/Dashboard/components/CalendarPanel.tsx @@ -3,7 +3,7 @@ * 使用 FullCalendar 展示投资计划、复盘等事件 */ -import React, { useState, lazy, Suspense } from 'react'; +import React, { useState, lazy, Suspense, useMemo, useCallback } from 'react'; import { Box, Modal, @@ -12,15 +12,9 @@ import { ModalHeader, ModalBody, ModalCloseButton, - useDisclosure, - VStack, - Text, Spinner, Center, - Icon, - Link, } from '@chakra-ui/react'; -import { FiCalendar } from 'react-icons/fi'; import FullCalendar from '@fullcalendar/react'; import dayGridPlugin from '@fullcalendar/daygrid'; import interactionPlugin from '@fullcalendar/interaction'; @@ -30,7 +24,7 @@ import dayjs, { Dayjs } from 'dayjs'; import 'dayjs/locale/zh-cn'; import { usePlanningData } from './PlanningContext'; -import { EventDetailCard } from './EventDetailCard'; +import { EventDetailModal } from './EventDetailModal'; import type { InvestmentEvent } from '@/types'; // 懒加载投资日历组件 @@ -60,184 +54,118 @@ interface CalendarEvent { export const CalendarPanel: React.FC = () => { const { allEvents, - loading, borderColor, secondaryText, setViewMode, setListTab, } = usePlanningData(); - // 详情弹窗 - const { isOpen, onOpen, onClose } = useDisclosure(); - // 投资日历弹窗 + // 弹窗状态(统一使用 useState) + const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); const [isInvestmentCalendarOpen, setIsInvestmentCalendarOpen] = useState(false); const [selectedDate, setSelectedDate] = useState(null); const [selectedDateEvents, setSelectedDateEvents] = useState([]); - // 转换数据为 FullCalendar 格式 - const calendarEvents: CalendarEvent[] = allEvents.map(event => ({ - ...event, - id: `${event.source || 'user'}-${event.id}`, - title: event.title, - start: event.event_date, - date: event.event_date, - backgroundColor: event.source === 'future' ? '#3182CE' : event.type === 'plan' ? '#8B5CF6' : '#38A169', - borderColor: event.source === 'future' ? '#3182CE' : event.type === 'plan' ? '#8B5CF6' : '#38A169', - extendedProps: { + // 转换数据为 FullCalendar 格式(使用 useMemo 缓存) + const calendarEvents: CalendarEvent[] = useMemo(() => + allEvents.map(event => ({ ...event, - isSystem: event.source === 'future', - } - })); + id: `${event.source || 'user'}-${event.id}`, + title: event.title, + start: event.event_date, + date: event.event_date, + backgroundColor: event.source === 'future' ? '#3182CE' : event.type === 'plan' ? '#8B5CF6' : '#38A169', + borderColor: event.source === 'future' ? '#3182CE' : event.type === 'plan' ? '#8B5CF6' : '#38A169', + extendedProps: { + ...event, + isSystem: event.source === 'future', + } + })), [allEvents]); - // 处理日期点击 - const handleDateClick = (info: DateClickArg): void => { - const clickedDate = dayjs(info.date); + // 抽取公共的打开事件详情函数 + const openEventDetail = useCallback((date: Date | null): void => { + if (!date) return; + const clickedDate = dayjs(date); setSelectedDate(clickedDate); const dayEvents = allEvents.filter(event => dayjs(event.event_date).isSame(clickedDate, 'day') ); setSelectedDateEvents(dayEvents); - onOpen(); - }; + setIsDetailModalOpen(true); + }, [allEvents]); + + // 处理日期点击 + const handleDateClick = useCallback((info: DateClickArg): void => { + openEventDetail(info.date); + }, [openEventDetail]); // 处理事件点击 - const handleEventClick = (info: EventClickArg): void => { - const event = info.event; - const clickedDate = dayjs(event.start); - setSelectedDate(clickedDate); - - const dayEvents = allEvents.filter(ev => - dayjs(ev.event_date).isSame(clickedDate, 'day') - ); - setSelectedDateEvents(dayEvents); - onOpen(); - }; + const handleEventClick = useCallback((info: EventClickArg): void => { + openEventDetail(info.event.start); + }, [openEventDetail]); return ( - {loading ? ( -
- -
- ) : ( - - - - )} + + + {/* 查看事件详情 Modal */} - {isOpen && ( - - - - - {selectedDate && selectedDate.format('YYYY年MM月DD日')} 的事件 - - - - {selectedDateEvents.length === 0 ? ( -
- - - 当天暂无事件 - - 可在 - { - onClose(); - setViewMode?.('list'); - setListTab?.(0); - }} - cursor="pointer" - > - 计划 - - 或 - { - onClose(); - setViewMode?.('list'); - setListTab?.(1); - }} - cursor="pointer" - > - 复盘 - - 添加,或关注 - { - onClose(); - setIsInvestmentCalendarOpen(true); - }} - cursor="pointer" - > - 投资日历 - - 中的未来事件 - - -
- ) : ( - - {selectedDateEvents.map((event, idx) => ( - - ))} - - )} -
-
-
- )} + setIsDetailModalOpen(false)} + selectedDate={selectedDate} + events={selectedDateEvents} + borderColor={borderColor} + secondaryText={secondaryText} + onNavigateToPlan={() => { + setViewMode('list'); + setListTab(0); + }} + onNavigateToReview={() => { + setViewMode('list'); + setListTab(1); + }} + onOpenInvestmentCalendar={() => { + setIsInvestmentCalendarOpen(true); + }} + /> {/* 投资日历 Modal */} {isInvestmentCalendarOpen && ( setIsInvestmentCalendarOpen(false)} - size="6xl" + size={{ base: 'full', md: '6xl' }} > - - 投资日历 + + 投资日历 - }> + }>