a
This commit is contained in:
parent
11e6361235
commit
c28400e51e
|
|
@ -351,12 +351,16 @@ const ConfigPanel = ({ currentUser }) => {
|
|||
useEffect(() => {
|
||||
loadConfigs()
|
||||
checkFeasibility()
|
||||
loadSystemStatus()
|
||||
if (isAdmin) {
|
||||
loadSystemStatus()
|
||||
}
|
||||
loadBackendStatus()
|
||||
loadAccountTradingStatus()
|
||||
|
||||
const timer = setInterval(() => {
|
||||
loadSystemStatus()
|
||||
if (isAdmin) {
|
||||
loadSystemStatus()
|
||||
}
|
||||
loadBackendStatus()
|
||||
loadAccountTradingStatus()
|
||||
}, 3000)
|
||||
|
|
@ -525,21 +529,21 @@ const ConfigPanel = ({ currentUser }) => {
|
|||
preset_detected: detectCurrentPreset(),
|
||||
system_status: systemStatus
|
||||
? {
|
||||
running: !!systemStatus.running,
|
||||
pid: systemStatus.pid || null,
|
||||
program: systemStatus.program || null,
|
||||
state: systemStatus.state || null,
|
||||
}
|
||||
running: !!systemStatus.running,
|
||||
pid: systemStatus.pid || null,
|
||||
program: systemStatus.program || null,
|
||||
state: systemStatus.state || null,
|
||||
}
|
||||
: null,
|
||||
feasibility_check_summary: feasibilityCheck
|
||||
? {
|
||||
feasible: !!feasibilityCheck.feasible,
|
||||
account_balance: feasibilityCheck.account_balance ?? null,
|
||||
base_leverage: feasibilityCheck.base_leverage ?? feasibilityCheck.leverage ?? null,
|
||||
max_leverage: feasibilityCheck.max_leverage ?? null,
|
||||
use_dynamic_leverage: feasibilityCheck.use_dynamic_leverage ?? null,
|
||||
min_margin_usdt: feasibilityCheck.current_config?.min_margin_usdt ?? null,
|
||||
}
|
||||
feasible: !!feasibilityCheck.feasible,
|
||||
account_balance: feasibilityCheck.account_balance ?? null,
|
||||
base_leverage: feasibilityCheck.base_leverage ?? feasibilityCheck.leverage ?? null,
|
||||
max_leverage: feasibilityCheck.max_leverage ?? null,
|
||||
use_dynamic_leverage: feasibilityCheck.use_dynamic_leverage ?? null,
|
||||
min_margin_usdt: feasibilityCheck.current_config?.min_margin_usdt ?? null,
|
||||
}
|
||||
: null,
|
||||
configs: entries,
|
||||
}
|
||||
|
|
@ -795,343 +799,343 @@ const ConfigPanel = ({ currentUser }) => {
|
|||
|
||||
{/* 系统控制:清缓存 / 启停 / 重启(supervisor) */}
|
||||
{isAdmin ? (
|
||||
<div className="system-section">
|
||||
<div className="system-header">
|
||||
<h3>系统控制</h3>
|
||||
<div className="system-status">
|
||||
<span className={`system-status-badge ${systemStatus?.running ? 'running' : 'stopped'}`}>
|
||||
{systemStatus?.running ? '运行中' : '未运行'}
|
||||
<div className="system-section">
|
||||
<div className="system-header">
|
||||
<h3>系统控制</h3>
|
||||
<div className="system-status">
|
||||
<span className={`system-status-badge ${systemStatus?.running ? 'running' : 'stopped'}`}>
|
||||
{systemStatus?.running ? '运行中' : '未运行'}
|
||||
</span>
|
||||
{systemStatus?.pid ? <span className="system-status-meta">PID: {systemStatus.pid}</span> : null}
|
||||
{systemStatus?.program ? <span className="system-status-meta">程序: {systemStatus.program}</span> : null}
|
||||
{systemStatus?.meta?.requested_at ? <span className="system-status-meta">上次重启: {systemStatus.meta.requested_at}</span> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="system-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={handleClearCache}
|
||||
disabled={systemBusy}
|
||||
title="清理Redis配置缓存并从数据库回灌。切换API Key后建议先点这里,再重启交易系统。"
|
||||
>
|
||||
清除缓存
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={handleStopTrading}
|
||||
disabled={systemBusy || systemStatus?.running === false}
|
||||
title="通过 supervisorctl 停止交易系统"
|
||||
>
|
||||
停止
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={handleStartTrading}
|
||||
disabled={systemBusy || systemStatus?.running === true}
|
||||
title="通过 supervisorctl 启动交易系统"
|
||||
>
|
||||
启动
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
onClick={handleRestartTrading}
|
||||
disabled={systemBusy}
|
||||
title="通过 supervisorctl 重启交易系统(建议切换API Key后使用)"
|
||||
>
|
||||
重启交易系统
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
onClick={handleRestartBackend}
|
||||
disabled={systemBusy}
|
||||
title="通过 backend/restart.sh 重启后端(uvicorn)。重启期间接口会短暂不可用。"
|
||||
>
|
||||
重启后端服务
|
||||
</button>
|
||||
</div>
|
||||
<div className="system-status" style={{ marginTop: '10px' }}>
|
||||
<span className={`system-status-badge ${backendStatus?.running ? 'running' : 'stopped'}`}>
|
||||
后端 {backendStatus?.running ? '运行中' : '未知'}
|
||||
</span>
|
||||
{systemStatus?.pid ? <span className="system-status-meta">PID: {systemStatus.pid}</span> : null}
|
||||
{systemStatus?.program ? <span className="system-status-meta">程序: {systemStatus.program}</span> : null}
|
||||
{systemStatus?.meta?.requested_at ? <span className="system-status-meta">上次重启: {systemStatus.meta.requested_at}</span> : null}
|
||||
{backendStatus?.pid ? <span className="system-status-meta">PID: {backendStatus.pid}</span> : null}
|
||||
{backendStatus?.meta?.requested_at ? <span className="system-status-meta">上次重启: {backendStatus.meta.requested_at}</span> : null}
|
||||
</div>
|
||||
<div className="system-hint">
|
||||
建议流程:先更新配置里的 Key → 点击“清除缓存” → 点击“重启交易系统”,确保不再使用旧账号下单。
|
||||
</div>
|
||||
</div>
|
||||
<div className="system-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={handleClearCache}
|
||||
disabled={systemBusy}
|
||||
title="清理Redis配置缓存并从数据库回灌。切换API Key后建议先点这里,再重启交易系统。"
|
||||
>
|
||||
清除缓存
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={handleStopTrading}
|
||||
disabled={systemBusy || systemStatus?.running === false}
|
||||
title="通过 supervisorctl 停止交易系统"
|
||||
>
|
||||
停止
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={handleStartTrading}
|
||||
disabled={systemBusy || systemStatus?.running === true}
|
||||
title="通过 supervisorctl 启动交易系统"
|
||||
>
|
||||
启动
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
onClick={handleRestartTrading}
|
||||
disabled={systemBusy}
|
||||
title="通过 supervisorctl 重启交易系统(建议切换API Key后使用)"
|
||||
>
|
||||
重启交易系统
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
onClick={handleRestartBackend}
|
||||
disabled={systemBusy}
|
||||
title="通过 backend/restart.sh 重启后端(uvicorn)。重启期间接口会短暂不可用。"
|
||||
>
|
||||
重启后端服务
|
||||
</button>
|
||||
</div>
|
||||
<div className="system-status" style={{ marginTop: '10px' }}>
|
||||
<span className={`system-status-badge ${backendStatus?.running ? 'running' : 'stopped'}`}>
|
||||
后端 {backendStatus?.running ? '运行中' : '未知'}
|
||||
</span>
|
||||
{backendStatus?.pid ? <span className="system-status-meta">PID: {backendStatus.pid}</span> : null}
|
||||
{backendStatus?.meta?.requested_at ? <span className="system-status-meta">上次重启: {backendStatus.meta.requested_at}</span> : null}
|
||||
</div>
|
||||
<div className="system-hint">
|
||||
建议流程:先更新配置里的 Key → 点击“清除缓存” → 点击“重启交易系统”,确保不再使用旧账号下单。
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* 账号管理(超管) */}
|
||||
{isAdmin ? (
|
||||
<div className="accounts-admin-section">
|
||||
<div className="accounts-admin-header">
|
||||
<h3>账号管理(多账号)</h3>
|
||||
<div className="accounts-admin-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
try {
|
||||
await loadAccountsAdmin()
|
||||
setShowAccountsAdmin((v) => !v)
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
disabled={accountsBusy}
|
||||
title="创建/禁用账号;为每个账号配置独立 API KEY/SECRET;交易/配置/统计会按账号隔离"
|
||||
>
|
||||
{showAccountsAdmin ? '收起' : '管理账号'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
try {
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
setMessage('账号列表已刷新')
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
disabled={accountsBusy}
|
||||
>
|
||||
刷新
|
||||
</button>
|
||||
<div className="accounts-admin-section">
|
||||
<div className="accounts-admin-header">
|
||||
<h3>账号管理(多账号)</h3>
|
||||
<div className="accounts-admin-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
try {
|
||||
await loadAccountsAdmin()
|
||||
setShowAccountsAdmin((v) => !v)
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
disabled={accountsBusy}
|
||||
title="创建/禁用账号;为每个账号配置独立 API KEY/SECRET;交易/配置/统计会按账号隔离"
|
||||
>
|
||||
{showAccountsAdmin ? '收起' : '管理账号'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
try {
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
setMessage('账号列表已刷新')
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
disabled={accountsBusy}
|
||||
>
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showAccountsAdmin ? (
|
||||
<div className="accounts-admin-body">
|
||||
<div className="accounts-admin-card">
|
||||
<div className="accounts-admin-card-title">新增账号</div>
|
||||
<div className="accounts-form">
|
||||
<label>
|
||||
名称
|
||||
<input
|
||||
type="text"
|
||||
value={newAccount.name}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, name: e.target.value })}
|
||||
placeholder="例如:user_a"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
API KEY
|
||||
<input
|
||||
type="password"
|
||||
value={newAccount.api_key}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, api_key: e.target.value })}
|
||||
placeholder="可先留空,后续再填"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
API SECRET
|
||||
<input
|
||||
type="password"
|
||||
value={newAccount.api_secret}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, api_secret: e.target.value })}
|
||||
placeholder="可先留空,后续再填"
|
||||
/>
|
||||
</label>
|
||||
<label className="accounts-inline">
|
||||
<span>测试网</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!newAccount.use_testnet}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, use_testnet: e.target.checked })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
状态
|
||||
<select
|
||||
value={newAccount.status}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, status: e.target.value })}
|
||||
>
|
||||
<option value="active">启用</option>
|
||||
<option value="disabled">禁用</option>
|
||||
</select>
|
||||
</label>
|
||||
<div className="accounts-form-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
disabled={accountsBusy || !newAccount.name.trim()}
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
setMessage('')
|
||||
try {
|
||||
await api.createAccount(newAccount)
|
||||
setMessage('账号已创建')
|
||||
setNewAccount({ name: '', api_key: '', api_secret: '', use_testnet: false, status: 'active' })
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
} catch (e) {
|
||||
setMessage('创建账号失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
创建账号
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="accounts-admin-card">
|
||||
<div className="accounts-admin-card-title">账号列表</div>
|
||||
<div className="accounts-table">
|
||||
{(accountsAdmin || []).length ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>名称</th>
|
||||
<th>状态</th>
|
||||
<th>测试网</th>
|
||||
<th>API KEY</th>
|
||||
<th>SECRET</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{accountsAdmin.map((a) => (
|
||||
<tr key={a.id}>
|
||||
<td>#{a.id}</td>
|
||||
<td>{a.name || '-'}</td>
|
||||
<td>
|
||||
<span className={`acct-badge ${a.status === 'active' ? 'ok' : 'off'}`}>
|
||||
{a.status === 'active' ? '启用' : '禁用'}
|
||||
</span>
|
||||
</td>
|
||||
<td>{a.use_testnet ? '是' : '否'}</td>
|
||||
<td>{a.api_key_masked || (a.has_api_key ? '已配置' : '未配置')}</td>
|
||||
<td>{a.has_api_secret ? '已配置' : '未配置'}</td>
|
||||
<td className="accounts-actions-cell">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
disabled={accountsBusy || a.id === 1}
|
||||
title={a.id === 1 ? '默认账号建议保留' : '切换启用/禁用'}
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
setMessage('')
|
||||
try {
|
||||
const next = a.status === 'active' ? 'disabled' : 'active'
|
||||
await api.updateAccount(a.id, { status: next })
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
setMessage(`账号 #${a.id} 已${next === 'active' ? '启用' : '禁用'}`)
|
||||
} catch (e) {
|
||||
setMessage('更新账号失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{a.status === 'active' ? '禁用' : '启用'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
disabled={accountsBusy}
|
||||
onClick={() => {
|
||||
setCredEditId(a.id)
|
||||
setCredForm({ api_key: '', api_secret: '', use_testnet: !!a.use_testnet })
|
||||
}}
|
||||
>
|
||||
更新密钥
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="accounts-empty">暂无账号(默认账号 #1 会自动存在)</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{credEditId ? (
|
||||
{showAccountsAdmin ? (
|
||||
<div className="accounts-admin-body">
|
||||
<div className="accounts-admin-card">
|
||||
<div className="accounts-admin-card-title">更新账号 #{credEditId} 的密钥</div>
|
||||
<div className="accounts-admin-card-title">新增账号</div>
|
||||
<div className="accounts-form">
|
||||
<label>
|
||||
API KEY(留空=不改)
|
||||
名称
|
||||
<input
|
||||
type="password"
|
||||
value={credForm.api_key}
|
||||
onChange={(e) => setCredForm({ ...credForm, api_key: e.target.value })}
|
||||
type="text"
|
||||
value={newAccount.name}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, name: e.target.value })}
|
||||
placeholder="例如:user_a"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
API SECRET(留空=不改)
|
||||
API KEY
|
||||
<input
|
||||
type="password"
|
||||
value={credForm.api_secret}
|
||||
onChange={(e) => setCredForm({ ...credForm, api_secret: e.target.value })}
|
||||
value={newAccount.api_key}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, api_key: e.target.value })}
|
||||
placeholder="可先留空,后续再填"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
API SECRET
|
||||
<input
|
||||
type="password"
|
||||
value={newAccount.api_secret}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, api_secret: e.target.value })}
|
||||
placeholder="可先留空,后续再填"
|
||||
/>
|
||||
</label>
|
||||
<label className="accounts-inline">
|
||||
<span>测试网</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!credForm.use_testnet}
|
||||
onChange={(e) => setCredForm({ ...credForm, use_testnet: e.target.checked })}
|
||||
checked={!!newAccount.use_testnet}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, use_testnet: e.target.checked })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
状态
|
||||
<select
|
||||
value={newAccount.status}
|
||||
onChange={(e) => setNewAccount({ ...newAccount, status: e.target.value })}
|
||||
>
|
||||
<option value="active">启用</option>
|
||||
<option value="disabled">禁用</option>
|
||||
</select>
|
||||
</label>
|
||||
<div className="accounts-form-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
disabled={accountsBusy}
|
||||
onClick={() => setCredEditId(null)}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
disabled={accountsBusy}
|
||||
disabled={accountsBusy || !newAccount.name.trim()}
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
setMessage('')
|
||||
try {
|
||||
const payload = {}
|
||||
if (credForm.api_key) payload.api_key = credForm.api_key
|
||||
if (credForm.api_secret) payload.api_secret = credForm.api_secret
|
||||
payload.use_testnet = !!credForm.use_testnet
|
||||
await api.updateAccountCredentials(credEditId, payload)
|
||||
setMessage(`账号 #${credEditId} 密钥已更新(建议重启该账号交易进程)`)
|
||||
setCredEditId(null)
|
||||
await api.createAccount(newAccount)
|
||||
setMessage('账号已创建')
|
||||
setNewAccount({ name: '', api_key: '', api_secret: '', use_testnet: false, status: 'active' })
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
} catch (e) {
|
||||
setMessage('更新密钥失败: ' + (e?.message || '未知错误'))
|
||||
setMessage('创建账号失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
保存
|
||||
创建账号
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="accounts-admin-card">
|
||||
<div className="accounts-admin-card-title">账号列表</div>
|
||||
<div className="accounts-table">
|
||||
{(accountsAdmin || []).length ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>名称</th>
|
||||
<th>状态</th>
|
||||
<th>测试网</th>
|
||||
<th>API KEY</th>
|
||||
<th>SECRET</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{accountsAdmin.map((a) => (
|
||||
<tr key={a.id}>
|
||||
<td>#{a.id}</td>
|
||||
<td>{a.name || '-'}</td>
|
||||
<td>
|
||||
<span className={`acct-badge ${a.status === 'active' ? 'ok' : 'off'}`}>
|
||||
{a.status === 'active' ? '启用' : '禁用'}
|
||||
</span>
|
||||
</td>
|
||||
<td>{a.use_testnet ? '是' : '否'}</td>
|
||||
<td>{a.api_key_masked || (a.has_api_key ? '已配置' : '未配置')}</td>
|
||||
<td>{a.has_api_secret ? '已配置' : '未配置'}</td>
|
||||
<td className="accounts-actions-cell">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
disabled={accountsBusy || a.id === 1}
|
||||
title={a.id === 1 ? '默认账号建议保留' : '切换启用/禁用'}
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
setMessage('')
|
||||
try {
|
||||
const next = a.status === 'active' ? 'disabled' : 'active'
|
||||
await api.updateAccount(a.id, { status: next })
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
setMessage(`账号 #${a.id} 已${next === 'active' ? '启用' : '禁用'}`)
|
||||
} catch (e) {
|
||||
setMessage('更新账号失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{a.status === 'active' ? '禁用' : '启用'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
disabled={accountsBusy}
|
||||
onClick={() => {
|
||||
setCredEditId(a.id)
|
||||
setCredForm({ api_key: '', api_secret: '', use_testnet: !!a.use_testnet })
|
||||
}}
|
||||
>
|
||||
更新密钥
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="accounts-empty">暂无账号(默认账号 #1 会自动存在)</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{credEditId ? (
|
||||
<div className="accounts-admin-card">
|
||||
<div className="accounts-admin-card-title">更新账号 #{credEditId} 的密钥</div>
|
||||
<div className="accounts-form">
|
||||
<label>
|
||||
API KEY(留空=不改)
|
||||
<input
|
||||
type="password"
|
||||
value={credForm.api_key}
|
||||
onChange={(e) => setCredForm({ ...credForm, api_key: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
API SECRET(留空=不改)
|
||||
<input
|
||||
type="password"
|
||||
value={credForm.api_secret}
|
||||
onChange={(e) => setCredForm({ ...credForm, api_secret: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="accounts-inline">
|
||||
<span>测试网</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!!credForm.use_testnet}
|
||||
onChange={(e) => setCredForm({ ...credForm, use_testnet: e.target.checked })}
|
||||
/>
|
||||
</label>
|
||||
<div className="accounts-form-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn"
|
||||
disabled={accountsBusy}
|
||||
onClick={() => setCredEditId(null)}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="system-btn primary"
|
||||
disabled={accountsBusy}
|
||||
onClick={async () => {
|
||||
setAccountsBusy(true)
|
||||
setMessage('')
|
||||
try {
|
||||
const payload = {}
|
||||
if (credForm.api_key) payload.api_key = credForm.api_key
|
||||
if (credForm.api_secret) payload.api_secret = credForm.api_secret
|
||||
payload.use_testnet = !!credForm.use_testnet
|
||||
await api.updateAccountCredentials(credEditId, payload)
|
||||
setMessage(`账号 #${credEditId} 密钥已更新(建议重启该账号交易进程)`)
|
||||
setCredEditId(null)
|
||||
await loadAccountsAdmin()
|
||||
notifyAccountsUpdated()
|
||||
} catch (e) {
|
||||
setMessage('更新密钥失败: ' + (e?.message || '未知错误'))
|
||||
} finally {
|
||||
setAccountsBusy(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* 预设方案快速切换 */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user