import { useMemo, useState } from "react"; import { Link, useNavigate, useParams } from "react-router-dom"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { isApiError } from "../api/types"; import { mapApiErrorMessage } from "../api/errorMessages"; import { approveWorkflowTask, fetchWorkflowApprovers, fetchWorkflowTaskDetail, rejectWorkflowTask, returnWorkflowTask, transferWorkflowTask, } from "../api/workflow"; import { toast } from "sonner"; import { useAuth } from "../auth/AuthContext"; import { hasPermission } from "../auth/permissions"; import { CardSkeleton } from "../components/common/CardSkeleton"; import { PageHeader } from "../components/common/PageHeader"; import { StatusBadge } from "../components/common/StatusBadge"; import { Button } from "../components/ui/button"; import { Textarea } from "../components/ui/textarea"; function entityTitle(entityType: string | undefined) { if (entityType === "leave") { return "请假申请"; } if (entityType === "reimbursement") { return "报销单"; } if (entityType === "purchase_request") { return "采购申请"; } return entityType || "业务单据"; } export function WorkflowTaskDetailPage() { const { taskId = "" } = useParams(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { permissions } = useAuth(); const [rejectComment, setRejectComment] = useState("请补充必要说明后重提"); const [returnComment, setReturnComment] = useState("请补充必要信息并修正后重新提交"); const [transferReason, setTransferReason] = useState("当前事项需转交对应审批人处理,请尽快处理。"); const [selectedApprover, setSelectedApprover] = useState(""); const [actionError, setActionError] = useState(""); const detailQuery = useQuery({ enabled: Boolean(taskId), queryKey: ["workflow", "task", taskId], queryFn: () => fetchWorkflowTaskDetail(taskId), }); const approverQuery = useQuery({ enabled: hasPermission(permissions, "expense.transfer"), queryKey: ["workflow", "approvers"], queryFn: () => fetchWorkflowApprovers({ page: 1, pageSize: 50 }), }); const allowedActions = useMemo(() => { return new Set(detailQuery.data?.allowedActions ?? []); }, [detailQuery.data?.allowedActions]); const mutation = useMutation({ mutationFn: async (action: "approve" | "reject" | "return" | "transfer") => { if (!taskId) { throw new Error("missing taskId"); } if (action === "approve") { return approveWorkflowTask(taskId); } if (action === "reject") { return rejectWorkflowTask(taskId, rejectComment.trim()); } if (action === "return") { return returnWorkflowTask(taskId, returnComment.trim()); } if (!selectedApprover) { throw new Error("请选择转审目标"); } return transferWorkflowTask(taskId, selectedApprover, transferReason.trim()); }, onSuccess: async (_data, action) => { await Promise.all([ queryClient.invalidateQueries({ queryKey: ["workflow", "tasks"] }), queryClient.invalidateQueries({ queryKey: ["workflow", "task", taskId] }), ]); setActionError(""); const messages: Record = { approve: "已通过", reject: "已驳回", return: "已退回", transfer: "已转审" }; toast.success(messages[action] ?? "操作成功"); }, onError: (error) => { let msg: string; if (error instanceof Error && !isApiError(error)) { msg = error.message; } else if (isApiError(error)) { msg = `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`; } else { msg = "动作执行失败,请稍后重试"; } setActionError(msg); toast.error(msg); }, }); if (!taskId) { return (

缺少 taskId 参数。

); } if (detailQuery.isLoading) { return ; } if (detailQuery.isError || !detailQuery.data) { const message = isApiError(detailQuery.error) ? `${detailQuery.error.code}: ${mapApiErrorMessage( detailQuery.error.code, detailQuery.error.details, detailQuery.error.message, )}` : "加载失败,请返回列表重试"; return (

{message}

); } const detail = detailQuery.data; const isLeaveTask = detail.workflowInstance?.entityType === "leave"; const canTransfer = allowedActions.has("transfer") && detail.workflowInstance?.entityType === "reimbursement"; return (
返回待办列表 } />
状态:
实例:{detail.workflowInstance?.id ?? "-"}
对象类型:{entityTitle(detail.workflowInstance?.entityType)}
{detail.reimbursement ? ( <>
报销单号:{detail.reimbursement.formNumber ?? "-"}
报销金额:{detail.reimbursement.totalAmount ?? "-"}
提交人:{detail.reimbursement.submitter?.name ?? "-"}
报销人:{detail.reimbursement.owner?.name ?? "-"}
) : null} {detail.leaveRequest ? ( <>
假期类型:{detail.leaveRequest.leaveType?.name ?? "-"}
申请人:{detail.leaveRequest.owner?.name ?? "-"}
时间段: {detail.leaveRequest.startDate ?? "-"} {detail.leaveRequest.startSession === "am" ? "上午" : "下午"} {" → "} {detail.leaveRequest.endDate ?? "-"} {detail.leaveRequest.endSession === "am" ? "上午" : "下午"}
请假天数:{detail.leaveRequest.requestedDays}
说明:{detail.leaveRequest.reason ?? "-"}
附件:{detail.leaveRequest.attachment?.originalName ?? "-"}
) : null} {detail.purchaseRequest ? ( <>
采购单号:{detail.purchaseRequest.requestNo ?? detail.purchaseRequest.id}
申请人:{detail.purchaseRequest.applicant?.name ?? "-"}
供应商:{detail.purchaseRequest.supplier?.name ?? "-"}
法体:{detail.purchaseRequest.legalEntity?.name ?? "-"}
成本中心:{detail.purchaseRequest.costCenter?.name ?? "-"}
单据分类:{detail.purchaseRequest.documentCategory?.name ?? "-"}
金额:{detail.purchaseRequest.totalAmount ?? "-"} {detail.purchaseRequest.currencyCode}
预计到货:{detail.purchaseRequest.expectedDeliveryDate ?? "-"}
说明:{detail.purchaseRequest.reason ?? "-"}
) : null}

审批动作

{actionError ? (

{actionError}

) : null}

驳回