Просмотр исходного кода

fix(s8): unify delivery/production/supply module card semantics with overview

YY968XX 4 месяцев назад
Родитель
Сommit
9cf2c0ddd4

+ 23 - 8
Web/src/views/aidop/s8/monitoring/S8MonitoringDeliveryPage.vue

@@ -132,7 +132,7 @@ import S8MonitoringCategoryGrid, { type CategoryGridCardData } from './component
 import S8MonitoringEditToolbar from './components/S8MonitoringEditToolbar.vue';
 import S8MonitoringStageConfigDrawer from './components/S8MonitoringStageConfigDrawer.vue';
 import { useS8UnsavedLayoutEditGuard } from './useS8UnsavedLayoutEditGuard';
-import { useS8StageConfig, buildModuleTriColorStats } from './useS8StageConfig';
+import { useS8StageConfig, buildModuleTriColorStats, DEFAULT_NORMAL_WORK_ORDER_COUNT } from './useS8StageConfig';
 import { useS8CategoryConfig } from './useS8CategoryConfig';
 import { useS8PageConfigDriver } from './useS8PageConfigDriver';
 
@@ -228,9 +228,10 @@ const moduleData = reactive<{ list: S8ModuleOrderSummary[] }>({ list: [] });
 const pageConfig = useS8PageConfigDriver({ pageCode: 'DELIVERY', pageCodePrefix: 'DELIVERY' });
 
 // fallback 硬编码(不删除,作 page-config 失败时回退)
+// S8-MONITORING-PAGES-STAGE-CARD-SEMANTICS-UNIFY-1:主指标统一为「累计异常数」+ value=total,与 overview 口径一致。
 const STAGE_META_FALLBACK = [
-	{ code: 'S1', title: '销售评审', icon: Checked as Component, metricLabel: '累计监控单量' },
-	{ code: 'S7', title: '订单交付', icon: Van as Component,     metricLabel: '累计异常数' },
+	{ code: 'S1', title: '销售评审', icon: Checked as Component, metricLabel: '累计异常数' },
+	{ code: 'S7', title: '订单交付', icon: Van as Component,     metricLabel: '累计异常数' },
 ] as const;
 
 const ANOMALY_TYPES_FALLBACK: ReadonlyArray<{ key: string; label: string }> = [
@@ -271,15 +272,25 @@ async function loadPageConfig() {
 
 const moduleMap = computed(() => new Map(moduleData.list.map((m) => [m.moduleCode, m])));
 
+// S8-MONITORING-PAGES-STAGE-CARD-SEMANTICS-UNIFY-1:与 overview 口径一致:
+// - healthRate = closeRate;total=0 时空态显示 `闭环率 --`
+// - 进度条分母 = normal + yellow + red(normal 暂用 DEFAULT_NORMAL_WORK_ORDER_COUNT)
+// - wideRateLabel 合成 `闭环率 X% / 均时 Yh`,total=0 显示 `闭环率 -- / 均时 --`
 const stageCards = computed(() =>
 	effectiveStageMeta.value.map((meta) => {
 		const m = moduleMap.value.get(meta.code);
 		const total  = Math.max(m?.total ?? 0, 0);
 		const red    = Math.max(m?.red ?? 0, 0);
 		const yellow = Math.max(m?.yellow ?? 0, 0);
-		const green  = Math.max(m?.green ?? Math.max(total - red - yellow, 0), 0);
-		const healthRate = total > 0 ? Math.round((green / total) * 100) : 0;
+		const closeRate = Math.max(0, Math.min(100, Math.round(m?.closeRate ?? 0)));
+		const avgProcessHours = m?.avgProcessHours ?? 0;
 		const tone: 'green' | 'yellow' | 'red' = red > 0 ? 'red' : yellow > 0 ? 'yellow' : 'green';
+		const hasSamples = total > 0;
+		const hasAvgHours = hasSamples && Number.isFinite(avgProcessHours) && avgProcessHours > 0;
+		const closeRateText = hasSamples ? formatPercent(closeRate) : '--';
+		const avgHoursText = hasAvgHours ? formatHours(avgProcessHours) : '--';
+		const normal = DEFAULT_NORMAL_WORK_ORDER_COUNT;
+		const denom = normal + yellow + red;
 		const raw = {
 			code: meta.code,
 			title: meta.title,
@@ -288,15 +299,19 @@ const stageCards = computed(() =>
 			value: formatInteger(total),
 			// S8-DASHBOARD-MODULE-TRICOLOR-SEMANTICS-1:三色数字(绿正常 / 黄关注 / 红严重)
 			values: buildModuleTriColorStats({ total: red + yellow, red, yellow }),
-			healthRate,
+			healthRate: closeRate,
+			healthRateText: hasSamples ? undefined : '--',
 			statusLabel: `${red + yellow} 异常`,
-			progress: total > 0
-				? { green: (green / total) * 100, yellow: (yellow / total) * 100, red: (red / total) * 100 }
+			progress: denom > 0
+				? { green: (normal / denom) * 100, yellow: (yellow / denom) * 100, red: (red / denom) * 100 }
 				: { green: 0, yellow: 0, red: 0 },
 			tone,
 			wide: false,
 			cardSpan: 1,
 			minHeight: 176,
+			wideRate: closeRate,
+			wideRateLabel: `闭环率 ${closeRateText} / 均时 ${avgHoursText}`,
+			avgProcessHours,
 		};
 		return applyConfig(raw);
 	}),

+ 22 - 13
Web/src/views/aidop/s8/monitoring/S8MonitoringProductionPage.vue

@@ -48,7 +48,7 @@
 							</div>
 							<div class="anomaly-type-card__metrics">
 								<div class="anomaly-type-card__metric">
-									<div class="anomaly-type-card__metric-label">频率/百单</div>
+									<div class="anomaly-type-card__metric-label">异常数</div>
 									<div class="anomaly-type-card__metric-value">{{ formatRate(item.avgProcessHours) }}</div>
 								</div>
 								<div class="anomaly-type-card__metric">
@@ -132,7 +132,7 @@ import S8MonitoringCategoryGrid, { type CategoryGridCardData } from './component
 import S8MonitoringEditToolbar from './components/S8MonitoringEditToolbar.vue';
 import S8MonitoringStageConfigDrawer from './components/S8MonitoringStageConfigDrawer.vue';
 import { useS8UnsavedLayoutEditGuard } from './useS8UnsavedLayoutEditGuard';
-import { useS8StageConfig, buildModuleTriColorStats } from './useS8StageConfig';
+import { useS8StageConfig, buildModuleTriColorStats, DEFAULT_NORMAL_WORK_ORDER_COUNT } from './useS8StageConfig';
 import { useS8CategoryConfig } from './useS8CategoryConfig';
 import { useS8PageConfigDriver } from './useS8PageConfigDriver';
 
@@ -226,9 +226,10 @@ const moduleData = reactive<{ list: S8ModuleOrderSummary[] }>({ list: [] });
 // ─── G-09 一期 Part D:page-config driver 接入 ─────────────────────────
 const pageConfig = useS8PageConfigDriver({ pageCode: 'PRODUCTION', pageCodePrefix: 'PRODUCTION' });
 
+// S8-MONITORING-PAGES-STAGE-CARD-SEMANTICS-UNIFY-1:主指标统一为「累计异常数」+ value=total。
 const STAGE_META_FALLBACK = [
-	{ code: 'S2', title: '计划排产', icon: TrendCharts as Component, metricLabel: '异常频率/百单' },
-	{ code: 'S6', title: '总装入库', icon: Box as Component,         metricLabel: '累计异常数' },
+	{ code: 'S2', title: '计划排产', icon: TrendCharts as Component, metricLabel: '累计异常数' },
+	{ code: 'S6', title: '总装入库', icon: Box as Component,         metricLabel: '累计异常数' },
 ] as const;
 
 const ANOMALY_TYPES_FALLBACK: ReadonlyArray<{ key: string; label: string }> = [
@@ -269,35 +270,43 @@ async function loadPageConfig() {
 
 const moduleMap = computed(() => new Map(moduleData.list.map((m) => [m.moduleCode, m])));
 
+// S8-MONITORING-PAGES-STAGE-CARD-SEMANTICS-UNIFY-1:与 overview 口径一致。S2 主数字改为 total(去除 frequency)。
 const stageCards = computed(() =>
 	effectiveStageMeta.value.map((meta) => {
 		const m = moduleMap.value.get(meta.code);
 		const total  = Math.max(m?.total ?? 0, 0);
 		const red    = Math.max(m?.red ?? 0, 0);
 		const yellow = Math.max(m?.yellow ?? 0, 0);
-		const green  = Math.max(m?.green ?? Math.max(total - red - yellow, 0), 0);
-		const healthRate = total > 0 ? Math.round((green / total) * 100) : 0;
+		const closeRate = Math.max(0, Math.min(100, Math.round(m?.closeRate ?? 0)));
+		const avgProcessHours = m?.avgProcessHours ?? 0;
 		const tone: 'green' | 'yellow' | 'red' = red > 0 ? 'red' : yellow > 0 ? 'yellow' : 'green';
-		const value = meta.code === 'S2'
-			? (m?.frequency ?? 0).toFixed(1)
-			: formatInteger(total);
+		const hasSamples = total > 0;
+		const hasAvgHours = hasSamples && Number.isFinite(avgProcessHours) && avgProcessHours > 0;
+		const closeRateText = hasSamples ? formatPercent(closeRate) : '--';
+		const avgHoursText = hasAvgHours ? formatHours(avgProcessHours) : '--';
+		const normal = DEFAULT_NORMAL_WORK_ORDER_COUNT;
+		const denom = normal + yellow + red;
 		const raw = {
 			code: meta.code,
 			title: meta.title,
 			icon: meta.icon,
 			metricLabel: meta.metricLabel,
-			value,
+			value: formatInteger(total),
 			// S8-DASHBOARD-MODULE-TRICOLOR-SEMANTICS-1:三色数字(绿正常 / 黄关注 / 红严重)
 			values: buildModuleTriColorStats({ total: red + yellow, red, yellow }),
-			healthRate,
+			healthRate: closeRate,
+			healthRateText: hasSamples ? undefined : '--',
 			statusLabel: `${red + yellow} 异常`,
-			progress: total > 0
-				? { green: (green / total) * 100, yellow: (yellow / total) * 100, red: (red / total) * 100 }
+			progress: denom > 0
+				? { green: (normal / denom) * 100, yellow: (yellow / denom) * 100, red: (red / denom) * 100 }
 				: { green: 0, yellow: 0, red: 0 },
 			tone,
 			wide: false,
 			cardSpan: 1,
 			minHeight: 176,
+			wideRate: closeRate,
+			wideRateLabel: `闭环率 ${closeRateText} / 均时 ${avgHoursText}`,
+			avgProcessHours,
 		};
 		return applyConfig(raw);
 	}),

+ 24 - 19
Web/src/views/aidop/s8/monitoring/S8MonitoringSupplyPage.vue

@@ -132,7 +132,7 @@ import S8MonitoringCategoryGrid, { type CategoryGridCardData } from './component
 import S8MonitoringEditToolbar from './components/S8MonitoringEditToolbar.vue';
 import S8MonitoringStageConfigDrawer from './components/S8MonitoringStageConfigDrawer.vue';
 import { useS8UnsavedLayoutEditGuard } from './useS8UnsavedLayoutEditGuard';
-import { useS8StageConfig, buildModuleTriColorStats } from './useS8StageConfig';
+import { useS8StageConfig, buildModuleTriColorStats, DEFAULT_NORMAL_WORK_ORDER_COUNT } from './useS8StageConfig';
 import { useS8CategoryConfig } from './useS8CategoryConfig';
 import { useS8PageConfigDriver } from './useS8PageConfigDriver';
 
@@ -232,10 +232,12 @@ const moduleData = reactive<{ list: S8ModuleOrderSummary[] }>({ list: [] });
 // ─── G-09 一期 Part D:page-config driver 接入 ─────────────────────────
 const pageConfig = useS8PageConfigDriver({ pageCode: 'SUPPLY', pageCodePrefix: 'SUPPLY' });
 
+// S8-MONITORING-PAGES-STAGE-CARD-SEMANTICS-UNIFY-1:主指标统一为「累计异常数」+ value=total。
+// S4 不再用 avgHours 作主数字;S5 不再用 closeRate% 作主数字;均时/闭环率改为底部 wideRateLabel 展示。
 const STAGE_META_FALLBACK = [
-	{ code: 'S3', title: '物料备料', icon: ShoppingBag as Component,  metricLabel: '累计异常数量' },
-	{ code: 'S4', title: '生产制造', icon: Tools as Component,        metricLabel: '平均处理时长' },
-	{ code: 'S5', title: '质量检测', icon: DataAnalysis as Component, metricLabel: '异常关闭率' },
+	{ code: 'S3', title: '物料备料', icon: ShoppingBag as Component,  metricLabel: '累计异常数' },
+	{ code: 'S4', title: '生产制造', icon: Tools as Component,        metricLabel: '累计异常数' },
+	{ code: 'S5', title: '质量检测', icon: DataAnalysis as Component, metricLabel: '累计异常数' },
 ] as const;
 
 const ANOMALY_TYPES_FALLBACK: ReadonlyArray<{ key: string; label: string }> = [
@@ -282,41 +284,44 @@ async function loadPageConfig() {
 
 const moduleMap = computed(() => new Map(moduleData.list.map((m) => [m.moduleCode, m])));
 
+// S8-MONITORING-PAGES-STAGE-CARD-SEMANTICS-UNIFY-1:与 overview 口径一致。
+// S4/S5 主数字一律改为 total(不再用 avgHours / closeRate%),均时/闭环率统一在底部 wideRateLabel 展示。
 const stageCards = computed(() =>
 	effectiveStageMeta.value.map((meta) => {
 		const m = moduleMap.value.get(meta.code);
 		const total  = Math.max(m?.total ?? 0, 0);
 		const red    = Math.max(m?.red ?? 0, 0);
 		const yellow = Math.max(m?.yellow ?? 0, 0);
-		const green  = Math.max(m?.green ?? Math.max(total - red - yellow, 0), 0);
-		const healthRate = total > 0 ? Math.round((green / total) * 100) : 0;
+		const closeRate = Math.max(0, Math.min(100, Math.round(m?.closeRate ?? 0)));
+		const avgProcessHours = m?.avgProcessHours ?? 0;
 		const tone: 'green' | 'yellow' | 'red' = red > 0 ? 'red' : yellow > 0 ? 'yellow' : 'green';
-		let value: string;
-		if (meta.code === 'S4') {
-			const h = m?.avgProcessHours ?? 0;
-			value = Number.isFinite(h) && h > 0 ? (h >= 24 ? `${Math.round(h)}h+` : `${h.toFixed(1)}h`) : '0h';
-		} else if (meta.code === 'S5') {
-			value = `${Math.max(0, Math.min(100, m?.closeRate ?? 0)).toFixed(0)}%`;
-		} else {
-			value = formatInteger(total);
-		}
+		const hasSamples = total > 0;
+		const hasAvgHours = hasSamples && Number.isFinite(avgProcessHours) && avgProcessHours > 0;
+		const closeRateText = hasSamples ? formatPercent(closeRate) : '--';
+		const avgHoursText = hasAvgHours ? formatHours(avgProcessHours) : '--';
+		const normal = DEFAULT_NORMAL_WORK_ORDER_COUNT;
+		const denom = normal + yellow + red;
 		const raw = {
 			code: meta.code,
 			title: meta.title,
 			icon: meta.icon,
 			metricLabel: meta.metricLabel,
-			value,
+			value: formatInteger(total),
 			// S8-DASHBOARD-MODULE-TRICOLOR-SEMANTICS-1:三色数字(绿正常 / 黄关注 / 红严重)
 			values: buildModuleTriColorStats({ total: red + yellow, red, yellow }),
-			healthRate,
+			healthRate: closeRate,
+			healthRateText: hasSamples ? undefined : '--',
 			statusLabel: `${red + yellow} 异常`,
-			progress: total > 0
-				? { green: (green / total) * 100, yellow: (yellow / total) * 100, red: (red / total) * 100 }
+			progress: denom > 0
+				? { green: (normal / denom) * 100, yellow: (yellow / denom) * 100, red: (red / denom) * 100 }
 				: { green: 0, yellow: 0, red: 0 },
 			tone,
 			wide: false,
 			cardSpan: 1,
 			minHeight: 176,
+			wideRate: closeRate,
+			wideRateLabel: `闭环率 ${closeRateText} / 均时 ${avgHoursText}`,
+			avgProcessHours,
 		};
 		return applyConfig(raw);
 	}),