Skip to content

Commit ad4dae7

Browse files
feat: implement depth chart visualization and comprehensive trading panel components including trade form, position list, and margin settings.
1 parent e038b4d commit ad4dae7

15 files changed

Lines changed: 1638 additions & 1315 deletions

src/App.tsx

Lines changed: 58 additions & 349 deletions
Large diffs are not rendered by default.
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { memo, useState, useCallback } from 'react';
2+
import ChartToolbar, {
3+
type Timeframe,
4+
type Indicator,
5+
} from './ChartToolbar';
6+
import KLineChart, { type KLineChartHandle } from './index';
7+
import StatsPanel from '../StatsPanel';
8+
import type { MarketStats } from '../../../hooks/useMarketStats';
9+
import type { DataSource } from '../../../hooks/useDataSource';
10+
11+
/* ============================================
12+
Constants
13+
============================================ */
14+
15+
const MAIN_INDICATORS = ['MA', 'EMA', 'BOLL'] as const;
16+
const SUB_INDICATORS = ['VOL', 'MACD', 'RSI'] as const;
17+
18+
/* ============================================
19+
Types
20+
============================================ */
21+
22+
export interface ChartViewProps {
23+
chartRef: React.RefObject<KLineChartHandle>;
24+
candleHistory: any[];
25+
currentLiveCandle: any | null;
26+
indicatorData: any;
27+
latestData: any | null;
28+
analysisResult: any | null;
29+
activeTimeframe: Timeframe;
30+
onTimeframeChange: (tf: Timeframe) => void;
31+
switchVisible: boolean;
32+
dataSource: DataSource;
33+
marketStats?: MarketStats | null;
34+
onChartViewClick?: () => void;
35+
}
36+
37+
/* ============================================
38+
Component
39+
============================================ */
40+
41+
function ChartView({
42+
chartRef,
43+
candleHistory,
44+
currentLiveCandle,
45+
indicatorData,
46+
latestData,
47+
analysisResult,
48+
activeTimeframe,
49+
onTimeframeChange,
50+
switchVisible,
51+
dataSource,
52+
marketStats,
53+
onChartViewClick,
54+
}: ChartViewProps) {
55+
// 指标状态
56+
const [activeIndicators, setActiveIndicators] = useState<Indicator[]>(['MA', 'VOL']);
57+
const [activeChartType, setActiveChartType] = useState<'TradingView' | 'Depth'>('TradingView');
58+
59+
// 指标切换
60+
const handleIndicatorToggle = useCallback((indicator: Indicator) => {
61+
setActiveIndicators((prev) => {
62+
const isMain = (MAIN_INDICATORS as readonly string[]).includes(indicator as string);
63+
const isSub = (SUB_INDICATORS as readonly string[]).includes(indicator as string);
64+
65+
if (isMain) {
66+
const others = prev.filter(
67+
(ind) => !(MAIN_INDICATORS as readonly string[]).includes(ind as string),
68+
);
69+
return [...others, indicator];
70+
}
71+
72+
if (isSub) {
73+
if (prev.includes(indicator)) {
74+
return prev.filter((ind) => ind !== indicator);
75+
}
76+
return [...prev, indicator];
77+
}
78+
79+
return prev;
80+
});
81+
}, []);
82+
83+
// 图表类型切换
84+
const handleChartTypeChange = useCallback((chartType: 'TradingView' | 'Depth') => {
85+
setActiveChartType(chartType);
86+
}, []);
87+
88+
// 截图
89+
const handleScreenshot = useCallback(() => {
90+
chartRef.current?.takeScreenshot();
91+
}, [chartRef]);
92+
93+
// 指标分类
94+
const activeMainIndicators = activeIndicators.filter((ind) =>
95+
MAIN_INDICATORS.includes(ind as (typeof MAIN_INDICATORS)[number]),
96+
);
97+
const activeSubIndicators = activeIndicators.filter((ind) =>
98+
SUB_INDICATORS.includes(ind as (typeof SUB_INDICATORS)[number]),
99+
);
100+
101+
return (
102+
<div className="flex flex-col h-full">
103+
{/* Chart Toolbar */}
104+
<ChartToolbar
105+
activeTimeframe={activeTimeframe}
106+
onTimeframeChange={onTimeframeChange}
107+
activeIndicators={activeIndicators}
108+
onIndicatorToggle={handleIndicatorToggle}
109+
activeChartType={activeChartType}
110+
onChartTypeChange={handleChartTypeChange}
111+
onScreenshotClick={handleScreenshot}
112+
onChartViewClick={onChartViewClick}
113+
candleCountdown={marketStats?.candleCountdown}
114+
/>
115+
116+
{/* Chart Area */}
117+
<div className="flex-1 min-h-0 flex flex-col relative group">
118+
{/* Sub-Header */}
119+
<div className="shrink-0 h-7 md:h-8 px-2 md:px-3 flex items-center justify-between border-b border-border-dark bg-bg-black">
120+
<div className="flex items-center gap-2 md:gap-3">
121+
<h2 className="text-[10px] md:text-[11px] font-medium text-gray-400 truncate max-w-[120px] md:max-w-none">
122+
{latestData?.symbol ?? 'BTC-USDT'} · Perp
123+
</h2>
124+
<span className="text-[9px] md:text-[10px] font-mono text-gray-600">
125+
{activeChartType === 'Depth'
126+
? 'Market Depth'
127+
: `${candleHistory.length} candles`}
128+
</span>
129+
</div>
130+
<div className="hidden sm:flex items-center gap-2 text-[10px] font-mono text-gray-500">
131+
<span className="flex items-center gap-1">
132+
<span className="w-2 h-2 bg-success rounded-sm" />
133+
<span className="w-2 h-2 bg-danger rounded-sm" />
134+
</span>
135+
<span>OHLC</span>
136+
</div>
137+
</div>
138+
139+
{/* Chart Body */}
140+
<div className="flex-1 min-h-0 overflow-hidden relative">
141+
{switchVisible && (
142+
<div className="absolute inset-0 z-10 flex items-center justify-center bg-bg-black/60 backdrop-blur-[1px]">
143+
<div className="flex flex-col items-center gap-2">
144+
<span className="w-6 h-6 border-2 border-border-dark border-t-success rounded-full animate-spin" />
145+
<span className="text-[11px] font-mono text-gray-400">
146+
{dataSource === 'binance' ? 'LIVE 切换中…' : 'MOCK 切换中…'}
147+
</span>
148+
</div>
149+
</div>
150+
)}
151+
<KLineChart
152+
ref={chartRef}
153+
candleHistory={candleHistory}
154+
currentLiveCandle={currentLiveCandle}
155+
indicatorData={indicatorData}
156+
activeMainIndicators={activeMainIndicators}
157+
activeSubIndicators={activeSubIndicators}
158+
/>
159+
</div>
160+
</div>
161+
162+
{/* Stats Panel */}
163+
<StatsPanel analysisResult={analysisResult} marketStats={marketStats ?? undefined} />
164+
</div>
165+
);
166+
}
167+
168+
export default memo(ChartView);

0 commit comments

Comments
 (0)