import { useMemo, useRef, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useSearchParams } from "react-router-dom"; import { cancelPaymentBatch, createPaymentBatch, downloadPaymentBatchReceiptAttachment, downloadPaymentOrder, executePaymentBatch, fetchPaymentBatchCandidates, fetchPaymentBatchDetail, fetchPaymentBatchReceiptAttachments, fetchPaymentBatches, reviewPaymentBatch, revealPaymentBatchPayeeBank, updatePaymentBatchItem, uploadPaymentBatchReceiptAttachments, voidPaymentBatchReceiptAttachment, type PaymentBatchReceiptAttachment, type PaymentChannel, } from "../api/paymentBatches"; import { isApiError } from "../api/types"; import { mapApiErrorMessage } from "../api/errorMessages"; import { useAuth } from "../auth/AuthContext"; import { hasPermission } from "../auth/permissions"; import { HeaderActionGroup, ListWorkspace, WorkspaceHeader } from "../components/patterns/ListWorkspace"; import { BusinessListToolbar, BusinessMobileList, BusinessTable, } from "../components/patterns/BusinessListTemplate"; import { ActionConfirmDialog } from "../components/common/ActionConfirmDialog"; import { AttachmentGovernanceBadges } from "../components/common/AttachmentGovernanceBadges"; import { useAttachmentDownloadReasonDialog } from "../components/common/useAttachmentDownloadReasonDialog"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../components/ui/dialog"; import { Textarea } from "../components/ui/textarea"; import { RiskFlagsCard } from "../components/common/RiskFlagsCard"; import { StatusBadge } from "../components/common/StatusBadge"; import { DateTextInput } from "../components/common/DateTextInput"; import { downloadBlob } from "../lib/download"; import { formatDisplayDate, formatDisplayDateTime, formatMoney } from "../lib/displayFormat"; import { paymentBatchStatusLabel } from "../lib/paymentBatchDisplay"; import { toast } from "sonner"; const PAYMENT_CHANNEL_OPTIONS: PaymentChannel[] = ["bank", "alipay", "wechat", "cash", "other"]; function entityTypeLabel(value: string) { if (value === "reimbursement") return "报销"; if (value === "travel_order") return "差旅订单"; return value || "-"; } function paymentChannelLabel(value: PaymentChannel | string | null | undefined) { if (value === "bank") return "银行转账"; if (value === "alipay") return "支付宝"; if (value === "wechat") return "微信"; if (value === "cash") return "现金"; if (value === "other") return "其他"; return value || "-"; } function paymentItemStatusLabel(value: string) { if (value === "pending") return "待付款"; if (value === "paid") return "已支付"; if (value === "executed") return "已付款"; if (value === "failed") return "付款失败"; if (value === "cancelled") return "已取消"; if (value === "skipped") return "已跳过"; return value || "-"; } function formatFileSize(value: number) { if (!Number.isFinite(value) || value <= 0) return "-"; if (value < 1024) return `${value} B`; if (value < 1024 * 1024) return `${Math.ceil(value / 1024)} KB`; return `${(value / 1024 / 1024).toFixed(1)} MB`; } function resolveError(error: unknown) { return isApiError(error) ? `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}` : error instanceof Error ? error.message : "操作失败"; } function paymentOrderBlockerLabel(blocker: { code: string; entityCode?: string }) { const subject = blocker.entityCode ? `${blocker.entityCode}:` : ""; const messages: Record = { status_not_reviewed: "仅待财务付款批次可生成制单", reimbursement_only: "仅报销付款批次支持制单", bank_channel_required: "付款渠道不是银行转账", legacy_payment_account_snapshot_missing: "历史批次未冻结公司付款账户,仅支持人工付款", payment_account_snapshot_invalid: "冻结的公司付款账户资料不完整", payer_account_unavailable: "冻结的公司付款账号不可用", payer_bank_institution_missing: "公司付款账户缺少规范化开户银行", payer_bank_not_supported: "公司付款账户不是建设银行", items_missing: "批次没有可制单的报销明细", legacy_payee_snapshot_missing: "未冻结完整收款账号,仅支持人工付款", payee_name_missing: "冻结的收款人姓名不完整", payee_bank_name_missing: "冻结的收款开户行名称不完整", payee_account_masked_missing: "冻结的收款账号摘要不完整", payee_bank_institution_missing: "未冻结规范化开户银行,仅支持人工付款", payee_bank_not_supported: "收款账户不是建设银行,首版请人工转账", }; return `${subject}${messages[blocker.code] ?? "制单资料不完整,请联系管理员核对"}`; } export function PaymentBatchesPage() { const queryClient = useQueryClient(); const { me, permissions } = useAuth(); const [searchParams, setSearchParams] = useSearchParams(); const canManageBatch = hasPermission(permissions, "payment.batch.manage"); const canReviewBatch = hasPermission(permissions, "payment.batch.review") && (me?.roleCodes.includes("manager") || me?.roleCodes.includes("ceo")); const canExportPaymentOrder = hasPermission(permissions, "payment.order.export") && Boolean(me?.roleCodes.some((roleCode) => ["finance", "finance_mgr"].includes(roleCode))); const canRevealPayeeBank = hasPermission(permissions, "employee.bank.read") && Boolean(me?.roleCodes.some((roleCode) => ["finance", "finance_mgr"].includes(roleCode))); const [candidateSearch, setCandidateSearch] = useState(""); const [selectedCandidateIds, setSelectedCandidateIds] = useState([]); const [selectedPaymentChannel, setSelectedPaymentChannel] = useState("bank"); const receiptInputRef = useRef(null); const [error, setError] = useState(""); const [showCreatePanel, setShowCreatePanel] = useState(false); const [confirmPaymentAction, setConfirmPaymentAction] = useState<{ kind: "financeReview" | "financePayment"; batchId: string; batchNo: string; highRisk: boolean; totalAmount: string; } | null>(null); const [reasonAction, setReasonAction] = useState< | { kind: "cancel"; batchId: string; batchNo: string } | { kind: "voidReceipt"; batchId: string; batchNo: string; attachmentId: string; attachmentName: string } | { kind: "paymentOrder"; batchId: string; batchNo: string; reexport: boolean } | { kind: "payeeBank"; batchId: string; batchNo: string; itemId: string; entityCode: string } | null >(null); const [actionReason, setActionReason] = useState(""); const [revealedPayeeAccount, setRevealedPayeeAccount] = useState(""); const listQuery = useQuery({ queryKey: ["payment-batches"], queryFn: () => fetchPaymentBatches(), }); const candidatesQuery = useQuery({ queryKey: ["payment-batch-candidates", candidateSearch], enabled: showCreatePanel && canManageBatch, queryFn: () => fetchPaymentBatchCandidates({ q: candidateSearch, page: 1, pageSize: 50 }), }); const selectedBatchId = searchParams.get("id") ?? ""; const batches = useMemo(() => listQuery.data?.items ?? [], [listQuery.data?.items]); const selectedBatchQuery = useQuery({ queryKey: ["payment-batches", "detail", selectedBatchId], enabled: Boolean(selectedBatchId), queryFn: () => fetchPaymentBatchDetail(selectedBatchId), }); const selectedBatch = selectedBatchQuery.data?.item ?? null; const receiptAttachmentsQuery = useQuery({ queryKey: ["payment-batch-receipts", selectedBatch?.id], enabled: Boolean(selectedBatch?.id), queryFn: () => fetchPaymentBatchReceiptAttachments(selectedBatch!.id), }); const canEditSelectedBatch = canManageBatch && !!selectedBatch && ["reviewed", "failed"].includes(selectedBatch.status); const canEditReceiptAttachments = canManageBatch && !!selectedBatch && ["reviewed", "failed"].includes(selectedBatch.status); const canUploadReceipt = canEditReceiptAttachments && receiptAttachmentsQuery.isSuccess; const canReviewSelectedBatch = canReviewBatch && !!selectedBatch && selectedBatch.createdBy !== me?.id && ( selectedBatch.status === "draft" && selectedBatch.reviewRequirement.nextStep === "first_review" ); const canExecuteSelectedBatch = canManageBatch && !!selectedBatch && ["reviewed", "failed"].includes(selectedBatch.status) && selectedBatch.reviewRequirement.nextStep === "ready_for_execute"; const requiresBankReceipt = Boolean(selectedBatch?.items.some((item) => ["pending", "failed"].includes(item.status) && (item.paymentChannelOverride ?? selectedBatch.defaultPaymentChannel) === "bank", )); const activeReceiptCount = (receiptAttachmentsQuery.data?.items ?? []).filter((item) => !item.archivedAt).length; const needsBankReceipt = canExecuteSelectedBatch && requiresBankReceipt && receiptAttachmentsQuery.isSuccess && activeReceiptCount === 0; const canRegisterPaymentComplete = canExecuteSelectedBatch && (!requiresBankReceipt || (receiptAttachmentsQuery.isSuccess && activeReceiptCount > 0)); const canCancelSelectedBatch = canManageBatch && !!selectedBatch && !["executed", "cancelled"].includes(selectedBatch.status); const refresh = async () => { await queryClient.invalidateQueries({ queryKey: ["payment-batches"] }); await queryClient.invalidateQueries({ queryKey: ["payment-batch-candidates"] }); await queryClient.invalidateQueries({ queryKey: ["payment-batch-receipts"] }); await queryClient.invalidateQueries({ queryKey: ["ap-operations"] }); }; const returnToList = () => { const next = new URLSearchParams(searchParams); next.delete("id"); setSearchParams(next, { replace: true }); }; const createMutation = useMutation({ mutationFn: async () => createPaymentBatch({ entityType: "reimbursement", entityIds: selectedCandidateIds, defaultPaymentChannel: selectedPaymentChannel, }), onSuccess: async ({ item }) => { setError(""); setSelectedCandidateIds([]); setCandidateSearch(""); setSelectedPaymentChannel("bank"); const next = new URLSearchParams(searchParams); next.set("id", item.id); setSearchParams(next, { replace: true }); await refresh(); toast.success("已提交付款复核,等待负责人处理"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const reviewMutation = useMutation({ mutationFn: async (batchId: string) => { const batch = selectedBatch?.id === batchId ? selectedBatch : listQuery.data?.items.find((item) => item.id === batchId); const resolveRiskFlagIds = (batch?.items ?? []).flatMap((item) => item.openRiskFlags.map((flag) => flag.id)); return reviewPaymentBatch(batchId, { resolveRiskFlagIds }); }, onSuccess: async ({ item }) => { setError(""); await refresh(); toast.success(item.reviewRequirement.nextStep === "ready_for_execute" ? "付款批次复核完成" : "付款批次已完成首轮复核"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const executeMutation = useMutation({ mutationFn: executePaymentBatch, onSuccess: async () => { setError(""); await refresh(); toast.success("付款已登记完成"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const cancelMutation = useMutation({ mutationFn: ({ batchId, reason }: { batchId: string; reason: string }) => cancelPaymentBatch(batchId, reason), onSuccess: async () => { setError(""); setReasonAction(null); setActionReason(""); await refresh(); toast.success("付款批次已取消"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const updateItemMutation = useMutation({ mutationFn: async (payload: { batchId: string; itemId: string; paymentVoucherNoOverride?: string | null; paymentDateOverride?: string | null }) => updatePaymentBatchItem(payload.batchId, payload.itemId, { paymentVoucherNoOverride: payload.paymentVoucherNoOverride, paymentDateOverride: payload.paymentDateOverride, }), onSuccess: async () => { setError(""); await refresh(); toast.success("批次项已更新"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const uploadReceiptMutation = useMutation({ mutationFn: async (files: File[]) => { if (!selectedBatch) { throw new Error("请先选择付款批次"); } if (!files.length) { throw new Error("请选择银行回单附件"); } return uploadPaymentBatchReceiptAttachments(selectedBatch.id, files); }, onSuccess: async () => { setError(""); if (receiptInputRef.current) { receiptInputRef.current.value = ""; } await refresh(); toast.success("银行回单附件已上传"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const { requestDownloadReason, downloadReasonDialog } = useAttachmentDownloadReasonDialog(); const downloadReceiptMutation = useMutation({ mutationFn: async ({ attachment, reason }: { attachment: PaymentBatchReceiptAttachment; reason?: string }) => { if (!selectedBatch) { throw new Error("请先选择付款批次"); } const response = await downloadPaymentBatchReceiptAttachment(selectedBatch.id, attachment.id, reason); return { response, attachment }; }, onSuccess: ({ response, attachment }) => { downloadBlob(response.blob, response.filename ?? attachment.originalName); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); async function handleDownloadReceipt(attachment: PaymentBatchReceiptAttachment) { const reason = await requestDownloadReason(attachment); if (reason === null) { return; } downloadReceiptMutation.mutate({ attachment, reason }); } const voidReceiptMutation = useMutation({ mutationFn: async ({ batchId, attachmentId, reason }: { batchId: string; attachmentId: string; reason: string }) => { return voidPaymentBatchReceiptAttachment(batchId, attachmentId, reason); }, onSuccess: async () => { setError(""); setReasonAction(null); setActionReason(""); await refresh(); toast.success("银行回单已留痕作废"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const paymentOrderMutation = useMutation({ mutationFn: ({ batchId, reason }: { batchId: string; reason: string }) => downloadPaymentOrder(batchId, reason), onSuccess: async (response) => { downloadBlob(response.blob, response.filename ?? "payment-order.xls"); setReasonAction(null); setActionReason(""); await refresh(); toast.success("银行制单已生成,请导入企业网银后核对结果"); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); const revealPayeeBankMutation = useMutation({ mutationFn: ({ batchId, itemId, reason }: { batchId: string; itemId: string; reason: string }) => revealPaymentBatchPayeeBank(batchId, itemId, reason), onSuccess: ({ bankAccount }) => { setRevealedPayeeAccount(bankAccount); window.setTimeout(() => revealPayeeBankMutation.reset(), 0); }, onError: (err) => { const message = resolveError(err); setError(message); toast.error(message); }, }); return ( {downloadReasonDialog} {selectedBatchId ? ( ) : null} {!selectedBatchId && canManageBatch ? ( ) : null} } /> {error ?
{error}
: null} {!selectedBatchId && showCreatePanel ? (

新建批次

{!canManageBatch ? (
当前账号仅可查看批次并执行复核,创建与编辑入口仅对付款批次管理员开放。
) : null}
setCandidateSearch(event.target.value)} placeholder="按报销编号、人员或摘要搜索" />
{candidatesQuery.isLoading ?
正在加载待付款报销...
: null} {candidatesQuery.isError ?
{resolveError(candidatesQuery.error)}
: null} {(candidatesQuery.data?.items ?? []).map((candidate) => { const selected = selectedCandidateIds.includes(candidate.entityId); return ( ); })} {!candidatesQuery.isLoading && !candidatesQuery.isError && !(candidatesQuery.data?.items ?? []).length ? (
没有可加入批次的已审批报销。
) : null}
已选择 {selectedCandidateIds.length} 项;系统自动生成编号和标题,按{paymentChannelLabel(selectedPaymentChannel)}提交负责人复核。
) : null} {selectedBatchId ? ( selectedBatchQuery.isLoading ? (
正在加载付款批次...
) : selectedBatchQuery.isError ? (
{resolveError(selectedBatchQuery.error)}
) : selectedBatch ? (
{selectedBatch.batchNo}
{selectedBatch.name}
{selectedBatch.highRisk ? ( 高风险 ) : null}
{canReviewSelectedBatch ? ( ) : null} {selectedBatch.status === "reviewed" && selectedBatch.canExportPaymentOrder && canExportPaymentOrder && selectedBatch.paymentOrderExportCount === 0 ? ( ) : null} {needsBankReceipt ? ( ) : null} {canRegisterPaymentComplete ? ( ) : null} {canManageBatch ? ( ) : null} {selectedBatch.status === "reviewed" && selectedBatch.canExportPaymentOrder && canExportPaymentOrder && selectedBatch.paymentOrderExportCount > 0 ? ( ) : null}
状态
{paymentBatchStatusLabel(selectedBatch.status)}
实体
{entityTypeLabel(selectedBatch.entityType)}
总金额
{formatMoney(selectedBatch.totalAmount)}
默认日期
{formatDisplayDate(selectedBatch.defaultPaymentDate)}
默认渠道
{paymentChannelLabel(selectedBatch.defaultPaymentChannel)}
复核进度
{selectedBatch.reviewRequirement.completedReviewCount}/{selectedBatch.reviewRequirement.requiredReviewCount}
付款账户
{selectedBatch.paymentAccount ? `${selectedBatch.paymentAccount.accountName} · ${selectedBatch.paymentAccount.accountNumberMasked}` : "未冻结(仅支持人工付款)"}
银行制单
{selectedBatch.paymentOrderExportCount > 0 ? `已生成 ${selectedBatch.paymentOrderExportCount} 次` : "尚未生成"}
{selectedBatch.status === "reviewed" && selectedBatch.paymentOrderExportBlockers.length > 0 ? (
当前批次不能生成银行制单,可继续人工付款:
    {selectedBatch.paymentOrderExportBlockers.map((blocker, index) => (
  • {paymentOrderBlockerLabel(blocker)}
  • ))}
) : null}

当前处理

当前责任
{selectedBatch.status === "draft" ? "负责人(非制单人)" : ["reviewed", "failed"].includes(selectedBatch.status) ? "财务" : selectedBatch.status === "executed" ? "已完成" : "已结束"}
下一步
{selectedBatch.status === "draft" ? "复核通过" : selectedBatch.status === "reviewed" && requiresBankReceipt && !receiptAttachmentsQuery.isSuccess ? "核对银行回单状态" : selectedBatch.status === "reviewed" && needsBankReceipt ? selectedBatch.paymentOrderExportCount > 0 ? "制单已生成,完成付款并上传银行回单" : "线下付款并上传银行回单" : selectedBatch.status === "reviewed" ? "登记付款完成" : selectedBatch.status === "failed" ? "核对失败原因并重试" : "-"}
{selectedBatch.status === "draft" ? "已提交付款复核,等待负责人处理;制单人不能复核自己的批次。" : selectedBatch.status === "reviewed" && requiresBankReceipt && !receiptAttachmentsQuery.isSuccess ? "付款复核已通过,正在核对银行回单状态。" : selectedBatch.status === "reviewed" && needsBankReceipt ? selectedBatch.paymentOrderExportCount > 0 ? "已生成银行制单,等待付款完成并上传回单。" : "付款复核已通过。等待财务线下付款并上传回单。" : selectedBatch.status === "reviewed" ? "付款复核已通过。请财务核对付款结果并登记付款完成。" : selectedBatch.status === "executed" ? "付款已登记完成。" : selectedBatch.status === "failed" ? "付款登记失败,请财务核对失败原因后重试。" : "该付款批次已取消。"} {selectedBatch.highRisk ? ` 批次金额达到 ${formatMoney(selectedBatch.reviewRequirement.thresholdAmount)},处理前需再次核对付款对象、金额和回单,但不增加审批层级。` : ""} {selectedBatch.cancelledReason ? ` 取消原因:${selectedBatch.cancelledReason}` : ""}

报销明细

{selectedBatch.items.map((item) => (
{item.entityCode}
{item.entityTitle}
{formatMoney(item.amount)} · {paymentItemStatusLabel(item.status)}
收款人
{item.payeeName || "-"}
开户银行
{item.bankName || "-"}
银行账户
{item.bankAccountMasked || "-"}
{canRevealPayeeBank && ["reviewed", "failed"].includes(selectedBatch.status) && selectedBatch.paymentAccount ? : null}
付款渠道
{paymentChannelLabel(item.paymentChannelOverride ?? selectedBatch.defaultPaymentChannel)}
{["reviewed", "failed"].includes(selectedBatch.status) ? (
updateItemMutation.mutate({ batchId: selectedBatch.id, itemId: item.id, paymentVoucherNoOverride: event.target.value || null })} /> updateItemMutation.mutate({ batchId: selectedBatch.id, itemId: item.id, paymentDateOverride: event.target.value || null })} />
{item.failureMessage ? `失败原因:${item.failureMessage}` : "付款异常:无"}
) : ["executed", "cancelled"].includes(selectedBatch.status) ? (
付款凭证号
{item.paymentVoucherNoOverride || "-"}
实际付款日期
{formatDisplayDate(item.paymentDateOverride ?? selectedBatch.defaultPaymentDate)}
付款结果
{item.failureMessage || paymentItemStatusLabel(item.status)}
) : (
付款复核通过后,由财务登记付款凭证号和实际付款日期。
)}
))}

银行回单

{selectedBatch.status === "draft" ? "付款复核通过后,由财务在线下完成付款并上传银行回单。" : "先在线下银行完成付款,再上传回单;系统不会连接银行自动转账。"}

{canUploadReceipt ? (
{ const files = Array.from(event.target.files ?? []); if (files.length) uploadReceiptMutation.mutate(files); }} /> {!needsBankReceipt ? ( ) : null}
) : null}
{receiptAttachmentsQuery.isError ? (
{resolveError(receiptAttachmentsQuery.error)}
) : null}
{(receiptAttachmentsQuery.data?.items ?? []).map((attachment) => (
{attachment.originalName}
{attachment.mimeType} · {formatFileSize(attachment.fileSize)} · {formatDisplayDateTime(attachment.createdAt)} {attachment.uploadedBy ? ` · ${attachment.uploadedBy.name}` : ""}
{canEditReceiptAttachments ? ( ) : null}
))} {receiptAttachmentsQuery.isSuccess && !(receiptAttachmentsQuery.data?.items ?? []).length ? (
暂无银行回单;银行转账必须先上传回单,才能登记付款完成。
) : null}
) : (
未找到付款批次
) ) : ( <> void refresh()}> 刷新 } > 全部批次 {batches.length} {batches.map((item) => ( ))} {!listQuery.data?.items?.length ?
暂无付款批次
: null}
批次 实体 金额 复核 状态 操作 {batches.length ? ( batches.map((item) => (
{item.batchNo}
{item.name}
{entityTypeLabel(item.entityType)} {formatMoney(item.totalAmount)} {item.reviewRequirement.completedReviewCount}/{item.reviewRequirement.requiredReviewCount}
{item.highRisk ? 高风险 : null}
)) ) : ( 暂无付款批次 )}
)} { if (!open) setConfirmPaymentAction(null); }} kind={confirmPaymentAction?.kind ?? "financePayment"} targetName={confirmPaymentAction?.batchNo} description={ confirmPaymentAction?.kind === "financeReview" ? confirmPaymentAction.highRisk ? `该批次金额为 ${formatMoney(confirmPaymentAction.totalAmount)},请核对付款对象和金额。确认后完成一次独立复核,不增加第二审批层级。` : "确认后将完成一次独立复核并写入付款批次审计记录。" : confirmPaymentAction?.highRisk ? `这是高额付款批次,金额 ${formatMoney(confirmPaymentAction.totalAmount)}。请再次核对付款对象、金额、渠道和回单;确认后将登记付款完成并更新源单状态。` : "请确认线下付款和银行回单均已核对。确认后将登记付款完成并更新源单状态。" } onConfirm={async () => { if (!confirmPaymentAction) return; if (confirmPaymentAction.kind === "financeReview") reviewMutation.mutate(confirmPaymentAction.batchId); if (confirmPaymentAction.kind === "financePayment") executeMutation.mutate(confirmPaymentAction.batchId); }} /> { if (!open) { setReasonAction(null); setActionReason(""); setRevealedPayeeAccount(""); } }} > {reasonAction?.kind === "voidReceipt" ? "作废银行回单" : reasonAction?.kind === "paymentOrder" ? (reasonAction.reexport ? "重新下载银行制单" : "下载银行制单") : reasonAction?.kind === "payeeBank" ? "查看收款完整账号" : "取消付款批次"} {reasonAction?.kind === "voidReceipt" ? `回单:${reasonAction.attachmentName}。文件记录会保留并标记为已作废。` : reasonAction?.kind === "paymentOrder" ? reasonAction.reexport ? `批次:${reasonAction.batchNo}。重新下载不会作废之前的文件。如果上一份已在网银导入,请勿重复提交。` : `批次:${reasonAction.batchNo}。文件包含完整银行账号,仅用于导入企业网银。` : reasonAction?.kind === "payeeBank" ? `报销单:${reasonAction.entityCode}。本次查看会记录查看人、批次项、时间和原因。` : reasonAction ? `批次:${reasonAction.batchNo}。只取消尚未执行的付款项,已执行项不会回滚。` : "请填写原因。"} {reasonAction?.kind === "payeeBank" && revealedPayeeAccount ? (
{revealedPayeeAccount}
) :