|
|
@@ -4,6 +4,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
import AidopDemoShell from '../../components/AidopDemoShell.vue';
|
|
|
import {
|
|
|
s8ConfigApi,
|
|
|
+ type S8DimensionNode,
|
|
|
type S8ExceptionTypeConfigRow,
|
|
|
type S8WatchRuleConfigRow,
|
|
|
type S8WatchRuleType,
|
|
|
@@ -92,6 +93,38 @@ const outOfRangeForm = reactive<OutOfRangeForm>({
|
|
|
const enabledForm = ref(true);
|
|
|
const rawParamsJson = ref<string>('');
|
|
|
|
|
|
+// TASK-002-RESET-DIMENSION-MODEL-DEV-3:业务维度(S_STAGE / ORDER_FLOW)+ 报警机制。
|
|
|
+interface DimensionForm {
|
|
|
+ stageCode: string;
|
|
|
+ orderFlowCode: string;
|
|
|
+ ruleMechanism: string;
|
|
|
+}
|
|
|
+const dimensionForm = reactive<DimensionForm>({
|
|
|
+ stageCode: '',
|
|
|
+ orderFlowCode: '',
|
|
|
+ ruleMechanism: '',
|
|
|
+});
|
|
|
+const stageNodes = ref<S8DimensionNode[]>([]);
|
|
|
+const orderFlowNodes = ref<S8DimensionNode[]>([]);
|
|
|
+const RULE_MECHANISM_OPTIONS = [
|
|
|
+ { value: 'DATE', label: '日期类' },
|
|
|
+ { value: 'VALUE_RANGE', label: '数值超差类' },
|
|
|
+ { value: 'RATIO', label: '比例类' },
|
|
|
+ { value: 'MANUAL_REPORT', label: '主动提报' },
|
|
|
+];
|
|
|
+function ruleMechanismLabel(code?: string | null): string {
|
|
|
+ const found = RULE_MECHANISM_OPTIONS.find(o => o.value === code);
|
|
|
+ return found?.label ?? code ?? '—';
|
|
|
+}
|
|
|
+function stageNodeLabel(code?: string | null): string {
|
|
|
+ if (!code) return '—';
|
|
|
+ return stageNodes.value.find(n => n.nodeCode === code)?.nodeName ?? code;
|
|
|
+}
|
|
|
+function orderFlowNodeLabel(code?: string | null): string {
|
|
|
+ if (!code) return '—';
|
|
|
+ return orderFlowNodes.value.find(n => n.nodeCode === code)?.nodeName ?? code;
|
|
|
+}
|
|
|
+
|
|
|
// S8-SCHED-FRONTEND-1:调度参数编辑表单。
|
|
|
interface ScheduleForm {
|
|
|
pollIntervalSeconds: number;
|
|
|
@@ -122,6 +155,13 @@ interface ScheduleSnapshot {
|
|
|
const originalScheduleSnapshot = ref<ScheduleSnapshot | null>(null);
|
|
|
const originalParamsSnapshot = ref<string | null>(null);
|
|
|
const originalEnabled = ref<boolean>(true);
|
|
|
+// TASK-002-RESET-DIMENSION-MODEL-DEV-3:维度三字段快照
|
|
|
+interface DimensionSnapshot {
|
|
|
+ stageCode: string;
|
|
|
+ orderFlowCode: string;
|
|
|
+ ruleMechanism: string;
|
|
|
+}
|
|
|
+const originalDimensionSnapshot = ref<DimensionSnapshot | null>(null);
|
|
|
|
|
|
// S8-SCHED-FRONTEND-1:30s 自动刷新;onMounted/onActivated 启动;onUnmounted/onDeactivated 清理。
|
|
|
const REFRESH_INTERVAL_MS = 30000;
|
|
|
@@ -456,6 +496,20 @@ async function loadExceptionTypes() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// TASK-002-RESET-DIMENSION-MODEL-DEV-3:加载 S_STAGE / ORDER_FLOW 维度节点(首版均为顶层叶节点)。
|
|
|
+async function loadDimensionNodes() {
|
|
|
+ try {
|
|
|
+ const [stage, flow] = await Promise.all([
|
|
|
+ s8ConfigApi.dimensionNodes({ tenantId: TENANT_ID, factoryId: FACTORY_ID, dimensionCode: 'S_STAGE' }),
|
|
|
+ s8ConfigApi.dimensionNodes({ tenantId: TENANT_ID, factoryId: FACTORY_ID, dimensionCode: 'ORDER_FLOW' }),
|
|
|
+ ]);
|
|
|
+ stageNodes.value = stage;
|
|
|
+ orderFlowNodes.value = flow;
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.message ?? '加载业务维度节点失败');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
function resetForms() {
|
|
|
Object.assign(timeoutForm, {
|
|
|
dueAtField: '', statusField: '', completedStates: '',
|
|
|
@@ -482,6 +536,15 @@ function loadFormFromRow(row: S8WatchRuleConfigRow) {
|
|
|
scheduleForm.pollIntervalSeconds = clampInt(row.pollIntervalSeconds ?? 300, 60, 86400, 300);
|
|
|
scheduleForm.triggerCountRequired = clampInt(row.triggerCountRequired ?? 1, 1, 10, 1);
|
|
|
scheduleForm.recoverCountRequired = clampInt(row.recoverCountRequired ?? 1, 1, 10, 1);
|
|
|
+ // TASK-002-RESET-DIMENSION-MODEL-DEV-3:维度三字段。机制缺省按 rule_type 推荐。
|
|
|
+ dimensionForm.stageCode = row.stageCode ?? '';
|
|
|
+ dimensionForm.orderFlowCode = row.orderFlowCode ?? '';
|
|
|
+ if (row.ruleMechanism) {
|
|
|
+ dimensionForm.ruleMechanism = row.ruleMechanism;
|
|
|
+ } else {
|
|
|
+ const t = (row.ruleType ?? '').toUpperCase();
|
|
|
+ dimensionForm.ruleMechanism = t === 'TIMEOUT' ? 'DATE' : t === 'OUT_OF_RANGE' ? 'VALUE_RANGE' : '';
|
|
|
+ }
|
|
|
|
|
|
if (!row.paramsJson) return;
|
|
|
let parsed: Record<string, any>;
|
|
|
@@ -552,9 +615,24 @@ function openEdit(row: S8WatchRuleConfigRow) {
|
|
|
};
|
|
|
originalParamsSnapshot.value = buildPayloadParamsJson();
|
|
|
originalEnabled.value = enabledForm.value;
|
|
|
+ originalDimensionSnapshot.value = {
|
|
|
+ stageCode: dimensionForm.stageCode,
|
|
|
+ orderFlowCode: dimensionForm.orderFlowCode,
|
|
|
+ ruleMechanism: dimensionForm.ruleMechanism,
|
|
|
+ };
|
|
|
drawerOpen.value = true;
|
|
|
}
|
|
|
|
|
|
+function hasDimensionChanged(): boolean {
|
|
|
+ const o = originalDimensionSnapshot.value;
|
|
|
+ if (!o) return false;
|
|
|
+ return (
|
|
|
+ dimensionForm.stageCode !== o.stageCode
|
|
|
+ || dimensionForm.orderFlowCode !== o.orderFlowCode
|
|
|
+ || dimensionForm.ruleMechanism !== o.ruleMechanism
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
function hasScheduleChanged(): boolean {
|
|
|
const o = originalScheduleSnapshot.value;
|
|
|
if (!o) return false;
|
|
|
@@ -568,6 +646,8 @@ function hasScheduleChanged(): boolean {
|
|
|
function hasParamsChanged(): boolean {
|
|
|
// enabled 在后端走 /params 端点,因此 enabled 切换视为 params 变更。
|
|
|
if (enabledForm.value !== originalEnabled.value) return true;
|
|
|
+ // TASK-002-RESET-DIMENSION-MODEL-DEV-3:维度三字段也走 /params 端点保存。
|
|
|
+ if (hasDimensionChanged()) return true;
|
|
|
const next = buildPayloadParamsJson();
|
|
|
return next !== originalParamsSnapshot.value;
|
|
|
}
|
|
|
@@ -673,6 +753,10 @@ async function save() {
|
|
|
await s8ConfigApi.watchRules.updateParams(editingRow.value.id, {
|
|
|
paramsJson: newParamsJson,
|
|
|
enabled: newEnabled,
|
|
|
+ // TASK-002-RESET-DIMENSION-MODEL-DEV-3:维度三字段随 params 一起保存。
|
|
|
+ stageCode: dimensionForm.stageCode || null,
|
|
|
+ orderFlowCode: dimensionForm.orderFlowCode || null,
|
|
|
+ ruleMechanism: dimensionForm.ruleMechanism || null,
|
|
|
});
|
|
|
paramsSaved = true;
|
|
|
} catch (e) {
|
|
|
@@ -687,6 +771,11 @@ async function save() {
|
|
|
if (paramsSaved) {
|
|
|
originalParamsSnapshot.value = newParamsJson;
|
|
|
originalEnabled.value = newEnabled;
|
|
|
+ originalDimensionSnapshot.value = {
|
|
|
+ stageCode: dimensionForm.stageCode,
|
|
|
+ orderFlowCode: dimensionForm.orderFlowCode,
|
|
|
+ ruleMechanism: dimensionForm.ruleMechanism,
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
if (scheduleChanged && paramsChanged) {
|
|
|
@@ -812,6 +901,10 @@ async function toggleEnabled(row: S8WatchRuleConfigRow) {
|
|
|
await s8ConfigApi.watchRules.updateParams(row.id, {
|
|
|
paramsJson: row.paramsJson ?? null,
|
|
|
enabled: next,
|
|
|
+ // TASK-002-RESET-DIMENSION-MODEL-DEV-3:列表行启停切换时保留维度三字段,避免被服务端清空。
|
|
|
+ stageCode: row.stageCode ?? null,
|
|
|
+ orderFlowCode: row.orderFlowCode ?? null,
|
|
|
+ ruleMechanism: row.ruleMechanism ?? null,
|
|
|
});
|
|
|
ElMessage.success(`${action}成功`);
|
|
|
await loadRows();
|
|
|
@@ -823,6 +916,7 @@ async function toggleEnabled(row: S8WatchRuleConfigRow) {
|
|
|
onMounted(() => {
|
|
|
loadRows();
|
|
|
loadExceptionTypes();
|
|
|
+ loadDimensionNodes();
|
|
|
startAutoRefresh();
|
|
|
});
|
|
|
onUnmounted(() => stopAutoRefresh());
|
|
|
@@ -859,6 +953,18 @@ onDeactivated(() => stopAutoRefresh());
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="sceneCode" label="场景" width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="机制" width="100">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag v-if="row.ruleMechanism" size="small" type="info">{{ ruleMechanismLabel(row.ruleMechanism) }}</el-tag>
|
|
|
+ <span v-else>—</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="阶段维度" width="140" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ stageNodeLabel(row.stageCode) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="订单流程" width="160" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ orderFlowNodeLabel(row.orderFlowCode) }}</template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="严重度" width="80">
|
|
|
<template #default="{ row }">
|
|
|
<el-tag v-if="row.severity" :type="severityTagType(row.severity)" size="small">
|
|
|
@@ -958,6 +1064,26 @@ onDeactivated(() => stopAutoRefresh());
|
|
|
<el-descriptions-item label="源对象">{{ editingRow.sourceObjectType ?? editingRow.watchObjectType ?? '—' }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
|
|
|
+ <el-divider content-position="left">维度归属与机制</el-divider>
|
|
|
+ <el-form label-position="top" size="small">
|
|
|
+ <el-form-item label="报警机制">
|
|
|
+ <el-select v-model="dimensionForm.ruleMechanism" clearable placeholder="选择报警机制" style="width: 100%">
|
|
|
+ <el-option v-for="o in RULE_MECHANISM_OPTIONS" :key="o.value" :label="o.label" :value="o.value" />
|
|
|
+ </el-select>
|
|
|
+ <span class="rule-hint">建议:日期类→DATE / 数值超差→VALUE_RANGE / 比例类→RATIO;缺料类待业务方确认</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="S_STAGE 阶段(S1-S7)">
|
|
|
+ <el-select v-model="dimensionForm.stageCode" clearable filterable placeholder="选择阶段维度" style="width: 100%">
|
|
|
+ <el-option v-for="n in stageNodes" :key="n.nodeCode" :label="n.nodeName" :value="n.nodeCode" :disabled="n.disabled" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="ORDER_FLOW 订单全流程(可选)">
|
|
|
+ <el-select v-model="dimensionForm.orderFlowCode" clearable filterable placeholder="选择订单流程节点(可不选)" style="width: 100%">
|
|
|
+ <el-option v-for="n in orderFlowNodes" :key="n.nodeCode" :label="n.nodeName" :value="n.nodeCode" :disabled="n.disabled" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
<el-divider content-position="left">触发条件</el-divider>
|
|
|
|
|
|
<!-- TIMEOUT form -->
|