mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-17 01:14:40 +08:00
UI: Unify trader colors between Leaderboard and Performance Chart
Problem: - Leaderboard used ai_model-based colors (qwen=purple, other=blue) - Performance Comparison used index-based colors (10 color pool) - This caused color mismatch between the two components - Same trader showed different colors in different sections Solution: - Create shared color utility (utils/traderColors.ts) - Define single color pool with 10 distinct colors - Implement unified getTraderColor function based on trader index - Update both ComparisonChart and CompetitionPage to use shared utility Changes: - New file: web/src/utils/traderColors.ts (shared color logic) - Updated: ComparisonChart.tsx (use shared utility) - Updated: CompetitionPage.tsx (use shared utility in Leaderboard and Head-to-Head sections) Now traders consistently display the same color across all UI sections. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { api } from '../lib/api';
|
import { api } from '../lib/api';
|
||||||
import type { CompetitionTraderData } from '../types';
|
import type { CompetitionTraderData } from '../types';
|
||||||
|
import { getTraderColor } from '../utils/traderColors';
|
||||||
|
|
||||||
interface ComparisonChartProps {
|
interface ComparisonChartProps {
|
||||||
traders: CompetitionTraderData[];
|
traders: CompetitionTraderData[];
|
||||||
@@ -177,26 +178,8 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Trader颜色配置 - 为每个trader分配不同的颜色
|
// 使用统一的颜色分配逻辑(与Leaderboard保持一致)
|
||||||
// 使用更丰富的颜色池,确保多个trader时不重复
|
const traderColor = (traderId: string) => getTraderColor(traders, traderId);
|
||||||
const TRADER_COLORS = [
|
|
||||||
'#60a5fa', // blue-400
|
|
||||||
'#c084fc', // purple-400
|
|
||||||
'#34d399', // emerald-400
|
|
||||||
'#fb923c', // orange-400
|
|
||||||
'#f472b6', // pink-400
|
|
||||||
'#fbbf24', // amber-400
|
|
||||||
'#38bdf8', // sky-400
|
|
||||||
'#a78bfa', // violet-400
|
|
||||||
'#4ade80', // green-400
|
|
||||||
'#fb7185', // rose-400
|
|
||||||
];
|
|
||||||
|
|
||||||
const getTraderColor = (traderId: string) => {
|
|
||||||
const traderIndex = traders.findIndex((t) => t.trader_id === traderId);
|
|
||||||
// 如果超出颜色池大小,循环使用
|
|
||||||
return TRADER_COLORS[traderIndex % TRADER_COLORS.length];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 自定义Tooltip - Binance Style
|
// 自定义Tooltip - Binance Style
|
||||||
const CustomTooltip = ({ active, payload }: any) => {
|
const CustomTooltip = ({ active, payload }: any) => {
|
||||||
@@ -216,7 +199,7 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
|
|||||||
<div key={trader.trader_id} className="mb-1.5 last:mb-0">
|
<div key={trader.trader_id} className="mb-1.5 last:mb-0">
|
||||||
<div
|
<div
|
||||||
className="text-xs font-semibold mb-0.5"
|
className="text-xs font-semibold mb-0.5"
|
||||||
style={{ color: getTraderColor(trader.trader_id) }}
|
style={{ color: traderColor(trader.trader_id) }}
|
||||||
>
|
>
|
||||||
{trader.trader_name}
|
{trader.trader_name}
|
||||||
</div>
|
</div>
|
||||||
@@ -257,8 +240,8 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
|
|||||||
x2="0"
|
x2="0"
|
||||||
y2="1"
|
y2="1"
|
||||||
>
|
>
|
||||||
<stop offset="5%" stopColor={getTraderColor(trader.trader_id)} stopOpacity={0.9} />
|
<stop offset="5%" stopColor={traderColor(trader.trader_id)} stopOpacity={0.9} />
|
||||||
<stop offset="95%" stopColor={getTraderColor(trader.trader_id)} stopOpacity={0.2} />
|
<stop offset="95%" stopColor={traderColor(trader.trader_id)} stopOpacity={0.2} />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
))}
|
))}
|
||||||
</defs>
|
</defs>
|
||||||
@@ -305,10 +288,10 @@ export function ComparisonChart({ traders }: ComparisonChartProps) {
|
|||||||
key={trader.trader_id}
|
key={trader.trader_id}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey={`${trader.trader_id}_pnl_pct`}
|
dataKey={`${trader.trader_id}_pnl_pct`}
|
||||||
stroke={getTraderColor(trader.trader_id)}
|
stroke={traderColor(trader.trader_id)}
|
||||||
strokeWidth={3}
|
strokeWidth={3}
|
||||||
dot={displayData.length < 50 ? { fill: getTraderColor(trader.trader_id), r: 3 } : false}
|
dot={displayData.length < 50 ? { fill: traderColor(trader.trader_id), r: 3 } : false}
|
||||||
activeDot={{ r: 6, fill: getTraderColor(trader.trader_id), stroke: '#fff', strokeWidth: 2 }}
|
activeDot={{ r: 6, fill: traderColor(trader.trader_id), stroke: '#fff', strokeWidth: 2 }}
|
||||||
name={trader.trader_name}
|
name={trader.trader_name}
|
||||||
connectNulls
|
connectNulls
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { CompetitionData } from '../types';
|
|||||||
import { ComparisonChart } from './ComparisonChart';
|
import { ComparisonChart } from './ComparisonChart';
|
||||||
import { useLanguage } from '../contexts/LanguageContext';
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
import { t } from '../i18n/translations';
|
import { t } from '../i18n/translations';
|
||||||
|
import { getTraderColor } from '../utils/traderColors';
|
||||||
|
|
||||||
export function CompetitionPage() {
|
export function CompetitionPage() {
|
||||||
const { language } = useLanguage();
|
const { language } = useLanguage();
|
||||||
@@ -108,7 +109,7 @@ export function CompetitionPage() {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{sortedTraders.map((trader, index) => {
|
{sortedTraders.map((trader, index) => {
|
||||||
const isLeader = index === 0;
|
const isLeader = index === 0;
|
||||||
const aiModelColor = trader.ai_model === 'qwen' ? '#c084fc' : '#60a5fa';
|
const traderColor = getTraderColor(sortedTraders, trader.trader_id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -128,7 +129,7 @@ export function CompetitionPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold text-sm" style={{ color: '#EAECEF' }}>{trader.trader_name}</div>
|
<div className="font-bold text-sm" style={{ color: '#EAECEF' }}>{trader.trader_name}</div>
|
||||||
<div className="text-xs mono font-semibold" style={{ color: aiModelColor }}>
|
<div className="text-xs mono font-semibold" style={{ color: traderColor }}>
|
||||||
{trader.ai_model.toUpperCase()}
|
{trader.ai_model.toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,7 +224,7 @@ export function CompetitionPage() {
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div
|
<div
|
||||||
className="text-base font-bold mb-2"
|
className="text-base font-bold mb-2"
|
||||||
style={{ color: trader.ai_model === 'qwen' ? '#c084fc' : '#60a5fa' }}
|
style={{ color: getTraderColor(sortedTraders, trader.trader_id) }}
|
||||||
>
|
>
|
||||||
{trader.trader_name}
|
{trader.trader_name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
31
web/src/utils/traderColors.ts
Normal file
31
web/src/utils/traderColors.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Trader颜色配置 - 统一的颜色分配逻辑
|
||||||
|
// 用于 ComparisonChart 和 Leaderboard,确保颜色一致性
|
||||||
|
|
||||||
|
export const TRADER_COLORS = [
|
||||||
|
'#60a5fa', // blue-400
|
||||||
|
'#c084fc', // purple-400
|
||||||
|
'#34d399', // emerald-400
|
||||||
|
'#fb923c', // orange-400
|
||||||
|
'#f472b6', // pink-400
|
||||||
|
'#fbbf24', // amber-400
|
||||||
|
'#38bdf8', // sky-400
|
||||||
|
'#a78bfa', // violet-400
|
||||||
|
'#4ade80', // green-400
|
||||||
|
'#fb7185', // rose-400
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据trader的索引位置获取颜色
|
||||||
|
* @param traders - trader列表
|
||||||
|
* @param traderId - 当前trader的ID
|
||||||
|
* @returns 对应的颜色值
|
||||||
|
*/
|
||||||
|
export function getTraderColor(
|
||||||
|
traders: Array<{ trader_id: string }>,
|
||||||
|
traderId: string
|
||||||
|
): string {
|
||||||
|
const traderIndex = traders.findIndex((t) => t.trader_id === traderId);
|
||||||
|
if (traderIndex === -1) return TRADER_COLORS[0]; // 默认返回第一个颜色
|
||||||
|
// 如果超出颜色池大小,循环使用
|
||||||
|
return TRADER_COLORS[traderIndex % TRADER_COLORS.length];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user