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
+633
View File
@@ -0,0 +1,633 @@
/**
* 统一 API 响应封装
*/
export type ApiEnvelope<T = unknown> = {
code?: number
message?: string
msg?: string
data?: T
} & Record<string, unknown>
/**
* 分页结果(CMS API 标准格式)
*/
export interface PageResult<T = unknown> {
list?: T[]
count?: number
total?: number
page?: number
limit?: number
pages?: number
}
/**
* 分页参数
*/
export interface PageParam {
page?: number
limit?: number
}
// ==================== 轮播图 ====================
/**
* CMS 轮播图(/cms/cms-banner/page 返回项)
*
* 字段命名以 CMS 实际返回为准,这里用宽松的 unknown 兜底,
* 避免后端字段名微调(image / imageUrl / pic / url)导致取不到图。
* 前端统一经 ensureFullUrl() 处理图片地址(OSS 直连或 /api/file/ 代理)。
*/
export interface CmsBanner {
id?: number
/** 轮播图地址(CMS 可能用 image / imageUrl / pic / url 任一字段名) */
image?: string
imageUrl?: string
pic?: string
url?: string
/** 标题 / 副标题(可选,部分模板会叠加在图上) */
title?: string
subtitle?: string
/** 点击跳转链接(可选) */
link?: string
/** 排序(升序)—— 上游字段名为 sortNum */
sortNum?: number
/** 排序(升序,兼容旧字段名) */
sortOrder?: number
/** 状态:banner 表约定 1=启用/已发布、2=下架(与 article/product/case 的 0=发布 相反,前端按「非 2 即显示」处理) */
status?: number
/** 逻辑删除标记:1=已删除 */
deleted?: number
[key: string]: unknown
}
// ==================== 站点信息 ====================
/**
* CMS getSiteInfo 返回的完整站点信息
*/
export interface CmsSiteInfo {
websiteId?: number
websiteName?: string
websiteCode?: string
websiteIcon?: string
websiteLogo?: string
websiteDarkLogo?: string
websiteType?: string
/**
* 模板主键(cms_website.template_id → app_template.id
* ⚠️ 主键存在跳号风险(历史上 id=8 被跳过),不可直接补零当目录名,请优先用 templateCode。
*/
templateId?: number
/**
* 模板标识(app_template.code,形如 'template-08'),与前端 app/templates/ 目录一一对应。
* 上游 getSiteInfo 若未返回,由 SSR 侧 server/utils/template-map.ts 反查模板表回填。
*/
templateCode?: string
domain?: string
keywords?: string
/** 品牌标语 / Slogan(后台「网站设置」可配置) */
slogan?: string
phone?: string
email?: string
address?: string
icpNo?: string
policeNo?: string
content?: string | null
comments?: string
prefix?: string
status?: number
running?: number
expirationTime?: string
tenantId?: number
topNavs?: CmsNavigation[]
bottomNavs?: CmsNavigation[]
slide?: unknown
ads?: unknown
layout?: unknown
/** 社交外部链接(后台「网站设置」录入;标准字段名为 socialLinks,可为数组或 JSON 字符串) */
socialLinks?: SocialLink[] | unknown
/** @deprecated 兼容旧字段名,优先使用 socialLinks */
links?: SocialLink[] | unknown
/** 二维码(顶层字段,通常为 null;微信公众号二维码优先取 config.wxQrcode */
qrCode?: string | null
config?: SiteConfig
setting?: SiteSetting
serverTime?: ServerTime
}
/**
* 站点配置(来自 config 字段)
*/
export interface SiteConfig {
icpNo?: string
copyright?: string
SysDomain?: string
Domain?: string
address?: string
tel?: string
email?: string
wxQrcode?: string
wxQrcodeText?: string
[key: string]: unknown
}
/**
* 站点设置
*/
export interface SiteSetting {
id?: number
websiteId?: number
official?: boolean
market?: boolean
search?: boolean
share?: boolean
articleReview?: boolean
plugin?: boolean
editor?: number
searchBtn?: boolean
loginBtn?: boolean
floatTool?: boolean
copyrightLink?: boolean
maxMenuNum?: boolean
sortNumber?: number
deleted?: number
tenantId?: number
/**
* 在线留言是否要求滑块验证码。
* true=必须完成滑块验证才能提交;未设置/false=不显示滑块、直接提交。
* 由后台「网站设置」下发,配合前端 ContactForm 与后端 submit 校验。
*/
requireCaptcha?: boolean
/**
* 首页「优势模块」配置(JSON 字符串,存于 cms_website_setting.features
* 结构见 FeatureSectionSetting;未配置(null/空)时前端回退模板默认数据。
*/
features?: string
}
/**
* 首页优势模块 - 单卡片
*
* icon 为图标名(对应前端 FeatureIcon 的 36 个通用图标之一,如 building / box / case / message)。
* 后台只存名字,不存 SVG,避免样式耦合。
*/
export interface FeatureItem {
/** 卡片标题 */
title: string
/** 卡片描述 */
desc: string
/** 图标名(FeatureIcon 组件支持的名称集合) */
icon: string
}
/**
* 首页单区块开关(预留位共用)
*/
export interface HomeBlockToggle {
/** 是否显示该区块;默认 true */
enabled?: boolean
}
/**
* 首页首屏特性卡片区块
*/
export interface HeroBlock extends HomeBlockToggle {
/** 首屏右侧特性卡片文案(纯文本,最多 4 条) */
features?: string[]
}
/**
* 首页优势模块区块
*/
export interface AdvantagesBlock extends HomeBlockToggle {
/** 模块标题,如「我们的优势」 */
title?: string
/** 模块副标题 */
subtitle?: string
/** 卡片列表,最多 4 个 */
items?: FeatureItem[]
}
/**
* 首页各区块统一开关集合
*/
export interface HomeBlocks {
/** 首屏特性卡片 */
hero?: HeroBlock
/** 我们的优势 */
advantages?: AdvantagesBlock
/** 产品展示(预留位) */
products?: HomeBlockToggle
/** 案例展示(预留位) */
cases?: HomeBlockToggle
/** 新闻动态(预留位) */
news?: HomeBlockToggle
/** Banner 轮播 */
banner?: HomeBlockToggle
/** CTA 联系 */
cta?: HomeBlockToggle
}
/**
* 首页优势模块 - 完整配置
*
* 存于 cms_website_setting.featuresJSON 字符串)。
* 前端 useFeatures() 解析:未配置 → 回退模板默认 4 项;enabled=false → 隐藏整块。
*
* 2026-08-05 后台新增「首页区块总览」,支持区块化新结构:
* { hero, advantages, products, cases, news, banner, cta }
* 同时兼容旧结构(顶层 enabled/title/subtitle/items/heroFeatures)。
*/
export interface FeatureSectionSetting extends HomeBlocks {
/** 旧结构:是否显示整块(默认 true) */
enabled?: boolean
/** 旧结构:模块标题 */
title?: string
/** 旧结构:模块副标题 */
subtitle?: string
/** 旧结构:卡片列表 */
items?: FeatureItem[]
/** 旧结构:Hero 首屏右侧特性卡片文案 */
heroFeatures?: string[]
}
/**
* 服务器时间
*/
export interface ServerTime {
now?: string
today?: string
tomorrow?: string
afterDay?: string
week?: number
nextWeek?: string
timestamp?: number
}
// ==================== 导航 ====================
/**
* CMS 导航项(来自 topNavs / bottomNavs
*/
export interface CmsNavigation {
navigationId?: number
type?: number
title?: string
parentId?: number
/** 栏目背景图(后台「栏目管理」上传,存 cms_navigation.background 列) */
background?: string
/** 模型类型: index | page | article | product */
model?: string
code?: string
/** 跳转路径/路由(CMS 原字段) */
path?: string
/** 链接地址(自定义 URL,优先级高于 path,用于导航跳转) */
url?: string
component?: string
suffix?: string | null
target?: string
icon?: string | null
banner?: string | null
mpBanner?: string | null
color?: string | null
hide?: number
permission?: number
password?: string
position?: number
top?: number
bottom?: number
active?: unknown
meta?: unknown
style?: unknown
sortNumber?: number
home?: number
recommend?: boolean
comments?: string
deleted?: number
status?: number
tenantId?: number
pageId?: number
/** 绑定单页的访问路径 slugVO 透传,来自 cms_page.path);存在时 getNavLink 优先跳 /page/{slug} */
pagePath?: string
/** 绑定单页的标题(VO 透传,来自 cms_page.title */
pageTitle?: string
/** 绑定单页的状态(VO 透传,来自 cms_page.status */
pageStatus?: number
itemId?: number
isMpWeixin?: boolean
gutter?: number
span?: number
readNum?: number
userId?: number
merchantId?: number
lang?: string
langCategoryId?: number
/** 子导航 */
children?: CmsNavigation[]
/** 分类名称 */
categoryName?: string
/** 分类路径 */
categoryPath?: string
photo?: string | null
text?: string
[key: string]: unknown
}
// ==================== 文章 ====================
/**
* CMS 文章
*/
export interface Article {
id?: number
articleId?: number
title?: string
summary?: string
content?: string
/** 文章附件(结构同 PageAttachmentname/url/size/ext/sort */
files?: PageAttachment[]
/** 文章封面图(CMS 实际返回字段,优先级最高) */
image?: string
cover?: string
photo?: string
author?: string
source?: string
categoryId?: number
categoryName?: string
navigationId?: number
tags?: string[]
viewCount?: number
readNum?: number
sortNumber?: number
status?: number
/** 推荐标识:1=推荐(后台勾选「推荐」)。上游 cms-article 实际返回字段为 recommend。 */
recommend?: number
/** 置顶标识:>0 为置顶(部分租户文章表含 top 字段;上游未普遍支持时以 recommend 为准) */
top?: number
publishTime?: string
createTime?: string
updateTime?: string
[key: string]: unknown
}
// ==================== 产品 ====================
/**
* CMS 产品
*/
export interface Product {
id?: number
productId?: number
productName?: string
subtitle?: string
description?: string
content?: string
cover?: string
photo?: string
images?: string[]
categoryId?: number
categoryName?: string
tags?: string[]
price?: string
sortNumber?: number
status?: number
/** 置顶标识:>0 为置顶(首页明星产品据此过滤) */
top?: number
/** 推荐标识:1=推荐(部分 CMS 版本产品表含 recommend 字段,与 top 并存) */
recommend?: number
createTime?: string
updateTime?: string
[key: string]: unknown
}
// ==================== 案例 ====================
/**
* CMS 案例
*/
export interface CaseItem {
id?: number
title?: string
summary?: string
content?: string
cover?: string
images?: string[]
categoryId?: number
categoryName?: string
clientName?: string
projectTime?: string
tags?: string[]
sortNumber?: number
status?: number
/** 置顶标识:>0 为置顶(当前上游 cms-case 表暂无此字段,预留以便后续版本自动生效) */
top?: number
/** 推荐标识:1=推荐(当前上游 cms-case 表暂无此字段,预留以便后续版本自动生效) */
recommend?: number
createTime?: string
updateTime?: string
[key: string]: unknown
}
// ==================== 单页详情(/api/page/detail ====================
/**
* 单页详情返回结构
*
* 代理层 server/api/page/detail.get.ts 有两个数据来源,返回结构统一:
* 1. ?navigationId=4296 → 上游 /cms/cms-navigation/{id},把节点内嵌的 design 扁平化
* 2. ?path=about → 上游 /cms/cms-page/getByPath/{slug},即「单页管理」cms_page 表
*
* 因此 navigationId(来源1专有)与 pageId/path/keywords/description(来源2专有)
* 均为可选字段,使用前先判空。前端用 status 区分三种状态:
* - 'ok' :记录存在且 content 非空,渲染正文
* - 'empty' :记录不存在,或记录存在但正文未录入
* - 'error' :上游接口异常
*/
export interface PageDetail {
status: 'ok' | 'empty' | 'error'
/** 导航节点 ID(仅 navigationId 入口返回) */
navigationId?: number
/** 单页 ID(仅 cms_page 入口返回) */
pageId?: number
/** 单页路径 slug,如 about / contact(仅 cms_page 入口返回) */
path?: string
title: string
model: string
parentId: number
/** 正文 HTML(来源1为 design.content,来源2为 cms_page.content */
content: string
/** 结构化设计(来自 design.layout,预留) */
layout: unknown
/** 头图(来源1为 design.photo,来源2为 cms_page.image */
photo: string | null
/** SEO 关键词(仅 cms_page 入口返回) */
keywords?: string
/** SEO 描述(仅 cms_page 入口返回) */
description?: string
hasContent: boolean
updateTime: string | null
/** 页面附件列表(仅 cms_page 入口返回,来自 cms_page.attachments JSON */
attachments?: PageAttachment[]
/** empty / error 时的说明 */
message?: string
}
/**
* 单页附件(来自后台「单页管理」cms_page.attachments
*
* 与 website-admin 上传组件写入结构保持一致:
* { name, url, size, ext, sort }
*/
export interface PageAttachment {
/** 原始文件名(含扩展名),用于展示与下载文件名 */
name: string
/** 文件直链(MinIO / OSS 全 URL */
url: string
/** 文件大小(字节) */
size?: number
/** 扩展名(小写,不含点),如 xlsx / pdf */
ext?: string
/** 排序(升序) */
sort?: number
}
// ==================== 兼容旧类型 ====================
/**
* 站点信息(兼容层,映射到 CmsSiteInfo
* @deprecated 使用 CmsSiteInfo
*/
export type SiteInfo = CmsSiteInfo
/**
* CMS 页面(兼容旧代码)
*/
export interface CmsPage {
id?: number
siteId?: number
slug?: string
title?: string
description?: string
keywords?: string
type?: string
sortNumber?: number
showInNav?: boolean
navName?: string
content?: string
cover?: string
seoTitle?: string
seoDescription?: string
createTime?: string
updateTime?: string
}
// ==================== 订阅与域名 ====================
/**
* 应用订阅状态
*/
export interface SubscriptionStatus {
subscribed?: boolean
status?: string
expireTime?: string
startTime?: string
expired?: boolean
subscriptionId?: number
productId?: number
productName?: string
}
/**
* 域名解析结果
* 后端改造后可直接返回完整 AppProduct
*/
export interface DomainResolveResult {
tenantId?: string
appId?: string
templateId?: string
found?: boolean
/** 后端可选返回的完整应用产品信息 */
appProduct?: AppProduct
}
// ==================== 应用产品 ====================
/**
* 应用产品信息(app_product 表)
* 后端库位于 https://websopy-api.websoft.top,表前缀 app_
*/
export interface AppProduct {
productId?: number
productName?: string
/** 应用唯一标识码 */
productCode?: string
/** 应用类型:1=网站 等 */
appType?: number
/** 绑定域名(核心字段) */
domain?: string
/** 域名前缀 */
prefix?: string
/** 前台访问 URL */
homeUrl?: string
/** 后台管理 URL */
adminUrl?: string
/** 应用图标 */
icon?: string
/** 应用 Logo */
logo?: string
/**
* 关联模板主键(app_template.id),也可能已是字符串 "template-XX"。
* ⚠️ 主键存在跳号风险,解析目录名请优先用 templateCode,本字段仅兜底。
*/
templateId?: number | string
/** 关联模板标识(app_template.code,形如 'template-08'),与前端模板目录一一对应 */
templateCode?: string
status?: number
running?: number
expirationTime?: string
tenantId?: number
createTime?: string
updateTime?: string
[key: string]: unknown
}
/**
* 应用域名绑定(app_domain 表)
* 自定义域名 → 租户/应用的绑定关系,作为「app_product.domain」之外的兜底解析来源
*/
export interface AppDomain {
/** 绑定域名 */
domain?: string
/** 关联租户 ID(核心字段,用于下游接口 TenantId 头) */
tenantId?: number
/** 关联应用产品 ID(用于回查完整 AppProduct 取模板/品牌) */
productId?: number
/** 关联模板 ID(可选,优先以 AppProduct 为准) */
templateId?: number | string
status?: number
[key: string]: unknown
}
// ==================== 租户上下文 ====================
/**
* 租户上下文(Server Middleware 识别结果)
*/
export interface TenantContext {
tenantId: string
appId: string
templateId?: string
source?: 'domain' | 'subdomain' | 'env' | 'header'
host?: string
/** 应用产品信息(中间件查询后填充) */
appProduct?: AppProduct
}
// ==================== 社交链接 ====================
export interface SocialLink {
type?: string
name?: string
url?: string
icon?: string
}