diff --git a/backend/app/api/v1/endpoints/webhooks.py b/backend/app/api/v1/endpoints/webhooks.py index 8437c3f..41d15ee 100644 --- a/backend/app/api/v1/endpoints/webhooks.py +++ b/backend/app/api/v1/endpoints/webhooks.py @@ -176,9 +176,10 @@ async def _append_warehouse_task( product_id=product.id, parent_task_id=last_main_task.id if last_main_task else None, task_name=task_name, - assignee_id="MOM 仓储系统", + # ★ assignee_id 显式置 None:不能填非 UUID 字符串,否则前端解析头像/用户信息报错导致节点跳过渲染 + assignee_id=None, status="COMPLETED", - task_type="TRANSFER", # 现有主线枚举 → is_main=True,画在中央主干道 + task_type="WAREHOUSE", # 仓储任务类型(is_main 判断已兼容 WAREHOUSE → 画在中央主干道) completed_at=_dt.now(), remark=record_remark, ) diff --git a/backend/app/services/product_service.py b/backend/app/services/product_service.py index 11a8e87..30186df 100644 --- a/backend/app/services/product_service.py +++ b/backend/app/services/product_service.py @@ -140,7 +140,7 @@ async def get_product_by_serial(db: AsyncSession, serial_number: str) -> Product all_mains: list = [] def _collect_main(tasks): for t in tasks: - if not t.parent_task_id or t.task_type in ("TRANSFER", "RECOVERY"): + if not t.parent_task_id or t.task_type in ("TRANSFER", "RECOVERY", "WAREHOUSE"): all_mains.append(t) if t.child_tasks: _collect_main(t.child_tasks) @@ -465,7 +465,7 @@ async def update_overall_status( Task.status.in_(["WIP", "PENDING"]), or_( Task.parent_task_id.is_(None), - Task.task_type.in_(["TRANSFER", "RECOVERY"]), + Task.task_type.in_(["TRANSFER", "RECOVERY", "WAREHOUSE"]), ), ).order_by(Task.created_at.desc()).limit(1) ) @@ -657,7 +657,7 @@ async def get_all_products( Task.product_id.in_(product_ids), or_( Task.parent_task_id.is_(None), - Task.task_type.in_(["TRANSFER", "RECOVERY"]), + Task.task_type.in_(["TRANSFER", "RECOVERY", "WAREHOUSE"]), ), ) prio_expr = sa_case( diff --git a/frontend/src/components/TaskTree/TaskFlowView.tsx b/frontend/src/components/TaskTree/TaskFlowView.tsx index 5bf5b89..2c69ad8 100644 --- a/frontend/src/components/TaskTree/TaskFlowView.tsx +++ b/frontend/src/components/TaskTree/TaskFlowView.tsx @@ -3,7 +3,7 @@ */ import { memo, useMemo, useState, useEffect } from "react"; import { Image } from "antd"; -import { FileText, User, X } from "lucide-react"; +import { FileText, Package, User, X } from "lucide-react"; import type { TaskResponse } from "../../types/api"; import { TASK_STATUS } from "../../types/api"; import { getStatusConfig } from "../../constants/task"; @@ -13,7 +13,7 @@ import type { ModalTarget } from "./TaskTreeViewer"; // 工具 // ============================================================ function fmtTime(d: string | null) { if (!d) return ""; const dt = new Date(d); const pad = (n: number) => String(n).padStart(2, "0"); return `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())} ${pad(dt.getHours())}:${pad(dt.getMinutes())}`; } -function isMain(t: TaskResponse) { return !t.parent_task_id || t.task_type === "TRANSFER" || t.task_type === "RECOVERY"; } +function isMain(t: TaskResponse) { return !t.parent_task_id || t.task_type === "TRANSFER" || t.task_type === "RECOVERY" || t.task_type === "WAREHOUSE"; } function active(s: string) { return s === "WIP" || s === "PENDING"; } /** 微型右箭头 SVG */ function ArrowRight({ color = "#9ca3af", big }: { color?: string; big?: boolean }) { @@ -117,17 +117,29 @@ const SlimCard = memo(function SlimCard({ {cfg.label}

{task.task_name}

- {/* 操作人(带用户图标)+ 在库转入人 */} + {/* 操作人(带用户图标)+ 在库转入人;仓储任务防御性渲染为系统节点 */}
- {!isWarehouse && ( - - {assigneeName || task.assignee_id || "—"} - - )} - {/* 📥 在库任务:显示实际执行"转入在库"的操作人(任务负责人) */} - {isWarehouse && task.assignee_id && ( - 📥 转入在库: {assigneeName || task.assignee_id} - )} + {(() => { + const isSystemTask = task.task_name?.includes("扫码") || task.task_type === "WAREHOUSE"; + if (isSystemTask) { + // ★ 仓储系统任务:不请求用户数据,直接显示系统图标 + 固定名(assignee_id 为 null 不报错) + return ( + + MOM 仓储系统 + + ); + } + if (isWarehouse && task.assignee_id) { + return ( + 📥 转入在库: {assigneeName || task.assignee_id} + ); + } + return ( + + {assigneeName || task.assignee_id || "—"} + + ); + })()}
{/* 单行时间(精确到分钟) */}

diff --git a/track-uniapp/src/pages/scan/components/FlowCard.vue b/track-uniapp/src/pages/scan/components/FlowCard.vue index 2c02c89..96e6d6f 100644 --- a/track-uniapp/src/pages/scan/components/FlowCard.vue +++ b/track-uniapp/src/pages/scan/components/FlowCard.vue @@ -8,8 +8,10 @@ {{ node.task_name }} + + 📦 MOM 仓储系统 - 📥 转入在库: {{ formatUserName(node.assignee_id) }} + 📥 转入在库: {{ formatUserName(node.assignee_id) }} 👤 {{ formatUserName(node.assignee_id) || '未分配' }} @@ -35,7 +37,13 @@ export default { isMainNode() { return !this.node.parent_task_id || this.node.task_type === "TRANSFER" - || this.node.task_type === "RECOVERY"; + || this.node.task_type === "RECOVERY" + || this.node.task_type === "WAREHOUSE"; + }, + // ★ 仓储系统任务(扫码入库/扫码出库):防御性渲染,不请求用户数据 + isSystemTask() { + const name = String(this.node.task_name || ""); + return name.includes("扫码") || this.node.task_type === "WAREHOUSE"; }, isWarehouseTask() { const name = String(this.node.task_name || ""); diff --git a/track-uniapp/src/pages/scan/components/TreeCanvas.vue b/track-uniapp/src/pages/scan/components/TreeCanvas.vue index e699356..94b2f49 100644 --- a/track-uniapp/src/pages/scan/components/TreeCanvas.vue +++ b/track-uniapp/src/pages/scan/components/TreeCanvas.vue @@ -120,7 +120,7 @@ export default { }, methods: { isMain(t) { - return !t.parent_task_id || t.task_type === "TRANSFER" || t.task_type === "RECOVERY"; + return !t.parent_task_id || t.task_type === "TRANSFER" || t.task_type === "RECOVERY" || t.task_type === "WAREHOUSE"; }, // 当前主线的直接分支子节点(非主线) branchChildren(mainTask) {