From 2f04293c6815c86f3cfc3aa1c212959d6f20f5f3 Mon Sep 17 00:00:00 2001
From: zdl <3489966805@qq.com>
Date: Fri, 5 Dec 2025 12:03:41 +0800
Subject: [PATCH] =?UTF-8?q?pref:=20=E5=88=A0=E9=99=A4=E5=A4=87=E4=BB=BD?=
=?UTF-8?q?=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../InvestmentCalendarChakra.js.bak | 493 ------
.../InvestmentPlanningCenter.js.bak | 1419 ----------------
.../InvestmentPlanningCenter.js.old | 1420 -----------------
.../InvestmentPlansAndReviews.js.bak | 587 -------
4 files changed, 3919 deletions(-)
delete mode 100644 src/views/Dashboard/components/InvestmentCalendarChakra.js.bak
delete mode 100644 src/views/Dashboard/components/InvestmentPlanningCenter.js.bak
delete mode 100644 src/views/Dashboard/components/InvestmentPlanningCenter.js.old
delete mode 100644 src/views/Dashboard/components/InvestmentPlansAndReviews.js.bak
diff --git a/src/views/Dashboard/components/InvestmentCalendarChakra.js.bak b/src/views/Dashboard/components/InvestmentCalendarChakra.js.bak
deleted file mode 100644
index a4c0b080..00000000
--- a/src/views/Dashboard/components/InvestmentCalendarChakra.js.bak
+++ /dev/null
@@ -1,493 +0,0 @@
-// src/views/Dashboard/components/InvestmentCalendarChakra.js
-import React, { useState, useEffect, useCallback } from 'react';
-import {
- Box,
- Card,
- CardHeader,
- CardBody,
- Heading,
- VStack,
- HStack,
- Text,
- Button,
- Modal,
- ModalOverlay,
- ModalContent,
- ModalHeader,
- ModalFooter,
- ModalBody,
- ModalCloseButton,
- useDisclosure,
- Badge,
- IconButton,
- Flex,
- Grid,
- useColorModeValue,
- Divider,
- Tooltip,
- Icon,
- Input,
- FormControl,
- FormLabel,
- Textarea,
- Select,
- useToast,
- Spinner,
- Center,
- Tag,
- TagLabel,
- TagLeftIcon,
-} from '@chakra-ui/react';
-import {
- FiCalendar,
- FiClock,
- FiStar,
- FiTrendingUp,
- FiPlus,
- FiEdit2,
- FiTrash2,
- FiSave,
- FiX,
-} from 'react-icons/fi';
-import FullCalendar from '@fullcalendar/react';
-import dayGridPlugin from '@fullcalendar/daygrid';
-import interactionPlugin from '@fullcalendar/interaction';
-import moment from 'moment';
-import 'moment/locale/zh-cn';
-import { logger } from '../../../utils/logger';
-import './InvestmentCalendar.css';
-
-moment.locale('zh-cn');
-
-export default function InvestmentCalendarChakra() {
- const { isOpen, onOpen, onClose } = useDisclosure();
- const { isOpen: isAddOpen, onOpen: onAddOpen, onClose: onAddClose } = useDisclosure();
- 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 [events, setEvents] = useState([]);
- const [selectedDate, setSelectedDate] = useState(null);
- const [selectedDateEvents, setSelectedDateEvents] = useState([]);
- const [loading, setLoading] = useState(false);
- const [newEvent, setNewEvent] = useState({
- title: '',
- description: '',
- type: 'plan',
- importance: 3,
- stocks: '',
- });
-
- // 加载事件数据
- const loadEvents = useCallback(async () => {
- try {
- setLoading(true);
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- // 直接加载用户相关的事件(投资计划 + 关注的未来事件)
- const userResponse = await fetch(base + '/api/account/calendar/events', {
- credentials: 'include'
- });
-
- if (userResponse.ok) {
- const userData = await userResponse.json();
- if (userData.success) {
- const allEvents = (userData.data || []).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' : '#8B5CF6',
- borderColor: event.source === 'future' ? '#3182CE' : '#8B5CF6',
- extendedProps: {
- ...event,
- isSystem: event.source === 'future',
- }
- }));
-
- setEvents(allEvents);
- logger.debug('InvestmentCalendar', '日历事件加载成功', {
- count: allEvents.length
- });
- }
- }
- } catch (error) {
- logger.error('InvestmentCalendar', 'loadEvents', error);
- // ❌ 移除数据加载失败 toast(非关键操作)
- } finally {
- setLoading(false);
- }
- }, []); // ✅ 移除 toast 依赖
-
- useEffect(() => {
- loadEvents();
- }, [loadEvents]);
-
- // 根据重要性获取颜色
- const getEventColor = (importance) => {
- if (importance >= 5) return '#E53E3E'; // 红色
- if (importance >= 4) return '#ED8936'; // 橙色
- if (importance >= 3) return '#ECC94B'; // 黄色
- if (importance >= 2) return '#48BB78'; // 绿色
- return '#3182CE'; // 蓝色
- };
-
- // 处理日期点击
- const handleDateClick = (info) => {
- const clickedDate = moment(info.date);
- setSelectedDate(clickedDate);
-
- // 筛选当天的事件
- const dayEvents = events.filter(event =>
- moment(event.start).isSame(clickedDate, 'day')
- );
- setSelectedDateEvents(dayEvents);
- onOpen();
- };
-
- // 处理事件点击
- const handleEventClick = (info) => {
- const event = info.event;
- const clickedDate = moment(event.start);
- setSelectedDate(clickedDate);
- setSelectedDateEvents([{
- title: event.title,
- start: event.start,
- extendedProps: {
- ...event.extendedProps,
- },
- }]);
- onOpen();
- };
-
- // 添加新事件
- const handleAddEvent = async () => {
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const eventData = {
- ...newEvent,
- event_date: (selectedDate ? selectedDate.format('YYYY-MM-DD') : moment().format('YYYY-MM-DD')),
- stocks: newEvent.stocks.split(',').map(s => s.trim()).filter(s => s),
- };
-
- const response = await fetch(base + '/api/account/calendar/events', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(eventData),
- });
-
- if (response.ok) {
- const data = await response.json();
- if (data.success) {
- logger.info('InvestmentCalendar', '添加事件成功', {
- eventTitle: eventData.title,
- eventDate: eventData.event_date
- });
- toast({
- title: '添加成功',
- description: '投资计划已添加',
- status: 'success',
- duration: 3000,
- });
- onAddClose();
- loadEvents();
- setNewEvent({
- title: '',
- description: '',
- type: 'plan',
- importance: 3,
- stocks: '',
- });
- }
- }
- } catch (error) {
- logger.error('InvestmentCalendar', 'handleAddEvent', error, {
- eventTitle: newEvent?.title
- });
- toast({
- title: '添加失败',
- description: '无法添加投资计划',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除用户事件
- const handleDeleteEvent = async (eventId) => {
- if (!eventId) {
- logger.warn('InvestmentCalendar', '删除事件失败', '缺少事件 ID', { eventId });
- toast({
- title: '无法删除',
- description: '缺少事件 ID',
- status: 'error',
- duration: 3000,
- });
- return;
- }
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const response = await fetch(base + `/api/account/calendar/events/${eventId}`, {
- method: 'DELETE',
- credentials: 'include',
- });
-
- if (response.ok) {
- logger.info('InvestmentCalendar', '删除事件成功', { eventId });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadEvents();
- }
- } catch (error) {
- logger.error('InvestmentCalendar', 'handleDeleteEvent', error, { eventId });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- return (
-
-
-
-
-
- 投资日历
-
- }
- onClick={() => { if (!selectedDate) setSelectedDate(moment()); onAddOpen(); }}
- >
- 添加计划
-
-
-
-
- {loading ? (
-
-
-
- ) : (
-
-
-
- )}
-
-
- {/* 查看事件详情 Modal - 条件渲染 */}
- {isOpen && (
-
-
-
-
- {selectedDate && selectedDate.format('YYYY年MM月DD日')} 的事件
-
-
-
- {selectedDateEvents.length === 0 ? (
-
-
- 当天没有事件
- }
- onClick={() => {
- onClose();
- onAddOpen();
- }}
- >
- 添加投资计划
-
-
-
- ) : (
-
- {selectedDateEvents.map((event, idx) => (
-
-
-
-
-
- {event.title}
-
- {event.extendedProps?.isSystem ? (
- 系统事件
- ) : (
- 我的计划
- )}
-
-
-
-
- 重要度: {event.extendedProps?.importance || 3}/5
-
-
-
- {!event.extendedProps?.isSystem && (
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDeleteEvent(event.extendedProps?.id)}
- />
- )}
-
-
- {event.extendedProps?.description && (
-
- {event.extendedProps.description}
-
- )}
-
- {event.extendedProps?.stocks && event.extendedProps.stocks.length > 0 && (
-
- 相关股票:
- {event.extendedProps.stocks.map((stock, i) => (
-
-
- {stock}
-
- ))}
-
- )}
-
- ))}
-
- )}
-
-
-
-
-
-
- )}
-
- {/* 添加投资计划 Modal - 条件渲染 */}
- {isAddOpen && (
-
-
-
-
- 添加投资计划
-
-
-
-
-
- 标题
- setNewEvent({ ...newEvent, title: e.target.value })}
- placeholder="例如:关注半导体板块"
- />
-
-
-
- 描述
-
-
-
- 类型
-
-
-
-
- 重要度
-
-
-
-
- 相关股票(用逗号分隔)
- setNewEvent({ ...newEvent, stocks: e.target.value })}
- placeholder="例如:600519,000858,002415"
- />
-
-
-
-
-
-
-
-
-
- )}
-
- );
-}
diff --git a/src/views/Dashboard/components/InvestmentPlanningCenter.js.bak b/src/views/Dashboard/components/InvestmentPlanningCenter.js.bak
deleted file mode 100644
index 7d2baae7..00000000
--- a/src/views/Dashboard/components/InvestmentPlanningCenter.js.bak
+++ /dev/null
@@ -1,1419 +0,0 @@
-// src/views/Dashboard/components/InvestmentPlanningCenter.js
-import React, { useState, useEffect, useCallback, createContext, useContext } from 'react';
-import {
- Box,
- Card,
- CardHeader,
- CardBody,
- Heading,
- VStack,
- HStack,
- Text,
- Button,
- Modal,
- ModalOverlay,
- ModalContent,
- ModalHeader,
- ModalFooter,
- ModalBody,
- ModalCloseButton,
- useDisclosure,
- Badge,
- IconButton,
- Flex,
- Grid,
- useColorModeValue,
- Divider,
- Tooltip,
- Icon,
- Input,
- FormControl,
- FormLabel,
- Textarea,
- Select,
- useToast,
- Spinner,
- Center,
- Tag,
- TagLabel,
- TagLeftIcon,
- TagCloseButton,
- Tabs,
- TabList,
- TabPanels,
- Tab,
- TabPanel,
- InputGroup,
- InputLeftElement,
-} from '@chakra-ui/react';
-import {
- FiCalendar,
- FiClock,
- FiStar,
- FiTrendingUp,
- FiPlus,
- FiEdit2,
- FiTrash2,
- FiSave,
- FiX,
- FiTarget,
- FiFileText,
- FiHash,
- FiCheckCircle,
- FiXCircle,
- FiAlertCircle,
-} from 'react-icons/fi';
-import FullCalendar from '@fullcalendar/react';
-import dayGridPlugin from '@fullcalendar/daygrid';
-import interactionPlugin from '@fullcalendar/interaction';
-import moment from 'moment';
-import 'moment/locale/zh-cn';
-import { logger } from '../../../utils/logger';
-import '../components/InvestmentCalendar.css';
-
-moment.locale('zh-cn');
-
-// 创建 Context 用于跨标签页共享数据
-const PlanningDataContext = createContext();
-
-export default function InvestmentPlanningCenter() {
- 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 () => {
- try {
- setLoading(true);
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- 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 = {
- allEvents,
- setAllEvents,
- loadAllData,
- loading,
- setLoading,
- activeTab,
- setActiveTab,
- toast,
- bgColor,
- borderColor,
- textColor,
- secondaryText,
- cardBg,
- };
-
- return (
-
-
-
-
-
-
- 投资规划中心
-
-
-
-
-
-
-
-
- 日历视图
-
-
-
- 我的计划 ({allEvents.filter(e => e.type === 'plan').length})
-
-
-
- 我的复盘 ({allEvents.filter(e => e.type === 'review').length})
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-// 日历视图面板
-function CalendarPanel() {
- const {
- allEvents,
- loadAllData,
- loading,
- setActiveTab,
- toast,
- borderColor,
- secondaryText,
- } = useContext(PlanningDataContext);
-
- const { isOpen, onOpen, onClose } = useDisclosure();
- const { isOpen: isAddOpen, onOpen: onAddOpen, onClose: onAddClose } = useDisclosure();
-
- const [selectedDate, setSelectedDate] = useState(null);
- const [selectedDateEvents, setSelectedDateEvents] = useState([]);
- const [newEvent, setNewEvent] = useState({
- title: '',
- description: '',
- type: 'plan',
- importance: 3,
- stocks: '',
- });
-
- // 转换数据为 FullCalendar 格式
- const calendarEvents = 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: {
- ...event,
- isSystem: event.source === 'future',
- }
- }));
-
- // 处理日期点击
- const handleDateClick = (info) => {
- const clickedDate = moment(info.date);
- setSelectedDate(clickedDate);
-
- const dayEvents = allEvents.filter(event =>
- moment(event.event_date).isSame(clickedDate, 'day')
- );
- setSelectedDateEvents(dayEvents);
- onOpen();
- };
-
- // 处理事件点击
- const handleEventClick = (info) => {
- const event = info.event;
- const clickedDate = moment(event.start);
- setSelectedDate(clickedDate);
-
- const dayEvents = allEvents.filter(ev =>
- moment(ev.event_date).isSame(clickedDate, 'day')
- );
- setSelectedDateEvents(dayEvents);
- onOpen();
- };
-
- // 添加新事件
- const handleAddEvent = async () => {
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const eventData = {
- ...newEvent,
- event_date: (selectedDate ? selectedDate.format('YYYY-MM-DD') : moment().format('YYYY-MM-DD')),
- stocks: newEvent.stocks.split(',').map(s => s.trim()).filter(s => s),
- };
-
- const response = await fetch(base + '/api/account/calendar/events', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(eventData),
- });
-
- if (response.ok) {
- const data = await response.json();
- if (data.success) {
- logger.info('CalendarPanel', '添加事件成功', {
- eventTitle: eventData.title,
- eventDate: eventData.event_date
- });
- toast({
- title: '添加成功',
- description: '投资计划已添加',
- status: 'success',
- duration: 3000,
- });
- onAddClose();
- loadAllData();
- setNewEvent({
- title: '',
- description: '',
- type: 'plan',
- importance: 3,
- stocks: '',
- });
- }
- }
- } catch (error) {
- logger.error('CalendarPanel', 'handleAddEvent', error, {
- eventTitle: newEvent?.title
- });
- toast({
- title: '添加失败',
- description: '无法添加投资计划',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除事件
- const handleDeleteEvent = async (eventId) => {
- if (!eventId) {
- logger.warn('CalendarPanel', '删除事件失败', '缺少事件 ID', { eventId });
- toast({
- title: '无法删除',
- description: '缺少事件 ID',
- status: 'error',
- duration: 3000,
- });
- return;
- }
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const response = await fetch(base + `/api/account/calendar/events/${eventId}`, {
- method: 'DELETE',
- credentials: 'include',
- });
-
- if (response.ok) {
- logger.info('CalendarPanel', '删除事件成功', { eventId });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadAllData();
- }
- } catch (error) {
- logger.error('CalendarPanel', 'handleDeleteEvent', error, { eventId });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 跳转到计划或复盘标签页
- const handleViewDetails = (event) => {
- if (event.type === 'plan') {
- setActiveTab(1); // 跳转到"我的计划"标签页
- } else if (event.type === 'review') {
- setActiveTab(2); // 跳转到"我的复盘"标签页
- }
- onClose();
- };
-
- return (
-
-
- }
- onClick={() => { if (!selectedDate) setSelectedDate(moment()); onAddOpen(); }}
- >
- 添加计划
-
-
-
- {loading ? (
-
-
-
- ) : (
-
-
-
- )}
-
- {/* 查看事件详情 Modal */}
- {isOpen && (
-
-
-
-
- {selectedDate && selectedDate.format('YYYY年MM月DD日')} 的事件
-
-
-
- {selectedDateEvents.length === 0 ? (
-
-
- 当天没有事件
- }
- onClick={() => {
- onClose();
- onAddOpen();
- }}
- >
- 添加投资计划
-
-
-
- ) : (
-
- {selectedDateEvents.map((event, idx) => (
-
-
-
-
-
- {event.title}
-
- {event.source === 'future' ? (
- 系统事件
- ) : event.type === 'plan' ? (
- 我的计划
- ) : (
- 我的复盘
- )}
-
- {event.importance && (
-
-
-
- 重要度: {event.importance}/5
-
-
- )}
-
-
- {!event.source || event.source === 'user' ? (
- <>
-
- }
- size="sm"
- variant="ghost"
- colorScheme="blue"
- onClick={() => handleViewDetails(event)}
- />
-
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDeleteEvent(event.id)}
- />
- >
- ) : null}
-
-
-
- {event.description && (
-
- {event.description}
-
- )}
-
- {event.stocks && event.stocks.length > 0 && (
-
- 相关股票:
- {event.stocks.map((stock, i) => (
-
-
- {stock}
-
- ))}
-
- )}
-
- ))}
-
- )}
-
-
-
-
-
-
- )}
-
- {/* 添加投资计划 Modal */}
- {isAddOpen && (
-
-
-
-
- 添加投资计划
-
-
-
-
-
- 标题
- setNewEvent({ ...newEvent, title: e.target.value })}
- placeholder="例如:关注半导体板块"
- />
-
-
-
- 描述
-
-
-
- 类型
-
-
-
-
- 重要度
-
-
-
-
- 相关股票(用逗号分隔)
- setNewEvent({ ...newEvent, stocks: e.target.value })}
- placeholder="例如:600519,000858,002415"
- />
-
-
-
-
-
-
-
-
-
- )}
-
- );
-}
-
-// 计划列表面板
-function PlansPanel() {
- const {
- allEvents,
- loadAllData,
- loading,
- toast,
- textColor,
- secondaryText,
- cardBg,
- borderColor,
- } = useContext(PlanningDataContext);
-
- const { isOpen, onOpen, onClose } = useDisclosure();
- const [editingItem, setEditingItem] = useState(null);
- const [formData, setFormData] = useState({
- date: moment().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'plan',
- stocks: [],
- tags: [],
- status: 'active',
- });
- const [stockInput, setStockInput] = useState('');
- const [tagInput, setTagInput] = useState('');
-
- const plans = allEvents.filter(event => event.type === 'plan' && event.source !== 'future');
-
- // 打开编辑/新建模态框
- const handleOpenModal = (item = null) => {
- if (item) {
- setEditingItem(item);
- setFormData({
- ...item,
- date: moment(item.event_date || item.date).format('YYYY-MM-DD'),
- content: item.description || item.content || '',
- });
- } else {
- setEditingItem(null);
- setFormData({
- date: moment().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'plan',
- stocks: [],
- tags: [],
- status: 'active',
- });
- }
- onOpen();
- };
-
- // 保存数据
- const handleSave = async () => {
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const url = editingItem
- ? base + `/api/account/investment-plans/${editingItem.id}`
- : base + '/api/account/investment-plans';
-
- const method = editingItem ? 'PUT' : 'POST';
-
- const response = await fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(formData),
- });
-
- if (response.ok) {
- logger.info('PlansPanel', `${editingItem ? '更新' : '创建'}成功`, {
- itemId: editingItem?.id,
- title: formData.title,
- });
- toast({
- title: editingItem ? '更新成功' : '创建成功',
- status: 'success',
- duration: 2000,
- });
- onClose();
- loadAllData();
- } else {
- throw new Error('保存失败');
- }
- } catch (error) {
- logger.error('PlansPanel', 'handleSave', error, {
- itemId: editingItem?.id,
- title: formData?.title
- });
- toast({
- title: '保存失败',
- description: '无法保存数据',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除数据
- const handleDelete = async (id) => {
- if (!window.confirm('确定要删除吗?')) return;
-
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const response = await fetch(base + `/api/account/investment-plans/${id}`, {
- method: 'DELETE',
- credentials: 'include',
- });
-
- if (response.ok) {
- logger.info('PlansPanel', '删除成功', { itemId: id });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadAllData();
- }
- } catch (error) {
- logger.error('PlansPanel', 'handleDelete', error, { itemId: id });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 添加股票
- const handleAddStock = () => {
- if (stockInput.trim() && !formData.stocks.includes(stockInput.trim())) {
- setFormData({
- ...formData,
- stocks: [...formData.stocks, stockInput.trim()],
- });
- setStockInput('');
- }
- };
-
- // 添加标签
- const handleAddTag = () => {
- if (tagInput.trim() && !formData.tags.includes(tagInput.trim())) {
- setFormData({
- ...formData,
- tags: [...formData.tags, tagInput.trim()],
- });
- setTagInput('');
- }
- };
-
- // 获取状态信息
- const getStatusInfo = (status) => {
- switch (status) {
- case 'completed':
- return { icon: FiCheckCircle, color: 'green', text: '已完成' };
- case 'cancelled':
- return { icon: FiXCircle, color: 'red', text: '已取消' };
- default:
- return { icon: FiAlertCircle, color: 'blue', text: '进行中' };
- }
- };
-
- // 渲染单个卡片
- const renderCard = (item) => {
- const statusInfo = getStatusInfo(item.status);
-
- return (
-
-
-
-
-
-
-
-
- {item.title}
-
-
-
-
-
- {moment(item.event_date || item.date).format('YYYY年MM月DD日')}
-
-
- {statusInfo.text}
-
-
-
-
- }
- size="sm"
- variant="ghost"
- onClick={() => handleOpenModal(item)}
- />
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDelete(item.id)}
- />
-
-
-
- {(item.content || item.description) && (
-
- {item.content || item.description}
-
- )}
-
-
- {item.stocks && item.stocks.length > 0 && (
- <>
- {item.stocks.map((stock, idx) => (
-
-
- {stock}
-
- ))}
- >
- )}
- {item.tags && item.tags.length > 0 && (
- <>
- {item.tags.map((tag, idx) => (
-
-
- {tag}
-
- ))}
- >
- )}
-
-
-
-
- );
- };
-
- return (
-
-
-
- }
- onClick={() => handleOpenModal(null)}
- >
- 新建计划
-
-
-
- {loading ? (
-
-
-
- ) : plans.length === 0 ? (
-
-
-
- 暂无投资计划
- }
- onClick={() => handleOpenModal(null)}
- >
- 创建第一个计划
-
-
-
- ) : (
-
- {plans.map(renderCard)}
-
- )}
-
-
- {/* 编辑/新建模态框 */}
- {isOpen && (
-
-
-
-
- {editingItem ? '编辑' : '新建'}投资计划
-
-
-
-
-
- 日期
-
-
-
-
- setFormData({ ...formData, date: e.target.value })}
- />
-
-
-
-
- 标题
- setFormData({ ...formData, title: e.target.value })}
- placeholder="例如:布局新能源板块"
- />
-
-
-
- 内容
-
-
-
- 相关股票
-
- setStockInput(e.target.value)}
- placeholder="输入股票代码"
- onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
- />
-
-
-
- {(formData.stocks || []).map((stock, idx) => (
-
-
- {stock}
- setFormData({
- ...formData,
- stocks: formData.stocks.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 标签
-
- setTagInput(e.target.value)}
- placeholder="输入标签"
- onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
- />
-
-
-
- {(formData.tags || []).map((tag, idx) => (
-
-
- {tag}
- setFormData({
- ...formData,
- tags: formData.tags.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 状态
-
-
-
-
-
-
- }
- >
- 保存
-
-
-
-
- )}
-
- );
-}
-
-// 复盘列表面板
-function ReviewsPanel() {
- const {
- allEvents,
- loadAllData,
- loading,
- toast,
- textColor,
- secondaryText,
- cardBg,
- borderColor,
- } = useContext(PlanningDataContext);
-
- const { isOpen, onOpen, onClose } = useDisclosure();
- const [editingItem, setEditingItem] = useState(null);
- const [formData, setFormData] = useState({
- date: moment().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'review',
- stocks: [],
- tags: [],
- status: 'active',
- });
- const [stockInput, setStockInput] = useState('');
- const [tagInput, setTagInput] = useState('');
-
- const reviews = allEvents.filter(event => event.type === 'review' && event.source !== 'future');
-
- // 打开编辑/新建模态框
- const handleOpenModal = (item = null) => {
- if (item) {
- setEditingItem(item);
- setFormData({
- ...item,
- date: moment(item.event_date || item.date).format('YYYY-MM-DD'),
- content: item.description || item.content || '',
- });
- } else {
- setEditingItem(null);
- setFormData({
- date: moment().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'review',
- stocks: [],
- tags: [],
- status: 'active',
- });
- }
- onOpen();
- };
-
- // 保存数据
- const handleSave = async () => {
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const url = editingItem
- ? base + `/api/account/investment-plans/${editingItem.id}`
- : base + '/api/account/investment-plans';
-
- const method = editingItem ? 'PUT' : 'POST';
-
- const response = await fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(formData),
- });
-
- if (response.ok) {
- logger.info('ReviewsPanel', `${editingItem ? '更新' : '创建'}成功`, {
- itemId: editingItem?.id,
- title: formData.title,
- });
- toast({
- title: editingItem ? '更新成功' : '创建成功',
- status: 'success',
- duration: 2000,
- });
- onClose();
- loadAllData();
- } else {
- throw new Error('保存失败');
- }
- } catch (error) {
- logger.error('ReviewsPanel', 'handleSave', error, {
- itemId: editingItem?.id,
- title: formData?.title
- });
- toast({
- title: '保存失败',
- description: '无法保存数据',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除数据
- const handleDelete = async (id) => {
- if (!window.confirm('确定要删除吗?')) return;
-
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const response = await fetch(base + `/api/account/investment-plans/${id}`, {
- method: 'DELETE',
- credentials: 'include',
- });
-
- if (response.ok) {
- logger.info('ReviewsPanel', '删除成功', { itemId: id });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadAllData();
- }
- } catch (error) {
- logger.error('ReviewsPanel', 'handleDelete', error, { itemId: id });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 添加股票
- const handleAddStock = () => {
- if (stockInput.trim() && !formData.stocks.includes(stockInput.trim())) {
- setFormData({
- ...formData,
- stocks: [...formData.stocks, stockInput.trim()],
- });
- setStockInput('');
- }
- };
-
- // 添加标签
- const handleAddTag = () => {
- if (tagInput.trim() && !formData.tags.includes(tagInput.trim())) {
- setFormData({
- ...formData,
- tags: [...formData.tags, tagInput.trim()],
- });
- setTagInput('');
- }
- };
-
- // 渲染单个卡片
- const renderCard = (item) => {
- return (
-
-
-
-
-
-
-
-
- {item.title}
-
-
-
-
-
- {moment(item.event_date || item.date).format('YYYY年MM月DD日')}
-
-
-
-
- }
- size="sm"
- variant="ghost"
- onClick={() => handleOpenModal(item)}
- />
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDelete(item.id)}
- />
-
-
-
- {(item.content || item.description) && (
-
- {item.content || item.description}
-
- )}
-
-
- {item.stocks && item.stocks.length > 0 && (
- <>
- {item.stocks.map((stock, idx) => (
-
-
- {stock}
-
- ))}
- >
- )}
- {item.tags && item.tags.length > 0 && (
- <>
- {item.tags.map((tag, idx) => (
-
-
- {tag}
-
- ))}
- >
- )}
-
-
-
-
- );
- };
-
- return (
-
-
-
- }
- onClick={() => handleOpenModal(null)}
- >
- 新建复盘
-
-
-
- {loading ? (
-
-
-
- ) : reviews.length === 0 ? (
-
-
-
- 暂无复盘记录
- }
- onClick={() => handleOpenModal(null)}
- >
- 创建第一个复盘
-
-
-
- ) : (
-
- {reviews.map(renderCard)}
-
- )}
-
-
- {/* 编辑/新建模态框 */}
- {isOpen && (
-
-
-
-
- {editingItem ? '编辑' : '新建'}复盘记录
-
-
-
-
-
- 日期
-
-
-
-
- setFormData({ ...formData, date: e.target.value })}
- />
-
-
-
-
- 标题
- setFormData({ ...formData, title: e.target.value })}
- placeholder="例如:本周交易复盘"
- />
-
-
-
- 内容
-
-
-
- 相关股票
-
- setStockInput(e.target.value)}
- placeholder="输入股票代码"
- onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
- />
-
-
-
- {(formData.stocks || []).map((stock, idx) => (
-
-
- {stock}
- setFormData({
- ...formData,
- stocks: formData.stocks.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 标签
-
- setTagInput(e.target.value)}
- placeholder="输入标签"
- onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
- />
-
-
-
- {(formData.tags || []).map((tag, idx) => (
-
-
- {tag}
- setFormData({
- ...formData,
- tags: formData.tags.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
-
-
- }
- >
- 保存
-
-
-
-
- )}
-
- );
-}
diff --git a/src/views/Dashboard/components/InvestmentPlanningCenter.js.old b/src/views/Dashboard/components/InvestmentPlanningCenter.js.old
deleted file mode 100644
index d8c93994..00000000
--- a/src/views/Dashboard/components/InvestmentPlanningCenter.js.old
+++ /dev/null
@@ -1,1420 +0,0 @@
-// src/views/Dashboard/components/InvestmentPlanningCenter.js
-import React, { useState, useEffect, useCallback, createContext, useContext } from 'react';
-import {
- Box,
- Card,
- CardHeader,
- CardBody,
- Heading,
- VStack,
- HStack,
- Text,
- Button,
- Modal,
- ModalOverlay,
- ModalContent,
- ModalHeader,
- ModalFooter,
- ModalBody,
- ModalCloseButton,
- useDisclosure,
- Badge,
- IconButton,
- Flex,
- Grid,
- useColorModeValue,
- Divider,
- Tooltip,
- Icon,
- Input,
- FormControl,
- FormLabel,
- Textarea,
- Select,
- useToast,
- Spinner,
- Center,
- Tag,
- TagLabel,
- TagLeftIcon,
- TagCloseButton,
- Tabs,
- TabList,
- TabPanels,
- Tab,
- TabPanel,
- InputGroup,
- InputLeftElement,
-} from '@chakra-ui/react';
-import {
- FiCalendar,
- FiClock,
- FiStar,
- FiTrendingUp,
- FiPlus,
- FiEdit2,
- FiTrash2,
- FiSave,
- FiX,
- FiTarget,
- FiFileText,
- FiHash,
- FiCheckCircle,
- FiXCircle,
- FiAlertCircle,
-} from 'react-icons/fi';
-import FullCalendar from '@fullcalendar/react';
-import dayGridPlugin from '@fullcalendar/daygrid';
-import interactionPlugin from '@fullcalendar/interaction';
-import dayjs from 'dayjs';
-import 'dayjs/locale/zh-cn';
-import { logger } from '../../../utils/logger';
-import { getApiBase } from '../../../utils/apiConfig';
-import '../components/InvestmentCalendar.css';
-
-dayjs.locale('zh-cn');
-
-// 创建 Context 用于跨标签页共享数据
-const PlanningDataContext = createContext();
-
-export default function InvestmentPlanningCenter() {
- 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 () => {
- 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 = {
- allEvents,
- setAllEvents,
- loadAllData,
- loading,
- setLoading,
- activeTab,
- setActiveTab,
- toast,
- bgColor,
- borderColor,
- textColor,
- secondaryText,
- cardBg,
- };
-
- return (
-
-
-
-
-
-
- 投资规划中心
-
-
-
-
-
-
-
-
- 日历视图
-
-
-
- 我的计划 ({allEvents.filter(e => e.type === 'plan').length})
-
-
-
- 我的复盘 ({allEvents.filter(e => e.type === 'review').length})
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-// 日历视图面板
-function CalendarPanel() {
- const {
- allEvents,
- loadAllData,
- loading,
- setActiveTab,
- toast,
- borderColor,
- secondaryText,
- } = useContext(PlanningDataContext);
-
- const { isOpen, onOpen, onClose } = useDisclosure();
- const { isOpen: isAddOpen, onOpen: onAddOpen, onClose: onAddClose } = useDisclosure();
-
- const [selectedDate, setSelectedDate] = useState(null);
- const [selectedDateEvents, setSelectedDateEvents] = useState([]);
- const [newEvent, setNewEvent] = useState({
- title: '',
- description: '',
- type: 'plan',
- importance: 3,
- stocks: '',
- });
-
- // 转换数据为 FullCalendar 格式
- const calendarEvents = 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: {
- ...event,
- isSystem: event.source === 'future',
- }
- }));
-
- // 处理日期点击
- const handleDateClick = (info) => {
- const clickedDate = dayjs(info.date);
- setSelectedDate(clickedDate);
-
- const dayEvents = allEvents.filter(event =>
- dayjs(event.event_date).isSame(clickedDate, 'day')
- );
- setSelectedDateEvents(dayEvents);
- onOpen();
- };
-
- // 处理事件点击
- const handleEventClick = (info) => {
- 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 handleAddEvent = async () => {
- try {
- const base = getApiBase();
-
- const eventData = {
- ...newEvent,
- event_date: (selectedDate ? selectedDate.format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD')),
- stocks: newEvent.stocks.split(',').map(s => s.trim()).filter(s => s),
- };
-
- const response = await fetch(base + '/api/account/calendar/events', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(eventData),
- });
-
- if (response.ok) {
- const data = await response.json();
- if (data.success) {
- logger.info('CalendarPanel', '添加事件成功', {
- eventTitle: eventData.title,
- eventDate: eventData.event_date
- });
- toast({
- title: '添加成功',
- description: '投资计划已添加',
- status: 'success',
- duration: 3000,
- });
- onAddClose();
- loadAllData();
- setNewEvent({
- title: '',
- description: '',
- type: 'plan',
- importance: 3,
- stocks: '',
- });
- }
- }
- } catch (error) {
- logger.error('CalendarPanel', 'handleAddEvent', error, {
- eventTitle: newEvent?.title
- });
- toast({
- title: '添加失败',
- description: '无法添加投资计划',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除事件
- const handleDeleteEvent = async (eventId) => {
- if (!eventId) {
- logger.warn('CalendarPanel', '删除事件失败', '缺少事件 ID', { eventId });
- toast({
- title: '无法删除',
- description: '缺少事件 ID',
- status: 'error',
- duration: 3000,
- });
- return;
- }
- try {
- const base = getApiBase();
-
- const response = await fetch(base + `/api/account/calendar/events/${eventId}`, {
- method: 'DELETE',
- credentials: 'include',
- });
-
- if (response.ok) {
- logger.info('CalendarPanel', '删除事件成功', { eventId });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadAllData();
- }
- } catch (error) {
- logger.error('CalendarPanel', 'handleDeleteEvent', error, { eventId });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 跳转到计划或复盘标签页
- const handleViewDetails = (event) => {
- if (event.type === 'plan') {
- setActiveTab(1); // 跳转到"我的计划"标签页
- } else if (event.type === 'review') {
- setActiveTab(2); // 跳转到"我的复盘"标签页
- }
- onClose();
- };
-
- return (
-
-
- }
- onClick={() => { if (!selectedDate) setSelectedDate(dayjs()); onAddOpen(); }}
- >
- 添加计划
-
-
-
- {loading ? (
-
-
-
- ) : (
-
-
-
- )}
-
- {/* 查看事件详情 Modal */}
- {isOpen && (
-
-
-
-
- {selectedDate && selectedDate.format('YYYY年MM月DD日')} 的事件
-
-
-
- {selectedDateEvents.length === 0 ? (
-
-
- 当天没有事件
- }
- onClick={() => {
- onClose();
- onAddOpen();
- }}
- >
- 添加投资计划
-
-
-
- ) : (
-
- {selectedDateEvents.map((event, idx) => (
-
-
-
-
-
- {event.title}
-
- {event.source === 'future' ? (
- 系统事件
- ) : event.type === 'plan' ? (
- 我的计划
- ) : (
- 我的复盘
- )}
-
- {event.importance && (
-
-
-
- 重要度: {event.importance}/5
-
-
- )}
-
-
- {!event.source || event.source === 'user' ? (
- <>
-
- }
- size="sm"
- variant="ghost"
- colorScheme="blue"
- onClick={() => handleViewDetails(event)}
- />
-
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDeleteEvent(event.id)}
- />
- >
- ) : null}
-
-
-
- {event.description && (
-
- {event.description}
-
- )}
-
- {event.stocks && event.stocks.length > 0 && (
-
- 相关股票:
- {event.stocks.map((stock, i) => (
-
-
- {stock}
-
- ))}
-
- )}
-
- ))}
-
- )}
-
-
-
-
-
-
- )}
-
- {/* 添加投资计划 Modal */}
- {isAddOpen && (
-
-
-
-
- 添加投资计划
-
-
-
-
-
- 标题
- setNewEvent({ ...newEvent, title: e.target.value })}
- placeholder="例如:关注半导体板块"
- />
-
-
-
- 描述
-
-
-
- 类型
-
-
-
-
- 重要度
-
-
-
-
- 相关股票(用逗号分隔)
- setNewEvent({ ...newEvent, stocks: e.target.value })}
- placeholder="例如:600519,000858,002415"
- />
-
-
-
-
-
-
-
-
-
- )}
-
- );
-}
-
-// 计划列表面板
-function PlansPanel() {
- const {
- allEvents,
- loadAllData,
- loading,
- toast,
- textColor,
- secondaryText,
- cardBg,
- borderColor,
- } = useContext(PlanningDataContext);
-
- const { isOpen, onOpen, onClose } = useDisclosure();
- const [editingItem, setEditingItem] = useState(null);
- const [formData, setFormData] = useState({
- date: dayjs().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'plan',
- stocks: [],
- tags: [],
- status: 'active',
- });
- const [stockInput, setStockInput] = useState('');
- const [tagInput, setTagInput] = useState('');
-
- const plans = allEvents.filter(event => event.type === 'plan' && event.source !== 'future');
-
- // 打开编辑/新建模态框
- const handleOpenModal = (item = null) => {
- if (item) {
- setEditingItem(item);
- setFormData({
- ...item,
- date: dayjs(item.event_date || item.date).format('YYYY-MM-DD'),
- content: item.description || item.content || '',
- });
- } else {
- setEditingItem(null);
- setFormData({
- date: dayjs().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'plan',
- stocks: [],
- tags: [],
- status: 'active',
- });
- }
- onOpen();
- };
-
- // 保存数据
- const handleSave = async () => {
- try {
- const base = getApiBase();
-
- const url = editingItem
- ? base + `/api/account/investment-plans/${editingItem.id}`
- : base + '/api/account/investment-plans';
-
- const method = editingItem ? 'PUT' : 'POST';
-
- const response = await fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(formData),
- });
-
- if (response.ok) {
- logger.info('PlansPanel', `${editingItem ? '更新' : '创建'}成功`, {
- itemId: editingItem?.id,
- title: formData.title,
- });
- toast({
- title: editingItem ? '更新成功' : '创建成功',
- status: 'success',
- duration: 2000,
- });
- onClose();
- loadAllData();
- } else {
- throw new Error('保存失败');
- }
- } catch (error) {
- logger.error('PlansPanel', 'handleSave', error, {
- itemId: editingItem?.id,
- title: formData?.title
- });
- toast({
- title: '保存失败',
- description: '无法保存数据',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除数据
- const handleDelete = async (id) => {
- 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('PlansPanel', '删除成功', { itemId: id });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadAllData();
- }
- } catch (error) {
- logger.error('PlansPanel', 'handleDelete', error, { itemId: id });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 添加股票
- const handleAddStock = () => {
- if (stockInput.trim() && !formData.stocks.includes(stockInput.trim())) {
- setFormData({
- ...formData,
- stocks: [...formData.stocks, stockInput.trim()],
- });
- setStockInput('');
- }
- };
-
- // 添加标签
- const handleAddTag = () => {
- if (tagInput.trim() && !formData.tags.includes(tagInput.trim())) {
- setFormData({
- ...formData,
- tags: [...formData.tags, tagInput.trim()],
- });
- setTagInput('');
- }
- };
-
- // 获取状态信息
- const getStatusInfo = (status) => {
- switch (status) {
- case 'completed':
- return { icon: FiCheckCircle, color: 'green', text: '已完成' };
- case 'cancelled':
- return { icon: FiXCircle, color: 'red', text: '已取消' };
- default:
- return { icon: FiAlertCircle, color: 'blue', text: '进行中' };
- }
- };
-
- // 渲染单个卡片
- const renderCard = (item) => {
- const statusInfo = getStatusInfo(item.status);
-
- return (
-
-
-
-
-
-
-
-
- {item.title}
-
-
-
-
-
- {dayjs(item.event_date || item.date).format('YYYY年MM月DD日')}
-
-
- {statusInfo.text}
-
-
-
-
- }
- size="sm"
- variant="ghost"
- onClick={() => handleOpenModal(item)}
- />
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDelete(item.id)}
- />
-
-
-
- {(item.content || item.description) && (
-
- {item.content || item.description}
-
- )}
-
-
- {item.stocks && item.stocks.length > 0 && (
- <>
- {item.stocks.map((stock, idx) => (
-
-
- {stock}
-
- ))}
- >
- )}
- {item.tags && item.tags.length > 0 && (
- <>
- {item.tags.map((tag, idx) => (
-
-
- {tag}
-
- ))}
- >
- )}
-
-
-
-
- );
- };
-
- return (
-
-
-
- }
- onClick={() => handleOpenModal(null)}
- >
- 新建计划
-
-
-
- {loading ? (
-
-
-
- ) : plans.length === 0 ? (
-
-
-
- 暂无投资计划
- }
- onClick={() => handleOpenModal(null)}
- >
- 创建第一个计划
-
-
-
- ) : (
-
- {plans.map(renderCard)}
-
- )}
-
-
- {/* 编辑/新建模态框 */}
- {isOpen && (
-
-
-
-
- {editingItem ? '编辑' : '新建'}投资计划
-
-
-
-
-
- 日期
-
-
-
-
- setFormData({ ...formData, date: e.target.value })}
- />
-
-
-
-
- 标题
- setFormData({ ...formData, title: e.target.value })}
- placeholder="例如:布局新能源板块"
- />
-
-
-
- 内容
-
-
-
- 相关股票
-
- setStockInput(e.target.value)}
- placeholder="输入股票代码"
- onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
- />
-
-
-
- {(formData.stocks || []).map((stock, idx) => (
-
-
- {stock}
- setFormData({
- ...formData,
- stocks: formData.stocks.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 标签
-
- setTagInput(e.target.value)}
- placeholder="输入标签"
- onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
- />
-
-
-
- {(formData.tags || []).map((tag, idx) => (
-
-
- {tag}
- setFormData({
- ...formData,
- tags: formData.tags.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 状态
-
-
-
-
-
-
- }
- >
- 保存
-
-
-
-
- )}
-
- );
-}
-
-// 复盘列表面板
-function ReviewsPanel() {
- const {
- allEvents,
- loadAllData,
- loading,
- toast,
- textColor,
- secondaryText,
- cardBg,
- borderColor,
- } = useContext(PlanningDataContext);
-
- const { isOpen, onOpen, onClose } = useDisclosure();
- const [editingItem, setEditingItem] = useState(null);
- const [formData, setFormData] = useState({
- date: dayjs().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'review',
- stocks: [],
- tags: [],
- status: 'active',
- });
- const [stockInput, setStockInput] = useState('');
- const [tagInput, setTagInput] = useState('');
-
- const reviews = allEvents.filter(event => event.type === 'review' && event.source !== 'future');
-
- // 打开编辑/新建模态框
- const handleOpenModal = (item = null) => {
- if (item) {
- setEditingItem(item);
- setFormData({
- ...item,
- date: dayjs(item.event_date || item.date).format('YYYY-MM-DD'),
- content: item.description || item.content || '',
- });
- } else {
- setEditingItem(null);
- setFormData({
- date: dayjs().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'review',
- stocks: [],
- tags: [],
- status: 'active',
- });
- }
- onOpen();
- };
-
- // 保存数据
- const handleSave = async () => {
- try {
- const base = getApiBase();
-
- const url = editingItem
- ? base + `/api/account/investment-plans/${editingItem.id}`
- : base + '/api/account/investment-plans';
-
- const method = editingItem ? 'PUT' : 'POST';
-
- const response = await fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(formData),
- });
-
- if (response.ok) {
- logger.info('ReviewsPanel', `${editingItem ? '更新' : '创建'}成功`, {
- itemId: editingItem?.id,
- title: formData.title,
- });
- toast({
- title: editingItem ? '更新成功' : '创建成功',
- status: 'success',
- duration: 2000,
- });
- onClose();
- loadAllData();
- } else {
- throw new Error('保存失败');
- }
- } catch (error) {
- logger.error('ReviewsPanel', 'handleSave', error, {
- itemId: editingItem?.id,
- title: formData?.title
- });
- toast({
- title: '保存失败',
- description: '无法保存数据',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除数据
- const handleDelete = async (id) => {
- 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('ReviewsPanel', '删除成功', { itemId: id });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadAllData();
- }
- } catch (error) {
- logger.error('ReviewsPanel', 'handleDelete', error, { itemId: id });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 添加股票
- const handleAddStock = () => {
- if (stockInput.trim() && !formData.stocks.includes(stockInput.trim())) {
- setFormData({
- ...formData,
- stocks: [...formData.stocks, stockInput.trim()],
- });
- setStockInput('');
- }
- };
-
- // 添加标签
- const handleAddTag = () => {
- if (tagInput.trim() && !formData.tags.includes(tagInput.trim())) {
- setFormData({
- ...formData,
- tags: [...formData.tags, tagInput.trim()],
- });
- setTagInput('');
- }
- };
-
- // 渲染单个卡片
- const renderCard = (item) => {
- return (
-
-
-
-
-
-
-
-
- {item.title}
-
-
-
-
-
- {dayjs(item.event_date || item.date).format('YYYY年MM月DD日')}
-
-
-
-
- }
- size="sm"
- variant="ghost"
- onClick={() => handleOpenModal(item)}
- />
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDelete(item.id)}
- />
-
-
-
- {(item.content || item.description) && (
-
- {item.content || item.description}
-
- )}
-
-
- {item.stocks && item.stocks.length > 0 && (
- <>
- {item.stocks.map((stock, idx) => (
-
-
- {stock}
-
- ))}
- >
- )}
- {item.tags && item.tags.length > 0 && (
- <>
- {item.tags.map((tag, idx) => (
-
-
- {tag}
-
- ))}
- >
- )}
-
-
-
-
- );
- };
-
- return (
-
-
-
- }
- onClick={() => handleOpenModal(null)}
- >
- 新建复盘
-
-
-
- {loading ? (
-
-
-
- ) : reviews.length === 0 ? (
-
-
-
- 暂无复盘记录
- }
- onClick={() => handleOpenModal(null)}
- >
- 创建第一个复盘
-
-
-
- ) : (
-
- {reviews.map(renderCard)}
-
- )}
-
-
- {/* 编辑/新建模态框 */}
- {isOpen && (
-
-
-
-
- {editingItem ? '编辑' : '新建'}复盘记录
-
-
-
-
-
- 日期
-
-
-
-
- setFormData({ ...formData, date: e.target.value })}
- />
-
-
-
-
- 标题
- setFormData({ ...formData, title: e.target.value })}
- placeholder="例如:本周交易复盘"
- />
-
-
-
- 内容
-
-
-
- 相关股票
-
- setStockInput(e.target.value)}
- placeholder="输入股票代码"
- onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
- />
-
-
-
- {(formData.stocks || []).map((stock, idx) => (
-
-
- {stock}
- setFormData({
- ...formData,
- stocks: formData.stocks.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 标签
-
- setTagInput(e.target.value)}
- placeholder="输入标签"
- onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
- />
-
-
-
- {(formData.tags || []).map((tag, idx) => (
-
-
- {tag}
- setFormData({
- ...formData,
- tags: formData.tags.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
-
-
- }
- >
- 保存
-
-
-
-
- )}
-
- );
-}
diff --git a/src/views/Dashboard/components/InvestmentPlansAndReviews.js.bak b/src/views/Dashboard/components/InvestmentPlansAndReviews.js.bak
deleted file mode 100644
index 4d769fc9..00000000
--- a/src/views/Dashboard/components/InvestmentPlansAndReviews.js.bak
+++ /dev/null
@@ -1,587 +0,0 @@
-// src/views/Dashboard/components/InvestmentPlansAndReviews.js
-import React, { useState, useEffect, useCallback } from 'react';
-import {
- Box,
- Card,
- CardHeader,
- CardBody,
- Heading,
- VStack,
- HStack,
- Text,
- Button,
- Modal,
- ModalOverlay,
- ModalContent,
- ModalHeader,
- ModalFooter,
- ModalBody,
- ModalCloseButton,
- useDisclosure,
- Badge,
- IconButton,
- Flex,
- useColorModeValue,
- Divider,
- Icon,
- Input,
- FormControl,
- FormLabel,
- Textarea,
- Select,
- useToast,
- Spinner,
- Center,
- Tag,
- TagLabel,
- TagLeftIcon,
- TagCloseButton,
- Grid,
- Tabs,
- TabList,
- TabPanels,
- Tab,
- TabPanel,
- InputGroup,
- InputLeftElement,
-} from '@chakra-ui/react';
-import {
- FiCalendar,
- FiClock,
- FiEdit2,
- FiTrash2,
- FiSave,
- FiPlus,
- FiFileText,
- FiTarget,
- FiTrendingUp,
- FiHash,
- FiCheckCircle,
- FiXCircle,
- FiAlertCircle,
-} from 'react-icons/fi';
-import moment from 'moment';
-import 'moment/locale/zh-cn';
-import { logger } from '../../../utils/logger';
-
-moment.locale('zh-cn');
-
-export default function InvestmentPlansAndReviews({ type = 'both' }) {
- const { isOpen, onOpen, onClose } = useDisclosure();
- 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 [plans, setPlans] = useState([]);
- const [reviews, setReviews] = useState([]);
- const [loading, setLoading] = useState(false);
- const [editingItem, setEditingItem] = useState(null);
- const [formData, setFormData] = useState({
- date: moment().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: 'plan',
- stocks: [],
- tags: [],
- status: 'active',
- });
- const [stockInput, setStockInput] = useState('');
- const [tagInput, setTagInput] = useState('');
-
- // 加载数据
- const loadData = useCallback(async () => {
- try {
- setLoading(true);
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const response = await fetch(base + '/api/account/investment-plans', {
- credentials: 'include'
- });
-
- if (response.ok) {
- const data = await response.json();
- if (data.success) {
- const allItems = data.data || [];
- setPlans(allItems.filter(item => item.type === 'plan'));
- setReviews(allItems.filter(item => item.type === 'review'));
- logger.debug('InvestmentPlansAndReviews', '数据加载成功', {
- plansCount: allItems.filter(item => item.type === 'plan').length,
- reviewsCount: allItems.filter(item => item.type === 'review').length
- });
- }
- }
- } catch (error) {
- logger.error('InvestmentPlansAndReviews', 'loadData', error);
- // ❌ 移除数据加载失败 toast(非关键操作)
- } finally {
- setLoading(false);
- }
- }, []); // ✅ 移除 toast 依赖
-
- useEffect(() => {
- loadData();
- }, [loadData]);
-
- // 打开编辑/新建模态框
- const handleOpenModal = (item = null, itemType = 'plan') => {
- if (item) {
- setEditingItem(item);
- setFormData({
- ...item,
- date: moment(item.date).format('YYYY-MM-DD'),
- });
- } else {
- setEditingItem(null);
- setFormData({
- date: moment().format('YYYY-MM-DD'),
- title: '',
- content: '',
- type: itemType,
- stocks: [],
- tags: [],
- status: 'active',
- });
- }
- onOpen();
- };
-
- // 保存数据
- const handleSave = async () => {
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const url = editingItem
- ? base + `/api/account/investment-plans/${editingItem.id}`
- : base + '/api/account/investment-plans';
-
- const method = editingItem ? 'PUT' : 'POST';
-
- const response = await fetch(url, {
- method,
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include',
- body: JSON.stringify(formData),
- });
-
- if (response.ok) {
- logger.info('InvestmentPlansAndReviews', `${editingItem ? '更新' : '创建'}成功`, {
- itemId: editingItem?.id,
- title: formData.title,
- type: formData.type
- });
- toast({
- title: editingItem ? '更新成功' : '创建成功',
- status: 'success',
- duration: 2000,
- });
- onClose();
- loadData();
- } else {
- throw new Error('保存失败');
- }
- } catch (error) {
- logger.error('InvestmentPlansAndReviews', 'handleSave', error, {
- itemId: editingItem?.id,
- title: formData?.title
- });
- toast({
- title: '保存失败',
- description: '无法保存数据',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 删除数据
- const handleDelete = async (id) => {
- if (!window.confirm('确定要删除吗?')) return;
-
- try {
- const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
-
- const response = await fetch(base + `/api/account/investment-plans/${id}`, {
- method: 'DELETE',
- credentials: 'include',
- });
-
- if (response.ok) {
- logger.info('InvestmentPlansAndReviews', '删除成功', { itemId: id });
- toast({
- title: '删除成功',
- status: 'success',
- duration: 2000,
- });
- loadData();
- }
- } catch (error) {
- logger.error('InvestmentPlansAndReviews', 'handleDelete', error, { itemId: id });
- toast({
- title: '删除失败',
- status: 'error',
- duration: 3000,
- });
- }
- };
-
- // 添加股票
- const handleAddStock = () => {
- if (stockInput.trim() && !formData.stocks.includes(stockInput.trim())) {
- setFormData({
- ...formData,
- stocks: [...formData.stocks, stockInput.trim()],
- });
- setStockInput('');
- }
- };
-
- // 添加标签
- const handleAddTag = () => {
- if (tagInput.trim() && !formData.tags.includes(tagInput.trim())) {
- setFormData({
- ...formData,
- tags: [...formData.tags, tagInput.trim()],
- });
- setTagInput('');
- }
- };
-
- // 获取状态图标和颜色
- const getStatusInfo = (status) => {
- switch (status) {
- case 'completed':
- return { icon: FiCheckCircle, color: 'green' };
- case 'cancelled':
- return { icon: FiXCircle, color: 'red' };
- default:
- return { icon: FiAlertCircle, color: 'blue' };
- }
- };
-
- // 渲染单个卡片
- const renderCard = (item) => {
- const statusInfo = getStatusInfo(item.status);
-
- return (
-
-
-
-
-
-
-
-
- {item.title}
-
-
-
-
-
- {moment(item.date).format('YYYY年MM月DD日')}
-
- }
- >
- {item.status === 'active' ? '进行中' :
- item.status === 'completed' ? '已完成' : '已取消'}
-
-
-
-
- }
- size="sm"
- variant="ghost"
- onClick={() => handleOpenModal(item)}
- />
- }
- size="sm"
- variant="ghost"
- colorScheme="red"
- onClick={() => handleDelete(item.id)}
- />
-
-
-
- {item.content && (
-
- {item.content}
-
- )}
-
-
- {item.stocks && item.stocks.length > 0 && (
- <>
- {item.stocks.map((stock, idx) => (
-
-
- {stock}
-
- ))}
- >
- )}
- {item.tags && item.tags.length > 0 && (
- <>
- {item.tags.map((tag, idx) => (
-
-
- {tag}
-
- ))}
- >
- )}
-
-
-
-
- );
- };
-
- return (
-
-
-
-
-
- 我的计划 ({plans.length})
-
-
-
- 我的复盘 ({reviews.length})
-
-
-
-
- {/* 计划面板 */}
-
-
-
- }
- onClick={() => handleOpenModal(null, 'plan')}
- >
- 新建计划
-
-
-
- {loading ? (
-
-
-
- ) : plans.length === 0 ? (
-
-
-
- 暂无投资计划
- }
- onClick={() => handleOpenModal(null, 'plan')}
- >
- 创建第一个计划
-
-
-
- ) : (
-
- {plans.map(renderCard)}
-
- )}
-
-
-
- {/* 复盘面板 */}
-
-
-
- }
- onClick={() => handleOpenModal(null, 'review')}
- >
- 新建复盘
-
-
-
- {loading ? (
-
-
-
- ) : reviews.length === 0 ? (
-
-
-
- 暂无复盘记录
- }
- onClick={() => handleOpenModal(null, 'review')}
- >
- 创建第一个复盘
-
-
-
- ) : (
-
- {reviews.map(renderCard)}
-
- )}
-
-
-
-
-
- {/* 编辑/新建模态框 - 条件渲染 */}
- {isOpen && (
-
-
-
-
- {editingItem ? '编辑' : '新建'}
- {formData.type === 'plan' ? '投资计划' : '复盘记录'}
-
-
-
-
-
- 日期
-
-
-
-
- setFormData({ ...formData, date: e.target.value })}
- />
-
-
-
-
- 标题
- setFormData({ ...formData, title: e.target.value })}
- placeholder={formData.type === 'plan' ? '例如:布局新能源板块' : '例如:本周交易复盘'}
- />
-
-
-
- 内容
-
-
-
- 相关股票
-
- setStockInput(e.target.value)}
- placeholder="输入股票代码"
- onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
- />
-
-
-
- {formData.stocks.map((stock, idx) => (
-
-
- {stock}
- setFormData({
- ...formData,
- stocks: formData.stocks.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 标签
-
- setTagInput(e.target.value)}
- placeholder="输入标签"
- onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
- />
-
-
-
- {formData.tags.map((tag, idx) => (
-
-
- {tag}
- setFormData({
- ...formData,
- tags: formData.tags.filter((_, i) => i !== idx)
- })}
- />
-
- ))}
-
-
-
-
- 状态
-
-
-
-
-
-
- }
- >
- 保存
-
-
-
-
- )}
-
- );
-}