用一套 JSON Schema DSL 把「页面」做成可配置产品
1. 问题:后台 CRUD 为什么总是越写越重
传统写法里,一个模块常见会散落在多处:
- 路由里定义入口参数
- 页面里请求数据、拼查询条件、渲染表格列
- 搜索组件里再维护一份“字段到组件”的映射
- 新增/编辑/详情又各写一套字段渲染逻辑
结果就是:字段一变动,四处改;新增一个模块,复制粘贴一套“模板工程”。
Elpis 的关键思路是:把“模块”抽象成 Project Model(项目模型)→ Menu(菜单)→ Module(模块类型)→ SchemaConfig(schema 模块配置),让页面变成 DSL 的解释结果。
2. DSL 的“产品级”入口:项目模型(Project Model)
每个菜单项选择一种 moduleType:
schema:配置驱动的动态表格/搜索custom:跳到自定义页面- 其它如
sider/iframe:承载不同布局/容器形态
3. 同一字段多场景复用:把“字段”当成领域对象
schema 不仅描述字段的 type 和 label,更把一个字段在不同使用场景下的呈现与交互统一收敛到同一份定义里。例如:
tableOption:字段在表格列中的展示规则(宽度、溢出提示、格式化等)searchOption:字段在搜索栏中的输入形态与数据来源(组件类型、默认值、枚举/远程选项等)
可以把它理解为:字段本身是一个稳定的领域对象,而表格、搜索、新增/编辑等只是它在不同场景下的“投影”(projection)。这样做的直接收益是:
- 一次建模:字段只需定义一次,减少重复与漂移
- 隔离变化:用
xxxOption把场景差异封装起来,避免互相影响 - 更贴近产品表达:配置从“写 UI”变成“描述业务字段在各场景如何被使用
4. 解释器的核心:把“统一 schema”拆成“场景 schema”
Elpis 不是把 DSL 直接塞给组件,而是先做“降噪 + 分场景构造”。
在 useSchema :
- 定位菜单项:根据
route.query.key/sider_key找到当前模块配置 - 深拷贝 schema:避免解释过程修改源配置带来副作用
- 按场景提取字段:例如
table场景只抽取存在tableOption的字段,并把它统一落到option字段上
5. 搜索栏组件的可插拔:用动态组件承接 DSL
schema-search-bar 做法:
- 只关心
schema.properties的遍历 - 每个字段渲染一个动态
<component :is="..."> - 组件选择来自
SearchItemConfig[comType].component - 通过
getValue()聚合各子组件的值(并暴露reset/getValue给外界)
使得“新增一个搜索控件类型”不需要改搜索栏主体逻辑,只需要:
- 新增一个控件组件
- 在
SearchItemConfig注册映射 - 在字段的
searchOption.comType指向它
6. 表格行为配置化:按钮不是写死的事件,而是“事件键 + 参数映射”
你在 tableConfig 里把按钮表达成:
eventKey:平台内置事件(例如展示组件、删除等)eventOption:事件细节(比如删除要拼哪些参数)params支持从行数据映射:例如schema::product_id这种“从表格行取值”的约定
这类“参数映射 DSL”能把 80% 的按钮逻辑变成声明式配置,剩下 20% 再留给 custom 模块或扩展事件系统。
7. 小结:这套 DSL 真正解决的是什么
用一句话概括:它不是“把页面写成配置”,而是把后台系统常见的三件事抽成平台能力:
- 结构:项目 → 菜单 → 模块类型(统一入口)
- 字段:同字段多场景投影(减少重复与漂移)
- 解释:统一 schema → 场景 schema(降低组件复杂度)
- 体验:URL 回填默认值(让状态可分享、可复现)
- 扩展:控件映射 + 事件键(让新增能力更像“装插件”)