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
+70
View File
@@ -0,0 +1,70 @@
/**
* 导航树聚合工具
*
* 场景:父级栏目(如「核心业务」)下挂多个子栏目,父栏目自身通常不放内容。
* 访问父级栏目时,需要聚合其下所有子栏目的数据。
*
* 做法:从完整导航树(allNavigations)中找到目标节点,递归收集
* 该节点自身 + 所有后代的 navigationId,交给后端按 category_id IN (...) 查询。
*/
interface NavNode {
navigationId?: number | null
children?: NavNode[] | null
}
/**
* 递归收集某导航节点自身 + 所有后代的 navigationId
*
* @param navId 目标栏目 navigationId(来自路由参数或导航查找)
* @param navigations 完整导航树(allNavigations
* @returns 包含自身及所有后代 navigationId 的数组;未命中时返回空数组
*/
export function collectDescendantNavIds(
navId: number | string | undefined,
navigations: NavNode[] = []
): number[] {
if (navId == null) return []
const target = Number(navId)
const result: number[] = []
const collectSelfAndChildren = (node: NavNode) => {
if (node?.navigationId != null) {
result.push(Number(node.navigationId))
}
node?.children?.forEach((child: NavNode) => collectSelfAndChildren(child))
}
const findNode = (items: NavNode[]): boolean => {
for (const it of items || []) {
if (Number(it?.navigationId) === target) {
collectSelfAndChildren(it)
return true
}
if (it?.children?.length && findNode(it.children)) return true
}
return false
}
findNode(navigations)
return result
}
/**
* 判断某 navigationId 是否为父栏目(拥有子栏目)
*/
export function isParentNavigation(
navId: number | string | undefined,
navigations: NavNode[] = []
): boolean {
if (navId == null) return false
const target = Number(navId)
const find = (items: NavNode[]): boolean => {
for (const it of items || []) {
if (Number(it?.navigationId) === target) return (it?.children?.length ?? 0) > 0
if (it?.children?.length && find(it.children)) return true
}
return false
}
return find(navigations)
}