208 lines
7.5 KiB
Vue
208 lines
7.5 KiB
Vue
<script setup lang="ts">
|
||
// 条件分支配置抽屉(2026-09-24):结构与 NotifyDrawer 同源(本地表单 + 确定回写)。
|
||
// 管节点名与判断方式(2026-10-10 二选一:服务/表达式);出口命名/条件表达式/分支编排 ConditionBox 就地做。
|
||
import { computed, inject, reactive, watch } from "vue";
|
||
import { Message } from "view-ui-plus";
|
||
import { ConditionNode, EDITOR_CTX, JudgeMode } from "../model";
|
||
|
||
const props = defineProps<{
|
||
modelValue: boolean;
|
||
node: ConditionNode | null;
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
"update:modelValue": [boolean];
|
||
/** 节点已删除(由抽屉内删除按钮触发),由外层关闭抽屉并重算校验 */
|
||
removed: [];
|
||
}>();
|
||
|
||
const ctx = inject(EDITOR_CTX)!;
|
||
|
||
const form = reactive({
|
||
name: "",
|
||
routerKey: "",
|
||
// 2026-10-12 SERVICE 收口:新建缺省翻转为表达式(主路径);存量归一化见 syncFromNode(勿动)
|
||
judgeMode: "EXPRESSION" as JudgeMode,
|
||
});
|
||
|
||
function syncFromNode(): void {
|
||
const n = props.node;
|
||
if (!n) return;
|
||
form.name = n.name;
|
||
form.routerKey = n.routerKey || "";
|
||
form.judgeMode = n.judgeMode === "EXPRESSION" ? "EXPRESSION" : "SERVICE";
|
||
}
|
||
|
||
watch(
|
||
() => [props.modelValue, props.node?.uid],
|
||
() => {
|
||
if (props.modelValue) syncFromNode();
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
/** 判断服务下拉:仅启用中的注册(ctx.routers 由编辑器载入);当前值兜底防选项丢失 */
|
||
const routerOptions = computed(() => {
|
||
const list = ctx.routers
|
||
.filter((r) => r.status === 1)
|
||
.map((r) => ({ value: r.routerKey, label: `${r.name}(${r.routerKey})` }));
|
||
if (form.routerKey && !list.some((o) => o.value === form.routerKey)) {
|
||
list.unshift({ value: form.routerKey, label: `${form.routerKey}(未注册或已停用)` });
|
||
}
|
||
return list;
|
||
});
|
||
|
||
const routerHint = computed(() => {
|
||
if (!form.routerKey) return "";
|
||
const r = ctx.routers.find((x) => x.routerKey === form.routerKey);
|
||
if (!r) return `判断服务「${form.routerKey}」未注册或已停用,发布会被拦截`;
|
||
return `调用 ${r.serviceKey}${r.path};返回出口名即分支走向`;
|
||
});
|
||
|
||
/** 切换判断方式:表达式模式下 conditions 补齐占位(与 outlets 同位) */
|
||
function switchJudgeMode(mode: JudgeMode): void {
|
||
form.judgeMode = mode;
|
||
const n = props.node;
|
||
if (n && mode === "EXPRESSION") {
|
||
n.conditions = n.conditions || [];
|
||
while (n.conditions.length < n.outlets.length) n.conditions.push("");
|
||
}
|
||
}
|
||
|
||
function onOk(): void {
|
||
const n = props.node;
|
||
if (!n) {
|
||
emit("update:modelValue", false);
|
||
return;
|
||
}
|
||
if (!form.name.trim()) {
|
||
Message.warning("节点名称不能为空");
|
||
return;
|
||
}
|
||
if (form.judgeMode === "SERVICE" && !form.routerKey) {
|
||
Message.warning("请选择判断服务(未配置的判断服务运行到该节点会挂起)");
|
||
return;
|
||
}
|
||
n.name = form.name.trim();
|
||
n.routerKey = form.judgeMode === "SERVICE" ? form.routerKey : "";
|
||
n.judgeMode = form.judgeMode;
|
||
if (form.judgeMode === "EXPRESSION") {
|
||
n.conditions = n.conditions || [];
|
||
while (n.conditions.length < n.outlets.length) n.conditions.push("");
|
||
}
|
||
emit("update:modelValue", false);
|
||
}
|
||
|
||
function onRemove(): void {
|
||
if (props.node) ctx.removeNode(props.node.uid);
|
||
emit("removed");
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<Drawer
|
||
:model-value="modelValue"
|
||
title="配置条件分支"
|
||
:width="430"
|
||
:mask-closable="false"
|
||
@update:model-value="emit('update:modelValue', $event)"
|
||
>
|
||
<Form :label-width="0" class="dw-form">
|
||
<div class="fld">
|
||
<label>节点名称</label>
|
||
<Input v-model="form.name" placeholder="如:发文部门判断" :maxlength="30" />
|
||
</div>
|
||
|
||
<div class="fld">
|
||
<label>判断方式</label>
|
||
<RadioGroup :model-value="form.judgeMode" @on-change="switchJudgeMode">
|
||
<Radio label="SERVICE">服务(外部判断接口)</Radio>
|
||
<Radio label="EXPRESSION">表达式</Radio>
|
||
</RadioGroup>
|
||
<div class="hint">
|
||
简单条件用表达式在编辑器内闭环;复杂判断(查库/调外部系统)沿用注册的判断服务
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="form.judgeMode === 'SERVICE'" class="fld">
|
||
<label>判断服务</label>
|
||
<Select v-model="form.routerKey" filterable placeholder="请选择判断服务(flw_router 注册)">
|
||
<Option v-for="o in routerOptions" :key="o.value" :value="o.value" :label="o.label" />
|
||
</Select>
|
||
<div v-if="routerHint" class="hint" :class="{ warn: routerHint.includes('拦截') }">
|
||
{{ routerHint.includes("拦截") ? "⚠ " : "" }}{{ routerHint }}
|
||
</div>
|
||
<div v-else class="hint">
|
||
判断逻辑由业务方实现:引擎运行到该节点时同步调用判断接口,按返回出口名走对应分支
|
||
</div>
|
||
<div class="hint">
|
||
仅当判断依赖流程到达时点的实时业务数据时使用;简单条件请用"表达式"(apply 时 formData
|
||
自带判断数据)。判断端点与登记由
|
||
<code>yh-flow-callback-spring-boot-starter</code>(流程中心接入件)自动完成,见《子应用接入流程中心规范》§6.6。
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else class="fld">
|
||
<label>表达式说明</label>
|
||
<div class="hint">
|
||
每个出口在分支列头部就地填写 AviatorScript 条件(如 amount > 1000);按分支顺序取
|
||
首个为真的出口。变量=表单字段 + 内置(schId/bizType/bizId/submitterId/instanceId 等)。
|
||
变量未提供或条件全不为真时流程挂起(不配默认出口),可在"条件路由"页调试表达式
|
||
</div>
|
||
</div>
|
||
|
||
<div class="fld">
|
||
<label>出口与分支</label>
|
||
<div class="hint">
|
||
出口名在各分支列头部就地编辑(互斥分支,出口名即判断依据,必填且唯一);判断失败流程挂起,
|
||
由管理端"重跑路由"恢复,不配默认出口
|
||
</div>
|
||
</div>
|
||
</Form>
|
||
|
||
<div class="dw-foot">
|
||
<Poptip confirm title="删除该条件分支及其全部分支?" ok-text="删除" cancel-text="取消" transfer @on-ok="onRemove">
|
||
<Button type="error" ghost>删除节点</Button>
|
||
</Poptip>
|
||
<span style="flex: 1" />
|
||
<Button @click="emit('update:modelValue', false)">取消</Button>
|
||
<Button type="primary" @click="onOk">确定</Button>
|
||
</div>
|
||
</Drawer>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.dw-form {
|
||
padding-bottom: 8px;
|
||
}
|
||
.fld {
|
||
margin-bottom: 18px;
|
||
}
|
||
.fld > label {
|
||
display: block;
|
||
margin-bottom: 6px;
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
}
|
||
.hint {
|
||
color: #808695;
|
||
font-size: 12px;
|
||
line-height: 1.6;
|
||
margin-top: 5px;
|
||
}
|
||
.hint.warn {
|
||
color: #ff9900;
|
||
}
|
||
.dw-foot {
|
||
position: sticky;
|
||
bottom: -16px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 12px 0;
|
||
margin-top: 8px;
|
||
background: #fff;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
</style>
|