auto_trade_sys/frontend/src/components/AdminUserManagement.jsx

106 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from 'react'
import { api } from '../services/api'
import { UserAccountGroup, CreateUserForm } from './AdminShared'
import './AdminDashboard.css'
export default function AdminUserManagement() {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const loadData = async () => {
try {
if (!data) setLoading(true)
const [usersRes, dashboardRes, servicesRes, accountsRes] = await Promise.all([
api.get('/admin/users/detailed').catch(() => ({ data: [] })),
api.getAdminDashboard(),
api.get('/system/trading/services').catch(() => ({ data: { services: [] } })),
api.get('/accounts').catch(() => ({ data: [] }))
])
const users = usersRes.data || []
const globalStats = dashboardRes
const services = servicesRes.data?.services || []
const allAccounts = accountsRes.data || []
const statsMap = {}
globalStats.accounts?.forEach(a => { statsMap[a.id] = a })
const serviceMap = {}
services.forEach(s => {
const match = s.program?.match(/auto_sys_acc(\d+)/)
if (match) serviceMap[match[1]] = s
})
const enrichedUsers = users.map(u => ({
...u,
accounts: (u.accounts || []).map(acc => ({
...acc,
total_balance: statsMap[acc.id]?.total_balance ?? 0,
total_pnl: statsMap[acc.id]?.total_pnl ?? 0,
open_positions: statsMap[acc.id]?.open_positions ?? 0,
serviceStatus: serviceMap[acc.id]
}))
}))
setData({ users: enrichedUsers, allAccounts })
setError(null)
} catch (err) {
setError(err?.message)
} finally {
setLoading(false)
}
}
const handleServiceAction = async (accountId, action) => {
if (action === 'refresh') { loadData(); return }
if (!window.confirm(`确定要${action === 'start' ? '启动' : '停止'}账号 #${accountId} 的交易服务吗?`)) return
try {
await api.post(`/accounts/${accountId}/service/${action}`)
setTimeout(loadData, 1000)
} catch (e) {
alert(`操作失败: ${e?.message || '未知错误'}`)
}
}
useEffect(() => {
loadData()
const t = setInterval(loadData, 30000)
return () => clearInterval(t)
}, [])
useEffect(() => {
const onUpdated = () => loadData()
window.addEventListener('ats:accounts:updated', onUpdated)
return () => window.removeEventListener('ats:accounts:updated', onUpdated)
}, [])
if (loading && !data) return <div className="loading">加载中...</div>
if (error) return <div className="error">加载失败: {error}</div>
if (!data) return null
const { users, allAccounts } = data
return (
<div className="admin-dashboard">
<div className="dashboard-header">
<h2>用户管理</h2>
<button className="refresh-btn" onClick={loadData}>刷新</button>
</div>
<div className="users-section">
<h3>用户管理 ({users.length})</h3>
<div className="users-section-grid">
<div className="create-user-block">
<h4 className="create-user-title"> 添加用户</h4>
<p className="create-user-desc">创建新用户后可在下方为其关联交易账号</p>
<CreateUserForm onSuccess={loadData} />
</div>
<div className="users-list">
{users.map(user => (
<UserAccountGroup
key={user.id}
user={user}
allAccounts={allAccounts}
onServiceAction={handleServiceAction}
/>
))}
</div>
</div>
</div>
</div>
)
}