|
|
@@ -19,6 +19,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import AidopDemoShell from '../../components/AidopDemoShell.vue';
|
|
|
import { s8ConfigApi, type S8HandlerUser, type S8RuleParametersPayload, type S8WatchRuleConfigRow, type S8WatchRulePreviewResult } from '../api/s8ConfigApi';
|
|
|
import { s8ExceptionApi } from '../api/s8ExceptionApi';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
import { s8ReportApi } from '../api/s8ReportApi';
|
|
|
|
|
|
const loading = ref(false);
|
|
|
@@ -192,6 +193,29 @@ async function saveHandlerPool() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// ── S8-RULE-4TAB-1:四页签状态 + 只读审核来源 ──
|
|
|
+const router = useRouter();
|
|
|
+const activeTab = ref('info');
|
|
|
+const reviewLoaded = ref(false);
|
|
|
+const reviewStages = ref<any[]>([]);
|
|
|
+const escalateRoles = ref<string[]>([]);
|
|
|
+
|
|
|
+async function loadReviewSource(ruleId: number) {
|
|
|
+ reviewLoaded.value = false;
|
|
|
+ try {
|
|
|
+ const r: any = await s8ConfigApi.watchRules.reviewSource(ruleId);
|
|
|
+ reviewStages.value = r?.stages ?? [];
|
|
|
+ escalateRoles.value = r?.escalateRoles ?? [];
|
|
|
+ } finally {
|
|
|
+ reviewLoaded.value = true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 审批流程配置归流程管理维护;本页只读展示,改配置去那边,不在规则弹窗里改。 */
|
|
|
+function goApprovalConfig() {
|
|
|
+ router.push('/aidop/flowManage/approvalFlowList');
|
|
|
+}
|
|
|
+
|
|
|
function openDetail(row: S8WatchRuleConfigRow) {
|
|
|
current.value = row;
|
|
|
form.pollIntervalSeconds = row.pollIntervalSeconds;
|
|
|
@@ -204,7 +228,9 @@ function openDetail(row: S8WatchRuleConfigRow) {
|
|
|
snapshot = JSON.stringify(form);
|
|
|
preview.value = null;
|
|
|
drawerOpen.value = true;
|
|
|
+ activeTab.value = 'info';
|
|
|
void loadHandlerPool(Number(row.id));
|
|
|
+ void loadReviewSource(Number(row.id));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -446,169 +472,247 @@ async function provision() {
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
- <!-- 详情抽屉:A 基础定义(只读) / B 运行参数 / C 预演 / D 运行状态 -->
|
|
|
- <el-drawer v-model="drawerOpen" size="720px" :title="current ? ruleName(current) : ''">
|
|
|
- <template v-if="current">
|
|
|
- <!-- S8-HANDLER-POOL-1:责任维度。谁负责这条规则,与「异常操作权限」的角色维度正交。 -->
|
|
|
- <el-card shadow="never" style="margin-bottom: 12px">
|
|
|
- <template #header>
|
|
|
- <div style="display: flex; justify-content: space-between; align-items: center">
|
|
|
- <span style="font-weight: 600">处理账号(该规则由谁负责)</span>
|
|
|
- <el-button type="primary" size="small" :loading="savingPool"
|
|
|
- :disabled="!handlerPoolLoaded" @click="saveHandlerPool">保存处理账号</el-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-alert v-if="handlerPoolLoaded && handlerPool.length === 0" type="warning"
|
|
|
- :closable="false" show-icon style="margin-bottom: 10px">
|
|
|
- <template #title>该规则尚未配置处理账号</template>
|
|
|
- <div style="line-height: 1.9">
|
|
|
- 没有处理账号的规则<strong>无法启用</strong>:它会源源不断建出异常、却没有人能认领,
|
|
|
- 而调度器每一轮都报成功。
|
|
|
- </div>
|
|
|
- </el-alert>
|
|
|
- <el-alert v-if="handlerPoolInvalid.length" type="error" :closable="false" show-icon
|
|
|
- style="margin-bottom: 10px">
|
|
|
- <template #title>有 {{ handlerPoolInvalid.length }} 个已配置的账号当前不可用</template>
|
|
|
- <div style="line-height: 1.9">
|
|
|
- 这些账号已停用或不在当前租户,<strong>不再具备认领资格</strong>。请重新选择后保存。
|
|
|
- </div>
|
|
|
- </el-alert>
|
|
|
- <el-select v-model="handlerPool" multiple filterable clearable collapse-tags collapse-tags-tooltip
|
|
|
- placeholder="选择负责该规则的系统账号" style="width: 100%" :loading="!handlerPoolLoaded">
|
|
|
- <el-option v-for="u in tenantUsers" :key="u.id" :label="u.name" :value="u.id">
|
|
|
- <span>{{ u.name }} <span style="color: #999; font-size: 12px">{{ u.account ? '(' + u.account + ')' : '' }}</span></span>
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- <div style="color: #909399; font-size: 12px; margin-top: 6px">
|
|
|
- 池成员才能认领 / 被转派该规则产生的异常。能否认领还同时取决于账号是否拥有「认领」动作权限,
|
|
|
- 以及单据当前状态。
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
+ <!--
|
|
|
+ S8-RULE-4TAB-1:规则配置弹窗统一为四页签。
|
|
|
+
|
|
|
+ 原来是一个 720px 抽屉,把「只读定义 / 运行参数 / 预演 / 运行状态 / 处理人员池」
|
|
|
+ 竖着堆在一起,管理员要滚很久才能找到要改的那一项,也分不清哪些是他能改的、
|
|
|
+ 哪些是系统给的。四个页签各自回答一个问题:
|
|
|
+ 这条规则是什么 / 它怎么跑 / 谁负责与谁审 / 出事通知谁。
|
|
|
+
|
|
|
+ 「异常操作权限」刻意**不**放进来 —— 它是 S8 全局配置(哪个角色能做认领这类动作),
|
|
|
+ 放进规则弹窗会让管理员以为改的是这条规则的权限,实际改的是全租户。
|
|
|
+ -->
|
|
|
+ <el-dialog v-model="drawerOpen" width="980px" top="5vh" destroy-on-close>
|
|
|
+ <template v-if="current" #header>
|
|
|
+ <div style="display: flex; align-items: center; gap: 10px">
|
|
|
+ <span style="font-weight: 600; font-size: 16px">{{ ruleName(current) }}</span>
|
|
|
+ <el-tag :type="current.enabled ? 'success' : 'info'" size="small">{{ current.enabled ? '已启用' : '已停用' }}</el-tag>
|
|
|
+ <span style="color: #909399; font-size: 12px">{{ current.ruleCode }}</span>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
+
|
|
|
<template v-if="current">
|
|
|
<el-alert v-if="current.definitionMissing" type="error" :closable="false" show-icon
|
|
|
title="当前版本未找到规则定义"
|
|
|
description="该规则在当前系统版本中已无对应定义,无法调整参数、启用或预演。请联系实施人员确认版本。" />
|
|
|
|
|
|
- <!-- A. 基础定义:由系统版本定义,全只读。
|
|
|
- 刻意用 descriptions 而非 disabled input——后者会让人以为"暂时不能编辑"。 -->
|
|
|
- <el-divider content-position="left">基础定义(由系统版本定义)</el-divider>
|
|
|
- <el-descriptions :column="2" border size="small">
|
|
|
- <el-descriptions-item label="规则名称">{{ current.displayName ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="规则编码">{{ current.ruleCode }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="业务说明" :span="2">{{ current.description ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="数据集">{{ current.datasetDisplayName ?? current.datasetCode ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="业务对象">{{ current.sourceObjectType ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="规则类型">{{ current.ruleType ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警机制">{{ current.ruleMechanism ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="场景">{{ current.sceneCode ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="阶段">{{ current.stageCode ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="订单流程">{{ current.orderFlowCode ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="异常类型">{{ current.exceptionTypeCode ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="判定口径" :span="2">{{ current.judgementSummary ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="已完成状态" :span="2">
|
|
|
- <template v-if="current.completedStates.length">
|
|
|
- <el-tag v-for="s in current.completedStates" :key="s" size="small" style="margin-right: 6px">{{ s }}</el-tag>
|
|
|
- </template>
|
|
|
- <template v-else>—</template>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="去重对象" :span="2">{{ current.dedupIdentitySummary ?? '—' }}</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
-
|
|
|
- <!-- B. 运行参数:本页唯一可写区。 -->
|
|
|
- <el-divider content-position="left">运行参数</el-divider>
|
|
|
- <el-form label-width="150px" size="small" :disabled="current.definitionMissing">
|
|
|
- <el-form-item label="轮询间隔(秒)">
|
|
|
- <el-input-number v-model="form.pollIntervalSeconds"
|
|
|
- :min="policy?.pollIntervalSecondsMin ?? 60" :max="policy?.pollIntervalSecondsMax ?? 86400" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="连续命中建单次数">
|
|
|
- <el-input-number v-model="form.triggerCountRequired"
|
|
|
- :min="policy?.triggerCountRequiredMin ?? 1" :max="policy?.triggerCountRequiredMax ?? 10" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="连续未命中恢复次数">
|
|
|
- <el-input-number v-model="form.recoverCountRequired"
|
|
|
- :min="policy?.recoverCountRequiredMin ?? 1" :max="policy?.recoverCountRequiredMax ?? 10" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="宽限分钟">
|
|
|
- <el-input-number v-model="form.graceMinutes" :min="policy?.graceMinutesMin ?? 0" :max="graceMax" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="严重程度">
|
|
|
- <el-select v-model="form.severity" style="width: 180px">
|
|
|
- <el-option v-for="s in (policy?.allowedSeverities ?? ['FOLLOW', 'SERIOUS'])"
|
|
|
- :key="s" :label="severityLabel(s)" :value="s" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item v-if="policy?.allowsDepartmentDefaults !== false" label="默认发生部门">
|
|
|
- <!-- 刻意不 clearable:后端当前不支持清空,给一个点了没用的清除按钮比不给更糟。 -->
|
|
|
- <el-select v-model="form.defaultOccurrenceDeptId" filterable placeholder="未配置" style="width: 260px">
|
|
|
- <el-option v-for="d in departments" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item v-if="policy?.allowsDepartmentDefaults !== false" label="默认责任部门">
|
|
|
- <el-select v-model="form.defaultResponsibleDeptId" filterable placeholder="未配置" style="width: 260px">
|
|
|
- <el-option v-for="d in departments" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item v-if="currentMissingDepartments.length > 0">
|
|
|
- <el-alert type="warning" show-icon :closable="false"
|
|
|
- title="启用前必须配置默认发生部门和默认责任部门。"
|
|
|
- :description="`当前缺少:${currentMissingDepartments.join('、')}。该规则的数据集不提供部门信息,未配置将导致启用被拒绝。`" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" :loading="saving" @click="saveParameters">保存参数</el-button>
|
|
|
- <span class="hint">当前版本暂不支持清除已配置部门,如需变更请重新选择。</span>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
-
|
|
|
- <!-- C. 预演:只读模拟,不产生 Detection / Exception / Timeline。 -->
|
|
|
- <el-divider content-position="left">预演</el-divider>
|
|
|
- <div class="preview-bar">
|
|
|
- <el-button :loading="previewing" :disabled="current.definitionMissing" @click="runPreview">预演</el-button>
|
|
|
- <span class="hint">只读模拟:按当前参数试算会命中什么,不会建立任何异常单。</span>
|
|
|
- </div>
|
|
|
- <el-descriptions v-if="preview" :column="2" border size="small" style="margin-top: 12px">
|
|
|
- <el-descriptions-item label="候选行数">{{ preview.candidateRows }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="命中数">{{ preview.hitCount }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="将新建异常">{{ preview.wouldCreateCount }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="将刷新既有异常">{{ preview.wouldMatchExistingCount }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="未命中行数" :span="2">{{ preview.noHitRows }}</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
- <el-table v-if="preview && preview.hits.length" :data="preview.hits.slice(0, 20)" size="small" border
|
|
|
- style="margin-top: 12px">
|
|
|
- <el-table-column label="业务对象" prop="sourceObjectId" min-width="160" />
|
|
|
- <el-table-column label="关联单号" prop="relatedObjectCode" min-width="140" />
|
|
|
- <el-table-column label="名称" prop="relatedObjectName" min-width="140" show-overflow-tooltip />
|
|
|
- <el-table-column label="处理方式" width="120">
|
|
|
- <template #default="{ row }">{{ row.existingActiveExceptionId ? '刷新既有' : '新建' }}</template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- <div v-if="preview && preview.hits.length > 20" class="hint" style="margin-top: 6px">
|
|
|
- 仅展示前 20 条,共 {{ preview.hits.length }} 条命中。
|
|
|
- </div>
|
|
|
+ <el-tabs v-model="activeTab">
|
|
|
+ <!-- TAB 1 · 规则信息:由代码版本定义,业务用户不可改。 -->
|
|
|
+ <el-tab-pane label="规则信息" name="info">
|
|
|
+ <!-- A. 基础定义:由系统版本定义,全只读。
|
|
|
+ 刻意用 descriptions 而非 disabled input——后者会让人以为"暂时不能编辑"。 -->
|
|
|
+ <el-divider content-position="left">基础定义(由系统版本定义)</el-divider>
|
|
|
+ <el-descriptions :column="2" border size="small">
|
|
|
+ <el-descriptions-item label="规则名称">{{ current.displayName ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规则编码">{{ current.ruleCode }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="业务说明" :span="2">{{ current.description ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="数据集">{{ current.datasetDisplayName ?? current.datasetCode ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="业务对象">{{ current.sourceObjectType ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规则类型">{{ current.ruleType ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="报警机制">{{ current.ruleMechanism ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="场景">{{ current.sceneCode ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="阶段">{{ current.stageCode ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="订单流程">{{ current.orderFlowCode ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="异常类型">{{ current.exceptionTypeCode ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="判定口径" :span="2">{{ current.judgementSummary ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="已完成状态" :span="2">
|
|
|
+ <template v-if="current.completedStates.length">
|
|
|
+ <el-tag v-for="s in current.completedStates" :key="s" size="small" style="margin-right: 6px">{{ s }}</el-tag>
|
|
|
+ </template>
|
|
|
+ <template v-else>—</template>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="去重对象" :span="2">{{ current.dedupIdentitySummary ?? '—' }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- TAB 2 · 运行设置:本弹窗唯一可写区 + 预演 + 运行状态。 -->
|
|
|
+ <el-tab-pane label="运行设置" name="runtime">
|
|
|
+ <!-- B. 运行参数:本页唯一可写区。 -->
|
|
|
+ <el-divider content-position="left">运行参数</el-divider>
|
|
|
+ <el-form label-width="150px" size="small" :disabled="current.definitionMissing">
|
|
|
+ <el-form-item label="轮询间隔(秒)">
|
|
|
+ <el-input-number v-model="form.pollIntervalSeconds"
|
|
|
+ :min="policy?.pollIntervalSecondsMin ?? 60" :max="policy?.pollIntervalSecondsMax ?? 86400" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="连续命中建单次数">
|
|
|
+ <el-input-number v-model="form.triggerCountRequired"
|
|
|
+ :min="policy?.triggerCountRequiredMin ?? 1" :max="policy?.triggerCountRequiredMax ?? 10" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="连续未命中恢复次数">
|
|
|
+ <el-input-number v-model="form.recoverCountRequired"
|
|
|
+ :min="policy?.recoverCountRequiredMin ?? 1" :max="policy?.recoverCountRequiredMax ?? 10" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="宽限分钟">
|
|
|
+ <el-input-number v-model="form.graceMinutes" :min="policy?.graceMinutesMin ?? 0" :max="graceMax" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="严重程度">
|
|
|
+ <el-select v-model="form.severity" style="width: 180px">
|
|
|
+ <el-option v-for="s in (policy?.allowedSeverities ?? ['FOLLOW', 'SERIOUS'])"
|
|
|
+ :key="s" :label="severityLabel(s)" :value="s" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="policy?.allowsDepartmentDefaults !== false" label="默认发生部门">
|
|
|
+ <!-- 刻意不 clearable:后端当前不支持清空,给一个点了没用的清除按钮比不给更糟。 -->
|
|
|
+ <el-select v-model="form.defaultOccurrenceDeptId" filterable placeholder="未配置" style="width: 260px">
|
|
|
+ <el-option v-for="d in departments" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="policy?.allowsDepartmentDefaults !== false" label="默认责任部门">
|
|
|
+ <el-select v-model="form.defaultResponsibleDeptId" filterable placeholder="未配置" style="width: 260px">
|
|
|
+ <el-option v-for="d in departments" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item v-if="currentMissingDepartments.length > 0">
|
|
|
+ <el-alert type="warning" show-icon :closable="false"
|
|
|
+ title="启用前必须配置默认发生部门和默认责任部门。"
|
|
|
+ :description="`当前缺少:${currentMissingDepartments.join('、')}。该规则的数据集不提供部门信息,未配置将导致启用被拒绝。`" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" :loading="saving" @click="saveParameters">保存参数</el-button>
|
|
|
+ <span class="hint">当前版本暂不支持清除已配置部门,如需变更请重新选择。</span>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- C. 预演:只读模拟,不产生 Detection / Exception / Timeline。 -->
|
|
|
+ <el-divider content-position="left">预演</el-divider>
|
|
|
+ <div class="preview-bar">
|
|
|
+ <el-button :loading="previewing" :disabled="current.definitionMissing" @click="runPreview">预演</el-button>
|
|
|
+ <span class="hint">只读模拟:按当前参数试算会命中什么,不会建立任何异常单。</span>
|
|
|
+ </div>
|
|
|
+ <el-descriptions v-if="preview" :column="2" border size="small" style="margin-top: 12px">
|
|
|
+ <el-descriptions-item label="候选行数">{{ preview.candidateRows }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="命中数">{{ preview.hitCount }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="将新建异常">{{ preview.wouldCreateCount }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="将刷新既有异常">{{ preview.wouldMatchExistingCount }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="未命中行数" :span="2">{{ preview.noHitRows }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <el-table v-if="preview && preview.hits.length" :data="preview.hits.slice(0, 20)" size="small" border
|
|
|
+ style="margin-top: 12px">
|
|
|
+ <el-table-column label="业务对象" prop="sourceObjectId" min-width="160" />
|
|
|
+ <el-table-column label="关联单号" prop="relatedObjectCode" min-width="140" />
|
|
|
+ <el-table-column label="名称" prop="relatedObjectName" min-width="140" show-overflow-tooltip />
|
|
|
+ <el-table-column label="处理方式" width="120">
|
|
|
+ <template #default="{ row }">{{ row.existingActiveExceptionId ? '刷新既有' : '新建' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div v-if="preview && preview.hits.length > 20" class="hint" style="margin-top: 6px">
|
|
|
+ 仅展示前 20 条,共 {{ preview.hits.length }} 条命中。
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- D. 运行状态:系统写,页面只读。不展示调度租约等内部细节。 -->
|
|
|
- <el-divider content-position="left">运行状态</el-divider>
|
|
|
- <el-descriptions :column="2" border size="small">
|
|
|
- <el-descriptions-item label="当前状态">
|
|
|
- <el-tag size="small" :type="STATE_META[ruleState(current)].type">{{ STATE_META[ruleState(current)].label }}</el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="下次运行">{{ current.nextRunAt ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="最近运行">{{ current.lastRunAt ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="最近结果">{{ lastResultText(current) }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="耗时">{{ current.lastDurationMs != null ? current.lastDurationMs + ' ms' : '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="连续失败">{{ current.consecutiveFailureCount }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="暂停至">{{ current.pausedUntil ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="暂停原因">{{ current.pauseReason ?? '—' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="错误摘要" :span="2">{{ current.lastError ?? '—' }}</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
- <div class="runtime-actions">
|
|
|
- <el-button size="small" type="success" :disabled="current.definitionMissing || !current.enabled"
|
|
|
- @click="runNow(current)">立即执行</el-button>
|
|
|
- <span class="hint">「立即执行」会真实运行并可能建立异常单,与上方「预演」不同。</span>
|
|
|
- </div>
|
|
|
+ <!-- D. 运行状态:系统写,页面只读。不展示调度租约等内部细节。 -->
|
|
|
+ <el-divider content-position="left">运行状态</el-divider>
|
|
|
+ <el-descriptions :column="2" border size="small">
|
|
|
+ <el-descriptions-item label="当前状态">
|
|
|
+ <el-tag size="small" :type="STATE_META[ruleState(current)].type">{{ STATE_META[ruleState(current)].label }}</el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="下次运行">{{ current.nextRunAt ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="最近运行">{{ current.lastRunAt ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="最近结果">{{ lastResultText(current) }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="耗时">{{ current.lastDurationMs != null ? current.lastDurationMs + ' ms' : '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="连续失败">{{ current.consecutiveFailureCount }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="暂停至">{{ current.pausedUntil ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="暂停原因">{{ current.pauseReason ?? '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="错误摘要" :span="2">{{ current.lastError ?? '—' }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <div class="runtime-actions">
|
|
|
+ <el-button size="small" type="success" :disabled="current.definitionMissing || !current.enabled"
|
|
|
+ @click="runNow(current)">立即执行</el-button>
|
|
|
+ <span class="hint">「立即执行」会真实运行并可能建立异常单,与上方「预演」不同。</span>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- TAB 3 · 处理与审核:责任维度(可写)+ 审核来源(只读事实)。 -->
|
|
|
+ <el-tab-pane label="处理与审核" name="people">
|
|
|
+ <!-- S8-HANDLER-POOL-1:责任维度。谁负责这条规则,与「异常操作权限」的角色维度正交。 -->
|
|
|
+ <el-card shadow="never" style="margin-bottom: 12px">
|
|
|
+ <template #header>
|
|
|
+ <div style="display: flex; justify-content: space-between; align-items: center">
|
|
|
+ <span style="font-weight: 600">处理账号(该规则由谁负责)</span>
|
|
|
+ <el-button type="primary" size="small" :loading="savingPool"
|
|
|
+ :disabled="!handlerPoolLoaded" @click="saveHandlerPool">保存处理账号</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-alert v-if="handlerPoolLoaded && handlerPool.length === 0" type="warning"
|
|
|
+ :closable="false" show-icon style="margin-bottom: 10px">
|
|
|
+ <template #title>该规则尚未配置处理账号</template>
|
|
|
+ <div style="line-height: 1.9">
|
|
|
+ 没有处理账号的规则<strong>无法启用</strong>:它会源源不断建出异常、却没有人能认领,
|
|
|
+ 而调度器每一轮都报成功。
|
|
|
+ </div>
|
|
|
+ </el-alert>
|
|
|
+ <el-alert v-if="handlerPoolInvalid.length" type="error" :closable="false" show-icon
|
|
|
+ style="margin-bottom: 10px">
|
|
|
+ <template #title>有 {{ handlerPoolInvalid.length }} 个已配置的账号当前不可用</template>
|
|
|
+ <div style="line-height: 1.9">
|
|
|
+ 这些账号已停用或不在当前租户,<strong>不再具备认领资格</strong>。请重新选择后保存。
|
|
|
+ </div>
|
|
|
+ </el-alert>
|
|
|
+ <el-select v-model="handlerPool" multiple filterable clearable collapse-tags collapse-tags-tooltip
|
|
|
+ placeholder="选择负责该规则的系统账号" style="width: 100%" :loading="!handlerPoolLoaded">
|
|
|
+ <el-option v-for="u in tenantUsers" :key="u.id" :label="u.name" :value="u.id">
|
|
|
+ <span>{{ u.name }} <span style="color: #999; font-size: 12px">{{ u.account ? '(' + u.account + ')' : '' }}</span></span>
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ <div style="color: #909399; font-size: 12px; margin-top: 6px">
|
|
|
+ 池成员才能认领 / 被转派该规则产生的异常。能否认领还同时取决于账号是否拥有「认领」动作权限,
|
|
|
+ 以及单据当前状态。
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!--
|
|
|
+ 审核区块**只读**。本批不新建 Rule 级 Reviewer Pool、不改 ApproverType、
|
|
|
+ 不动流程节点、不改 owner/escalate 语义 —— 只如实说明当前审核人来自哪里。
|
|
|
+ 此前这里显示的是「主管」,而系统里根本不存在组织主管关系
|
|
|
+ (SysOrg.DirectorId 与 SysUser.ManagerUserId 实测填充率均为 0)。
|
|
|
+ -->
|
|
|
+ <el-card shadow="never" v-loading="!reviewLoaded">
|
|
|
+ <template #header>
|
|
|
+ <span style="font-weight: 600">审核来源(只读)</span>
|
|
|
+ <span style="color: #909399; font-size: 12px; margin-left: 8px">当前审核人由下列机制决定,本页不修改它们</span>
|
|
|
+ </template>
|
|
|
+ <div v-for="st in reviewStages" :key="st.stage" style="margin-bottom: 14px">
|
|
|
+ <div style="font-weight: 600; margin-bottom: 4px">{{ st.stage }}</div>
|
|
|
+ <div style="color: #606266; line-height: 1.9">{{ st.decidedBy }}</div>
|
|
|
+ <el-alert v-for="(w, wi) in st.warnings" :key="wi" type="warning" :closable="false" show-icon style="margin-top: 6px" :title="w" />
|
|
|
+ <el-table v-if="st.nodes.length" :data="st.nodes" size="small" border style="margin-top: 8px">
|
|
|
+ <el-table-column prop="nodeName" label="审批节点" width="150" />
|
|
|
+ <el-table-column prop="approverType" label="审批人类型" width="130" />
|
|
|
+ <el-table-column prop="approverIds" label="配置值" min-width="200" />
|
|
|
+ <el-table-column label="本租户实际解析" min-width="240">
|
|
|
+ <template #default="scope">
|
|
|
+ <span v-if="scope.row.resolvedUsers.length">{{ scope.row.resolvedUsers.join('、') }}</span>
|
|
|
+ <el-tag v-else type="danger" size="small">解析不出任何审批人</el-tag>
|
|
|
+ <div v-if="scope.row.crossTenantRefs.length" style="color: #e6a23c; font-size: 12px; margin-top: 4px">越界引用:{{ scope.row.crossTenantRefs.join('、') }}</div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ <el-divider v-if="escalateRoles.length" content-position="left">超时自动升级角色(来自异常类型配置)</el-divider>
|
|
|
+ <div v-for="(r, ri) in escalateRoles" :key="ri" style="color: #606266; line-height: 1.9">{{ r }}</div>
|
|
|
+ <div style="margin-top: 10px">
|
|
|
+ <el-button link type="primary" @click="goApprovalConfig">查看 / 修改审批流程配置</el-button>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!--
|
|
|
+ TAB 4 · 通知与升级:P1-E 的 UI 容器。
|
|
|
+ 刻意不铺 L1_OPERATOR / L2_MANAGER / L3_DIRECTOR ——
|
|
|
+ 那三档在库里只有 L1 真正被用过,且「主管 / 总监」没有对应业务实体。
|
|
|
+ -->
|
|
|
+ <el-tab-pane label="通知与升级" name="notify">
|
|
|
+ <el-empty description="通知收件人配置将在下一批(P1-E)接入">
|
|
|
+ <div style="color: #909399; font-size: 12px; line-height: 1.9; max-width: 560px">
|
|
|
+ 届时可按事件配置收件人:异常产生 → 处理人员池;认领成功 → 当前处理人;<br />
|
|
|
+ 提交复核 → 上方「审核来源」解析出的实际审核人;超时升级 → 指定账号。
|
|
|
+ </div>
|
|
|
+ </el-empty>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
</template>
|
|
|
- </el-drawer>
|
|
|
+ </el-dialog>
|
|
|
</AidopDemoShell>
|
|
|
</template>
|
|
|
|