feat: 个人中心页添加mock数据

This commit is contained in:
zdl
2025-10-19 16:17:31 +08:00
parent c0aaa5bde1
commit 923611f3a8
7 changed files with 4000 additions and 828 deletions

499
src/mocks/data/account.js Normal file
View File

@@ -0,0 +1,499 @@
// src/mocks/data/account.js
// 个人中心相关的 Mock 数据
// ==================== 自选股数据 ====================
export const mockWatchlist = [
{
id: 1,
user_id: 1,
stock_code: '600519.SH',
stock_name: '贵州茅台',
industry: '白酒',
current_price: 1650.50,
change_percent: 2.5,
added_at: '2025-01-10T10:30:00Z'
},
{
id: 2,
user_id: 1,
stock_code: '000001.SZ',
stock_name: '平安银行',
industry: '银行',
current_price: 12.34,
change_percent: 4.76,
added_at: '2025-01-15T14:20:00Z'
},
{
id: 3,
user_id: 1,
stock_code: '000858.SZ',
stock_name: '五粮液',
industry: '白酒',
current_price: 156.78,
change_percent: 1.52,
added_at: '2025-01-08T09:15:00Z'
},
{
id: 4,
user_id: 1,
stock_code: '300750.SZ',
stock_name: '宁德时代',
industry: '新能源',
current_price: 168.90,
change_percent: -1.23,
added_at: '2025-01-12T16:45:00Z'
},
{
id: 5,
user_id: 1,
stock_code: '002594.SZ',
stock_name: 'BYD比亚迪',
industry: '新能源汽车',
current_price: 256.88,
change_percent: 3.45,
added_at: '2025-01-05T11:20:00Z'
}
];
// ==================== 实时行情数据 ====================
export const mockRealtimeQuotes = [
{
stock_code: '600519.SH',
current_price: 1650.50,
change_percent: 2.5,
change: 40.25,
volume: 2345678,
turnover: 3945678901.23,
high: 1665.00,
low: 1645.00,
open: 1648.80,
prev_close: 1610.25,
update_time: '15:00:00'
},
{
stock_code: '000001.SZ',
current_price: 12.34,
change_percent: 4.76,
change: 0.56,
volume: 123456789,
turnover: 1523456789.12,
high: 12.50,
low: 11.80,
open: 11.90,
prev_close: 11.78,
update_time: '15:00:00'
},
{
stock_code: '000858.SZ',
current_price: 156.78,
change_percent: 1.52,
change: 2.34,
volume: 45678901,
turnover: 7123456789.45,
high: 158.00,
low: 154.50,
open: 155.00,
prev_close: 154.44,
update_time: '15:00:00'
},
{
stock_code: '300750.SZ',
current_price: 168.90,
change_percent: -1.23,
change: -2.10,
volume: 98765432,
turnover: 16678945612.34,
high: 172.30,
low: 167.50,
open: 171.00,
prev_close: 171.00,
update_time: '15:00:00'
},
{
stock_code: '002594.SZ',
current_price: 256.88,
change_percent: 3.45,
change: 8.56,
volume: 56789012,
turnover: 14567890123.45,
high: 260.00,
low: 252.00,
open: 253.50,
prev_close: 248.32,
update_time: '15:00:00'
}
];
// ==================== 关注事件数据 ====================
export const mockFollowingEvents = [
{
id: 101,
title: '央行宣布降准0.5个百分点释放长期资金约1.2万亿元',
tags: ['货币政策', '央行', '降准', '银行'],
view_count: 12340,
comment_count: 156,
upvote_count: 489,
heat_score: 95,
exceed_expectation_score: 85,
creator: {
id: 1001,
username: '财经分析师',
avatar_url: 'https://i.pravatar.cc/150?img=11'
},
created_at: '2025-01-15T09:00:00Z',
followed_at: '2025-01-15T10:30:00Z'
},
{
id: 102,
title: 'ChatGPT-5 即将发布AI 算力需求将迎来爆发式增长',
tags: ['人工智能', 'ChatGPT', '算力', '科技'],
view_count: 8950,
comment_count: 234,
upvote_count: 567,
heat_score: 88,
exceed_expectation_score: 78,
creator: {
id: 1002,
username: '科技观察者',
avatar_url: 'https://i.pravatar.cc/150?img=12'
},
created_at: '2025-01-14T14:20:00Z',
followed_at: '2025-01-14T15:00:00Z'
},
{
id: 103,
title: '新能源汽车补贴政策延续至2026年行业持续受益',
tags: ['新能源', '汽车', '补贴政策', '产业政策'],
view_count: 6780,
comment_count: 98,
upvote_count: 345,
heat_score: 72,
exceed_expectation_score: 68,
creator: {
id: 1003,
username: '产业研究员',
avatar_url: 'https://i.pravatar.cc/150?img=13'
},
created_at: '2025-01-13T11:15:00Z',
followed_at: '2025-01-13T12:00:00Z'
},
{
id: 104,
title: '芯片法案正式实施,国产半导体迎来黄金发展期',
tags: ['半导体', '芯片', '国产替代', '政策'],
view_count: 9540,
comment_count: 178,
upvote_count: 432,
heat_score: 80,
exceed_expectation_score: 72,
creator: {
id: 1004,
username: '半导体观察',
avatar_url: 'https://i.pravatar.cc/150?img=14'
},
created_at: '2025-01-12T16:30:00Z',
followed_at: '2025-01-12T17:00:00Z'
},
{
id: 105,
title: '医保目录调整,创新药企业有望获得更多市场份额',
tags: ['医药', '医保', '创新药', '政策'],
view_count: 5430,
comment_count: 87,
upvote_count: 234,
heat_score: 65,
exceed_expectation_score: null,
creator: {
id: 1005,
username: '医药行业专家',
avatar_url: 'https://i.pravatar.cc/150?img=15'
},
created_at: '2025-01-11T10:00:00Z',
followed_at: '2025-01-11T11:30:00Z'
}
];
// ==================== 评论数据 ====================
export const mockEventComments = [
{
id: 201,
user_id: 1,
event_id: 101,
event_title: '央行宣布降准0.5个百分点释放长期资金约1.2万亿元',
content: '这次降准对银行股是重大利好!预计四大行和股份制银行都会受益,特别是净息差承压的中小银行。建议重点关注招商银行、兴业银行等优质标的。',
created_at: '2025-01-15T11:20:00Z',
likes: 45,
replies: 12
},
{
id: 202,
user_id: 1,
event_id: 102,
event_title: 'ChatGPT-5 即将发布AI 算力需求将迎来爆发式增长',
content: 'AI 板块又要起飞了重点关注算力基础设施概念股如服务器、芯片、数据中心等。另外AI 应用端也值得关注,特别是已经有成熟产品的公司。',
created_at: '2025-01-14T16:45:00Z',
likes: 38,
replies: 8
},
{
id: 203,
user_id: 1,
event_id: 103,
event_title: '新能源汽车补贴政策延续至2026年行业持续受益',
content: '政策延续对整个产业链都是好消息。上游的锂电池、下游的整车厂都会受益。比亚迪和宁德时代可以继续持有,长期看好新能源汽车的渗透率提升。',
created_at: '2025-01-13T14:30:00Z',
likes: 56,
replies: 15
},
{
id: 204,
user_id: 1,
event_id: 104,
event_title: '芯片法案正式实施,国产半导体迎来黄金发展期',
content: '国产替代是大趋势!设备材料、设计封测、制造都有机会。关注那些有核心技术、已经打入国内大厂供应链的公司。半导体是长期主线,波动中坚定持有。',
created_at: '2025-01-12T18:00:00Z',
likes: 67,
replies: 20
},
{
id: 205,
user_id: 1,
event_id: 105,
event_title: '医保目录调整,创新药企业有望获得更多市场份额',
content: '医保谈判结果出来了,创新药企业普遍受益。重点关注有多个重磅品种的药企,以及 CXO 产业链。医药板块经过调整后,估值已经比较合理,可以逐步配置。',
created_at: '2025-01-11T13:15:00Z',
likes: 42,
replies: 10
}
];
// ==================== 投资计划与复盘数据 ====================
export const mockInvestmentPlans = [
{
id: 301,
user_id: 1,
type: 'plan',
title: '2025年Q1 新能源板块布局计划',
content: '计划在Q1分批建仓新能源板块重点关注宁德时代、比亚迪、隆基绿能三只标的。目标仓位15%预计收益率20%。\n\n具体策略\n1. 宁德时代占比6%等待回调至160元附近分批买入\n2. 比亚迪占比6%,当前价位可以开始建仓\n3. 隆基绿能占比3%,观察光伏行业景气度再决定\n\n风险控制单只个股止损-8%,板块整体止损-10%',
target_date: '2025-03-31',
status: 'in_progress',
created_at: '2025-01-10T10:00:00Z',
updated_at: '2025-01-15T14:30:00Z',
tags: ['新能源', '布局计划', 'Q1计划']
},
{
id: 302,
user_id: 1,
type: 'review',
title: '2024年12月投资复盘 - 白酒板块大涨',
content: '12月白酒板块表现优异贵州茅台上涨12%五粮液上涨8%。\n\n操作回顾\n1. 11月底在1550元加仓茅台获利6.5%\n2. 五粮液持仓未动获利4.2%\n\n经验总结\n- 消费板块在年底有明显的估值修复行情\n- 龙头白马股在市场震荡时更具韧性\n- 应该更大胆一些仓位可以再提高2-3个点\n\n下月计划\n- 继续持有茅台、五粮液,不轻易卖出\n- 关注春节前的消费旺季催化',
target_date: '2024-12-31',
status: 'completed',
created_at: '2025-01-02T09:00:00Z',
updated_at: '2025-01-02T09:00:00Z',
tags: ['月度复盘', '白酒', '2024年12月']
},
{
id: 303,
user_id: 1,
type: 'plan',
title: 'AI 算力板块波段交易计划',
content: '随着ChatGPT-5即将发布AI算力板块有望迎来新一轮炒作。\n\n标的选择\n- 寒武纪AI芯片龙头弹性最大\n- 中科曙光:服务器厂商,业绩支撑\n- 浪潮信息:算力基础设施\n\n交易策略\n- 仓位控制在10%以内(高风险高弹性)\n- 采用金字塔式买入第一笔3%\n- 快进快出涨幅20%分批止盈\n- 破位及时止损,控制在-5%以内',
target_date: '2025-02-28',
status: 'pending',
created_at: '2025-01-14T16:00:00Z',
updated_at: '2025-01-14T16:00:00Z',
tags: ['AI', '算力', '波段交易']
},
{
id: 304,
user_id: 1,
type: 'review',
title: '2024年全年投资总结 - 收益率25.6%',
content: '2024年全年收益率25.6%跑赢沪深300指数12个百分点。\n\n全年亮点\n1. 新能源板块贡献最大年度收益35%\n2. 白酒板块稳健增长年度收益18%\n3. 半导体板块波动较大年度收益8%\n\n教训与反思\n1. 年初追高了一些热门概念股,后续回调损失较大\n2. 止损执行不够坚决,有两次错过最佳止损时机\n3. 仓位管理有待提高,牛市时仓位偏低\n\n2025年目标\n- 收益率目标30%\n- 优化仓位管理,提高资金使用效率\n- 严格执行止损纪律\n- 加强行业研究,提前布局',
target_date: '2024-12-31',
status: 'completed',
created_at: '2025-01-01T10:00:00Z',
updated_at: '2025-01-01T10:00:00Z',
tags: ['年度复盘', '2024年', '总结']
}
];
// ==================== 投资日历事件数据 ====================
export const mockCalendarEvents = [
{
id: 401,
user_id: 1,
title: '贵州茅台年报披露',
date: '2025-12-20',
event_date: '2025-12-20',
type: 'earnings',
category: 'financial_report',
description: '关注营收和净利润增速,以及渠道库存情况',
stock_code: '600519.SH',
stock_name: '贵州茅台',
importance: 5,
source: 'future',
stocks: ['600519'],
created_at: '2025-01-10T10:00:00Z'
},
{
id: 402,
user_id: 1,
title: '宁德时代业绩快报',
date: '2025-11-28',
event_date: '2025-11-28',
type: 'earnings',
category: 'financial_report',
description: '重点关注出货量和单位盈利情况',
stock_code: '300750.SZ',
stock_name: '宁德时代',
importance: 5,
source: 'future',
stocks: ['300750'],
created_at: '2025-01-12T14:00:00Z'
},
{
id: 403,
user_id: 1,
title: '央行货币政策委员会例会',
date: '2025-10-25',
event_date: '2025-10-25',
type: 'policy',
category: 'macro_policy',
description: '关注货币政策基调和利率调整信号',
importance: 4,
source: 'future',
stocks: [],
created_at: '2025-01-08T09:00:00Z'
},
{
id: 404,
user_id: 1,
title: '春节假期后首个交易日',
date: '2025-11-15',
event_date: '2025-11-15',
type: 'reminder',
category: 'trading',
description: '节后第一天,关注资金面和市场情绪',
importance: 3,
source: 'future',
stocks: [],
created_at: '2025-01-05T16:00:00Z'
},
{
id: 405,
user_id: 1,
title: '定投日 - 沪深300ETF',
date: '2025-10-20',
event_date: '2025-10-20',
type: 'reminder',
category: 'investment',
description: '每月20日定投3000元',
importance: 2,
source: 'user',
stocks: [],
is_recurring: true,
recurrence_rule: 'monthly',
created_at: '2024-12-15T10:00:00Z'
},
{
id: 406,
user_id: 1,
title: '美联储FOMC会议',
date: '2025-11-07',
event_date: '2025-11-07',
type: 'policy',
category: 'macro_policy',
description: '关注美联储利率决议和鲍威尔讲话',
importance: 5,
source: 'future',
stocks: [],
created_at: '2025-01-07T11:00:00Z'
},
{
id: 407,
user_id: 1,
title: '持仓股票复盘日',
date: '2025-10-26',
event_date: '2025-10-26',
type: 'reminder',
category: 'review',
description: '每周六进行持仓复盘和下周计划',
importance: 3,
source: 'user',
stocks: [],
is_recurring: true,
recurrence_rule: 'weekly',
created_at: '2025-01-01T10:00:00Z'
}
];
// ==================== 订阅信息数据 ====================
export const mockSubscriptionCurrent = {
type: 'pro',
status: 'active',
is_active: true,
days_left: 90,
end_date: '2025-04-15T23:59:59Z',
plan_name: 'Pro版',
features: [
'无限事件查看',
'实时行情推送',
'专业分析报告',
'优先客服支持',
'关联股票分析',
'历史事件对比'
],
price: 0.01,
currency: 'CNY',
billing_cycle: 'monthly',
auto_renew: true,
next_billing_date: '2025-02-15T00:00:00Z'
};
// ==================== 辅助函数 ====================
// 根据用户ID获取自选股
export function getWatchlistByUserId(userId) {
return mockWatchlist.filter(item => item.user_id === userId);
}
// 根据用户ID获取关注事件
export function getFollowingEventsByUserId(userId) {
return mockFollowingEvents;
}
// 根据用户ID获取评论
export function getCommentsByUserId(userId) {
return mockEventComments.filter(comment => comment.user_id === userId);
}
// 根据用户ID获取投资计划
export function getInvestmentPlansByUserId(userId) {
return mockInvestmentPlans.filter(plan => plan.user_id === userId);
}
// 根据用户ID获取日历事件
export function getCalendarEventsByUserId(userId) {
return mockCalendarEvents.filter(event => event.user_id === userId);
}
// 获取指定日期范围的日历事件
export function getCalendarEventsByDateRange(userId, startDate, endDate) {
const start = new Date(startDate);
const end = new Date(endDate);
return mockCalendarEvents.filter(event => {
if (event.user_id !== userId) return false;
const eventDate = new Date(event.date);
return eventDate >= start && eventDate <= end;
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -311,179 +311,183 @@ export default function InvestmentCalendarChakra() {
)}
</CardBody>
{/* 查看事件详情 Modal */}
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
{selectedDate && selectedDate.format('YYYY年MM月DD日')} 的事件
</ModalHeader>
<ModalCloseButton />
<ModalBody>
{selectedDateEvents.length === 0 ? (
<Center py={8}>
<VStack>
<Text color={secondaryText}>当天没有事件</Text>
<Button
size="sm"
colorScheme="blue"
leftIcon={<FiPlus />}
onClick={() => {
onClose();
onAddOpen();
}}
>
添加投资计划
</Button>
</VStack>
</Center>
) : (
<VStack align="stretch" spacing={4}>
{selectedDateEvents.map((event, idx) => (
<Box
key={idx}
p={4}
borderRadius="md"
border="1px"
borderColor={borderColor}
>
<Flex justify="space-between" align="start" mb={2}>
<VStack align="start" spacing={1} flex={1}>
<HStack>
<Text fontWeight="bold" fontSize="lg">
{event.title}
</Text>
{event.extendedProps?.isSystem ? (
<Badge colorScheme="blue" variant="subtle">系统事件</Badge>
) : (
<Badge colorScheme="purple" variant="subtle">我的计划</Badge>
)}
</HStack>
<HStack spacing={2}>
<Icon as={FiStar} color="yellow.500" />
<Text fontSize="sm" color={secondaryText}>
重要度: {event.extendedProps?.importance || 3}/5
</Text>
</HStack>
</VStack>
{!event.extendedProps?.isSystem && (
<IconButton
icon={<FiTrash2 />}
size="sm"
variant="ghost"
colorScheme="red"
onClick={() => handleDeleteEvent(event.extendedProps?.id)}
/>
{/* 查看事件详情 Modal - 条件渲染 */}
{isOpen && (
<Modal isOpen={isOpen} onClose={onClose} size="xl" closeOnOverlayClick={false} closeOnEsc={true}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{selectedDate && selectedDate.format('YYYY年MM月DD日')} 的事件
</ModalHeader>
<ModalCloseButton />
<ModalBody>
{selectedDateEvents.length === 0 ? (
<Center py={8}>
<VStack>
<Text color={secondaryText}>当天没有事件</Text>
<Button
size="sm"
colorScheme="blue"
leftIcon={<FiPlus />}
onClick={() => {
onClose();
onAddOpen();
}}
>
添加投资计划
</Button>
</VStack>
</Center>
) : (
<VStack align="stretch" spacing={4}>
{selectedDateEvents.map((event, idx) => (
<Box
key={idx}
p={4}
borderRadius="md"
border="1px"
borderColor={borderColor}
>
<Flex justify="space-between" align="start" mb={2}>
<VStack align="start" spacing={1} flex={1}>
<HStack>
<Text fontWeight="bold" fontSize="lg">
{event.title}
</Text>
{event.extendedProps?.isSystem ? (
<Badge colorScheme="blue" variant="subtle">系统事件</Badge>
) : (
<Badge colorScheme="purple" variant="subtle">我的计划</Badge>
)}
</HStack>
<HStack spacing={2}>
<Icon as={FiStar} color="yellow.500" />
<Text fontSize="sm" color={secondaryText}>
重要度: {event.extendedProps?.importance || 3}/5
</Text>
</HStack>
</VStack>
{!event.extendedProps?.isSystem && (
<IconButton
icon={<FiTrash2 />}
size="sm"
variant="ghost"
colorScheme="red"
onClick={() => handleDeleteEvent(event.extendedProps?.id)}
/>
)}
</Flex>
{event.extendedProps?.description && (
<Text fontSize="sm" color={secondaryText} mb={2}>
{event.extendedProps.description}
</Text>
)}
</Flex>
{event.extendedProps?.description && (
<Text fontSize="sm" color={secondaryText} mb={2}>
{event.extendedProps.description}
</Text>
)}
{event.extendedProps?.stocks && event.extendedProps.stocks.length > 0 && (
<HStack spacing={2} flexWrap="wrap">
<Text fontSize="sm" color={secondaryText}>相关股票:</Text>
{event.extendedProps.stocks.map((stock, i) => (
<Tag key={i} size="sm" colorScheme="blue">
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
</Tag>
))}
</HStack>
)}
</Box>
))}
{event.extendedProps?.stocks && event.extendedProps.stocks.length > 0 && (
<HStack spacing={2} flexWrap="wrap">
<Text fontSize="sm" color={secondaryText}>相关股票:</Text>
{event.extendedProps.stocks.map((stock, i) => (
<Tag key={i} size="sm" colorScheme="blue">
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
</Tag>
))}
</HStack>
)}
</Box>
))}
</VStack>
)}
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>关闭</Button>
</ModalFooter>
</ModalContent>
</Modal>
)}
{/* 添加投资计划 Modal - 条件渲染 */}
{isAddOpen && (
<Modal isOpen={isAddOpen} onClose={onAddClose} size="lg" closeOnOverlayClick={false} closeOnEsc={true}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
添加投资计划
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing={4}>
<FormControl isRequired>
<FormLabel>标题</FormLabel>
<Input
value={newEvent.title}
onChange={(e) => setNewEvent({ ...newEvent, title: e.target.value })}
placeholder="例如:关注半导体板块"
/>
</FormControl>
<FormControl>
<FormLabel>描述</FormLabel>
<Textarea
value={newEvent.description}
onChange={(e) => setNewEvent({ ...newEvent, description: e.target.value })}
placeholder="详细描述您的投资计划..."
rows={3}
/>
</FormControl>
<FormControl>
<FormLabel>类型</FormLabel>
<Select
value={newEvent.type}
onChange={(e) => setNewEvent({ ...newEvent, type: e.target.value })}
>
<option value="plan">投资计划</option>
<option value="reminder">提醒事项</option>
<option value="analysis">分析任务</option>
</Select>
</FormControl>
<FormControl>
<FormLabel>重要度</FormLabel>
<Select
value={newEvent.importance}
onChange={(e) => setNewEvent({ ...newEvent, importance: parseInt(e.target.value) })}
>
<option value={5}> 非常重要</option>
<option value={4}> 重要</option>
<option value={3}> 一般</option>
<option value={2}> 次要</option>
<option value={1}> 不重要</option>
</Select>
</FormControl>
<FormControl>
<FormLabel>相关股票用逗号分隔</FormLabel>
<Input
value={newEvent.stocks}
onChange={(e) => setNewEvent({ ...newEvent, stocks: e.target.value })}
placeholder="例如600519,000858,002415"
/>
</FormControl>
</VStack>
)}
</ModalBody>
<ModalFooter>
<Button onClick={onClose}>关闭</Button>
</ModalFooter>
</ModalContent>
</Modal>
{/* 添加投资计划 Modal */}
<Modal isOpen={isAddOpen} onClose={onAddClose} size="lg">
<ModalOverlay />
<ModalContent>
<ModalHeader>
添加投资计划
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing={4}>
<FormControl isRequired>
<FormLabel>标题</FormLabel>
<Input
value={newEvent.title}
onChange={(e) => setNewEvent({ ...newEvent, title: e.target.value })}
placeholder="例如:关注半导体板块"
/>
</FormControl>
<FormControl>
<FormLabel>描述</FormLabel>
<Textarea
value={newEvent.description}
onChange={(e) => setNewEvent({ ...newEvent, description: e.target.value })}
placeholder="详细描述您的投资计划..."
rows={3}
/>
</FormControl>
<FormControl>
<FormLabel>类型</FormLabel>
<Select
value={newEvent.type}
onChange={(e) => setNewEvent({ ...newEvent, type: e.target.value })}
>
<option value="plan">投资计划</option>
<option value="reminder">提醒事项</option>
<option value="analysis">分析任务</option>
</Select>
</FormControl>
<FormControl>
<FormLabel>重要度</FormLabel>
<Select
value={newEvent.importance}
onChange={(e) => setNewEvent({ ...newEvent, importance: parseInt(e.target.value) })}
>
<option value={5}> 非常重要</option>
<option value={4}> 重要</option>
<option value={3}> 一般</option>
<option value={2}> 次要</option>
<option value={1}> 不重要</option>
</Select>
</FormControl>
<FormControl>
<FormLabel>相关股票用逗号分隔</FormLabel>
<Input
value={newEvent.stocks}
onChange={(e) => setNewEvent({ ...newEvent, stocks: e.target.value })}
placeholder="例如600519,000858,002415"
/>
</FormControl>
</VStack>
</ModalBody>
<ModalFooter>
<Button variant="ghost" mr={3} onClick={onAddClose}>
取消
</Button>
<Button
colorScheme="blue"
onClick={handleAddEvent}
isDisabled={!newEvent.title}
>
添加
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</ModalBody>
<ModalFooter>
<Button variant="ghost" mr={3} onClick={onAddClose}>
取消
</Button>
<Button
colorScheme="blue"
onClick={handleAddEvent}
isDisabled={!newEvent.title}
>
添加
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)}
</Card>
);
}

View File

@@ -452,134 +452,136 @@ export default function InvestmentPlansAndReviews({ type = 'both' }) {
</TabPanels>
</Tabs>
{/* 编辑/新建模态框 */}
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
{editingItem ? '编辑' : '新建'}
{formData.type === 'plan' ? '投资计划' : '复盘记录'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing={4}>
<FormControl isRequired>
<FormLabel>日期</FormLabel>
<InputGroup>
<InputLeftElement pointerEvents="none">
<Icon as={FiCalendar} color={secondaryText} />
</InputLeftElement>
{/* 编辑/新建模态框 - 条件渲染 */}
{isOpen && (
<Modal isOpen={isOpen} onClose={onClose} size="xl" closeOnOverlayClick={false} closeOnEsc={true}>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{editingItem ? '编辑' : '新建'}
{formData.type === 'plan' ? '投资计划' : '复盘记录'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing={4}>
<FormControl isRequired>
<FormLabel>日期</FormLabel>
<InputGroup>
<InputLeftElement pointerEvents="none">
<Icon as={FiCalendar} color={secondaryText} />
</InputLeftElement>
<Input
type="date"
value={formData.date}
onChange={(e) => setFormData({ ...formData, date: e.target.value })}
/>
</InputGroup>
</FormControl>
<FormControl isRequired>
<FormLabel>标题</FormLabel>
<Input
type="date"
value={formData.date}
onChange={(e) => setFormData({ ...formData, date: e.target.value })}
value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
placeholder={formData.type === 'plan' ? '例如:布局新能源板块' : '例如:本周交易复盘'}
/>
</InputGroup>
</FormControl>
</FormControl>
<FormControl isRequired>
<FormLabel>标题</FormLabel>
<Input
value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
placeholder={formData.type === 'plan' ? '例如:布局新能源板块' : '例如:本周交易复盘'}
/>
</FormControl>
<FormControl>
<FormLabel>内容</FormLabel>
<Textarea
value={formData.content}
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
placeholder={formData.type === 'plan' ?
'详细描述您的投资计划...' :
'记录您的交易心得和经验教训...'}
rows={6}
/>
</FormControl>
<FormControl>
<FormLabel>相关股票</FormLabel>
<HStack>
<Input
value={stockInput}
onChange={(e) => setStockInput(e.target.value)}
placeholder="输入股票代码"
onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
<FormControl>
<FormLabel>内容</FormLabel>
<Textarea
value={formData.content}
onChange={(e) => setFormData({ ...formData, content: e.target.value })}
placeholder={formData.type === 'plan' ?
'详细描述您的投资计划...' :
'记录您的交易心得和经验教训...'}
rows={6}
/>
<Button onClick={handleAddStock}>添加</Button>
</HStack>
<HStack mt={2} spacing={2} flexWrap="wrap">
{formData.stocks.map((stock, idx) => (
<Tag key={idx} size="sm" colorScheme="blue">
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
<TagCloseButton
onClick={() => setFormData({
...formData,
stocks: formData.stocks.filter((_, i) => i !== idx)
})}
/>
</Tag>
))}
</HStack>
</FormControl>
</FormControl>
<FormControl>
<FormLabel>标签</FormLabel>
<HStack>
<Input
value={tagInput}
onChange={(e) => setTagInput(e.target.value)}
placeholder="输入标签"
onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
/>
<Button onClick={handleAddTag}>添加</Button>
</HStack>
<HStack mt={2} spacing={2} flexWrap="wrap">
{formData.tags.map((tag, idx) => (
<Tag key={idx} size="sm" colorScheme="purple">
<TagLeftIcon as={FiHash} />
<TagLabel>{tag}</TagLabel>
<TagCloseButton
onClick={() => setFormData({
...formData,
tags: formData.tags.filter((_, i) => i !== idx)
})}
/>
</Tag>
))}
</HStack>
</FormControl>
<FormControl>
<FormLabel>相关股票</FormLabel>
<HStack>
<Input
value={stockInput}
onChange={(e) => setStockInput(e.target.value)}
placeholder="输入股票代码"
onKeyPress={(e) => e.key === 'Enter' && handleAddStock()}
/>
<Button onClick={handleAddStock}>添加</Button>
</HStack>
<HStack mt={2} spacing={2} flexWrap="wrap">
{formData.stocks.map((stock, idx) => (
<Tag key={idx} size="sm" colorScheme="blue">
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
<TagCloseButton
onClick={() => setFormData({
...formData,
stocks: formData.stocks.filter((_, i) => i !== idx)
})}
/>
</Tag>
))}
</HStack>
</FormControl>
<FormControl>
<FormLabel>状态</FormLabel>
<Select
value={formData.status}
onChange={(e) => setFormData({ ...formData, status: e.target.value })}
>
<option value="active">进行中</option>
<option value="completed">已完成</option>
<option value="cancelled">已取消</option>
</Select>
</FormControl>
</VStack>
</ModalBody>
<ModalFooter>
<Button variant="ghost" mr={3} onClick={onClose}>
取消
</Button>
<Button
colorScheme="blue"
onClick={handleSave}
isDisabled={!formData.title || !formData.date}
leftIcon={<FiSave />}
>
保存
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<FormControl>
<FormLabel>标签</FormLabel>
<HStack>
<Input
value={tagInput}
onChange={(e) => setTagInput(e.target.value)}
placeholder="输入标签"
onKeyPress={(e) => e.key === 'Enter' && handleAddTag()}
/>
<Button onClick={handleAddTag}>添加</Button>
</HStack>
<HStack mt={2} spacing={2} flexWrap="wrap">
{formData.tags.map((tag, idx) => (
<Tag key={idx} size="sm" colorScheme="purple">
<TagLeftIcon as={FiHash} />
<TagLabel>{tag}</TagLabel>
<TagCloseButton
onClick={() => setFormData({
...formData,
tags: formData.tags.filter((_, i) => i !== idx)
})}
/>
</Tag>
))}
</HStack>
</FormControl>
<FormControl>
<FormLabel>状态</FormLabel>
<Select
value={formData.status}
onChange={(e) => setFormData({ ...formData, status: e.target.value })}
>
<option value="active">进行中</option>
<option value="completed">已完成</option>
<option value="cancelled">已取消</option>
</Select>
</FormControl>
</VStack>
</ModalBody>
<ModalFooter>
<Button variant="ghost" mr={3} onClick={onClose}>
取消
</Button>
<Button
colorScheme="blue"
onClick={handleSave}
isDisabled={!formData.title || !formData.date}
leftIcon={<FiSave />}
>
保存
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)}
</Box>
);
}

View File

@@ -665,18 +665,19 @@ function Subscription() {
)))}
</Grid>
{/* 支付模态框 */}
<Modal
isOpen={isPaymentModalOpen}
onClose={() => {
stopAutoPaymentCheck();
setPaymentOrder(null);
setPaymentCountdown(0);
onPaymentModalClose();
}}
size='lg'
closeOnOverlayClick={false}
>
{/* 支付模态框 - 条件渲染 */}
{isPaymentModalOpen && (
<Modal
isOpen={isPaymentModalOpen}
onClose={() => {
stopAutoPaymentCheck();
setPaymentOrder(null);
setPaymentCountdown(0);
onPaymentModalClose();
}}
size='lg'
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>
@@ -862,7 +863,8 @@ function Subscription() {
)}
</ModalBody>
</ModalContent>
</Modal>
</Modal>
)}
{/* 调试面板 */}
{process.env.NODE_ENV === 'development' && (