低代码开发平台设计方向之一:基于菜单体系的开发平台设计
一、一种常见的设计取向
低代码平台有一个绕不开的问题:用它搭出来的系统,怎么组织?
行业里有两条主流路径:
| 路径 | 应用的一等公民 | 典型形态 |
|---|---|---|
| 页面画布 | 页面(一个应用 = 一堆页面 + 路由) | 拖拽设计器,最后生成页面文件 |
| 菜单体系 | 功能入口(一个应用 = 有类型的菜单树) | 系统 → 模块 → 菜单,逐级授权 |
本文讨论的是第二条:基于菜单体系的开发平台设计。
它的核心主张可以概括成一句:开发一个业务系统,本质上是在按「系统 → 模块 → 菜单」三层,堆积一组有类型的功能入口。
- 系统:一个应用或子系统
- 模块:功能分组
- 菜单 :具体功能入口,且带类型
在驰骋 CCFast 中,这三层分别对应实体 MySystem、Module、Menu,落库到 GPM_System、GPM_Module、GPM_Menu 三张表。
二、为什么是「三维组织」而不是「一张页面表」
2.1 这是 MIS 系统的经典骨架
「系统 → 模块 → 菜单」并不是新发明。过去二十年绝大多数管理信息系统(MIS)的导航结构都是这个样子:
应用
├── 模块 A
│ ├── 菜单 1
│ └── 菜单 2
└── 模块 B
└── 菜单 3
它是被反复验证过的一种组织方式,好处是结构稳定、扩展自然:加功能就在对应模块下加一个菜单,不会动到全局。
低代码平台把这个经典骨架显式建模成三张表 + 三个实体 ,就得到了一件很实在的东西:每一层都是一个独立的授权对象。
2.2 三维组织 = 三种授权颗粒度
看驰骋的实体定义,权限不是挂在某一个地方,而是每一层都能挂:
| 实体 | 权限挂载点(代码) |
|---|---|
MySystem(系统) |
map.AddRM_DtlSearch('应用权限', new PCenters(), ..., '&CtrlObj=System') |
Module(模块) |
map.AddRM_DtlSearch('权限', new PCenters(), ..., '&CtrlObj=Module') |
Menu(菜单) |
map.AddRM_DtlSearch('权限', new PCenters(), ..., '&CtrlObj=Menu') |
三处调用的结构一致,区别只在 CtrlObj 是 System / Module 还是 Menu------同一套权限中心(GPM_PowerCenter),挂到三层不同的控制对象上。
这就是「三维组织」真正的工程含义:权限的颗粒度不是二元的(有/无),而是分层的。
三、用一个比喻把权限讲清楚:院子、房子、家具
权限分层这件事,用「院子 / 房子 / 家具」来理解最直观:
| 层级 | 比喻 | 控制什么 | 代码载体 |
|---|---|---|---|
| 系统 | 院子 | 谁能进这个院子 | GPM_PowerCenter.CtrlObj = System |
| 模块 | 房子 | 进了院子的人,谁能进这间房 | CtrlObj = Module |
| 菜单 | 家具 | 进了房子的人,谁能用这件家具 | CtrlObj = Menu |
三层是逐级收窄的关系:
- 先控制院子:哪些人能进入这个应用
- 再控制房子:进入应用的人里,谁能看到这个模块
- 最后控制家具:能看到模块的人里,谁能使用这个具体功能
这个模型的价值在于:它把「授权」拆成了三个可以独立回答的问题,而不是把所有权限堆在一个「用户-功能」的大表里。
举例:一个集团 OA 系统(院子)里,「人事管理」模块(房子)下有「员工花名册」「薪资查询」(家具)。可以配置成:
- 院子:全集团可进
- 房子:只有 HR 部门和各级管理者能进「人事管理」
- 家具:「薪资查询」只对 HR 薪酬岗开放,「员工花名册」对本部门管理者开放
三级各有各的授权入口,互不干扰。
四、菜单是终点:功能页面与操作者的交互面
三层里,菜单是最终的功能页面,是用户真正「用」的东西。前面两层是组织结构,菜单才是交互。
所以菜单的定义里,除了归属(SystemNo / ModuleNo),还要回答「点了之后打开什么」。在 Menu.EnMap 里,这件事由几个字段共同决定:
| 字段 | 作用 |
|---|---|
MenuModel |
菜单类型(决定用哪个解析页、绑哪类后端) |
UrlPath |
Vue 组件文件路径,如 /@/WF/Comm/Search.vue |
UrlExt |
路由参数,如 x?EnName=TS.Demo.Student |
MobileUrlExt |
移动端入口 |
Icon |
图标 |
ListModel |
列表模式(编辑模式 / 视图模式) |
IsEnable |
是否启用 |
其中 MenuModel 是最关键的一个------它让菜单不再是「一个 URL + 一个标题」,而是「一个有语义的类型」。这一点是下一篇文章(《菜单的类型》)的主题,这里先按下。
值得注意的是 UrlPath 的约束:
此项需要配置连接对应的 vue 文件地址,仅支持 /src/ 下文件
这句话透露了菜单体系的边界:菜单指向的是平台内已存在的能力 ,不是任意页面。要接外部系统,走 SelfUrl 类型或 IframeOpenType(内嵌 / 外开)。这条约束是有意为之------它保证了「菜单是工具箱里的工具」,而不是「什么都能塞的洞」。
五、这套设计怎么落地:菜单向导与层级维护
设计理念要能落地,得有对应的操作入口。从代码看,至少有三类:
5.1 新建菜单走向导,不走空白路由
GPN_Menu.ts 是「新建积木菜单」向导,Init() 里用 AddGroup 把工具分组:
| 分组 | 内容 |
|---|---|
| A 流程 | 公文、请假、订单、物联网流程等 |
| B 单据 | 报销单、出库单、出门单 |
| C 实体 | 资产、学生、车辆等主数据 |
| U 字典管理 | 省份、职位、大类小类 |
| P 工程 | 项目管理、甘特图、任务跟踪 |
| E 大屏列表 | 白色 / 蓝色大屏 |
| J 视图 | 数据源驱动的列表、左树右表、甘特视图 |
| D 高代码 | TS 全栈配置,基于页面模式 |
| G 页面引用 | 自定义 URL、Tabs 容器 |
| H OA 应用 | 日历、知识库、工作日志等 |
实施人员是先选工具类型,再配置,而不是从空白开始。这就是「低」的来源------类型决定了打开哪个解析页、绑哪类实体、初始化哪些按钮与权限。
5.2 模块 → 菜单的层级维护
PG_Module2Menu.ts 是「分组编辑面板」模式:左侧是模块列表、右侧是该模块下的菜单,支持拖拽排序、组内新建。它的 Init() 直接从 GPM_Module / GPM_Menu 取数:
this.GroupsEns = await new Modules().Retrieve('SystemNo', this.PKVal, 'Idx');
this.DtlEns = await new Menus().Retrieve('SystemNo', this.PKVal, 'Idx');
this.RefKey = MenuAttr.ModuleNo;
RefKey = ModuleNo 就是「菜单属于哪个模块」的关联键------层级关系在数据层是外键,不是靠命名约定。
5.3 权限在三级各自配置
GPN_PCenter.ts 是权限配置向导,把授权方式建模成一组选项(AddBlank / SelectItemsBy*):
| 授权方式 | 代码 | 含义 |
|---|---|---|
| 所有人 | AuthType.Anyone |
本组织所有人 |
| 管理员 | AuthType.Adminer |
管理员 |
| 管理员 + 二级管理员 | AuthType.AdminerAndAdmin2 |
两级管理员 |
| 按人员 | AuthType.Emps |
选人 |
| 按角色 | AuthType.Stations |
选角色 |
| 按部门 | AuthType.Depts |
选部门 |
| 按 SQL | AuthType.SQL |
SQL 返回数量 |
| 指定组织的指定岗位 | AuthType.SpecOrgStations |
高级权限(集团场景) |
同一套向导,换个 CtrlObj 就能用在系统、模块、菜单上------授权方式复用、授权对象分层。
六、这套设计的优点
6.1 权限颗粒度天然分层
不需要额外设计「权限模型」,因为层级本身就是权限模型。系统 / 模块 / 菜单三级各自可授权,避免了「只能整应用授权」或「只能功能级授权」的两个极端。
6.2 结构稳定,扩展自然
「系统 → 模块 → 菜单」是 MIS 的经典骨架,团队不需要学习新概念。加功能 = 加菜单,加分组 = 加模块,不会牵动全局。
6.3 菜单与实现解耦
菜单存的是「类型 + 路径 + 参数」(MenuModel / UrlPath / UrlExt),不是硬编码的页面。同一类功能,换实体名就是另一个菜单;换个模块归属,就是另一个位置。菜单是元数据,不是代码。
6.4 与流程、表单、权限共享底座
CCFast 的菜单不是独立于 BPM 的另一套东西。它把流程、单据、实体、大屏、视图都挂到同一棵导航树上,共享同一套组织(Port_*)与权限中心(GPM_PowerCenter)。一个应用导航树背后是同一套引擎。
6.5 交付物明确
按菜单体系搭出来的交付物,是一棵「可导航、可授权、可运行」的菜单树,而不是一堆零散页面文件。这对项目验收、权限交接、后期维护都更友好。
七、代价与边界
任何设计都有代价,这套菜单体系也不例外:
| 边界 | 说明 |
|---|---|
| 需要理解分类 | 实施人员必须懂「单据 / 实体 / 字典 / 视图」的区别,否则容易选错工具 |
| 强定制 UI 受限 | 菜单指向平台内能力,重度定制前端仍要走高代码或外部页面 |
| 层级不是越深越好 | 系统 / 模块 / 菜单三层够用;再往下加层级,授权与导航都会变复杂 |
| 组织模型是前提 | 三级授权依赖 Port_* 组织数据,组织不准,权限就会失真 |
需要说清楚的是:菜单体系是「一种」设计取向,不是唯一解。 如果应用形态本身不是「功能入口的集合」(比如强流程编排、纯数据加工),页面画布或模型驱动可能更合适。选型看场景,不看口号。
八、小结
基于菜单体系的开发平台设计,可以归纳成三句话:
- 应用 = 系统 / 模块 / 菜单 三层的堆积,每一层是有类型、有归属、可授权的结构;
- 三层 = 三种权限颗粒度,像院子、房子、家具一样逐级收窄;
- 菜单是终点,是功能页面与操作者交互的界面,其它两层是它的组织与授权骨架。
在驰骋 CCFast 中,这三句话分别落地为 GPM_System / GPM_Module / GPM_Menu 三张表、CtrlObj = System/Module/Menu 的三级授权,以及 GPN_Menu 向导 + PG_Module2Menu 层级维护的操作入口。
附录:本文涉及的代码索引
| 主题 | 路径 |
|---|---|
| 系统实体 | Vue3/src/CCFast/GPM/MySystem.ts |
| 模块实体 | Vue3/src/CCFast/GPM/Module.ts |
| 菜单实体 | Vue3/src/CCFast/GPM/CCMenu/Menu.ts |
| 新建菜单向导 | Vue3/src/CCFast/GPM/CCMenu/GPN_Menu.ts |
| 模块 → 菜单维护 | Vue3/src/CCFast/GPM/PG_Module2Menu.ts |
| 权限中心实体 | Vue3/src/CCFast/GPM/CCMenu/PowerCenter.ts、PCenter/PCenter.ts |
| 权限配置向导 | Vue3/src/CCFast/GPM/PCenter/GPN_PCenter.ts |
| 菜单设计说明 | Vue3/src/CCFast/GPM/CCMenu/readme.md |
本文基于当前仓库源码静态分析整理,具体能力以你所使用的产品版本与实测为准。