DSL 交互与联动
CRUD 谁都会做。低代码平台真正的墙,是交互:
- 录入地址时,选了省份,城市下拉要跟着变;
- 「折扣」字段只在订单类型是「批发」时才出现、才必填;
- 销售订单详情页,「提交」按钮只在草稿态可见,「审核」只在待审态可见;
- 点一次「发布」,要弹确认 → 调接口 → 弹成功提示 → 刷新菜单——一串后续动作。
手写时,这些逻辑散落在前端的 onChange、useEffect、if (status === ...) 里。改一条规则要翻好几个组件,而且测不全。AuraBoot 把整个交互层收进 DSL,用三套声明式机制覆盖:条件表达式、LinkageEngine 联动规则、Action 三层架构。这一页用销售订单 / CRM 线索这类真实场景,逐个讲透。
前置:DSL 引擎原理 · Command。本页所有机制/数字来自 live 源码:
business-functions.ts、LinkageEngine.ts、ActionRegistry.ts。
1. 条件表达式 —— 字段与按钮的「看情况」
最轻量的交互:某个东西要不要显示、能不能编辑、是不是必填,取决于当前记录或表单的其它值。DSL 用表达式声明,而不是写事件回调。
1.1 三个上下文前缀
表达式里能读什么,取决于页面类型:
| 页面类型 | 前缀 | 示例 |
|---|---|---|
| 列表(行操作) | row. | row.status === 'draft' |
| 详情 | record. | record.pe_so_status === 'submitted' |
| 表单 | form. | form.order_type === 'BULK' |
1.2 字段级:visibleWhen / disableWhen / 条件必填
{ "field": "discount", "visibleWhen": "form.order_type === 'BULK'" }字段还支持运行时条件控制 visible / readonly / required:
{
"conditional": {
"visible": "#orderType == 'ONLINE'",
"readonly": "#status == 'completed'",
"required": "#amount > 10000"
}
}「折扣字段只在批发单出现」「金额过万才必填审批意见」——都是一行声明,不是一段 JS。
1.3 fn.* —— 72 个业务函数
表达式不止能做比较,还能调一整套 fn.* 业务函数(共 72 个,来自 business-functions.ts),让声明式表达式有接近 Excel 公式的表达力:
| 分类 | 数量 | 代表函数 |
|---|---|---|
| 逻辑 | 7 | IF IFS CASE SWITCH AND OR NOT |
| 文本 | 16 | CONCAT LEFT MID LEN REPLACE CONTAINS SPLIT … |
| 数值 | 13 | ROUND FLOOR CEIL ABS MOD CLAMP PERCENT … |
| 日期 | 14 | NOW TODAY DATEADD DATEDIFF DATEFORMAT WEEKDAY … |
| 类型 | 10 | ISBLANK ISNUMBER COALESCE DEFAULT TONUMBER … |
| 集合 | 12 | COUNTIF SUMIF PLUCK UNIQUE GROUPBY SORTBY JOIN … |
${fn.IF(form.amount > 1000, 'VIP', 'Standard')}
${fn.ROUND(form.price * form.qty, 2)}
${fn.COALESCE(form.nickname, form.name, 'Anonymous')}
${fn.DATEDIFF(form.startDate, form.endDate, 'day')}2. LinkageEngine —— 字段间的连锁反应
条件表达式是「字段看自己周围」;联动规则是「一个字段变了,主动去改别的字段」。这正是省/市级联、按选择动态加载选项、自动算总价这类需求的归宿。可视化配置在 Page Designer 右侧「联动」面板,落到 DSL 是 linkageRules。
2.1 8 种联动 action
一条规则 = 触发器(fieldCode + change/blur/focus + 可选 condition)+ 一组 action。8 种 action 类型(来自 LinkageActionType):
| action | 作用 |
|---|---|
show / hide | 显示 / 隐藏目标字段 |
enable / disable | 启用 / 禁用目标字段 |
setRequired | 动态设必填 |
setValue | 给目标字段赋值(支持表达式) |
setOptions | 动态加载选项(dict / api / parent 数据源) |
validate | 触发目标字段的校验规则 |
2.2 三个真实场景
{
"linkageRules": [
{
"id": "rule-show-subcategory",
"trigger": { "fieldCode": "category", "event": "change" },
"condition": "form.category === 'other'",
"actions": [{ "type": "show", "targets": ["subcategory"] }],
"enabled": true
},
{
"id": "rule-cascade-city",
"trigger": { "fieldCode": "province", "event": "change" },
"actions": [{
"type": "setOptions",
"target": "city",
"dataSource": { "type": "api", "url": "/api/cities?province=${form.province}" }
}],
"enabled": true
},
{
"id": "rule-compute-total",
"trigger": { "fieldCode": "qty", "event": "change" },
"actions": [{ "type": "setValue", "target": "total", "value": "form.qty * form.unit_price" }],
"enabled": true
}
]
}分类选「其它」才显示子类;选省份动态拉城市;改数量自动算总价——全是配置。
2.3 多级级联(v2)
当一个 setValue 改了的字段自己也挂了联动规则,引擎会自动继续触发,形成级联链:
province(change) → setValue city="default"
→ city(change) → show district, setRequired zipcode
→ district(change) → setValue address="…"- 最大深度:
maxDepth(默认 5),防无限循环;到顶会 console 告警。 - 每级新上下文:每层级联拿到反映最新表单值的全新
ExpressionContext。 - 关闭级联:设
maxDepth: 1退回单级行为。
联动产生的
FieldMeta覆盖优先于静态的 visible/readonly/required 表达式。
3. Action 三层架构 —— 从一次点击到一串编排
按钮被点之后发生什么,由 Action 系统统一处理。它是三层互补结构(useActionHandler → ActionRegistry → FlowRunner),不是冗余:
| 层 | 职责 | 触发 |
|---|---|---|
useActionHandler | 入口,normalizeAction 兼容旧格式,按 type 分发 | 按钮点击 |
ActionRegistry | 约 30 个原子操作(导航 / 删除 / 提示 / API 请求 / 数据源刷新…) | builtin 或 flow 步骤 |
SchemaRuntime + FlowRunner | 多步编排(含条件分支) | flow |
3.1 一个按钮,一种 action(5 形态)
ActionDef 用 type 区分行为,一个按钮只挂一种:
| type | 行为 |
|---|---|
command | 执行后端命令(走命令管道) |
navigate | 页面导航(可携带 command,用于新建/编辑) |
builtin | 内置 UI 操作(search/reset/refresh/export/new/edit/view/delete/back/noop) |
flow(inline steps) | 1–3 步简单流程,一次性 |
flow(handler 引用) | 复杂流程(5+ 步 / 条件分支)或多按钮复用,定义在 schema handlers |
// 执行命令 + 危险确认
{ "code": "delete", "label": "delete",
"action": { "type": "command", "command": "crm:delete_lead" },
"confirm": "delete.confirm", "danger": true }
// 导航到表单 + 携带命令
{ "code": "edit", "label": "edit",
"action": { "type": "navigate", "to": "crm_lead_form", "command": "crm:update_lead" } }3.2 多步 flow —— 点一次,跑一串
{ "code": "publish", "label": "publish",
"action": { "type": "flow", "steps": [
{ "action": "dialog.confirm", "args": { "message": "Publish?" } },
{ "action": "api.request", "endpoint": "/api/dashboards/{pid}/publish", "method": "POST" },
{ "action": "toast.success", "args": { "message": "Published" } },
{ "action": "dataSource.reload", "args": { "target": "list" } }
] } }每一步都是 ActionRegistry 里的一个原子 action。复用度高的流程抽成 handlers 里的命名 handler,按钮只写 { "type": "flow", "handler": "publishDashboard" }——本质是命名函数 vs 匿名函数,底层都走 FlowRunner.run(steps, context)。
3.3 原子 action 速览(ActionRegistry)
| 分类 | action |
|---|---|
| 导航 | navigate new edit view router.push router.back |
| 数据操作 | delete search reset refresh export |
| 数据源 | reloadDataSource dataSource.fetch dataSource.reload |
| 状态 | setState state.set |
| UI 交互 | toast.show/success/error dialog.confirm dialog.form ui.openContainer/closeContainer notify |
| 表单 | form.validate form.reset |
| 其它 | api.request event.emit noop |
4. 综合实战 —— 状态感知工具栏
把上面三套机制拼起来:销售订单详情页的工具栏,按钮按记录状态显隐,点击走命令管道(带权限 + 审计 + 状态机)。visibleWhen 决定可见性,action.command 决定行为:
{
"blockType": "toolbar",
"buttons": [
{ "code": "edit", "label": "edit", "icon": "Edit",
"action": { "type": "navigate", "to": "pe_sales_order_form", "command": "pe:update_sales_order" },
"visibleWhen": "record.pe_so_status === 'draft'" },
{ "code": "submit", "icon": "Send",
"action": { "type": "command", "command": "pe:submit_sales_order" },
"visibleWhen": "record.pe_so_status === 'draft'",
"label": { "zh-CN": "提交", "en": "Submit" } },
{ "code": "approve", "icon": "CheckCircle",
"action": { "type": "command", "command": "pe:approve_sales_order" },
"visibleWhen": "record.pe_so_status === 'pending'",
"label": { "zh-CN": "审核", "en": "Approve" } },
{ "code": "cancel", "icon": "XCircle", "danger": true,
"action": { "type": "command", "command": "pe:cancel_sales_order" },
"visibleWhen": "['draft','pending','approved'].includes(record.pe_so_status)",
"label": { "zh-CN": "取消", "en": "Cancel" } }
]
}草稿态看到「编辑 / 提交」,待审态看到「审核」,全程没写一行前端事件代码。按钮接的是命令码,所以 UI、自动化、AI agent 走的是同一条被授权、被审计的路径。
典型错误
- 用前端事件兜底联动:能用
linkageRules/visibleWhen表达的,不要退回customblock 写onChange。 - 按钮直接调接口:写操作一律
action.command走命令管道,别用flow里裸api.request绕过权限/审计。 - 级联死循环:
setValue链路成环时靠maxDepth兜底,但应从规则设计上避免互相赋值。 - 混淆
disableWhen与enableWhen:enableWhen已废弃,统一用disableWhen(取反逻辑)。
下一步
- 字段类型与 Smart 组件 —— 联动的操作对象:字段与控件
- DSL 能力矩阵 —— 回到全景
- Command · 命令管道 —— 按钮点下去之后