Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
5f7e5184
Commit
5f7e5184
authored
Dec 23, 2025
by
shaw
Browse files
feat: admin/subscriptions新增重置时间显示
parent
008a1112
Changes
3
Show whitespace changes
Inline
Side-by-side
frontend/src/i18n/locales/en.ts
View file @
5f7e5184
...
@@ -581,6 +581,10 @@ export default {
...
@@ -581,6 +581,10 @@ export default {
weekly
:
'
Weekly
'
,
weekly
:
'
Weekly
'
,
monthly
:
'
Monthly
'
,
monthly
:
'
Monthly
'
,
noLimits
:
'
No limits configured
'
,
noLimits
:
'
No limits configured
'
,
resetNow
:
'
Resetting soon
'
,
resetInMinutes
:
'
Resets in {minutes}m
'
,
resetInHoursMinutes
:
'
Resets in {hours}h {minutes}m
'
,
resetInDaysHours
:
'
Resets in {days}d {hours}h
'
,
daysRemaining
:
'
days remaining
'
,
daysRemaining
:
'
days remaining
'
,
noExpiration
:
'
No expiration
'
,
noExpiration
:
'
No expiration
'
,
status
:
{
status
:
{
...
...
frontend/src/i18n/locales/zh.ts
View file @
5f7e5184
...
@@ -638,6 +638,10 @@ export default {
...
@@ -638,6 +638,10 @@ export default {
weekly
:
'
每周
'
,
weekly
:
'
每周
'
,
monthly
:
'
每月
'
,
monthly
:
'
每月
'
,
noLimits
:
'
未配置限额
'
,
noLimits
:
'
未配置限额
'
,
resetNow
:
'
即将重置
'
,
resetInMinutes
:
'
{minutes} 分钟后重置
'
,
resetInHoursMinutes
:
'
{hours} 小时 {minutes} 分钟后重置
'
,
resetInDaysHours
:
'
{days} 天 {hours} 小时后重置
'
,
daysRemaining
:
'
天剩余
'
,
daysRemaining
:
'
天剩余
'
,
noExpiration
:
'
无过期时间
'
,
noExpiration
:
'
无过期时间
'
,
status
:
{
status
:
{
...
...
frontend/src/views/admin/SubscriptionsView.vue
View file @
5f7e5184
...
@@ -66,46 +66,83 @@
...
@@ -66,46 +66,83 @@
<
/template
>
<
/template
>
<
template
#
cell
-
usage
=
"
{ row
}
"
>
<
template
#
cell
-
usage
=
"
{ row
}
"
>
<
div
class
=
"
space-y-1 min-w-[200px]
"
>
<
div
class
=
"
space-y-2 min-w-[280px]
"
>
<
div
v
-
if
=
"
row.group?.daily_limit_usd
"
class
=
"
flex items-center gap-2
"
>
<!--
Daily
Usage
-->
<
span
class
=
"
text-xs text-gray-500 w-12
"
>
{{
t
(
'
admin.subscriptions.daily
'
)
}}
<
/span
>
<
div
v
-
if
=
"
row.group?.daily_limit_usd
"
class
=
"
usage-row
"
>
<
div
class
=
"
flex-1 bg-gray-200 dark:bg-dark-600 rounded-full h-2
"
>
<
div
class
=
"
flex items-center gap-2
"
>
<
span
class
=
"
usage-label
"
>
{{
t
(
'
admin.subscriptions.daily
'
)
}}
<
/span
>
<
div
class
=
"
flex-1 bg-gray-200 dark:bg-dark-600 rounded-full h-1.5
"
>
<
div
<
div
class
=
"
h-
2
rounded-full transition-all
"
class
=
"
h-
1.5
rounded-full transition-all
"
:
class
=
"
getProgressClass(row.daily_usage_usd, row.group?.daily_limit_usd)
"
:
class
=
"
getProgressClass(row.daily_usage_usd, row.group?.daily_limit_usd)
"
:
style
=
"
{ width: getProgressWidth(row.daily_usage_usd, row.group?.daily_limit_usd)
}
"
:
style
=
"
{ width: getProgressWidth(row.daily_usage_usd, row.group?.daily_limit_usd)
}
"
><
/div
>
><
/div
>
<
/div
>
<
/div
>
<
span
class
=
"
text-xs text-gray-500 w-20 text-right
"
>
<
span
class
=
"
usage-amount
"
>
$
{{
row
.
daily_usage_usd
?.
toFixed
(
2
)
||
'
0.00
'
}}
/
$
{{
row
.
group
?.
daily_limit_usd
?.
toFixed
(
2
)
}}
$
{{
row
.
daily_usage_usd
?.
toFixed
(
2
)
||
'
0.00
'
}}
<
span
class
=
"
text-gray-400
"
>
/</
span
>
$
{{
row
.
group
?.
daily_limit_usd
?.
toFixed
(
2
)
}}
<
/span
>
<
/span
>
<
/div
>
<
/div
>
<
div
v
-
if
=
"
row.group?.weekly_limit_usd
"
class
=
"
flex items-center gap-2
"
>
<
div
class
=
"
reset-info
"
v
-
if
=
"
row.daily_window_start
"
>
<
span
class
=
"
text-xs text-gray-500 w-12
"
>
{{
t
(
'
admin.subscriptions.weekly
'
)
}}
<
/span
>
<
svg
class
=
"
w-3 h-3
"
fill
=
"
none
"
viewBox
=
"
0 0 24 24
"
stroke
=
"
currentColor
"
stroke
-
width
=
"
2
"
>
<
div
class
=
"
flex-1 bg-gray-200 dark:bg-dark-600 rounded-full h-2
"
>
<
path
stroke
-
linecap
=
"
round
"
stroke
-
linejoin
=
"
round
"
d
=
"
M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z
"
/>
<
/svg
>
<
span
>
{{
formatResetTime
(
row
.
daily_window_start
,
'
daily
'
)
}}
<
/span
>
<
/div
>
<
/div
>
<!--
Weekly
Usage
-->
<
div
v
-
if
=
"
row.group?.weekly_limit_usd
"
class
=
"
usage-row
"
>
<
div
class
=
"
flex items-center gap-2
"
>
<
span
class
=
"
usage-label
"
>
{{
t
(
'
admin.subscriptions.weekly
'
)
}}
<
/span
>
<
div
class
=
"
flex-1 bg-gray-200 dark:bg-dark-600 rounded-full h-1.5
"
>
<
div
<
div
class
=
"
h-
2
rounded-full transition-all
"
class
=
"
h-
1.5
rounded-full transition-all
"
:
class
=
"
getProgressClass(row.weekly_usage_usd, row.group?.weekly_limit_usd)
"
:
class
=
"
getProgressClass(row.weekly_usage_usd, row.group?.weekly_limit_usd)
"
:
style
=
"
{ width: getProgressWidth(row.weekly_usage_usd, row.group?.weekly_limit_usd)
}
"
:
style
=
"
{ width: getProgressWidth(row.weekly_usage_usd, row.group?.weekly_limit_usd)
}
"
><
/div
>
><
/div
>
<
/div
>
<
/div
>
<
span
class
=
"
text-xs text-gray-500 w-20 text-right
"
>
<
span
class
=
"
usage-amount
"
>
$
{{
row
.
weekly_usage_usd
?.
toFixed
(
2
)
||
'
0.00
'
}}
/
$
{{
row
.
group
?.
weekly_limit_usd
?.
toFixed
(
2
)
}}
$
{{
row
.
weekly_usage_usd
?.
toFixed
(
2
)
||
'
0.00
'
}}
<
span
class
=
"
text-gray-400
"
>
/</
span
>
$
{{
row
.
group
?.
weekly_limit_usd
?.
toFixed
(
2
)
}}
<
/span
>
<
/span
>
<
/div
>
<
/div
>
<
div
v
-
if
=
"
row.group?.monthly_limit_usd
"
class
=
"
flex items-center gap-2
"
>
<
div
class
=
"
reset-info
"
v
-
if
=
"
row.weekly_window_start
"
>
<
span
class
=
"
text-xs text-gray-500 w-12
"
>
{{
t
(
'
admin.subscriptions.monthly
'
)
}}
<
/span
>
<
svg
class
=
"
w-3 h-3
"
fill
=
"
none
"
viewBox
=
"
0 0 24 24
"
stroke
=
"
currentColor
"
stroke
-
width
=
"
2
"
>
<
div
class
=
"
flex-1 bg-gray-200 dark:bg-dark-600 rounded-full h-2
"
>
<
path
stroke
-
linecap
=
"
round
"
stroke
-
linejoin
=
"
round
"
d
=
"
M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z
"
/>
<
/svg
>
<
span
>
{{
formatResetTime
(
row
.
weekly_window_start
,
'
weekly
'
)
}}
<
/span
>
<
/div
>
<
/div
>
<!--
Monthly
Usage
-->
<
div
v
-
if
=
"
row.group?.monthly_limit_usd
"
class
=
"
usage-row
"
>
<
div
class
=
"
flex items-center gap-2
"
>
<
span
class
=
"
usage-label
"
>
{{
t
(
'
admin.subscriptions.monthly
'
)
}}
<
/span
>
<
div
class
=
"
flex-1 bg-gray-200 dark:bg-dark-600 rounded-full h-1.5
"
>
<
div
<
div
class
=
"
h-
2
rounded-full transition-all
"
class
=
"
h-
1.5
rounded-full transition-all
"
:
class
=
"
getProgressClass(row.monthly_usage_usd, row.group?.monthly_limit_usd)
"
:
class
=
"
getProgressClass(row.monthly_usage_usd, row.group?.monthly_limit_usd)
"
:
style
=
"
{ width: getProgressWidth(row.monthly_usage_usd, row.group?.monthly_limit_usd)
}
"
:
style
=
"
{ width: getProgressWidth(row.monthly_usage_usd, row.group?.monthly_limit_usd)
}
"
><
/div
>
><
/div
>
<
/div
>
<
/div
>
<
span
class
=
"
text-xs text-gray-500 w-20 text-right
"
>
<
span
class
=
"
usage-amount
"
>
$
{{
row
.
monthly_usage_usd
?.
toFixed
(
2
)
||
'
0.00
'
}}
/
$
{{
row
.
group
?.
monthly_limit_usd
?.
toFixed
(
2
)
}}
$
{{
row
.
monthly_usage_usd
?.
toFixed
(
2
)
||
'
0.00
'
}}
<
span
class
=
"
text-gray-400
"
>
/</
span
>
$
{{
row
.
group
?.
monthly_limit_usd
?.
toFixed
(
2
)
}}
<
/span
>
<
/span
>
<
/div
>
<
/div
>
<
div
class
=
"
reset-info
"
v
-
if
=
"
row.monthly_window_start
"
>
<
svg
class
=
"
w-3 h-3
"
fill
=
"
none
"
viewBox
=
"
0 0 24 24
"
stroke
=
"
currentColor
"
stroke
-
width
=
"
2
"
>
<
path
stroke
-
linecap
=
"
round
"
stroke
-
linejoin
=
"
round
"
d
=
"
M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z
"
/>
<
/svg
>
<
span
>
{{
formatResetTime
(
row
.
monthly_window_start
,
'
monthly
'
)
}}
<
/span
>
<
/div
>
<
/div
>
<!--
No
Limits
-->
<
div
v
-
if
=
"
!row.group?.daily_limit_usd && !row.group?.weekly_limit_usd && !row.group?.monthly_limit_usd
"
class
=
"
text-xs text-gray-500
"
>
<
div
v
-
if
=
"
!row.group?.daily_limit_usd && !row.group?.weekly_limit_usd && !row.group?.monthly_limit_usd
"
class
=
"
text-xs text-gray-500
"
>
{{
t
(
'
admin.subscriptions.noLimits
'
)
}}
{{
t
(
'
admin.subscriptions.noLimits
'
)
}}
<
/div
>
<
/div
>
...
@@ -553,9 +590,63 @@ const getProgressClass = (used: number, limit: number | null): string => {
...
@@ -553,9 +590,63 @@ const getProgressClass = (used: number, limit: number | null): string => {
return
'
bg-green-500
'
return
'
bg-green-500
'
}
}
// Format reset time based on window start and period type
const
formatResetTime
=
(
windowStart
:
string
,
period
:
'
daily
'
|
'
weekly
'
|
'
monthly
'
):
string
=>
{
const
start
=
new
Date
(
windowStart
)
const
now
=
new
Date
()
// Calculate reset time based on period
let
resetTime
:
Date
switch
(
period
)
{
case
'
daily
'
:
resetTime
=
new
Date
(
start
.
getTime
()
+
24
*
60
*
60
*
1000
)
break
case
'
weekly
'
:
resetTime
=
new
Date
(
start
.
getTime
()
+
7
*
24
*
60
*
60
*
1000
)
break
case
'
monthly
'
:
resetTime
=
new
Date
(
start
.
getTime
()
+
30
*
24
*
60
*
60
*
1000
)
break
}
const
diffMs
=
resetTime
.
getTime
()
-
now
.
getTime
()
if
(
diffMs
<=
0
)
return
t
(
'
admin.subscriptions.resetNow
'
)
const
diffSeconds
=
Math
.
floor
(
diffMs
/
1000
)
const
days
=
Math
.
floor
(
diffSeconds
/
86400
)
const
hours
=
Math
.
floor
((
diffSeconds
%
86400
)
/
3600
)
const
minutes
=
Math
.
floor
((
diffSeconds
%
3600
)
/
60
)
if
(
days
>
0
)
{
return
t
(
'
admin.subscriptions.resetInDaysHours
'
,
{
days
,
hours
}
)
}
else
if
(
hours
>
0
)
{
return
t
(
'
admin.subscriptions.resetInHoursMinutes
'
,
{
hours
,
minutes
}
)
}
else
{
return
t
(
'
admin.subscriptions.resetInMinutes
'
,
{
minutes
}
)
}
}
onMounted
(()
=>
{
onMounted
(()
=>
{
loadSubscriptions
()
loadSubscriptions
()
loadGroups
()
loadGroups
()
loadUsers
()
loadUsers
()
}
)
}
)
<
/script
>
<
/script
>
<
style
scoped
>
.
usage
-
row
{
@
apply
space
-
y
-
1
;
}
.
usage
-
label
{
@
apply
text
-
xs
font
-
medium
text
-
gray
-
500
dark
:
text
-
gray
-
400
w
-
10
flex
-
shrink
-
0
;
}
.
usage
-
amount
{
@
apply
text
-
xs
text
-
gray
-
600
dark
:
text
-
gray
-
300
tabular
-
nums
whitespace
-
nowrap
;
}
.
reset
-
info
{
@
apply
flex
items
-
center
gap
-
1
text
-
[
10
px
]
text
-
blue
-
600
dark
:
text
-
blue
-
400
pl
-
12
;
}
<
/style
>
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