fix(驳回/返工): 抽屉与卡片数字口径一致,同时展示已驳回+返工任务
- 根因:卡片数字含返工任务(tasks_rework 实时快照),但抽屉只列已驳回 → 点开为空 - get_rejected_tasks 扩展返回两类(kind=rejected 按时间过滤 / kind=rework 实时不过滤) - 前端抽屉按「已驳回」「返工任务」分组展示,数字与明细一一对应
This commit is contained in:
@ -191,26 +191,42 @@ function CompletedRow({ t }: { t: CompletedTask }) {
|
||||
// ─── 被驳回明细项(卡片式) ──────────────────────────────
|
||||
function RejectedRow({ t }: { t: RejectedTask }) {
|
||||
const nav = useNavigate();
|
||||
const rejectedTime = t.rejected_at ? dayjs(t.rejected_at).format("YYYY-MM-DD HH:mm") : "";
|
||||
const timeStr = t.rejected_at ? dayjs(t.rejected_at).format("YYYY-MM-DD HH:mm") : "";
|
||||
const isRework = t.kind === "rework";
|
||||
return (
|
||||
<div
|
||||
onClick={() => t.product_sn && nav(`/admin/tasks?sn=${t.product_sn}`)}
|
||||
className="cursor-pointer rounded-lg border border-gray-100 bg-white px-4 py-3 transition-shadow hover:border-red-200 hover:shadow-md"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertTriangle className="h-4 w-4 shrink-0 text-red-500" />
|
||||
{isRework ? <RefreshCw className="h-4 w-4 shrink-0 text-orange-500" /> : <AlertTriangle className="h-4 w-4 shrink-0 text-red-500" />}
|
||||
<span className="text-sm font-semibold text-gray-800 truncate">{t.task_name}</span>
|
||||
<span className="text-xs text-gray-500">|</span>
|
||||
<span className="shrink-0 rounded-full bg-red-50 px-2 py-0.5 text-[10px] font-bold text-red-600">已驳回</span>
|
||||
<span className="text-xs text-gray-500 shrink-0">驳回人: {t.rejected_by}</span>
|
||||
<span className="ml-auto shrink-0 text-xs text-gray-400">{rejectedTime}</span>
|
||||
{isRework ? (
|
||||
<span className="shrink-0 rounded-full bg-orange-50 px-2 py-0.5 text-[10px] font-bold text-orange-600">
|
||||
{t.status === "WIP" ? "返工中" : t.status === "PENDING" ? "待返工" : "已返工"}
|
||||
</span>
|
||||
) : (
|
||||
<span className="shrink-0 rounded-full bg-red-50 px-2 py-0.5 text-[10px] font-bold text-red-600">已驳回</span>
|
||||
)}
|
||||
<span className="text-xs text-gray-500 shrink-0">
|
||||
{isRework ? `返工人: ${t.rework_assignee}` : `驳回人: ${t.rejected_by}`}
|
||||
</span>
|
||||
<span className="ml-auto shrink-0 text-xs text-gray-400">{timeStr}</span>
|
||||
</div>
|
||||
<div className="mt-1.5 flex items-center gap-1.5 text-[11px] text-gray-400">
|
||||
<span className="inline-flex items-center gap-1 rounded bg-orange-50 px-1.5 py-0.5 font-medium text-orange-600">
|
||||
🔁 返工给: {t.rework_assignee}
|
||||
</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span className="truncate text-red-500/80" title={t.reject_reason || ""}>原因: {t.reject_reason || "—"}</span>
|
||||
{!isRework && (
|
||||
<>
|
||||
<span className="inline-flex items-center gap-1 rounded bg-orange-50 px-1.5 py-0.5 font-medium text-orange-600">
|
||||
🔁 返工给: {t.rework_assignee}
|
||||
</span>
|
||||
<span className="text-gray-300">|</span>
|
||||
<span className="truncate text-red-500/80" title={t.reject_reason || ""}>原因: {t.reject_reason || "—"}</span>
|
||||
</>
|
||||
)}
|
||||
{isRework && (
|
||||
<span className="truncate text-orange-600/80">🔁 返工任务 · {t.rework_assignee} 负责</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1.5 flex items-center gap-1.5 text-[11px] text-gray-400">
|
||||
<span className="font-medium text-gray-500">{t.material_name || "未知设备"}</span>
|
||||
@ -588,7 +604,7 @@ export default function AdminDashboard() {
|
||||
|
||||
{/* ═══ 驳回/返工明细抽屉 ═══ */}
|
||||
<Drawer
|
||||
title={<span className="text-base font-bold">🔴 驳回/返工明细 <span className="font-normal text-gray-400">被驳回 {rejectedTasks.length} 条</span></span>}
|
||||
title={<span className="text-base font-bold">🔴 驳回/返工明细 <span className="font-normal text-gray-400">共 {rejectedTasks.length} 条</span></span>}
|
||||
open={rejectedDrawerOpen}
|
||||
onClose={() => setRejectedDrawerOpen(false)}
|
||||
size="large"
|
||||
@ -600,11 +616,34 @@ export default function AdminDashboard() {
|
||||
</div>
|
||||
) : rejectedTasks.length === 0 ? (
|
||||
<div className="py-16 text-center text-sm text-gray-400">
|
||||
该时段暂无驳回记录
|
||||
该时段暂无驳回/返工记录
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
{rejectedTasks.map(t => <RejectedRow key={t.task_id} t={t} />)}
|
||||
<div className="space-y-4">
|
||||
{(() => {
|
||||
const rejected = rejectedTasks.filter(t => t.kind === "rejected");
|
||||
const rework = rejectedTasks.filter(t => t.kind === "rework");
|
||||
return (
|
||||
<>
|
||||
{rejected.length > 0 && (
|
||||
<div>
|
||||
<h4 className="mb-2 flex items-center gap-1.5 text-xs font-bold text-red-600">
|
||||
<AlertTriangle className="h-3.5 w-3.5" />已驳回 · {rejected.length} 条
|
||||
</h4>
|
||||
{rejected.map(t => <RejectedRow key={t.task_id} t={t} />)}
|
||||
</div>
|
||||
)}
|
||||
{rework.length > 0 && (
|
||||
<div>
|
||||
<h4 className="mb-2 flex items-center gap-1.5 text-xs font-bold text-orange-600">
|
||||
<RefreshCw className="h-3.5 w-3.5" />返工任务 · {rework.length} 条
|
||||
</h4>
|
||||
{rework.map(t => <RejectedRow key={t.task_id} t={t} />)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
</Drawer>
|
||||
|
||||
@ -41,6 +41,7 @@ export interface CompletedTask {
|
||||
|
||||
export interface RejectedTask {
|
||||
task_id: string;
|
||||
kind: "rejected" | "rework";
|
||||
task_name: string;
|
||||
product_sn: string;
|
||||
external_serial: string | null;
|
||||
@ -49,7 +50,8 @@ export interface RejectedTask {
|
||||
rejected_by: string;
|
||||
rework_assignee: string;
|
||||
reject_reason: string | null;
|
||||
rejected_at: string;
|
||||
status: string | null;
|
||||
rejected_at: string | null;
|
||||
}
|
||||
|
||||
export interface PersonDevice {
|
||||
|
||||
Reference in New Issue
Block a user