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
+142
View File
@@ -0,0 +1,142 @@
import { computed } from 'vue'
import { useSite } from './useSite'
import type { FeatureItem, FeatureSectionSetting, HomeBlocks } from '~/types'
/**
* 首页「优势模块」统一数据来源
*
* 配置来自后台 cms_website_setting.features(经 getSiteInfo 合并进 siteInfo.setting),
* 为 JSON 字符串。未配置 / 解析失败时回退到各模板自己的默认数据(defaultFeatures)。
*
* 兼容策略:
* - 完全未配置(siteSetting.features 不存在)→ 显示 defaultFeatures,标题走兜底
* - 已配置且 enabled=false → enabled=false,上层 v-if 隐藏整块
* - 已配置但 items 为空 → 回退到 defaultFeatures(避免空白区)
* - items 超过 4 个 → 截断到 4 个(后台也限制最多 4 个)
*
* 2026-08-05 新增「首页区块总览」新结构:
* { hero, advantages, products, cases, news, banner, cta }
* 同时保留对旧结构(顶层 enabled/title/subtitle/items/heroFeatures)的兼容。
*
* @param defaultFeatures 模板默认卡片(各模板 FeatureSection 传入自己的硬编码项)
*/
export function useFeatures(defaultFeatures: FeatureItem[] = []) {
const { siteSetting } = useSite()
const config = computed<FeatureSectionSetting | null>(() => {
const raw = siteSetting.value?.features
if (!raw) return null
try {
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw
if (parsed && typeof parsed === 'object') return parsed as FeatureSectionSetting
return null
} catch {
return null
}
})
/** 是否已配置(用于区分「后台显式关闭」与「未接入」) */
const configured = computed(() => !!config.value)
/**
* 判断是否为新结构(区块化)
* 新结构至少包含 hero 或 advantages 字段。
*/
const isBlockMode = computed(() => {
const c = config.value
if (!c) return false
return !!(c.hero || c.advantages)
})
/** 是否显示优势模块:未配置默认显示;已配置以 enabled 为准(默认 true */
const enabled = computed(() => {
const c = config.value
if (!c) return true
// 新结构:读 advantages.enabled;旧结构:读顶层 enabled
if (isBlockMode.value) {
return c.advantages?.enabled !== false
}
return c.enabled !== false
})
const title = computed(() => {
const c = config.value
if (!c) return '我们的优势'
if (isBlockMode.value) return c.advantages?.title || '我们的优势'
return c.title || '我们的优势'
})
const subtitle = computed(() => {
const c = config.value
if (!c) return ''
if (isBlockMode.value) return c.advantages?.subtitle || ''
return c.subtitle || ''
})
const items = computed<FeatureItem[]>(() => {
const c = config.value
const cfgItems = isBlockMode.value ? c?.advantages?.items : c?.items
if (!cfgItems || !Array.isArray(cfgItems) || cfgItems.length === 0) {
return defaultFeatures
}
return cfgItems.slice(0, 4).map((it) => ({
title: it?.title || '',
desc: it?.desc || '',
icon: it?.icon || 'box'
}))
})
/** Hero 首屏右侧特性卡片文案(纯文本),空数组时上层回退模板默认值 */
const heroFeatures = computed<string[]>(() => {
const c = config.value
const hf = isBlockMode.value ? c?.hero?.features : c?.heroFeatures
if (!hf || !Array.isArray(hf) || hf.length === 0) return []
return hf.slice(0, 4).map((s) => String(s ?? '').trim()).filter(Boolean)
})
/**
* 首页各区块显示开关。
* 未配置(或旧结构)时,除预留位 products/cases/news/banner/cta 默认 false 外,
* hero 与 advantages 默认 truenews/cta 在 template-01 有实际内容,旧结构下默认 true。
*/
const homeBlocks = computed<Required<Pick<HomeBlocks, 'hero' | 'advantages' | 'products' | 'cases' | 'news' | 'banner' | 'cta'>>>(() => {
const c = config.value
const defaults = {
hero: true,
advantages: true,
products: false,
cases: false,
news: true,
banner: true,
cta: true
}
if (!c) return defaults
if (isBlockMode.value) {
return {
hero: c.hero?.enabled !== false,
advantages: c.advantages?.enabled !== false,
// 产品/案例为预留位,默认不显示,需后台显式开启
products: c.products?.enabled === true,
cases: c.cases?.enabled === true,
// 新闻/CTA 为多数模板首页实际区块,默认显示,除非后台显式关闭
news: c.news?.enabled !== false,
banner: c.banner?.enabled !== false,
cta: c.cta?.enabled !== false
}
}
// 旧结构:只有 advantages/hero 的语义;保留 news/cta 默认显示以兼容存量站点
return {
hero: c.enabled !== false,
advantages: c.enabled !== false,
products: false,
cases: false,
news: true,
banner: true,
cta: true
}
})
return { configured, enabled, title, subtitle, items, heroFeatures, homeBlocks, isBlockMode }
}