refactor(Planning): 投资规划中心重构为 Redux 状态管理
- 新增 planningSlice 管理计划/复盘数据 - InvestmentPlanningCenter 改用 Redux 而非本地 state - 列表和日历视图共享同一数据源,保持同步 - 优化 Mock handlers,改进事件 ID 生成和调试日志 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* EventPanel - 通用事件面板组件
|
||||
* EventPanel - 通用事件面板组件 (Redux 版本)
|
||||
* 用于显示、编辑和管理投资计划或复盘
|
||||
*
|
||||
* 通过 props 配置差异化行为:
|
||||
@@ -17,15 +17,22 @@ import {
|
||||
Spinner,
|
||||
Center,
|
||||
Icon,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { FiFileText } from 'react-icons/fi';
|
||||
|
||||
import { usePlanningData } from './PlanningContext';
|
||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||
import {
|
||||
fetchAllEvents,
|
||||
deleteEvent,
|
||||
selectPlans,
|
||||
selectReviews,
|
||||
selectPlanningLoading,
|
||||
} from '@/store/slices/planningSlice';
|
||||
import { EventFormModal } from './EventFormModal';
|
||||
import { FUIEventCard } from './FUIEventCard';
|
||||
import type { InvestmentEvent } from '@/types';
|
||||
import { logger } from '@/utils/logger';
|
||||
import { getApiBase } from '@/utils/apiConfig';
|
||||
|
||||
/**
|
||||
* EventPanel Props
|
||||
@@ -51,15 +58,16 @@ export const EventPanel: React.FC<EventPanelProps> = ({
|
||||
label,
|
||||
openModalTrigger,
|
||||
}) => {
|
||||
const {
|
||||
allEvents,
|
||||
loadAllData,
|
||||
loading,
|
||||
toast,
|
||||
textColor,
|
||||
secondaryText,
|
||||
cardBg,
|
||||
} = usePlanningData();
|
||||
const dispatch = useAppDispatch();
|
||||
const toast = useToast();
|
||||
|
||||
// Redux 状态
|
||||
const plans = useAppSelector(selectPlans);
|
||||
const reviews = useAppSelector(selectReviews);
|
||||
const loading = useAppSelector(selectPlanningLoading);
|
||||
|
||||
// 根据类型选择事件列表
|
||||
const events = type === 'plan' ? plans : reviews;
|
||||
|
||||
// 弹窗状态
|
||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||
@@ -69,9 +77,6 @@ export const EventPanel: React.FC<EventPanelProps> = ({
|
||||
// 使用 ref 记录上一次的 trigger 值,避免组件挂载时误触发
|
||||
const prevTriggerRef = useRef<number>(openModalTrigger || 0);
|
||||
|
||||
// 筛选事件列表(按类型过滤,排除系统事件)
|
||||
const events = allEvents.filter(event => event.type === type && event.source !== 'future');
|
||||
|
||||
// 监听外部触发打开新建模态框(修复 bug:只在值变化时触发)
|
||||
useEffect(() => {
|
||||
if (openModalTrigger !== undefined && openModalTrigger > prevTriggerRef.current) {
|
||||
@@ -99,27 +104,18 @@ export const EventPanel: React.FC<EventPanelProps> = ({
|
||||
setEditingItem(null);
|
||||
};
|
||||
|
||||
// 删除数据
|
||||
// 删除数据 - 使用 Redux action
|
||||
const handleDelete = async (id: number): Promise<void> => {
|
||||
if (!window.confirm('确定要删除吗?')) return;
|
||||
|
||||
try {
|
||||
const base = getApiBase();
|
||||
|
||||
const response = await fetch(base + `/api/account/investment-plans/${id}`, {
|
||||
method: 'DELETE',
|
||||
credentials: 'include',
|
||||
await dispatch(deleteEvent(id)).unwrap();
|
||||
logger.info('EventPanel', `删除${label}成功`, { itemId: id });
|
||||
toast({
|
||||
title: '删除成功',
|
||||
status: 'success',
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
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({
|
||||
@@ -130,11 +126,19 @@ export const EventPanel: React.FC<EventPanelProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新数据
|
||||
const handleRefresh = useCallback(() => {
|
||||
dispatch(fetchAllEvents());
|
||||
}, [dispatch]);
|
||||
|
||||
// 使用 useCallback 优化回调函数
|
||||
const handleEdit = useCallback((item: InvestmentEvent) => {
|
||||
handleOpenModal(item);
|
||||
}, []);
|
||||
|
||||
// 颜色主题
|
||||
const secondaryText = 'rgba(255, 255, 255, 0.6)';
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<VStack align="stretch" spacing={4}>
|
||||
@@ -172,7 +176,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
|
||||
mode={modalMode}
|
||||
eventType={type}
|
||||
editingEvent={editingItem}
|
||||
onSuccess={loadAllData}
|
||||
onSuccess={handleRefresh}
|
||||
label={label}
|
||||
apiEndpoint="investment-plans"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user