diff --git a/frontend/src/mid-section-defect/App.vue b/frontend/src/mid-section-defect/App.vue index ed9e0bf..1c0eeb7 100644 --- a/frontend/src/mid-section-defect/App.vue +++ b/frontend/src/mid-section-defect/App.vue @@ -10,7 +10,9 @@ import KpiCards from './components/KpiCards.vue'; import MultiSelect from './components/MultiSelect.vue'; import ParetoChart from './components/ParetoChart.vue'; import TrendChart from './components/TrendChart.vue'; +import AnalysisSummary from './components/AnalysisSummary.vue'; import DetailTable from './components/DetailTable.vue'; +import SuspectContextPanel from './components/SuspectContextPanel.vue'; ensureMesApiAvailable(); @@ -170,6 +172,12 @@ const filteredByMachineData = computed(() => { return filtered.length > 0 ? buildMachineChartFromAttribution(filtered) : []; }); +const suspectMachineNames = computed(() => { + const data = filteredByMachineData.value; + if (!Array.isArray(data)) return []; + return data.filter((d) => d.name && d.name !== '其他').map((d) => d.name); +}); + const isForward = computed(() => committedFilters.value.direction === 'forward'); const committedStation = computed(() => { const key = committedFilters.value.station || '測試'; @@ -198,7 +206,25 @@ const eventsAggregation = computed(() => trace.stage_results.events?.aggregation const showAnalysisSkeleton = computed(() => hasQueried.value && loading.querying && !eventsAggregation.value); const showAnalysisCharts = computed(() => hasQueried.value && (Boolean(eventsAggregation.value) || restoredFromCache.value)); -const skeletonChartCount = computed(() => (isForward.value ? 4 : 6)); +const skeletonChartCount = computed(() => (isForward.value ? 4 : 5)); + +const totalAncestorCount = computed(() => trace.stage_results.lineage?.total_ancestor_count || analysisData.value?.total_ancestor_count || 0); + +const summaryQueryParams = computed(() => { + const snap = committedFilters.value; + const params = { + queryMode: snap.queryMode || 'date_range', + startDate: snap.startDate, + endDate: snap.endDate, + lossReasons: snap.lossReasons || [], + }; + if (snap.queryMode === 'container') { + params.containerInputType = snap.containerInputType || 'lot'; + params.resolvedCount = resolutionInfo.value?.resolved_count || 0; + params.notFoundCount = resolutionInfo.value?.not_found?.length || 0; + } + return params; +}); function emptyAnalysisData() { return { @@ -364,6 +390,7 @@ async function loadAnalysis() { analysisData.value = { ...analysisData.value, ...eventsAggregation.value, + total_ancestor_count: trace.stage_results.lineage?.total_ancestor_count || 0, }; } @@ -429,6 +456,21 @@ function exportCsv() { document.body.removeChild(link); } +// Suspect context panel state +const suspectPanelMachine = ref(null); + +function handleMachineBarClick({ name, dataIndex }) { + if (!name || name === '其他') return; + const attribution = analysisData.value?.attribution; + if (!Array.isArray(attribution)) return; + const match = attribution.find( + (rec) => rec.EQUIPMENT_NAME === name, + ); + if (match) { + suspectPanelMachine.value = suspectPanelMachine.value?.EQUIPMENT_NAME === name ? null : match; + } +} + const _abortControllers = new Map(); function createAbortSignal(key = 'default') { const prev = _abortControllers.get(key); @@ -542,6 +584,14 @@ void initPage();
+ +