1
This commit is contained in:
parent
ce493b622f
commit
8e025adba9
@ -145,8 +145,81 @@
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
/* Real-time Prices */
|
||||
.price-section {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.price-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.price-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.price-item .symbol {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.price-item .price {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* Core Metrics Grid */
|
||||
.core-metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.core-metric-card {
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.core-metric-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.core-metric-value {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.core-metric-value.positive {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.core-metric-value.negative {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metrics-grid, .stat-grid, .grade-stats {
|
||||
.metrics-grid, .stat-grid, .grade-stats, .core-metrics-grid, .price-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@ -273,6 +346,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Real-time Prices -->
|
||||
<div v-if="Object.keys(latestPrices).length > 0" class="price-section">
|
||||
<div class="stat-label" style="margin-bottom: 8px;">实时价格</div>
|
||||
<div class="price-list">
|
||||
<div class="price-item" v-for="(price, symbol) in latestPrices" :key="symbol">
|
||||
<span class="symbol">{{ symbol }}</span>
|
||||
<span class="price">${{ price.toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tabs">
|
||||
<button class="tab" :class="{ active: currentTab === 'positions' }" @click="switchTab('positions')">
|
||||
@ -284,6 +368,9 @@
|
||||
<button class="tab" :class="{ active: currentTab === 'history' }" @click="switchTab('history')">
|
||||
历史订单 ({{ orderHistory.length }})
|
||||
</button>
|
||||
<button class="tab" :class="{ active: currentTab === 'stats' }" @click="switchTab('stats')">
|
||||
详细统计
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
@ -483,6 +570,94 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detailed Statistics Tab -->
|
||||
<div v-if="currentTab === 'stats'" class="table-container">
|
||||
<!-- Core Metrics -->
|
||||
<div class="core-metrics-grid">
|
||||
<div class="core-metric-card">
|
||||
<div class="core-metric-label">累计收益率</div>
|
||||
<div class="core-metric-value" :class="stats.total_pnl_percent >= 0 ? 'positive' : 'negative'">
|
||||
{{ stats.total_pnl_percent >= 0 ? '+' : '' }}{{ stats.total_pnl_percent ? stats.total_pnl_percent.toFixed(2) : '0.00' }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="core-metric-card">
|
||||
<div class="core-metric-label">总盈亏</div>
|
||||
<div class="core-metric-value" :class="stats.total_pnl >= 0 ? 'positive' : 'negative'">
|
||||
{{ stats.total_pnl >= 0 ? '+' : '' }}${{ stats.total_pnl ? stats.total_pnl.toFixed(2) : '0.00' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="core-metric-card">
|
||||
<div class="core-metric-label">胜率</div>
|
||||
<div class="core-metric-value positive">
|
||||
{{ stats.win_rate ? stats.win_rate.toFixed(1) : '0.0' }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="core-metric-card">
|
||||
<div class="core-metric-label">盈亏比</div>
|
||||
<div class="core-metric-value">
|
||||
{{ stats.profit_factor === Infinity ? '∞' : (stats.profit_factor ? stats.profit_factor.toFixed(2) : '0.00') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grade Stats -->
|
||||
<div class="grade-stats" style="margin-top: 24px;">
|
||||
<div class="grade-card">
|
||||
<div class="grade-card-header">
|
||||
<span class="grade-card-title">交易详情</span>
|
||||
</div>
|
||||
<div class="grade-card-stats">
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">总交易数</span>
|
||||
<span class="grade-stat-value">{{ stats.total_trades || 0 }}</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">盈利交易</span>
|
||||
<span class="grade-stat-value positive">{{ stats.winning_trades || 0 }}</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">亏损交易</span>
|
||||
<span class="grade-stat-value negative">{{ stats.losing_trades || 0 }}</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">最佳交易</span>
|
||||
<span class="grade-stat-value positive">{{ stats.best_trade ? stats.best_trade.toFixed(2) : '0.00' }}%</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">最差交易</span>
|
||||
<span class="grade-stat-value negative">{{ stats.worst_trade ? stats.worst_trade.toFixed(2) : '0.00' }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grade-card">
|
||||
<div class="grade-card-header">
|
||||
<span class="grade-card-title">收益分析</span>
|
||||
</div>
|
||||
<div class="grade-card-stats">
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">平均盈利</span>
|
||||
<span class="grade-stat-value positive">${{ stats.average_win ? stats.average_win.toFixed(2) : '0.00' }}</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">平均亏损</span>
|
||||
<span class="grade-stat-value negative">${{ stats.average_loss ? stats.average_loss.toFixed(2) : '0.00' }}</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">最大回撤</span>
|
||||
<span class="grade-stat-value negative">{{ stats.max_drawdown ? stats.max_drawdown.toFixed(2) : '0.00' }}%</span>
|
||||
</div>
|
||||
<div class="grade-stat-row">
|
||||
<span class="grade-stat-label">收益率</span>
|
||||
<span class="grade-stat-value" :class="stats.return_percent >= 0 ? 'positive' : 'negative'">
|
||||
{{ stats.return_percent >= 0 ? '+' : '' }}{{ stats.return_percent ? stats.return_percent.toFixed(2) : '0.00' }}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -534,7 +709,9 @@
|
||||
showAdminMenu: false,
|
||||
adminPassword: '223388',
|
||||
titleClickCount: 0,
|
||||
titleClickTimer: null
|
||||
titleClickTimer: null,
|
||||
refreshInterval: null,
|
||||
latestPrices: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -571,6 +748,18 @@
|
||||
}
|
||||
},
|
||||
|
||||
async silentRefresh() {
|
||||
try {
|
||||
await Promise.all([
|
||||
this.fetchAccountStatus(),
|
||||
this.fetchStatistics(),
|
||||
this.fetchOrders()
|
||||
]);
|
||||
} catch (e) {
|
||||
console.error('静默刷新失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
async fetchAccountStatus() {
|
||||
try {
|
||||
const response = await axios.get('/api/trading/account');
|
||||
@ -599,6 +788,10 @@
|
||||
console.log('API Response:', response.data);
|
||||
if (response.data.success) {
|
||||
this.orders = response.data.orders || [];
|
||||
// 获取实时价格
|
||||
if (response.data.latest_prices) {
|
||||
this.latestPrices = response.data.latest_prices;
|
||||
}
|
||||
console.log('Orders loaded:', this.orders.length);
|
||||
console.log('Orders data:', this.orders);
|
||||
console.log('Open positions:', this.orders.filter(o => o.status === 'open'));
|
||||
@ -768,12 +961,22 @@
|
||||
mounted() {
|
||||
this.refreshData();
|
||||
|
||||
// 每3秒自动刷新(静默刷新,不显示 loading)
|
||||
this.refreshInterval = setInterval(() => {
|
||||
this.silentRefresh();
|
||||
}, 3000);
|
||||
|
||||
// 点击外部关闭管理菜单
|
||||
document.addEventListener('click', (e) => {
|
||||
if (this.showAdminMenu && !e.target.closest('.admin-dropdown')) {
|
||||
this.showAdminMenu = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.refreshInterval) {
|
||||
clearInterval(this.refreshInterval);
|
||||
}
|
||||
}
|
||||
}).mount('#app');
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user