feat(任务全景): 流转树改为宽屏弹窗并放大字体
- TaskFlowView 增加 size prop(sm默认/lg放大),弹窗模式卡片/字体/按钮全部放大 - TaskTreeViewer 通用 Modal 导出并支持 widthClass/bodyClassName 定制 - AdminTasksPage 内嵌展开改为宽屏 Modal(max-w-6xl),保留接收/转交/驳回操作 - 记录弹窗放大且 z-index 提升到 z-[60] 保证覆盖弹窗 - 修复 TaskRecordResponse 无 note 字段的类型错误,清理未使用 import
This commit is contained in:
@ -13,7 +13,7 @@ import {
|
||||
} from "../../services/taskApi";
|
||||
import { TaskFlowView } from "../../components/TaskTree/TaskFlowView";
|
||||
import type { ModalTarget } from "../../components/TaskTree/TaskTreeViewer";
|
||||
import { ReceiveConfirmModal, RejectModal, TransferModal } from "../../components/TaskTree/TaskTreeViewer";
|
||||
import { Modal, ReceiveConfirmModal, RejectModal, TransferModal } from "../../components/TaskTree/TaskTreeViewer";
|
||||
import type { ProductResponse } from "../../types/admin";
|
||||
import type { ProductScanResponse } from "../../types/api";
|
||||
import { useToast } from "../../components/ui/Toast";
|
||||
@ -174,12 +174,11 @@ export default function AdminTasksPage() {
|
||||
{
|
||||
key: "actions", label: "操作", colSpan: 2,
|
||||
render: (p) => {
|
||||
const productExpanded = activeTreeProductId === p.serial_number;
|
||||
const isTreeLoading = treeLoading[p.serial_number];
|
||||
return (
|
||||
<button onClick={() => toggleProductTree(p.serial_number)} className="flex items-center gap-1 rounded border border-blue-200 px-2.5 py-1 text-xs font-medium text-blue-600 hover:bg-blue-50 transition-colors">
|
||||
{isTreeLoading ? <Loader2 className="h-3 w-3 animate-spin" /> : productExpanded ? <X className="h-3 w-3" /> : <GitBranch className="h-3 w-3" />}
|
||||
{productExpanded ? "收起" : "流转树"}
|
||||
<button onClick={() => openTreeModal(p.serial_number)} className="flex items-center gap-1 rounded border border-blue-200 px-2.5 py-1 text-xs font-medium text-blue-600 hover:bg-blue-50 transition-colors">
|
||||
{isTreeLoading ? <Loader2 className="h-3 w-3 animate-spin" /> : <GitBranch className="h-3 w-3" />}
|
||||
流转树
|
||||
</button>
|
||||
);
|
||||
},
|
||||
@ -221,9 +220,9 @@ export default function AdminTasksPage() {
|
||||
if (autoSn) {
|
||||
setKeyword(autoSn);
|
||||
loadProducts(autoSn).then((data) => {
|
||||
// 产品加载完成后自动展开流转树
|
||||
// 产品加载完成后自动打开流转树弹窗
|
||||
const found = data.find((p: ProductResponse) => p.serial_number === autoSn);
|
||||
if (found) toggleProductTree(found.serial_number);
|
||||
if (found) openTreeModal(found.serial_number);
|
||||
});
|
||||
} else {
|
||||
loadProducts(keyword);
|
||||
@ -234,6 +233,7 @@ export default function AdminTasksPage() {
|
||||
e?.preventDefault();
|
||||
setExpandedOrders(new Set());
|
||||
setTaskTrees({});
|
||||
setActiveTreeProductId(null);
|
||||
loadProducts(keyword);
|
||||
}
|
||||
|
||||
@ -362,14 +362,8 @@ export default function AdminTasksPage() {
|
||||
});
|
||||
}
|
||||
|
||||
// ---- 排他展开(手风琴模式) ----
|
||||
async function toggleProductTree(serialNumber: string) {
|
||||
// 点击已展开的树 → 收起
|
||||
if (activeTreeProductId === serialNumber) {
|
||||
setActiveTreeProductId(null);
|
||||
return;
|
||||
}
|
||||
// 展开新的 → 自动收起旧的
|
||||
// ---- 流转树宽屏弹窗(懒加载,缓存到 taskTrees) ----
|
||||
function openTreeModal(serialNumber: string) {
|
||||
setActiveTreeProductId(serialNumber);
|
||||
if (!taskTrees[serialNumber]) {
|
||||
setTreeLoading((s) => ({ ...s, [serialNumber]: true }));
|
||||
@ -379,6 +373,9 @@ export default function AdminTasksPage() {
|
||||
.finally(() => setTreeLoading((s) => ({ ...s, [serialNumber]: false })));
|
||||
}
|
||||
}
|
||||
function closeTreeModal() {
|
||||
setActiveTreeProductId(null);
|
||||
}
|
||||
|
||||
// ---- 任务操作 ----
|
||||
const refreshProductTree = useCallback(async (serialNumber: string) => {
|
||||
@ -695,10 +692,6 @@ export default function AdminTasksPage() {
|
||||
|
||||
{/* 产品行 */}
|
||||
{group.products.map((p) => {
|
||||
const productExpanded = activeTreeProductId === p.serial_number;
|
||||
const isTreeLoading = treeLoading[p.serial_number];
|
||||
const tree = taskTrees[p.serial_number];
|
||||
|
||||
return (
|
||||
<div key={p.id}>
|
||||
<div className="grid gap-2 border-t border-gray-50 px-5 py-3 items-center text-sm hover:bg-gray-50/50" style={{ gridTemplateColumns: `repeat(${TOTAL_SPAN}, minmax(0, 1fr))` }}>
|
||||
@ -708,36 +701,6 @@ export default function AdminTasksPage() {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 展开的流转树 — 卡片堆叠视图 */}
|
||||
{productExpanded && tree && (
|
||||
<div className="border-t border-dashed border-blue-100 bg-gradient-to-b from-blue-50/40 to-white px-5 py-4">
|
||||
<h4 className="mb-3 flex items-center gap-2 text-xs font-semibold text-gray-500">
|
||||
<GitBranch className="h-3.5 w-3.5" />
|
||||
流转卡片 — {p.serial_number}
|
||||
</h4>
|
||||
{tree.task_tree && tree.task_tree.length > 0 ? (
|
||||
<TaskFlowView
|
||||
tasks={tree.task_tree}
|
||||
onAction={setModalTarget}
|
||||
currentUser={currentUser}
|
||||
assigneeNames={tree.assignee_names}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-gray-400">
|
||||
<GitBranch className="mb-3 h-10 w-10 text-gray-300" />
|
||||
<p className="text-sm font-medium text-gray-500">暂无流转记录</p>
|
||||
<p className="mt-1 text-xs text-gray-400">产品刚创建,尚未分配生产任务</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{productExpanded && isTreeLoading && (
|
||||
<div className="border-t border-dashed border-gray-100 bg-gray-50/50 px-5 py-12 text-center">
|
||||
<Loader2 className="mx-auto h-6 w-6 animate-spin text-blue-400" />
|
||||
<p className="mt-2 text-xs text-gray-400">加载流转树...</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@ -762,6 +725,37 @@ export default function AdminTasksPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 🔧 流转树宽屏弹窗 */}
|
||||
{activeTreeProductId && (
|
||||
<Modal
|
||||
open
|
||||
onClose={closeTreeModal}
|
||||
title={`🔀 流转树 — ${activeTreeProductId}`}
|
||||
widthClass="max-w-6xl"
|
||||
bodyClassName="max-h-[90vh] overflow-y-auto"
|
||||
>
|
||||
{treeLoading[activeTreeProductId] ? (
|
||||
<div className="flex justify-center py-16">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-blue-400" />
|
||||
</div>
|
||||
) : taskTrees[activeTreeProductId]?.task_tree?.length ? (
|
||||
<TaskFlowView
|
||||
tasks={taskTrees[activeTreeProductId].task_tree}
|
||||
onAction={setModalTarget}
|
||||
currentUser={currentUser}
|
||||
assigneeNames={taskTrees[activeTreeProductId].assignee_names}
|
||||
size="lg"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-gray-400">
|
||||
<GitBranch className="mb-3 h-12 w-12 text-gray-300" />
|
||||
<p className="text-sm font-medium text-gray-500">暂无流转记录</p>
|
||||
<p className="mt-1 text-xs text-gray-400">产品刚创建,尚未分配生产任务</p>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
{/* ---- 弹窗 ---- */}
|
||||
<ReceiveConfirmModal
|
||||
open={modalTarget?.action === "receive"}
|
||||
|
||||
Reference in New Issue
Block a user