feat: 添加投资日历mock数据

投资日历提取计划列表卡片组件
This commit is contained in:
zdl
2025-12-05 13:35:12 +08:00
parent d296b0919c
commit 863212f53f
3 changed files with 183 additions and 64 deletions

View File

@@ -537,6 +537,31 @@ export const mockFutureEvents = [
];
export const mockCalendarEvents = [
{
id: 408,
user_id: 1,
title: '2025中医药高质量发展大会将于12月5日至7日举办',
date: '2025-12-05',
event_date: '2025-12-05',
type: 'policy',
category: 'industry_event',
description: `基于提供的路演记录、新闻动态以及上市公司公告,以下是与"2025中医药高质量发展大会将于12月5日至7日举办"相关的信息整理:
事件背景:
"2025中医药高质量发展大会"将于12月5日至7日在北京召开由国家中医药管理局主办旨在总结十四五期间中医药发展成果部署下一阶段重点任务。大会主题为"守正创新、传承发展",将邀请国内外中医药领域专家学者、企业代表共商中医药现代化发展路径。
政策支持:
1. 国务院办公厅印发《中医药振兴发展重大工程实施方案》明确到2025年中医药服务体系更加完善
2. 国家医保局持续推进中成药集采,优质中药企业有望受益于市场集中度提升
3. 各地出台中医药产业发展支持政策,加大对中药创新药研发的资金支持
行业展望:
中医药行业正处于政策红利期,创新中药、配方颗粒、中药材种植等细分领域景气度较高。预计大会将释放更多利好政策信号,推动行业高质量发展。`,
importance: 5,
source: 'future',
stocks: ['002424.SZ', '002873.SZ', '600518.SH', '002907.SZ', '600129.SH', '300519.SZ', '300878.SZ', '002275.SZ', '600222.SH'],
created_at: '2025-12-01T10:00:00Z'
},
{
id: 401,
user_id: 1,

View File

@@ -6,31 +6,20 @@
import React, { useState } from 'react';
import {
Box,
Button,
Badge,
Flex,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
useDisclosure,
VStack,
HStack,
Text,
Spinner,
Center,
Icon,
Tag,
TagLabel,
TagLeftIcon,
} from '@chakra-ui/react';
import {
FiStar,
FiTrendingUp,
} from 'react-icons/fi';
import { FiCalendar } from 'react-icons/fi';
import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
@@ -40,6 +29,7 @@ import dayjs, { Dayjs } from 'dayjs';
import 'dayjs/locale/zh-cn';
import { usePlanningData } from './PlanningContext';
import { EventDetailCard } from './EventDetailCard';
import type { InvestmentEvent } from '@/types';
dayjs.locale('zh-cn');
@@ -161,68 +151,27 @@ export const CalendarPanel: React.FC = () => {
<ModalBody>
{selectedDateEvents.length === 0 ? (
<Center py={8}>
<Text color={secondaryText}></Text>
<VStack spacing={3}>
<Icon as={FiCalendar} boxSize={10} color="gray.300" />
<Text color={secondaryText}></Text>
<Text fontSize="sm" color={secondaryText}>
</Text>
</VStack>
</Center>
) : (
<VStack align="stretch" spacing={4}>
{selectedDateEvents.map((event, idx) => (
<Box
<EventDetailCard
key={idx}
p={4}
borderRadius="md"
border="1px"
event={event}
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.source === 'future' ? (
<Badge colorScheme="blue" variant="subtle"></Badge>
) : event.type === 'plan' ? (
<Badge colorScheme="purple" variant="subtle"></Badge>
) : (
<Badge colorScheme="green" variant="subtle"></Badge>
)}
</HStack>
{event.importance && (
<HStack spacing={2}>
<Icon as={FiStar} color="yellow.500" />
<Text fontSize="sm" color={secondaryText}>
: {event.importance}/5
</Text>
</HStack>
)}
</VStack>
</Flex>
{event.description && (
<Text fontSize="sm" color={secondaryText} mb={2}>
{event.description}
</Text>
)}
{event.stocks && event.stocks.length > 0 && (
<HStack spacing={2} flexWrap="wrap">
<Text fontSize="sm" color={secondaryText}>:</Text>
{event.stocks.map((stock, i) => (
<Tag key={i} size="sm" colorScheme="blue">
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
</Tag>
))}
</HStack>
)}
</Box>
secondaryText={secondaryText}
/>
))}
</VStack>
)}
</ModalBody>
<ModalFooter>
<Button onClick={onClose}></Button>
</ModalFooter>
</ModalContent>
</Modal>
)}

View File

@@ -0,0 +1,145 @@
/**
* EventDetailCard - 事件详情卡片组件
* 用于日历视图中展示单个事件的详细信息
*/
import React, { useState, useRef, useEffect } from 'react';
import {
Box,
Badge,
Flex,
HStack,
Text,
Tag,
TagLabel,
TagLeftIcon,
Button,
useColorModeValue,
} from '@chakra-ui/react';
import {
FiTrendingUp,
FiChevronDown,
FiChevronUp,
} from 'react-icons/fi';
import type { InvestmentEvent } from '@/types';
/**
* EventDetailCard Props
*/
export interface EventDetailCardProps {
/** 事件数据 */
event: InvestmentEvent;
/** 边框颜色 */
borderColor?: string;
/** 次要文字颜色 */
secondaryText?: string;
}
/**
* 最大显示行数
*/
const MAX_LINES = 3;
/**
* EventDetailCard 组件
*/
export const EventDetailCard: React.FC<EventDetailCardProps> = ({
event,
borderColor: borderColorProp,
secondaryText: secondaryTextProp,
}) => {
const [isExpanded, setIsExpanded] = useState(false);
const [isOverflow, setIsOverflow] = useState(false);
const descriptionRef = useRef<HTMLParagraphElement>(null);
// 默认颜色
const defaultBorderColor = useColorModeValue('gray.200', 'gray.600');
const defaultSecondaryText = useColorModeValue('gray.600', 'gray.400');
const borderColor = borderColorProp || defaultBorderColor;
const secondaryText = secondaryTextProp || defaultSecondaryText;
// 检测内容是否溢出
useEffect(() => {
const el = descriptionRef.current;
if (el) {
// 计算行高和最大高度
const lineHeight = parseInt(getComputedStyle(el).lineHeight) || 20;
const maxHeight = lineHeight * MAX_LINES;
setIsOverflow(el.scrollHeight > maxHeight + 5); // 5px 容差
}
}, [event.description]);
// 获取事件类型标签
const getEventBadge = () => {
if (event.source === 'future') {
return <Badge colorScheme="blue" variant="subtle"></Badge>;
} else if (event.type === 'plan') {
return <Badge colorScheme="purple" variant="subtle"></Badge>;
} else if (event.type === 'review') {
return <Badge colorScheme="green" variant="subtle"></Badge>;
}
return null;
};
return (
<Box
p={4}
borderRadius="md"
border="1px"
borderColor={borderColor}
>
{/* 标题和标签 */}
<Flex justify="space-between" align="start" mb={2}>
<HStack flexWrap="wrap" flex={1}>
<Text fontWeight="bold" fontSize="lg">
{event.title}
</Text>
{getEventBadge()}
</HStack>
</Flex>
{/* 描述内容 - 支持展开/收起 */}
{event.description && (
<Box mb={2}>
<Text
ref={descriptionRef}
fontSize="sm"
color={secondaryText}
noOfLines={isExpanded ? undefined : MAX_LINES}
whiteSpace="pre-wrap"
>
{event.description}
</Text>
{isOverflow && (
<Button
size="xs"
variant="link"
colorScheme="blue"
mt={1}
onClick={() => setIsExpanded(!isExpanded)}
rightIcon={isExpanded ? <FiChevronUp /> : <FiChevronDown />}
>
{isExpanded ? '收起' : '展开'}
</Button>
)}
</Box>
)}
{/* 相关股票 */}
{event.stocks && event.stocks.length > 0 && (
<HStack spacing={2} flexWrap="wrap">
<Text fontSize="sm" color={secondaryText}>:</Text>
{event.stocks.map((stock, i) => (
<Tag key={i} size="sm" colorScheme="blue" mb={1}>
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
</Tag>
))}
</HStack>
)}
</Box>
);
};
export default EventDetailCard;