Files
----/前端源码/uni-app/components/MarketingNavBar.vue

509 lines
11 KiB
Vue
Raw Permalink 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.
<!--
响应式营销站顶栏参考桌面端横向导航 + 手机端抽屉菜单
用法在页面中 <MarketingNavBar />按需改 navSections / brand / @login
-->
<template>
<view class="mnav">
<view class="mnav-inner">
<view class="mnav-brand">
<text class="mnav-logo">H</text>
<text class="mnav-title">海珀AI</text>
</view>
<!-- 桌面横向链接 -->
<view class="mnav-desktop">
<text
v-for="link in topLinks"
:key="link.id"
class="mnav-link"
:class="{ 'mnav-link--active': activeDropdown === link.id }"
@mouseenter="onDesktopEnter(link)"
@mouseleave="onDesktopLeave"
@click="onTopLinkClick(link)"
>
{{ link.label }}
</text>
</view>
<view class="mnav-right">
<text class="mnav-login mnav-login--bar" @click="emit('login')">登录</text>
<!-- 手机汉堡 -->
<view class="mnav-burger" @click="openDrawer">
<view class="mnav-burger-line" />
<view class="mnav-burger-line" />
<view class="mnav-burger-line" />
</view>
</view>
</view>
<!-- 桌面下拉面板 -->
<view
v-if="activeDropdown === 'solutions' && showMega"
class="mnav-mega"
@mouseenter="setMegaHover(true)"
@mouseleave="setMegaHover(false)"
>
<view class="mnav-mega-inner">
<view v-for="col in megaColumns" :key="col.title" class="mnav-mega-col">
<text class="mnav-mega-title">{{ col.title }}</text>
<text v-for="(item, i) in col.items" :key="i" class="mnav-mega-item" @click="emitNav(item)">{{ item }}</text>
</view>
</view>
</view>
<!-- 手机遮罩 + 抽屉 -->
<view v-if="drawerOpen" class="mnav-overlay" @click="closeDrawer" />
<view class="mnav-drawer" :class="{ 'mnav-drawer--open': drawerOpen }">
<view class="mnav-drawer-head">
<text class="mnav-drawer-title">菜单</text>
<view class="mnav-drawer-close" @click="closeDrawer">
<text class="mnav-drawer-close-x">×</text>
</view>
</view>
<scroll-view scroll-y class="mnav-drawer-scroll">
<view
v-for="sec in navSections"
:key="sec.id"
class="mnav-acc"
>
<view class="mnav-acc-head" @click="toggleAcc(sec.id)">
<text class="mnav-acc-title">{{ sec.title }}</text>
<text class="mnav-acc-chevron">{{ expandedId === sec.id ? '' : '+' }}</text>
</view>
<view v-if="expandedId === sec.id" class="mnav-acc-body">
<text
v-for="(line, li) in sec.items"
:key="li"
class="mnav-acc-link"
@click="onDrawerItem(line)"
>
{{ line }}
</text>
</view>
</view>
<view class="mnav-drawer-login-wrap">
<text class="mnav-drawer-login" @click="onDrawerLogin">登录</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const emit = defineEmits<{
login: [];
navigate: [payload: { label: string; section?: string }];
}>();
/** 顶部一级链接(无下拉的直接 emit */
const topLinks = [
{ id: 'home', label: '首页' },
{ id: 'cases', label: '客户案例' },
{ id: 'rpa', label: '电商RPA实例' },
{ id: 'solutions', label: '行业解决方案', mega: true },
{ id: 'eco', label: '生态合作' },
{ id: 'about', label: '关于HyperAigc' },
{ id: 'user', label: '个人中心' },
] as const;
/** 桌面端「行业解决方案」三列(可按后端配置替换) */
const megaColumns = [
{
title: '政务与公用事业',
items: ['党务政务', '研政购务', '政府政策运营', '舆情分析'],
},
{
title: '企业解决方案',
items: ['智能知识', '供应链管理', 'CRM', '对话管理', '人力资源', '营销自动化', '安全监督', '行业培训'],
},
{
title: '领域解决方案',
items: ['金融科技', '电商零售', '在线教育'],
},
];
/** 手机抽屉:分组折叠(与桌面信息等价,纵向更易读) */
const navSections = [
{
id: 'gov',
title: '政务与公用事业',
items: ['党务政务', '研政购务', '政府政策运营', '舆情分析'],
},
{
id: 'ent',
title: '企业解决方案',
items: ['智能知识', '供应链管理', 'CRM', '对话管理', '人力资源', '营销自动化', '安全监督', '行业培训'],
},
{
id: 'domain',
title: '领域解决方案',
items: ['金融科技', '电商零售', '在线教育'],
},
{
id: 'more',
title: '更多',
items: ['首页', '万年历', '起名服务', '姓名测试', '缘分合盘', '客户案例', '电商RPA实例', '生态合作', '关于HyperAigc', '个人中心'],
},
];
const drawerOpen = ref(false);
const expandedId = ref<string | null>('gov');
const activeDropdown = ref<string | null>(null);
const showMega = ref(false);
let megaTimer: ReturnType<typeof setTimeout> | null = null;
const megaEnter = ref(false);
function openDrawer() {
drawerOpen.value = true;
}
function closeDrawer() {
drawerOpen.value = false;
}
function toggleAcc(id: string) {
expandedId.value = expandedId.value === id ? null : id;
}
function onDrawerItem(label: string) {
emit('navigate', { label });
closeDrawer();
}
function onDrawerLogin() {
emit('login');
closeDrawer();
}
function onDesktopEnter(link: (typeof topLinks)[number]) {
if (megaTimer) {
clearTimeout(megaTimer);
megaTimer = null;
}
activeDropdown.value = link.id;
showMega.value = link.id === 'solutions' && 'mega' in link && link.mega;
}
function onDesktopLeave() {
megaTimer = setTimeout(() => {
if (!megaEnter.value) {
activeDropdown.value = null;
showMega.value = false;
}
}, 120);
}
function setMegaHover(v: boolean) {
if (v && megaTimer) {
clearTimeout(megaTimer);
megaTimer = null;
}
megaEnter.value = v;
if (!v) {
megaTimer = setTimeout(() => {
if (!megaEnter.value) {
activeDropdown.value = null;
showMega.value = false;
}
}, 80);
}
}
function onTopLinkClick(link: (typeof topLinks)[number]) {
if (link.id === 'solutions') {
// 窄屏无 hover点击展开/收起 mega宽屏以悬停为准避免误触反复开关
if (typeof window !== 'undefined' && window.innerWidth < 992) {
showMega.value = !showMega.value;
activeDropdown.value = showMega.value ? 'solutions' : null;
}
return;
}
emit('navigate', { label: link.label });
}
function emitNav(label: string) {
emit('navigate', { label, section: '行业解决方案' });
activeDropdown.value = null;
showMega.value = false;
megaEnter.value = false;
}
</script>
<style scoped>
.mnav {
position: relative;
z-index: 200;
background: rgba(8, 12, 28, 0.85);
backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.mnav-inner {
max-width: 1200px;
margin: 0 auto;
padding: 12px 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.mnav-brand {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.mnav-logo {
width: 36px;
height: 36px;
border-radius: 8px;
background: linear-gradient(145deg, #3b82f6, #1d4ed8);
color: #fff;
font-size: 18px;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
}
.mnav-title {
font-size: 18px;
font-weight: 700;
color: #e8eefc;
letter-spacing: 0.06em;
}
.mnav-desktop {
display: none;
flex: 1;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 8px 20px;
}
.mnav-link {
font-size: 14px;
color: rgba(226, 232, 255, 0.85);
padding: 6px 0;
cursor: pointer;
}
.mnav-link--active {
color: #93c5fd;
}
.mnav-right {
display: flex;
align-items: center;
gap: 12px;
}
.mnav-login {
font-size: 14px;
color: #e8eefc;
padding: 8px 18px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.2);
cursor: pointer;
}
.mnav-burger {
display: flex;
flex-direction: column;
justify-content: center;
gap: 5px;
width: 40px;
height: 40px;
padding: 8px;
box-sizing: border-box;
cursor: pointer;
}
.mnav-burger-line {
height: 2px;
background: #e8eefc;
border-radius: 1px;
}
.mnav-mega {
position: absolute;
left: 0;
right: 0;
top: 100%;
padding: 16px 20px 24px;
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.2);
}
.mnav-mega-inner {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.mnav-mega-title {
display: block;
font-size: 13px;
font-weight: 700;
color: #0f172a;
padding-bottom: 8px;
margin-bottom: 8px;
border-bottom: 2px solid #1e3a8a;
}
.mnav-mega-item {
display: block;
font-size: 13px;
color: #334155;
padding: 6px 0;
cursor: pointer;
}
.mnav-mega-item:active {
color: #1d4ed8;
}
.mnav-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 210;
}
.mnav-drawer {
position: fixed;
top: 0;
left: 0;
width: min(88vw, 360px);
height: 100%;
background: #f8fafc;
z-index: 220;
transform: translateX(-100%);
transition: transform 0.28s ease;
display: flex;
flex-direction: column;
box-shadow: 8px 0 32px rgba(0, 0, 0, 0.15);
}
.mnav-drawer--open {
transform: translateX(0);
}
.mnav-drawer-head {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 18px;
border-bottom: 1px solid #e2e8f0;
background: #fff;
}
.mnav-drawer-title {
font-size: 17px;
font-weight: 700;
color: #0f172a;
}
.mnav-drawer-close {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.mnav-drawer-close-x {
font-size: 28px;
color: #64748b;
line-height: 1;
}
.mnav-drawer-scroll {
flex: 1;
height: 0;
padding: 8px 0 24px;
}
.mnav-acc {
border-bottom: 1px solid #e2e8f0;
background: #fff;
}
.mnav-acc-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 18px;
}
.mnav-acc-title {
font-size: 15px;
font-weight: 600;
color: #0f172a;
}
.mnav-acc-chevron {
font-size: 18px;
color: #64748b;
}
.mnav-acc-body {
padding: 0 18px 14px;
}
.mnav-acc-link {
display: block;
font-size: 14px;
color: #475569;
padding: 10px 0;
border-bottom: 1px solid #f1f5f9;
}
.mnav-acc-link:last-child {
border-bottom: none;
}
.mnav-drawer-login-wrap {
padding: 20px 18px;
}
.mnav-drawer-login {
display: block;
text-align: center;
padding: 12px;
border-radius: 10px;
background: #0f172a;
color: #f8fafc;
font-size: 15px;
font-weight: 600;
}
@media (min-width: 992px) {
.mnav-desktop {
display: flex;
}
.mnav-burger {
display: none;
}
}
@media (max-width: 991px) {
.mnav-mega {
display: none;
}
.mnav-login--bar {
display: none;
}
}
</style>