Files
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

132 lines
5.0 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.
<template>
<div class="min-h-screen bg-white pb-16">
<!-- Banner背景优先取父级栏目 background回退栏目自身再回退内置图 -->
<section
class="t10-page-banner"
:style="{ backgroundImage: `url('${bannerImage}')`, minHeight: '280px' }"
>
<div class="t10-container t10-banner-inner">
<nav class="t10-breadcrumb mb-3">
<NuxtLink to="/">首页</NuxtLink>
<span class="mx-2">/</span>
<span class="text-white">{{ pageTitle }}</span>
</nav>
<h1 class="text-3xl sm:text-4xl font-bold text-white">{{ pageTitle }}</h1>
</div>
</section>
<div class="t10-container py-12 lg:py-16">
<div class="max-w-5xl mx-auto">
<!-- 单页配图 -->
<img
v-if="coverImage"
:src="coverImage"
:alt="pageTitle"
class="w-full h-[240px] lg:h-[360px] object-cover rounded-xl mb-10"
>
<div class="bg-white rounded-xl border border-[var(--t10-border)] p-6 lg:p-10 shadow-sm">
<template v-if="hasContent">
<div class="flex flex-wrap items-center gap-3 mb-8 pb-6 border-b border-[var(--t10-border)]">
<span class="w-1.5 h-7 rounded-full bg-[var(--t10-primary)]" />
<h2 class="text-xl sm:text-2xl font-bold text-[var(--t10-text-strong)]">{{ pageTitle }}</h2>
<span v-if="updateTimeText" class="sm:ml-auto text-sm text-[var(--t10-muted-2)]">
更新于 {{ updateTimeText }}
</span>
</div>
<div class="prose max-w-none">
<RichText :content="pageData?.content" />
</div>
<PageAttachments
v-if="pageData?.attachments?.length"
:attachments="pageData.attachments"
class="mt-10"
/>
</template>
<div v-else class="text-center py-16">
<svg class="w-16 h-16 mx-auto text-[var(--t10-muted-light)] mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z" />
</svg>
<p class="text-[var(--t10-text)] font-medium mb-2">{{ emptyState.title }}</p>
<p class="text-[var(--t10-muted-2)] text-sm leading-relaxed mb-6">{{ emptyState.desc }}</p>
<NuxtLink to="/" class="text-[var(--t10-primary)] hover:underline font-medium">返回首页</NuxtLink>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
/**
* 模板 10 - 通用单页(创芯存储科技蓝风格)
* 用于品牌优势、服务条款等后台「单页管理」维护的页面
*/
import dayjs from 'dayjs'
import type { PageDetail } from '~/types'
const { siteInfo, allNavigations, fetchSiteInfo } = useSite()
const { fileUrl } = useFileUrl()
const route = useRoute()
await fetchSiteInfo()
const slug = computed(() => (route.params.slug as string) || (route.params.id as string) || '')
const pagePath = computed(() => slug.value || String(route.query.path || ''))
const { data: pageData } = await useFetch<PageDetail>('/api/page/detail', {
key: `page-${pagePath.value || route.path}`,
query: { path: pagePath.value || 'about' }
})
const pageTitle = computed(() => pageData.value?.title?.trim() || '页面')
const hasContent = computed(() => Boolean(pageData.value?.hasContent && pageData.value?.content))
const coverImage = computed(() => fileUrl(pageData.value?.photo || ''))
/**
* 栏目页 Banner 背景图:优先取「父级栏目」的 background(后台上传),
* 回退到当前栏目自身的 background,再回退到模板内置静态图。
* 父级栏目定位:按当前单页 slug 在导航树里找到对应栏目,再取其 parentId 指向的栏目。
*/
const currentNav = computed(() => {
const s = slug.value
if (!s) return undefined
return allNavigations.value.find((n) => n.pagePath === s)
})
const bannerImage = computed<string>(() => {
const cur = currentNav.value
const parent = cur?.parentId
? allNavigations.value.find((n) => n.navigationId === cur.parentId)
: undefined
const bg = parent?.background || cur?.background || ''
return bg ? fileUrl(bg) : '/images/template-10/advantage-banner.jpg'
})
const updateTimeText = computed(() => {
const t = pageData.value?.updateTime
return t ? dayjs(t).format('YYYY-MM-DD') : ''
})
const emptyState = computed(() => {
if (pageData.value?.status === 'error') {
return { title: '内容暂时无法加载', desc: '内容接口暂时不可用,请稍后再试。' }
}
return {
title: '内容尚未录入',
desc: '请前往管理后台「单页管理」补充该页面的正文内容。'
}
})
usePageSeo(
{
title: pageTitle.value,
path: route.path,
keywords: pageData.value?.keywords || undefined,
description: pageData.value?.description || undefined,
image: coverImage.value || undefined
},
siteInfo.value
)
</script>