diff --git a/frontend/src/components/AccountSelector.jsx b/frontend/src/components/AccountSelector.jsx
index f4c295e..1d0ca9d 100644
--- a/frontend/src/components/AccountSelector.jsx
+++ b/frontend/src/components/AccountSelector.jsx
@@ -107,11 +107,37 @@ const AccountSelector = ({ onChanged }) => {
const optionsKey = options.map((x) => x.id).join(',')
useEffect(() => {
- if (!options.length) return
- if (options.some((a) => a.id === accountId)) return
- const firstActive = options.find((a) => String(a?.status || 'active') === 'active') || options[0]
+ if (!options.length) {
+ // 如果没有账号,清空 accountId
+ if (accountId) {
+ dispatch(setAccountId(null))
+ }
+ return
+ }
+
+ // 检查当前选中的账号是否在新列表中且是 active 的
+ const currentAccount = options.find((a) => a.id === accountId)
+ if (currentAccount) {
+ // 如果当前账号是 disabled,需要切换到 active 账号
+ if (String(currentAccount?.status || 'active') === 'disabled') {
+ const firstActive = options.find((a) => String(a?.status || 'active') === 'active')
+ if (firstActive) {
+ dispatch(setAccountId(parseInt(String(firstActive.id || ''), 10)))
+ } else {
+ // 如果没有 active 账号,清空 accountId
+ dispatch(setAccountId(null))
+ }
+ }
+ return
+ }
+
+ // 如果当前账号不在新列表中,选择第一个 active 账号
+ const firstActive = options.find((a) => String(a?.status || 'active') === 'active')
if (firstActive) {
dispatch(setAccountId(parseInt(String(firstActive.id || ''), 10)))
+ } else {
+ // 如果没有 active 账号,清空 accountId
+ dispatch(setAccountId(null))
}
}, [optionsKey, accountId, dispatch])
@@ -123,20 +149,46 @@ const AccountSelector = ({ onChanged }) => {
value={accountId || ''}
onChange={(e) => {
const v = parseInt(e.target.value, 10)
- dispatch(setAccountId(Number.isFinite(v) && v > 0 ? v : 1))
+ if (Number.isFinite(v) && v > 0) {
+ // 检查选中的账号是否是 disabled
+ const selectedAccount = options.find((a) => a.id === v)
+ if (selectedAccount && String(selectedAccount?.status || 'active') === 'disabled') {
+ // 如果选中的是 disabled 账号,不允许选择,保持当前值
+ return
+ }
+ dispatch(setAccountId(v))
+ } else {
+ dispatch(setAccountId(null))
+ }
}}
title="切换账号后:配置/持仓/交易记录/统计会按账号隔离;推荐仍是全局"
disabled={isAdmin && !effectiveUserId}
>
{options.length === 0 ? (
+ ) : accountId === null ? (
+ <>
+
+ {options.map((a) => {
+ const isDisabled = String(a?.status || 'active') === 'disabled'
+ return (
+
+ )
+ })}
+ >
) : (
- options.map((a) => (
-
- ))
+ options.map((a) => {
+ const isDisabled = String(a?.status || 'active') === 'disabled'
+ return (
+
+ )
+ })
)}
diff --git a/frontend/src/components/GlobalConfig.jsx b/frontend/src/components/GlobalConfig.jsx
index dcb24da..9d33657 100644
--- a/frontend/src/components/GlobalConfig.jsx
+++ b/frontend/src/components/GlobalConfig.jsx
@@ -361,18 +361,30 @@ const GlobalConfig = () => {
const loadConfigs = async () => {
try {
- // 管理员全局配置:使用全局策略账号的配置,不依赖当前 account
- if (isAdmin && configMeta?.global_strategy_account_id) {
- const globalAccountId = parseInt(String(configMeta?.global_strategy_account_id || '1'), 10) || 1
+ // 管理员全局配置:始终使用全局策略账号的配置,不依赖当前 account
+ // 即使 configMeta 还没加载完成,也使用默认值 1
+ if (isAdmin) {
+ const globalAccountId = configMeta?.global_strategy_account_id
+ ? parseInt(String(configMeta.global_strategy_account_id || '1'), 10) || 1
+ : 1 // 如果 configMeta 还没加载,使用默认值 1
const data = await api.getGlobalConfigs(globalAccountId)
setConfigs(data)
} else {
- // 非管理员或未加载 configMeta 时,使用默认方式
+ // 非管理员:使用默认方式(会受当前 account 影响,但非管理员不应该访问这个页面)
const data = await api.getConfigs()
setConfigs(data)
}
} catch (error) {
console.error('Failed to load configs:', error)
+ // 如果加载失败,尝试使用默认值 1 重试一次
+ if (isAdmin) {
+ try {
+ const data = await api.getGlobalConfigs(1)
+ setConfigs(data)
+ } catch (retryError) {
+ console.error('Retry load configs with accountId=1 failed:', retryError)
+ }
+ }
}
}
@@ -440,11 +452,20 @@ const GlobalConfig = () => {
loadAccounts()
// 只有管理员才加载配置和系统状态
if (isAdmin) {
- // 先加载 configMeta,再加载 configs(因为 loadConfigs 需要 global_strategy_account_id)
+ // 立即加载 configs(不等待 configMeta,使用默认值 1)
+ // 同时加载 configMeta,加载完成后会触发重新加载 configs(如果 global_strategy_account_id 不是 1)
+ loadConfigs().catch(() => {})
loadConfigMeta()
.then(() => {
- // configMeta 加载完成后,再加载 configs
- loadConfigs().catch(() => {})
+ // configMeta 加载完成后,如果 global_strategy_account_id 不是 1,重新加载 configs
+ // 这样可以确保使用正确的全局策略账号ID
+ const globalAccountId = configMeta?.global_strategy_account_id
+ ? parseInt(String(configMeta.global_strategy_account_id || '1'), 10) || 1
+ : 1
+ // 如果 globalAccountId 不是 1,说明之前加载的是默认值,需要重新加载
+ if (globalAccountId !== 1) {
+ loadConfigs().catch(() => {})
+ }
})
.catch(() => {}) // 静默失败
loadSystemStatus().catch(() => {}) // 静默失败
@@ -608,12 +629,15 @@ const GlobalConfig = () => {
}
}).filter(Boolean)
- // 管理员全局配置:应用到全局策略账号
+ // 管理员全局配置:始终使用全局策略账号,即使 configMeta 还没加载也使用默认值 1
let response
- if (isAdmin && configMeta?.global_strategy_account_id) {
- const globalAccountId = parseInt(String(configMeta?.global_strategy_account_id || '1'), 10) || 1
+ if (isAdmin) {
+ const globalAccountId = configMeta?.global_strategy_account_id
+ ? parseInt(String(configMeta.global_strategy_account_id || '1'), 10) || 1
+ : 1
response = await api.updateGlobalConfigsBatch(configItems, globalAccountId)
} else {
+ // 非管理员不应该访问这个页面,但为了安全还是处理一下
response = await api.updateConfigsBatch(configItems)
}
setMessage(response.message || `已应用${preset.name}`)
@@ -654,10 +678,12 @@ const GlobalConfig = () => {
}
const buildConfigSnapshot = async (includeSecrets) => {
- // 管理员全局配置:使用全局策略账号的配置
+ // 管理员全局配置:始终使用全局策略账号的配置,即使 configMeta 还没加载也使用默认值 1
let data
- if (isAdmin && configMeta?.global_strategy_account_id) {
- const globalAccountId = parseInt(String(configMeta?.global_strategy_account_id || '1'), 10) || 1
+ if (isAdmin) {
+ const globalAccountId = configMeta?.global_strategy_account_id
+ ? parseInt(String(configMeta.global_strategy_account_id || '1'), 10) || 1
+ : 1
data = await api.getGlobalConfigs(globalAccountId)
} else {
data = await api.getConfigs()
@@ -1169,10 +1195,16 @@ const GlobalConfig = () => {
try {
setSaving(true)
setMessage('')
- // 检查 configMeta 是否存在,如果不存在则使用默认值 1
- const globalAccountId = configMeta?.global_strategy_account_id
- ? parseInt(String(configMeta?.global_strategy_account_id || '1'), 10) || 1
- : 1
+ // 管理员始终使用全局策略账号,即使 configMeta 还没加载也使用默认值 1
+ const globalAccountId = isAdmin
+ ? (configMeta?.global_strategy_account_id
+ ? parseInt(String(configMeta.global_strategy_account_id || '1'), 10) || 1
+ : 1)
+ : null
+ if (!isAdmin || !globalAccountId) {
+ setMessage('只有管理员可以修改全局配置')
+ return
+ }
await api.updateGlobalConfigsBatch([{
key,
value,
diff --git a/frontend/src/store/appSlice.js b/frontend/src/store/appSlice.js
index da14209..0a9a483 100644
--- a/frontend/src/store/appSlice.js
+++ b/frontend/src/store/appSlice.js
@@ -74,6 +74,13 @@ const appSlice = createSlice({
} catch (e) {
// ignore
}
+ } else {
+ // 如果 accountId 为 null,清空 localStorage
+ try {
+ localStorage.removeItem(ACCOUNT_ID_STORAGE_KEY)
+ } catch (e) {
+ // ignore
+ }
}
},
setAccounts: (state, action) => {
@@ -97,8 +104,9 @@ const appSlice = createSlice({
// 等待账号列表加载完成后再切换
},
// 切换用户后,账号列表加载完成,自动选择第一个active账号
+ // 如果没有 active 账号,不改变当前 accountId(保持为 null 或之前的值)
selectFirstActiveAccount: (state) => {
- const firstActive = state.accounts.find((a) => String(a?.status || 'active') === 'active') || state.accounts[0]
+ const firstActive = state.accounts.find((a) => String(a?.status || 'active') === 'active')
if (firstActive) {
const nextAccountId = parseInt(String(firstActive.id || ''), 10)
if (Number.isFinite(nextAccountId) && nextAccountId > 0) {
@@ -115,6 +123,14 @@ const appSlice = createSlice({
// ignore
}
}
+ } else {
+ // 如果没有 active 账号,清空 accountId
+ state.accountId = null
+ try {
+ localStorage.removeItem(ACCOUNT_ID_STORAGE_KEY)
+ } catch (e) {
+ // ignore
+ }
}
},
},