优化财务与管理页视觉 Wave4(05-27-pc-finance-admin-visual-wave4)
这个提交包含在:
@@ -123,10 +123,18 @@ export function PaymentBatchesPage() {
|
||||
});
|
||||
|
||||
const selectedBatchId = searchParams.get("id") ?? "";
|
||||
const batches = useMemo(() => listQuery.data?.items ?? [], [listQuery.data?.items]);
|
||||
const selectedBatch = useMemo<PaymentBatch | null>(() => {
|
||||
const items = listQuery.data?.items ?? [];
|
||||
return items.find((item) => item.id === selectedBatchId) ?? items[0] ?? null;
|
||||
}, [listQuery.data?.items, selectedBatchId]);
|
||||
return batches.find((item) => item.id === selectedBatchId) ?? batches[0] ?? null;
|
||||
}, [batches, selectedBatchId]);
|
||||
const batchStats = useMemo(() => {
|
||||
return {
|
||||
total: batches.length,
|
||||
actionable: batches.filter((item) => ["draft", "reviewed", "failed"].includes(item.status)).length,
|
||||
highRisk: batches.filter((item) => item.highRisk).length,
|
||||
failed: batches.filter((item) => item.status === "failed").length,
|
||||
};
|
||||
}, [batches]);
|
||||
|
||||
const receiptAttachmentsQuery = useQuery({
|
||||
queryKey: ["payment-batch-receipts", selectedBatch?.id],
|
||||
@@ -332,12 +340,35 @@ export function PaymentBatchesPage() {
|
||||
|
||||
return (
|
||||
<ListWorkspace>
|
||||
<WorkspaceHeader title="付款批次" subtitle="线下银行付款后的 OA 复核、回单留存和已付款标记;高风险批次达到 5 万元后需完成二次复核。" />
|
||||
<WorkspaceHeader title="付款批次" subtitle="复核、回单和已付款标记集中处理。" />
|
||||
|
||||
{error ? <div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">{error}</div> : null}
|
||||
|
||||
<div className="grid gap-6 xl:grid-cols-[360px,1fr]">
|
||||
<div className="card-std space-y-4 p-4 md:p-5">
|
||||
<div className="finance-status-strip" data-testid="payment-batches-summary">
|
||||
<div className="finance-metric-card">
|
||||
<div className="finance-metric-card__label">批次数</div>
|
||||
<div className="finance-metric-card__value">{batchStats.total}</div>
|
||||
</div>
|
||||
<div className="finance-metric-card">
|
||||
<div className="finance-metric-card__label">可处理</div>
|
||||
<div className="finance-metric-card__value">{batchStats.actionable}</div>
|
||||
</div>
|
||||
<div className="finance-metric-card">
|
||||
<div className="finance-metric-card__label">高风险</div>
|
||||
<div className="finance-metric-card__value">{batchStats.highRisk}</div>
|
||||
</div>
|
||||
<div className="finance-metric-card">
|
||||
<div className="finance-metric-card__label">失败批次</div>
|
||||
<div className="finance-metric-card__value">{batchStats.failed}</div>
|
||||
</div>
|
||||
<div className="finance-metric-card">
|
||||
<div className="finance-metric-card__label">当前批次</div>
|
||||
<div className="finance-metric-card__value text-base">{selectedBatch?.batchNo ?? "-"}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="finance-workspace-grid">
|
||||
<div className="finance-command-panel space-y-4">
|
||||
<h3 className="text-base font-semibold text-zinc-900">新建批次</h3>
|
||||
{!canManageBatch ? (
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm text-zinc-600">
|
||||
@@ -411,15 +442,15 @@ export function PaymentBatchesPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="card-std p-4 md:p-5">
|
||||
<div className="space-y-5">
|
||||
<div className="finance-command-panel">
|
||||
<h3 className="mb-3 text-base font-semibold text-zinc-900">批次列表</h3>
|
||||
<div className="space-y-2">
|
||||
{(listQuery.data?.items ?? []).map((item) => (
|
||||
{batches.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
className={selectedBatch?.id === item.id ? "w-full rounded-lg border border-primary bg-primary/5 px-3 py-3 text-left" : "w-full rounded-lg border border-zinc-200 px-3 py-3 text-left hover:bg-zinc-50"}
|
||||
className={selectedBatch?.id === item.id ? "finance-list-card finance-list-card--active" : "finance-list-card"}
|
||||
onClick={() => {
|
||||
const next = new URLSearchParams(searchParams);
|
||||
next.set("id", item.id);
|
||||
@@ -443,8 +474,8 @@ export function PaymentBatchesPage() {
|
||||
</div>
|
||||
|
||||
{selectedBatch ? (
|
||||
<div className="space-y-6">
|
||||
<div className="card-std space-y-3 p-4 md:p-5">
|
||||
<div className="space-y-5">
|
||||
<div className="finance-command-panel space-y-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<div className="text-xs text-zinc-500">{selectedBatch.batchNo}</div>
|
||||
@@ -499,13 +530,13 @@ export function PaymentBatchesPage() {
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3 text-sm md:grid-cols-3 xl:grid-cols-6">
|
||||
<div>状态:{batchStatusLabel(selectedBatch.status)}</div>
|
||||
<div>实体:{entityTypeLabel(selectedBatch.entityType)}</div>
|
||||
<div>总金额:{selectedBatch.totalAmount}</div>
|
||||
<div>默认日期:{formatDisplayDate(selectedBatch.defaultPaymentDate)}</div>
|
||||
<div>默认渠道:{paymentChannelLabel(selectedBatch.defaultPaymentChannel)}</div>
|
||||
<div>复核进度:{selectedBatch.reviewRequirement.completedReviewCount}/{selectedBatch.reviewRequirement.requiredReviewCount}</div>
|
||||
<div className="detail-fact-grid text-sm xl:grid-cols-6">
|
||||
<div className="detail-fact-card"><div className="detail-fact-card__label">状态</div><div className="detail-fact-card__value">{batchStatusLabel(selectedBatch.status)}</div></div>
|
||||
<div className="detail-fact-card"><div className="detail-fact-card__label">实体</div><div className="detail-fact-card__value">{entityTypeLabel(selectedBatch.entityType)}</div></div>
|
||||
<div className="detail-fact-card"><div className="detail-fact-card__label">总金额</div><div className="detail-fact-card__value">{selectedBatch.totalAmount}</div></div>
|
||||
<div className="detail-fact-card"><div className="detail-fact-card__label">默认日期</div><div className="detail-fact-card__value">{formatDisplayDate(selectedBatch.defaultPaymentDate)}</div></div>
|
||||
<div className="detail-fact-card"><div className="detail-fact-card__label">默认渠道</div><div className="detail-fact-card__value">{paymentChannelLabel(selectedBatch.defaultPaymentChannel)}</div></div>
|
||||
<div className="detail-fact-card"><div className="detail-fact-card__label">复核进度</div><div className="detail-fact-card__value">{selectedBatch.reviewRequirement.completedReviewCount}/{selectedBatch.reviewRequirement.requiredReviewCount}</div></div>
|
||||
</div>
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-3 text-sm text-zinc-600">
|
||||
当前要求:{selectedBatch.reviewRequirement.requiredReviewCount === 2 ? `批次金额达到 ${selectedBatch.reviewRequirement.thresholdAmount},需两轮复核` : "单轮复核后可执行"}。
|
||||
@@ -515,7 +546,7 @@ export function PaymentBatchesPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-std space-y-4 p-4 md:p-5">
|
||||
<div className="finance-command-panel space-y-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-zinc-900">银行回单附件</h3>
|
||||
@@ -562,7 +593,7 @@ export function PaymentBatchesPage() {
|
||||
) : null}
|
||||
<div className="space-y-2">
|
||||
{(receiptAttachmentsQuery.data?.items ?? []).map((attachment) => (
|
||||
<div key={attachment.id} className="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-zinc-200 px-3 py-3">
|
||||
<div key={attachment.id} className="detail-evidence-card flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-medium text-zinc-900">{attachment.originalName}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
@@ -602,11 +633,11 @@ export function PaymentBatchesPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-std space-y-4 p-4 md:p-5">
|
||||
<div className="finance-command-panel space-y-4">
|
||||
<h3 className="text-base font-semibold text-zinc-900">批次项</h3>
|
||||
<div className="space-y-4">
|
||||
{selectedBatch.items.map((item) => (
|
||||
<div key={item.id} className="rounded-lg border border-zinc-200 p-4">
|
||||
<div key={item.id} className="detail-evidence-card">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<div className="font-medium text-zinc-900">{item.entityCode}</div>
|
||||
|
||||
在新工单中引用
屏蔽一个用户