import { useParams } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { fetchPurchaseRequestDetail } from "../api/purchaseRequests"; import { MobileDetailHeader } from "../components/mobile/MobileDetailHeader"; import { AttachmentGovernanceBadges } from "../components/common/AttachmentGovernanceBadges"; import { StatusBadge } from "../components/common/StatusBadge"; import { PageScaffold } from "../components/patterns/PageScaffold"; import { formatApprovalHistoryTime, resolvePurchaseDisplayCode, resolvePurchaseStatusHint, workflowActionLabel, } from "../lib/approvalDisplay"; import { joinBusinessMeta } from "../lib/businessDisplay"; import { resolveErrorMessage } from "../lib/resolveErrorMessage"; function formatCurrency(value: string | null | undefined, currencyCode = "CNY") { const amount = Number(value ?? 0); if (!Number.isFinite(amount)) { return "-"; } return new Intl.NumberFormat("zh-CN", { style: "currency", currency: currencyCode }).format(amount); } function formatDate(value: string | null | undefined) { return value ? value.slice(0, 10) : "-"; } function formatBytes(value: number) { if (value < 1024) { return `${value} B`; } if (value < 1024 * 1024) { return `${(value / 1024).toFixed(1)} KB`; } return `${(value / 1024 / 1024).toFixed(1)} MB`; } function purchaseProgressFallback(status: string | undefined) { if (status === "draft") return "/mobile/approvals?progress=draft"; if (status === "approved") return "/mobile/approvals?progress=approved"; if (status === "rejected" || status === "cancelled") return "/mobile/approvals?progress=returned"; return "/mobile/approvals?progress=reviewing"; } export function MobilePurchaseRequestDetailPage() { const { purchaseRequestId = "" } = useParams(); const detailQuery = useQuery({ enabled: Boolean(purchaseRequestId), queryKey: ["mobile", "purchase-request", "detail", purchaseRequestId], queryFn: () => fetchPurchaseRequestDetail(purchaseRequestId), }); const detail = detailQuery.data ?? null; const displayCode = detail ? resolvePurchaseDisplayCode(detail) : "采购详情"; const nextStepText = detail ? detail.pendingWorkflow?.task?.stepName ? `当前在 ${detail.pendingWorkflow.task.stepName},可从审批详情处理;本页用于核对采购依据。` : resolvePurchaseStatusHint(detail) : ""; return ( : null} fallbackTo={purchaseProgressFallback(detail?.status)} testId="mobile-purchase-detail-header" /> {!purchaseRequestId ? (
缺少采购申请 ID。
) : null} {detailQuery.isLoading ?
加载中...
: null} {detailQuery.isError ? (
{resolveErrorMessage(detailQuery.error)}
) : null} {detail ? ( <>

{displayCode}

{detail.title || "未命名采购申请"}

金额
{formatCurrency(detail.totalAmount, detail.currencyCode)}
申请人
{detail.applicant?.name ?? "-"}
当前节点
{detail.pendingWorkflow?.task?.stepName ?? "-"}
下一步
{nextStepText}

业务信息

供应商
{detail.supplier?.name ?? "-"}
期望到货
{formatDate(detail.expectedDeliveryDate)}
法体
{detail.legalEntity?.name ?? "-"}
成本中心
{detail.costCenter?.name ?? "-"}
预算池
{detail.budgetPool?.name ?? "未匹配"}
当前节点
{detail.pendingWorkflow?.task?.stepName ?? "-"}
{detail.reason ? (
申请说明
{detail.reason}
) : null}

采购明细

{detail.items.length ? detail.items.map((item) => (
{item.description}
{joinBusinessMeta([`数量:${item.quantity}`, `单价:${formatCurrency(item.unitPrice, detail.currencyCode)}`, `金额:${formatCurrency(item.lineAmount, detail.currencyCode)}`])}
{item.specification ?
{item.specification}
: null}
)) :
暂无采购明细
}

附件

{detail.attachments.length ? detail.attachments.map((attachment) => (
{attachment.originalName}
{joinBusinessMeta([formatBytes(attachment.fileSize), attachment.uploader?.name, formatDate(attachment.createdAt)])}
)) :
暂无附件
}

审批记录

{detail.approvalHistory.length ? detail.approvalHistory.map((item) => (
{workflowActionLabel(item.action)}
{formatApprovalHistoryTime(item.createdAt)}
{item.actorName ?? item.actorEmployeeNo ?? "-"}
{item.comment ?
{item.comment}
: null}
)) :
暂无审批记录
}
) : null}
); } export default MobilePurchaseRequestDetailPage;