mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-26 21:42:41 +08:00
feat: cream terminal redesign, English-only UI, autopilot launch fixes
- Redesign dashboard into a cream-paper + vermilion IBM Plex Mono terminal (live L2 order book, cost/liq map, WS K-line, signal matrix, orchestration topology, risk radar, execution log, current positions, equity curve) - Convert all user-facing UI and backend strings/prompts from Chinese to English (multi-language retained, default English) - Add /api/statistics/full endpoint + full-stats frontend wiring - Fix Autopilot launch: reuse the existing trader instead of creating duplicates (eliminates repeat ~35s create cost and stale-trader 404s); launch sends 5m scan interval - Fix unreadable toasts: cream theme with high-contrast text + per-type accent - Silence background dashboard polls (getTraderConfig) to stop error-toast spam
This commit is contained in:
@@ -3,7 +3,7 @@ import { useLanguage } from '../../contexts/LanguageContext'
|
||||
import { t } from '../../i18n/translations'
|
||||
import { ChevronDown, TrendingUp, X } from 'lucide-react'
|
||||
|
||||
// 支持的交易所列表 (合约格式)
|
||||
// Supported exchanges list (futures format)
|
||||
const EXCHANGES = [
|
||||
{ id: 'BINANCE', name: 'Binance', prefix: 'BINANCE:', suffix: '.P' },
|
||||
{ id: 'BYBIT', name: 'Bybit', prefix: 'BYBIT:', suffix: '.P' },
|
||||
@@ -13,7 +13,7 @@ const EXCHANGES = [
|
||||
{ id: 'GATEIO', name: 'Gate.io', prefix: 'GATEIO:', suffix: '.P' },
|
||||
] as const
|
||||
|
||||
// 热门交易对
|
||||
// Popular trading pairs
|
||||
const POPULAR_SYMBOLS = [
|
||||
'BTCUSDT',
|
||||
'ETHUSDT',
|
||||
@@ -29,7 +29,7 @@ const POPULAR_SYMBOLS = [
|
||||
'LTCUSDT',
|
||||
]
|
||||
|
||||
// 时间周期选项
|
||||
// Time interval options
|
||||
const INTERVALS = [
|
||||
{ id: '1', label: '1m' },
|
||||
{ id: '5', label: '5m' },
|
||||
@@ -46,7 +46,7 @@ interface TradingViewChartProps {
|
||||
defaultExchange?: string
|
||||
height?: number
|
||||
showToolbar?: boolean
|
||||
embedded?: boolean // 嵌入模式(不显示外层卡片)
|
||||
embedded?: boolean // Embedded mode (does not show the outer card)
|
||||
}
|
||||
|
||||
function TradingViewChartComponent({
|
||||
@@ -66,26 +66,26 @@ function TradingViewChartComponent({
|
||||
const [showSymbolDropdown, setShowSymbolDropdown] = useState(false)
|
||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
||||
|
||||
// 当外部传入的 defaultSymbol 变化时,更新内部 symbol
|
||||
// Update the internal symbol when the external defaultSymbol changes
|
||||
useEffect(() => {
|
||||
if (defaultSymbol && defaultSymbol !== symbol) {
|
||||
// console.log('[TradingViewChart] 更新币种:', defaultSymbol)
|
||||
// console.log('[TradingViewChart] Updating symbol:', defaultSymbol)
|
||||
setSymbol(defaultSymbol)
|
||||
}
|
||||
}, [defaultSymbol])
|
||||
|
||||
// 当外部传入的 defaultExchange 变化时,更新内部 exchange
|
||||
// Update the internal exchange when the external defaultExchange changes
|
||||
useEffect(() => {
|
||||
if (defaultExchange && defaultExchange !== exchange) {
|
||||
const normalizedExchange = defaultExchange.toUpperCase()
|
||||
// console.log('[TradingViewChart] 更新交易所:', normalizedExchange)
|
||||
// console.log('[TradingViewChart] Updating exchange:', normalizedExchange)
|
||||
if (EXCHANGES.some(e => e.id === normalizedExchange)) {
|
||||
setExchange(normalizedExchange)
|
||||
}
|
||||
}
|
||||
}, [defaultExchange])
|
||||
|
||||
// 获取完整的交易对符号 (合约格式: BINANCE:BTCUSDT.P)
|
||||
// Get the full trading pair symbol (futures format: BINANCE:BTCUSDT.P)
|
||||
const getFullSymbol = () => {
|
||||
const exchangeInfo = EXCHANGES.find((e) => e.id === exchange)
|
||||
const prefix = exchangeInfo?.prefix || 'BINANCE:'
|
||||
@@ -93,14 +93,14 @@ function TradingViewChartComponent({
|
||||
return `${prefix}${symbol}${suffix}`
|
||||
}
|
||||
|
||||
// 加载 TradingView Widget
|
||||
// Load the TradingView Widget
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return
|
||||
|
||||
// 清空容器
|
||||
// Clear the container
|
||||
containerRef.current.innerHTML = ''
|
||||
|
||||
// 创建 widget 容器
|
||||
// Create the widget container
|
||||
const widgetContainer = document.createElement('div')
|
||||
widgetContainer.className = 'tradingview-widget-container'
|
||||
widgetContainer.style.height = '100%'
|
||||
@@ -114,7 +114,7 @@ function TradingViewChartComponent({
|
||||
widgetContainer.appendChild(widgetDiv)
|
||||
containerRef.current.appendChild(widgetContainer)
|
||||
|
||||
// 加载 TradingView 脚本
|
||||
// Load the TradingView script
|
||||
const script = document.createElement('script')
|
||||
script.src =
|
||||
'https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js'
|
||||
@@ -126,12 +126,12 @@ function TradingViewChartComponent({
|
||||
symbol: getFullSymbol(),
|
||||
interval: timeInterval,
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'Asia/Shanghai',
|
||||
theme: 'dark',
|
||||
theme: 'light',
|
||||
style: '1',
|
||||
locale: language === 'zh' ? 'zh_CN' : 'en',
|
||||
enable_publishing: false,
|
||||
backgroundColor: 'rgba(11, 14, 17, 1)',
|
||||
gridColor: 'rgba(43, 49, 57, 0.5)',
|
||||
backgroundColor: 'rgba(241, 236, 226, 1)',
|
||||
gridColor: 'rgba(26, 24, 19, 0.08)',
|
||||
hide_top_toolbar: !showToolbar,
|
||||
hide_legend: false,
|
||||
save_image: false,
|
||||
@@ -149,11 +149,11 @@ function TradingViewChartComponent({
|
||||
}
|
||||
}, [exchange, symbol, timeInterval, language, showToolbar])
|
||||
|
||||
// 处理自定义交易对输入
|
||||
// Handle custom trading pair input
|
||||
const handleCustomSymbolSubmit = () => {
|
||||
if (customSymbol.trim()) {
|
||||
let sym = customSymbol.trim().toUpperCase()
|
||||
// 如果没有 USDT 后缀,自动加上
|
||||
// If there is no USDT suffix, add it automatically
|
||||
if (!sym.endsWith('USDT')) {
|
||||
sym = sym + 'USDT'
|
||||
}
|
||||
@@ -169,19 +169,19 @@ function TradingViewChartComponent({
|
||||
? 'fixed inset-0 z-50 rounded-none flex flex-col'
|
||||
: ''
|
||||
}`}
|
||||
style={isFullscreen ? { background: '#0B0E11' } : undefined}
|
||||
style={isFullscreen ? { background: '#F1ECE2' } : undefined}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className="flex flex-wrap items-center gap-2 p-3 sm:p-4"
|
||||
style={{ borderBottom: embedded ? 'none' : '1px solid #2B3139' }}
|
||||
style={{ borderBottom: embedded ? 'none' : '1px solid rgba(26, 24, 19, 0.14)' }}
|
||||
>
|
||||
{!embedded && (
|
||||
<div className="flex items-center gap-2">
|
||||
<TrendingUp className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
<TrendingUp className="w-5 h-5" style={{ color: '#E0483B' }} />
|
||||
<h3
|
||||
className="text-base sm:text-lg font-bold"
|
||||
style={{ color: '#EAECEF' }}
|
||||
style={{ color: '#1A1813' }}
|
||||
>
|
||||
{t('marketChart', language)}
|
||||
</h3>
|
||||
@@ -199,21 +199,21 @@ function TradingViewChartComponent({
|
||||
}}
|
||||
className="flex items-center gap-1 px-3 py-1.5 rounded text-sm font-medium transition-all"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
border: '1px solid #2B3139',
|
||||
color: '#EAECEF',
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
color: '#1A1813',
|
||||
}}
|
||||
>
|
||||
{EXCHANGES.find((e) => e.id === exchange)?.name || exchange}
|
||||
<ChevronDown className="w-4 h-4" style={{ color: '#848E9C' }} />
|
||||
<ChevronDown className="w-4 h-4" style={{ color: '#8A8478' }} />
|
||||
</button>
|
||||
|
||||
{showExchangeDropdown && (
|
||||
<div
|
||||
className="absolute top-full left-0 mt-1 py-1 rounded-lg shadow-xl z-20 min-w-[120px]"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
border: '1px solid #2B3139',
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
{EXCHANGES.map((ex) => (
|
||||
@@ -225,10 +225,10 @@ function TradingViewChartComponent({
|
||||
}}
|
||||
className="w-full px-4 py-2 text-left text-sm transition-all hover:bg-opacity-50"
|
||||
style={{
|
||||
color: exchange === ex.id ? '#F0B90B' : '#EAECEF',
|
||||
color: exchange === ex.id ? '#E0483B' : '#1A1813',
|
||||
background:
|
||||
exchange === ex.id
|
||||
? 'rgba(240, 185, 11, 0.1)'
|
||||
? 'rgba(224, 72, 59, 0.1)'
|
||||
: 'transparent',
|
||||
}}
|
||||
>
|
||||
@@ -248,9 +248,9 @@ function TradingViewChartComponent({
|
||||
}}
|
||||
className="flex items-center gap-1 px-3 py-1.5 rounded text-sm font-bold transition-all"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.3)',
|
||||
color: '#F0B90B',
|
||||
background: 'rgba(224, 72, 59, 0.1)',
|
||||
border: '1px solid rgba(224, 72, 59, 0.3)',
|
||||
color: '#E0483B',
|
||||
}}
|
||||
>
|
||||
{symbol}
|
||||
@@ -261,12 +261,12 @@ function TradingViewChartComponent({
|
||||
<div
|
||||
className="absolute top-full left-0 mt-1 py-2 rounded-lg shadow-xl z-20 w-[280px]"
|
||||
style={{
|
||||
background: '#1E2329',
|
||||
border: '1px solid #2B3139',
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
{/* Custom Input */}
|
||||
<div className="px-3 pb-2" style={{ borderBottom: '1px solid #2B3139' }}>
|
||||
<div className="px-3 pb-2" style={{ borderBottom: '1px solid rgba(26, 24, 19, 0.14)' }}>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
@@ -276,17 +276,17 @@ function TradingViewChartComponent({
|
||||
placeholder={t('enterSymbol', language)}
|
||||
className="flex-1 px-3 py-1.5 rounded text-sm"
|
||||
style={{
|
||||
background: '#0B0E11',
|
||||
border: '1px solid #2B3139',
|
||||
color: '#EAECEF',
|
||||
background: '#F1ECE2',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
color: '#1A1813',
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
onClick={handleCustomSymbolSubmit}
|
||||
className="px-3 py-1.5 rounded text-sm font-medium"
|
||||
style={{
|
||||
background: '#F0B90B',
|
||||
color: '#0B0E11',
|
||||
background: '#E0483B',
|
||||
color: '#F1ECE2',
|
||||
}}
|
||||
>
|
||||
OK
|
||||
@@ -298,7 +298,7 @@ function TradingViewChartComponent({
|
||||
<div className="px-2 pt-2">
|
||||
<div
|
||||
className="text-xs px-2 py-1 mb-1"
|
||||
style={{ color: '#848E9C' }}
|
||||
style={{ color: '#8A8478' }}
|
||||
>
|
||||
{t('popularSymbols', language)}
|
||||
</div>
|
||||
@@ -312,11 +312,11 @@ function TradingViewChartComponent({
|
||||
}}
|
||||
className="px-2 py-1.5 rounded text-xs font-medium transition-all"
|
||||
style={{
|
||||
color: symbol === sym ? '#F0B90B' : '#EAECEF',
|
||||
color: symbol === sym ? '#E0483B' : '#1A1813',
|
||||
background:
|
||||
symbol === sym
|
||||
? 'rgba(240, 185, 11, 0.1)'
|
||||
: 'rgba(43, 49, 57, 0.3)',
|
||||
? 'rgba(224, 72, 59, 0.1)'
|
||||
: 'rgba(26, 24, 19, 0.04)',
|
||||
}}
|
||||
>
|
||||
{sym.replace('USDT', '')}
|
||||
@@ -331,7 +331,7 @@ function TradingViewChartComponent({
|
||||
{/* Interval Selector */}
|
||||
<div
|
||||
className="flex gap-0.5 p-0.5 rounded"
|
||||
style={{ background: '#0B0E11', border: '1px solid #2B3139' }}
|
||||
style={{ background: '#E8E2D5', border: '1px solid rgba(26, 24, 19, 0.14)' }}
|
||||
>
|
||||
{INTERVALS.map((int) => (
|
||||
<button
|
||||
@@ -339,8 +339,8 @@ function TradingViewChartComponent({
|
||||
onClick={() => setTimeInterval(int.id)}
|
||||
className="px-2 py-1 rounded text-xs font-medium transition-all"
|
||||
style={{
|
||||
background: timeInterval === int.id ? '#F0B90B' : 'transparent',
|
||||
color: timeInterval === int.id ? '#0B0E11' : '#848E9C',
|
||||
background: timeInterval === int.id ? '#E0483B' : 'transparent',
|
||||
color: timeInterval === int.id ? '#F1ECE2' : '#8A8478',
|
||||
}}
|
||||
>
|
||||
{int.label}
|
||||
@@ -353,9 +353,9 @@ function TradingViewChartComponent({
|
||||
onClick={() => setIsFullscreen(!isFullscreen)}
|
||||
className="p-1.5 rounded transition-all"
|
||||
style={{
|
||||
background: isFullscreen ? '#F0B90B' : 'transparent',
|
||||
color: isFullscreen ? '#0B0E11' : '#848E9C',
|
||||
border: '1px solid #2B3139',
|
||||
background: isFullscreen ? '#E0483B' : 'transparent',
|
||||
color: isFullscreen ? '#F1ECE2' : '#8A8478',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
title={isFullscreen ? t('exitFullscreen', language) : t('fullscreen', language)}
|
||||
>
|
||||
@@ -375,7 +375,7 @@ function TradingViewChartComponent({
|
||||
ref={containerRef}
|
||||
style={{
|
||||
height: isFullscreen ? 'calc(100vh - 65px)' : height,
|
||||
background: '#0B0E11',
|
||||
background: '#F1ECE2',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
/>
|
||||
@@ -394,5 +394,5 @@ function TradingViewChartComponent({
|
||||
)
|
||||
}
|
||||
|
||||
// 使用 memo 避免不必要的重渲染
|
||||
// Use memo to avoid unnecessary re-renders
|
||||
export const TradingViewChart = memo(TradingViewChartComponent)
|
||||
|
||||
Reference in New Issue
Block a user