146 lines
4.0 KiB
TypeScript
146 lines
4.0 KiB
TypeScript
/**
|
|
* 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={{ base: 3, md: 4 }}
|
|
borderRadius="md"
|
|
border="1px"
|
|
borderColor={borderColor}
|
|
>
|
|
{/* 标题和标签 */}
|
|
<Flex justify="space-between" align="start" mb={{ base: 1, md: 2 }} gap={{ base: 1, md: 2 }}>
|
|
<HStack flexWrap="wrap" flex={1} spacing={{ base: 1, md: 2 }} gap={1}>
|
|
<Text fontWeight="bold" fontSize={{ base: 'md', md: 'lg' }}>
|
|
{event.title}
|
|
</Text>
|
|
{getEventBadge()}
|
|
</HStack>
|
|
</Flex>
|
|
|
|
{/* 描述内容 - 支持展开/收起 */}
|
|
{event.description && (
|
|
<Box mb={{ base: 1, md: 2 }}>
|
|
<Text
|
|
ref={descriptionRef}
|
|
fontSize={{ base: 'xs', md: '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={{ base: 1, md: 2 }} flexWrap="wrap" gap={1}>
|
|
<Text fontSize={{ base: 'xs', md: '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;
|