129 lines
4.3 KiB
JavaScript
129 lines
4.3 KiB
JavaScript
import React, { useEffect, useState } from 'react'
|
||
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'
|
||
import ConfigPanel from './components/ConfigPanel'
|
||
import ConfigGuide from './components/ConfigGuide'
|
||
import TradeList from './components/TradeList'
|
||
import StatsDashboard from './components/StatsDashboard'
|
||
import Recommendations from './components/Recommendations'
|
||
import LogMonitor from './components/LogMonitor'
|
||
import AccountSelector from './components/AccountSelector'
|
||
import Login from './components/Login'
|
||
import { api, clearAuthToken, clearCurrentAccountId, setCurrentAccountId, getCurrentAccountId } from './services/api'
|
||
import './App.css'
|
||
|
||
function App() {
|
||
const [me, setMe] = useState(null)
|
||
const [checking, setChecking] = useState(true)
|
||
|
||
const refreshMe = async () => {
|
||
try {
|
||
const u = await api.me()
|
||
setMe(u)
|
||
|
||
// 登录后默认选择账号(避免 admin/用户切换时沿用旧 accountId)
|
||
try {
|
||
const list = await api.getAccounts()
|
||
const accounts = Array.isArray(list) ? list : []
|
||
const active = accounts.filter((a) => String(a?.status || 'active') === 'active')
|
||
const isAdmin = (u?.role || '') === 'admin'
|
||
|
||
let target = null
|
||
if (isAdmin) {
|
||
// 管理员:默认选第一个 active 账号(避免沿用旧值导致“看起来还是上个账号”)
|
||
target = active[0]?.id || accounts[0]?.id
|
||
} else {
|
||
// 普通用户:优先选“自己的账号”(且必须 active),否则选第一个 active
|
||
const uid = parseInt(String(u?.id || ''), 10)
|
||
const match = active.find((a) => parseInt(String(a?.id || ''), 10) === uid)
|
||
target = match?.id || active[0]?.id || accounts[0]?.id
|
||
}
|
||
|
||
if (target) {
|
||
const cur = getCurrentAccountId()
|
||
const next = parseInt(String(target), 10)
|
||
if (Number.isFinite(next) && next > 0 && cur !== next) setCurrentAccountId(next)
|
||
}
|
||
} catch (e) {
|
||
// ignore
|
||
}
|
||
} catch (e) {
|
||
setMe(null)
|
||
} finally {
|
||
setChecking(false)
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
refreshMe()
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [])
|
||
|
||
const isAdmin = (me?.role || '') === 'admin'
|
||
|
||
if (checking) {
|
||
return (
|
||
<div className="app">
|
||
<div className="main-content" style={{ padding: '24px' }}>
|
||
正在初始化...
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
if (!me) {
|
||
return <Login onLoggedIn={refreshMe} />
|
||
}
|
||
|
||
return (
|
||
<Router>
|
||
<div className="app">
|
||
<nav className="navbar">
|
||
<div className="nav-container">
|
||
<div className="nav-left">
|
||
<h1 className="nav-title">自动交易系统</h1>
|
||
<AccountSelector />
|
||
</div>
|
||
<div className="nav-links">
|
||
<Link to="/">仪表板</Link>
|
||
<Link to="/recommendations">交易推荐</Link>
|
||
<Link to="/config">配置</Link>
|
||
<Link to="/trades">交易记录</Link>
|
||
{isAdmin ? <Link to="/logs">日志监控</Link> : null}
|
||
</div>
|
||
<div className="nav-user">
|
||
<span className="nav-user-name">
|
||
{me?.username ? me.username : 'user'}{isAdmin ? '(管理员)' : ''}
|
||
</span>
|
||
<button
|
||
type="button"
|
||
className="nav-logout"
|
||
onClick={() => {
|
||
clearAuthToken()
|
||
clearCurrentAccountId()
|
||
setMe(null)
|
||
setChecking(false)
|
||
}}
|
||
>
|
||
退出
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<main className="main-content">
|
||
<Routes>
|
||
<Route path="/" element={<StatsDashboard />} />
|
||
<Route path="/recommendations" element={<Recommendations />} />
|
||
<Route path="/config" element={<ConfigPanel currentUser={me} />} />
|
||
<Route path="/config/guide" element={<ConfigGuide />} />
|
||
<Route path="/trades" element={<TradeList />} />
|
||
<Route path="/logs" element={isAdmin ? <LogMonitor /> : <div style={{ padding: '24px' }}>无权限</div>} />
|
||
</Routes>
|
||
</main>
|
||
</div>
|
||
</Router>
|
||
)
|
||
}
|
||
|
||
export default App
|