import { useState } from "react"; import { Button } from "../ui/button"; import { formatDisplayDate, formatMoney } from "../../lib/displayFormat"; import type { ReimbursementApprovalEvidenceProjection, ReimbursementAttachmentEvidence, ReimbursementInvoiceEvidence, ReimbursementItemEvidence, } from "../../lib/reimbursementApprovalProjection"; export type ReimbursementEvidenceCapabilities = { onOpenInvoice?: (invoice: ReimbursementInvoiceEvidence) => void; onOpenAttachment?: (attachment: ReimbursementAttachmentEvidence) => void; onOpenMaterialBundle?: () => void; onDeleteAttachment?: (attachment: ReimbursementAttachmentEvidence) => void; onReferenceItem?: (item: ReimbursementItemEvidence) => void; onReferenceInvoice?: (invoice: ReimbursementInvoiceEvidence) => void; onEditItem?: (item: ReimbursementItemEvidence) => void; onDeleteItem?: (item: ReimbursementItemEvidence) => void; onLinkInvoice?: (invoice: ReimbursementInvoiceEvidence, linkedItemId: string) => void; linkInvoicePending?: boolean; linkInvoiceErrors?: Record; onUnlinkInvoice?: (invoice: ReimbursementInvoiceEvidence) => void; }; type Props = { evidence: ReimbursementApprovalEvidenceProjection; capabilities?: ReimbursementEvidenceCapabilities; mobile?: boolean; }; function evidenceStatusCopy(status: ReimbursementInvoiceEvidence["amountEvidenceStatus"]) { if (status === "ocr_failed") return "该发票的票面金额未能读取,暂时无法提交;请重新上传这张发票,或联系财务处理。"; if (status === "manual_review") return "票面金额需财务查看原件核对;这是一项审批提示,不会阻止提交。"; return null; } function InvoiceCard({ invoice, capabilities, linkTargets, }: { invoice: ReimbursementInvoiceEvidence; capabilities: ReimbursementEvidenceCapabilities; linkTargets: ReimbursementItemEvidence[]; }) { const [selectedItemId, setSelectedItemId] = useState(""); const title = invoice.invoiceNumber || invoice.originalName || "未命名发票"; const hasBlockingIssue = !invoice.linkedItemId || invoice.amountEvidenceStatus === "ocr_failed"; return (

{title}

{invoice.sellerName || invoice.documentSubtype || invoice.documentCategory || "票据"} {" · "}{formatDisplayDate(invoice.invoiceDate)} {" · "}{formatMoney(invoice.totalAmount)}

{evidenceStatusCopy(invoice.amountEvidenceStatus) ? (

{evidenceStatusCopy(invoice.amountEvidenceStatus)}

) : null}
{capabilities.onOpenInvoice ? ( ) : null} {capabilities.onReferenceInvoice ? ( ) : null} {capabilities.onLinkInvoice && !invoice.linkedItemId ? (
{capabilities.linkInvoiceErrors?.[invoice.id] ? (

{capabilities.linkInvoiceErrors[invoice.id]}

) : null}
) : null} {capabilities.onUnlinkInvoice && invoice.linkedItemId ? ( ) : null}
); } export function ReimbursementItemEvidenceList({ evidence, capabilities = {}, mobile = false }: Props) { return (
{evidence.items.length ? evidence.items.map((item, index) => { const hasBlockingIssue = item.amountExceeded || item.invoices.some((invoice) => invoice.amountEvidenceStatus === "ocr_failed") || (item.expenseType?.requiresInvoice !== false && item.invoices.length === 0); return (

明细 {index + 1}

{item.expenseType?.name ?? "未分类费用"}

{formatDisplayDate(item.expenseDate)} · {formatMoney(item.amount)}

{capabilities.onReferenceItem ? ( ) : null} {capabilities.onEditItem ? ( ) : null} {capabilities.onDeleteItem ? ( ) : null}

{item.description || "未填写明细说明"}

{item.assetPriorApprovalBasis ? (

事前批准依据:{item.assetPriorApprovalBasis}

) : null} {item.standardViolations.map((violation) => (

提交时费用标准提示:申报 {formatMoney(violation.amount)},标准 {formatMoney(violation.maxAmount)},超出 {formatMoney(violation.exceededAmount)}。审批时请核对原因。

))} {item.showOfficeElectronicsHint ? (

该金额通常应选择办公电子设备,请核对费用类型和事前批准依据。

) : null} {item.amountExceeded ? (

当前申报金额高于已读取的票面金额合计,暂时不能据此通过;请核对明细金额和票据是否完整。

) : null} {item.invoices.length || item.expenseType?.requiresInvoice !== false ?

所属票据({item.invoices.length})

{item.invoices.length ? item.invoices.map((invoice) => ( )) : (

这条明细尚无对应票据,暂时无法提交;请添加票据并选择这条明细。

)}
: null}
); }) : (

当前报销单没有可核验的明细。

)}
{evidence.unlinkedInvoices.length ? (

尚未对应明细的票据({evidence.unlinkedInvoices.length})

这些票据当前没有明确费用归属,暂时无法提交;请为每张票据选择对应的报销明细。

{evidence.unlinkedInvoices.map((invoice) => ( ))}
) : null}

补充附件({evidence.attachments.length})

{capabilities.onOpenMaterialBundle ? ( ) : null}
{evidence.attachments.length ? evidence.attachments.map((attachment) => (

{attachment.originalName}

{attachment.mimeType} · {(attachment.fileSize / 1024).toFixed(1)} KB

{capabilities.onOpenAttachment ? ( ) : null} {capabilities.onDeleteAttachment ? ( ) : null}
)) :

未上传补充附件

}
); }