diff --git a/frontend/src/components/AdminDashboard.css b/frontend/src/components/AdminDashboard.css new file mode 100644 index 0000000..08e6240 --- /dev/null +++ b/frontend/src/components/AdminDashboard.css @@ -0,0 +1,117 @@ +.admin-dashboard { + padding: 24px; + max-width: 1200px; + margin: 0 auto; +} + +.dashboard-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; +} + +.refresh-btn { + padding: 8px 16px; + background-color: #f0f0f0; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; +} + +.refresh-btn:hover { + background-color: #e0e0e0; +} + +.summary-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 24px; + margin-bottom: 32px; +} + +.card { + background: white; + padding: 24px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.card-title { + color: #666; + font-size: 14px; + margin-bottom: 8px; +} + +.card-value { + font-size: 24px; + font-weight: bold; + color: #333; +} + +.card-value.profit { + color: #2e7d32; +} + +.card-value.loss { + color: #c62828; +} + +.accounts-section { + background: white; + padding: 24px; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.accounts-section h3 { + margin-top: 0; + margin-bottom: 16px; +} + +.table-container { + overflow-x: auto; +} + +.data-table { + width: 100%; + border-collapse: collapse; +} + +.data-table th, +.data-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #eee; +} + +.data-table th { + background-color: #f8f9fa; + font-weight: 600; + color: #555; +} + +.status-badge { + display: inline-block; + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; +} + +.status-badge.active { + background-color: #e8f5e9; + color: #2e7d32; +} + +.status-badge.stopped { + background-color: #ffebee; + color: #c62828; +} + +.profit { + color: #2e7d32; +} + +.loss { + color: #c62828; +} diff --git a/frontend/src/components/AdminDashboard.jsx b/frontend/src/components/AdminDashboard.jsx new file mode 100644 index 0000000..9abf379 --- /dev/null +++ b/frontend/src/components/AdminDashboard.jsx @@ -0,0 +1,98 @@ +import React, { useEffect, useState } from 'react' +import { api } from '../services/api' +import './AdminDashboard.css' + +const AdminDashboard = () => { + const [data, setData] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + const loadData = async () => { + try { + setLoading(true) + const res = await api.getAdminDashboard() + setData(res) + setError(null) + } catch (err) { + setError(err.message) + } finally { + setLoading(false) + } + } + + useEffect(() => { + loadData() + const timer = setInterval(loadData, 30000) // 30秒刷新一次 + return () => clearInterval(timer) + }, []) + + if (loading && !data) return
加载中...
+ if (error) return
加载失败: {error}
+ if (!data) return null + + const { summary, accounts } = data + + return ( +
+
+

全局交易监控看板

+ +
+ +
+
+
总资产 (USDT)
+
{summary.total_assets_usdt.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
+
+
+
总盈亏 (USDT)
+
= 0 ? 'profit' : 'loss'}`}> + {summary.total_pnl_usdt > 0 ? '+' : ''}{summary.total_pnl_usdt.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} +
+
+
+
活跃账户数
+
{summary.active_accounts} / {summary.total_accounts}
+
+
+ +
+

账户列表

+
+ + + + + + + + + + + + + {accounts.map(acc => ( + + + + + + + + + ))} + +
ID名称状态总资产总盈亏持仓数
{acc.id}{acc.name} + + {acc.status === 'active' ? '运行中' : '停止'} + + {Number(acc.total_balance).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}= 0 ? 'profit' : 'loss'}> + {Number(acc.total_pnl) > 0 ? '+' : ''}{Number(acc.total_pnl).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} + {acc.open_positions}
+
+
+
+ ) +} + +export default AdminDashboard