From 0ed69ce3260411e95749a9c13689104a6ccd9f2f Mon Sep 17 00:00:00 2001 From: egg Date: Thu, 12 Feb 2026 11:46:37 +0800 Subject: [PATCH] fix(lot-detail): use actual data update time and add LF/wafer description fields Hold Detail "Last Update" now reads dataUpdateDate from the API response instead of using browser-local page load time. Lot Detail panels in both WIP Detail and Resource Status tooltip now show LEADFRAMEDESC and WAFERDESC from DWH.DW_MES_LOT_V, with multi-row values joined by ", ". Co-Authored-By: Claude Opus 4.6 --- frontend/src/hold-detail/App.vue | 5 +++-- .../components/FloatingTooltip.vue | 8 ++++++++ .../wip-detail/components/LotDetailPanel.vue | 2 +- src/mes_dashboard/services/wip_service.py | 20 ++++++++++++++++++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/frontend/src/hold-detail/App.vue b/frontend/src/hold-detail/App.vue index 5e7d920..84e5239 100644 --- a/frontend/src/hold-detail/App.vue +++ b/frontend/src/hold-detail/App.vue @@ -36,7 +36,9 @@ const refreshing = ref(false); const lotsLoading = ref(false); const lotsError = ref(''); const loadError = ref(''); -const lastUpdate = ref(''); +const lastUpdate = computed(() => { + return summary.value?.dataUpdateDate ? `Last Update: ${summary.value.dataUpdateDate}` : ''; +}); function unwrapApiResult(result, fallbackMessage) { if (result?.success) { @@ -216,7 +218,6 @@ async function loadAllData(showOverlay = true) { totalPages: Number(lotsData?.pagination?.totalPages || 1), }; - lastUpdate.value = `Last Update: ${new Date().toLocaleString('zh-TW')}`; } catch (error) { if (error?.name === 'AbortError') { return; diff --git a/frontend/src/resource-status/components/FloatingTooltip.vue b/frontend/src/resource-status/components/FloatingTooltip.vue index f3fbea8..9902ed4 100644 --- a/frontend/src/resource-status/components/FloatingTooltip.vue +++ b/frontend/src/resource-status/components/FloatingTooltip.vue @@ -275,10 +275,18 @@ onBeforeUnmount(() => { Wafer P/N {{ lotDetailValue(getLotDetail(lot.RUNCARDLOTID), 'waferPn') }} +
+ Wafer Description + {{ lotDetailValue(getLotDetail(lot.RUNCARDLOTID), 'waferDesc') }} +
Leadframe {{ lotDetailValue(getLotDetail(lot.RUNCARDLOTID), 'leadframeName') }}
+
+ LF Description + {{ lotDetailValue(getLotDetail(lot.RUNCARDLOTID), 'leadframeDesc') }} +
Compound {{ lotDetailValue(getLotDetail(lot.RUNCARDLOTID), 'compoundName') }} diff --git a/frontend/src/wip-detail/components/LotDetailPanel.vue b/frontend/src/wip-detail/components/LotDetailPanel.vue index 5e02649..8883d56 100644 --- a/frontend/src/wip-detail/components/LotDetailPanel.vue +++ b/frontend/src/wip-detail/components/LotDetailPanel.vue @@ -103,7 +103,7 @@ function hasHoldSection() { const basicFields = ['lotId', 'workorder', 'wipStatus', 'status', 'qty', 'qty2', 'ageByDays', 'priority']; const productFields = ['product', 'productLine', 'packageLef', 'pjType', 'pjFunction', 'bop', 'dateCode', 'produceRegion']; const processFields = ['workcenterGroup', 'workcenter', 'spec', 'specSequence', 'workflow', 'equipment', 'equipmentCount', 'location']; -const materialFields = ['waferLotId', 'waferPn', 'waferLotPrefix', 'leadframeName', 'leadframeOption', 'compoundName', 'dieConsumption', 'uts']; +const materialFields = ['waferLotId', 'waferPn', 'waferLotPrefix', 'waferDesc', 'leadframeName', 'leadframeOption', 'leadframeDesc', 'compoundName', 'dieConsumption', 'uts']; const holdFields = ['holdReason', 'holdCount', 'holdEmp', 'holdDept', 'holdComment', 'releaseTime', 'releaseEmp', 'releaseComment']; const ncrFields = ['ncrId', 'ncrDate']; const commentFields = ['comment', 'commentDate', 'commentEmp', 'futureHoldComment']; diff --git a/src/mes_dashboard/services/wip_service.py b/src/mes_dashboard/services/wip_service.py index 4d4dbff..c75a84c 100644 --- a/src/mes_dashboard/services/wip_service.py +++ b/src/mes_dashboard/services/wip_service.py @@ -3036,6 +3036,8 @@ LOT_DETAIL_FIELD_LABELS = { 'priority': 'Work Order Priority', 'tmttRemaining': 'TMTT Remaining', 'dieConsumption': 'Die Consumption Qty', + 'leadframeDesc': 'LF Description', + 'waferDesc': 'Wafer Description', 'wipStatus': 'WIP Status', 'dataUpdateDate': 'Data Update Date' } @@ -3062,6 +3064,12 @@ def get_lot_detail(lotid: str) -> Optional[Dict[str, Any]]: return None row = df.iloc[0] + if len(df) > 1: + row = row.copy() + for col in ('LEADFRAMEDESC', 'WAFERDESC'): + if col in df.columns: + vals = df[col].dropna().unique() + row[col] = ', '.join(str(v) for v in vals) if len(vals) > 0 else None return _build_lot_detail_response(row) except (DatabasePoolExhaustedError, DatabaseCircuitOpenError): raise @@ -3127,7 +3135,9 @@ def _get_lot_detail_from_oracle(lotid: str) -> Optional[Dict[str, Any]]: PRIORITYCODENAME, TMTT_R, WAFER_FACTOR, - SYS_DATE + SYS_DATE, + LEADFRAMEDESC, + WAFERDESC FROM {WIP_VIEW} WHERE LOTID = :lotid """ @@ -3137,6 +3147,12 @@ def _get_lot_detail_from_oracle(lotid: str) -> Optional[Dict[str, Any]]: return None row = df.iloc[0] + if len(df) > 1: + row = row.copy() + for col in ('LEADFRAMEDESC', 'WAFERDESC'): + if col in df.columns: + vals = df[col].dropna().unique() + row[col] = ', '.join(str(v) for v in vals) if len(vals) > 0 else None return _build_lot_detail_response(row) except (DatabasePoolExhaustedError, DatabaseCircuitOpenError): raise @@ -3233,7 +3249,9 @@ def _build_lot_detail_response(row) -> Dict[str, Any]: 'dateCode': _safe_value(safe_get('DATECODE')), 'leadframeName': _safe_value(safe_get('LEADFRAMENAME')), 'leadframeOption': _safe_value(safe_get('LEADFRAMEOPTION')), + 'leadframeDesc': _safe_value(safe_get('LEADFRAMEDESC')), 'compoundName': _safe_value(safe_get('COMNAME')), + 'waferDesc': _safe_value(safe_get('WAFERDESC')), 'location': _safe_value(safe_get('LOCATIONNAME')), 'ncrId': _safe_value(safe_get('EVENTNAME')), 'ncrDate': format_date('OCCURRENCEDATE'),