文档目录
前台导航系统设计

简介

本系统的前台导航由"后台配置 + 前台构建"两部分组成:管理员在后台维护导航条目(模块、路径、图标、排序、层级等),前台通过服务类将数据转换为模板可直接渲染的树形结构,并自动计算当前页高亮与外链目标。同时提供小程序端导航构建能力,输出端无关的导航数据供多端复用。

最新更新:系统已实现全面的导航高亮集中化,通过BaseController的pageFactVars()方法和NavigationBuilder的重大增强,提供了精确的菜单项高亮功能和自动模块上下文检测。

项目结构

围绕导航功能的关键目录与职责如下:

  • 前台
    • 控制器:首页控制器负责组装页面布局变量,注入顶部、中部、底部导航列表;基类控制器提供统一的页面事实变量管理
    • 服务:导航构建器负责从数据库读取、语言化、URL 生成、高亮判断、递归子菜单
  • 后台
    • 控制器:导航管理 CRUD、父级下拉片段、删除二次确认
    • 服务:导航业务逻辑(树形数据构建、目标选择器、插入/更新/删除)
    • 模型:导航表 ORM 定义、排序查询、父子关系校验
    • 路由:声明式资源路由 + 自定义 POST 片段
  • 核心
    • 小程序导航构建器:面向小程序的导航数据构建,URL 由路由层统一生成
graph TB
subgraph "前台"
IC["IndexController<br/>首页控制器"]
FBC["Front BaseController<br/>前台基类控制器"]
NB["NavigationBuilder<br/>前台导航构建器"]
end
subgraph "后台"
NC["NavController<br/>导航管理控制器"]
NS["NavService<br/>导航服务"]
NM["Nav<br/>导航模型"]
NR["nav.php<br/>路由"]
end
subgraph "核心"
CBC["Core BaseController<br/>核心基类控制器"]
MPNB["MiniprogramNavigationBuilder<br/>小程序导航构建器"]
end
IC --> FBC
FBC --> NB
NC --> NS
NS --> NM
NR --> NC
MPNB --> |"读 nav 表"| NB
CBC --> FBC

图表来源

  • IndexController.php:80-88
  • BaseController.php:81-109
  • NavigationBuilder.php:72-90
  • BaseController.php:45-84

核心组件

  • 前台导航构建器:负责加载状态为启用且按排序的导航记录,进行语言化、URL 生成、高亮判定、递归子菜单构建,并提供 top/middle/bottom 三种类型入口;middle 支持传入当前模块与内容 ID 以精确高亮,新增静态guideMatches()方法确保精确匹配
  • 前台基类控制器:提供pageFactVars()方法统一管理页面事实变量,包括导航列表的fail-safe处理和顶层cur变量的自动派生
  • 后台导航服务:实现导航的增删改查、树形展示、父级下拉、目标选择器(页面、栏目、单页模块)、图标处理与审计日志
  • 导航模型:封装 nav 表的 ORM 访问,提供有序全表查询、父子关系检查、父级读取
  • 小程序导航构建器:独立于 Smarty 的导航数据构建,输出 id/name/icon/module/guide/url/status/sort,url 通过路由层生成

章节来源

  • NavigationBuilder.php:28-208
  • BaseController.php:81-109
  • NavService.php:34-456
  • Nav.php:24-102
  • MiniprogramNavigationBuilder.php:25-63

架构总览

前台导航的数据流:

  • 控制器在 layoutVars 中调用导航构建器的 top/middle/bottom,返回数组给模板渲染
  • 前台基类控制器的 pageFactVars() 方法统一处理页面事实变量,包括导航列表的fail-safe处理和cur变量派生
  • 导航构建器一次性缓存全表数据,逐条语言化、解析 URL、计算高亮、递归子项
  • 后台通过资源路由暴露导航管理的 CRUD,服务层负责数据组装与持久化
sequenceDiagram
participant C as "前台 IndexController"
participant FBC as "Front BaseController"
participant B as "NavigationBuilder"
participant DB as "数据库(nav)"
participant T as "模板"
C->>FBC : view(template, data)
FBC->>FBC : pageFactVars(合并数据)
FBC->>B : middle() (自动检测模块上下文)
B->>DB : 读取 status=1 并按 sort 排序
DB-->>B : 导航行集合
B->>B : 语言化 / URL 生成 / 高亮判断 / 递归子菜单
B-->>FBC : 导航树数组 + contextModule
FBC->>FBC : 设置 cur = contextModule
FBC->>T : 注入所有导航变量和cur
T-->>T : 渲染导航菜单

图表来源

  • BaseController.php:53-61
  • BaseController.php:81-109
  • NavigationBuilder.php:72-90
  • NavigationBuilder.php:111-168

详细组件分析

前台导航构建器(NavigationBuilder)

  • 职责
    • 提供 top/middle/bottom 三类导航构建方法
    • 一次实例内全表静态缓存,避免重复读库
    • 对 module=nav 的条目使用 guide 直链或相对路径匹配;其他模块通过路由参数生成 URL
    • 基于当前模块与内容 ID 计算 cur 高亮
    • 递归构建 child 子菜单
    • 新增:静态guideMatches()方法实现精确路径匹配,避免前缀误亮
    • 新增:contextModule()静态方法追踪最近模块上下文
  • 关键流程
    • buildType 根据 type 与 parentId 过滤,语言化 name/guide,解析 URL,计算 cur,检测是否有子节点并递归
    • staticGuideMatches 对静态 guide 做规范化路径全等比较,避免前缀误亮
    • loadRows 仅读取 status=1 并按 sort 排序,预热语言包
    • middle()方法支持自动模块上下文检测,当currentModule为空时从路由获取
flowchart TD
Start(["进入 buildType"]) --> Load["loadRows() 获取已启用导航"]
Load --> ForEach{"遍历每条记录"}
ForEach --> |parent_id/type 不匹配| Next["跳过"]
ForEach --> |匹配| Lang["语言化 name/guide"]
Lang --> IsNav{"module == 'nav' ?"}
IsNav -- 是 --> GenUrlNav["guide 为外链则 target=true<br/>否则 url=ROOT_URL+guide<br/>cur=staticGuideMatches(guide)"]
IsNav -- 否 --> GenUrlMod["UrlGenerator::navStorageToUrlArgs()<br/>route(...) 生成 URL<br/>cur=Util::isCurrent(...)"]
GenUrlNav --> Icon["按配置转换 icon"]
GenUrlMod --> Icon
Icon --> ChildCheck{"是否存在子项?"}
ChildCheck -- 是 --> Recurse["递归 buildType(type, id, ...)"]
ChildCheck -- 否 --> Append["加入结果集"]
Recurse --> Append
Append --> End(["返回导航树"])

图表来源

  • NavigationBuilder.php:111-168
  • NavigationBuilder.php:170-190
  • NavigationBuilder.php:195-208

章节来源

  • NavigationBuilder.php:28-208

前台基类控制器(Front BaseController)

  • 职责
    • 提供view()方法统一处理视图响应
    • 新增:pageFactVars()方法统一管理页面事实变量
    • 提供layoutVars()方法作为扩展点
    • 提供buildLinkUserCenter()方法处理会员中心导航
  • 关键功能
    • fail-safe处理:确保导航列表键始终存在
    • cur变量自动派生:从NavigationBuilder::contextModule()获取
    • route_module/route_action自动注入:当前路由信息
    • 懒求值:仅在真正渲染时才执行导航构建
flowchart TD
ViewCall["调用 view() 方法"] --> Merge["合并 action data + layoutVars"]
Merge --> PageFactVars["pageFactVars() 处理"]
PageFactVars --> CheckNav{"检查导航键是否存在"}
CheckNav -- 不存在 --> BuildNav["调用 NavigationBuilder 构建导航"]
CheckNav -- 存在 --> SkipNav["跳过构建"]
BuildNav --> SetCur{"检查 cur 是否存在"}
SkipNav --> SetCur
SetCur -- 不存在 --> GetContext["获取 NavigationBuilder::contextModule()"]
GetContext --> HasContext{"有上下文模块?"}
HasContext -- 是 --> SetCurValue["设置 merged['cur']"]
HasContext -- 否 --> SkipCur["跳过设置"]
SetCurValue --> InjectRoute["注入 route_module/route_action"]
SkipCur --> InjectRoute
InjectRoute --> Return["返回处理后的数据"]

图表来源

  • BaseController.php:53-61
  • BaseController.php:81-109

章节来源

  • BaseController.php:38-184

后台导航管理(NavController + NavService + Nav)

  • 控制器
    • index/create/edit/store/update/destroy/nav_select 覆盖完整 CRUD 与父级下拉片段
    • 通过 Service 获取列表、默认数据、编辑数据、目标选择器
  • 服务
    • buildNavListData:收集全表数据并在内存中构建树,填充展示用 url/icon/mark
    • buildNavTargetList:聚合页面、栏目分类、单页模块作为可选导航目标
    • insert/update/delete:写入/更新/删除导航记录,处理图标上传与审计日志
  • 模型
    • listAllOrdered/fetchAllOrdered:有序全表查询
    • hasChild/getParentId:父子关系与父级读取
classDiagram
class NavController {
+index(request) Response
+create() Response
+store(formRequest, request) Response
+edit(request) Response
+update(formRequest, request) Response
+destroy(request) Response
+navSelect(request) Response
}
class NavService {
+buildNavListData(type, excludeCurrentId) array
+buildNavDefaultData() array
+buildNavEditData(id) array|null
+buildNavParentSelectHtml(type, currentNavId) string
+insert(data, openIcon) int
+update(data, openIcon) void
+delete(id, post) array
+buildNavTargetList(module, id, currentModule) array
}
class Nav {
+listAllOrdered() Collection
+fetchAllOrdered() array
+hasChild(parentId) bool
+getParentId(id) mixed
}
NavController --> NavService : "依赖"
NavService --> Nav : "ORM 访问"

图表来源

  • NavController.php:50-175
  • NavService.php:59-456
  • Nav.php:65-102

章节来源

  • NavController.php:30-175
  • NavService.php:34-456
  • Nav.php:24-102
  • nav.php:21-29

小程序导航构建器(MiniprogramNavigationBuilder)

  • 职责
    • 按 type 筛选导航,输出端无关字段(id/name/icon/module/guide/url/status/sort)
    • url 通过路由层 Url::urlMini 生成,不依赖 Smarty
  • 适用场景
    • admin/api 共用,用于小程序端导航数据构建

章节来源

  • MiniprogramNavigationBuilder.php:25-63

依赖关系分析

  • 前台
    • IndexController 依赖 Front BaseController 提供的统一视图处理
    • Front BaseController 依赖 NavigationBuilder 进行导航构建和事实变量管理
    • NavigationBuilder 依赖数据库、URL 生成、配置、工具类进行数据增强
  • 后台
    • NavController 依赖 NavService 完成业务编排
    • NavService 依赖 Nav 模型、附件服务、配置、路由生成、审计日志
  • 共享
    • MiniprogramNavigationBuilder 直接读取 nav 表并通过路由层生成 URL,供多端复用
    • Core BaseController 提供基础HTTP响应处理能力
graph LR
IC["IndexController"] --> FBC["Front BaseController"]
FBC --> NB["NavigationBuilder"]
NB --> DB["数据库(nav)"]
NB --> CFG["配置(site.open_icon)"]
NB --> URLG["URL/路由工具"]
NC["NavController"] --> NS["NavService"]
NS --> NM["Nav(模型)"]
NS --> ATT["附件服务"]
NS --> AUD["审计日志"]
NS --> RG["路由生成"]
CBC["Core BaseController"] --> FBC

图表来源

  • IndexController.php:80-88
  • BaseController.php:81-109
  • NavigationBuilder.php:147-149
  • NavService.php:118-123
  • NavService.php:188-196
  • NavService.php:245-247

章节来源

  • IndexController.php:80-88
  • BaseController.php:81-109
  • NavigationBuilder.php:147-149
  • NavService.php:118-123
  • NavService.php:188-196
  • NavService.php:245-247

性能考量

  • 前台导航构建器采用实例级缓存,避免同一请求多次读库
  • 仅读取 status=1 的记录并按 sort 排序,减少无效数据
  • 语言包预热:在加载导航时批量预热 name/guide,降低后续语言化开销
  • 精确高亮判断:使用staticGuideMatches()方法进行精确路径比对,避免字符串前缀误判导致的额外分支
  • 懒求值机制:仅在真正渲染时才执行导航构建,减少不必要的计算开销
  • 后台树形构建在内存中进行,单次读取后递归组装,减少 N+1 查询

故障排查指南

  • 导航未显示
    • 检查 nav 表中对应记录的 status 是否为启用,type 是否与模板调用一致
    • 确认 module 与 guide 组合是否能通过路由生成有效 URL
  • 高亮异常
    • 对于 module=nav 的条目,确认 guide 与当前请求路径经根路径归一化后是否完全一致
    • 对于其他模块,确认 middle() 传入的 currentModule/currentId/currentParentId 是否正确
    • 新增:检查NavigationBuilder::contextModule()是否正确设置了模块上下文
  • 图标不显示
    • 若站点配置为图片模式,需确保 icon 字段为附件编号且可被附件服务解析为 URL
  • 删除失败
    • 若存在子导航,删除会提示不可删除;先删除子项再删除父项
    • 删除流程包含二次确认,注意 confirm_url 与超时时间
  • cur变量问题
    • 确认控制器调用了NavigationBuilder::middle()方法
    • 检查pageFactVars()是否正确处理了contextModule返回值

章节来源

  • NavigationBuilder.php:170-190
  • BaseController.php:95-100
  • NavService.php:257-289
  • NavService.php:118-123

结论

该导航系统通过清晰的职责划分实现了"后台配置、前台渲染、多端复用"的目标:最新的高亮集中化系统进一步提升了系统的稳定性和准确性。前台构建器专注于数据增强与高亮计算,基类控制器提供统一的页面事实变量管理,后台服务负责业务规则与数据持久化,小程序构建器提供跨端能力。整体设计兼顾了可读性、扩展性与性能,便于在多主题、多端环境下稳定运行。

最新更新亮点:

  • 精确的菜单项高亮:通过staticGuideMatches()方法避免前缀误亮
  • 自动模块上下文检测:middle()方法智能获取当前模块
  • 统一的页面事实管理:pageFactVars()方法确保导航列表和cur变量的可靠性
  • Fail-safe机制:即使控制器忘记调用导航构建,模板也不会报错

附录

  • 导航类型
    • top:顶部导航
    • middle:中部主导航(可携带当前上下文)
    • bottom:底部导航(为空时回退到中部)
  • 导航字段说明(示例)
    • name:名称(支持多语言键)
    • module:模块名(如 product、article、page、nav 等)
    • guide:模块引导标识(可为空、数字或路径)
    • parent_id:父级导航 ID
    • type:导航类型
    • sort:排序权重
    • icon:图标(文本或附件编号)
    • status:状态(启用/禁用)
  • 新增特性
    • contextModule:最近一次middle()解析的当前归属模块
    • staticGuideMatches:静态guide精确路径匹配方法
    • pageFactVars:页面事实变量统一处理方法
添加日期:2026-10-05