feat(app): 添加多模板关于我们页面及相关路由和404页面

- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
This commit is contained in:
2026-09-08 12:13:44 +08:00
commit 2b69686795
381 changed files with 59891 additions and 0 deletions
@@ -0,0 +1,147 @@
<template>
<header class="sticky top-0 z-50 bg-[#f5efe6] border-b border-[#e8e0d4]">
<div class="container mx-auto px-4 h-16 flex items-center justify-between">
<!-- Logo -->
<SiteBrand
link-class="flex-shrink-0"
:logo="siteLogo"
:icon="siteIcon"
:name="siteName"
logo-class="h-12 w-auto max-w-[200px] object-contain"
icon-class="h-8 w-auto object-contain"
name-class="font-bold text-[#8b6f47] truncate max-w-[160px] sm:max-w-xs"
/>
<!-- 搜索框后台 setting.searchBtn 控制是否显示 -->
<SiteSearchBox v-if="showHeaderSearch" variant="light" accent="#8b6f47" class="hidden lg:flex items-center ml-8 mr-auto" />
<!-- 桌面端导航 -->
<nav class="hidden md:flex items-center gap-6 text-base">
<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="font-medium text-gray-700 hover:text-[#8b6f47] 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-0 top-full pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all">
<div class="bg-white rounded-lg shadow-lg border border-[#e8e0d4] py-2 min-w-[160px]">
<NuxtLink
v-for="child in item.children"
:key="child.navigationId"
:to="getChildPath(child)"
class="block px-4 py-2 text-[15px] text-gray-700 hover:text-[#8b6f47] hover:bg-[#f5efe6] transition-colors whitespace-nowrap"
>
{{ child.title }}
</NuxtLink>
</div>
</div>
</div>
<!-- 无子菜单 -->
<NuxtLink
v-else
:to="getNavLink(item)"
:target="item.target === '_blank' ? '_blank' : undefined"
class="font-medium text-gray-700 hover:text-[#8b6f47] transition-colors"
active-class="text-[#8b6f47]"
>
{{ item.title }}
</NuxtLink>
</template>
</nav>
<!-- CTA -->
<div class="hidden md:block">
<NuxtLink
to="/contact"
class="inline-flex items-center justify-center px-4 py-2 bg-[#8b6f47] text-white text-base font-semibold rounded-full hover:bg-[#6f5638] transition-colors"
>
在线咨询
</NuxtLink>
</div>
<!-- 移动端菜单按钮 -->
<button
class="md:hidden p-2 rounded-lg hover:bg-[#e8e0d4]"
@click="mobileMenuOpen = !mobileMenuOpen"
>
<svg v-if="!mobileMenuOpen" class="w-6 h-6 text-[#8b6f47]" 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-[#8b6f47]" 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
v-if="mobileMenuOpen"
class="md:hidden bg-[#f5efe6] border-t border-[#e8e0d4]"
>
<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-[#8b6f47]">
{{ 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-gray-600 hover:text-[#8b6f47] hover:bg-[#e8e0d4] rounded-lg 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-gray-700 hover:text-[#8b6f47] hover:bg-[#e8e0d4] rounded-lg transition-colors"
active-class="text-[#8b6f47] bg-[#e8e0d4]"
@click="mobileMenuOpen = false"
>
{{ item.title }}
</NuxtLink>
</template>
<NuxtLink
to="/contact"
class="block mx-10 px-4 py-3 mt-2 text-center bg-[#8b6f47] text-white font-semibold rounded-lg hover:bg-[#6f5638] transition-colors"
@click="mobileMenuOpen = false"
>
在线咨询
</NuxtLink>
</div>
</div>
</header>
</template>
<script setup lang="ts">
/**
* 模板 3 - 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)
/** 子导航链接统一走 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>