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
+49 -55
View File
@@ -1,18 +1,18 @@
<template>
<div class="space-y-6">
<!-- 状态显示 -->
<div class="flex items-center justify-between p-4 bg-bg-primary-50 rounded-xl border border-border-secondary">
<div class="flex items-center justify-between p-4 bg-zinc-950/50 rounded-xl border border-zinc-800">
<div class="flex items-center gap-3">
<div
class="p-2 rounded-lg flex items-center justify-center"
:class="isEnabled ? 'bg-success-10 text-success' : 'bg-bg-tertiary text-text-tertiary'"
class="p-2 rounded-lg"
:class="isEnabled ? 'bg-green-500/10 text-green-500' : 'bg-zinc-800 text-zinc-500'"
>
<ShieldCheck :size="20" />
</div>
<div>
<h3 class="font-bold text-text-primary">{{ locale.title }}</h3>
<p class="text-xs text-text-tertiary">
{{ isEnabled ? locale.enabledDesc : locale.disabledDesc }}
<h3 class="font-bold text-zinc-100">双重认证 (2FA)</h3>
<p class="text-xs text-zinc-500">
{{ isEnabled ? '您的账户已开启双重认证保护' : '未开启,建议开启以提升账户安全性' }}
</p>
</div>
</div>
@@ -20,36 +20,36 @@
<button
v-if="!isEnabled"
@click="startSetup"
class="px-4 py-2 bg-primary-hover hover:bg-primary text-text-primary text-sm font-bold rounded-lg transition-colors"
class="px-4 py-2 bg-blue-600 hover:bg-blue-500 text-white text-sm font-bold rounded-lg transition-colors"
>
{{ locale.enable }}
开启
</button>
<button
v-else
@click="confirmDisable"
class="px-4 py-2 bg-error-10 hover:bg-error-20 text-error border border-error-20 text-sm font-bold rounded-lg transition-colors"
class="px-4 py-2 bg-red-500/10 hover:bg-red-500/20 text-red-500 border border-red-500/20 text-sm font-bold rounded-lg transition-colors"
>
{{ locale.disable }}
关闭
</button>
</div>
<!-- 开启流程 -->
<div v-if="showSetup" class="bg-bg-primary-30 rounded-xl border border-border-secondary p-6 space-y-6 animate-in fade-in slide-in-from-top-4">
<div v-if="showSetup" class="bg-zinc-950/30 rounded-xl border border-zinc-800 p-6 space-y-6 animate-in fade-in slide-in-from-top-4">
<div class="flex items-start justify-between">
<div>
<h4 class="font-bold text-text-primary mb-1">{{ locale.setupTitle }}</h4>
<p class="text-sm text-text-tertiary">{{ locale.setupDesc }}</p>
<h4 class="font-bold text-zinc-100 mb-1">设置双重认证</h4>
<p class="text-sm text-zinc-400">请使用 Google Authenticator 或其他验证器应用扫描下方二维码</p>
</div>
<button @click="cancelSetup" class="text-text-tertiary hover:text-text-secondary">
<button @click="cancelSetup" class="text-zinc-500 hover:text-zinc-300">
<X :size="20" />
</button>
</div>
<div class="flex flex-col md:flex-row gap-8 items-center md:items-start">
<!-- 二维码区域 -->
<div class="flex-shrink-0 bg-bg-secondary p-2 rounded-lg">
<div class="flex-shrink-0 bg-white p-2 rounded-lg">
<img v-if="qrCodeUrl" :src="qrCodeUrl" alt="2FA QR Code" class="w-48 h-48" />
<div v-else class="w-48 h-48 flex items-center justify-center bg-bg-secondary text-text-tertiary">
<div v-else class="w-48 h-48 flex items-center justify-center bg-zinc-100 text-zinc-400">
<Loader2 class="animate-spin" />
</div>
</div>
@@ -57,17 +57,17 @@
<!-- 验证输入区域 -->
<div class="flex-1 space-y-4 w-full">
<div>
<label class="block text-xs font-bold text-text-tertiary uppercase tracking-wider mb-2">
{{ locale.manualKey }}
<label class="block text-xs font-bold text-zinc-500 uppercase tracking-wider mb-2">
手动输入密钥
</label>
<div class="flex items-center gap-2">
<code class="px-3 py-2 bg-bg-secondary rounded-lg text-text-secondary font-mono text-sm border border-border-secondary select-all">
<code class="px-3 py-2 bg-zinc-900 rounded-lg text-zinc-300 font-mono text-sm border border-zinc-800 select-all">
{{ secret }}
</code>
<button
@click="copySecret"
class="p-2 hover:bg-bg-tertiary rounded-lg text-text-tertiary transition-colors"
:title="locale.copyKey"
class="p-2 hover:bg-zinc-800 rounded-lg text-zinc-400 transition-colors"
title="复制密钥"
>
<Copy :size="16" />
</button>
@@ -75,25 +75,25 @@
</div>
<div>
<label class="block text-xs font-bold text-text-tertiary uppercase tracking-wider mb-2">
{{ locale.code }}
<label class="block text-xs font-bold text-zinc-500 uppercase tracking-wider mb-2">
验证码
</label>
<div class="flex gap-2">
<input
v-model="verificationCode"
type="text"
:placeholder="locale.codePlaceholder"
placeholder="请输入6位验证码"
maxlength="6"
class="flex-1 bg-bg-secondary border border-border-secondary rounded-lg px-4 py-2 text-text-primary placeholder-text-disabled focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all font-mono text-center tracking-widest"
class="flex-1 bg-zinc-900 border border-zinc-800 rounded-lg px-4 py-2 text-zinc-100 placeholder-zinc-600 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all font-mono text-center tracking-widest"
@keyup.enter="enable2FA"
/>
<button
@click="enable2FA"
:disabled="loading || verificationCode.length !== 6"
class="px-6 py-2 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 gap-2"
class="px-6 py-2 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 gap-2"
>
<Loader2 v-if="loading" class="animate-spin" :size="16" />
<span>{{ locale.verifyAndEnable }}</span>
<span>验证并开启</span>
</button>
</div>
</div>
@@ -103,12 +103,12 @@
<UIConfirmDialog
:show="showDisableConfirm"
:title="locale.disableTitle"
:message="locale.disableContent"
:confirm-text="locale.verifyAndDisable"
type="danger"
title="关闭双重认证"
content="关闭后,您的账户将不再受到双重认证保护。为确保安全,请输入您的登录密码以确认。"
confirm-text="验证并关闭"
confirm-type="danger"
:show-input="true"
:input-placeholder="locale.passwordPlaceholder"
input-placeholder="请输入当前登录密码"
input-type="password"
@confirm="disable2FA"
@cancel="showDisableConfirm = false"
@@ -116,18 +116,14 @@
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
<script setup lang="ts">
import { ref } from 'vue'
import { ShieldCheck, X, Copy, Loader2 } from '@lucide/vue'
import { useToast } from '~/composables/useToast'
import { useLocale } from '~/utils/locale'
const props = defineProps({
initialEnabled: {
type: Boolean,
default: false
}
})
const props = defineProps<{
initialEnabled?: boolean
}>()
const isEnabled = ref(props.initialEnabled || false)
const showSetup = ref(false)
@@ -137,8 +133,6 @@ const qrCodeUrl = ref('')
const secret = ref('')
const verificationCode = ref('')
const { showToast } = useToast()
const { auth } = useLocale()
const locale = computed(() => auth.value?.twoFactorSetup || {})
// 获取当前状态(可选,如果父组件没传可以通过API获取,这里暂时假设父组件会传或者初始化为false,
// 实际生产中最好有一个 API check status,但为了简化,这里先依赖父组件或默认)
@@ -161,8 +155,8 @@ const startSetup = async () => {
showSetup.value = true
verificationCode.value = ''
}
} catch (err) {
showToast(err.data?.message || err.message || locale.value.fetchFailed, 'error')
} catch (err: any) {
showToast(err.data?.message || err.message || '获取验证码失败', 'error')
} finally {
loading.value = false
}
@@ -178,9 +172,9 @@ const cancelSetup = () => {
const copySecret = async () => {
try {
await navigator.clipboard.writeText(secret.value)
showToast(locale.value.copied, 'success')
showToast('密钥已复制', 'success')
} catch (err) {
showToast(locale.value.copyFailed, 'error')
showToast('复制失败', 'error')
}
}
@@ -199,11 +193,11 @@ const enable2FA = async () => {
if (error.value) throw error.value
showToast(locale.value.enabled, 'success')
showToast('双重认证已开启', 'success')
isEnabled.value = true
cancelSetup()
} catch (err) {
showToast(err.data?.message || err.message || locale.value.verifyFailed, 'error')
} catch (err: any) {
showToast(err.data?.message || err.message || '验证失败', 'error')
} finally {
loading.value = false
}
@@ -213,9 +207,9 @@ const confirmDisable = () => {
showDisableConfirm.value = true
}
const disable2FA = async (password) => {
const disable2FA = async (password: string) => {
if (!password) {
showToast(locale.value.passwordRequired, 'warning')
showToast('请输入密码', 'warning')
return
}
@@ -228,11 +222,11 @@ const disable2FA = async (password) => {
if (error.value) throw error.value
showToast(locale.value.disabled, 'success')
showToast('双重认证已关闭', 'success')
isEnabled.value = false
showDisableConfirm.value = false
} catch (err) {
showToast(err.data?.message || err.message || locale.value.disableFailed, 'error')
} catch (err: any) {
showToast(err.data?.message || err.message || '关闭失败', 'error')
} finally {
loading.value = false
}