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:
tinkle-community
2026-06-30 16:03:52 +08:00
parent eba28bcf0e
commit 110bf52908
149 changed files with 6835 additions and 3611 deletions

View File

@@ -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)