R92 A3-A4 优化发票上传状态与批量上传

这个提交包含在:
2026-05-05 22:16:35 +08:00
父节点 abba3371dd
当前提交 9d8c7e0478
共修改 6 个文件,包含 385 行新增和 122 行删除
@@ -4,6 +4,7 @@ import { StatusBadge } from "../../components/common/StatusBadge";
import { TableSkeleton } from "../../components/common/TableSkeleton";
import { Pagination } from "../../components/common/Pagination";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../components/ui/dropdown-menu";
import { Button } from "../../components/ui/button";
import { formatInvoiceOcrErrorMessage } from "../../lib/invoiceOcr";
export type InvoiceListCardProps = {
@@ -34,9 +35,9 @@ export function InvoiceListCard({
}: InvoiceListCardProps) {
return (
<div className="card-std p-6 space-y-4">
<div className="flex items-center justify-between">
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<h3 className="card-title">筛选与列表</h3>
<div className="flex items-center gap-2">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
<select
className="select-sm"
data-testid="invoice-filter-status"
@@ -72,7 +73,72 @@ export function InvoiceListCard({
{listQuery.isLoading ? <TableSkeleton columns={5} rows={5} /> : null}
{!listQuery.isLoading ? (
<>
<div className="table-container">
<div className="space-y-3 md:hidden" data-testid="invoice-pool-mobile-list">
{(listQuery.data?.items ?? []).length ? (
listQuery.data?.items.map((item) => (
<article className="rounded-xl border border-zinc-200 bg-white p-4 shadow-sm" data-testid="invoice-mobile-row" key={item.id}>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="truncate text-sm font-semibold text-zinc-900">{item.originalName || item.id}</div>
<div className="mt-1 font-mono text-xs text-zinc-500">{item.id.slice(0, 8)}</div>
</div>
<StatusBadge className="shrink-0" status={item.status} />
</div>
<div className="mt-3 grid grid-cols-2 gap-3 text-sm">
<div>
<div className="text-xs text-zinc-500">发票号码</div>
<div className="truncate font-medium text-zinc-900">
{item.invoiceCode || "-"}-{item.invoiceNumber || "-"}
</div>
</div>
<div>
<div className="text-xs text-zinc-500">金额</div>
<div className="font-medium text-zinc-900">{item.totalAmount ?? "-"}</div>
</div>
</div>
<div className="mt-3 rounded-lg bg-zinc-50 px-3 py-2 text-xs text-zinc-600">
{item.status === "pending_ocr"
? `OCR 识别中,重试 ${item.ocrAttempts} 次`
: item.status === "ocr_failed"
? `OCR 失败:${formatInvoiceOcrErrorMessage(item.ocrLastError)}`
: "OCR 已完成或无需处理"}
</div>
<div className="mt-3 grid grid-cols-2 gap-2">
<Button
className="min-h-10"
data-testid="invoice-mobile-detail-button"
onClick={() => setDetailInvoiceId(item.id)}
size="sm"
type="button"
variant="secondary"
>
查看详情
</Button>
<Button
className="min-h-10"
data-testid="invoice-mobile-preview-button"
disabled={signedUrlMutation.isPending}
onClick={() => signedUrlMutation.mutate(item.id)}
size="sm"
type="button"
variant="secondary"
>
预览
</Button>
</div>
</article>
))
) : (
<div className="rounded-xl border border-dashed border-zinc-300 px-4 py-8 text-center text-sm text-zinc-500">
暂无票据数据
</div>
)}
</div>
<div className="table-container hidden md:block">
<table className="w-full text-left text-sm text-zinc-600" data-testid="invoice-pool-table">
<thead className="bg-zinc-50/50">
<tr>