From 9460a8492a0d95552068f68bf905cc89639dcd4b Mon Sep 17 00:00:00 2001 From: duxingchen Date: Wed, 12 Aug 2026 14:33:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9C=A8=E5=88=B6=E5=93=81=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E4=BF=A1=E6=81=AF=E5=B1=82=E7=BA=A7=E9=87=8D=E6=9E=84?= =?UTF-8?q?=20+=20SLA=E9=A2=84=E8=AD=A6=E8=89=B2=20+=20=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 信息层级重构: 主信息: [状态Tag] 任务名 | 负责人: 张三 ⏰3h 附加信息: 物料名 SN:25022 ...A1B2C3 08-12 10:00 新增后端 material_name 字段, 前端卡片式布局 2. SLA滞留预警色: <12h → 绿色(正常) | 12-24h → 橙色(警告) | >24h → 红色加粗(危险) 3. 交互: 点击卡片新窗口打开对应产品扫码详情页 --- backend/app/services/dashboard_service.py | 8 ++- frontend/src/pages/admin/AdminDashboard.tsx | 66 +++++++++++++-------- frontend/src/services/dashboardApi.ts | 1 + 3 files changed, 46 insertions(+), 29 deletions(-) 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;