Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
eff51857
authored
Mar 20, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: show codex account info tag and highlight plan type in usage modal
parent
ed6ff0f2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
173 additions
and
38 deletions
+173
-38
web/src/components/table/channels/ChannelsColumnDefs.jsx
+12
-7
web/src/components/table/channels/modals/CodexUsageModal.jsx
+161
-31
No files found.
web/src/components/table/channels/ChannelsColumnDefs.jsx
View file @
eff51857
...
...
@@ -538,19 +538,24 @@ export const getChannelsColumns = ({
</
Tooltip
>
<
Tooltip
content=
{
t
(
'剩余额度'
)
+
': '
+
renderQuotaWithAmount
(
record
.
balance
)
+
t
(
',点击更新'
)
record
.
type
===
57
?
t
(
'查看 Codex 帐号信息与用量'
)
:
t
(
'剩余额度'
)
+
': '
+
renderQuotaWithAmount
(
record
.
balance
)
+
t
(
',点击更新'
)
}
>
<
Tag
color=
'white'
type=
'ghost'
color=
{
record
.
type
===
57
?
'light-blue'
:
'white'
}
type=
{
record
.
type
===
57
?
'light'
:
'ghost'
}
shape=
'circle'
className=
{
record
.
type
===
57
?
'cursor-pointer'
:
''
}
onClick=
{
()
=>
updateChannelBalance
(
record
)
}
>
{
renderQuotaWithAmount
(
record
.
balance
)
}
{
record
.
type
===
57
?
t
(
'帐号信息'
)
:
renderQuotaWithAmount
(
record
.
balance
)
}
</
Tag
>
</
Tooltip
>
</
Space
>
...
...
web/src/components/table/channels/modals/CodexUsageModal.jsx
View file @
eff51857
...
...
@@ -22,9 +22,9 @@ import {
Modal
,
Button
,
Progress
,
Tag
,
Typography
,
Spin
,
Tag
,
}
from
'@douyinfe/semi-ui'
;
import
{
API
,
showError
}
from
'../../../../helpers'
;
...
...
@@ -128,6 +128,91 @@ const formatUnixSeconds = (unixSeconds) => {
}
};
const
getDisplayText
=
(
value
)
=>
{
if
(
value
==
null
)
return
''
;
return
String
(
value
).
trim
();
};
const
formatAccountTypeLabel
=
(
value
,
t
)
=>
{
const
tt
=
typeof
t
===
'function'
?
t
:
(
v
)
=>
v
;
const
normalized
=
normalizePlanType
(
value
);
switch
(
normalized
)
{
case
'free'
:
return
'Free'
;
case
'plus'
:
return
'Plus'
;
case
'pro'
:
return
'Pro'
;
case
'team'
:
return
'Team'
;
case
'enterprise'
:
return
'Enterprise'
;
default
:
return
getDisplayText
(
value
)
||
tt
(
'未识别'
);
}
};
const
getAccountTypeTagColor
=
(
value
)
=>
{
const
normalized
=
normalizePlanType
(
value
);
switch
(
normalized
)
{
case
'enterprise'
:
return
'green'
;
case
'team'
:
return
'cyan'
;
case
'pro'
:
return
'blue'
;
case
'plus'
:
return
'violet'
;
case
'free'
:
return
'amber'
;
default
:
return
'grey'
;
}
};
const
resolveUsageStatusTag
=
(
t
,
rateLimit
)
=>
{
const
tt
=
typeof
t
===
'function'
?
t
:
(
v
)
=>
v
;
if
(
!
rateLimit
||
Object
.
keys
(
rateLimit
).
length
===
0
)
{
return
<
Tag
color=
'grey'
>
{
tt
(
'待确认'
)
}
</
Tag
>;
}
if
(
rateLimit
?.
allowed
&&
!
rateLimit
?.
limit_reached
)
{
return
<
Tag
color=
'green'
>
{
tt
(
'可用'
)
}
</
Tag
>;
}
return
<
Tag
color=
'red'
>
{
tt
(
'受限'
)
}
</
Tag
>;
};
const
AccountInfoCard
=
({
t
,
label
,
value
,
onCopy
,
monospace
=
false
})
=>
{
const
tt
=
typeof
t
===
'function'
?
t
:
(
v
)
=>
v
;
const
text
=
getDisplayText
(
value
);
const
hasValue
=
text
!==
''
;
return
(
<
div
className=
'rounded-xl border border-semi-color-border bg-semi-color-fill-0 p-3'
>
<
div
className=
'text-[11px] uppercase tracking-[0.18em] text-semi-color-text-2'
>
{
label
}
</
div
>
<
div
className=
'mt-2 flex items-start justify-between gap-3'
>
<
div
className=
{
`min-w-0 flex-1 break-all text-sm text-semi-color-text-0 ${
monospace ? 'font-mono' : 'font-medium'
}`
}
>
{
hasValue
?
text
:
'-'
}
</
div
>
<
Button
size=
'small'
type=
'tertiary'
theme=
'borderless'
disabled=
{
!
hasValue
}
onClick=
{
()
=>
onCopy
?.(
text
)
}
>
{
tt
(
'复制'
)
}
</
Button
>
</
div
>
</
div
>
);
};
const
RateLimitWindowCard
=
({
t
,
title
,
windowData
})
=>
{
const
tt
=
typeof
t
===
'function'
?
t
:
(
v
)
=>
v
;
const
hasWindowData
=
...
...
@@ -184,47 +269,92 @@ const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => {
const
data
=
payload
?.
data
??
null
;
const
rateLimit
=
data
?.
rate_limit
??
{};
const
{
fiveHourWindow
,
weeklyWindow
}
=
resolveRateLimitWindows
(
data
);
const
allowed
=
!!
rateLimit
?.
allowed
;
const
limitReached
=
!!
rateLimit
?.
limit_reached
;
const
upstreamStatus
=
payload
?.
upstream_status
;
const
statusTag
=
allowed
&&
!
limitReached
?
(
<
Tag
color=
'green'
>
{
tt
(
'可用'
)
}
</
Tag
>
)
:
(
<
Tag
color=
'red'
>
{
tt
(
'受限'
)
}
</
Tag
>
);
const
accountType
=
data
?.
plan_type
??
rateLimit
?.
plan_type
;
const
accountTypeLabel
=
formatAccountTypeLabel
(
accountType
,
tt
);
const
accountTypeTagColor
=
getAccountTypeTagColor
(
accountType
);
const
statusTag
=
resolveUsageStatusTag
(
tt
,
rateLimit
);
const
userId
=
data
?.
user_id
;
const
email
=
data
?.
email
;
const
accountId
=
data
?.
account_id
;
const
errorMessage
=
payload
?.
success
===
false
?
getDisplayText
(
payload
?.
message
)
||
tt
(
'获取用量失败'
)
:
''
;
const
rawText
=
typeof
data
===
'string'
?
data
:
JSON
.
stringify
(
data
??
payload
,
null
,
2
);
return
(
<
div
className=
'flex flex-col gap-3'
>
<
div
className=
'flex flex-wrap items-center justify-between gap-2'
>
<
Text
type=
'tertiary'
size=
'small'
>
<
div
className=
'flex flex-col gap-4'
>
{
errorMessage
&&
(
<
div
className=
'rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700'
>
{
errorMessage
}
</
div
>
)
}
<
div
className=
'overflow-hidden rounded-2xl border border-semi-color-border bg-semi-color-bg-0 shadow-sm'
>
<
div
className=
'bg-slate-900 px-4 py-4 text-white'
>
<
div
className=
'flex flex-wrap items-start justify-between gap-3'
>
<
div
className=
'min-w-0'
>
<
div
className=
'text-[11px] uppercase tracking-[0.22em] text-white/60'
>
{
tt
(
'Codex 帐号'
)
}
</
div
>
<
div
className=
'mt-2 flex flex-wrap items-center gap-2'
>
<
Tag
color=
{
accountTypeTagColor
}
type=
'light'
shape=
'circle'
size=
'large'
className=
'font-semibold'
>
{
accountTypeLabel
}
</
Tag
>
{
statusTag
}
<
div
className=
'inline-flex items-center rounded-full border border-white/10 bg-white/10 px-3 py-1 text-xs text-white/80'
>
{
tt
(
'上游状态码:'
)
}
{
upstreamStatus
??
'-'
}
</
div
>
</
div
>
</
div
>
<
Button
type=
'primary'
theme=
'solid'
onClick=
{
onRefresh
}
>
{
tt
(
'刷新'
)
}
</
Button
>
</
div
>
</
div
>
<
div
className=
'grid grid-cols-1 gap-3 p-4 md:grid-cols-3'
>
<
AccountInfoCard
t=
{
tt
}
label=
'User ID'
value=
{
userId
}
onCopy=
{
onCopy
}
monospace=
{
true
}
/>
<
AccountInfoCard
t=
{
tt
}
label=
{
tt
(
'邮箱'
)
}
value=
{
email
}
onCopy=
{
onCopy
}
/>
<
AccountInfoCard
t=
{
tt
}
label=
'Account ID'
value=
{
accountId
}
onCopy=
{
onCopy
}
monospace=
{
true
}
/>
</
div
>
<
div
className=
'border-t border-semi-color-border px-4 py-3 text-xs text-semi-color-text-2'
>
{
tt
(
'渠道:'
)
}
{
record
?.
name
||
'-'
}
(
{
tt
(
'编号:'
)
}
{
record
?.
id
||
'-'
}
)
</
Text
>
<
div
className=
'flex items-center gap-2'
>
{
statusTag
}
<
Button
size=
'small'
type=
'tertiary'
theme=
'borderless'
onClick=
{
onRefresh
}
>
{
tt
(
'刷新'
)
}
</
Button
>
</
div
>
</
div
>
<
div
className=
'flex flex-wrap items-center justify-between gap-2'
>
<
Text
type=
'tertiary'
size=
'small'
>
{
tt
(
'上游状态码:'
)
}
{
upstreamStatus
??
'-'
}
</
Text
>
<
div
>
<
div
className=
'mb-2'
>
<
div
className=
'text-sm font-semibold text-semi-color-text-0'
>
{
tt
(
'额度窗口'
)
}
</
div
>
<
Text
type=
'tertiary'
size=
'small'
>
{
tt
(
'用于观察当前帐号在 Codex 上游的限额使用情况'
)
}
</
Text
>
</
div
>
</
div
>
<
div
className=
'grid grid-cols-1 gap-3 md:grid-cols-2'
>
...
...
@@ -351,7 +481,7 @@ export const openCodexUsageModal = ({ t, record, payload, onCopy }) => {
const
tt
=
typeof
t
===
'function'
?
t
:
(
v
)
=>
v
;
Modal
.
info
({
title
:
tt
(
'Codex 用量'
),
title
:
tt
(
'Codex
帐号与
用量'
),
centered
:
true
,
width
:
900
,
style
:
{
maxWidth
:
'95vw'
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment