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 ( -
- - {t.status === "WIP" ? "进行中" : "待接收"} - -
-
- {t.task_name} - - {t.external_serial || t.product_sn} - - {t.assignee} -
-
- {t.received_at && 接收: {t.received_at}} -
+
+ {/* 主信息行:状态 + 任务名 + 负责人 */} +
+ + {t.status === "WIP" ? "进行中" : "待接收"} + + {t.task_name} + | + 负责人: {t.assignee} + {/* 滞留时间 */} + + + {durationLabel(t.duration_hours)} + +
+ {/* 附加信息行:物料名 + 序列号 + 身份证后6位 + 接收时间 */} +
+ {t.material_name && {t.material_name}} + SN: {sn} + …{trace} + {t.received_at && {t.received_at}}
- - - {durationLabel(t.duration_hours)} -
); } diff --git a/frontend/src/services/dashboardApi.ts b/frontend/src/services/dashboardApi.ts index d2769b6..cb4398f 100644 --- a/frontend/src/services/dashboardApi.ts +++ b/frontend/src/services/dashboardApi.ts @@ -21,6 +21,7 @@ export interface WipTask { assignee: string; product_sn: string; external_serial: string | null; + material_name: string; status: string; received_at: string; duration_hours: number;