fix(ui): 用户验收反馈 R2——单卡片骨架+实测高度对齐 Notice、应用管理启用/停用、Tag 真组件、科室行 deptKey、成员多选、顶栏去房子图标钮
build / build (push) Successful in 4m4s

This commit is contained in:
chenxing
2026-10-10 22:42:58 +08:00
parent affb1bdeb7
commit a998e455d2
11 changed files with 257 additions and 273 deletions
+4 -6
View File
@@ -2,17 +2,15 @@
// 我的办件(提交人视角):列表 + 撤回/催办/详情
import { ref } from "vue";
import { useRouter } from "vue-router";
import { Message } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import { OperatorEnum } from "@/types";
import { flowApi, INSTANCE_STATUS, INSTANCE_STATUS_COLOR } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
const router = useRouter();
const tableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const tableHeight = ref(Utils.useTableHeight({ isSearch: true, isPage: true }));
const dataParams = ref<{ status?: number }>({ status: undefined });
const columns = [
@@ -20,7 +18,7 @@ const columns = [
{ title: "办件标题", key: "bizTitle", minWidth: 220 },
{ title: "业务类型", key: "bizType", width: 110 },
{ title: "状态", width: 100, align: "center", render: (h: any, p: any) =>
h("Tag", { color: INSTANCE_STATUS_COLOR[p.row.status] || "gray" }, () => INSTANCE_STATUS[p.row.status] || p.row.status) },
h(Tag, { color: INSTANCE_STATUS_COLOR[p.row.status] || "gray" }, () => INSTANCE_STATUS[p.row.status] || p.row.status) },
{ title: "当前节点", key: "currentNodes", width: 150 },
{ title: "提交时间", width: 170, render: (h: any, p: any) => h("span", (p.row.submitTime || "").replace("T", " ")) },
{ title: "办结时间", width: 170, render: (h: any, p: any) => h("span", (p.row.finishTime || "").replace("T", " ") || "-") },
@@ -70,7 +68,7 @@ async function onUrge(row: any) {
</script>
<template>
<div class="page-wrapper">
<div class="page-wrapper yh-fill">
<div class="main-container">
<div class="search-wrap fix">
<Select v-model="dataParams.status" class="search-wrap_sel" clearable placeholder="状态" @on-change="onSearch">
@@ -80,7 +78,7 @@ async function onUrge(row: any) {
<Option :value="40" label="已撤回" />
</Select>
</div>
<TablePageAlovaNew ref="tableRef" :dataApi="tableApi" :columns="columns" :tableHeight="tableHeight" />
<TablePageAlovaNew ref="tableRef" :dataApi="tableApi" :columns="columns" />
</div>
</div>
</template>
+19 -19
View File
@@ -3,19 +3,17 @@
// 规范骨架(bsch应用页规范 §1):tabs-wrapper + Tabs type=card,Tab 切换只 v-show 内容区,分页状态各自独立
import { ref } from "vue";
import { useRouter } from "vue-router";
import { Tag } from "view-ui-plus";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import { OperatorEnum } from "@/types";
import { flowApi, TASK_STATUS, TASK_STATUS_COLOR, INSTANCE_STATUS } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
const router = useRouter();
const tab = ref<"todo" | "done" | "cc">("todo");
const todoTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const doneTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const ccTableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
// tabs 页:表格高度在常规骨架上再扣 Tab 条
const tableHeight = ref(Utils.useTableHeight({ isTabs: true, isOperat: true, isPage: true }));
const todoColumns = [
{ title: "序号", type: "index", width: 70, align: "center" },
@@ -35,13 +33,13 @@ const doneColumns = [
title: "节点", minWidth: 150, render: (h: any, p: any) => {
const kids: any[] = [h("span", p.row.nodeName || p.row.nodeId || "-")];
if (p.row.doneCount > 1) {
kids.push(h("Tag", { color: "default", size: "small", style: "margin-left:6px" },
kids.push(h(Tag, { color: "default", size: "small", style: "margin-left:6px" },
() => `共办理 ${p.row.doneCount} 次`));
}
return h("div", kids);
},
},
{ title: "结果", width: 90, align: "center", render: (h: any, p: any) => h("Tag", { color: TASK_STATUS_COLOR[p.row.status] || "default" }, () => TASK_STATUS[p.row.status] || p.row.status) },
{ title: "结果", width: 90, align: "center", render: (h: any, p: any) => h(Tag, { color: TASK_STATUS_COLOR[p.row.status] || "default" }, () => TASK_STATUS[p.row.status] || p.row.status) },
{ title: "意见", key: "opinion", minWidth: 120 },
{ title: "办理时间", width: 170, render: (h: any, p: any) => h("span", (p.row.handleTime || "").replace("T", " ")) },
{ title: "办件状态", width: 100, render: (h: any, p: any) => h("span", INSTANCE_STATUS[p.row.instanceStatus] || p.row.instanceStatus) },
@@ -84,21 +82,23 @@ function switchTab(name: string) {
</script>
<template>
<div class="tabs-wrapper">
<Tabs class="tabs-panel" :value="tab" @on-click="switchTab">
<TabPane label="我的待办" name="todo" />
<TabPane label="我的已办" name="done" />
<TabPane label="抄送我的" name="cc" />
</Tabs>
<div class="tabs-wrapper yh-fill">
<div class="main-container yh-tabs-card">
<Tabs class="tabs-panel" :value="tab" @on-click="switchTab">
<TabPane label="我的待办" name="todo" />
<TabPane label="我的已办" name="done" />
<TabPane label="抄送我的" name="cc" />
</Tabs>
<div class="main-container" v-show="tab === 'todo'">
<TablePageAlovaNew ref="todoTableRef" :dataApi="todoApi" :columns="todoColumns" :tableHeight="tableHeight" />
</div>
<div class="main-container" v-show="tab === 'done'">
<TablePageAlovaNew ref="doneTableRef" :dataApi="doneApi" :columns="doneColumns" :tableHeight="tableHeight" :autoLoad="false" />
</div>
<div class="main-container" v-show="tab === 'cc'">
<TablePageAlovaNew ref="ccTableRef" :dataApi="ccApi" :columns="ccColumns" :tableHeight="tableHeight" :autoLoad="false" />
<div class="tab-body" v-show="tab === 'todo'">
<TablePageAlovaNew ref="todoTableRef" :dataApi="todoApi" :columns="todoColumns" />
</div>
<div class="tab-body" v-show="tab === 'done'">
<TablePageAlovaNew ref="doneTableRef" :dataApi="doneApi" :columns="doneColumns" :autoLoad="false" />
</div>
<div class="tab-body" v-show="tab === 'cc'">
<TablePageAlovaNew ref="ccTableRef" :dataApi="ccApi" :columns="ccColumns" :autoLoad="false" />
</div>
</div>
</div>
</template>
+86 -68
View File
@@ -3,42 +3,40 @@
// tab2 科室管理(flw_dept 组+成员模型,取代旧「办理人映射」页——deptKey=本地自增 id,
// 切源后旧流程定义引用的 UserBase 组 id 需重建科室后重新配置)。
import { ref, onMounted } from "vue";
import { Message } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import UserSelectInput from "@/components/UserSelectInput.vue";
import UserCheckModal from "@/components/UserCheckModal.vue";
import { flowApi } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
import { useElementHeight } from "@/utils/hook/useElementHeight";
const tab = ref("apps");
// ==================== tab1 应用管理 ====================
const loading = ref(false);
const rows = ref<any[]>([]);
const tableHeight = ref(Utils.useTableHeight({ isOperat: true }));
const appRegionRef = ref<HTMLElement | null>(null);
const appH = useElementHeight(appRegionRef);
const columns = [
{ title: "序号", type: "index", width: 70, align: "center" },
{ title: "应用", key: "appName", minWidth: 200 },
{ title: "appKey", key: "appKey", width: 120, align: "center" },
{
title: "对接流程",
width: 120,
align: "center",
render: (h: any, p: any) =>
h("i-switch", {
props: { value: p.row.enabled === 1, size: "large", loading: p.row._switching },
on: { "on-change": (v: boolean) => onToggle(p.row, v) },
}),
},
{
title: "状态",
width: 90,
align: "center",
render: (h: any, p: any) =>
h("Tag", { props: { color: p.row.enabled === 1 ? "green" : "gray" } },
h(Tag, { color: p.row.enabled === 1 ? "green" : "gray" },
() => (p.row.enabled === 1 ? "已开启" : "已停用")),
},
actionColumn(
[
{ key: "disable", label: "停用", icon: "icon-stop", on: (r: any) => onToggle(r, false), disabledWhen: (r: any) => r.enabled !== 1, danger: true },
{ key: "enable", label: "启用", icon: "icon-play", on: (r: any) => onToggle(r, true), disabledWhen: (r: any) => r.enabled === 1 },
],
{ mode: "text", width: 120 },
),
];
async function load() {
@@ -67,6 +65,8 @@ async function onToggle(row: any, enabled: boolean) {
// ==================== tab2 科室管理 ====================
const deptLoading = ref(false);
const depts = ref<any[]>([]);
const deptRegionRef = ref<HTMLElement | null>(null);
const deptH = useElementHeight(deptRegionRef);
const deptColumns = [
{ title: "序号", type: "index", width: 60, align: "center" },
{ title: "科室名", key: "deptName", minWidth: 160 },
@@ -78,18 +78,12 @@ const deptColumns = [
align: "center",
render: (h: any, p: any) => h("span", String(p.row.count)),
},
{
title: "来源",
width: 90,
align: "center",
render: (h: any, p: any) => h("Tag", () => (p.row._pulled ? "拉取" : "手工")),
},
{
title: "状态",
width: 80,
align: "center",
render: (h: any, p: any) =>
h("Tag", { props: { color: p.row.status === 0 ? "green" : "gray" } },
h(Tag, { color: p.row.status === 0 ? "green" : "gray" },
() => (p.row.status === 0 ? "启用" : "停用")),
},
actionColumn(
@@ -160,7 +154,7 @@ async function doRename() {
}
renameSaving.value = true;
try {
await flowApi.deptRename(renameRow.value.id, renameName.value.trim()).send();
await flowApi.deptRename(renameRow.value.deptKey, renameName.value.trim()).send();
Message.success("已改名");
renameVisible.value = false;
loadDepts();
@@ -180,7 +174,7 @@ function onDeptStatus(row: any, enabled: boolean) {
? "引用它的流程节点恢复解析到科室成员"
: "停用后引用它的流程定义不能发布;在途办件推进到该节点会报无可用办理人",
async onOk() {
await flowApi.deptStatus(row.id, enabled).send();
await flowApi.deptStatus(row.deptKey, enabled).send();
Message.success("操作成功");
loadDepts();
},
@@ -193,7 +187,7 @@ function onDelete(row: any) {
msg: `确定删除「${row.deptName}」及其全部成员吗?`,
info: "删除后引用该 deptKey 的流程定义节点将解析失败(发布校验拦截/办理报错),请先确认无流程引用",
async onOk() {
await flowApi.deptDelete(row.id).send();
await flowApi.deptDelete(row.deptKey).send();
Message.success("已删除");
loadDepts();
},
@@ -205,8 +199,6 @@ const memberVisible = ref(false);
const memberRow = ref<any>(null);
const members = ref<any[]>([]);
const memberLoading = ref(false);
const addUserId = ref<number | null>(null);
const addUserName = ref("");
function onMembers(row: any) {
memberRow.value = row;
@@ -217,22 +209,43 @@ function onMembers(row: any) {
async function loadMembers(row: any) {
memberLoading.value = true;
try {
members.value = ((await flowApi.deptMembers(row.id).send()) as any[]) || [];
members.value = ((await flowApi.deptMembers(row.deptKey).send()) as any[]) || [];
} catch (e: any) {
Message.error(e?.message || "成员加载失败");
members.value = [];
} finally {
memberLoading.value = false;
}
}
async function onMemberAdd() {
if (!addUserId.value) {
// 添加成员:规范选人组件(多选),提交即批量加入当前科室
const addMemberCheckRef = ref();
const memberSources = [
{
name: "基础平台",
loader: () => flowApi.adminTeacherTree().send() as Promise<any[]>,
},
];
function onAddMemberShow() {
addMemberCheckRef.value?.show();
}
async function onMemberAddSubmit(users: any[]) {
if (!users || !users.length) {
Message.warning("请先选择用户");
return;
}
const ids = users
.map((u: any) => Number(typeof u === "object" && u != null ? u.id : u))
.filter((n: number) => Number.isFinite(n) && n > 0);
if (!ids.length) {
Message.warning("请先选择用户");
return;
}
try {
const added: any = await flowApi.deptMemberAdd(memberRow.value.id, [addUserId.value]).send();
Message.success(added > 0 ? `已添加 ${added} 人` : "该成员已存在");
addUserId.value = null;
addUserName.value = "";
const added: any = await flowApi.deptMemberAdd(memberRow.value.deptKey, ids).send();
Message.success(added > 0 ? `已添加 ${added} 人` : "所选成员均已存在");
loadMembers(memberRow.value);
loadDepts();
} catch (e: any) {
@@ -298,33 +311,43 @@ onMounted(() => {
</script>
<template>
<div class="page-wrapper">
<div class="main-container">
<Tabs v-model="tab">
<TabPane label="应用管理" name="apps">
<div class="fix mb-12">
<Button class="fl" type="primary" size="large" @click="load">刷新(同步学校池)</Button>
<span class="fl" style="color: #808695; font-size: 12px; line-height: 40px; margin-left: 10px">
应用清单来自本校已开通应用(UserBase 学校组开通,含学校别名);开启后可用于流程归属与筛选。
新开通/改名在 UserBase 管理端操作后回本页刷新即可
</span>
</div>
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
:height="tableHeight" no-data-text="本校暂无已开通应用(请先在用户中心开通)" />
</TabPane>
<TabPane label="科室管理" name="depts">
<div class="fix mb-12">
<Button class="fl" type="primary" size="large" @click="onCreate">新建科室</Button>
<Button class="fl" size="large" style="margin-left: 8px" @click="onGroupPreview">一键拉取教师组</Button>
<span class="fl" style="color: #808695; font-size: 12px; line-height: 40px; margin-left: 10px">
流程节点「按科室」办理的解析源;重复拉取=刷新组名与成员集(不重复建组)
</span>
</div>
<Table :columns="deptColumns" :data="depts" :loading="deptLoading" class="link-table table-bottom-box"
:height="tableHeight" no-data-text="暂无科室(可一键拉取教师组或手工新建)" />
</TabPane>
<div class="page-wrapper yh-fill">
<div class="main-container yh-tabs-card">
<Tabs class="tabs-panel" v-model="tab">
<TabPane label="应用管理" name="apps"></TabPane>
<TabPane label="科室管理" name="depts"></TabPane>
</Tabs>
<!-- 应用管理 -->
<div class="tab-body" v-show="tab === 'apps'">
<div class="fix mb-12">
<Button class="fl" type="primary" size="large" @click="load">刷新(同步学校池)</Button>
<span class="fl" style="color: #808695; font-size: 12px; line-height: 40px; margin-left: 10px">
应用清单来自本校已开通应用(UserBase 学校组开通,含学校别名);开启后可用于流程归属与筛选。
新开通/改名在 UserBase 管理端操作后回本页刷新即可
</span>
</div>
<div class="table-region" ref="appRegionRef">
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
:height="appH || undefined" no-data-text="本校暂无已开通应用(请先在用户中心开通)" />
</div>
</div>
<!-- 科室管理 -->
<div class="tab-body" v-show="tab === 'depts'">
<div class="fix mb-12">
<Button class="fl" type="primary" size="large" @click="onCreate">新建科室</Button>
<Button class="fl" size="large" style="margin-left: 8px" @click="onGroupPreview">一键拉取教师组</Button>
<span class="fl" style="color: #808695; font-size: 12px; line-height: 40px; margin-left: 10px">
流程节点「按科室」办理的解析源;重复拉取=刷新组名与成员集(不重复建组)
</span>
</div>
<div class="table-region" ref="deptRegionRef">
<Table :columns="deptColumns" :data="depts" :loading="deptLoading" class="link-table table-bottom-box"
:height="deptH || undefined" no-data-text="暂无科室(可一键拉取教师组或手工新建)" />
</div>
</div>
<Modal v-model="createVisible" title="新建科室" :mask-closable="false">
<Input v-model="createName" placeholder="科室名称,如 人事处" :maxlength="30" />
<template #footer>
@@ -343,15 +366,7 @@ onMounted(() => {
<Modal v-model="memberVisible" :title="`成员维护 - ${memberRow?.deptName || ''}`" :mask-closable="false" width="640">
<div class="fix" style="margin-bottom: 10px">
<UserSelectInput
class="fl"
style="width: 260px"
:model-value="addUserId"
:display-name="addUserName"
@update:model-value="addUserId = $event"
@update:display-name="addUserName = $event"
/>
<Button class="fl" type="primary" style="margin-left: 8px" @click="onMemberAdd">添加</Button>
<Button class="fl" type="primary" icon="md-add" @click="onAddMemberShow">添加成员</Button>
</div>
<Table :columns="[
{ title: '用户ID', key: 'userId', width: 100 },
@@ -374,6 +389,9 @@ onMounted(() => {
<Button type="primary" :loading="groupImporting" :disabled="!groupRows.length" @click="onGroupImport">导入</Button>
</template>
</Modal>
<!-- 添加成员选人弹窗(多选,与 Notice 设置中心同款组件) -->
<UserCheckModal ref="addMemberCheckRef" :sources="memberSources" @on-submit="onMemberAddSubmit" />
</div>
</div>
</template>
+53 -47
View File
@@ -2,13 +2,13 @@
// 回调管理(概览 §9.2):注册列表/启停/secret 重置 + 推送日志 + 挂起(持续重试可视/标记死信)+ 死信重推。
// 规范骨架(bsch应用页规范 §1):tabs-wrapper + Tabs type=card,Tab 切换只 v-show 内容区;「重推选中」落在分页栏左侧批量操作位
import { ref, onMounted } from "vue";
import { Message } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import { OperatorEnum } from "@/types";
import { flowApi } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
import { useElementHeight } from "@/utils/hook/useElementHeight";
const LOG_STATUS: Record<number, string> = { 0: "待推/重试中", 1: "成功", 2: "死信" };
const EVENT_OPTIONS = ["flow.submitted", "flow.task.created", "flow.task.handled", "flow.completed", "flow.rejected", "flow.withdrawn"];
@@ -16,10 +16,11 @@ const EVENT_OPTIONS = ["flow.submitted", "flow.task.created", "flow.task.handled
const tab = ref("regs");
const loading = ref(false);
// tabs 页高度:在常规骨架上再扣 Tab 条;日志页含搜索区+分页,挂起页含提示条
const regsTableHeight = ref(Utils.useTableHeight({ isTabs: true }));
const logTableHeight = ref(Utils.useTableHeight({ isTabs: true, isSearch: true, isPage: true }));
const hungTableHeight = ref(Utils.useTableHeight({ isTabs: true, otherH: 46 }));
// 无分页 tab 的表格高度:实测各自 .table-region(flex:1),替代 useTableHeight 常量减法
const regsRegionRef = ref<HTMLElement | null>(null);
const hungRegionRef = ref<HTMLElement | null>(null);
const regsH = useElementHeight(regsRegionRef);
const hungH = useElementHeight(hungRegionRef);
// ---- 注册列表 ----
const regs = ref<any[]>([]);
@@ -29,7 +30,7 @@ const regColumns = [
{ title: "流程 GUID", key: "flowGuid", width: 210 },
{ title: "回调地址", key: "callbackUrl", minWidth: 240 },
{ title: "订阅事件", key: "eventTypes", width: 200, render: (h: any, p: any) => h("span", p.row.eventTypes || "全部") },
{ title: "状态", width: 80, align: "center", render: (h: any, p: any) => h("Tag", { color: p.row.status === 0 ? "green" : "gray" }, () => (p.row.status === 0 ? "启用" : "停用")) },
{ title: "状态", width: 80, align: "center", render: (h: any, p: any) => h(Tag, { color: p.row.status === 0 ? "green" : "gray" }, () => (p.row.status === 0 ? "启用" : "停用")) },
actionColumn(
[
{ key: "disable", label: "停用", icon: "icon-stop", on: (r: any) => onRegStatus(r, true), disabledWhen: (r: any) => r.status !== 0, danger: true },
@@ -51,7 +52,7 @@ const logColumns = [
{ title: "biz", width: 160, render: (h: any, p: any) => h("span", `${p.row.bizType}/${p.row.bizId}`) },
{ title: "状态", width: 110, align: "center", render: (h: any, p: any) => {
const c = p.row.status === 1 ? "green" : p.row.status === 2 ? "red" : "orange";
return h("Tag", { color: c }, () => LOG_STATUS[p.row.status]);
return h(Tag, { color: c }, () => LOG_STATUS[p.row.status]);
} },
{ title: "重试", key: "retryCount", width: 60, align: "center" },
{ title: "末次结果", key: "lastError", minWidth: 200, render: (h: any, p: any) => h("span", p.row.lastError || p.row.lastHttpStatus || "-") },
@@ -66,7 +67,7 @@ const hungColumns = [
{ title: "事件", key: "event", width: 150 },
{ title: "办件", key: "instanceId", width: 80 },
{ title: "biz", width: 160, render: (h: any, p: any) => h("span", `${p.row.bizType}/${p.row.bizId}`) },
{ title: "重试", key: "retryCount", width: 70, align: "center", render: (h: any, p: any) => h("Tag", { color: "orange" }, () => `${p.row.retryCount} 次`) },
{ title: "重试", key: "retryCount", width: 70, align: "center", render: (h: any, p: any) => h(Tag, { color: "orange" }, () => `${p.row.retryCount} 次`) },
{ title: "末次结果", key: "lastError", minWidth: 220, render: (h: any, p: any) => h("span", p.row.lastError || p.row.lastHttpStatus || "-") },
{ title: "下次重推", width: 160, render: (h: any, p: any) => h("span", (p.row.nextRetryTime || "").replace("T", " ") || "-") },
actionColumn([{ key: "markDead", label: "标记死信", icon: "icon-ashbin", on: (r: any) => onMarkDead(r), danger: true }], { mode: "text", width: 120, fixed: "right" }),
@@ -175,48 +176,53 @@ onMounted(loadRegs);
</script>
<template>
<div class="tabs-wrapper">
<Tabs class="tabs-panel" :value="tab" @on-click="switchTab">
<TabPane label="回调注册" name="regs" />
<TabPane label="推送日志" name="logs" />
<TabPane label="挂起中" name="hung" />
</Tabs>
<div class="tabs-wrapper yh-fill">
<div class="main-container yh-tabs-card">
<Tabs class="tabs-panel" :value="tab" @on-click="switchTab">
<TabPane label="回调注册" name="regs" />
<TabPane label="推送日志" name="logs" />
<TabPane label="挂起中" name="hung" />
</Tabs>
<!-- 回调注册:无分页 -->
<div class="main-container" v-show="tab === 'regs'">
<Table :columns="regColumns" :data="regs" :loading="loading" class="link-table table-bottom-box"
:height="regsTableHeight" no-data-text="暂无数据" />
</div>
<!-- 推送日志:搜索区 + 分页,重推选中落分页栏左侧批量操作位 -->
<div class="main-container" v-show="tab === 'logs'">
<div class="search-wrap fix">
<Select v-model="logFilter.status" class="search-wrap_sel" clearable placeholder="状态" @on-change="onLogSearch">
<Option :value="0" label="待推/重试中" />
<Option :value="1" label="成功" />
<Option :value="2" label="死信" />
</Select>
<Select v-model="logFilter.event" class="search-wrap_sel ml-12" clearable placeholder="事件" @on-change="onLogSearch">
<Option v-for="e in EVENT_OPTIONS" :key="e" :value="e" :label="e" />
</Select>
<Input class="fr" v-model="logFilter.instanceId" placeholder="办件ID" search suffix="ios-search"
style="width: 200px" @on-search="onLogSearch" @on-clear="onLogSearch" />
<!-- 回调注册:无分页 -->
<div class="tab-body" v-show="tab === 'regs'">
<div class="table-region" ref="regsRegionRef">
<Table :columns="regColumns" :data="regs" :loading="loading" class="link-table table-bottom-box"
:height="regsH || undefined" no-data-text="暂无数据" />
</div>
</div>
<TablePageAlovaNew ref="logTableRef" :dataApi="logApi" :columns="logColumns"
:tableHeight="logTableHeight" :autoLoad="false">
<template #default="{ checkList }">
<Button size="small" type="primary" :disabled="!checkList.length" @click="onRepush(checkList)">重推选中</Button>
</template>
</TablePageAlovaNew>
</div>
<!-- 挂起中:无分页 -->
<div class="main-container" v-show="tab === 'hung'">
<div class="tip">
持续重试中的事件(消费方疑似停机;消费方恢复后自动追平,无需干预)。仅当人工判定事件已无意义时才标记死信。
<!-- 推送日志:搜索区 + 分页,重推选中落分页栏左侧批量操作位 -->
<div class="tab-body" v-show="tab === 'logs'">
<div class="search-wrap fix">
<Select v-model="logFilter.status" class="search-wrap_sel" clearable placeholder="状态" @on-change="onLogSearch">
<Option :value="0" label="待推/重试中" />
<Option :value="1" label="成功" />
<Option :value="2" label="死信" />
</Select>
<Select v-model="logFilter.event" class="search-wrap_sel ml-12" clearable placeholder="事件" @on-change="onLogSearch">
<Option v-for="e in EVENT_OPTIONS" :key="e" :value="e" :label="e" />
</Select>
<Input class="fr" v-model="logFilter.instanceId" placeholder="办件ID" search suffix="ios-search"
style="width: 200px" @on-search="onLogSearch" @on-clear="onLogSearch" />
</div>
<TablePageAlovaNew ref="logTableRef" :dataApi="logApi" :columns="logColumns" :autoLoad="false">
<template #default="{ checkList }">
<Button size="small" type="primary" :disabled="!checkList.length" @click="onRepush(checkList)">重推选中</Button>
</template>
</TablePageAlovaNew>
</div>
<!-- 挂起中:无分页 -->
<div class="tab-body" v-show="tab === 'hung'">
<div class="tip">
持续重试中的事件(消费方疑似停机;消费方恢复后自动追平,无需干预)。仅当人工判定事件已无意义时才标记死信。
</div>
<div class="table-region" ref="hungRegionRef">
<Table :columns="hungColumns" :data="hungs" :loading="hungLoading" class="link-table table-bottom-box"
:height="hungH || undefined" no-data-text="暂无数据" />
</div>
</div>
<Table :columns="hungColumns" :data="hungs" :loading="hungLoading" class="link-table table-bottom-box"
:height="hungTableHeight" no-data-text="暂无数据" />
</div>
</div>
</template>
+20 -9
View File
@@ -4,12 +4,12 @@
// 本页不再直接编辑 JSON 文本(编辑器内置"源码"高级模式兜底);行内「源码」为只读查看+复制。
import { ref, computed, onMounted } from "vue";
import { useRouter } from "vue-router";
import { Message } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import SourceDrawer from "@/views/flow/components/SourceDrawer.vue";
import { flowApi } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
import { useElementHeight } from "@/utils/hook/useElementHeight";
const router = useRouter();
const DEF_STATUS: Record<number, string> = { 0: "草稿", 1: "已发布", 2: "停用" };
@@ -22,8 +22,9 @@ const versions = ref<any[]>([]);
const sourceVisible = ref(false);
const sourceText = ref("");
const sourceTitle = ref("");
// 操作按钮区 + 表格,无搜索、无分页
const tableHeight = ref(Utils.useTableHeight({ isOperat: true }));
// 操作按钮区 + 表格,无搜索、无分页:表格高度实测 .table-region(flex:1)
const defsRegionRef = ref<HTMLElement | null>(null);
const defsH = useElementHeight(defsRegionRef);
// 按应用筛选(flw_def.app_key,2026-10-10):下拉数据源来自应用设置白名单(flw_app enabled=1)
const apps = ref<{ appKey: string; appName: string }[]>([]);
const appMap = ref<Record<string, string>>({});
@@ -37,7 +38,7 @@ const columns = [
{ title: "版本", key: "version", width: 70, align: "center" },
{ title: "状态", width: 90, align: "center", render: (h: any, p: any) => {
const c = p.row.status === 1 ? "green" : p.row.status === 0 ? "orange" : "gray";
return h("Tag", { color: c }, () => DEF_STATUS[p.row.status] || p.row.status);
return h(Tag, { color: c }, () => DEF_STATUS[p.row.status] || p.row.status);
} },
{ title: "GUID", key: "flowGuid", width: 200 },
{ title: "发布时间", width: 160, render: (h: any, p: any) => h("span", (p.row.publishTime || "").replace("T", " ") || "-") },
@@ -220,7 +221,7 @@ onMounted(() => {
</script>
<template>
<div class="page-wrapper">
<div class="page-wrapper yh-fill">
<div class="main-container">
<!-- 操作按钮区:主操作靠左(bsch应用页规范 §1) -->
<div class="fix mb-12">
@@ -231,14 +232,16 @@ onMounted(() => {
</Select>
</div>
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
:height="tableHeight" no-data-text="暂无数据" />
<div class="table-region" ref="defsRegionRef">
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
:height="defsH || undefined" no-data-text="暂无数据" />
</div>
<Modal v-model="versionVisible" title="版本历史" width="640">
<Table :columns="[
{ title: '版本', key: 'version', width: 80 },
{ title: '名称', key: 'name' },
{ title: '状态', width: 90, render: (h, p) => h('Tag', () => DEF_STATUS[p.row.status]) },
{ title: '状态', width: 90, render: (h, p) => h(Tag, () => DEF_STATUS[p.row.status]) },
{ title: '发布时间', width: 160, render: (h, p) => (p.row.publishTime || '').replace('T', ' ') || '-' },
]" :data="versions" size="small" no-data-text="暂无数据" />
</Modal>
@@ -269,3 +272,11 @@ onMounted(() => {
</div>
</div>
</template>
<style scoped>
/* 无分页脚:通道只对含 .page-panel 的卡片自动转纵向分栏,本页自声明保证 table-region 撑满 */
.main-container {
display: flex;
flex-direction: column;
}
</style>
+4 -6
View File
@@ -2,17 +2,15 @@
// 办件记录(管理端):全校办件查询;CONDITION 判断失败挂起的办件在此"重跑路由"恢复
import { ref } from "vue";
import { useRouter } from "vue-router";
import { Message } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import { OperatorEnum } from "@/types";
import TablePageAlovaNew from "@/components/TablePageAlovaNew.vue";
import { flowApi, INSTANCE_STATUS, INSTANCE_STATUS_COLOR } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
const router = useRouter();
const tableRef = ref<InstanceType<typeof TablePageAlovaNew> | null>(null);
const tableHeight = ref(Utils.useTableHeight({ isSearch: true, isPage: true }));
const dataParams = ref<{ status?: number; keyword?: string }>({ status: undefined, keyword: "" });
const columns = [
@@ -20,7 +18,7 @@ const columns = [
{ title: "办件标题", key: "bizTitle", minWidth: 220 },
{ title: "业务", width: 170, render: (h: any, p: any) => h("span", `${p.row.bizType || "-"}/${p.row.bizId || "-"}`) },
{ title: "状态", width: 100, align: "center", render: (h: any, p: any) =>
h("Tag", { color: INSTANCE_STATUS_COLOR[p.row.status] || "gray" }, () => INSTANCE_STATUS[p.row.status] || p.row.status) },
h(Tag, { color: INSTANCE_STATUS_COLOR[p.row.status] || "gray" }, () => INSTANCE_STATUS[p.row.status] || p.row.status) },
{ title: "提交人", key: "submitterName", width: 100 },
{ title: "提交时间", width: 160, render: (h: any, p: any) => h("span", (p.row.submitTime || "").replace("T", " ")) },
actionColumn(
@@ -61,7 +59,7 @@ function onSearch() {
</script>
<template>
<div class="page-wrapper">
<div class="page-wrapper yh-fill">
<div class="main-container">
<div class="search-wrap fix">
<Select v-model="dataParams.status" class="search-wrap_sel" clearable placeholder="状态" @on-change="onSearch">
@@ -73,7 +71,7 @@ function onSearch() {
<Input class="fr" v-model="dataParams.keyword" placeholder="标题关键字" search suffix="ios-search"
style="width: 200px" @on-search="onSearch" @on-clear="onSearch" />
</div>
<TablePageAlovaNew ref="tableRef" :dataApi="tableApi" :columns="columns" :tableHeight="tableHeight" />
<TablePageAlovaNew ref="tableRef" :dataApi="tableApi" :columns="columns" />
</div>
</div>
</template>
+19 -7
View File
@@ -3,15 +3,17 @@
// CONDITION 节点的 routerKey 必须命中启用中的注册(发布校验口径);basePath 是对端 context-path,
// 漏填必 401(规范 §4.2 同款坑)——生产 notice 为 /notice,本地为空。
import { ref, onMounted } from "vue";
import { Message } from "view-ui-plus";
import { Message, Tag } from "view-ui-plus";
import MyConfirm from "@/components/MyConfirm";
import { flowApi } from "@/api/module/flow";
import { actionColumn } from "@/utils/tableActions";
import Utils from "@/utils";
import { useElementHeight } from "@/utils/hook/useElementHeight";
const loading = ref(false);
const rows = ref<any[]>([]);
const tableHeight = ref(Utils.useTableHeight({ isSearch: true, isOperat: true }));
// 无搜索分页脚:表格高度实测 .table-region(flex:1)
const routerRegionRef = ref<HTMLElement | null>(null);
const routersH = useElementHeight(routerRegionRef);
const columns = [
{ title: "序号", type: "index", width: 70, align: "center" },
@@ -29,7 +31,7 @@ const columns = [
width: 90,
align: "center",
render: (h: any, p: any) =>
h("Tag", { color: p.row.status === 1 ? "green" : "gray" }, () => (p.row.status === 1 ? "启用" : "停用")),
h(Tag, { color: p.row.status === 1 ? "green" : "gray" }, () => (p.row.status === 1 ? "启用" : "停用")),
},
actionColumn(
[
@@ -160,7 +162,7 @@ onMounted(load);
</script>
<template>
<div class="page-wrapper">
<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>
@@ -174,8 +176,10 @@ onMounted(load);
</span>
</div>
<Table :columns="columns" :data="rows" :loading="loading" class="link-table table-bottom-box"
:height="tableHeight" no-data-text="暂无数据" />
<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">
@@ -235,3 +239,11 @@ onMounted(load);
</div>
</div>
</template>
<style scoped>
/* 无分页脚:通道只对含 .page-panel 的卡片自动转纵向分栏,本页自声明保证 table-region 撑满 */
.main-container {
display: flex;
flex-direction: column;
}
</style>