remove: 移除官方 GitHub 链接
Some checks failed
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled

- 删除顶部导航 GitHub 链接
- 删除首页页脚 GitHub 链接
- 清理相关变量定义
This commit is contained in:
huangzhenpc
2026-01-04 17:15:57 +08:00
parent f7a5cee262
commit e27c1acf79
2 changed files with 1045 additions and 1073 deletions

View File

@@ -1,351 +1,334 @@
<template> <template>
<header class="glass sticky top-0 z-30 border-b border-gray-200/50 dark:border-dark-700/50"> <header class="glass sticky top-0 z-30 border-b border-gray-200/50 dark:border-dark-700/50">
<div class="flex h-16 items-center justify-between px-4 md:px-6"> <div class="flex h-16 items-center justify-between px-4 md:px-6">
<!-- Left: Mobile Menu Toggle + Page Title --> <!-- Left: Mobile Menu Toggle + Page Title -->
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<button <button
@click="toggleMobileSidebar" @click="toggleMobileSidebar"
class="btn-ghost btn-icon lg:hidden" class="btn-ghost btn-icon lg:hidden"
aria-label="Toggle Menu" aria-label="Toggle Menu"
> >
<svg <svg
class="h-5 w-5" class="h-5 w-5"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"
> >
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
/> />
</svg> </svg>
</button> </button>
<div class="hidden lg:block"> <div class="hidden lg:block">
<h1 class="text-lg font-semibold text-gray-900 dark:text-white"> <h1 class="text-lg font-semibold text-gray-900 dark:text-white">
{{ pageTitle }} {{ pageTitle }}
</h1> </h1>
<p v-if="pageDescription" class="text-xs text-gray-500 dark:text-dark-400"> <p v-if="pageDescription" class="text-xs text-gray-500 dark:text-dark-400">
{{ pageDescription }} {{ pageDescription }}
</p> </p>
</div> </div>
</div> </div>
<!-- Right: Language + Subscriptions + Balance + User Dropdown --> <!-- Right: Language + Subscriptions + Balance + User Dropdown -->
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<!-- Language Switcher --> <!-- Language Switcher -->
<LocaleSwitcher /> <LocaleSwitcher />
<!-- Subscription Progress (for users with active subscriptions) --> <!-- Subscription Progress (for users with active subscriptions) -->
<SubscriptionProgressMini v-if="user" /> <SubscriptionProgressMini v-if="user" />
<!-- Balance Display --> <!-- Balance Display -->
<div <div
v-if="user" v-if="user"
class="hidden items-center gap-2 rounded-xl bg-primary-50 px-3 py-1.5 dark:bg-primary-900/20 sm:flex" class="hidden items-center gap-2 rounded-xl bg-primary-50 px-3 py-1.5 dark:bg-primary-900/20 sm:flex"
> >
<svg <svg
class="h-4 w-4 text-primary-600 dark:text-primary-400" class="h-4 w-4 text-primary-600 dark:text-primary-400"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"
> >
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z" d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z"
/> />
</svg> </svg>
<span class="text-sm font-semibold text-primary-700 dark:text-primary-300"> <span class="text-sm font-semibold text-primary-700 dark:text-primary-300">
${{ user.balance?.toFixed(2) || '0.00' }} ${{ user.balance?.toFixed(2) || '0.00' }}
</span> </span>
</div> </div>
<!-- User Dropdown --> <!-- User Dropdown -->
<div v-if="user" class="relative" ref="dropdownRef"> <div v-if="user" class="relative" ref="dropdownRef">
<button <button
@click="toggleDropdown" @click="toggleDropdown"
class="flex items-center gap-2 rounded-xl p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-dark-800" class="flex items-center gap-2 rounded-xl p-1.5 transition-colors hover:bg-gray-100 dark:hover:bg-dark-800"
aria-label="User Menu" aria-label="User Menu"
> >
<div <div
class="flex h-8 w-8 items-center justify-center rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 text-sm font-medium text-white shadow-sm" class="flex h-8 w-8 items-center justify-center rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 text-sm font-medium text-white shadow-sm"
> >
{{ userInitials }} {{ userInitials }}
</div> </div>
<div class="hidden text-left md:block"> <div class="hidden text-left md:block">
<div class="text-sm font-medium text-gray-900 dark:text-white"> <div class="text-sm font-medium text-gray-900 dark:text-white">
{{ displayName }} {{ displayName }}
</div> </div>
<div class="text-xs capitalize text-gray-500 dark:text-dark-400"> <div class="text-xs capitalize text-gray-500 dark:text-dark-400">
{{ user.role }} {{ user.role }}
</div> </div>
</div> </div>
<svg <svg
class="hidden h-4 w-4 text-gray-400 md:block" class="hidden h-4 w-4 text-gray-400 md:block"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"
> >
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="M19.5 8.25l-7.5 7.5-7.5-7.5" d="M19.5 8.25l-7.5 7.5-7.5-7.5"
/> />
</svg> </svg>
</button> </button>
<!-- Dropdown Menu --> <!-- Dropdown Menu -->
<transition name="dropdown"> <transition name="dropdown">
<div v-if="dropdownOpen" class="dropdown right-0 mt-2 w-56"> <div v-if="dropdownOpen" class="dropdown right-0 mt-2 w-56">
<!-- User Info --> <!-- User Info -->
<div class="border-b border-gray-100 px-4 py-3 dark:border-dark-700"> <div class="border-b border-gray-100 px-4 py-3 dark:border-dark-700">
<div class="text-sm font-medium text-gray-900 dark:text-white"> <div class="text-sm font-medium text-gray-900 dark:text-white">
{{ displayName }} {{ displayName }}
</div> </div>
<div class="text-xs text-gray-500 dark:text-dark-400">{{ user.email }}</div> <div class="text-xs text-gray-500 dark:text-dark-400">{{ user.email }}</div>
</div> </div>
<!-- Balance (mobile only) --> <!-- Balance (mobile only) -->
<div class="border-b border-gray-100 px-4 py-2 dark:border-dark-700 sm:hidden"> <div class="border-b border-gray-100 px-4 py-2 dark:border-dark-700 sm:hidden">
<div class="text-xs text-gray-500 dark:text-dark-400"> <div class="text-xs text-gray-500 dark:text-dark-400">
{{ t('common.balance') }} {{ t('common.balance') }}
</div> </div>
<div class="text-sm font-semibold text-primary-600 dark:text-primary-400"> <div class="text-sm font-semibold text-primary-600 dark:text-primary-400">
${{ user.balance?.toFixed(2) || '0.00' }} ${{ user.balance?.toFixed(2) || '0.00' }}
</div> </div>
</div> </div>
<div class="py-1"> <div class="py-1">
<router-link to="/profile" @click="closeDropdown" class="dropdown-item"> <router-link to="/profile" @click="closeDropdown" class="dropdown-item">
<svg <svg
class="h-4 w-4" class="h-4 w-4"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"
> >
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
/> />
</svg> </svg>
{{ t('nav.profile') }} {{ t('nav.profile') }}
</router-link> </router-link>
<router-link to="/keys" @click="closeDropdown" class="dropdown-item"> <router-link to="/keys" @click="closeDropdown" class="dropdown-item">
<svg <svg
class="h-4 w-4" class="h-4 w-4"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
stroke-width="1.5" stroke-width="1.5"
> >
<path <path
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
/> />
</svg> </svg>
{{ t('nav.apiKeys') }} {{ t('nav.apiKeys') }}
</router-link> </router-link>
</div>
<a
href="https://github.com/Wei-Shaw/sub2api" <!-- Contact Support (only show if configured) -->
target="_blank" <div
rel="noopener noreferrer" v-if="contactInfo"
@click="closeDropdown" class="border-t border-gray-100 px-4 py-2.5 dark:border-dark-700"
class="dropdown-item" >
> <div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"> <svg
<path class="h-3.5 w-3.5 flex-shrink-0"
fill-rule="evenodd" fill="none"
clip-rule="evenodd" viewBox="0 0 24 24"
d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z" stroke="currentColor"
/> stroke-width="1.5"
</svg> >
{{ t('nav.github') }} <path
</a> stroke-linecap="round"
</div> stroke-linejoin="round"
d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 01-.825-.242m9.345-8.334a2.126 2.126 0 00-.476-.095 48.64 48.64 0 00-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0011.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"
<!-- Contact Support (only show if configured) --> />
<div </svg>
v-if="contactInfo" <span>{{ t('common.contactSupport') }}:</span>
class="border-t border-gray-100 px-4 py-2.5 dark:border-dark-700" <span class="font-medium text-gray-700 dark:text-gray-300">{{
> contactInfo
<div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400"> }}</span>
<svg </div>
class="h-3.5 w-3.5 flex-shrink-0" </div>
fill="none"
viewBox="0 0 24 24" <div v-if="showOnboardingButton" class="border-t border-gray-100 py-1 dark:border-dark-700">
stroke="currentColor" <button @click="handleReplayGuide" class="dropdown-item w-full">
stroke-width="1.5" <svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
> <path
<path d="M12 2a10 10 0 100 20 10 10 0 000-20zm0 14a1 1 0 110 2 1 1 0 010-2zm1.07-7.75c0-.6-.49-1.25-1.32-1.25-.7 0-1.22.4-1.43 1.02a1 1 0 11-1.9-.62A3.41 3.41 0 0111.8 5c2.02 0 3.25 1.4 3.25 2.9 0 2-1.83 2.55-2.43 3.12-.43.4-.47.75-.47 1.23a1 1 0 01-2 0c0-1 .16-1.82 1.1-2.7.69-.64 1.82-1.05 1.82-2.06z"
stroke-linecap="round" />
stroke-linejoin="round" </svg>
d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 01-.825-.242m9.345-8.334a2.126 2.126 0 00-.476-.095 48.64 48.64 0 00-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0011.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155" {{ $t('onboarding.restartTour') }}
/> </button>
</svg> </div>
<span>{{ t('common.contactSupport') }}:</span>
<span class="font-medium text-gray-700 dark:text-gray-300">{{ <div class="border-t border-gray-100 py-1 dark:border-dark-700">
contactInfo <button
}}</span> @click="handleLogout"
</div> class="dropdown-item w-full text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
</div> >
<svg
<div v-if="showOnboardingButton" class="border-t border-gray-100 py-1 dark:border-dark-700"> class="h-4 w-4"
<button @click="handleReplayGuide" class="dropdown-item w-full"> fill="none"
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"> viewBox="0 0 24 24"
<path stroke="currentColor"
d="M12 2a10 10 0 100 20 10 10 0 000-20zm0 14a1 1 0 110 2 1 1 0 010-2zm1.07-7.75c0-.6-.49-1.25-1.32-1.25-.7 0-1.22.4-1.43 1.02a1 1 0 11-1.9-.62A3.41 3.41 0 0111.8 5c2.02 0 3.25 1.4 3.25 2.9 0 2-1.83 2.55-2.43 3.12-.43.4-.47.75-.47 1.23a1 1 0 01-2 0c0-1 .16-1.82 1.1-2.7.69-.64 1.82-1.05 1.82-2.06z" stroke-width="1.5"
/> >
</svg> <path
{{ $t('onboarding.restartTour') }} stroke-linecap="round"
</button> stroke-linejoin="round"
</div> d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75"
/>
<div class="border-t border-gray-100 py-1 dark:border-dark-700"> </svg>
<button {{ t('nav.logout') }}
@click="handleLogout" </button>
class="dropdown-item w-full text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20" </div>
> </div>
<svg </transition>
class="h-4 w-4" </div>
fill="none" </div>
viewBox="0 0 24 24" </div>
stroke="currentColor" </header>
stroke-width="1.5" </template>
>
<path <script setup lang="ts">
stroke-linecap="round" import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
stroke-linejoin="round" import { useRouter, useRoute } from 'vue-router'
d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" import { useI18n } from 'vue-i18n'
/> import { useAppStore, useAuthStore, useOnboardingStore } from '@/stores'
</svg> import LocaleSwitcher from '@/components/common/LocaleSwitcher.vue'
{{ t('nav.logout') }} import SubscriptionProgressMini from '@/components/common/SubscriptionProgressMini.vue'
</button>
</div> const router = useRouter()
</div> const route = useRoute()
</transition> const { t } = useI18n()
</div> const appStore = useAppStore()
</div> const authStore = useAuthStore()
</div> const onboardingStore = useOnboardingStore()
</header>
</template> const user = computed(() => authStore.user)
const dropdownOpen = ref(false)
<script setup lang="ts"> const dropdownRef = ref<HTMLElement | null>(null)
import { ref, computed, onMounted, onBeforeUnmount } from 'vue' const contactInfo = computed(() => appStore.contactInfo)
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n' // 只在标准模式的管理员下显示新手引导按钮
import { useAppStore, useAuthStore, useOnboardingStore } from '@/stores' const showOnboardingButton = computed(() => {
import LocaleSwitcher from '@/components/common/LocaleSwitcher.vue' return !authStore.isSimpleMode && user.value?.role === 'admin'
import SubscriptionProgressMini from '@/components/common/SubscriptionProgressMini.vue' })
const router = useRouter() const userInitials = computed(() => {
const route = useRoute() if (!user.value) return ''
const { t } = useI18n() // Prefer username, fallback to email
const appStore = useAppStore() if (user.value.username) {
const authStore = useAuthStore() return user.value.username.substring(0, 2).toUpperCase()
const onboardingStore = useOnboardingStore() }
if (user.value.email) {
const user = computed(() => authStore.user) // Get the part before @ and take first 2 chars
const dropdownOpen = ref(false) const localPart = user.value.email.split('@')[0]
const dropdownRef = ref<HTMLElement | null>(null) return localPart.substring(0, 2).toUpperCase()
const contactInfo = computed(() => appStore.contactInfo) }
return ''
// 只在标准模式的管理员下显示新手引导按钮 })
const showOnboardingButton = computed(() => {
return !authStore.isSimpleMode && user.value?.role === 'admin' const displayName = computed(() => {
}) if (!user.value) return ''
return user.value.username || user.value.email?.split('@')[0] || ''
const userInitials = computed(() => { })
if (!user.value) return ''
// Prefer username, fallback to email const pageTitle = computed(() => {
if (user.value.username) { const titleKey = route.meta.titleKey as string
return user.value.username.substring(0, 2).toUpperCase() if (titleKey) {
} return t(titleKey)
if (user.value.email) { }
// Get the part before @ and take first 2 chars return (route.meta.title as string) || ''
const localPart = user.value.email.split('@')[0] })
return localPart.substring(0, 2).toUpperCase()
} const pageDescription = computed(() => {
return '' const descKey = route.meta.descriptionKey as string
}) if (descKey) {
return t(descKey)
const displayName = computed(() => { }
if (!user.value) return '' return (route.meta.description as string) || ''
return user.value.username || user.value.email?.split('@')[0] || '' })
})
function toggleMobileSidebar() {
const pageTitle = computed(() => { appStore.toggleMobileSidebar()
const titleKey = route.meta.titleKey as string }
if (titleKey) {
return t(titleKey) function toggleDropdown() {
} dropdownOpen.value = !dropdownOpen.value
return (route.meta.title as string) || '' }
})
function closeDropdown() {
const pageDescription = computed(() => { dropdownOpen.value = false
const descKey = route.meta.descriptionKey as string }
if (descKey) {
return t(descKey) async function handleLogout() {
} closeDropdown()
return (route.meta.description as string) || '' authStore.logout()
}) await router.push('/login')
}
function toggleMobileSidebar() {
appStore.toggleMobileSidebar() function handleReplayGuide() {
} closeDropdown()
onboardingStore.replay()
function toggleDropdown() { }
dropdownOpen.value = !dropdownOpen.value
} function handleClickOutside(event: MouseEvent) {
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
function closeDropdown() { closeDropdown()
dropdownOpen.value = false }
} }
async function handleLogout() { onMounted(() => {
closeDropdown() document.addEventListener('click', handleClickOutside)
authStore.logout() })
await router.push('/login')
} onBeforeUnmount(() => {
document.removeEventListener('click', handleClickOutside)
function handleReplayGuide() { })
closeDropdown() </script>
onboardingStore.replay()
} <style scoped>
.dropdown-enter-active,
function handleClickOutside(event: MouseEvent) { .dropdown-leave-active {
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) { transition: all 0.2s ease;
closeDropdown() }
}
} .dropdown-enter-from,
.dropdown-leave-to {
onMounted(() => { opacity: 0;
document.addEventListener('click', handleClickOutside) transform: scale(0.95) translateY(-4px);
}) }
</style>
onBeforeUnmount(() => {
document.removeEventListener('click', handleClickOutside)
})
</script>
<style scoped>
.dropdown-enter-active,
.dropdown-leave-active {
transition: all 0.2s ease;
}
.dropdown-enter-from,
.dropdown-leave-to {
opacity: 0;
transform: scale(0.95) translateY(-4px);
}
</style>

File diff suppressed because it is too large Load Diff