CabloyJS 全栈框架的"两级页签"本质上不是单纯的 UI 花样,而是一种把"业务模块切换"与"模块内多工作项切换"分层管理的工作台机制。
一、先说结论:两级页签分别代表什么
1)一级页签:业务模块 / 业务入口
一级页签对应 RouteTab,核心标识是 tabKey,标题和图标来自菜单信息 getTabInfo(tabKey),也就是它更像一个业务分组 或功能入口。
一级页签的业务意义是:
让用户在"用户管理、订单管理、内容管理、首页"等业务域之间切换。
它代表的是"我现在在哪个业务模块里工作"。
2)二级页签:模块内的具体工作对象 / 工作实例
二级页签对应 RouteTab.items[] 中的每个 IRouteViewRouteItem,核心标识是 componentKey,标题来自运行时 pageMeta.pageTitle,不是菜单标题。
二级页签的业务意义是:
在同一个业务模块下,同时打开多个具体任务、表单、记录或页面实例。
例如在"用户管理"一级页签下,可以同时有:
- 用户列表
- 新建用户
- 编辑用户 A
- 编辑用户 B
这就是典型的模块内多文档 / 多任务工作模式。
二、代码里是怎么把"两级"区分出来的
核心在路由被转换成两个 key:
tabKey:决定归属哪个一级页签componentKey:决定模块内是不是同一个页面实例
1)tabKey:业务归组
ini
const tabKey = this._handleRouteProp(route, 'tabKey') || componentKey;
含义是:
- 如果路由显式声明了
meta.tabKey,那么多个页面可以归到同一个一级页签下 - 如果没声明,就退化成
tabKey = componentKey,也就是"一页一个一级页签"
这说明两级页签不是硬编码死的,而是一个按业务需要启用的分组机制。
2)componentKey:页面实例区分
componentKey 优先取路由 meta 配置,否则按 route name/path 推导。
这里特别重要的是:
- 默认可能按
route.path区分 - 如果
componentKeyMode === 'nameOnly',则按路由名归一
业务含义是:
- 有些页面希望"同类页面共用一个实例"
- 有些页面希望"不同参数/路径形成不同实例"
也就是说,二级页签不是简单"多开页面",而是支持按业务语义控制"是否多开"。
三、为什么要做两级,而不是只做一级多标签
这是这套机制最核心的业务价值。
1)避免一级页签爆炸
如果只有一级页签,那么每打开一个"编辑用户""查看订单""新建文章",顶层标签都会越来越多,最后变成:
- 用户管理
- 编辑用户A
- 编辑用户B
- 订单管理
- 编辑订单X
- 创建订单
- 内容管理
- 编辑文章1
- 编辑文章2
这样顶层结构就失去"业务导航"的意义了。
两级设计后:
- 一级页签保持稳定:用户管理 / 订单管理 / 内容管理
- 二级页签承载临时工作项:编辑A / 编辑B / 创建 / 查看详情
这就把导航层 和工作层分开了。
2)让用户保留"模块上下文"
点一级页签时,系统不是简单跳到固定首页,而是优先回到该一级页签关联的首个路由项。
这意味着:
用户切回"用户管理"时,不只是重新打开菜单,而是回到这个业务域当前的工作上下文。
业务上这很重要,因为后台系统用户常常是在多个模块间来回切换处理事务,而不是一次只做一个任务。
3)支持同模块下的多对象并行处理
RouteTab.items 是一个数组,一个一级页签下可以挂多个页面实例。
这意味着同一个模块下可以并行做多件事,比如:
- 在"商品管理"里同时编辑商品 A、B、C
- 在"订单管理"里同时查看订单详情、发货页、售后页
- 在"内容管理"里同时新建文章、修改文章、预览文章
这比传统单页返回列表再点下一条的方式,更接近桌面应用/IDE 的工作体验。
4)二级页签标题是"任务标题",不是"菜单标题"
一级页签标题来自菜单 title/icon,而二级页签标题来自页面运行时 pageMeta.pageTitle。
业务意义很明确:
- 一级页签告诉你"在哪个模块"
- 二级页签告诉你"当前具体在处理什么对象"
这是两个不同层次的信息。
四、这套机制还有几个很有业务味道的设计
1)一级页签有"固定业务入口"概念
Admin 初始化时默认放入一个 affix 页签 /,在模型里 affix 页签不可随意被裁剪、也不会轻易消失。
业务上就是:
首页、主工作台、固定入口是"常驻工作台"的一部分。
2)二级页签里有一个"隐形首项"
渲染二级页签时会跳过 componentKey === tabKey 的那一项,同时模型里又明确"不删除 first tabItem"。
这说明每个一级页签下其实有一个基础入口页作为锚点,但这个锚点不一定展示成二级标签。
业务意义:
一级页签不是空容器,它总有一个基础着陆页;二级页签显示的是"额外打开的工作项"。
这让结构更稳定。
3)支持未保存、创建态、编辑态提示
二级页签图标会根据 pageMeta 变化:
pageDirty:星号formScene === create:新建图标formScene === edit:编辑图标
业务意义是:
二级页签不仅表示"打开了什么",还表示"这个工作项当前处于什么状态"。
非常适合后台表单密集型业务。
4)支持容量控制,防止工作台失控
模型支持:
- 一级页签最大数量
max - 二级页签最大数量
maxItems - 超出后按最近最少使用倾向清理旧项
业务意义:
系统允许用户多开,但不会无限膨胀到难以管理。
五、可以把它理解成什么业务模型
我觉得最准确的理解是:
一级页签 = Workspace / Business Domain
代表一个业务工作区,比如:
- 首页
- 用户管理
- 订单管理
- 内容管理
二级页签 = Task / Document / Instance
代表这个工作区里的具体工作对象,比如:
- 新建用户
- 编辑用户张三
- 编辑用户李四
- 查看订单 2026001
这其实很像:
- 浏览器:站点级标签 + 页面内上下文
- IDE:项目/目录 + 打开的文件
- ERP/后台:模块入口 + 当前处理单据/表单
所以它的业务意义不是"多一行 tabs",而是:
把后台系统从"菜单跳转式操作"升级成"工作台式并行处理"。
六、结合 Admin 这套实现,可得出的最终判断
在 Admin 布局里,这套两级页签机制的目标非常明确:
- 一级页签承载菜单级业务导航
- 二级页签承载当前业务模块下的多任务/多对象并行处理
- 通过
tabKey与componentKey解耦"业务归组"和"页面实例" - 通过
pageMeta给工作项增加状态语义 - 通过 cache / keepAlive / prune 让工作台既连续又可控
七、用一句话概括
一句话总结:
Cabloy 的两级页签机制,本质上是在后台工作台中,把"业务模块切换"与"模块内多文档/多任务处理"分层管理,从而同时兼顾导航稳定性、并行作业能力、页面状态表达和工作现场恢复。
GitHub:github.com/cabloy/cabl...