细节优化

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>
<div class="grid grid-cols-2 gap-4 md:flex md:flex-wrap">
<a
v-for="(item, index) in downloadLinks"
:key="index"
: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>
<div class="mx-auto grid grid-cols-2 gap-4 md:flex md:flex-wrap">
<template v-for="(item, index) in downloadLinks" :key="index">
<router-link
v-if="item.isInternal"
:to="item.link"
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>
</router-link>
<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>
</template>
@ -38,7 +63,7 @@ import Icon4 from './Group 108.svg?component'
//
const downloadLinks = [
{ 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: Icon4, link: 'https://down.hi.com/mac' },
]

View File

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

View File

@ -30,7 +30,16 @@
<!-- Mobile Logo -->
<MobileLogo alt="Hi快VPN" class="ml-6 block h-[28px] w-[67px] md:hidden" />
</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
v-else
@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"
>
@ -106,8 +115,9 @@
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { ref, onMounted, watch, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useLocalStorage } from '@vueuse/core'
import type { LocationQueryValue } from 'vue-router'
import LoginFormModal from './components/LoginFormModal.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 ScreenshotMobile from './screenshot-mobile.png'
import ScreenshotDesktop from './screenshot-desktop.png'
import request from '@/utils/request'
const route = useRoute()
const router = useRouter()
const loginModalRef = ref<InstanceType<typeof LoginFormModal> | null>(null)
const token = useLocalStorage('Authorization', '')
const userEmail = useLocalStorage('UserEmail', '')
const openLoginModal = () => {
loginModalRef.value?.show()
const isLoggedIn = computed(() => !!token.value)
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(() => {
fetchUserInfo()
if (route.query.login === 'true') {
openLoginModal()
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>

View File

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