|
@@ -11,6 +11,15 @@
|
|
|
<button type="button" class="s8-monitor__error-retry" @click="loadData">重试</button>
|
|
<button type="button" class="s8-monitor__error-retry" @click="loadData">重试</button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <section class="s8-monitor__result-kpi">
|
|
|
|
|
+ <S9ResultKpiCard
|
|
|
|
|
+ :items="resultKpiItems"
|
|
|
|
|
+ :source="resultKpiSource"
|
|
|
|
|
+ :loading="resultKpiState === 'loading'"
|
|
|
|
|
+ :error="resultKpiState === 'error' ? resultKpiError : null"
|
|
|
|
|
+ />
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
<S8MonitoringResizableShell
|
|
<S8MonitoringResizableShell
|
|
|
:shell="draftLayout.shell"
|
|
:shell="draftLayout.shell"
|
|
|
:editable="editMode"
|
|
:editable="editMode"
|
|
@@ -74,6 +83,7 @@ import {
|
|
|
type S8MonitoringSummary,
|
|
type S8MonitoringSummary,
|
|
|
type S8OrderGridData,
|
|
type S8OrderGridData,
|
|
|
type S8QdcSummaryItem,
|
|
type S8QdcSummaryItem,
|
|
|
|
|
+ type S8ResultKpiItem,
|
|
|
} from '../api/s8MonitoringApi';
|
|
} from '../api/s8MonitoringApi';
|
|
|
import { deepClone, useS8Layout } from './useS8Layout';
|
|
import { deepClone, useS8Layout } from './useS8Layout';
|
|
|
import type { S8LayoutSchema } from './useS8Layout';
|
|
import type { S8LayoutSchema } from './useS8Layout';
|
|
@@ -82,6 +92,7 @@ import S8MonitoringResizableShell from './components/S8MonitoringResizableShell.
|
|
|
import S8MonitoringModulesGrid, { type SummaryCardData } from './components/S8MonitoringModulesGrid.vue';
|
|
import S8MonitoringModulesGrid, { type SummaryCardData } from './components/S8MonitoringModulesGrid.vue';
|
|
|
import S8MonitoringEditToolbar from './components/S8MonitoringEditToolbar.vue';
|
|
import S8MonitoringEditToolbar from './components/S8MonitoringEditToolbar.vue';
|
|
|
import S8MonitoringStageConfigDrawer from './components/S8MonitoringStageConfigDrawer.vue';
|
|
import S8MonitoringStageConfigDrawer from './components/S8MonitoringStageConfigDrawer.vue';
|
|
|
|
|
+import S9ResultKpiCard from './components/S9ResultKpiCard.vue';
|
|
|
import { useS8StageConfig, buildModuleTriColorStats, DEFAULT_NORMAL_WORK_ORDER_COUNT, ORDER_STATUS_MOCK } from './useS8StageConfig';
|
|
import { useS8StageConfig, buildModuleTriColorStats, DEFAULT_NORMAL_WORK_ORDER_COUNT, ORDER_STATUS_MOCK } from './useS8StageConfig';
|
|
|
import { useS8CategoryConfig } from './useS8CategoryConfig';
|
|
import { useS8CategoryConfig } from './useS8CategoryConfig';
|
|
|
import { useS9DeptConfig } from './useS9DeptConfig';
|
|
import { useS9DeptConfig } from './useS9DeptConfig';
|
|
@@ -590,6 +601,26 @@ const loadState = shallowRef<'idle' | 'loading' | 'ok' | 'error'>('idle');
|
|
|
const loadError = shallowRef<string>('');
|
|
const loadError = shallowRef<string>('');
|
|
|
const qdcItems = shallowRef<S8QdcSummaryItem[]>([]);
|
|
const qdcItems = shallowRef<S8QdcSummaryItem[]>([]);
|
|
|
|
|
|
|
|
|
|
+const resultKpiItems = shallowRef<S8ResultKpiItem[]>([]);
|
|
|
|
|
+const resultKpiSource = shallowRef<string>('DICTIONARY_MOCK');
|
|
|
|
|
+const resultKpiState = shallowRef<'idle' | 'loading' | 'ok' | 'error'>('idle');
|
|
|
|
|
+const resultKpiError = shallowRef<string>('');
|
|
|
|
|
+
|
|
|
|
|
+async function loadResultKpi() {
|
|
|
|
|
+ resultKpiState.value = 'loading';
|
|
|
|
|
+ resultKpiError.value = '';
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data = await s8MonitoringApi.resultKpiSummary({ tenantId: 1, factoryId: 1 });
|
|
|
|
|
+ resultKpiItems.value = data.items ?? [];
|
|
|
|
|
+ resultKpiSource.value = data.source ?? 'DICTIONARY_MOCK';
|
|
|
|
|
+ resultKpiState.value = 'ok';
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error('[S8MonitoringOverviewPage] loadResultKpi failed:', err);
|
|
|
|
|
+ resultKpiState.value = 'error';
|
|
|
|
|
+ resultKpiError.value = err instanceof Error ? err.message : '加载失败';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function loadData() {
|
|
async function loadData() {
|
|
|
loadState.value = 'loading';
|
|
loadState.value = 'loading';
|
|
|
loadError.value = '';
|
|
loadError.value = '';
|
|
@@ -650,7 +681,7 @@ onMounted(async () => {
|
|
|
// initializeFromCards 因 initialized=true 跳过,导致真实 API 数据无法覆盖。
|
|
// initializeFromCards 因 initialized=true 跳过,导致真实 API 数据无法覆盖。
|
|
|
// 改用 reset(无条件用最新 cards 重建 state.items),每次进入本页都对齐 API。
|
|
// 改用 reset(无条件用最新 cards 重建 state.items),每次进入本页都对齐 API。
|
|
|
await loadPageConfig();
|
|
await loadPageConfig();
|
|
|
- await loadData();
|
|
|
|
|
|
|
+ await Promise.all([loadData(), loadResultKpi()]);
|
|
|
resetStageConfigState(stageCards.value);
|
|
resetStageConfigState(stageCards.value);
|
|
|
resetCategoryConfigState(categoryCards.value);
|
|
resetCategoryConfigState(categoryCards.value);
|
|
|
resetDeptConfigState(deptCards.value);
|
|
resetDeptConfigState(deptCards.value);
|
|
@@ -737,6 +768,10 @@ watch(deptCards, (depts) => {
|
|
|
pointer-events: none;
|
|
pointer-events: none;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.s8-monitor__result-kpi {
|
|
|
|
|
+ margin: 44px 0 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.s8-monitor__period-filter {
|
|
.s8-monitor__period-filter {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
top: 10px;
|
|
top: 10px;
|