website-client-Nigeria/src/components/top/title-bar.vue

719 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, reactive, onMounted, computed, nextTick, watch } from "vue";
import { useI18n } from "#imports";
import { useRoute, useRouter } from "#imports";
import { useNavigation } from "@/composables/useNavigation.js";
import { useLanguageSwitch } from "@/composables/useLanguageSwitch.js";
const { locale, t } = useI18n();
const route = useRoute();
const router = useRouter();
const { navigateTo, localePath } = useNavigation();
const { switchLanguage, initLanguage } = useLanguageSwitch();
// 语言显示
const langToShow = computed(() => {
switch (locale.value) {
case "en":
return "English";
case "cn":
return "中文";
default:
return "English";
}
});
// 语言列表
const langList = ref([
{ name: "简体中文", code: "cn", checked: false },
{ name: "English", code: "en", checked: false },
]);
// 菜单数据 - 使用计算属性自动响应语言变化
const menuItems = computed(() => [
{
name: t("nav.home"),
enName: "home",
path: "/",
active: false,
},
{
name: t("nav.about"),
enName: "about",
path: "/about-us",
active: false,
},
{
name: t("nav.products"),
enName: "products",
path: "/product",
active: false,
},
{
name: t("nav.contact"),
enName: "contact",
path: "/contact-us",
active: false,
},
]);
// 移动端菜单控制
const isMobileMenuOpen = ref(false);
const langPopup = ref(false);
// 语言切换下拉显示控制
const showLangDropdown = ref(false);
// 语言切换防抖
const isSwitching = ref(false);
// 语言切换
const chooseLanguage = async (lang) => {
if (lang.code === locale.value || isSwitching.value) {
showLangDropdown.value = false;
return;
}
try {
isSwitching.value = true;
// 使用新的语言切换方法,传递正确的参数
await switchLanguage(lang.code, router);
} catch (error) {
console.warn('Language switch failed:', error);
} finally {
isSwitching.value = false;
showLangDropdown.value = false;
}
};
// 菜单点击
const handleMenuClick = (item) => {
// 使用统一的导航方法
navigateTo(item.path);
isMobileMenuOpen.value = false;
};
// 设置当前活跃菜单
const setActiveMenu = () => {
const currentPath = route.path;
// 重置所有状态
const items = menuItems.value;
items.forEach((item) => {
item.active = false;
});
// 根据路径设置激活状态
if (currentPath === "/" || currentPath === "/cn") {
const homeItem = items.find((item) => item.enName === "home");
if (homeItem) {
homeItem.active = true;
}
} else if (currentPath.includes("/about-us")) {
const aboutItem = items.find((item) => item.enName === "about");
if (aboutItem) {
aboutItem.active = true;
}
} else if (currentPath.includes("/product")) {
const productItem = items.find((item) => item.enName === "products");
if (productItem) {
productItem.active = true;
}
} else if (currentPath.includes("/contact-us")) {
const contactItem = items.find((item) => item.enName === "contact");
if (contactItem) {
contactItem.active = true;
}
}
};
// 切换移动端菜单
const toggleMobileMenu = () => {
isMobileMenuOpen.value = !isMobileMenuOpen.value;
};
// 处理logo点击
const handleLogoClick = () => {
// 使用统一的导航方法跳转到首页
navigateTo("/");
isMobileMenuOpen.value = false;
};
onMounted(() => {
// 初始化语言
initLanguage();
// 更新语言列表选中状态
langList.value.forEach((item) => {
item.checked = item.code === locale.value;
});
// 确保在下一个 tick 中设置激活菜单
nextTick(() => {
setActiveMenu();
});
});
// 监听语言变化,更新语言列表选中状态
watch(() => locale.value, (newLocale) => {
langList.value.forEach((item) => {
item.checked = item.code === newLocale;
});
}, { immediate: true });
// 监听路由变化,更新激活状态
watch(
() => route.path,
(newPath) => {
setTimeout(() => {
setActiveMenu();
}, 50);
},
{ immediate: true }
);
</script>
<template>
<ClientOnly>
<header class="navbar">
<div class="navbar-container">
<!-- Logo -->
<div class="navbar-logo" @click="handleLogoClick">
<img src="/logo.png" alt="明阳良光" class="logo-image" />
</div>
<!-- 右侧区域菜单+语言切换 -->
<div class="navbar-right">
<!-- 桌面端菜单 -->
<nav class="navbar-menu desktop-menu">
<ul class="menu-list">
<li
v-for="item in menuItems"
:key="item.enName"
class="menu-item"
:class="{ active: item.active }"
@click="handleMenuClick(item)"
>
{{ item.name }}
</li>
</ul>
</nav>
<!-- 语言切换和移动端按钮 -->
<div class="navbar-actions">
<!-- 语言切换 -->
<div
class="language-container"
@mouseenter="showLangDropdown = true"
@mouseleave="showLangDropdown = false"
@click="showLangDropdown = !showLangDropdown"
>
<div class="language-selector">
<img
class="lang-earth-icon"
src="/static/home/earch.png"
:alt="langToShow"
:title="langToShow"
/>
<span class="lang-text">{{ langToShow }}</span>
<svg
class="lang-icon"
:class="{ rotate: showLangDropdown }"
width="12"
height="12"
viewBox="0 0 12 12"
>
<path
d="M2 4l4 4 4-4"
stroke="currentColor"
stroke-width="2"
fill="none"
/>
</svg>
</div>
<!-- 语言下拉菜单 -->
<div
class="lang-dropdown"
v-show="showLangDropdown"
@click.stop
>
<div
v-for="lang in langList"
:key="lang.code"
class="lang-option"
:class="{ active: lang.checked, disabled: isSwitching }"
@click="chooseLanguage(lang)"
>
{{ lang.name }}
<span v-if="isSwitching && lang.checked" class="loading-dot">...</span>
</div>
</div>
</div>
<!-- 移动端菜单按钮 -->
<button class="mobile-menu-btn" @click="toggleMobileMenu">
<span
class="hamburger-line"
:class="{ active: isMobileMenuOpen }"
></span>
<span
class="hamburger-line"
:class="{ active: isMobileMenuOpen }"
></span>
<span
class="hamburger-line"
:class="{ active: isMobileMenuOpen }"
></span>
</button>
</div>
</div>
</div>
<!-- 移动端菜单 -->
<div class="mobile-menu" :class="{ open: isMobileMenuOpen }">
<ul class="mobile-menu-list">
<li
v-for="item in menuItems"
:key="item.enName"
class="mobile-menu-item"
:class="{ active: item.active }"
@click="handleMenuClick(item)"
>
{{ item.name }}
</li>
</ul>
</div>
<!-- 移动端背景遮罩 -->
<div
class="mobile-overlay"
:class="{ show: isMobileMenuOpen }"
@click="isMobileMenuOpen = false"
></div>
</header>
</ClientOnly>
</template>
<style lang="scss" scoped>
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: #ffffff;
backdrop-filter: blur(10px);
border-bottom: 1px solid #e0e0e0;
z-index: 1000;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
pointer-events: auto;
line-height: 1.2;
&-container {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
height: 50px;
gap: 60px;
pointer-events: auto;
/* 4K 和超大屏幕 */
@media (min-width: 2560px) {
max-width: 1800px;
padding: 0 60px;
}
/* 1920px 屏幕 */
@media (min-width: 1920px) and (max-width: 2559px) {
max-width: 1200px;
padding: 0 50px;
}
/* 1440px 屏幕 */
@media (min-width: 1440px) and (max-width: 1919px) {
max-width: 1200px;
padding: 0 40px;
}
/* 1024px 屏幕 */
@media (max-width: 1024px) {
padding: 0 30px;
}
/* 768px 屏幕 */
@media (max-width: 768px) {
padding: 0 20px;
height: 50px;
gap: 30px;
}
/* 480px 屏幕 */
@media (max-width: 480px) {
padding: 0 16px;
}
/* 375px 屏幕 */
@media (max-width: 375px) {
padding: 0 12px;
}
}
&-logo {
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.02);
}
.logo-image {
height: 36px;
width: auto;
object-fit: contain;
@media (max-width: 768px) {
height: 32px;
}
}
}
&-right {
display: flex;
align-items: center;
gap: 40px;
pointer-events: auto;
}
&-menu {
&.desktop-menu {
@media (max-width: 768px) {
display: none;
}
}
}
&-actions {
display: flex;
align-items: center;
gap: 20px;
pointer-events: auto;
@media (max-width: 768px) {
gap: 16px;
}
}
}
// 菜单样式
.menu-list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 0;
align-items: center;
}
.menu-item {
position: relative;
padding: 16px 20px;
font-size: 15px;
font-weight: 500;
color: #333333;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
display: flex;
align-items: center;
justify-content: center;
line-height: 1.4;
// 下划线样式
&::after {
content: "";
position: absolute;
bottom: 12px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 2px;
background-color: #000000;
transition: all 0.3s ease;
}
&:hover {
color: #000000;
&::after {
width: 30px;
bottom: 12px;
}
}
&.active {
color: #000000;
font-weight: 600;
// 激活时显示下划线
&::after {
width: 25px;
bottom: 12px;
}
}
@media (max-width: 1024px) {
padding: 14px 16px;
font-size: 14px;
}
@media (max-width: 768px) {
padding: 12px 14px;
font-size: 13px;
}
}
// 语言容器
.language-container {
position: relative;
z-index: 1001;
}
// 语言选择器
.language-selector {
position: relative;
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: transparent;
border: none;
cursor: pointer;
transition: all 0.3s ease;
z-index: 1001;
pointer-events: auto;
border-radius: 4px;
&:hover {
background: rgba(0, 0, 0, 0.02);
}
.lang-earth-icon {
width: 18px;
height: 18px;
object-fit: contain;
filter: brightness(0.6);
}
.lang-text {
font-size: 15px;
color: #333333;
font-weight: 500;
white-space: nowrap;
pointer-events: none;
transition: opacity 0.2s ease;
}
.lang-icon {
color: #666666;
transition: transform 0.3s ease;
pointer-events: none;
width: 12px;
height: 12px;
&.rotate {
transform: rotate(180deg);
}
}
@media (max-width: 768px) {
padding: 8px 12px;
gap: 6px;
.lang-earth-icon {
width: 16px;
height: 16px;
}
.lang-text {
font-size: 14px;
}
}
}
.lang-dropdown {
position: absolute;
top: 100%;
right: 0;
margin-top: 0;
background: white;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
overflow: hidden;
min-width: 140px;
z-index: 1002;
pointer-events: auto;
}
.lang-option {
padding: 10px 14px;
font-size: 12px;
color: #000000;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: space-between;
pointer-events: auto;
&:hover {
background: rgba(0, 0, 0, 0.04);
color: #000000;
}
&.active {
background: rgba(0, 0, 0, 0.08);
color: #000000;
font-weight: 600;
}
&.disabled {
cursor: not-allowed;
opacity: 0.6;
}
.loading-dot {
font-size: 10px;
color: #4f8cef;
animation: loading 1.5s infinite;
}
}
@keyframes loading {
0%, 20% { opacity: 0; }
50% { opacity: 1; }
80%, 100% { opacity: 0; }
}
// 移动端菜单按钮
.mobile-menu-btn {
display: none;
flex-direction: column;
justify-content: center;
width: 30px;
height: 30px;
background: none;
border: none;
cursor: pointer;
@media (max-width: 768px) {
display: flex;
}
.hamburger-line {
width: 20px;
height: 2px;
background: #000000;
border-radius: 1px;
transition: all 0.3s ease;
margin: 2px 0;
&:nth-child(1).active {
transform: rotate(45deg) translate(5px, 5px);
}
&:nth-child(2).active {
opacity: 0;
}
&:nth-child(3).active {
transform: rotate(-45deg) translate(7px, -6px);
}
}
}
// 移动端菜单
.mobile-menu {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
transform: translateY(-100%);
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
&.open {
transform: translateY(0);
opacity: 1;
visibility: visible;
}
&-list {
list-style: none;
margin: 0;
padding: 0;
}
&-item {
padding: 16px 20px;
font-size: 16px;
color: #000000;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
transition: all 0.2s ease;
&:hover {
background: rgba(0, 0, 0, 0.04);
color: #000000;
}
&.active {
color: #000000;
background: rgba(0, 0, 0, 0.06);
}
&:last-child {
border-bottom: none;
}
}
}
// 移动端背景遮罩
.mobile-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: -1;
&.show {
opacity: 1;
visibility: visible;
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.navbar {
&-container {
.logo-image {
height: 34px;
}
}
}
}
</style>