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
59231668
Commit
59231668
authored
Jan 29, 2026
by
cyhhao
Browse files
Merge branch 'main' of github.com:Wei-Shaw/sub2api
parents
ffe43f60
cadca752
Changes
24
Show whitespace changes
Inline
Side-by-side
frontend/src/stores/app.ts
View file @
59231668
...
...
@@ -324,6 +324,8 @@ export const useAppStore = defineStore('app', () => {
doc_url
:
docUrl
.
value
,
home_content
:
''
,
hide_ccs_import_button
:
false
,
purchase_subscription_enabled
:
false
,
purchase_subscription_url
:
''
,
linuxdo_oauth_enabled
:
false
,
version
:
siteVersion
.
value
}
...
...
frontend/src/types/index.ts
View file @
59231668
...
...
@@ -82,6 +82,8 @@ export interface PublicSettings {
doc_url
:
string
home_content
:
string
hide_ccs_import_button
:
boolean
purchase_subscription_enabled
:
boolean
purchase_subscription_url
:
string
linuxdo_oauth_enabled
:
boolean
version
:
string
}
...
...
frontend/src/views/admin/SettingsView.vue
View file @
59231668
...
...
@@ -935,6 +935,51 @@
</div>
</div>
<!-- Purchase Subscription Page -->
<div
class=
"card"
>
<div
class=
"border-b border-gray-100 px-6 py-4 dark:border-dark-700"
>
<h2
class=
"text-lg font-semibold text-gray-900 dark:text-white"
>
{{ t('admin.settings.purchase.title') }}
</h2>
<p
class=
"mt-1 text-sm text-gray-500 dark:text-gray-400"
>
{{ t('admin.settings.purchase.description') }}
</p>
</div>
<div
class=
"space-y-6 p-6"
>
<!-- Enable Toggle -->
<div
class=
"flex items-center justify-between"
>
<div>
<label
class=
"font-medium text-gray-900 dark:text-white"
>
{{
t('admin.settings.purchase.enabled')
}}
</label>
<p
class=
"text-sm text-gray-500 dark:text-gray-400"
>
{{ t('admin.settings.purchase.enabledHint') }}
</p>
</div>
<Toggle
v-model=
"form.purchase_subscription_enabled"
/>
</div>
<!-- URL -->
<div>
<label
class=
"mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>
{{ t('admin.settings.purchase.url') }}
</label>
<input
v-model=
"form.purchase_subscription_url"
type=
"url"
class=
"input font-mono text-sm"
:placeholder=
"t('admin.settings.purchase.urlPlaceholder')"
/>
<p
class=
"mt-1.5 text-xs text-gray-500 dark:text-gray-400"
>
{{ t('admin.settings.purchase.urlHint') }}
</p>
<p
class=
"mt-2 text-xs text-amber-600 dark:text-amber-400"
>
{{ t('admin.settings.purchase.iframeWarning') }}
</p>
</div>
</div>
</div>
<!-- Send Test Email - Only show when email verification is enabled -->
<div
v-if=
"form.email_verify_enabled"
class=
"card"
>
<div
class=
"border-b border-gray-100 px-6 py-4 dark:border-dark-700"
>
...
...
@@ -1083,6 +1128,8 @@ const form = reactive<SettingsForm>({
doc_url
:
''
,
home_content
:
''
,
hide_ccs_import_button
:
false
,
purchase_subscription_enabled
:
false
,
purchase_subscription_url
:
''
,
smtp_host
:
''
,
smtp_port
:
587
,
smtp_username
:
''
,
...
...
@@ -1208,6 +1255,8 @@ async function saveSettings() {
doc_url
:
form
.
doc_url
,
home_content
:
form
.
home_content
,
hide_ccs_import_button
:
form
.
hide_ccs_import_button
,
purchase_subscription_enabled
:
form
.
purchase_subscription_enabled
,
purchase_subscription_url
:
form
.
purchase_subscription_url
,
smtp_host
:
form
.
smtp_host
,
smtp_port
:
form
.
smtp_port
,
smtp_username
:
form
.
smtp_username
,
...
...
frontend/src/views/user/PurchaseSubscriptionView.vue
0 → 100644
View file @
59231668
<
template
>
<AppLayout>
<div
class=
"purchase-page-layout"
>
<div
class=
"flex items-start justify-between gap-4"
>
<div>
<h2
class=
"text-lg font-semibold text-gray-900 dark:text-white"
>
{{
t
(
'
purchase.title
'
)
}}
</h2>
<p
class=
"mt-1 text-sm text-gray-500 dark:text-dark-400"
>
{{
t
(
'
purchase.description
'
)
}}
</p>
</div>
<div
class=
"flex items-center gap-2"
>
<a
v-if=
"isValidUrl"
:href=
"purchaseUrl"
target=
"_blank"
rel=
"noopener noreferrer"
class=
"btn btn-secondary btn-sm"
>
<Icon
name=
"externalLink"
size=
"sm"
class=
"mr-1.5"
:stroke-width=
"2"
/>
{{
t
(
'
purchase.openInNewTab
'
)
}}
</a>
</div>
</div>
<div
class=
"card flex-1 min-h-0 overflow-hidden"
>
<div
v-if=
"loading"
class=
"flex h-full items-center justify-center py-12"
>
<div
class=
"h-8 w-8 animate-spin rounded-full border-2 border-primary-500 border-t-transparent"
></div>
</div>
<div
v-else-if=
"!purchaseEnabled"
class=
"flex h-full items-center justify-center p-10 text-center"
>
<div
class=
"max-w-md"
>
<div
class=
"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-gray-100 dark:bg-dark-700"
>
<Icon
name=
"creditCard"
size=
"lg"
class=
"text-gray-400"
/>
</div>
<h3
class=
"text-lg font-semibold text-gray-900 dark:text-white"
>
{{
t
(
'
purchase.notEnabledTitle
'
)
}}
</h3>
<p
class=
"mt-2 text-sm text-gray-500 dark:text-dark-400"
>
{{
t
(
'
purchase.notEnabledDesc
'
)
}}
</p>
</div>
</div>
<div
v-else-if=
"!isValidUrl"
class=
"flex h-full items-center justify-center p-10 text-center"
>
<div
class=
"max-w-md"
>
<div
class=
"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-gray-100 dark:bg-dark-700"
>
<Icon
name=
"link"
size=
"lg"
class=
"text-gray-400"
/>
</div>
<h3
class=
"text-lg font-semibold text-gray-900 dark:text-white"
>
{{
t
(
'
purchase.notConfiguredTitle
'
)
}}
</h3>
<p
class=
"mt-2 text-sm text-gray-500 dark:text-dark-400"
>
{{
t
(
'
purchase.notConfiguredDesc
'
)
}}
</p>
</div>
</div>
<iframe
v-else
:src=
"purchaseUrl"
class=
"h-full w-full border-0"
allowfullscreen
></iframe>
</div>
</div>
</AppLayout>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
onMounted
,
ref
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
Icon
from
'
@/components/icons/Icon.vue
'
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
const
loading
=
ref
(
false
)
const
purchaseEnabled
=
computed
(()
=>
{
return
appStore
.
cachedPublicSettings
?.
purchase_subscription_enabled
??
false
})
const
purchaseUrl
=
computed
(()
=>
{
return
(
appStore
.
cachedPublicSettings
?.
purchase_subscription_url
||
''
).
trim
()
})
const
isValidUrl
=
computed
(()
=>
{
const
url
=
purchaseUrl
.
value
return
url
.
startsWith
(
'
http://
'
)
||
url
.
startsWith
(
'
https://
'
)
})
onMounted
(
async
()
=>
{
if
(
appStore
.
publicSettingsLoaded
)
return
loading
.
value
=
true
try
{
await
appStore
.
fetchPublicSettings
()
}
finally
{
loading
.
value
=
false
}
})
</
script
>
<
style
scoped
>
.purchase-page-layout
{
@apply
flex
flex-col
gap-6;
height
:
calc
(
100vh
-
64px
-
4rem
);
/* 减去 header + lg:p-8 的上下padding */
}
</
style
>
Prev
1
2
Next
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