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
39fad63c
Unverified
Commit
39fad63c
authored
Jan 20, 2026
by
Wesley Liddick
Committed by
GitHub
Jan 20, 2026
Browse files
Merge pull request #345 from whoismonay/main
mod(frontend): 管理员订阅/兑换码分组选择展示备注
parents
5602d02b
88b63584
Changes
3
Hide whitespace changes
Inline
Side-by-side
frontend/src/views/admin/RedeemView.vue
View file @
39fad63c
...
...
@@ -238,7 +238,30 @@
v
-
model
=
"
generateForm.group_id
"
:
options
=
"
subscriptionGroupOptions
"
:
placeholder
=
"
t('admin.redeem.selectGroupPlaceholder')
"
/>
>
<
template
#
selected
=
"
{ option
}
"
>
<
GroupBadge
v
-
if
=
"
option
"
:
name
=
"
(option as unknown as GroupOption).label
"
:
platform
=
"
(option as unknown as GroupOption).platform
"
:
subscription
-
type
=
"
(option as unknown as GroupOption).subscriptionType
"
:
rate
-
multiplier
=
"
(option as unknown as GroupOption).rate
"
/>
<
span
v
-
else
class
=
"
text-gray-400
"
>
{{
t
(
'
admin.redeem.selectGroupPlaceholder
'
)
}}
<
/span
>
<
/template
>
<
template
#
option
=
"
{ option, selected
}
"
>
<
GroupOptionItem
:
name
=
"
(option as unknown as GroupOption).label
"
:
platform
=
"
(option as unknown as GroupOption).platform
"
:
subscription
-
type
=
"
(option as unknown as GroupOption).subscriptionType
"
:
rate
-
multiplier
=
"
(option as unknown as GroupOption).rate
"
:
description
=
"
(option as unknown as GroupOption).description
"
:
selected
=
"
selected
"
/>
<
/template
>
<
/Select
>
<
/div
>
<
div
>
<
label
class
=
"
input-label
"
>
{{
t
(
'
admin.redeem.validityDays
'
)
}}
<
/label
>
...
...
@@ -370,7 +393,7 @@ import { useAppStore } from '@/stores/app'
import
{
useClipboard
}
from
'
@/composables/useClipboard
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
formatDateTime
}
from
'
@/utils/format
'
import
type
{
RedeemCode
,
RedeemCodeType
,
Group
}
from
'
@/types
'
import
type
{
RedeemCode
,
RedeemCodeType
,
Group
,
GroupPlatform
,
SubscriptionType
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
TablePageLayout
from
'
@/components/layout/TablePageLayout.vue
'
...
...
@@ -378,12 +401,23 @@ import DataTable from '@/components/common/DataTable.vue'
import
Pagination
from
'
@/components/common/Pagination.vue
'
import
ConfirmDialog
from
'
@/components/common/ConfirmDialog.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
GroupBadge
from
'
@/components/common/GroupBadge.vue
'
import
GroupOptionItem
from
'
@/components/common/GroupOptionItem.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
const
{
copyToClipboard
:
clipboardCopy
}
=
useClipboard
()
interface
GroupOption
{
value
:
number
label
:
string
description
:
string
|
null
platform
:
GroupPlatform
subscriptionType
:
SubscriptionType
rate
:
number
}
const
showGenerateDialog
=
ref
(
false
)
const
showResultDialog
=
ref
(
false
)
const
generatedCodes
=
ref
<
RedeemCode
[]
>
([])
...
...
@@ -395,7 +429,11 @@ const subscriptionGroupOptions = computed(() => {
.
filter
((
g
)
=>
g
.
subscription_type
===
'
subscription
'
)
.
map
((
g
)
=>
({
value
:
g
.
id
,
label
:
g
.
name
label
:
g
.
name
,
description
:
g
.
description
,
platform
:
g
.
platform
,
subscriptionType
:
g
.
subscription_type
,
rate
:
g
.
rate_multiplier
}
))
}
)
...
...
frontend/src/views/admin/SubscriptionsView.vue
View file @
39fad63c
...
...
@@ -466,7 +466,28 @@
v
-
model
=
"
assignForm.group_id
"
:
options
=
"
subscriptionGroupOptions
"
:
placeholder
=
"
t('admin.subscriptions.selectGroup')
"
/>
>
<
template
#
selected
=
"
{ option
}
"
>
<
GroupBadge
v
-
if
=
"
option
"
:
name
=
"
(option as unknown as GroupOption).label
"
:
platform
=
"
(option as unknown as GroupOption).platform
"
:
subscription
-
type
=
"
(option as unknown as GroupOption).subscriptionType
"
:
rate
-
multiplier
=
"
(option as unknown as GroupOption).rate
"
/>
<
span
v
-
else
class
=
"
text-gray-400
"
>
{{
t
(
'
admin.subscriptions.selectGroup
'
)
}}
<
/span
>
<
/template
>
<
template
#
option
=
"
{ option, selected
}
"
>
<
GroupOptionItem
:
name
=
"
(option as unknown as GroupOption).label
"
:
platform
=
"
(option as unknown as GroupOption).platform
"
:
subscription
-
type
=
"
(option as unknown as GroupOption).subscriptionType
"
:
rate
-
multiplier
=
"
(option as unknown as GroupOption).rate
"
:
description
=
"
(option as unknown as GroupOption).description
"
:
selected
=
"
selected
"
/>
<
/template
>
<
/Select
>
<
p
class
=
"
input-hint
"
>
{{
t
(
'
admin.subscriptions.groupHint
'
)
}}
<
/p
>
<
/div
>
<
div
>
...
...
@@ -599,7 +620,7 @@ import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
UserSubscription
,
Group
}
from
'
@/types
'
import
type
{
UserSubscription
,
Group
,
GroupPlatform
,
SubscriptionType
}
from
'
@/types
'
import
type
{
SimpleUser
}
from
'
@/api/admin/usage
'
import
type
{
Column
}
from
'
@/components/common/types
'
import
{
formatDateOnly
}
from
'
@/utils/format
'
...
...
@@ -612,11 +633,21 @@ import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
import
EmptyState
from
'
@/components/common/EmptyState.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
GroupBadge
from
'
@/components/common/GroupBadge.vue
'
import
GroupOptionItem
from
'
@/components/common/GroupOptionItem.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
interface
GroupOption
{
value
:
number
label
:
string
description
:
string
|
null
platform
:
GroupPlatform
subscriptionType
:
SubscriptionType
rate
:
number
}
// User column display mode: 'email' or 'username'
const
userColumnMode
=
ref
<
'
email
'
|
'
username
'
>
(
'
email
'
)
const
USER_COLUMN_MODE_KEY
=
'
subscription-user-column-mode
'
...
...
@@ -792,7 +823,14 @@ const groupOptions = computed(() => [
const
subscriptionGroupOptions
=
computed
(()
=>
groups
.
value
.
filter
((
g
)
=>
g
.
subscription_type
===
'
subscription
'
&&
g
.
status
===
'
active
'
)
.
map
((
g
)
=>
({
value
:
g
.
id
,
label
:
g
.
name
}
))
.
map
((
g
)
=>
({
value
:
g
.
id
,
label
:
g
.
name
,
description
:
g
.
description
,
platform
:
g
.
platform
,
subscriptionType
:
g
.
subscription_type
,
rate
:
g
.
rate_multiplier
}
))
)
const
applyFilters
=
()
=>
{
...
...
frontend/vite.config.ts
View file @
39fad63c
import
{
defineConfig
,
Plugin
}
from
'
vite
'
import
{
defineConfig
,
loadEnv
,
Plugin
}
from
'
vite
'
import
vue
from
'
@vitejs/plugin-vue
'
import
checker
from
'
vite-plugin-checker
'
import
{
resolve
}
from
'
path
'
...
...
@@ -7,9 +7,7 @@ import { resolve } from 'path'
* Vite 插件:开发模式下注入公开配置到 index.html
* 与生产模式的后端注入行为保持一致,消除闪烁
*/
function
injectPublicSettings
():
Plugin
{
const
backendUrl
=
process
.
env
.
VITE_DEV_PROXY_TARGET
||
'
http://localhost:8080
'
function
injectPublicSettings
(
backendUrl
:
string
):
Plugin
{
return
{
name
:
'
inject-public-settings
'
,
transformIndexHtml
:
{
...
...
@@ -35,15 +33,21 @@ function injectPublicSettings(): Plugin {
}
}
export
default
defineConfig
({
plugins
:
[
vue
(),
checker
({
typescript
:
true
,
vueTsc
:
true
}),
injectPublicSettings
()
],
export
default
defineConfig
(({
mode
})
=>
{
// 加载环境变量
const
env
=
loadEnv
(
mode
,
process
.
cwd
(),
''
)
const
backendUrl
=
env
.
VITE_DEV_PROXY_TARGET
||
'
http://localhost:8080
'
const
devPort
=
Number
(
env
.
VITE_DEV_PORT
||
3000
)
return
{
plugins
:
[
vue
(),
checker
({
typescript
:
true
,
vueTsc
:
true
}),
injectPublicSettings
(
backendUrl
)
],
resolve
:
{
alias
:
{
'
@
'
:
resolve
(
__dirname
,
'
src
'
),
...
...
@@ -102,17 +106,18 @@ export default defineConfig({
}
}
},
server
:
{
host
:
'
0.0.0.0
'
,
port
:
Number
(
process
.
env
.
VITE_DEV_PORT
||
3000
),
proxy
:
{
'
/api
'
:
{
target
:
process
.
env
.
VITE_DEV_PROXY_TARGET
||
'
http://localhost:8080
'
,
changeOrigin
:
true
},
'
/setup
'
:
{
target
:
process
.
env
.
VITE_DEV_PROXY_TARGET
||
'
http://localhost:8080
'
,
changeOrigin
:
true
server
:
{
host
:
'
0.0.0.0
'
,
port
:
devPort
,
proxy
:
{
'
/api
'
:
{
target
:
backendUrl
,
changeOrigin
:
true
},
'
/setup
'
:
{
target
:
backendUrl
,
changeOrigin
:
true
}
}
}
}
...
...
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