142 lines
4.8 KiB
Vue
142 lines
4.8 KiB
Vue
<template>
|
||
<header class="bg-white shadow-sm border-b border-gray-200">
|
||
<div class="container-custom">
|
||
<div class="flex items-center justify-between h-16">
|
||
<!-- Logo -->
|
||
<NuxtLink to="/" class="flex items-center space-x-2">
|
||
<div class="w-8 h-8 bg-aws-orange rounded-lg flex items-center justify-center">
|
||
<span class="text-white font-bold text-lg">C</span>
|
||
</div>
|
||
<span class="text-xl font-bold text-gray-900">CloudBridge</span>
|
||
</NuxtLink>
|
||
|
||
<!-- Desktop Navigation -->
|
||
<nav class="hidden md:flex items-center space-x-8">
|
||
<NuxtLink
|
||
v-for="item in navItems"
|
||
:key="item.key"
|
||
:to="item.to"
|
||
class="text-gray-700 hover:text-aws-orange font-medium transition-colors duration-200"
|
||
>
|
||
{{ item.label }}
|
||
</NuxtLink>
|
||
</nav>
|
||
|
||
<!-- Right side actions -->
|
||
<div class="flex items-center space-x-4">
|
||
<!-- Language Switcher -->
|
||
<div class="relative">
|
||
<select
|
||
v-model="currentLocale"
|
||
@change="switchLanguage"
|
||
class="appearance-none bg-white border border-gray-300 rounded-md px-3 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-aws-orange focus:border-transparent"
|
||
>
|
||
<option v-for="locale in locales" :key="locale.code" :value="locale.code">
|
||
{{ locale.name }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Social Links -->
|
||
<div class="flex items-center space-x-3">
|
||
<a
|
||
href="https://t.me/pinnovatecloud"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="text-gray-600 hover:text-aws-orange transition-colors duration-200"
|
||
:title="$t('contact.telegram')"
|
||
>
|
||
<MessageCircle class="w-5 h-5" />
|
||
</a>
|
||
<a
|
||
href="https://wa.me/19174029875"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="text-gray-600 hover:text-aws-orange transition-colors duration-200"
|
||
:title="$t('contact.whatsapp')"
|
||
>
|
||
<Phone class="w-5 h-5" />
|
||
</a>
|
||
<span class="text-sm text-gray-500">+1 9174029875</span>
|
||
</div>
|
||
|
||
<!-- Mobile menu button -->
|
||
<button
|
||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||
class="md:hidden p-2 rounded-md text-gray-600 hover:text-aws-orange hover:bg-gray-100"
|
||
>
|
||
<Menu v-if="!mobileMenuOpen" class="w-6 h-6" />
|
||
<X v-else class="w-6 h-6" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Mobile Navigation -->
|
||
<div v-if="mobileMenuOpen" class="md:hidden border-t border-gray-200 py-4">
|
||
<nav class="flex flex-col space-y-4">
|
||
<NuxtLink
|
||
v-for="item in navItems"
|
||
:key="item.key"
|
||
:to="item.to"
|
||
@click="mobileMenuOpen = false"
|
||
class="text-gray-700 hover:text-aws-orange font-medium py-2"
|
||
>
|
||
{{ item.label }}
|
||
</NuxtLink>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { MessageCircle, Phone, Menu, X } from 'lucide-vue-next'
|
||
|
||
const { t, locale, locales, setLocale } = useI18n()
|
||
const localePath = useLocalePath()
|
||
const switchLocalePath = useSwitchLocalePath()
|
||
const router = useRouter()
|
||
const mobileMenuOpen = ref(false)
|
||
|
||
const currentLocale = ref(locale.value)
|
||
|
||
const navItems = computed(() => [
|
||
{ key: 'home', label: t('nav.home'), to: localePath('/') },
|
||
{ key: 'services', label: t('nav.services'), to: localePath('/services') },
|
||
{ key: 'pricing', label: t('nav.pricing'), to: localePath('/pricing') },
|
||
{ key: 'blog', label: t('nav.blog'), to: localePath('/blog') },
|
||
{ key: 'contact', label: t('nav.contact'), to: localePath('/contact') }
|
||
])
|
||
|
||
const route = useRoute()
|
||
|
||
const switchLanguage = async () => {
|
||
const target = currentLocale.value
|
||
let path = switchLocalePath(target)
|
||
|
||
// 强力回退:若未返回路径,则拼接目标语言前缀到当前非语言前缀路径
|
||
if (!path) {
|
||
const withoutPrefix = route.fullPath.replace(/^\/(en|zh|zh-hant)(?=\/|$)/, '') || '/'
|
||
path = target === 'en' ? withoutPrefix : `/${target}${withoutPrefix}`
|
||
}
|
||
|
||
// 规范化默认语言(en)不应包含 /en 前缀(strategy: prefix_except_default)
|
||
if (path && /^\/en(\/|$)/.test(path)) {
|
||
path = path.replace(/^\/en(\/|$)/, '/').replace(/\/$/, '') || '/'
|
||
}
|
||
|
||
await router.push(path)
|
||
mobileMenuOpen.value = false
|
||
}
|
||
|
||
// Keep select value in sync when locale changes externally (e.g., via links)
|
||
watch(locale, (val) => {
|
||
currentLocale.value = val
|
||
})
|
||
|
||
// Close mobile menu when route changes
|
||
watch(() => useRoute().path, () => {
|
||
mobileMenuOpen.value = false
|
||
})
|
||
</script>
|