This commit is contained in:
薇薇安 2026-01-22 21:49:42 +08:00
parent d051be3f65
commit 1c1580b344
3 changed files with 128 additions and 28 deletions

View File

@ -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 ? (
<option value="">{isAdmin && !effectiveUserId ? '请先选择用户' : '暂无账号'}</option>
) : (
options.map((a) => (
<option key={a.id} value={a.id}>
) : accountId === null ? (
<>
<option value="">请选择账号</option>
{options.map((a) => {
const isDisabled = String(a?.status || 'active') === 'disabled'
return (
<option key={a.id} value={a.id} disabled={isDisabled}>
#{a.id} {a.name || 'account'}
{String(a?.status || 'active') === 'disabled' ? '(已禁用)' : ''}
{isDisabled ? '(已禁用)' : ''}
</option>
))
)
})}
</>
) : (
options.map((a) => {
const isDisabled = String(a?.status || 'active') === 'disabled'
return (
<option key={a.id} value={a.id} disabled={isDisabled}>
#{a.id} {a.name || 'account'}
{isDisabled ? '(已禁用)' : ''}
</option>
)
})
)}
</select>
</div>

View File

@ -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
// 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,

View File

@ -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
}
}
},
},