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

634 lines
17 KiB
TypeScript
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.
/**
* 统一 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
}