diff --git a/backend/app/services/dashboard_service.py b/backend/app/services/dashboard_service.py index 703bfa4..852491c 100644 --- a/backend/app/services/dashboard_service.py +++ b/backend/app/services/dashboard_service.py @@ -30,6 +30,7 @@ class WipTask(BaseModel): assignee: str product_sn: str external_serial: str | None # 业务序列号 + material_name: str # 物料名称 status: str received_at: str duration_hours: float @@ -132,10 +133,10 @@ async def get_wip_tasks(db: AsyncSession, limit: int = 20) -> list[WipTask]: from app.core.time_utils import get_beijing_time, BEIJING_TZ stmt = ( - select(Task, Product.serial_number, Product.external_serial) + select(Task, Product.serial_number, Product.external_serial, Product.material_name) .join(Product, Task.product_id == Product.id) .where(Task.status.in_([TASK_STATUS_PENDING, TASK_STATUS_WIP])) - .limit(limit * 2) # 多取一些,后面 Python 统一排序 + .limit(limit * 2) ) result = await db.execute(stmt) rows = result.all() @@ -148,7 +149,7 @@ async def get_wip_tasks(db: AsyncSession, limit: int = 20) -> list[WipTask]: now = get_beijing_time() wip_list: list[WipTask] = [] - for task, product_sn, ext_sn in rows: + for task, product_sn, ext_sn, mat_name in rows: start = task.received_at or task.created_at if start: if start.tzinfo is None: @@ -167,6 +168,7 @@ async def get_wip_tasks(db: AsyncSession, limit: int = 20) -> list[WipTask]: assignee=name_map.get(task.assignee_id or "", task.assignee_id or "未分配"), product_sn=product_sn or "", external_serial=ext_sn or None, + material_name=mat_name or "", status=task.status, received_at=recv_str, duration_hours=hours, diff --git a/frontend/src/pages/admin/AdminDashboard.tsx b/frontend/src/pages/admin/AdminDashboard.tsx index e5b6cb5..a28c233 100644 --- a/frontend/src/pages/admin/AdminDashboard.tsx +++ b/frontend/src/pages/admin/AdminDashboard.tsx @@ -64,12 +64,11 @@ function ProgressBar({ a, b, c, total, labels, d }: { ); } -// ─── 滞留时间 ───────────────────────────────────────────── +// ─── 滞留时间 SLA 预警颜色 ──────────────────────────────── function durationColor(h: number) { - if (h >= 48) return "text-red-600 bg-red-50"; - if (h >= 24) return "text-orange-600 bg-orange-50"; - if (h >= 8) return "text-amber-600 bg-amber-50"; - return "text-gray-500 bg-gray-50"; + if (h >= 24) return "text-red-600 bg-red-100 font-bold"; // 超24h 危险 + if (h >= 12) return "text-orange-600 bg-orange-50"; // 12-24h 警告 + return "text-emerald-600 bg-emerald-50"; // <12h 正常 } function durationLabel(h: number) { if (h >= 48) return `${Math.round(h / 24)}天`; @@ -79,29 +78,44 @@ function durationLabel(h: number) { } function WipRow({ t }: { t: WipTask }) { + const nav = useNavigate(); + const handleClick = () => { + if (t.product_sn) { + // 跳转到扫码详情页 + const base = window.location.origin; + window.open(`${base}/scan?sn=${t.product_sn}`, "_blank"); + } + }; + const sn = t.external_serial || "未录入"; + const trace = t.product_sn ? t.product_sn.slice(-6) : ""; return ( -