- 新增 views/flow/components/ExprEvalModal.vue(原型 S1-S5,宽 560→700):
1) 从办件导入:adminInstances(page1,size50) 的 filterable Select(#id·bizType/bizId·标题·提交人·时间+状态Tag)、
bizType distinct chips 筛选、选中即 formData 解析缩进填充(解析失败填原文+红字)、内置变量只读条
(instanceId/instanceVersion/bizType/bizId/submitterId;nodeId/nodeName 提示按调试目标自填;schId 不展示)
2) 从路由日志导入:detail(instanceId) 取 kind=ROUTE 行,表格 节点名/注册键/命中出口(空=未命中)/attempt/时间
+ 每行 rawResponse <pre>(>200 字截断、title 全量、无留痕显示「(无留痕)」);
「载入该办件变量并复现」= 填 formData + 保留表达式再求值;无 ROUTE 行给空态文案
3) 手动编写:原 textarea 语义保留 + 最近 50 条办件 formData 键聚合 chips(点击插光标处)+ 引导文案
可用变量 chips 点击插入表达式光标(selectionStart/End,无焦点退化追加);求值仍走 /admin/router/eval,
结果区四态文案(true/false/error/未求值)含线上行为说明与「宁挂起不误放行」口径
- Routers.vue:删旧内联求值弹窗(evalForm/evalResult/doEval),改挂 <ExprEvalModal v-model="exprVisible">
- ConditionBox.vue(B5/S6):EXPRESSION 模式出口表达式行右侧加「试算」小钮(@click.stop 不触发容器交互),
带该出口表达式打开同一弹窗
注意:本提交连带纳入了并发会话在 Routers.vue 的 6 行「来源」列改动(regAppKey 自注册标识,
属 SERVICE 收口工作),非本批次内容,review 时请单独留意
208 lines
8.4 KiB
Vue
208 lines
8.4 KiB
Vue
<script setup lang="ts">
|
||
// 条件路由维护(2026-09-24 二期第二批 §4.2):flw_router 判断服务注册表(全局表)。
|
||
// CONDITION 节点的 routerKey 必须命中启用中的注册(发布校验口径);basePath 是对端 context-path,
|
||
// 漏填必 401(规范 §4.2 同款坑)——生产 notice 为 /notice,本地为空。
|
||
import { ref, onMounted } from "vue";
|
||
import { Message, Tag } from "view-ui-plus";
|
||
import MyConfirm from "@/components/MyConfirm";
|
||
import ExprEvalModal from "@/views/flow/components/ExprEvalModal.vue";
|
||
import { flowApi } from "@/api/module/flow";
|
||
import { actionColumn } from "@/utils/tableActions";
|
||
import { useElementHeight } from "@/utils/hook/useElementHeight";
|
||
|
||
const loading = ref(false);
|
||
const rows = ref<any[]>([]);
|
||
// 无搜索分页脚:表格高度实测 .table-region(flex:1)
|
||
const routerRegionRef = ref<HTMLElement | null>(null);
|
||
const routersH = useElementHeight(routerRegionRef);
|
||
|
||
const columns = [
|
||
{ title: "序号", type: "index", width: 70, align: "center" },
|
||
{ title: "注册键", key: "routerKey", width: 220 },
|
||
{ title: "名称", key: "name", width: 160 },
|
||
{ title: "服务目录键", key: "serviceKey", width: 110 },
|
||
{
|
||
// 2026-10-12 SERVICE 收口:reg_app_key 非空=yh-flow-route starter 自注册行(归属应用)
|
||
title: "来源",
|
||
width: 160,
|
||
render: (h: any, p: any) => h("span", p.row.regAppKey ? `自注册 · ${p.row.regAppKey}` : "手工"),
|
||
},
|
||
{ title: "判断端点", key: "path", minWidth: 300 },
|
||
{
|
||
title: "basePath",
|
||
width: 110,
|
||
render: (h: any, p: any) => h("span", p.row.basePath || "(空=沿用目录)"),
|
||
},
|
||
{
|
||
title: "状态",
|
||
width: 90,
|
||
align: "center",
|
||
render: (h: any, p: any) =>
|
||
h(Tag, { color: p.row.status === 1 ? "green" : "gray" }, () => (p.row.status === 1 ? "启用" : "停用")),
|
||
},
|
||
actionColumn(
|
||
[
|
||
{ key: "edit", label: "编辑", icon: "icon-edit", on: (r: any) => onEdit(r) },
|
||
{ key: "disable", label: "停用", icon: "icon-stop", on: (r: any) => onDisable(r), disabledWhen: (r: any) => r.status !== 1, danger: true },
|
||
{ key: "enable", label: "启用", icon: "icon-play", on: (r: any) => onEnable(r), disabledWhen: (r: any) => r.status === 1 },
|
||
],
|
||
{ mode: "text", width: 150, fixed: "right" },
|
||
),
|
||
];
|
||
|
||
async function load() {
|
||
loading.value = true;
|
||
try {
|
||
rows.value = (await flowApi.routerList().send()) || [];
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
// ---- 新增/编辑弹窗 ----
|
||
const saveVisible = ref(false);
|
||
const saving = ref(false);
|
||
const formRef = ref<any>(null);
|
||
const form = ref<any>({ id: null, routerKey: "", name: "", serviceKey: "", path: "", basePath: "" });
|
||
const saveRules = {
|
||
routerKey: [{ required: true, message: "请填写红色星号内容!", trigger: "blur" }],
|
||
name: [{ required: true, message: "请填写红色星号内容!", trigger: "blur" }],
|
||
serviceKey: [{ required: true, message: "请填写红色星号内容!", trigger: "blur" }],
|
||
path: [{ required: true, message: "请填写红色星号内容!", trigger: "blur" }],
|
||
};
|
||
|
||
function onAdd() {
|
||
form.value = { id: null, routerKey: "", name: "", serviceKey: "notice", path: "", basePath: "" };
|
||
saveVisible.value = true;
|
||
}
|
||
|
||
function onEdit(row: any) {
|
||
form.value = {
|
||
id: row.id,
|
||
routerKey: row.routerKey,
|
||
name: row.name,
|
||
serviceKey: row.serviceKey,
|
||
path: row.path,
|
||
basePath: row.basePath || "",
|
||
};
|
||
saveVisible.value = true;
|
||
}
|
||
|
||
function onSave() {
|
||
formRef.value?.validate(async (valid: boolean) => {
|
||
if (!valid) return;
|
||
saving.value = true;
|
||
try {
|
||
await flowApi.routerSave({
|
||
id: form.value.id || undefined,
|
||
routerKey: form.value.routerKey.trim(),
|
||
name: form.value.name.trim(),
|
||
serviceKey: form.value.serviceKey.trim(),
|
||
path: form.value.path.trim(),
|
||
basePath: form.value.basePath.trim(),
|
||
}).send();
|
||
Message.success("已保存");
|
||
saveVisible.value = false;
|
||
load();
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
});
|
||
}
|
||
|
||
function onDisable(row: any) {
|
||
MyConfirm({
|
||
title: "停用",
|
||
msg: `确定停用判断服务「${row.name || row.routerKey}」吗?`,
|
||
info: "停用后引用它的流程定义不能发布;在途挂起办件的重跑会失败",
|
||
async onOk() {
|
||
await flowApi.routerStatus(row.id, false).send();
|
||
Message.success("已停用");
|
||
load();
|
||
},
|
||
});
|
||
}
|
||
|
||
function onEnable(row: any) {
|
||
MyConfirm({
|
||
title: "启用",
|
||
msg: `确定启用判断服务「${row.name || row.routerKey}」吗?`,
|
||
info: "启用后流程编辑器的判断服务下拉可选它,发布校验放行引用",
|
||
async onOk() {
|
||
await flowApi.routerStatus(row.id, true).send();
|
||
Message.success("已启用");
|
||
load();
|
||
},
|
||
});
|
||
}
|
||
|
||
// ---- 表达式调试(2026-10-10 四议题;2026-10-12 议题三改为三模式共享弹窗 ExprEvalModal)----
|
||
const exprVisible = ref(false);
|
||
|
||
function onEval() {
|
||
exprVisible.value = true;
|
||
}
|
||
|
||
onMounted(load);
|
||
</script>
|
||
|
||
<template>
|
||
<div class="page-wrapper yh-fill">
|
||
<div class="main-container">
|
||
<div class="fix mb-12">
|
||
<Button class="fl" type="primary" size="large" @click="onAdd">新增注册</Button>
|
||
<Button class="fl" size="large" style="margin-left: 8px" @click="onEval">表达式调试</Button>
|
||
</div>
|
||
|
||
<div class="search-wrap fix">
|
||
<span class="fr" style="color: #808695; font-size: 12px; line-height: 32px">
|
||
判断逻辑由业务方实现:引擎运行到 CONDITION 节点时按注册键同步调用(3s×3 重试),
|
||
失败挂起、管理端办件记录里重跑路由
|
||
</span>
|
||
</div>
|
||
|
||
<div class="table-region" ref="routerRegionRef">
|
||
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
|
||
:height="routersH || undefined" no-data-text="暂无数据" />
|
||
</div>
|
||
|
||
<Modal v-model="saveVisible" title="新增/编辑判断服务" :mask-closable="false">
|
||
<Form ref="formRef" :model="form" :rules="saveRules" :label-width="100">
|
||
<FormItem label="注册键" prop="routerKey">
|
||
<Input v-model="form.routerKey" placeholder="CONDITION 节点 routerKey 引用它,如 notice.dept-judge" :maxlength="64" />
|
||
</FormItem>
|
||
<FormItem label="名称" prop="name">
|
||
<Input v-model="form.name" placeholder="展示名(编辑器下拉),如 发文部门判断" :maxlength="128" />
|
||
</FormItem>
|
||
<FormItem label="服务目录键" prop="serviceKey">
|
||
<Input v-model="form.serviceKey" placeholder="服务目录三级解析的 key,如 notice" :maxlength="64" />
|
||
</FormItem>
|
||
<FormItem label="判断端点" prop="path">
|
||
<Input v-model="form.path" placeholder="以 / 开头,如 /oapi/notice/flow/route/dept-judge" :maxlength="255" />
|
||
</FormItem>
|
||
<FormItem label="basePath">
|
||
<Input v-model="form.basePath" placeholder="对端 context-path(生产 /notice;本地无则留空)" :maxlength="64" />
|
||
<p style="color: #808695; font-size: 12px; line-height: 1.6; margin-top: 4px">
|
||
签名拼串要剥它,漏填必 401;留空=沿用服务目录条目自带的前缀
|
||
</p>
|
||
</FormItem>
|
||
</Form>
|
||
<template #footer>
|
||
<Button @click="saveVisible = false">取消</Button>
|
||
<Button type="primary" :loading="saving" @click="onSave">提交</Button>
|
||
</template>
|
||
</Modal>
|
||
|
||
<ExprEvalModal v-model="exprVisible" />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* 无分页脚:通道只对含 .page-panel 的卡片自动转纵向分栏,本页自声明保证 table-region 撑满 */
|
||
.main-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
</style>
|