From 05422796367e1247d01cf7ad756e2ffa1a5e33f1 Mon Sep 17 00:00:00 2001 From: chenxing Date: Wed, 14 Oct 2026 18:18:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(condition):=20=E6=9D=A1=E4=BB=B6=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E5=BC=8F=E5=86=85=E8=81=94=E5=8F=98=E9=87=8F=20chips?= =?UTF-8?q?=EF=BC=88=E8=AE=AE=E9=A2=98=E4=B8=80=20P0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 编辑器出口表达式下展示可用变量(近期 50 条办件 formData 顶层键聚合去重截 40 + 内置,口径同调试弹窗),点击插入光标处;未知标识符内联提示疑似拼错(仅提示不拦截,去字符串/函数名/属性访问后比对)。EditorCtx 增 availableVars,index.vue 载入一次(拉取失败退化为仅内置,不阻塞编辑)。vite build:base 通过。 --- .../def-editor/components/ConditionBox.vue | 117 +++++++++++++++++- src/views/flow/admin/def-editor/index.vue | 25 ++++ src/views/flow/admin/def-editor/model.ts | 2 + 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/src/views/flow/admin/def-editor/components/ConditionBox.vue b/src/views/flow/admin/def-editor/components/ConditionBox.vue index 24c025a..0aa7820 100644 --- a/src/views/flow/admin/def-editor/components/ConditionBox.vue +++ b/src/views/flow/admin/def-editor/components/ConditionBox.vue @@ -3,7 +3,7 @@ // 分支按出口名互斥选路(运行时调判断服务/表达式求值),分支尾隐式汇合到容器后继。 // 分支头部的出口名 Input 即 contentJson 里 CONDITION 命名出边的 name(判断接口 outlets 契约); // EXPRESSION 模式(2026-10-10)下分支头部再就地编辑出边 condition,序号即求值优先级。 -import { computed, inject, ref } from "vue"; +import { computed, inject, nextTick, ref } from "vue"; import { BRANCH_LABELS, EDITOR_CTX, MAX_BRANCHES, MIN_BRANCHES, ConditionNode } from "../model"; import NodeList from "./NodeList.vue"; import ExprEvalModal from "@/views/flow/components/ExprEvalModal.vue"; @@ -45,6 +45,57 @@ function openTry(i: number): void { tryExpr.value = (props.node.conditions || [])[i] || ""; tryVisible.value = true; } + +// ==================== 可用变量 chips(2026-10-14 议题一 P0):内联提示 + 点击插入 ==================== +/** 可用变量(表单字段聚合键 + 内置),来自编辑器上下文;未拉到为空数组则不展示 chips */ +const availableVars = computed(() => ctx.availableVars || []); +const BUILTIN_VARS = new Set([ + "schId", "instanceId", "instanceVersion", "bizType", "bizId", "nodeId", "nodeName", "submitterId", +]); +const isBuiltin = (k: string): boolean => BUILTIN_VARS.has(k); + +/** Aviator 关键字/字面量:不参与"未知变量"提示 */ +const EXPR_KEYWORDS = new Set([ + "true", "false", "nil", "null", "and", "or", "not", "if", "else", "elsif", "let", "while", + "for", "return", "in", "like", "new", "end", "break", "continue", "seq", "lambda", +]); + +const condInputs = ref>({}); +function setCondInput(el: any, i: number): void { + condInputs.value[i] = (el as HTMLInputElement) || null; +} + +/** chips 点击:插入到该出口表达式光标处(无输入框时追加到末尾) */ +function insertChip(i: number, key: string): void { + const cur = (props.node.conditions || [])[i] || ""; + const el = condInputs.value[i]; + const s = el?.selectionStart ?? cur.length; + const e = el?.selectionEnd ?? cur.length; + editCondition(i, cur.slice(0, s) + key + cur.slice(e)); + nextTick(() => { + const input = condInputs.value[i]; + if (input) { + input.focus(); + input.setSelectionRange(s + key.length, s + key.length); + } + }); +} + +/** 该出口表达式里不在可用列表中的标识符(疑似拼错提示,仅提示不拦截) */ +function unknownVars(i: number): string[] { + const cond = (props.node.conditions || [])[i] || ""; + if (!cond.trim()) return []; + const stripped = cond + .replace(/'[^']*'|"[^"]*"/g, " ") // 字符串字面量 + .replace(/[A-Za-z_][A-Za-z0-9_]*\s*\(/g, " ") // 函数名 + .replace(/\.\s*[A-Za-z_][A-Za-z0-9_]*/g, " "); // 命名空间/属性访问 + const known = new Set([...availableVars.value, ...BUILTIN_VARS]); + const out = new Set(); + (stripped.match(/[A-Za-z_][A-Za-z0-9_]*/g) || []).forEach((t) => { + if (!EXPR_KEYWORDS.has(t) && !known.has(t)) out.add(t); + }); + return Array.from(out); +}