import { create } from 'zustand'; import api, { mesApi } from '../api/client'; import type { OverviewData, EquipmentItem, ProgressItem, AlarmItem, TrendItem, SamplingOverview, SamplingTrendItem, InspectionOverview, InspectionTrendItem, DevStatus, CncDisplay, WasherPoint, CncHistoryPoint, CncOeeItem, WasherOeeItem, } from '../types'; // MES /api/internal/dashboard/overview 响应结构 interface MesOverviewReply { activeOrderCount: number; activeJobCount: number; completedToday: number; scrappedToday: number; stationFaultCount: number; } /** MES 返回(camelCase,包在 Body.data 里)→ 看板 OverviewData(snake_case) */ function mapMesOverview(body: { data?: MesOverviewReply } | MesOverviewReply): OverviewData { const d = (body as any)?.data ?? body; return { active_work_orders: d.activeOrderCount ?? 0, completed_today: d.completedToday ?? 0, yield_rate: d.completedToday + d.scrappedToday > 0 ? Math.round((d.completedToday / (d.completedToday + d.scrappedToday)) * 1000) / 10 : 100, equipment_util_rate: d.activeJobCount ?? 0, active_alarms: d.stationFaultCount ?? 0, }; } async function fetchOverview(): Promise { try { const r = await mesApi.get('/dashboard/overview'); return mapMesOverview(r.data); } catch { // 回退旧接口(保留兼容) return api.get('/overview').then(r => r.data); } } // MES 内部 API 优先,失败回退旧接口(本地兜底服务 :3001) async function fromMes(path: string, fallbackPath?: string): Promise { try { const r = await mesApi.get(`/dashboard/${path}`); return r.data as T; } catch { if (!fallbackPath) return null; try { const r = await api.get(fallbackPath); return r.data as T; } catch { return null; } } } interface DashboardState { // Page 1: Overview overview: OverviewData | null; equipment: EquipmentItem[]; progress: ProgressItem[]; alarms: AlarmItem[]; trends: TrendItem[]; samplingOverview: SamplingOverview | null; samplingTrends: SamplingTrendItem[]; inspectionOverview: InspectionOverview | null; inspectionTrends: InspectionTrendItem[]; loading: boolean; // Page 2: Machine Monitor devStatus: DevStatus[]; cncLatest: CncDisplay[]; washerLatest: WasherPoint[]; cncHistory: CncHistoryPoint[]; cncOee: CncOeeItem[]; washerOee: WasherOeeItem[]; monitorLoading: boolean; fetchAll: () => Promise; refresh: () => Promise; fetchMonitor: () => Promise; refreshMonitor: () => Promise; } export const useStore = create((set) => ({ // Page 1 state overview: null, equipment: [], progress: [], alarms: [], trends: [], samplingOverview: null, samplingTrends: [], inspectionOverview: null, inspectionTrends: [], loading: false, // Page 2 state devStatus: [], cncLatest: [], washerLatest: [], cncHistory: [], cncOee: [], washerOee: [], monitorLoading: false, fetchAll: async () => { set({ loading: true }); try { const [overview, equipment, progress, alarms, trends, cncLatest, samplingOverview, samplingTrends, inspectionOverview, inspectionTrends] = await Promise.all([ fetchOverview(), fromMes('equipment', '/equipment-status').then(v => v ?? []), fromMes('progress', '/production-progress').then(v => v ?? []), fromMes('alarms', '/alarms').then(v => v ?? []), fromMes('trends', '/trends').then(v => v ?? []), api.get('/cnc-latest').then(r => r.data).catch(() => []), api.get('/sampling-overview').then(r => r.data).catch(() => null), api.get('/sampling-trends').then(r => r.data).catch(() => []), api.get('/inspection-overview').then(r => r.data).catch(() => null), api.get('/inspection-trends').then(r => r.data).catch(() => []), ]); set({ overview, equipment, progress, alarms, trends, cncLatest, samplingOverview, samplingTrends, inspectionOverview, inspectionTrends, loading: false }); } catch { set({ loading: false }); } }, refresh: async () => { try { const [overview, equipment, alarms, trends, cncLatest, samplingOverview, inspectionOverview] = await Promise.all([ fetchOverview(), fromMes('equipment', '/equipment-status'), fromMes('alarms', '/alarms'), fromMes('trends', '/trends'), api.get('/cnc-latest').then(r => r.data).catch(() => undefined), api.get('/sampling-overview').then(r => r.data).catch(() => undefined), api.get('/inspection-overview').then(r => r.data).catch(() => undefined), ]); set((prev) => ({ overview, ...(equipment !== null ? { equipment } : {}), ...(alarms !== null ? { alarms } : {}), ...(trends !== null ? { trends } : {}), ...(cncLatest !== undefined ? { cncLatest } : {}), ...(samplingOverview !== undefined ? { samplingOverview } : {}), ...(inspectionOverview !== undefined ? { inspectionOverview } : {}), })); } catch { /* ignore */ } }, fetchMonitor: async () => { set({ monitorLoading: true }); try { const [devStatus, cncLatest, washerLatest, cncOee, washerOee] = await Promise.all([ api.get('/device-status').then(r => r.data), api.get('/cnc-latest').then(r => r.data), api.get('/washer-latest').then(r => r.data), api.get('/cnc-oee').then(r => r.data), api.get('/washer-oee').then(r => r.data), ]); set({ devStatus, cncLatest, washerLatest, cncOee, washerOee, monitorLoading: false }); } catch { set({ monitorLoading: false }); } }, refreshMonitor: async () => { try { const [devStatus, cncLatest, washerLatest, cncOee, washerOee] = await Promise.all([ api.get('/device-status').then(r => r.data), api.get('/cnc-latest').then(r => r.data), api.get('/washer-latest').then(r => r.data), api.get('/cnc-oee').then(r => r.data), api.get('/washer-oee').then(r => r.data), ]); set({ devStatus, cncLatest, washerLatest, cncOee, washerOee }); } catch { /* ignore */ } }, }));