refactor(StockQuoteCard): 子组件提取与 DEEP_SPACE_THEME 统一
- PriceDisplay: 更新为 DEEP_SPACE_THEME,添加发光效果 - StockHeader: 更新为 DEEP_SPACE_THEME,金色边框和发光 - MetricRow: 新建指标行组件,支持普通和高亮模式 - 主组件从 321 行精简到 180 行(-44%) - 统一使用提取的子组件,移除内联代码 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* MetricRow - 指标行原子组件
|
||||||
|
*
|
||||||
|
* 用于数据区块内的单行指标展示
|
||||||
|
* 支持普通和高亮两种模式
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { HStack, Text } from '@chakra-ui/react';
|
||||||
|
import { DEEP_SPACE_THEME as T } from './theme';
|
||||||
|
|
||||||
|
export interface MetricRowProps {
|
||||||
|
/** 指标标签 */
|
||||||
|
label: string;
|
||||||
|
/** 指标值 */
|
||||||
|
value: string | number;
|
||||||
|
/** 值的颜色(默认白色) */
|
||||||
|
valueColor?: string;
|
||||||
|
/** 是否高亮显示(加粗 + 发光) */
|
||||||
|
highlight?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指标行组件
|
||||||
|
*
|
||||||
|
* 布局:左侧标签 + 右侧值
|
||||||
|
* 高亮模式:更大字号 + 发光效果
|
||||||
|
*/
|
||||||
|
export const MetricRow: React.FC<MetricRowProps> = memo(({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
valueColor = T.textWhite,
|
||||||
|
highlight = false,
|
||||||
|
}) => (
|
||||||
|
<HStack justify="space-between" fontSize="13px">
|
||||||
|
<Text color={T.textMuted}>{label}</Text>
|
||||||
|
<Text
|
||||||
|
color={valueColor}
|
||||||
|
fontWeight={highlight ? '700' : '600'}
|
||||||
|
fontSize={highlight ? '15px' : '13px'}
|
||||||
|
textShadow={highlight ? `0 0 10px ${valueColor}40` : undefined}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
));
|
||||||
|
|
||||||
|
MetricRow.displayName = 'MetricRow';
|
||||||
@@ -1,38 +1,67 @@
|
|||||||
/**
|
/**
|
||||||
* PriceDisplay - 价格显示原子组件
|
* PriceDisplay - 价格显示原子组件
|
||||||
* 显示当前价格和涨跌幅 Badge
|
*
|
||||||
|
* 深空 FUI 设计风格:
|
||||||
|
* - 大号价格数字,带涨跌色发光
|
||||||
|
* - Badge 使用半透明背景 + 边框发光
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { HStack, Text, Badge } from '@chakra-ui/react';
|
import { HStack, Text, Badge } from '@chakra-ui/react';
|
||||||
import { formatPrice, formatChangePercent } from './formatters';
|
import { formatPrice, formatChangePercent } from './formatters';
|
||||||
import { STOCK_CARD_THEME } from './theme';
|
import { DEEP_SPACE_THEME as T } from './theme';
|
||||||
|
|
||||||
export interface PriceDisplayProps {
|
export interface PriceDisplayProps {
|
||||||
|
/** 当前价格 */
|
||||||
currentPrice: number;
|
currentPrice: number;
|
||||||
|
/** 涨跌幅(百分比) */
|
||||||
changePercent: number;
|
changePercent: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 价格显示组件
|
||||||
|
*
|
||||||
|
* 使用发光效果突出涨跌状态:
|
||||||
|
* - 涨:红色 (#FF4757) + 红色光晕
|
||||||
|
* - 跌:绿色 (#00D984) + 绿色光晕
|
||||||
|
*/
|
||||||
export const PriceDisplay: React.FC<PriceDisplayProps> = memo(({
|
export const PriceDisplay: React.FC<PriceDisplayProps> = memo(({
|
||||||
currentPrice,
|
currentPrice,
|
||||||
changePercent,
|
changePercent,
|
||||||
}) => {
|
}) => {
|
||||||
const { upColor, downColor } = STOCK_CARD_THEME;
|
// 根据涨跌计算颜色和发光效果
|
||||||
const priceColor = changePercent >= 0 ? upColor : downColor;
|
const { priceColor, priceGlow, priceBg } = useMemo(() => {
|
||||||
|
const isUp = changePercent >= 0;
|
||||||
|
return {
|
||||||
|
priceColor: isUp ? T.upColor : T.downColor,
|
||||||
|
priceGlow: isUp ? T.upGlow : T.downGlow,
|
||||||
|
priceBg: isUp ? T.upColorMuted : T.downColorMuted,
|
||||||
|
};
|
||||||
|
}, [changePercent]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack align="baseline" spacing={3} mb={3}>
|
<HStack align="baseline" spacing={3}>
|
||||||
<Text fontSize="48px" fontWeight="bold" color={priceColor}>
|
{/* 主价格 - 大号字体 + 发光 */}
|
||||||
|
<Text
|
||||||
|
fontSize="42px"
|
||||||
|
fontWeight="bold"
|
||||||
|
color={priceColor}
|
||||||
|
textShadow={priceGlow}
|
||||||
|
lineHeight="1"
|
||||||
|
>
|
||||||
{formatPrice(currentPrice)}
|
{formatPrice(currentPrice)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
|
{/* 涨跌幅 Badge - 半透明背景 + 边框发光 */}
|
||||||
<Badge
|
<Badge
|
||||||
bg={changePercent >= 0 ? upColor : downColor}
|
bg={priceBg}
|
||||||
color="#FFFFFF"
|
color={priceColor}
|
||||||
fontSize="20px"
|
fontSize="18px"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
px={3}
|
px={3}
|
||||||
py={1}
|
py={1.5}
|
||||||
borderRadius="md"
|
borderRadius={T.radiusMD}
|
||||||
|
boxShadow={priceGlow}
|
||||||
>
|
>
|
||||||
{formatChangePercent(changePercent)}
|
{formatChangePercent(changePercent)}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* StockHeader - 股票头部原子组件
|
* StockHeader - 股票头部原子组件
|
||||||
* 显示股票名称、代码、行业标签、指数标签、操作按钮
|
*
|
||||||
|
* 深空 FUI 设计风格:
|
||||||
|
* - 股票名称带金色发光
|
||||||
|
* - 行业标签使用金色边框
|
||||||
|
* - 操作按钮悬停态有玻璃效果
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
@@ -8,14 +12,20 @@ import { Flex, HStack, Text, Badge, IconButton, Tooltip } from '@chakra-ui/react
|
|||||||
import { Share2 } from 'lucide-react';
|
import { Share2 } from 'lucide-react';
|
||||||
import FavoriteButton from '@components/FavoriteButton';
|
import FavoriteButton from '@components/FavoriteButton';
|
||||||
import CompareStockInput from './CompareStockInput';
|
import CompareStockInput from './CompareStockInput';
|
||||||
import { STOCK_CARD_THEME } from './theme';
|
import { DEEP_SPACE_THEME as T } from './theme';
|
||||||
|
|
||||||
export interface StockHeaderProps {
|
export interface StockHeaderProps {
|
||||||
|
/** 股票名称 */
|
||||||
name: string;
|
name: string;
|
||||||
|
/** 股票代码 */
|
||||||
code: string;
|
code: string;
|
||||||
|
/** 一级行业 */
|
||||||
industryL1?: string;
|
industryL1?: string;
|
||||||
|
/** 二级行业 */
|
||||||
industry?: string;
|
industry?: string;
|
||||||
|
/** 指数标签(沪深300、中证500等) */
|
||||||
indexTags?: string[];
|
indexTags?: string[];
|
||||||
|
/** 更新时间 */
|
||||||
updateTime?: string;
|
updateTime?: string;
|
||||||
// 关注相关
|
// 关注相关
|
||||||
isInWatchlist?: boolean;
|
isInWatchlist?: boolean;
|
||||||
@@ -28,6 +38,13 @@ export interface StockHeaderProps {
|
|||||||
onCompare?: (stockCode: string) => void;
|
onCompare?: (stockCode: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 股票头部组件
|
||||||
|
*
|
||||||
|
* 包含:
|
||||||
|
* - 左侧:股票名称、代码、行业标签、指数标签
|
||||||
|
* - 右侧:对比输入、关注按钮、分享按钮、更新时间
|
||||||
|
*/
|
||||||
export const StockHeader: React.FC<StockHeaderProps> = memo(({
|
export const StockHeader: React.FC<StockHeaderProps> = memo(({
|
||||||
name,
|
name,
|
||||||
code,
|
code,
|
||||||
@@ -42,32 +59,35 @@ export const StockHeader: React.FC<StockHeaderProps> = memo(({
|
|||||||
isCompareLoading = false,
|
isCompareLoading = false,
|
||||||
onCompare,
|
onCompare,
|
||||||
}) => {
|
}) => {
|
||||||
const { labelColor, valueColor, borderColor } = STOCK_CARD_THEME;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex justify="space-between" align="center" mb={4}>
|
<Flex justify="space-between" align="center">
|
||||||
{/* 左侧:股票名称 + 行业标签 + 指数标签 */}
|
{/* 左侧:股票名称 + 行业标签 + 指数标签 */}
|
||||||
<HStack spacing={3} align="center">
|
<HStack spacing={4} align="center">
|
||||||
{/* 股票名称 - 突出显示 */}
|
{/* 股票名称 - 金色发光效果 */}
|
||||||
<Text fontSize="26px" fontWeight="800" color={valueColor}>
|
<Text
|
||||||
|
fontSize="24px"
|
||||||
|
fontWeight="800"
|
||||||
|
color={T.textPrimary}
|
||||||
|
textShadow={`0 0 20px ${T.gold}40`}
|
||||||
|
>
|
||||||
{name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
<Text fontSize="18px" fontWeight="normal" color={labelColor}>
|
<Text fontSize="16px" color={T.textMuted} fontWeight="normal">
|
||||||
({code})
|
({code})
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* 行业标签 */}
|
{/* 行业标签 - 金色边框 */}
|
||||||
{(industryL1 || industry) && (
|
{(industryL1 || industry) && (
|
||||||
<Badge
|
<Badge
|
||||||
bg="transparent"
|
bg="transparent"
|
||||||
color={labelColor}
|
color={T.textSecondary}
|
||||||
fontSize="14px"
|
fontSize="13px"
|
||||||
fontWeight="medium"
|
fontWeight="500"
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor={borderColor}
|
borderColor={T.borderGold}
|
||||||
px={2}
|
px={3}
|
||||||
py={0.5}
|
py={1}
|
||||||
borderRadius="md"
|
borderRadius={T.radiusMD}
|
||||||
>
|
>
|
||||||
{industryL1 && industry
|
{industryL1 && industry
|
||||||
? `${industryL1} · ${industry}`
|
? `${industryL1} · ${industry}`
|
||||||
@@ -77,7 +97,7 @@ export const StockHeader: React.FC<StockHeaderProps> = memo(({
|
|||||||
|
|
||||||
{/* 指数标签 */}
|
{/* 指数标签 */}
|
||||||
{indexTags && indexTags.length > 0 && (
|
{indexTags && indexTags.length > 0 && (
|
||||||
<Text fontSize="14px" color={labelColor}>
|
<Text fontSize="13px" color={T.textMuted}>
|
||||||
{indexTags.join('、')}
|
{indexTags.join('、')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
@@ -103,13 +123,14 @@ export const StockHeader: React.FC<StockHeaderProps> = memo(({
|
|||||||
aria-label="分享"
|
aria-label="分享"
|
||||||
icon={<Share2 size={18} />}
|
icon={<Share2 size={18} />}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color={labelColor}
|
color={T.textSecondary}
|
||||||
size="sm"
|
size="sm"
|
||||||
|
borderRadius={T.radiusSM}
|
||||||
onClick={onShare}
|
onClick={onShare}
|
||||||
_hover={{ bg: 'whiteAlpha.100' }}
|
_hover={{ bg: T.borderGlass, color: T.textPrimary }}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Text fontSize="14px" color={labelColor}>
|
<Text fontSize="13px" color={T.textMuted}>
|
||||||
{updateTime?.split(' ')[1] || '--:--'}
|
{updateTime?.split(' ')[1] || '--:--'}
|
||||||
</Text>
|
</Text>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export { KeyMetrics } from './KeyMetrics';
|
|||||||
export { MainForceInfo } from './MainForceInfo';
|
export { MainForceInfo } from './MainForceInfo';
|
||||||
export { CompanyInfo } from './CompanyInfo';
|
export { CompanyInfo } from './CompanyInfo';
|
||||||
export { StockHeader } from './StockHeader';
|
export { StockHeader } from './StockHeader';
|
||||||
|
export { MetricRow } from './MetricRow';
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 容器组件 - 布局
|
// 容器组件 - 布局
|
||||||
@@ -50,4 +51,5 @@ export type { KeyMetricsProps } from './KeyMetrics';
|
|||||||
export type { MainForceInfoProps } from './MainForceInfo';
|
export type { MainForceInfoProps } from './MainForceInfo';
|
||||||
export type { CompanyInfoProps, CompanyBasicInfo } from './CompanyInfo';
|
export type { CompanyInfoProps, CompanyBasicInfo } from './CompanyInfo';
|
||||||
export type { StockHeaderProps } from './StockHeader';
|
export type { StockHeaderProps } from './StockHeader';
|
||||||
|
export type { MetricRowProps } from './MetricRow';
|
||||||
export type { GlassSectionProps } from './GlassSection';
|
export type { GlassSectionProps } from './GlassSection';
|
||||||
|
|||||||
@@ -6,97 +6,44 @@
|
|||||||
* - 光影深度,弥散背景光
|
* - 光影深度,弥散背景光
|
||||||
* - 极致圆角,科幻数据终端感
|
* - 极致圆角,科幻数据终端感
|
||||||
*
|
*
|
||||||
* 功能模块:
|
|
||||||
* - 股票头部:名称、代码、行业标签、操作按钮(对比、关注、分享)
|
|
||||||
* - 价格展示:当前价格、涨跌幅 Badge
|
|
||||||
* - 次要行情:今开、昨收、最高、最低(SecondaryQuote 组件)
|
|
||||||
* - 数据区块:估值指标、市值股本、主力动态(三列 GlassSection 布局)
|
|
||||||
*
|
|
||||||
* 组件结构:
|
* 组件结构:
|
||||||
* - GlowDecorations:装饰性光效(背景层)
|
* - StockHeader:股票名称、代码、行业标签、操作按钮
|
||||||
* - LoadingSkeleton:加载骨架屏
|
* - PriceDisplay:当前价格、涨跌幅 Badge
|
||||||
* - GlassSection:玻璃容器(包装数据区块)
|
* - SecondaryQuote:今开、昨收、最高、最低
|
||||||
* - SecondaryQuote:次要行情展示
|
* - GlassSection + MetricRow:估值指标、市值股本
|
||||||
* - KeyMetrics:关键指标(估值 + 市值)
|
|
||||||
* - MainForceInfo:主力动态
|
* - MainForceInfo:主力动态
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import {
|
import { Box, Flex, VStack, useDisclosure } from '@chakra-ui/react';
|
||||||
Box,
|
|
||||||
Flex,
|
|
||||||
HStack,
|
|
||||||
VStack,
|
|
||||||
Text,
|
|
||||||
Badge,
|
|
||||||
IconButton,
|
|
||||||
Tooltip,
|
|
||||||
useDisclosure,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { Share2 } from 'lucide-react';
|
|
||||||
import FavoriteButton from '@components/FavoriteButton';
|
|
||||||
import { CardGlow } from '@components/FUI';
|
import { CardGlow } from '@components/FUI';
|
||||||
|
|
||||||
// 子组件导入
|
// 子组件导入
|
||||||
import {
|
import {
|
||||||
StockCompareModal,
|
StockCompareModal,
|
||||||
CompareStockInput,
|
|
||||||
LoadingSkeleton,
|
LoadingSkeleton,
|
||||||
GlassSection,
|
GlassSection,
|
||||||
|
StockHeader,
|
||||||
|
PriceDisplay,
|
||||||
SecondaryQuote,
|
SecondaryQuote,
|
||||||
|
MetricRow,
|
||||||
MainForceInfo,
|
MainForceInfo,
|
||||||
|
DEEP_SPACE_THEME as T,
|
||||||
|
formatPrice,
|
||||||
} from './components';
|
} from './components';
|
||||||
|
import { glassCardStyle } from './components/theme';
|
||||||
|
|
||||||
// Hooks 和工具
|
// Hooks
|
||||||
import { useStockQuoteData, useStockCompare } from './hooks';
|
import { useStockQuoteData, useStockCompare } from './hooks';
|
||||||
import { DEEP_SPACE_THEME, glassCardStyle } from './components/theme';
|
|
||||||
import { formatPrice, formatChangePercent } from './components/formatters';
|
|
||||||
import type { StockQuoteCardProps } from './types';
|
import type { StockQuoteCardProps } from './types';
|
||||||
|
|
||||||
/** 主题常量简写 */
|
|
||||||
const T = DEEP_SPACE_THEME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指标行组件 - 用于数据区块内的单行指标展示
|
|
||||||
*
|
|
||||||
* @param label - 指标标签
|
|
||||||
* @param value - 指标值
|
|
||||||
* @param valueColor - 值的颜色(默认白色)
|
|
||||||
* @param highlight - 是否高亮显示(加粗 + 发光)
|
|
||||||
*/
|
|
||||||
interface MetricRowProps {
|
|
||||||
label: string;
|
|
||||||
value: string | number;
|
|
||||||
valueColor?: string;
|
|
||||||
highlight?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MetricRow: React.FC<MetricRowProps> = ({
|
|
||||||
label,
|
|
||||||
value,
|
|
||||||
valueColor = T.textWhite,
|
|
||||||
highlight = false,
|
|
||||||
}) => (
|
|
||||||
<HStack justify="space-between" fontSize="13px">
|
|
||||||
<Text color={T.textMuted}>{label}</Text>
|
|
||||||
<Text
|
|
||||||
color={valueColor}
|
|
||||||
fontWeight={highlight ? '700' : '600'}
|
|
||||||
fontSize={highlight ? '15px' : '13px'}
|
|
||||||
textShadow={highlight ? `0 0 10px ${valueColor}40` : undefined}
|
|
||||||
>
|
|
||||||
{value}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
);
|
|
||||||
|
|
||||||
const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
|
const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
|
||||||
stockCode,
|
stockCode,
|
||||||
isInWatchlist = false,
|
isInWatchlist = false,
|
||||||
isWatchlistLoading = false,
|
isWatchlistLoading = false,
|
||||||
onWatchlistToggle,
|
onWatchlistToggle,
|
||||||
}) => {
|
}) => {
|
||||||
const { quoteData, basicInfo, isLoading } = useStockQuoteData(stockCode);
|
const { quoteData, isLoading } = useStockQuoteData(stockCode);
|
||||||
const {
|
const {
|
||||||
currentStockInfo,
|
currentStockInfo,
|
||||||
compareStockInfo,
|
compareStockInfo,
|
||||||
@@ -122,121 +69,33 @@ const StockQuoteCard: React.FC<StockQuoteCardProps> = ({
|
|||||||
return <LoadingSkeleton />;
|
return <LoadingSkeleton />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 涨跌判断(用于价格颜色)
|
|
||||||
const isUp = quoteData.changePercent >= 0;
|
|
||||||
const priceColor = isUp ? T.upColor : T.downColor;
|
|
||||||
const priceGlow = isUp ? T.upGlow : T.downGlow;
|
|
||||||
const priceBg = isUp ? T.upColorMuted : T.downColorMuted;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box {...glassCardStyle.containerGold} p={6}>
|
||||||
{...glassCardStyle.containerGold}
|
|
||||||
p={6}
|
|
||||||
>
|
|
||||||
<CardGlow variant="gold" />
|
<CardGlow variant="gold" />
|
||||||
|
|
||||||
{/* 内容区域(在装饰层之上)*/}
|
{/* 内容区域(在装饰层之上)*/}
|
||||||
<VStack align="stretch" spacing={4} position="relative" zIndex={1}>
|
<VStack align="stretch" spacing={4} position="relative" zIndex={1}>
|
||||||
|
|
||||||
{/* ========== 头部区域 ========== */}
|
{/* ========== 头部区域 ========== */}
|
||||||
<Flex justify="space-between" align="center">
|
<StockHeader
|
||||||
{/* 左侧:股票名称 + 代码 + 行业 */}
|
name={quoteData.name}
|
||||||
<HStack spacing={4} align="center">
|
code={quoteData.code}
|
||||||
<Text
|
industryL1={quoteData.industryL1}
|
||||||
fontSize="24px"
|
industry={quoteData.industry}
|
||||||
fontWeight="800"
|
indexTags={quoteData.indexTags}
|
||||||
color={T.textPrimary}
|
updateTime={quoteData.updateTime}
|
||||||
textShadow={`0 0 20px ${T.gold}40`}
|
isInWatchlist={isInWatchlist}
|
||||||
>
|
isWatchlistLoading={isWatchlistLoading}
|
||||||
{quoteData.name}
|
onWatchlistToggle={onWatchlistToggle}
|
||||||
</Text>
|
isCompareLoading={isCompareLoading}
|
||||||
<Text fontSize="16px" color={T.textMuted} fontWeight="normal">
|
onCompare={handleCompare}
|
||||||
({quoteData.code})
|
/>
|
||||||
</Text>
|
|
||||||
|
|
||||||
{/* 行业标签 */}
|
|
||||||
{(quoteData.industryL1 || quoteData.industry) && (
|
|
||||||
<Badge
|
|
||||||
bg="transparent"
|
|
||||||
color={T.textSecondary}
|
|
||||||
fontSize="13px"
|
|
||||||
fontWeight="500"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor={T.borderGold}
|
|
||||||
px={3}
|
|
||||||
py={1}
|
|
||||||
borderRadius={T.radiusMD}
|
|
||||||
>
|
|
||||||
{quoteData.industryL1 && quoteData.industry
|
|
||||||
? `${quoteData.industryL1} · ${quoteData.industry}`
|
|
||||||
: quoteData.industry || quoteData.industryL1}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 指数标签 */}
|
|
||||||
{quoteData.indexTags && quoteData.indexTags.length > 0 && (
|
|
||||||
<Text fontSize="13px" color={T.textMuted}>
|
|
||||||
{quoteData.indexTags.join('、')}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</HStack>
|
|
||||||
|
|
||||||
{/* 右侧:操作按钮 */}
|
|
||||||
<HStack spacing={3}>
|
|
||||||
<CompareStockInput
|
|
||||||
onCompare={handleCompare}
|
|
||||||
isLoading={isCompareLoading}
|
|
||||||
currentStockCode={quoteData.code}
|
|
||||||
/>
|
|
||||||
<FavoriteButton
|
|
||||||
isFavorite={isInWatchlist}
|
|
||||||
isLoading={isWatchlistLoading}
|
|
||||||
onClick={onWatchlistToggle || (() => {})}
|
|
||||||
colorScheme="gold"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
<Tooltip label="分享" placement="top">
|
|
||||||
<IconButton
|
|
||||||
aria-label="分享"
|
|
||||||
icon={<Share2 size={18} />}
|
|
||||||
variant="ghost"
|
|
||||||
color={T.textSecondary}
|
|
||||||
size="sm"
|
|
||||||
borderRadius={T.radiusSM}
|
|
||||||
_hover={{ bg: T.borderGlass, color: T.textPrimary }}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Text fontSize="13px" color={T.textMuted}>
|
|
||||||
{quoteData.updateTime?.split(' ')[1] || '--:--'}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{/* ========== 价格区域 ========== */}
|
{/* ========== 价格区域 ========== */}
|
||||||
<HStack align="baseline" spacing={3}>
|
<PriceDisplay
|
||||||
<Text
|
currentPrice={quoteData.currentPrice}
|
||||||
fontSize="42px"
|
changePercent={quoteData.changePercent}
|
||||||
fontWeight="bold"
|
/>
|
||||||
color={priceColor}
|
|
||||||
textShadow={priceGlow}
|
|
||||||
lineHeight="1"
|
|
||||||
>
|
|
||||||
{formatPrice(quoteData.currentPrice)}
|
|
||||||
</Text>
|
|
||||||
<Badge
|
|
||||||
bg={priceBg}
|
|
||||||
color={priceColor}
|
|
||||||
fontSize="18px"
|
|
||||||
fontWeight="bold"
|
|
||||||
px={3}
|
|
||||||
py={1.5}
|
|
||||||
borderRadius={T.radiusMD}
|
|
||||||
boxShadow={priceGlow}
|
|
||||||
>
|
|
||||||
{formatChangePercent(quoteData.changePercent)}
|
|
||||||
</Badge>
|
|
||||||
</HStack>
|
|
||||||
|
|
||||||
{/* ========== 次要行情 ========== */}
|
{/* ========== 次要行情 ========== */}
|
||||||
<SecondaryQuote
|
<SecondaryQuote
|
||||||
|
|||||||
Reference in New Issue
Block a user