import { useEffect, useRef, useState, memo } from 'react' import { useLanguage } from '../contexts/LanguageContext' import { t } from '../i18n/translations' import { ChevronDown, TrendingUp, X } from 'lucide-react' // 支持的交易所列表 (合约格式) const EXCHANGES = [ { id: 'BINANCE', name: 'Binance', prefix: 'BINANCE:', suffix: '.P' }, { id: 'BYBIT', name: 'Bybit', prefix: 'BYBIT:', suffix: '.P' }, { id: 'OKX', name: 'OKX', prefix: 'OKX:', suffix: '.P' }, { id: 'BITGET', name: 'Bitget', prefix: 'BITGET:', suffix: '.P' }, { id: 'MEXC', name: 'MEXC', prefix: 'MEXC:', suffix: '.P' }, { id: 'GATEIO', name: 'Gate.io', prefix: 'GATEIO:', suffix: '.P' }, ] as const // 热门交易对 const POPULAR_SYMBOLS = [ 'BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT', 'DOGEUSDT', 'ADAUSDT', 'AVAXUSDT', 'DOTUSDT', 'LINKUSDT', 'MATICUSDT', 'LTCUSDT', ] // 时间周期选项 const INTERVALS = [ { id: '1', label: '1m' }, { id: '5', label: '5m' }, { id: '15', label: '15m' }, { id: '30', label: '30m' }, { id: '60', label: '1H' }, { id: '240', label: '4H' }, { id: 'D', label: '1D' }, { id: 'W', label: '1W' }, ] interface TradingViewChartProps { defaultSymbol?: string defaultExchange?: string height?: number showToolbar?: boolean embedded?: boolean // 嵌入模式(不显示外层卡片) } function TradingViewChartComponent({ defaultSymbol = 'BTCUSDT', defaultExchange = 'BINANCE', height = 400, showToolbar = true, embedded = false, }: TradingViewChartProps) { const { language } = useLanguage() const containerRef = useRef(null) const [exchange, setExchange] = useState(defaultExchange) const [symbol, setSymbol] = useState(defaultSymbol) const [timeInterval, setTimeInterval] = useState('60') const [customSymbol, setCustomSymbol] = useState('') const [showExchangeDropdown, setShowExchangeDropdown] = useState(false) const [showSymbolDropdown, setShowSymbolDropdown] = useState(false) const [isFullscreen, setIsFullscreen] = useState(false) // 当外部传入的 defaultSymbol 变化时,更新内部 symbol useEffect(() => { if (defaultSymbol && defaultSymbol !== symbol) { // console.log('[TradingViewChart] 更新币种:', defaultSymbol) setSymbol(defaultSymbol) } }, [defaultSymbol]) // 当外部传入的 defaultExchange 变化时,更新内部 exchange useEffect(() => { if (defaultExchange && defaultExchange !== exchange) { const normalizedExchange = defaultExchange.toUpperCase() // console.log('[TradingViewChart] 更新交易所:', normalizedExchange) if (EXCHANGES.some(e => e.id === normalizedExchange)) { setExchange(normalizedExchange) } } }, [defaultExchange]) // 获取完整的交易对符号 (合约格式: BINANCE:BTCUSDT.P) const getFullSymbol = () => { const exchangeInfo = EXCHANGES.find((e) => e.id === exchange) const prefix = exchangeInfo?.prefix || 'BINANCE:' const suffix = exchangeInfo?.suffix || '.P' return `${prefix}${symbol}${suffix}` } // 加载 TradingView Widget useEffect(() => { if (!containerRef.current) return // 清空容器 containerRef.current.innerHTML = '' // 创建 widget 容器 const widgetContainer = document.createElement('div') widgetContainer.className = 'tradingview-widget-container' widgetContainer.style.height = '100%' widgetContainer.style.width = '100%' const widgetDiv = document.createElement('div') widgetDiv.className = 'tradingview-widget-container__widget' widgetDiv.style.height = '100%' widgetDiv.style.width = '100%' widgetContainer.appendChild(widgetDiv) containerRef.current.appendChild(widgetContainer) // 加载 TradingView 脚本 const script = document.createElement('script') script.src = 'https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js' script.type = 'text/javascript' script.async = true script.innerHTML = JSON.stringify({ width: '100%', height: '100%', symbol: getFullSymbol(), interval: timeInterval, timezone: 'Etc/UTC', theme: 'dark', style: '1', locale: language === 'zh' ? 'zh_CN' : 'en', enable_publishing: false, backgroundColor: 'rgba(11, 14, 17, 1)', gridColor: 'rgba(43, 49, 57, 0.5)', hide_top_toolbar: !showToolbar, hide_legend: false, save_image: false, calendar: false, hide_volume: false, support_host: 'https://www.tradingview.com', }) widgetContainer.appendChild(script) return () => { if (containerRef.current) { containerRef.current.innerHTML = '' } } }, [exchange, symbol, timeInterval, language, showToolbar]) // 处理自定义交易对输入 const handleCustomSymbolSubmit = () => { if (customSymbol.trim()) { let sym = customSymbol.trim().toUpperCase() // 如果没有 USDT 后缀,自动加上 if (!sym.endsWith('USDT')) { sym = sym + 'USDT' } setSymbol(sym) setCustomSymbol('') setShowSymbolDropdown(false) } } return (
{/* Header */}
{!embedded && (

{t('marketChart', language)}

)} {/* Controls */}
{/* Exchange Selector */}
{showExchangeDropdown && (
{EXCHANGES.map((ex) => ( ))}
)}
{/* Symbol Selector */}
{showSymbolDropdown && (
{/* Custom Input */}
setCustomSymbol(e.target.value.toUpperCase())} onKeyDown={(e) => e.key === 'Enter' && handleCustomSymbolSubmit()} placeholder={t('enterSymbol', language)} className="flex-1 px-3 py-1.5 rounded text-sm" style={{ background: '#0B0E11', border: '1px solid #2B3139', color: '#EAECEF', }} />
{/* Popular Symbols */}
{t('popularSymbols', language)}
{POPULAR_SYMBOLS.map((sym) => ( ))}
)}
{/* Interval Selector */}
{INTERVALS.map((int) => ( ))}
{/* Fullscreen Toggle */}
{/* Chart Container */}
{/* Click outside to close dropdowns */} {(showExchangeDropdown || showSymbolDropdown) && (
{ setShowExchangeDropdown(false) setShowSymbolDropdown(false) }} /> )}
) } // 使用 memo 避免不必要的重渲染 export const TradingViewChart = memo(TradingViewChartComponent)