feat: 迁移自定义功能至 1.5.9.9 版本
包含音乐源限制、CI 防 OOM 优化、数据库连接池优化、管理端权限及 UI 细节定制等功能
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user