From 32c234fea192f12f4e455c64643c24709035edb9 Mon Sep 17 00:00:00 2001 From: duxingchen Date: Wed, 12 Aug 2026 13:59:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=9C=A8=E5=88=B6=E5=93=81=E6=8C=89?= =?UTF-8?q?=E6=BB=9E=E7=95=99=E6=97=B6=E9=97=B4=E9=99=8D=E5=BA=8F=20+=20?= =?UTF-8?q?=E7=95=99=E8=A8=80=E6=8A=BD=E5=B1=89UI=E7=BE=8E=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在制品排序: nulls_first → nulls_last 已接收任务按receipt_at升序(最早=滞留最久)排最前 未接收任务(null)排最后 2. 留言抽屉UI重设计: - 卡片式布局: 彩色头像圆+边框阴影+hover效果 - 头像颜色按姓名首字自动分配7色 - Input.Search替代普通Input+Search图标 - 灰色背景区分卡片, 时间格式改为YYYY-MM-DD HH:mm - 手动分页按钮替代Antd分页器 --- backend/app/services/dashboard_service.py | 2 +- frontend/src/pages/admin/AdminDashboard.tsx | 119 ++++++++++++-------- 2 files changed, 73 insertions(+), 48 deletions(-) diff --git a/backend/app/services/dashboard_service.py b/backend/app/services/dashboard_service.py index 0581912..1766d85 100644 --- a/backend/app/services/dashboard_service.py +++ b/backend/app/services/dashboard_service.py @@ -131,7 +131,7 @@ async def get_wip_tasks(db: AsyncSession, limit: int = 20) -> list[WipTask]: select(Task, Product.serial_number) .join(Product, Task.product_id == Product.id) .where(Task.status.in_([TASK_STATUS_PENDING, TASK_STATUS_WIP])) - .order_by(Task.received_at.asc().nulls_first(), Task.created_at.asc()) + .order_by(Task.received_at.asc().nulls_last(), Task.created_at.asc()) .limit(limit) ) result = await db.execute(stmt) diff --git a/frontend/src/pages/admin/AdminDashboard.tsx b/frontend/src/pages/admin/AdminDashboard.tsx index 8c0b69f..26f4843 100644 --- a/frontend/src/pages/admin/AdminDashboard.tsx +++ b/frontend/src/pages/admin/AdminDashboard.tsx @@ -1,10 +1,10 @@ import { useEffect, useState, useCallback } from "react"; import { Package, ClipboardList, Bell, TrendingUp, AlertTriangle, - RefreshCw, Loader2, AlertCircle, ArrowRight, Clock, MessageCircle, Search, + RefreshCw, Loader2, AlertCircle, ArrowRight, Clock, MessageCircle, } from "lucide-react"; import { useNavigate } from "react-router-dom"; -import { Radio, DatePicker, Drawer, Input, List, Tag } from "antd"; +import { Radio, DatePicker, Drawer, Input, Tag } from "antd"; import dayjs, { type Dayjs } from "dayjs"; import { fetchDashboardStats, fetchWipTasks, fetchDashboardMessages, @@ -102,23 +102,38 @@ function WipRow({ t }: { t: WipTask }) { ); } -// ─── 留言列表项 ─────────────────────────────────────────── +// ─── 留言列表项(卡片式) ───────────────────────────────── +const AVATAR_COLORS = ["#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#ef4444", "#06b6d4"]; function MsgRow({ m }: { m: ProductMessageItem }) { - const t = m.created_at ? dayjs(m.created_at).format("MM-DD HH:mm") : ""; + const t = m.created_at ? dayjs(m.created_at).format("YYYY-MM-DD HH:mm") : ""; + const initial = (m.operator_name || "?").charAt(0); + const color = AVATAR_COLORS[initial.charCodeAt(0) % AVATAR_COLORS.length]; return ( - -
-
- {m.operator_name} - {t} +
+
+ {/* 头像 */} +
+ {initial}
-
{m.content}
-
- {m.product_sn} - {m.material_name} + {/* 内容 */} +
+
+ {m.operator_name} + {t} +
+

{m.content}

+
+ {m.product_sn} + {m.material_name && ( + {m.material_name} + )} +
- +
); } @@ -372,43 +387,53 @@ export default function AdminDashboard() { {/* ═══ 留言抽屉 ═══ */} 💬 协同留言板 全厂 · {msgTotal} 条} open={msgDrawerOpen} onClose={() => setMsgDrawerOpen(false)} - width={520} - styles={{ body: { padding: 0 } }} + width={560} + styles={{ body: { padding: 16, background: "#f8fafc" } }} > -
- } - placeholder="搜索 SN码 / 物料名称 / 留言人 / 内容" - value={msgKeyword} - onChange={e => setMsgKeyword(e.target.value)} - onPressEnter={() => onMsgSearch(msgKeyword)} - allowClear - onClear={() => onMsgSearch("")} - /> -
- } - pagination={{ - total: msgTotal, - pageSize: 30, - size: "small", - onChange: (page, size) => { - loadMessages(msgKeyword); - // simplified: re-fetch with skip - fetchDashboardMessages(msgKeyword, (page - 1) * size, size) - .then(res => { setMsgData(res.items); setMsgTotal(res.total); }) - .catch(() => {}); - }, - showTotal: (t) => `共 ${t} 条`, - }} + setMsgKeyword(e.target.value)} + onSearch={onMsgSearch} + allowClear + onClear={() => onMsgSearch("")} + enterButton + className="mb-4" /> + {msgLoading ? ( +
+ +
+ ) : msgData.length === 0 ? ( +
+ {msgKeyword ? "未找到匹配的留言" : "暂无留言记录"} +
+ ) : ( +
+ {msgData.map(m => )} + {/* 简易分页 */} + {msgTotal > 30 && ( +
+ {Array.from({ length: Math.ceil(msgTotal / 30) }, (_, i) => ( + + ))} +
+ )} +
+ )}
);