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
c17439a1
authored
Jul 05, 2025
by
CaIon
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/alpha' into alpha
parents
95775a5a
8bb1522d
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
166 additions
and
21 deletions
+166
-21
web/src/components/settings/ChannelSelectorModal.js
+0
-1
web/src/components/table/UsersTable.js
+1
-1
web/src/i18n/locales/en.json
+6
-2
web/src/pages/Detail/index.js
+148
-3
web/src/pages/Home/index.js
+1
-4
web/src/pages/Setting/Ratio/UpstreamRatioSync.js
+4
-5
web/src/pages/User/EditUser.js
+6
-5
No files found.
web/src/components/settings/ChannelSelectorModal.js
View file @
c17439a1
...
...
@@ -197,7 +197,6 @@ const ChannelSelectorModal = forwardRef(({
value
=
{
searchText
}
onChange
=
{
setSearchText
}
showClear
className
=
"!rounded-full"
/>
<
Table
...
...
web/src/components/table/UsersTable.js
View file @
c17439a1
...
...
@@ -119,7 +119,7 @@ const UsersTable = () => {
<
Tooltip
content
=
{
remark
}
position
=
"top"
showArrow
>
<
Tag
color
=
'white'
size
=
'large'
shape
=
'circle'
className
=
"!text-xs"
>
<
div
className
=
"flex items-center gap-1"
>
<
div
className
=
"w-2 h-2 flex-shrink-0"
style
=
{{
backgroundColor
:
'#10b981'
}}
/
>
<
div
className
=
"w-2 h-2 flex-shrink-0
rounded-full
"
style
=
{{
backgroundColor
:
'#10b981'
}}
/
>
{
displayRemark
}
<
/div
>
<
/Tag
>
...
...
web/src/i18n/locales/en.json
View file @
c17439a1
...
...
@@ -876,7 +876,7 @@
"加载token失败"
:
"Failed to load token"
,
"配置聊天"
:
"Configure chat"
,
"模型消耗分布"
:
"Model consumption distribution"
,
"模型调用次数占比"
:
"
Proportion of model calls
"
,
"模型调用次数占比"
:
"
Model call ratio
"
,
"用户消耗分布"
:
"User consumption distribution"
,
"时间粒度"
:
"Time granularity"
,
"天"
:
"day"
,
...
...
@@ -1119,6 +1119,10 @@
"平均TPM"
:
"Average TPM"
,
"消耗分布"
:
"Consumption distribution"
,
"调用次数分布"
:
"Models call distribution"
,
"消耗趋势"
:
"Consumption trend"
,
"模型消耗趋势"
:
"Model consumption trend"
,
"调用次数排行"
:
"Models call ranking"
,
"模型调用次数排行"
:
"Model call ranking"
,
"添加渠道"
:
"Add channel"
,
"测试所有通道"
:
"Test all channels"
,
"删除禁用通道"
:
"Delete disabled channels"
,
...
...
@@ -1199,7 +1203,7 @@
"添加用户"
:
"Add user"
,
"角色"
:
"Role"
,
"已绑定的 Telegram 账户"
:
"Bound Telegram account"
,
"新额度
"
:
"New quota
"
,
"新额度
:"
:
"New quota:
"
,
"需要添加的额度(支持负数)"
:
"Need to add quota (supports negative numbers)"
,
"此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改"
:
"Read-only, user's personal settings, and cannot be modified directly"
,
"请输入新的密码,最短 8 位"
:
"Please enter a new password, at least 8 characterss"
,
...
...
web/src/pages/Detail/index.js
View file @
c17439a1
...
...
@@ -366,6 +366,86 @@ const Detail = (props) => {
},
});
// 模型消耗趋势折线图
const
[
spec_model_line
,
setSpecModelLine
]
=
useState
({
type
:
'line'
,
data
:
[
{
id
:
'lineData'
,
values
:
[],
},
],
xField
:
'Time'
,
yField
:
'Count'
,
seriesField
:
'Model'
,
legends
:
{
visible
:
true
,
selectMode
:
'single'
,
},
title
:
{
visible
:
true
,
text
:
t
(
'模型消耗趋势'
),
subtext
:
''
,
},
tooltip
:
{
mark
:
{
content
:
[
{
key
:
(
datum
)
=>
datum
[
'Model'
],
value
:
(
datum
)
=>
renderNumber
(
datum
[
'Count'
]),
},
],
},
},
color
:
{
specified
:
modelColorMap
,
},
});
// 模型调用次数排行柱状图
const
[
spec_rank_bar
,
setSpecRankBar
]
=
useState
({
type
:
'bar'
,
data
:
[
{
id
:
'rankData'
,
values
:
[],
},
],
xField
:
'Model'
,
yField
:
'Count'
,
seriesField
:
'Model'
,
legends
:
{
visible
:
true
,
selectMode
:
'single'
,
},
title
:
{
visible
:
true
,
text
:
t
(
'模型调用次数排行'
),
subtext
:
''
,
},
bar
:
{
state
:
{
hover
:
{
stroke
:
'#000'
,
lineWidth
:
1
,
},
},
},
tooltip
:
{
mark
:
{
content
:
[
{
key
:
(
datum
)
=>
datum
[
'Model'
],
value
:
(
datum
)
=>
renderNumber
(
datum
[
'Count'
]),
},
],
},
},
color
:
{
specified
:
modelColorMap
,
},
});
// ========== Hooks - Memoized Values ==========
const
performanceMetrics
=
useMemo
(()
=>
{
const
timeDiff
=
(
Date
.
parse
(
end_timestamp
)
-
Date
.
parse
(
start_timestamp
))
/
60000
;
...
...
@@ -853,6 +933,46 @@ const Detail = (props) => {
'barData'
);
// ===== 模型调用次数折线图 =====
let
modelLineData
=
[];
chartTimePoints
.
forEach
((
time
)
=>
{
const
timeData
=
Array
.
from
(
uniqueModels
).
map
((
model
)
=>
{
const
key
=
`
${
time
}
-
${
model
}
`
;
const
aggregated
=
aggregatedData
.
get
(
key
);
return
{
Time
:
time
,
Model
:
model
,
Count
:
aggregated
?.
count
||
0
,
};
});
modelLineData
.
push
(...
timeData
);
});
modelLineData
.
sort
((
a
,
b
)
=>
a
.
Time
.
localeCompare
(
b
.
Time
));
// ===== 模型调用次数排行柱状图 =====
const
rankData
=
Array
.
from
(
modelTotals
)
.
map
(([
model
,
count
])
=>
({
Model
:
model
,
Count
:
count
,
}))
.
sort
((
a
,
b
)
=>
b
.
Count
-
a
.
Count
);
updateChartSpec
(
setSpecModelLine
,
modelLineData
,
`
${
t
(
'总计'
)}
:
${
renderNumber
(
totalTimes
)}
`
,
newModelColors
,
'lineData'
);
updateChartSpec
(
setSpecRankBar
,
rankData
,
`
${
t
(
'总计'
)}
:
${
renderNumber
(
totalTimes
)}
`
,
newModelColors
,
'rankData'
);
setPieData
(
newPieData
);
setLineData
(
newLineData
);
setConsumeQuota
(
totalQuota
);
...
...
@@ -1124,26 +1244,51 @@ const Detail = (props) => {
}
itemKey
=
"1"
/>
<
TabPane
tab
=
{
<
span
>
<
IconPulse
/>
{
t
(
'消耗趋势'
)}
<
/span
>
}
itemKey
=
"2"
/>
<
TabPane
tab
=
{
<
span
>
<
IconPieChart2Stroked
/>
{
t
(
'调用次数分布'
)}
<
/span
>
}
itemKey
=
"2"
/>
}
itemKey
=
"3"
/>
<
TabPane
tab
=
{
<
span
>
<
IconHistogram
/>
{
t
(
'调用次数排行'
)}
<
/span
>
}
itemKey
=
"4"
/>
<
/Tabs
>
<
/div
>
}
>
<
div
style
=
{{
height
:
400
}}
>
{
activeChartTab
===
'1'
?
(
{
activeChartTab
===
'1'
&&
(
<
VChart
spec
=
{
spec_line
}
option
=
{
CHART_CONFIG
}
/
>
)
:
(
)}
{
activeChartTab
===
'2'
&&
(
<
VChart
spec
=
{
spec_model_line
}
option
=
{
CHART_CONFIG
}
/
>
)}
{
activeChartTab
===
'3'
&&
(
<
VChart
spec
=
{
spec_pie
}
option
=
{
CHART_CONFIG
}
/
>
)}
{
activeChartTab
===
'4'
&&
(
<
VChart
spec
=
{
spec_rank_bar
}
option
=
{
CHART_CONFIG
}
/
>
)}
<
/div
>
<
/Card
>
...
...
web/src/pages/Home/index.js
View file @
c17439a1
...
...
@@ -272,10 +272,7 @@ const Home = () => {
className
=
"w-full h-screen border-none"
/>
)
:
(
<
div
className
=
"text-base md:text-lg p-4 md:p-6 lg:p-8 overflow-x-hidden max-w-6xl mx-auto"
dangerouslySetInnerHTML
=
{{
__html
:
homePageContent
}}
><
/div
>
<
div
className
=
"mt-[64px]"
dangerouslySetInnerHTML
=
{{
__html
:
homePageContent
}}
/
>
)}
<
/div
>
)}
...
...
web/src/pages/Setting/Ratio/UpstreamRatioSync.js
View file @
c17439a1
...
...
@@ -373,7 +373,7 @@ export default function UpstreamRatioSync(props) {
<
div
className
=
"flex flex-col md:flex-row gap-2 w-full md:w-auto order-2 md:order-1"
>
<
Button
icon
=
{
<
RefreshCcw
size
=
{
14
}
/>
}
className
=
"
!rounded-full
w-full md:w-auto mt-2"
className
=
"w-full md:w-auto mt-2"
onClick
=
{()
=>
{
setModalVisible
(
true
);
if
(
allChannels
.
length
===
0
)
{
...
...
@@ -393,7 +393,7 @@ export default function UpstreamRatioSync(props) {
type
=
'secondary'
onClick
=
{
applySync
}
disabled
=
{
!
hasSelections
}
className
=
"
!rounded-full
w-full md:w-auto mt-2"
className
=
"w-full md:w-auto mt-2"
>
{
t
(
'应用同步'
)}
<
/Button
>
...
...
@@ -406,7 +406,7 @@ export default function UpstreamRatioSync(props) {
placeholder
=
{
t
(
'搜索模型名称'
)}
value
=
{
searchKeyword
}
onChange
=
{
setSearchKeyword
}
className
=
"
!rounded-full
w-full sm:w-64"
className
=
"w-full sm:w-64"
showClear
/>
...
...
@@ -414,7 +414,7 @@ export default function UpstreamRatioSync(props) {
placeholder
=
{
t
(
'按倍率类型筛选'
)}
value
=
{
ratioTypeFilter
}
onChange
=
{
setRatioTypeFilter
}
className
=
"
!rounded-full
w-full sm:w-48"
className
=
"w-full sm:w-48"
showClear
onClear
=
{()
=>
setRatioTypeFilter
(
''
)}
>
...
...
@@ -704,7 +704,6 @@ export default function UpstreamRatioSync(props) {
scroll
=
{{
x
:
'max-content'
}}
size
=
'middle'
loading
=
{
loading
||
syncLoading
}
className
=
"rounded-xl overflow-hidden"
/
>
);
};
...
...
web/src/pages/User/EditUser.js
View file @
c17439a1
...
...
@@ -22,6 +22,7 @@ import {
Row
,
Col
,
Input
,
InputNumber
,
}
from
'@douyinfe/semi-ui'
;
import
{
IconUser
,
...
...
@@ -39,7 +40,7 @@ const EditUser = (props) => {
const
userId
=
props
.
editingUser
.
id
;
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
addQuotaModalOpen
,
setIsModalOpen
]
=
useState
(
false
);
const
[
addQuotaLocal
,
setAddQuotaLocal
]
=
useState
(
'
0
'
);
const
[
addQuotaLocal
,
setAddQuotaLocal
]
=
useState
(
''
);
const
[
groupOptions
,
setGroupOptions
]
=
useState
([]);
const
formApiRef
=
useRef
(
null
);
...
...
@@ -254,7 +255,6 @@ const EditUser = (props) => {
field
=
'quota'
label
=
{
t
(
'剩余额度'
)}
placeholder
=
{
t
(
'请输入新的剩余额度'
)}
min
=
{
0
}
step
=
{
500000
}
extraText
=
{
renderQuotaWithPrompt
(
values
.
quota
||
0
)}
rules
=
{[{
required
:
true
,
message
:
t
(
'请输入额度'
)
}]}
...
...
@@ -328,18 +328,19 @@ const EditUser = (props) => {
const
current
=
formApiRef
.
current
?.
getValue
(
'quota'
)
||
0
;
return
(
<
Text
type
=
'secondary'
className
=
'block mb-2'
>
{
`
${
t
(
'新额度'
)}${
renderQuota
(
current
)}
+
${
renderQuota
(
addQuotaLocal
)}
=
${
renderQuota
(
current
+
parseInt
(
addQuotaLocal
||
0
))}
`
}
{
`
${
t
(
'新额度
:
'
)}${
renderQuota
(
current
)}
+
${
renderQuota
(
addQuotaLocal
)}
=
${
renderQuota
(
current
+
parseInt
(
addQuotaLocal
||
0
))}
`
}
<
/Text
>
);
})()
}
<
/div
>
<
Input
<
Input
Number
placeholder
=
{
t
(
'需要添加的额度(支持负数)'
)}
type
=
'number'
value
=
{
addQuotaLocal
}
onChange
=
{
setAddQuotaLocal
}
style
=
{{
width
:
'100%'
}}
showClear
step
=
{
500000
}
/
>
<
/Modal
>
<
/
>
...
...
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