// src/views/Community/components/DynamicNewsDetail/StockListItem.js // 股票卡片组件(融合表格功能的卡片样式) import React, { useState } from 'react'; import { Box, Flex, VStack, HStack, Text, Button, IconButton, Collapse, Tooltip, useColorModeValue, } from '@chakra-ui/react'; import { StarIcon } from '@chakra-ui/icons'; import MiniTimelineChart from '../StockDetailPanel/components/MiniTimelineChart'; import MiniKLineChart from './MiniKLineChart'; import StockChartModal from '../../../../components/StockChart/StockChartModal'; /** * 股票卡片组件 * @param {Object} props * @param {Object} props.stock - 股票对象 * @param {string} props.stock.stock_name - 股票名称 * @param {string} props.stock.stock_code - 股票代码 * @param {string} props.stock.relation_desc - 关联描述 * @param {Object} props.quote - 股票行情数据(可选) * @param {number} props.quote.change - 涨跌幅 * @param {string} props.eventTime - 事件时间(可选) * @param {boolean} props.isInWatchlist - 是否在自选股中 * @param {Function} props.onWatchlistToggle - 切换自选股回调 */ const StockListItem = ({ stock, quote = null, eventTime = null, isInWatchlist = false, onWatchlistToggle }) => { const cardBg = useColorModeValue('white', 'gray.800'); const borderColor = useColorModeValue('gray.200', 'gray.700'); const codeColor = useColorModeValue('blue.600', 'blue.300'); const nameColor = useColorModeValue('gray.700', 'gray.300'); const descColor = useColorModeValue('gray.600', 'gray.400'); const dividerColor = useColorModeValue('gray.200', 'gray.600'); const [isDescExpanded, setIsDescExpanded] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); const handleViewDetail = () => { const stockCode = stock.stock_code.split('.')[0]; window.open(`https://valuefrontier.cn/company?scode=${stockCode}`, '_blank'); }; const handleWatchlistClick = (e) => { e.stopPropagation(); onWatchlistToggle?.(stock.stock_code, isInWatchlist); }; // 格式化涨跌幅显示 const formatChange = (value) => { if (value === null || value === undefined || isNaN(value)) return '--'; const prefix = value > 0 ? '+' : ''; return `${prefix}${parseFloat(value).toFixed(2)}%`; }; // 获取涨跌幅颜色 const getChangeColor = (value) => { const num = parseFloat(value); if (isNaN(num) || num === 0) return 'gray.500'; return num > 0 ? 'red.500' : 'green.500'; }; // 获取涨跌幅数据(优先使用 quote,fallback 到 stock) const change = quote?.change ?? stock.daily_change ?? null; // 处理关联描述 const getRelationDesc = () => { const relationDesc = stock.relation_desc; if (!relationDesc) return '--'; if (typeof relationDesc === 'string') { return relationDesc; } else if (typeof relationDesc === 'object' && relationDesc.data && Array.isArray(relationDesc.data)) { // 新格式:{data: [{query_part: "...", sentences: "..."}]} return relationDesc.data .map(item => item.query_part || item.sentences || '') .filter(s => s) .join(';') || '--'; } return '--'; }; const relationText = getRelationDesc(); const maxLength = 50; // 收缩时显示的最大字符数 const needTruncate = relationText && relationText !== '--' && relationText.length > maxLength; return ( <> {/* 单行紧凑布局:名称+涨跌幅 | 分时图 | K线图 | 关联描述 */} {/* 左侧:股票名称 + 涨跌幅(垂直排列) */} {stock.stock_name} {formatChange(change)} {onWatchlistToggle && ( } onClick={handleWatchlistClick} aria-label={isInWatchlist ? '已关注' : '加自选'} borderRadius="full" /> )} {/* 分时图 */} { e.stopPropagation(); setIsModalOpen(true); }} cursor="pointer" _hover={{ borderColor: useColorModeValue('blue.300', 'blue.500'), boxShadow: 'sm' }} transition="all 0.2s" > 📈 分时 {/* K线图 */} { e.stopPropagation(); setIsModalOpen(true); }} cursor="pointer" _hover={{ borderColor: useColorModeValue('purple.300', 'purple.500'), boxShadow: 'sm' }} transition="all 0.2s" > 📊 日线 {/* 关联描述(单行显示,点击展开) */} {relationText && relationText !== '--' && ( { e.stopPropagation(); setIsDescExpanded(!isDescExpanded); }} cursor="pointer" px={3} py={2} bg={useColorModeValue('gray.50', 'gray.700')} borderRadius="md" _hover={{ bg: useColorModeValue('gray.100', 'gray.600'), }} transition="background 0.2s" > 关联描述 {relationText} {isDescExpanded && ( ⚠️ AI生成,仅供参考 )} )} {/* 股票详情弹窗 - 未打开时不渲染 */} {isModalOpen && ( setIsModalOpen(false)} stock={stock} eventTime={eventTime} size="6xl" /> )} ); }; export default StockListItem;