feat(condition): 编辑器支持表达式判断——判断方式切换 + 出口条件就地编辑 + 表达式调试

- ConditionNode 增 judgeMode/conditions(SERVICE 缺省,旧定义天然兼容);DefEdgeJson/DefNodeJson 透传 condition/judgeMode
- convert.ts 双向透传:CONDITION 分支首边带 condition,序列化顺序=分支顺序(求值优先级保序)
- ConditionDrawer 判断方式 Radio(服务/表达式);ConditionBox 表达式模式分支头部条件输入(序号即优先级)
- validate.ts 镜像校验:表达式模式每出口条件必填(语法由后端发布期拦)
- Routers 页加「表达式调试」Modal(/admin/router/eval,与线上同一求值核心)
- addBranch/removeBranch 同步维护 conditions 占位
This commit is contained in:
chenxing
2026-10-10 18:52:54 +08:00
parent a4cc8ea2fa
commit acbf601e7a
8 changed files with 205 additions and 20 deletions
+12 -2
View File
@@ -103,9 +103,19 @@ export function validateModel(model: EditorModel, depts: DeptOption[], routers:
if (item.kind === "condition") {
// 条件分支(2026-09-24):判断服务必选(未注册仅 warn,发布期后端权威拦截);
// 出口名即判断接口 outlets 契约,非空唯一(镜像后端 CONDITION 校验口径)
// 出口名即判断接口 outlets 契约,非空唯一(镜像后端 CONDITION 校验口径)。
// EXPRESSION(2026-10-10):每出口条件非空(语法后端拦,前端不重复实现)
if (!item.name.trim()) push(item.uid, "error", path, "节点名称不能为空");
if (!item.routerKey) {
const expression = item.judgeMode === "EXPRESSION";
if (expression) {
item.outlets.forEach((o, i) => {
const cond = (item.conditions || [])[i] || "";
if (!cond.trim()) {
push(item.uid, "error", path,
`出口「${o || i + 1}」未配条件表达式(表达式模式下每个出口必须配置,发布会被拦截)`);
}
});
} else if (!item.routerKey) {
push(item.uid, "error", path, "未选择判断服务(发布后运行到该节点会挂起)");
} else if (routers.length && !routers.some((r) => r.routerKey === item.routerKey && r.status === 1)) {
push(item.uid, "warn", path, `判断服务「${item.routerKey}」未注册或已停用,发布会被拦截`);