feat: improve trading UI with interactive position table and chart tabs

- Add clickable position rows that scroll to chart and update symbol
- Add framer-motion animations to chart tab transitions
- Sync exchange selection between positions and TradingView chart
- Optimize position table layout with compact styling
- Update CSS with glass effects and premium button styles
This commit is contained in:
tinkle-community
2025-12-08 00:34:49 +08:00
parent 2334d78e4a
commit 0636ced476
5 changed files with 1204 additions and 1123 deletions

View File

@@ -4,16 +4,18 @@ import { TradingViewChart } from './TradingViewChart'
import { useLanguage } from '../contexts/LanguageContext'
import { t } from '../i18n/translations'
import { BarChart3, CandlestickChart } from 'lucide-react'
import { motion, AnimatePresence } from 'framer-motion'
interface ChartTabsProps {
traderId: string
selectedSymbol?: string // 从外部选择的币种
updateKey?: number // 强制更新的 key
exchangeId?: string // 交易所ID
}
type ChartTab = 'equity' | 'kline'
export function ChartTabs({ traderId, selectedSymbol, updateKey }: ChartTabsProps) {
export function ChartTabs({ traderId, selectedSymbol, updateKey, exchangeId }: ChartTabsProps) {
const { language } = useLanguage()
const [activeTab, setActiveTab] = useState<ChartTab>('equity')
const [chartSymbol, setChartSymbol] = useState<string>('BTCUSDT')
@@ -44,20 +46,10 @@ export function ChartTabs({ traderId, selectedSymbol, updateKey }: ChartTabsProp
console.log('[ChartTabs] switching to equity')
setActiveTab('equity')
}}
className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold"
style={
activeTab === 'equity'
? {
background: 'rgba(240, 185, 11, 0.15)',
color: '#F0B90B',
border: '1px solid rgba(240, 185, 11, 0.3)',
}
: {
background: 'transparent',
color: '#848E9C',
border: '1px solid transparent',
}
}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all ${activeTab === 'equity'
? 'bg-yellow-500/10 text-yellow-500 border border-yellow-500/30 shadow-[0_0_10px_rgba(252,213,53,0.15)]'
: 'text-gray-400 hover:text-white hover:bg-white/5 border border-transparent'
}`}
>
<BarChart3 className="w-4 h-4" />
{t('accountEquityCurve', language)}
@@ -68,20 +60,10 @@ export function ChartTabs({ traderId, selectedSymbol, updateKey }: ChartTabsProp
console.log('[ChartTabs] switching to kline')
setActiveTab('kline')
}}
className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold"
style={
activeTab === 'kline'
? {
background: 'rgba(240, 185, 11, 0.15)',
color: '#F0B90B',
border: '1px solid rgba(240, 185, 11, 0.3)',
}
: {
background: 'transparent',
color: '#848E9C',
border: '1px solid transparent',
}
}
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold transition-all ${activeTab === 'kline'
? 'bg-yellow-500/10 text-yellow-500 border border-yellow-500/30 shadow-[0_0_10px_rgba(252,213,53,0.15)]'
: 'text-gray-400 hover:text-white hover:bg-white/5 border border-transparent'
}`}
>
<CandlestickChart className="w-4 h-4" />
{t('marketChart', language)}
@@ -89,17 +71,38 @@ export function ChartTabs({ traderId, selectedSymbol, updateKey }: ChartTabsProp
</div>
{/* Tab Content */}
<div>
{activeTab === 'equity' ? (
<EquityChart traderId={traderId} embedded />
) : (
<TradingViewChart
height={400}
embedded
defaultSymbol={chartSymbol}
key={chartSymbol} // 强制重新渲染当币种变化时
/>
)}
<div className="relative overflow-hidden min-h-[400px]">
<AnimatePresence mode="wait">
{activeTab === 'equity' ? (
<motion.div
key="equity"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
transition={{ duration: 0.2 }}
className="h-full"
>
<EquityChart traderId={traderId} embedded />
</motion.div>
) : (
<motion.div
key={`kline-${chartSymbol}-${exchangeId}`}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.2 }}
className="h-full"
>
<TradingViewChart
height={400}
embedded
defaultSymbol={chartSymbol}
defaultExchange={exchangeId}
key={`${chartSymbol}-${exchangeId}-${updateKey || ''}`}
/>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
)

View File

@@ -69,11 +69,22 @@ function TradingViewChartComponent({
// 当外部传入的 defaultSymbol 变化时,更新内部 symbol
useEffect(() => {
if (defaultSymbol && defaultSymbol !== symbol) {
console.log('[TradingViewChart] 更新币种:', defaultSymbol)
// 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)
@@ -154,11 +165,10 @@ function TradingViewChartComponent({
return (
<div
className={`${embedded ? '' : 'binance-card'} overflow-hidden ${embedded ? '' : 'animate-fade-in'} ${
isFullscreen
className={`${embedded ? '' : 'binance-card'} overflow-hidden ${embedded ? '' : 'animate-fade-in'} ${isFullscreen
? 'fixed inset-0 z-50 rounded-none flex flex-col'
: ''
}`}
}`}
style={isFullscreen ? { background: '#0B0E11' } : undefined}
>
{/* Header */}