细节优化

This commit is contained in:
speakeloudest 2025-12-31 07:26:24 -08:00
parent 107d950771
commit ecd6cf1c94
4 changed files with 98 additions and 32 deletions

View File

@ -1,31 +1,56 @@
<template> <template>
<div class="grid grid-cols-2 gap-4 md:flex md:flex-wrap"> <div class="mx-auto grid grid-cols-2 gap-4 md:flex md:flex-wrap">
<a <template v-for="(item, index) in downloadLinks" :key="index">
v-for="(item, index) in downloadLinks" <router-link
:key="index" v-if="item.isInternal"
:href="item.link" :to="item.link"
target="_blank" class="flex h-[40px] w-[140px] shrink-0 items-center space-x-2 rounded-full transition-transform active:scale-95 md:h-[50px] md:w-[180px]"
class="flex h-[40px] w-[140px] shrink-0 items-center space-x-2 rounded-full transition-transform active:scale-95 md:h-[50px] md:w-[180px]" style="
style=" backdrop-filter: blur(36px);
backdrop-filter: blur(36px); box-shadow:
box-shadow: 0px 0px 33px 0px #f2f2f280 inset,
0px 0px 33px 0px #f2f2f280 inset, -3px -4.5px 1.5px -3px #b3b3b3 inset,
-3px -4.5px 1.5px -3px #b3b3b3 inset, 3px 4.5px 1.5px -3px #b3b3b333 inset,
3px 4.5px 1.5px -3px #b3b3b333 inset, 4.5px 4.5px 1.5px -5.25px #ffffff80 inset,
4.5px 4.5px 1.5px -5.25px #ffffff80 inset, -4.5px -4.5px 1.5px -5.25px #ffffff80 inset;
-4.5px -4.5px 1.5px -5.25px #ffffff80 inset; border-image-source: linear-gradient(
border-image-source: linear-gradient( 135deg,
135deg, rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0) 30%
rgba(255, 255, 255, 0) 30% );
); border-image-slice: 1;
border-image-slice: 1; "
" >
> <div class="flex items-center justify-center">
<div class="flex items-center justify-center"> <component :is="item.icon" class="h-[40px] w-[140px] md:h-[50px] md:w-[180px]" />
<component :is="item.icon" class="h-[40px] w-[140px] md:h-[50px] md:w-[180px]" /> </div>
</div> </router-link>
</a> <a
v-else
:href="item.link"
target="_blank"
class="flex h-[40px] w-[140px] shrink-0 items-center space-x-2 rounded-full transition-transform active:scale-95 md:h-[50px] md:w-[180px]"
style="
backdrop-filter: blur(36px);
box-shadow:
0px 0px 33px 0px #f2f2f280 inset,
-3px -4.5px 1.5px -3px #b3b3b3 inset,
3px 4.5px 1.5px -3px #b3b3b333 inset,
4.5px 4.5px 1.5px -5.25px #ffffff80 inset,
-4.5px -4.5px 1.5px -5.25px #ffffff80 inset;
border-image-source: linear-gradient(
135deg,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0) 30%
);
border-image-slice: 1;
"
>
<div class="flex items-center justify-center">
<component :is="item.icon" class="h-[40px] w-[140px] md:h-[50px] md:w-[180px]" />
</div>
</a>
</template>
</div> </div>
</template> </template>
@ -38,7 +63,7 @@ import Icon4 from './Group 108.svg?component'
// //
const downloadLinks = [ const downloadLinks = [
{ icon: Icon1, link: 'https://apple.com/...' }, { icon: Icon1, link: 'https://apple.com/...' },
{ icon: Icon2, link: 'https://down.hi.com/win' }, { icon: Icon2, link: '/help', isInternal: true },
{ icon: Icon3, link: 'https://down.hi.com/apk' }, { icon: Icon3, link: 'https://down.hi.com/apk' },
{ icon: Icon4, link: 'https://down.hi.com/mac' }, { icon: Icon4, link: 'https://down.hi.com/mac' },
] ]

View File

@ -102,6 +102,7 @@ const handleLogin = () => {
}) })
.then((res) => { .then((res) => {
localStorage.setItem('Authorization', res.token) localStorage.setItem('Authorization', res.token)
localStorage.setItem('UserEmail', email.value)
router.push({ path: '/user-center' }) router.push({ path: '/user-center' })
}) })
} }

View File

@ -30,7 +30,16 @@
<!-- Mobile Logo --> <!-- Mobile Logo -->
<MobileLogo alt="Hi快VPN" class="ml-6 block h-[28px] w-[67px] md:hidden" /> <MobileLogo alt="Hi快VPN" class="ml-6 block h-[28px] w-[67px] md:hidden" />
</div> </div>
<div v-if="isLoggedIn" class="flex items-center">
<router-link
to="/user-center"
class="mr-2 flex size-[48px] items-center justify-center rounded-full bg-[#A8FF53] text-xl font-bold text-black shadow-lg transition hover:scale-105 md:size-[60px] md:text-3xl"
>
{{ userLetter }}
</router-link>
</div>
<button <button
v-else
@click="openLoginModal" @click="openLoginModal"
class="flex h-[48px] items-center rounded-full bg-[#70C877] px-6 text-sm font-bold backdrop-blur-md transition hover:scale-[0.97] hover:bg-white/30 md:text-2xl" class="flex h-[48px] items-center rounded-full bg-[#70C877] px-6 text-sm font-bold backdrop-blur-md transition hover:scale-[0.97] hover:bg-white/30 md:text-2xl"
> >
@ -106,8 +115,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch } from 'vue' import { ref, onMounted, watch, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { useLocalStorage } from '@vueuse/core'
import type { LocationQueryValue } from 'vue-router' import type { LocationQueryValue } from 'vue-router'
import LoginFormModal from './components/LoginFormModal.vue' import LoginFormModal from './components/LoginFormModal.vue'
import DownloadButton from './components/DownloadButton.vue' import DownloadButton from './components/DownloadButton.vue'
@ -115,16 +125,39 @@ import Logo from './logo.svg?component'
import MobileLogo from './mobile-logo.svg?component' import MobileLogo from './mobile-logo.svg?component'
import ScreenshotMobile from './screenshot-mobile.png' import ScreenshotMobile from './screenshot-mobile.png'
import ScreenshotDesktop from './screenshot-desktop.png' import ScreenshotDesktop from './screenshot-desktop.png'
import request from '@/utils/request'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const loginModalRef = ref<InstanceType<typeof LoginFormModal> | null>(null) const token = useLocalStorage('Authorization', '')
const userEmail = useLocalStorage('UserEmail', '')
const openLoginModal = () => { const isLoggedIn = computed(() => !!token.value)
loginModalRef.value?.show() const userLetter = computed(() => {
if (!userEmail.value) return '?'
return userEmail.value.charAt(0).toUpperCase()
})
const fetchUserInfo = async () => {
if (!token.value) return
try {
const res = (await request.get('/api/v1/public/user/info')) as any
const emailInfo = res.auth_methods?.find((item: any) => item.auth_type === 'email')
if (emailInfo) {
userEmail.value = emailInfo.auth_identifier
}
} catch (error: any) {
console.error('Failed to fetch user info:', error)
if (error?.code === 401 || error?.status === 401) {
token.value = ''
userEmail.value = ''
}
}
} }
onMounted(() => { onMounted(() => {
fetchUserInfo()
if (route.query.login === 'true') { if (route.query.login === 'true') {
openLoginModal() openLoginModal()
router.replace({ query: { ...route.query, login: undefined } }) router.replace({ query: { ...route.query, login: undefined } })
@ -140,4 +173,10 @@ watch(
} }
}, },
) )
const loginModalRef = ref<InstanceType<typeof LoginFormModal> | null>(null)
const openLoginModal = () => {
loginModalRef.value?.show()
}
</script> </script>

View File

@ -202,6 +202,7 @@ async function init() {
const emailInfo = res.auth_methods?.find((item: any) => item.auth_type === 'email') const emailInfo = res.auth_methods?.find((item: any) => item.auth_type === 'email')
if (emailInfo) { if (emailInfo) {
userSubInfo.value.email = emailInfo.auth_identifier userSubInfo.value.email = emailInfo.auth_identifier
localStorage.setItem('UserEmail', emailInfo.auth_identifier)
} }
}) })
.finally(() => { .finally(() => {