From dc0feb60b24b02b1a02b66fd9806e6794ccd6dc5 Mon Sep 17 00:00:00 2001 From: shunli Date: Thu, 5 Mar 2026 18:38:56 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ui):=20=E6=8F=90=E5=8F=96=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E5=8E=9F=E7=94=9F=E7=BB=84=E4=BB=B6=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=B9=B6=E4=BF=AE=E5=A4=8D=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E8=BE=B9=E8=B7=9Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/invoice-pool/InvoiceBindCard.tsx | 69 ++++ .../pages/invoice-pool/InvoiceDetailPanel.tsx | 375 ++++++++++++++++++ .../pages/invoice-pool/InvoiceListCard.tsx | 144 +++++++ .../pages/invoice-pool/InvoiceUploadCard.tsx | 73 ++++ 4 files changed, 661 insertions(+) create mode 100644 client/src/pages/invoice-pool/InvoiceBindCard.tsx create mode 100644 client/src/pages/invoice-pool/InvoiceDetailPanel.tsx create mode 100644 client/src/pages/invoice-pool/InvoiceListCard.tsx create mode 100644 client/src/pages/invoice-pool/InvoiceUploadCard.tsx diff --git a/client/src/pages/invoice-pool/InvoiceBindCard.tsx b/client/src/pages/invoice-pool/InvoiceBindCard.tsx new file mode 100644 index 00000000..c75a62a2 --- /dev/null +++ b/client/src/pages/invoice-pool/InvoiceBindCard.tsx @@ -0,0 +1,69 @@ +import type { UseMutationResult, UseQueryResult } from "@tanstack/react-query"; +import type { InvoicePoolListResponse } from "../../api/invoicePool"; +import type { ReimbursementListResponse } from "../../api/reimbursements"; +import { Button } from "../../components/ui/button"; + +export type InvoiceBindCardProps = { + listQuery: UseQueryResult; + reimbursementQuery: UseQueryResult; + bindMutation: UseMutationResult, Error, void>; + selectedInvoiceId: string; + setSelectedInvoiceId: (id: string) => void; + selectedReimbursementId: string; + setSelectedReimbursementId: (id: string) => void; +}; + +export function InvoiceBindCard({ + listQuery, + reimbursementQuery, + bindMutation, + selectedInvoiceId, + setSelectedInvoiceId, + selectedReimbursementId, + setSelectedReimbursementId, +}: InvoiceBindCardProps) { + return ( +
+

绑定报销

+ + + + +
+ +
+
+ ); +} diff --git a/client/src/pages/invoice-pool/InvoiceDetailPanel.tsx b/client/src/pages/invoice-pool/InvoiceDetailPanel.tsx new file mode 100644 index 00000000..a16399ff --- /dev/null +++ b/client/src/pages/invoice-pool/InvoiceDetailPanel.tsx @@ -0,0 +1,375 @@ +import type { UseMutationResult, UseQueryResult } from "@tanstack/react-query"; +import type { InvoicePoolDetail, InvoiceVerificationRecord } from "../../api/invoicePool"; +import { ConfirmDialog } from "../../components/common/ConfirmDialog"; +import { Button } from "../../components/ui/button"; + +export type InvoiceDetailPanelProps = { + detail: InvoicePoolDetail | undefined; + detailQuery: UseQueryResult; + canEditDetail: boolean; + canVerifyInvoice: boolean; + canUploadInvoice: boolean; + canOverrideDuplicate: boolean; + patchMutation: UseMutationResult; + archiveMutation: UseMutationResult; + unbindMutation: UseMutationResult; + retryOcrMutation: UseMutationResult; + triggerVerificationMutation: UseMutationResult<{ invoiceId: string; verificationId: string; provider: string; triggerMode: string }, Error, string>; + retryVerificationMutation: UseMutationResult<{ invoiceId: string; verificationId: string; status: string }, Error, string>; + overrideDuplicateMutation: UseMutationResult; + verificationQuery: UseQueryResult<{ invoiceId: string; items: InvoiceVerificationRecord[] }, Error>; + editOpen: boolean; + setEditOpen: (open: boolean) => void; + editSetAvailable: boolean; + setEditSetAvailable: (value: boolean) => void; + editRemarks: string; + setEditRemarks: (value: string) => void; + editInvoiceCode: string; + setEditInvoiceCode: (value: string) => void; + editInvoiceNumber: string; + setEditInvoiceNumber: (value: string) => void; + editInvoiceDate: string; + setEditInvoiceDate: (value: string) => void; + editTotalAmount: string; + setEditTotalAmount: (value: string) => void; + confirmAction: { type: string; invoiceId: string } | null; + setConfirmAction: (action: { type: string; invoiceId: string } | null) => void; +}; + +export function InvoiceDetailPanel({ + detail, + canEditDetail, + canVerifyInvoice, + canUploadInvoice, + canOverrideDuplicate, + patchMutation, + archiveMutation, + unbindMutation, + retryOcrMutation, + triggerVerificationMutation, + retryVerificationMutation, + overrideDuplicateMutation, + verificationQuery, + editOpen, + setEditOpen, + editSetAvailable, + setEditSetAvailable, + editRemarks, + setEditRemarks, + editInvoiceCode, + setEditInvoiceCode, + editInvoiceNumber, + setEditInvoiceNumber, + editInvoiceDate, + setEditInvoiceDate, + editTotalAmount, + setEditTotalAmount, + confirmAction, + setConfirmAction, +}: InvoiceDetailPanelProps) { + return ( + <> +
+

详情轮询

+ {detail ? ( + <> +
+
票据ID:{detail.id}
+
状态:{detail.status}
+
+ 发票号: + {detail.invoiceCode || "-"}-{detail.invoiceNumber || "-"} +
+
+ 总金额: + {detail.totalAmount ? new Intl.NumberFormat("zh-CN", { style: "currency", currency: "CNY" }).format(Number(detail.totalAmount)) : "-"} +
+
开票日期:{detail.invoiceDate || "-"}
+
关联报销:{detail.linkedReimbursementId || "-"}
+
+
+ 验真状态: + {detail.verification ? `${detail.verification.status} (${detail.verification.provider})` : "未触发"} +
+ {detail.verification?.resultMessage ? ( +
{detail.verification.resultMessage}
+ ) : null} + {detail.verification?.errorMessage ? ( +
+ error + {detail.verification.errorMessage} +
+ ) : null} +
+ {detail.ocrLastError ?
OCR错误:{detail.ocrLastError}
: null} + {detail.remarks ?
备注:{detail.remarks}
: null} +
+ 判重例外: + {detail.isDuplicateOverride ? "已例外" : "否"} {detail.overrideRefFormNumber ? `(ref=${detail.overrideRefFormNumber})` : ""} +
+
+ +
+ {canVerifyInvoice ? ( + + ) : null} + + {canVerifyInvoice && + detail.verification && + ["failed", "error"].includes(detail.verification.status) ? ( + + ) : null} + + {canEditDetail ? ( + + ) : null} + + {canEditDetail && detail.status === "ocr_failed" ? ( + + ) : null} + + {canEditDetail && canUploadInvoice && detail.status === "bound" ? ( + + ) : null} + + {canEditDetail && detail.status !== "bound" && detail.status !== "archived" ? ( + + ) : null} + + {canOverrideDuplicate && + !detail.isDuplicateOverride && + ["available", "ocr_failed"].includes(detail.status) ? ( + + ) : null} +
+ + {canVerifyInvoice ? ( +
+

验真记录

+ {(verificationQuery.data?.items ?? []).length ? ( +
+ {(verificationQuery.data?.items ?? []).map((record) => ( +
+
+ {record.id} + + {record.status} + +
+
+ 服务商: {record.provider} | + 重试次数: {record.attempts} | + 验真时间: {record.requestedAt || "-"} +
+ {record.resultMessage ?
{record.resultMessage}
: null} + {record.errorMessage ?
{record.errorMessage}
: null} +
+ ))} +
+ ) : ( +

暂无验真记录

+ )} +
+ ) : null} + + {editOpen ? ( +
+

人工修正(仅必要字段)

+ + {detail.status === "ocr_failed" ? ( + + ) : null} + +
+
+ + setEditInvoiceCode(event.target.value)} + value={editInvoiceCode} + /> +
+
+ + setEditInvoiceNumber(event.target.value)} + value={editInvoiceNumber} + /> +
+
+ + setEditInvoiceDate(event.target.value)} + type="date" + value={editInvoiceDate} + /> +
+
+ + setEditTotalAmount(event.target.value)} + value={editTotalAmount} + /> +
+
+ +