feat: 任务全景表格新增"最新动态"列

后端:
  - ProductResponse 新增 latest_record_time/content 字段
  - get_all_products 批量查询活跃任务(WIP/PENDING)的最新 task_records
  - 使用窗口函数 ROW_NUMBER() PARTITION BY product_id

前端:
  - AdminTasksPage: grid-cols-12→grid-cols-[14]
  - 表头+数据行新增"最新动态"列
  - Tooltip悬浮显示完整内容, 截断+灰字二级排版
This commit is contained in:
2026-08-12 15:43:33 +08:00
parent 1fdc607c07
commit 163d9c20bb
4 changed files with 53 additions and 9 deletions

View File

@ -5,6 +5,7 @@ import {
Search, Loader2, Package, ChevronDown, ChevronRight,
Warehouse, GitBranch, X,
} from "lucide-react";
import { Tooltip } from "antd";
import api from "../../services/api";
import { scanProduct } from "../../services/productApi";
import {
@ -368,14 +369,15 @@ export default function AdminTasksPage() {
{isOpen && (
<div className="border-t border-gray-100">
{/* 表头 */}
<div className="grid grid-cols-12 gap-2 bg-gray-50 px-5 py-2 text-xs font-medium text-gray-500">
<div className="grid grid-cols-[14] gap-2 bg-gray-50 px-5 py-2 text-xs font-medium text-gray-500">
<div className="col-span-2">产品身份证</div>
<div className="col-span-1">序列号</div>
<div className="col-span-2">规格型号</div>
<div className="col-span-1">规格型号</div>
<div className="col-span-1">宏观状态</div>
<div className="col-span-1">任务状态</div>
<div className="col-span-1">当前位置</div>
<div className="col-span-2">创建时间</div>
<div className="col-span-2">最新动态</div>
<div className="col-span-1">创建时间</div>
<div className="col-span-2">操作</div>
</div>
@ -391,14 +393,25 @@ export default function AdminTasksPage() {
return (
<div key={p.id}>
<div className="grid grid-cols-12 gap-2 border-t border-gray-50 px-5 py-3 items-center text-sm hover:bg-gray-50/50">
<div className="grid grid-cols-14 gap-2 border-t border-gray-50 px-5 py-3 items-center text-sm hover:bg-gray-50/50">
<div className="col-span-2 font-mono text-xs font-semibold text-gray-800 tracking-wider cursor-pointer hover:text-blue-600 underline decoration-dotted" onClick={() => setQrSerial(p.serial_number)} title="点击查看二维码">{p.serial_number}</div>
<div className="col-span-1 font-mono text-xs text-gray-600 truncate">{p.external_serial || "—"}</div>
<div className="col-span-2 text-xs text-gray-500 truncate">{p.spec_model || p.material_name || p.material_id || "—"}</div>
<div className="col-span-1 text-xs text-gray-500 truncate">{p.spec_model || p.material_name || p.material_id || "—"}</div>
<div className="col-span-1"><span className="text-xs font-medium text-gray-700">{p.overall_status || "—"}</span></div>
<div className="col-span-1"><span className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium ${statusCfg.bg} ${statusCfg.text}`}>{statusCfg.label}</span></div>
<div className="col-span-1 text-xs text-gray-500 truncate">{p.current_location_id === "virtual_warehouse" ? (<span className="inline-flex items-center gap-1 text-purple-600">🏭 仓库</span>) : (p.current_location_name || p.current_location_id || "—")}</div>
<div className="col-span-2 text-xs text-gray-400">{new Date(p.created_at).toLocaleDateString("zh-CN")}</div>
{/* 最新动态 */}
<div className="col-span-2 text-xs">
{p.latest_record_time ? (
<Tooltip title={p.latest_record_content || ""}>
<div className="cursor-default">
<div className="text-[10px] text-gray-400">{new Date(p.latest_record_time).toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" })}</div>
<div className="mt-0.5 truncate text-[11px] text-gray-600">{p.latest_record_content || "—"}</div>
</div>
</Tooltip>
) : <span className="text-gray-300">—</span>}
</div>
<div className="col-span-1 text-xs text-gray-400">{new Date(p.created_at).toLocaleDateString("zh-CN")}</div>
<div className="col-span-2">
<button
onClick={() => toggleProductTree(p.serial_number)}

View File

@ -18,6 +18,8 @@ export interface ProductResponse {
overall_status: string | null;
status: string;
created_at: string;
latest_record_time: string | null;
latest_record_content: string | null;
}
/** MOM 物料选项 */