feat: 添加事件详情面板
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// src/views/Community/components/DynamicNewsCard.js
|
||||
// 横向滚动事件卡片组件(实时要闻·动态追踪)
|
||||
|
||||
import React, { forwardRef, useRef, useState } from 'react';
|
||||
import React, { forwardRef, useRef, useState, useEffect } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { ChevronLeftIcon, ChevronRightIcon, TimeIcon } from '@chakra-ui/icons';
|
||||
import DynamicNewsEventCard from './EventCard/DynamicNewsEventCard';
|
||||
import DynamicNewsDetailPanel from './DynamicNewsDetailPanel';
|
||||
|
||||
/**
|
||||
* 实时要闻·动态追踪 - 横向滚动卡片组件
|
||||
@@ -43,6 +44,14 @@ const DynamicNewsCard = forwardRef(({
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||
const [showRightArrow, setShowRightArrow] = useState(true);
|
||||
const [selectedEvent, setSelectedEvent] = useState(null);
|
||||
|
||||
// 默认选中第一个事件
|
||||
useEffect(() => {
|
||||
if (events && events.length > 0 && !selectedEvent) {
|
||||
setSelectedEvent(events[0]);
|
||||
}
|
||||
}, [events, selectedEvent]);
|
||||
|
||||
// 滚动到左侧
|
||||
const scrollLeft = () => {
|
||||
@@ -214,11 +223,15 @@ const DynamicNewsCard = forwardRef(({
|
||||
index={index}
|
||||
isFollowing={false}
|
||||
followerCount={event.follower_count || 0}
|
||||
onEventClick={onEventClick}
|
||||
onEventClick={(clickedEvent) => {
|
||||
setSelectedEvent(clickedEvent);
|
||||
if (onEventClick) onEventClick(clickedEvent);
|
||||
}}
|
||||
onTitleClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onEventClick(event);
|
||||
setSelectedEvent(event);
|
||||
if (onEventClick) onEventClick(event);
|
||||
}}
|
||||
onToggleFollow={() => {}}
|
||||
timelineStyle={getTimelineBoxStyle()}
|
||||
@@ -229,6 +242,13 @@ const DynamicNewsCard = forwardRef(({
|
||||
</Flex>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 详情面板 */}
|
||||
{!loading && events && events.length > 0 && (
|
||||
<Box mt={6}>
|
||||
<DynamicNewsDetailPanel event={selectedEvent} />
|
||||
</Box>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
|
||||
201
src/views/Community/components/DynamicNewsDetailPanel.js
Normal file
201
src/views/Community/components/DynamicNewsDetailPanel.js
Normal file
@@ -0,0 +1,201 @@
|
||||
// src/views/Community/components/DynamicNewsDetailPanel.js
|
||||
// 动态新闻详情面板组件(固定展示,非弹窗)
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
CardBody,
|
||||
VStack,
|
||||
HStack,
|
||||
Text,
|
||||
Badge,
|
||||
Tag,
|
||||
Divider,
|
||||
Heading,
|
||||
List,
|
||||
ListItem,
|
||||
Button,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react';
|
||||
import moment from 'moment';
|
||||
import { getImportanceConfig } from '../../../constants/importanceLevels';
|
||||
import HistoricalEvents from '../../EventDetail/components/HistoricalEvents';
|
||||
import TransmissionChainAnalysis from '../../EventDetail/components/TransmissionChainAnalysis';
|
||||
import { eventService } from '../../../services/eventService';
|
||||
|
||||
/**
|
||||
* 动态新闻详情面板组件
|
||||
* @param {Object} props
|
||||
* @param {Object} props.event - 事件对象(包含详情数据)
|
||||
*/
|
||||
const DynamicNewsDetailPanel = ({ event }) => {
|
||||
const cardBg = useColorModeValue('white', 'gray.800');
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
||||
const headingColor = useColorModeValue('gray.700', 'gray.200');
|
||||
const textColor = useColorModeValue('gray.600', 'gray.400');
|
||||
|
||||
if (!event) {
|
||||
return (
|
||||
<Card bg={cardBg} borderColor={borderColor} borderWidth="1px">
|
||||
<CardBody>
|
||||
<Text color={textColor} textAlign="center">请选择一个事件查看详情</Text>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const importance = getImportanceConfig(event.importance);
|
||||
|
||||
// 渲染涨跌幅标签
|
||||
const renderPriceTag = (value, label) => {
|
||||
if (value === null || value === undefined) return `${label}: --`;
|
||||
|
||||
const color = value > 0 ? 'red' : value < 0 ? 'green' : 'gray';
|
||||
const prefix = value > 0 ? '+' : '';
|
||||
|
||||
return (
|
||||
<Tag colorScheme={color} mr={2}>
|
||||
{label}: {prefix}{value.toFixed(2)}%
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card bg={cardBg} borderColor={borderColor} borderWidth="1px">
|
||||
<CardBody>
|
||||
<VStack align="stretch" spacing={4}>
|
||||
{/* 标题 */}
|
||||
<Heading size="md" color={headingColor}>
|
||||
{event.title}
|
||||
</Heading>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* 基本信息 */}
|
||||
<VStack align="stretch" spacing={2}>
|
||||
<HStack spacing={4} flexWrap="wrap">
|
||||
<Text fontSize="sm" color={textColor}>
|
||||
<strong>创建时间:</strong>
|
||||
{moment(event.created_at).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</Text>
|
||||
<Text fontSize="sm" color={textColor}>
|
||||
<strong>重要性:</strong>
|
||||
<Badge colorScheme={importance.level === 'S' ? 'purple' : importance.level === 'A' ? 'red' : importance.level === 'B' ? 'orange' : 'green'} ml={1}>
|
||||
{importance.level}级
|
||||
</Badge>
|
||||
</Text>
|
||||
<Text fontSize="sm" color={textColor}>
|
||||
<strong>浏览数:</strong>{event.view_count || 0}
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
{/* 涨跌幅统计 */}
|
||||
<HStack spacing={2} flexWrap="wrap">
|
||||
{renderPriceTag(event.related_avg_chg, '平均涨幅')}
|
||||
{renderPriceTag(event.related_max_chg, '最大涨幅')}
|
||||
{renderPriceTag(event.related_week_chg, '周涨幅')}
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
||||
<Divider />
|
||||
|
||||
{/* 事件描述 */}
|
||||
{event.description && (
|
||||
<Box>
|
||||
<Heading size="sm" color={headingColor} mb={2}>
|
||||
事件描述
|
||||
</Heading>
|
||||
<Text fontSize="sm" color={textColor} lineHeight="tall">
|
||||
{event.description}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 相关概念 */}
|
||||
{event.keywords && event.keywords.length > 0 && (
|
||||
<Box>
|
||||
<Heading size="sm" color={headingColor} mb={2}>
|
||||
相关概念
|
||||
</Heading>
|
||||
<HStack spacing={2} flexWrap="wrap">
|
||||
{event.keywords.map((keyword, index) => (
|
||||
<Tag key={index} colorScheme="blue" size="sm">
|
||||
{keyword}
|
||||
</Tag>
|
||||
))}
|
||||
</HStack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 相关股票 */}
|
||||
{event.related_stocks && event.related_stocks.length > 0 && (
|
||||
<Box>
|
||||
<Heading size="sm" color={headingColor} mb={2}>
|
||||
相关股票
|
||||
</Heading>
|
||||
<List spacing={2}>
|
||||
{event.related_stocks.map((stock, index) => (
|
||||
<ListItem
|
||||
key={index}
|
||||
p={2}
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
borderRadius="md"
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<VStack align="start" spacing={0}>
|
||||
<Text fontSize="sm" fontWeight="bold">
|
||||
{stock.stock_name} ({stock.stock_code})
|
||||
</Text>
|
||||
<Text fontSize="xs" color={textColor}>
|
||||
{stock.relation_desc || '相关股票'}
|
||||
</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
size="sm"
|
||||
colorScheme="blue"
|
||||
onClick={() => {
|
||||
const stockCode = stock.stock_code.split('.')[0];
|
||||
window.open(`https://valuefrontier.cn/company?scode=${stockCode}`, '_blank');
|
||||
}}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
</HStack>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 历史事件对比 */}
|
||||
<Box>
|
||||
<Heading size="sm" color={headingColor} mb={2}>
|
||||
历史事件对比
|
||||
</Heading>
|
||||
<HistoricalEvents
|
||||
eventId={event.id}
|
||||
eventTitle={event.title}
|
||||
historicalEvents={event.historical_events || []}
|
||||
eventService={eventService}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 传导链分析 */}
|
||||
<Box>
|
||||
<Heading size="sm" color={headingColor} mb={2}>
|
||||
传导链分析
|
||||
</Heading>
|
||||
<TransmissionChainAnalysis
|
||||
eventId={event.id}
|
||||
eventService={eventService}
|
||||
/>
|
||||
</Box>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicNewsDetailPanel;
|
||||
Reference in New Issue
Block a user