refactor(StockOverviewHeader): 模块化重构与性能优化
目录结构拆分: - types.ts: 提取 6 个 TypeScript 接口定义 - styles.ts: 提取颜色常量和样式对象 - utils.ts: 提取趋势计算函数 (getAmountTrend, getMarketCapTrend) - components/StatCard.tsx: 提取统计卡片原子组件 性能优化: - StatCard 使用 memo + useMemo 缓存趋势图标和颜色 - 主组件使用 useMemo 缓存所有计算值 - 趋势函数外移避免每次渲染重新创建 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* StatCard - 统计卡片原子组件
|
||||||
|
* 用于展示单个统计指标,支持趋势显示和进度条
|
||||||
|
*/
|
||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Text,
|
||||||
|
Icon,
|
||||||
|
HStack,
|
||||||
|
VStack,
|
||||||
|
Progress,
|
||||||
|
Skeleton,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { ArrowUp, ArrowDown } from 'lucide-react';
|
||||||
|
import type { StatCardProps } from '../types';
|
||||||
|
import { COLORS, statCardStyles, watermarkIconStyles } from '../styles';
|
||||||
|
|
||||||
|
const StatCard: React.FC<StatCardProps> = memo(({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
valueColor,
|
||||||
|
icon: IconComponent,
|
||||||
|
iconColor = COLORS.gold,
|
||||||
|
trend,
|
||||||
|
progressBar,
|
||||||
|
helpText,
|
||||||
|
}) => {
|
||||||
|
// 缓存趋势图标计算
|
||||||
|
const TrendIcon = useMemo(() => {
|
||||||
|
if (!trend) return null;
|
||||||
|
if (trend.direction === 'up') return ArrowUp;
|
||||||
|
if (trend.direction === 'down') return ArrowDown;
|
||||||
|
return null;
|
||||||
|
}, [trend]);
|
||||||
|
|
||||||
|
// 缓存趋势颜色
|
||||||
|
const trendColor = useMemo(() => {
|
||||||
|
if (!trend) return undefined;
|
||||||
|
return trend.direction === 'up' ? COLORS.up : COLORS.down;
|
||||||
|
}, [trend]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={statCardStyles}>
|
||||||
|
{/* 水印图标 */}
|
||||||
|
<Icon
|
||||||
|
as={IconComponent}
|
||||||
|
sx={watermarkIconStyles}
|
||||||
|
color={iconColor}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<VStack align="start" spacing={2}>
|
||||||
|
<Text fontSize="xs" color={COLORS.subText} fontWeight="medium">
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{value === null ? (
|
||||||
|
<Skeleton height="28px" width="80px" />
|
||||||
|
) : (
|
||||||
|
<HStack spacing={2} align="baseline">
|
||||||
|
<Text
|
||||||
|
fontSize="xl"
|
||||||
|
fontWeight="bold"
|
||||||
|
color={valueColor || COLORS.text}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
{trend && TrendIcon && (
|
||||||
|
<HStack spacing={1} fontSize="xs" color={trendColor}>
|
||||||
|
<Icon as={TrendIcon} boxSize={3} />
|
||||||
|
<Text>{trend.percent.toFixed(1)}%</Text>
|
||||||
|
{trend.label && (
|
||||||
|
<Text color={trendColor}>
|
||||||
|
{trend.label}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{helpText && (
|
||||||
|
<Text fontSize="xs" color={COLORS.subText}>
|
||||||
|
{helpText}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{progressBar && (
|
||||||
|
<Box w="100%" mt={1}>
|
||||||
|
<Progress
|
||||||
|
value={(progressBar.value / (progressBar.value + progressBar.total)) * 100}
|
||||||
|
size="xs"
|
||||||
|
borderRadius="full"
|
||||||
|
bg={progressBar.negativeColor}
|
||||||
|
sx={{
|
||||||
|
'& > div': {
|
||||||
|
bg: progressBar.positiveColor,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
StatCard.displayName = 'StatCard';
|
||||||
|
|
||||||
|
export default StatCard;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as StatCard } from './StatCard';
|
||||||
222
src/views/StockOverview/components/StockOverviewHeader/index.tsx
Normal file
222
src/views/StockOverview/components/StockOverviewHeader/index.tsx
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
/**
|
||||||
|
* StockOverviewHeader - 个股中心头部组件
|
||||||
|
* 左右分栏:左侧标题+统计指标,右侧热力图
|
||||||
|
*
|
||||||
|
* 优化点:
|
||||||
|
* 1. 类型拆分到 types.ts
|
||||||
|
* 2. 样式常量拆分到 styles.ts
|
||||||
|
* 3. 工具函数拆分到 utils.ts
|
||||||
|
* 4. StatCard 拆分为原子组件
|
||||||
|
* 5. 使用 useMemo 缓存计算结果
|
||||||
|
*/
|
||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
Text,
|
||||||
|
SimpleGrid,
|
||||||
|
Icon,
|
||||||
|
HStack,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import {
|
||||||
|
TrendingUp,
|
||||||
|
Zap,
|
||||||
|
Scale,
|
||||||
|
Banknote,
|
||||||
|
Wallet,
|
||||||
|
Rocket,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import MarketHeatmap from '../MarketHeatmap';
|
||||||
|
|
||||||
|
import type { StockOverviewHeaderProps } from './types';
|
||||||
|
import { StatCard } from './components';
|
||||||
|
import { COLORS, heatmapContainerStyles } from './styles';
|
||||||
|
import { getAmountTrend, getMarketCapTrend, formatChangePercent, formatAmount } from './utils';
|
||||||
|
|
||||||
|
const StockOverviewHeader: React.FC<StockOverviewHeaderProps> = ({
|
||||||
|
hotspotData,
|
||||||
|
limitStats,
|
||||||
|
marketStats,
|
||||||
|
heatmapData,
|
||||||
|
loadingHeatmap,
|
||||||
|
onStockClick,
|
||||||
|
}) => {
|
||||||
|
// 缓存趋势计算
|
||||||
|
const amountTrend = useMemo(() => getAmountTrend(marketStats), [marketStats]);
|
||||||
|
const marketCapTrend = useMemo(() => getMarketCapTrend(marketStats), [marketStats]);
|
||||||
|
|
||||||
|
// 缓存涨跌幅显示值
|
||||||
|
const changePercentValue = useMemo(() => {
|
||||||
|
if (hotspotData?.index?.change_pct == null) return null;
|
||||||
|
return formatChangePercent(hotspotData.index.change_pct);
|
||||||
|
}, [hotspotData?.index?.change_pct]);
|
||||||
|
|
||||||
|
const changePercentColor = useMemo(() => {
|
||||||
|
return (hotspotData?.index?.change_pct ?? 0) >= 0 ? COLORS.up : COLORS.down;
|
||||||
|
}, [hotspotData?.index?.change_pct]);
|
||||||
|
|
||||||
|
// 缓存涨停/跌停值
|
||||||
|
const limitValue = useMemo(() => {
|
||||||
|
if (limitStats.limitUpCount > 0 || limitStats.limitDownCount > 0) {
|
||||||
|
return `${limitStats.limitUpCount}/${limitStats.limitDownCount}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [limitStats.limitUpCount, limitStats.limitDownCount]);
|
||||||
|
|
||||||
|
const limitProgressBar = useMemo(() => {
|
||||||
|
if (limitStats.limitUpCount > 0 || limitStats.limitDownCount > 0) {
|
||||||
|
return {
|
||||||
|
value: limitStats.limitUpCount,
|
||||||
|
total: limitStats.limitDownCount || 1,
|
||||||
|
positiveColor: COLORS.up,
|
||||||
|
negativeColor: COLORS.down,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [limitStats.limitUpCount, limitStats.limitDownCount]);
|
||||||
|
|
||||||
|
// 缓存多空对比值
|
||||||
|
const bullBearValue = useMemo(() => {
|
||||||
|
if (marketStats?.rising_count && marketStats?.falling_count) {
|
||||||
|
return `${marketStats.rising_count}/${marketStats.falling_count}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [marketStats?.rising_count, marketStats?.falling_count]);
|
||||||
|
|
||||||
|
const bullBearProgressBar = useMemo(() => {
|
||||||
|
if (marketStats?.rising_count && marketStats?.falling_count) {
|
||||||
|
return {
|
||||||
|
value: marketStats.rising_count,
|
||||||
|
total: marketStats.falling_count,
|
||||||
|
positiveColor: COLORS.up,
|
||||||
|
negativeColor: COLORS.down,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}, [marketStats?.rising_count, marketStats?.falling_count]);
|
||||||
|
|
||||||
|
// 缓存成交额和市值显示值
|
||||||
|
const amountValue = useMemo(() => {
|
||||||
|
return marketStats?.total_amount ? formatAmount(marketStats.total_amount) : null;
|
||||||
|
}, [marketStats?.total_amount]);
|
||||||
|
|
||||||
|
const marketCapValue = useMemo(() => {
|
||||||
|
return marketStats?.total_market_cap ? formatAmount(marketStats.total_market_cap) : null;
|
||||||
|
}, [marketStats?.total_market_cap]);
|
||||||
|
|
||||||
|
// 缓存连板龙头显示值
|
||||||
|
const continuousValue = useMemo(() => {
|
||||||
|
return limitStats.limitUpCount > 0 ? `${limitStats.limitUpCount}只` : '暂无';
|
||||||
|
}, [limitStats.limitUpCount]);
|
||||||
|
|
||||||
|
const continuousHelpText = useMemo(() => {
|
||||||
|
return limitStats.maxContinuousDays > 1
|
||||||
|
? `最高${limitStats.maxContinuousDays}天`
|
||||||
|
: undefined;
|
||||||
|
}, [limitStats.maxContinuousDays]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box position="relative" zIndex={1} pt={8} pb={6} px={6}>
|
||||||
|
<Flex
|
||||||
|
direction={{ base: 'column', md: 'row' }}
|
||||||
|
gap={6}
|
||||||
|
align="stretch"
|
||||||
|
>
|
||||||
|
{/* 左侧:标题 + 统计指标 (40%) */}
|
||||||
|
<Box flex="4" minW={0}>
|
||||||
|
{/* 标题区 */}
|
||||||
|
<HStack spacing={3} mb={4}>
|
||||||
|
<Icon as={TrendingUp} boxSize={8} color={COLORS.gold} />
|
||||||
|
<Heading
|
||||||
|
size="xl"
|
||||||
|
bgGradient={`linear(to-r, ${COLORS.gold}, white)`}
|
||||||
|
bgClip="text"
|
||||||
|
>
|
||||||
|
个股中心
|
||||||
|
</Heading>
|
||||||
|
</HStack>
|
||||||
|
<Text color={COLORS.subText} fontSize="md" mb={6}>
|
||||||
|
实时追踪市场动态,洞察投资机会
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{/* 统计指标网格 */}
|
||||||
|
<SimpleGrid columns={{ base: 2, md: 3 }} spacing={4}>
|
||||||
|
{/* 大盘涨跌幅 */}
|
||||||
|
<StatCard
|
||||||
|
label="大盘涨跌幅"
|
||||||
|
value={changePercentValue}
|
||||||
|
valueColor={changePercentColor}
|
||||||
|
icon={TrendingUp}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 涨停/跌停 */}
|
||||||
|
<StatCard
|
||||||
|
label="涨停/跌停"
|
||||||
|
value={limitValue}
|
||||||
|
icon={Zap}
|
||||||
|
iconColor={COLORS.up}
|
||||||
|
progressBar={limitProgressBar}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 多空对比 */}
|
||||||
|
<StatCard
|
||||||
|
label="多空对比"
|
||||||
|
value={bullBearValue}
|
||||||
|
icon={Scale}
|
||||||
|
progressBar={bullBearProgressBar}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 今日成交额 */}
|
||||||
|
<StatCard
|
||||||
|
label="今日成交额"
|
||||||
|
value={amountValue}
|
||||||
|
icon={Banknote}
|
||||||
|
trend={amountTrend}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* A股总市值 */}
|
||||||
|
<StatCard
|
||||||
|
label="A股总市值"
|
||||||
|
value={marketCapValue}
|
||||||
|
icon={Wallet}
|
||||||
|
trend={marketCapTrend}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 连板龙头 */}
|
||||||
|
<StatCard
|
||||||
|
label="连板龙头"
|
||||||
|
value={continuousValue}
|
||||||
|
valueColor={COLORS.warning}
|
||||||
|
icon={Rocket}
|
||||||
|
iconColor={COLORS.warning}
|
||||||
|
helpText={continuousHelpText}
|
||||||
|
/>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* 右侧:热力图 (60%) */}
|
||||||
|
<Box flex="6" minH="400px" sx={heatmapContainerStyles}>
|
||||||
|
<MarketHeatmap
|
||||||
|
data={heatmapData}
|
||||||
|
height="384px"
|
||||||
|
loading={loadingHeatmap}
|
||||||
|
onStockClick={onStockClick}
|
||||||
|
showLegend={true}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(StockOverviewHeader);
|
||||||
|
|
||||||
|
// 导出类型供外部使用
|
||||||
|
export type {
|
||||||
|
StockOverviewHeaderProps,
|
||||||
|
HotspotData,
|
||||||
|
LimitStats,
|
||||||
|
MarketStats,
|
||||||
|
HeatmapDataItem,
|
||||||
|
} from './types';
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* StockOverviewHeader 样式常量
|
||||||
|
*/
|
||||||
|
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
/** 颜色常量 */
|
||||||
|
export const COLORS = {
|
||||||
|
// 主题色
|
||||||
|
gold: '#8b5cf6',
|
||||||
|
|
||||||
|
// 文字颜色
|
||||||
|
text: 'rgba(255, 255, 255, 0.95)',
|
||||||
|
subText: 'rgba(255, 255, 255, 0.6)',
|
||||||
|
|
||||||
|
// 涨跌颜色
|
||||||
|
up: '#ff4d4d',
|
||||||
|
down: '#22c55e',
|
||||||
|
|
||||||
|
// 警告/连板颜色
|
||||||
|
warning: '#f59e0b',
|
||||||
|
|
||||||
|
// 背景与边框
|
||||||
|
cardBg: 'rgba(255, 255, 255, 0.03)',
|
||||||
|
border: 'rgba(255, 255, 255, 0.08)',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/** StatCard 卡片样式 */
|
||||||
|
export const statCardStyles: SystemStyleObject = {
|
||||||
|
bg: COLORS.cardBg,
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderColor: COLORS.border,
|
||||||
|
borderRadius: 'xl',
|
||||||
|
p: 4,
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
|
transition: 'all 0.2s',
|
||||||
|
_hover: {
|
||||||
|
borderColor: `${COLORS.gold}50`,
|
||||||
|
transform: 'translateY(-2px)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 水印图标样式 */
|
||||||
|
export const watermarkIconStyles: SystemStyleObject = {
|
||||||
|
position: 'absolute',
|
||||||
|
right: 2,
|
||||||
|
bottom: 2,
|
||||||
|
boxSize: 12,
|
||||||
|
opacity: 0.1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 热力图容器样式 */
|
||||||
|
export const heatmapContainerStyles: SystemStyleObject = {
|
||||||
|
bg: COLORS.cardBg,
|
||||||
|
borderWidth: '1px',
|
||||||
|
borderColor: COLORS.border,
|
||||||
|
borderRadius: '2xl',
|
||||||
|
overflow: 'hidden',
|
||||||
|
p: 2,
|
||||||
|
};
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* StockOverviewHeader 类型定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 趋势方向类型 */
|
||||||
|
export type TrendDirection = 'up' | 'down' | 'flat';
|
||||||
|
|
||||||
|
/** 热点数据 */
|
||||||
|
export interface HotspotData {
|
||||||
|
index?: {
|
||||||
|
change_pct?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 涨跌停统计 */
|
||||||
|
export interface LimitStats {
|
||||||
|
limitUpCount: number;
|
||||||
|
limitDownCount: number;
|
||||||
|
continuousLimitCount: number;
|
||||||
|
maxContinuousDays: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 市场统计数据 */
|
||||||
|
export interface MarketStats {
|
||||||
|
rising_count?: number;
|
||||||
|
falling_count?: number;
|
||||||
|
total_amount?: number;
|
||||||
|
total_market_cap?: number;
|
||||||
|
yesterday?: {
|
||||||
|
total_amount?: number;
|
||||||
|
total_market_cap?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 热力图数据项 */
|
||||||
|
export interface HeatmapDataItem {
|
||||||
|
stock_code: string;
|
||||||
|
stock_name: string;
|
||||||
|
change_percent: number;
|
||||||
|
market_cap: number;
|
||||||
|
amount: number;
|
||||||
|
industry?: string;
|
||||||
|
province?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** StockOverviewHeader 组件 Props */
|
||||||
|
export interface StockOverviewHeaderProps {
|
||||||
|
hotspotData: HotspotData | null;
|
||||||
|
limitStats: LimitStats;
|
||||||
|
marketStats: MarketStats | null;
|
||||||
|
heatmapData: HeatmapDataItem[];
|
||||||
|
loadingHeatmap: boolean;
|
||||||
|
onStockClick?: (code: string, name: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 趋势配置 */
|
||||||
|
export interface TrendConfig {
|
||||||
|
direction: TrendDirection;
|
||||||
|
percent: number;
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 进度条配置 */
|
||||||
|
export interface ProgressBarConfig {
|
||||||
|
value: number;
|
||||||
|
total: number;
|
||||||
|
positiveColor: string;
|
||||||
|
negativeColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** StatCard 组件 Props */
|
||||||
|
export interface StatCardProps {
|
||||||
|
label: string;
|
||||||
|
value: string | null;
|
||||||
|
valueColor?: string;
|
||||||
|
icon: React.ElementType;
|
||||||
|
iconColor?: string;
|
||||||
|
trend?: TrendConfig;
|
||||||
|
progressBar?: ProgressBarConfig;
|
||||||
|
helpText?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* StockOverviewHeader 工具函数
|
||||||
|
*/
|
||||||
|
import type { MarketStats, TrendConfig, TrendDirection } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取趋势方向
|
||||||
|
* @param change 变化百分比
|
||||||
|
* @returns 趋势方向
|
||||||
|
*/
|
||||||
|
export const getTrendDirection = (change: number): TrendDirection => {
|
||||||
|
if (change > 0.01) return 'up';
|
||||||
|
if (change < -0.01) return 'down';
|
||||||
|
return 'flat';
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算成交额趋势
|
||||||
|
* @param marketStats 市场统计数据
|
||||||
|
* @returns 趋势配置
|
||||||
|
*/
|
||||||
|
export const getAmountTrend = (marketStats: MarketStats | null): TrendConfig | undefined => {
|
||||||
|
if (!marketStats?.yesterday?.total_amount || !marketStats.total_amount) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const change =
|
||||||
|
((marketStats.total_amount - marketStats.yesterday.total_amount) /
|
||||||
|
marketStats.yesterday.total_amount) *
|
||||||
|
100;
|
||||||
|
|
||||||
|
const direction = getTrendDirection(change);
|
||||||
|
|
||||||
|
return {
|
||||||
|
direction,
|
||||||
|
percent: Math.abs(change),
|
||||||
|
label: change > 5 ? '放量' : change < -5 ? '缩量' : undefined,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算市值趋势
|
||||||
|
* @param marketStats 市场统计数据
|
||||||
|
* @returns 趋势配置
|
||||||
|
*/
|
||||||
|
export const getMarketCapTrend = (marketStats: MarketStats | null): TrendConfig | undefined => {
|
||||||
|
if (!marketStats?.yesterday?.total_market_cap || !marketStats.total_market_cap) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const change =
|
||||||
|
((marketStats.total_market_cap - marketStats.yesterday.total_market_cap) /
|
||||||
|
marketStats.yesterday.total_market_cap) *
|
||||||
|
100;
|
||||||
|
|
||||||
|
const direction = getTrendDirection(change);
|
||||||
|
|
||||||
|
return {
|
||||||
|
direction,
|
||||||
|
percent: Math.abs(change),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化涨跌幅显示
|
||||||
|
* @param value 涨跌幅数值
|
||||||
|
* @returns 格式化后的字符串
|
||||||
|
*/
|
||||||
|
export const formatChangePercent = (value: number): string => {
|
||||||
|
const sign = value >= 0 ? '+' : '';
|
||||||
|
return `${sign}${value.toFixed(2)}%`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化金额(单位:万亿)
|
||||||
|
* @param value 金额数值(单位:亿)
|
||||||
|
* @returns 格式化后的字符串
|
||||||
|
*/
|
||||||
|
export const formatAmount = (value: number): string => {
|
||||||
|
return `${(value / 10000).toFixed(1)}万亿`;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user