Files
hjc-web/app/pages/page/[id].vue
T
gxwebsoft 2b69686795 feat(app): 添加多模板关于我们页面及相关路由和404页面
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
2026-09-08 12:13:44 +08:00

93 lines
3.1 KiB
Vue

<template>
<div>
<Component :is="templatePage" v-if="templatePage" />
<SiteLoading v-else />
</div>
</template>
<script setup lang="ts">
/**
* CMS 页面路由
* /page/:id → 既支持旧的导航节点 ID(数字),也支持新版「单页管理」的 path(slug)
* 数字 id → 按 navigationId 取 cms_navigation + cms_design 内容
* 非数字 slug → 按 path 取 cms_page 已发布单页(website-admin「单页管理」模块)
*/
const route = useRoute()
const id = route.params.id as string
const isNumeric = /^\d+$/.test(id)
const slug = isNumeric ? undefined : id
// 规范路由收敛:单页的规范地址是顶层 /about、/contact(独立富组件),
// /page/about、/page/contact 仅作兼容入口,统一 301 到规范地址,避免重复页与 SEO 分散。
const CANONICAL_PAGE_ROUTE: Record<string, string> = { about: '/about', contact: '/contact' }
if (slug && CANONICAL_PAGE_ROUTE[slug]) {
// 规范地址收敛:用 Nuxt 标准的 navigateTo 301 重定向(createError 仅渲染错误页,不会真正跳转)
throw navigateTo(CANONICAL_PAGE_ROUTE[slug], { redirectCode: 301 })
}
const { loadTemplate } = useTemplate()
const { siteInfo, navigations, fetchSiteInfo } = useSite()
await fetchSiteInfo()
const templatePage = shallowRef<Component | null>(null)
const components = await loadTemplate()
// 语义化 slug 优先映射到独立页面组件(about→关于我们、contact→联系我们),
// 避免有专属组件的 slug 落入通用单页 Page 逻辑——CMS 未录入正文时会显示空状态。
// (这些页面已有结构化组件,内容更丰富,无需依赖 CMS 单页正文。)
const standaloneComp =
slug === 'about' ? components?.About
: slug === 'contact' ? components?.Contact
: null
templatePage.value = standaloneComp || components?.Page || null
// 从导航中查找当前页面信息(仅数字 id 时有效)
const currentNav = computed(() => {
if (isNumeric) {
const navs = navigations.value || []
const findNav = (items: any[]): any => {
for (const item of items) {
if (String(item.navigationId) === id) return item
if (item.children?.length) {
const found = findNav(item.children)
if (found) return found
}
}
return null
}
return findNav(navs)
}
return undefined
})
// 非数字 slug:拉取单页 SEO 信息(标题/关键词/描述/头图)
let seoData = ref<{
title?: string
keywords?: string
description?: string
photo?: string | null
} | null>(null)
if (!isNumeric && slug) {
const { data } = await useFetch<{
title?: string
keywords?: string
description?: string
photo?: string | null
}>('/api/page/detail', {
query: { path: slug }
})
seoData = data
}
usePageSeo(
{
title: seoData.value?.title || currentNav.value?.title || (slug === 'about' ? '关于我们' : slug === 'contact' ? '联系我们' : '页面'),
path: route.fullPath,
keywords: seoData.value?.keywords,
description: seoData.value?.description,
image: seoData.value?.photo || undefined
},
siteInfo.value
)
</script>