2b69686795
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引 - 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理 - 实现/article、/case、/product及/page动态路由兼容列表与详情展示 - 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置 - 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持 - 模板增强支持CMS单页内容加载及SEO信息动态设置 - 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
172 lines
7.6 KiB
Vue
172 lines
7.6 KiB
Vue
<template>
|
||
<!-- 顶部数据条(满坛记品牌数据;具体数值请替换为真实数据) -->
|
||
<div class="bg-[#2E2620] text-[#E9DFD3] text-xs">
|
||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||
<div class="flex items-center justify-center sm:justify-end gap-x-5 gap-y-1 py-1.5">
|
||
<span v-for="stat in topStats" :key="stat" class="flex items-center gap-1.5">
|
||
<svg class="w-3.5 h-3.5 text-[#F2A900]" viewBox="0 0 24 24" fill="currentColor">
|
||
<path d="M12 2l2.4 7.4H22l-6 4.6 2.3 7.4-6.3-4.8L5.7 21.4 8 14 2 9.4h7.6z" />
|
||
</svg>
|
||
{{ stat }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<header class="sticky top-0 z-50 bg-[#FFF8F0]/95 backdrop-blur-sm border-b border-[#F0E2D5]">
|
||
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||
<div class="flex items-center justify-between h-16 lg:h-20">
|
||
<!-- Logo -->
|
||
<SiteBrand
|
||
link-class="flex-shrink-0"
|
||
:logo="siteLogo"
|
||
:icon="siteIcon"
|
||
:name="siteName"
|
||
default-name="品牌官网"
|
||
logo-class="h-12 w-auto max-w-[200px] object-contain"
|
||
icon-class="h-8 w-auto object-contain"
|
||
name-class="text-lg font-bold text-[#33291F] truncate max-w-[160px] sm:max-w-xs"
|
||
>
|
||
<template #fallback-icon>
|
||
<div class="w-9 h-9 rounded-xl bg-[#E5472E] flex items-center justify-center">
|
||
<svg class="w-5 h-5 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||
<path d="M12 2C7 2 3 6 3 11c0 5 4 9 9 9s9-4 9-9c0-5-4-9-9-9zm0 16c-3.9 0-7-3-7-7 0-2.8 1.6-5.2 4-6.4.2 1.6 1.1 3 2.5 3.8-1-.7-1.6-1.9-1.6-3.1 2.3.3 4.1 2.1 4.4 4.4.8.7 1.3 1.7 1.3 2.9 0 .6-.1 1.2-.4 1.7 1.4-.7 2.4-2.1 2.4-3.7 1.8 1.2 3 3.3 3 5.4 0 4-3.1 7-7 7z" />
|
||
</svg>
|
||
</div>
|
||
</template>
|
||
</SiteBrand>
|
||
|
||
<!-- 搜索框(后台 setting.searchBtn 控制是否显示) -->
|
||
<SiteSearchBox v-if="showHeaderSearch" variant="light" accent="#E5472E" class="hidden lg:flex items-center ml-8 mr-auto" />
|
||
|
||
<!-- 桌面端导航 -->
|
||
<nav class="hidden lg:flex items-center gap-8">
|
||
<template v-for="item in navItems" :key="item.navigationId || item.path">
|
||
<div v-if="item.children && item.children.length > 0" class="relative group">
|
||
<button class="text-base font-medium text-[#33291F] hover:text-[#E5472E] transition-colors flex items-center gap-1">
|
||
{{ item.title }}
|
||
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</button>
|
||
<div class="absolute left-1/2 -translate-x-1/2 top-full pt-3 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all">
|
||
<div
|
||
class="bg-white rounded-2xl shadow-xl border border-[#F0E2D5] py-2"
|
||
:class="item.children.length > 4 ? 'grid grid-cols-2 gap-x-2 px-2 min-w-[340px]' : 'min-w-[170px]'"
|
||
>
|
||
<NuxtLink
|
||
v-for="child in item.children"
|
||
:key="child.navigationId"
|
||
:to="getChildPath(child)"
|
||
class="block px-4 py-2 text-[15px] text-[#33291F] hover:text-[#E5472E] hover:bg-[#FFF1E6] transition-colors whitespace-nowrap"
|
||
>
|
||
{{ child.title }}
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<NuxtLink
|
||
v-else
|
||
:to="getNavLink(item)"
|
||
:target="item.target === '_blank' ? '_blank' : undefined"
|
||
class="text-base font-medium text-[#33291F] hover:text-[#E5472E] transition-colors"
|
||
active-class="text-[#E5472E]"
|
||
>
|
||
{{ item.title }}
|
||
</NuxtLink>
|
||
</template>
|
||
</nav>
|
||
|
||
<!-- CTA -->
|
||
<div class="hidden lg:block mx-10">
|
||
<NuxtLink
|
||
to="/contact"
|
||
class="inline-flex items-center justify-center px-5 py-2 bg-[#E5472E] text-white text-base font-semibold rounded-full hover:bg-[#C53A22] transition-colors shadow-sm"
|
||
>
|
||
立即咨询
|
||
</NuxtLink>
|
||
</div>
|
||
|
||
<!-- 移动端菜单按钮 -->
|
||
<button
|
||
class="lg:hidden p-2 rounded-lg hover:bg-[#F0E2D5]"
|
||
@click="mobileMenuOpen = !mobileMenuOpen"
|
||
>
|
||
<svg v-if="!mobileMenuOpen" class="w-6 h-6 text-[#33291F]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||
</svg>
|
||
<svg v-else class="w-6 h-6 text-[#33291F]" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 移动端菜单 -->
|
||
<div v-if="mobileMenuOpen" class="lg:hidden bg-[#FFF8F0] border-t border-[#F0E2D5]">
|
||
<div class="container mx-auto px-4 py-4 space-y-1">
|
||
<template v-for="item in navItems" :key="item.navigationId || item.path">
|
||
<div v-if="item.children && item.children.length > 0">
|
||
<div class="px-4 py-3 text-base font-semibold text-[#33291F]">
|
||
{{ item.title }}
|
||
</div>
|
||
<NuxtLink
|
||
v-for="child in item.children"
|
||
:key="child.navigationId"
|
||
:to="getChildPath(child)"
|
||
class="block px-8 py-2 text-sm text-[#8A7B6B] hover:text-[#E5472E] hover:bg-[#FFF1E6] rounded-xl transition-colors"
|
||
@click="mobileMenuOpen = false"
|
||
>
|
||
{{ child.title }}
|
||
</NuxtLink>
|
||
</div>
|
||
<NuxtLink
|
||
v-else
|
||
:to="getNavLink(item)"
|
||
:target="item.target === '_blank' ? '_blank' : undefined"
|
||
class="block px-4 py-3 text-base font-medium text-[#33291F] hover:text-[#E5472E] hover:bg-[#FFF1E6] rounded-xl transition-colors"
|
||
active-class="text-[#E5472E] bg-[#FFF1E6]"
|
||
@click="mobileMenuOpen = false"
|
||
>
|
||
{{ item.title }}
|
||
</NuxtLink>
|
||
</template>
|
||
<NuxtLink
|
||
to="/contact"
|
||
class="block px-4 py-3 mt-2 text-center bg-[#E5472E] text-white font-semibold rounded-xl hover:bg-[#C53A22] transition-colors"
|
||
@click="mobileMenuOpen = false"
|
||
>
|
||
立即咨询
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
/**
|
||
* 模板 5 - Header 组件(温暖自然食品风)
|
||
* 使用 useSite 的 navigations(合并 topNavs + bottomNavs,过滤 top===1)渲染导航菜单
|
||
*/
|
||
import type { CmsNavigation } from '~/types'
|
||
|
||
const { siteInfo, siteName, siteLogo, siteIcon, navigations, showHeaderSearch, fetchSiteInfo } = useSite()
|
||
const mobileMenuOpen = ref(false)
|
||
|
||
/** 顶部数据条(满坛记品牌;可按站点实际数据替换为接口字段) */
|
||
const topStats = ['全球11大工厂', '远销近70个国家地区', '品牌价值568.72亿']
|
||
|
||
/** 子导航链接统一走 app/utils 的 getNavLink(自动避免 /page/4598?navId=4598 冗余拼接) */
|
||
function getChildPath(child: CmsNavigation): string {
|
||
return getNavLink(child)
|
||
}
|
||
|
||
// 获取站点信息(含导航数据)
|
||
await fetchSiteInfo()
|
||
|
||
/** 导航项(从 useSite 的 navigations 获取,已合并 topNavs + bottomNavs 并过滤 top===1) */
|
||
const navItems = computed<CmsNavigation[]>(() => {
|
||
return navigations.value || []
|
||
})
|
||
</script>
|