/** * EventPanel - 通用事件面板组件 * 用于显示、编辑和管理投资计划或复盘 * * 通过 props 配置差异化行为: * - type: 'plan' | 'review' * - colorScheme: 主题色 * - label: 显示文案 */ import React, { useState, useEffect, useRef, useCallback } from 'react'; import { Box, Grid, VStack, Text, Spinner, Center, Icon, } from '@chakra-ui/react'; import { FiFileText } from 'react-icons/fi'; import { usePlanningData } from './PlanningContext'; import { EventFormModal } from './EventFormModal'; import { EventCard } from './EventCard'; import type { InvestmentEvent } from '@/types'; import { logger } from '@/utils/logger'; import { getApiBase } from '@/utils/apiConfig'; /** * EventPanel Props */ export interface EventPanelProps { /** 事件类型 */ type: 'plan' | 'review'; /** 主题颜色 */ colorScheme: string; /** 显示标签(如 "计划" 或 "复盘") */ label: string; /** 外部触发打开模态框的计数器 */ openModalTrigger?: number; } /** * EventPanel 组件 * 通用事件列表面板,显示投资计划或复盘 */ export const EventPanel: React.FC = ({ type, colorScheme, label, openModalTrigger, }) => { const { allEvents, loadAllData, loading, toast, textColor, secondaryText, cardBg, } = usePlanningData(); // 弹窗状态 const [isModalOpen, setIsModalOpen] = useState(false); const [editingItem, setEditingItem] = useState(null); const [modalMode, setModalMode] = useState<'create' | 'edit'>('create'); // 使用 ref 记录上一次的 trigger 值,避免组件挂载时误触发 const prevTriggerRef = useRef(openModalTrigger || 0); // 筛选事件列表(按类型过滤,排除系统事件) const events = allEvents.filter(event => event.type === type && event.source !== 'future'); // 监听外部触发打开新建模态框(修复 bug:只在值变化时触发) useEffect(() => { if (openModalTrigger !== undefined && openModalTrigger > prevTriggerRef.current) { // 只有当 trigger 值增加时才打开弹窗 handleOpenModal(null); } prevTriggerRef.current = openModalTrigger || 0; }, [openModalTrigger]); // 打开编辑/新建模态框 const handleOpenModal = (item: InvestmentEvent | null = null): void => { if (item) { setEditingItem(item); setModalMode('edit'); } else { setEditingItem(null); setModalMode('create'); } setIsModalOpen(true); }; // 关闭弹窗 const handleCloseModal = (): void => { setIsModalOpen(false); setEditingItem(null); }; // 删除数据 const handleDelete = async (id: number): Promise => { if (!window.confirm('确定要删除吗?')) return; try { const base = getApiBase(); const response = await fetch(base + `/api/account/investment-plans/${id}`, { method: 'DELETE', credentials: 'include', }); if (response.ok) { logger.info('EventPanel', `删除${label}成功`, { itemId: id }); toast({ title: '删除成功', status: 'success', duration: 2000, }); loadAllData(); } } catch (error) { logger.error('EventPanel', 'handleDelete', error, { itemId: id }); toast({ title: '删除失败', status: 'error', duration: 3000, }); } }; // 使用 useCallback 优化回调函数 const handleEdit = useCallback((item: InvestmentEvent) => { handleOpenModal(item); }, []); return ( {loading ? (
) : events.length === 0 ? (
暂无投资{label}
) : ( {events.map(event => ( ))} )}
{/* 使用通用弹窗组件 */}
); }; export default EventPanel;