diff --git a/frontend/src/pages/admin/AdminTasksPage.tsx b/frontend/src/pages/admin/AdminTasksPage.tsx index 961aa33..01e2f1c 100644 --- a/frontend/src/pages/admin/AdminTasksPage.tsx +++ b/frontend/src/pages/admin/AdminTasksPage.tsx @@ -47,8 +47,8 @@ export default function AdminTasksPage() { // 手风琴展开状态 const [expandedOrders, setExpandedOrders] = useState>(new Set()); - // 任务树展开 - const [expandedProducts, setExpandedProducts] = useState>(new Set()); + // 🔧 排他展开:同时只展开一个产品的流转树 + const [activeTreeProductId, setActiveTreeProductId] = useState(null); const [taskTrees, setTaskTrees] = useState>({}); const [treeLoading, setTreeLoading] = useState>({}); @@ -121,38 +121,22 @@ export default function AdminTasksPage() { }); } - // ---- 任务树展开/收起 ---- + // ---- 排他展开(手风琴模式) ---- async function toggleProductTree(serialNumber: string) { - setExpandedProducts((prev) => { - const next = new Set(prev); - if (next.has(serialNumber)) { - next.delete(serialNumber); - return next; - } - // 加载任务树 - next.add(serialNumber); - if (!taskTrees[serialNumber]) { - setTreeLoading((s) => ({ ...s, [serialNumber]: true })); - scanProduct(serialNumber) - .then((result) => { - setTaskTrees((s) => ({ ...s, [serialNumber]: result })); - }) - .catch((err: any) => { - const msg = - err?.response?.data?.detail ?? err?.message ?? "加载任务树失败"; - toast(msg, "error"); - setExpandedProducts((prev2) => { - const n2 = new Set(prev2); - n2.delete(serialNumber); - return n2; - }); - }) - .finally(() => { - setTreeLoading((s) => ({ ...s, [serialNumber]: false })); - }); - } - return next; - }); + // 点击已展开的树 → 收起 + if (activeTreeProductId === serialNumber) { + setActiveTreeProductId(null); + return; + } + // 展开新的 → 自动收起旧的 + setActiveTreeProductId(serialNumber); + if (!taskTrees[serialNumber]) { + setTreeLoading((s) => ({ ...s, [serialNumber]: true })); + scanProduct(serialNumber) + .then((result) => setTaskTrees((s) => ({ ...s, [serialNumber]: result }))) + .catch((err: any) => toast(err?.response?.data?.detail ?? err?.message ?? "加载任务树失败", "error")) + .finally(() => setTreeLoading((s) => ({ ...s, [serialNumber]: false }))); + } } // ---- 任务操作 ---- @@ -378,7 +362,7 @@ export default function AdminTasksPage() { {/* 产品行 */} {group.products.map((p) => { - const productExpanded = expandedProducts.has(p.serial_number); + const productExpanded = activeTreeProductId === p.serial_number; const isTreeLoading = treeLoading[p.serial_number]; const tree = taskTrees[p.serial_number]; // 综合状态:优先流转树状态,兜底产品状态