fiee-official-website/src/components/customHeader/size375/index.vue

216 lines
5.0 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.

<template>
<!-- 通用页头 -->
<NLayoutHeader
class="custom-header"
:class="{ 'header-scrolled': isScrolled }"
>
<div class="header-container">
<div class="logo" @click="handleToHome">
<NImage
style="width: 108px; height: 33px; max-width: 100%"
:src="FiEELogo"
preview-disabled
/>
</div>
<div class="menu-btn" :class="{ 'menu-open': showMenu }" @click="toggleMenu">
<n-icon size="28" class="menu-icon menu-icon-menu">
<menu-sharp />
</n-icon>
<n-icon size="28" class="menu-icon menu-icon-close">
<close-sharp />
</n-icon>
</div>
</div>
</NLayoutHeader>
<transition name="fade-slide">
<div v-if="showMenu" class="mobile-menu-wrapper" @click.self="closeMenu">
<NMenu
mode="vertical"
:options="menuOptions"
:inverted="isScrolled"
class="mobile-menu"
accordion
v-model:value="selectedKey"
@update:value="handleMenuSelect"
/>
</div>
</transition>
</template>
<script setup>
import FiEELogo from '@/assets/image/header/logo.png'
import { ref, onMounted, onUnmounted } from 'vue'
import { NMenu, NLayoutHeader, NImage, NIcon } from 'naive-ui'
import { MenuSharp, CloseSharp } from '@vicons/ionicons5'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useHeaderMenuConfig } from '@/config/headerMenuConfig'
const { t } = useI18n()
const router = useRouter()
const isScrolled = ref(false)
const showMenu = ref(false)
const selectedKey = ref(null)
const toggleMenu = () => {
showMenu.value = !showMenu.value;
};
const closeMenu = () => {
showMenu.value = false;
};
// 递归查找菜单项
function findMenuOptionByKey(options, key) {
for (const option of options) {
if (option.key === key) return option;
if (option.children) {
const found = findMenuOptionByKey(option.children, key);
if (found) return found;
}
}
return null;
}
// 菜单点击跳转
const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key);
if (option && option.href) {
router.push(option.href);
showMenu.value = false; // 跳转后收起菜单
}
};
// 使用统一的菜单配置
const menuOptions = useHeaderMenuConfig()
// 监听滚动事件
const handleScroll = () => {
//滚动距离大于100px时处理对应的header样式
isScrolled.value = window.scrollY >= 100;
};
onMounted(() => {
window.addEventListener("scroll", handleScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
//点击回到首页
const handleToHome = () => {
router.push('/')
selectedKey.value = null // 重置菜单选中状态
showMenu.value = false // 在移动端同时关闭菜单
}
</script>
<style scoped lang="scss">
.custom-header {
top: 0;
left: 0;
right: 0;
z-index: 1000;
transition: all 0.3s ease;
background: transparent;
height: 320px;
&.header-scrolled {
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
.header-container {
width: 100%;
min-width: 0;
box-sizing: border-box;
padding: 0 80px;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
flex-shrink: 0;
margin-right: 12px;
display: flex;
align-items: center;
}
.logo :deep(.n-image) {
max-width: 100px;
height: auto;
}
.menu-btn {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 75px;
padding: 40px 70px;
border-radius: 30px;
background: transparent;
user-select: none;
transition: background 0.2s;
position: relative;
.menu-icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(0deg);
opacity: 1;
transition:
opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
}
.menu-icon-close {
opacity: 0;
transform: translate(-50%, -50%) rotate(-90deg) scale(0.8);
}
&.menu-open {
.menu-icon-menu {
opacity: 0;
transform: translate(-50%, -50%) rotate(90deg) scale(0.8);
}
.menu-icon-close {
opacity: 1;
transform: translate(-50%, -50%) rotate(0deg) scale(1);
}
}
}
.mobile-menu-wrapper {
position: fixed;
top: 320px;
left: 0;
width: 100vw;
background: rgba(220, 207, 248, 0.95);
z-index: 1100;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
padding: 40px 0 80px 0;
max-height: 1500px;
overflow-y: auto;
:deep(.n-menu-item) {
font-weight: 600;
}
}
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-slide-enter-from,
.fade-slide-leave-to {
opacity: 0;
transform: translateY(-50px);
}
</style>