feat: 迁移自定义功能至 1.5.9.9 版本

包含音乐源限制、CI 防 OOM 优化、数据库连接池优化、管理端权限及 UI 细节定制等功能
This commit is contained in:
2026-09-12 19:26:42 +08:00
parent 34f0679117
commit 1106aeea6c
161 changed files with 18963 additions and 27900 deletions
+25 -35
View File
@@ -7,35 +7,35 @@
>
<div
v-if="show"
class="fixed inset-0 z-[2000] bg-bg-primary-80 backdrop-blur-sm flex items-center justify-center p-4"
class="fixed inset-0 z-[2000] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4"
@click="$emit('cancel')"
>
<div
class="w-full max-w-md bg-bg-primary border border-border-secondary rounded-2xl p-6 shadow-2xl space-y-6"
class="w-full max-w-md bg-zinc-950 border border-zinc-800 rounded-2xl p-6 shadow-2xl space-y-6"
@click.stop
>
<div class="text-center space-y-2">
<h3 class="text-xl font-bold text-text-primary">{{ locale.title }}</h3>
<p class="text-sm text-text-tertiary">
{{ method === 'totp' ? locale.totpDesc : locale.emailDesc }}
<h3 class="text-xl font-bold text-zinc-100">双重认证</h3>
<p class="text-sm text-zinc-400">
{{ method === 'totp' ? '请输入您的认证器应用生成的6位验证码' : '请输入发送到您邮箱的验证码' }}
</p>
</div>
<div class="space-y-4">
<!-- 邮箱输入 (仅 Email 模式) -->
<div v-if="method === 'email'" class="space-y-2">
<label class="block text-sm text-text-tertiary">
{{ locale.emailLabel }}
<span v-if="maskedEmail" class="block text-xs text-text-tertiary mt-1">
{{ locale.maskedEmailTip }} {{ maskedEmail }}
<label class="block text-sm text-zinc-400">
请补全您的邮箱以接收验证码
<span v-if="maskedEmail" class="block text-xs text-zinc-500 mt-1">
提示: {{ maskedEmail }}
</span>
</label>
<div class="relative">
<input
v-model="emailInput"
type="email"
:placeholder="locale.emailPlaceholder"
class="w-full bg-bg-secondary border border-border-secondary rounded-lg px-4 py-3 text-text-primary placeholder-text-disabled focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
placeholder="请输入完整邮箱地址"
class="w-full bg-zinc-900 border border-zinc-800 rounded-lg px-4 py-3 text-zinc-100 placeholder-zinc-700 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all"
:disabled="emailSent && cooldown > 0"
@keyup.enter="!emailSent && sendEmailCode()"
/>
@@ -50,7 +50,7 @@
type="text"
maxlength="6"
placeholder="000000"
class="w-full bg-bg-secondary border border-border-secondary rounded-lg px-4 py-3 text-center text-2xl font-mono tracking-widest text-text-primary placeholder-text-disabled focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
class="w-full bg-zinc-900 border border-zinc-800 rounded-lg px-4 py-3 text-center text-2xl font-mono tracking-widest text-zinc-100 placeholder-zinc-700 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all"
@keyup.enter="handleVerify"
ref="inputRef"
/>
@@ -63,13 +63,13 @@
type="button"
@click="sendEmailCode"
:disabled="cooldown > 0 || sending || !emailInput"
class="text-sm text-primary hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
class="text-sm text-blue-500 hover:text-blue-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
{{ resendButtonText }}
{{ cooldown > 0 ? `${cooldown}秒后重新发送` : (emailSent ? '重新发送验证码' : '发送验证码') }}
</button>
</div>
<div v-if="error" class="p-3 bg-error-10 border border-error-20 rounded-lg flex items-center gap-2 text-error text-sm">
<div v-if="error" class="p-3 bg-red-500/10 border border-red-500/20 rounded-lg flex items-center gap-2 text-red-500 text-sm">
<AlertCircle :size="16" />
<span>{{ error }}</span>
</div>
@@ -77,26 +77,26 @@
<button
@click="handleVerify"
:disabled="loading || code.length !== 6"
class="w-full py-3 bg-primary-hover hover:bg-primary disabled:opacity-50 disabled:cursor-not-allowed text-text-primary font-bold rounded-lg transition-colors flex items-center justify-center gap-2"
class="w-full py-3 bg-blue-600 hover:bg-blue-500 disabled:opacity-50 disabled:cursor-not-allowed text-white font-bold rounded-lg transition-colors flex items-center justify-center gap-2"
>
<Loader2 v-if="loading" class="animate-spin" :size="18" />
<span>{{ locale.verifyLogin }}</span>
<span>验证登录</span>
</button>
<!-- 切换验证方式按钮 -->
<button
v-if="hasMultipleMethods"
@click="toggleMethod"
class="w-full py-2 text-primary hover:text-primary text-sm transition-colors"
class="w-full py-2 text-blue-500 hover:text-blue-400 text-sm transition-colors"
>
{{ method === 'totp' ? locale.switchToEmail : locale.switchToTotp }}
{{ method === 'totp' ? '无法使用验证器?切换到邮箱验证' : '使用验证器应用验证' }}
</button>
<button
@click="$emit('cancel')"
class="w-full py-2 text-text-tertiary hover:text-text-secondary text-sm transition-colors"
class="w-full py-2 text-zinc-500 hover:text-zinc-300 text-sm transition-colors"
>
{{ locale.backLogin }}
返回登录
</button>
</div>
</div>
@@ -109,7 +109,6 @@
import { ref, watch, nextTick, computed, onUnmounted } from 'vue'
import { Loader2, AlertCircle } from '@lucide/vue'
import { useToast } from '~/composables/useToast'
import { useLocale } from '~/utils/locale'
const props = defineProps<{
show: boolean
@@ -125,15 +124,6 @@ const emit = defineEmits<{
}>()
const { showToast } = useToast()
const { auth } = useLocale()
const locale = computed(() => auth.value?.twoFactorVerify || {})
const { format: formatLocaleValue } = useLocaleText(locale)
const resendButtonText = computed(() => {
if (cooldown.value > 0) {
return formatLocaleValue(locale.value.resendCountdown, '', cooldown.value)
}
return emailSent.value ? locale.value.resendCode : locale.value.sendCode
})
// 默认优先使用 TOTP,如果没有则使用 Email
const defaultMethod = computed(() => props.availableMethods?.includes('totp') ? 'totp' : 'email')
const method = ref(defaultMethod.value)
@@ -170,7 +160,7 @@ const toggleMethod = () => {
const sendEmailCode = async () => {
try {
if (!emailInput.value) {
error.value = locale.value.emailRequired || '请输入邮箱'
error.value = '请输入您的邮箱地址'
return
}
@@ -186,7 +176,7 @@ const sendEmailCode = async () => {
}
})
showToast(locale.value.codeSent, 'success')
showToast('验证码已发送', 'success')
emailSent.value = true
cooldown.value = 60
timer = setInterval(() => {
@@ -200,7 +190,7 @@ const sendEmailCode = async () => {
// 发送成功后自动聚焦验证码输入框
nextTick(() => inputRef.value?.focus())
} catch (err: any) {
error.value = err.data?.message || err.message || locale.value.sendFailed || '发送失败'
error.value = err.data?.message || err.message || '发送失败'
} finally {
sending.value = false
}
@@ -218,7 +208,7 @@ const handleVerify = async () => {
emit('success', response)
} catch (err: any) {
error.value = err.data?.message || err.message || locale.value.verifyFailed || '验证失败'
error.value = err.data?.message || err.message || '验证失败'
code.value = '' // 出错清空
} finally {
loading.value = false