// 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;