NOTIFY(2026-09-23):编辑器抄送卡片+抽屉+画布 bpmn:ServiceTask 映射、工作台「抄送我的」页签、轨迹 kind=CC 行、示意图青框卡片 CONDITION(2026-09-24):ConditionBox 互斥分支容器(分支列出口名就地编辑、无 JOIN 语义分支尾隐式汇合)、ConditionDrawer 判断服务下拉(flw_router)、convert 双向序列化(CONDITION+命名出边、uid+c 尾缀往返收敛)、validate 镜像后端口径、画布 bpmn:ExclusiveGateway 映射、轨迹「系统判断→出口」行(失败挂起提示)、示意图紫菱形(SVG 与服务端 PNG 同配色)、管理面 flw_router 维护页(/admin/routers)与办件记录「重跑路由」入口 convert 往返 54 项全过、npm run build 通过
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
<script setup lang="ts">
|
||||
// 条件分支容器(2026-09-24):紫系区分并行橙;与 ParallelBox 的本质差异——无 JOIN 语义,
|
||||
// 分支按出口名互斥选路(运行时调判断服务),分支尾隐式汇合到容器后继。
|
||||
// 分支头部的出口名 Input 即 contentJson 里 CONDITION 命名出边的 name(判断接口 outlets 契约)。
|
||||
import { computed, inject } from "vue";
|
||||
import { BRANCH_LABELS, EDITOR_CTX, MAX_BRANCHES, MIN_BRANCHES, ConditionNode } from "../model";
|
||||
import NodeList from "./NodeList.vue";
|
||||
|
||||
const props = defineProps<{ node: ConditionNode }>();
|
||||
|
||||
const ctx = inject(EDITOR_CTX)!;
|
||||
|
||||
const issues = computed(() => ctx.issueOf(props.node.uid));
|
||||
const hasError = computed(() => issues.value.some((i) => i.level === "error"));
|
||||
const canRemoveBranch = computed(() => props.node.branches.length > MIN_BRANCHES);
|
||||
const canAddBranch = computed(() => props.node.branches.length < MAX_BRANCHES);
|
||||
|
||||
const routerLabel = computed(() => {
|
||||
if (!props.node.routerKey) return "判断服务:未选择";
|
||||
const r = ctx.routers.find((x) => x.routerKey === props.node.routerKey);
|
||||
return r ? `判断服务:${r.name}(${props.node.routerKey})` : `判断服务:${props.node.routerKey}`;
|
||||
});
|
||||
|
||||
/** 出口名就地编辑(重复/空由校验提示;与 branches 下标一一对应) */
|
||||
function renameOutlet(i: number, v: string): void {
|
||||
props.node.outlets[i] = v;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="condition-box" :class="{ error: hasError }">
|
||||
<div class="cb-title" @click="ctx.openNode(node.uid)">
|
||||
条件分支(按出口互斥选路)
|
||||
<span class="cb-hint">(点击标题配置判断服务)</span>
|
||||
</div>
|
||||
<div class="cb-router">{{ routerLabel }}</div>
|
||||
<div class="cb-cols">
|
||||
<div v-for="(branch, i) in node.branches" :key="i" class="cb-col">
|
||||
<div class="cb-col-head">
|
||||
<span class="cb-col-no">{{ BRANCH_LABELS[i] || i + 1 }}</span>
|
||||
<input
|
||||
class="cb-outlet"
|
||||
:value="node.outlets[i]"
|
||||
placeholder="出口名(必填唯一)"
|
||||
maxlength="32"
|
||||
@input="renameOutlet(i, ($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
<Poptip
|
||||
v-if="canRemoveBranch"
|
||||
confirm
|
||||
title="删除该分支及其节点?"
|
||||
ok-text="删除"
|
||||
cancel-text="取消"
|
||||
transfer
|
||||
@on-ok="ctx.removeBranch(node.uid, i)"
|
||||
>
|
||||
<span class="cb-col-del" title="删除分支">×</span>
|
||||
</Poptip>
|
||||
</div>
|
||||
<NodeList :items="branch" :container="`${node.uid}:${i}`" />
|
||||
</div>
|
||||
</div>
|
||||
<Button v-if="canAddBranch" class="cb-add" size="small" long @click="ctx.addBranch(node.uid)">
|
||||
+ 添加分支
|
||||
</Button>
|
||||
<div v-for="(issue, k) in issues" :key="k" class="cb-issue" :class="issue.level">⚠ {{ issue.text }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.condition-box {
|
||||
border: 1.5px dashed #b37feb;
|
||||
border-radius: 10px;
|
||||
padding: 14px 14px 12px;
|
||||
background: rgba(249, 240, 255, 0.45);
|
||||
}
|
||||
.condition-box.error {
|
||||
border-color: #ed4014;
|
||||
}
|
||||
.cb-title {
|
||||
text-align: center;
|
||||
color: #722ed1;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.cb-title:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.cb-hint {
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
color: #b37feb;
|
||||
}
|
||||
.cb-router {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #9254de;
|
||||
margin: 6px 0 10px;
|
||||
}
|
||||
.cb-cols {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
.cb-col {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 12px 10px 14px;
|
||||
min-width: 230px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.cb-col-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
.cb-col-no {
|
||||
flex: none;
|
||||
color: #9254de;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
}
|
||||
.cb-outlet {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border: 1px solid #e3e5ee;
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
color: #722ed1;
|
||||
font-weight: 600;
|
||||
}
|
||||
.cb-outlet:focus {
|
||||
outline: none;
|
||||
border-color: #9254de;
|
||||
}
|
||||
.cb-col-del {
|
||||
flex: none;
|
||||
cursor: pointer;
|
||||
color: #c5c9d1;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
.cb-col-del:hover {
|
||||
color: #ed4014;
|
||||
}
|
||||
.cb-add {
|
||||
margin-top: 12px;
|
||||
border-style: dashed;
|
||||
color: #9254de;
|
||||
border-color: #9254de;
|
||||
background: transparent;
|
||||
width: auto;
|
||||
padding: 0 16px;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.cb-add:hover {
|
||||
background: #f9f0ff;
|
||||
color: #722ed1;
|
||||
border-color: #9254de;
|
||||
}
|
||||
.cb-issue {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.cb-issue.error {
|
||||
color: #ed4014;
|
||||
}
|
||||
.cb-issue.warn {
|
||||
color: #ff9900;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user