From 9deb9ff3505abcb7ee9733a5da543478a35da993 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Tue, 30 Dec 2025 15:55:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(StatCard):=20=E6=96=B0=E5=A2=9E=E8=B6=8B?= =?UTF-8?q?=E5=8A=BF=E6=8C=87=E7=A4=BA=E5=99=A8=E5=92=8C=E5=A4=9A=E7=A9=BA?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E6=9D=A1=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 TrendIndicator 组件显示环比变化(箭头+百分比+标签) - 新增 BullBearBar 组件显示红绿进度条 - 新增 WatermarkIcon 组件支持卡片水印背景 - 支持双色数值显示(如 121/79 红绿分色) - StatCard 根据配置自动渲染趋势和进度条 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/components/HeroSection/HeroSection.tsx | 34 +++- .../components/HeroStats/StatCard.tsx | 186 ++++++++++++++++-- 2 files changed, 195 insertions(+), 25 deletions(-) diff --git a/src/components/HeroSection/HeroSection.tsx b/src/components/HeroSection/HeroSection.tsx index 495fc7cb..cd5f973d 100644 --- a/src/components/HeroSection/HeroSection.tsx +++ b/src/components/HeroSection/HeroSection.tsx @@ -52,29 +52,49 @@ export const HeroSection = memo(({ return ( - {/* 背景装饰层 */} - + {/* 背景装饰层 */} + + + + {/* 底部渐变过渡区域 - 创造纵深感 */} + {/* 主内容区 - 自适应高度,上下 padding 40px */} {children ? ( // 完全自定义内容 diff --git a/src/components/HeroSection/components/HeroStats/StatCard.tsx b/src/components/HeroSection/components/HeroStats/StatCard.tsx index 66626e9a..a586c52a 100644 --- a/src/components/HeroSection/components/HeroStats/StatCard.tsx +++ b/src/components/HeroSection/components/HeroStats/StatCard.tsx @@ -1,5 +1,6 @@ /** * StatCard 单个统计卡片 + * 支持趋势变化显示和多空进度条 */ import React, { memo } from 'react'; @@ -12,15 +13,138 @@ import { Icon, Text, Skeleton, + Box, + Flex, } from '@chakra-ui/react'; -import type { StatCardProps } from '../../types'; +import { ArrowUp, ArrowDown, Minus } from 'lucide-react'; +import type { StatCardProps, TrendInfo, ProgressBarConfig, WatermarkIconConfig } from '../../types'; + +/** 趋势指示器组件 */ +const TrendIndicator = memo<{ trend: TrendInfo }>(({ trend }) => { + const { direction, percent, label, compareText = '较昨日' } = trend; + + // 根据方向确定颜色和图标 + const colorMap = { + up: '#ff4d4d', + down: '#22c55e', + flat: 'gray.400', + }; + const IconMap = { + up: ArrowUp, + down: ArrowDown, + flat: Minus, + }; + const color = colorMap[direction]; + const TrendIcon = IconMap[direction]; + + // 格式化百分比 + const formattedPercent = direction === 'flat' ? '0%' : `${direction === 'up' ? '+' : ''}${percent.toFixed(1)}%`; + + return ( + + + + {formattedPercent} + + {label && ( + + ({label}) + + )} + + {compareText} + + + ); +}); + +TrendIndicator.displayName = 'TrendIndicator'; + +/** 多空进度条组件 */ +const BullBearBar = memo<{ config: ProgressBarConfig }>(({ config }) => { + const { + value, + total, + positiveColor = '#ff4d4d', + negativeColor = '#22c55e', + compareLabel, + compareValue, + } = config; + + // 计算百分比 + const sum = value + total; + const positivePercent = sum > 0 ? (value / sum) * 100 : 50; + const negativePercent = 100 - positivePercent; + + return ( + + {/* 进度条 */} + + + + + + + {/* 标签(仅当有 compareLabel 时显示) */} + {compareLabel && compareValue !== undefined && ( + + + {value.toLocaleString()} + + + {compareLabel}: {compareValue.toLocaleString()} + + + )} + + ); +}); + +BullBearBar.displayName = 'BullBearBar'; + +/** 水印图标组件 */ +const WatermarkIcon = memo<{ config: WatermarkIconConfig }>(({ config }) => { + const { icon: WIcon, color = 'white', opacity = 0.08, size = 48 } = config; + + return ( + + + + ); +}); + +WatermarkIcon.displayName = 'WatermarkIcon'; export const StatCard = memo(({ item, themeColors, showIcon = false, }) => { - const { label, value, valueColor, icon, iconColor, suffix, isLoading } = item; + const { label, value, valueColor, icon, iconColor, suffix, isLoading, trend, progressBar, watermark } = item; // 格式化显示值 const displayValue = (() => { @@ -67,24 +191,50 @@ export const StatCard = memo(({ borderRadius="md" border="1px solid" borderColor={themeColors.statCardBorder} + position="relative" + overflow="hidden" > - - {label} - - {isLoading ? ( - - ) : ( - } + + + - {displayValue} - - )} + {label} + + {isLoading ? ( + + ) : ( + <> + {/* 多空对比特殊渲染:双色显示 */} + {progressBar && typeof value === 'string' && value.includes('/') ? ( + + + {value.split('/')[0]} + + / + + {value.split('/')[1]} + + + ) : ( + + {displayValue} + + )} + {/* 趋势指示器 */} + {trend && } + {/* 多空进度条 */} + {progressBar && } + + )} + ); });