优化详情与移动关键页视觉 Wave3(05-27-pc-detail-mobile-visual-wave3)

这个提交包含在:
2026-05-27 20:41:16 +08:00
父节点 2e9e68e614
当前提交 6e1e00f176
共修改 17 个文件,包含 1194 行新增和 173 行删除
+67 -55
查看文件
@@ -961,7 +961,7 @@ export function ReimbursementDetailPage() {
<PageScaffold>
<EntityOverviewHero
title={`报销详情 ${displayCode}`}
subtitle="明细、发票和审批。"
subtitle="状态、票据、审批和付款集中处理。"
meta={(
<>
<StatusBadge status={detail.status} />
@@ -1005,11 +1005,11 @@ export function ReimbursementDetailPage() {
)}
/>
<div className="detail-shell-grid">
<div className="space-y-6">
<SectionPanel title="概览与关联">
<div className="space-y-6" data-testid="reimbursement-overview">
<StatStrip columns={4}>
<div className="detail-workspace-grid">
<div className="space-y-5">
<SectionPanel title="办理摘要">
<div className="space-y-4" data-testid="reimbursement-overview">
<StatStrip columns={4} className="ops-status-strip">
<MetricCard
title="关联项目"
value={
@@ -1026,20 +1026,32 @@ export function ReimbursementDetailPage() {
/>
</StatStrip>
<div className="rounded-lg border border-zinc-200 p-4 text-sm" data-testid="reimbursement-summary">
<div>报销编号:{displayCode}</div>
<div>报销日期:{detail.reimbursementDate ?? "-"}</div>
<div>金额:{detail.totalAmount ?? "-"}</div>
<div>提交人:{detail.submitter?.name ?? "-"}</div>
<div>报销人:{detail.owner?.name ?? "-"}</div>
<div>说明:{detail.description || "-"}</div>
<div className="detail-command-panel" data-testid="reimbursement-summary">
<div className="detail-fact-grid">
<div className="detail-fact-card">
<div className="detail-fact-card__label">报销编号</div>
<div className="detail-fact-card__value">{displayCode}</div>
</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">报销日期</div>
<div className="detail-fact-card__value">{detail.reimbursementDate ?? "-"}</div>
</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">提交人 / 报销人</div>
<div className="detail-fact-card__value">{detail.submitter?.name ?? "-"} / {detail.owner?.name ?? "-"}</div>
</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">说明</div>
<div className="detail-fact-card__value">{detail.description || "-"}</div>
</div>
</div>
</div>
<div className="rounded-lg border border-zinc-200 p-4 text-sm" data-testid="reimbursement-payment-status">
<div className="detail-command-panel" data-testid="reimbursement-payment-status">
<div className="flex items-center justify-between gap-3">
<div>
<h4 className="text-base font-semibold text-zinc-900">付款状态</h4>
<p className="text-xs text-zinc-500">付款备注写入审计日志,凭证、日期和操作人用于财务台账核对。</p>
<p className="text-xs text-zinc-500">凭证、日期和操作人用于财务台账核对。</p>
</div>
<span
className={`inline-flex rounded-full border px-2 py-0.5 text-xs font-medium ${
@@ -1051,22 +1063,22 @@ export function ReimbursementDetailPage() {
{detail.status === "paid" ? "已付款" : "未付款"}
</span>
</div>
<div className="mt-3 grid gap-3 md:grid-cols-2 xl:grid-cols-4">
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2">
<div className="text-xs text-zinc-500">付款日期</div>
<div className="mt-1 font-medium text-zinc-900">{payment.paymentDate ?? "-"}</div>
<div className="detail-fact-grid mt-3 xl:grid-cols-4">
<div className="detail-fact-card">
<div className="detail-fact-card__label">付款日期</div>
<div className="detail-fact-card__value">{payment.paymentDate ?? "-"}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2">
<div className="text-xs text-zinc-500">付款凭证号</div>
<div className="mt-1 font-medium text-zinc-900">{payment.paymentVoucherNo ?? "-"}</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">付款凭证号</div>
<div className="detail-fact-card__value">{payment.paymentVoucherNo ?? "-"}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2">
<div className="text-xs text-zinc-500">付款渠道</div>
<div className="mt-1 font-medium text-zinc-900">{payment.paymentChannel ?? "-"}</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">付款渠道</div>
<div className="detail-fact-card__value">{payment.paymentChannel ?? "-"}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-zinc-50 px-3 py-2">
<div className="text-xs text-zinc-500">操作人 / 时间</div>
<div className="mt-1 font-medium text-zinc-900">
<div className="detail-fact-card">
<div className="detail-fact-card__label">操作人 / 时间</div>
<div className="detail-fact-card__value">
{payment.paidBy?.name ?? "-"} · {formatDateTime(payment.paidAt)}
</div>
</div>
@@ -1110,11 +1122,11 @@ export function ReimbursementDetailPage() {
</div>
) : null}
<div className="rounded-lg border border-zinc-200 p-4 text-sm">
<div className="detail-command-panel">
<div className="flex items-center justify-between gap-3">
<div>
<h4 className="text-base font-semibold text-zinc-900">关联项目</h4>
<p className="text-xs text-zinc-500">报销可选挂到项目,用于项目执行面板汇总。</p>
<p className="text-xs text-zinc-500">用于项目执行面板汇总。</p>
</div>
{detail.project ? (
<Link to={`/projects/${detail.project.id}`} className="text-sm text-primary hover:underline">
@@ -1179,7 +1191,7 @@ export function ReimbursementDetailPage() {
{(detail.attachments ?? []).length ? (
<div className="space-y-2">
{(detail.attachments ?? []).map((attachment) => (
<div key={attachment.id} className="flex flex-col gap-3 rounded-lg border border-zinc-200 px-4 py-3 md:flex-row md:items-center md:justify-between">
<div key={attachment.id} className="detail-evidence-card flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div className="min-w-0">
<div className="truncate font-medium text-zinc-900">{attachment.originalName}</div>
<div className="text-xs text-zinc-500">
@@ -1254,7 +1266,7 @@ export function ReimbursementDetailPage() {
<p className="text-sm text-zinc-500">正在刷新费用标准校验结果...</p>
) : null}
<div className="rounded-lg border border-zinc-200">
<div className="ops-table-focus overflow-x-auto border border-zinc-200">
<Table data-testid="reimbursement-items-table">
<TableHeader>
<tr>
@@ -1401,7 +1413,7 @@ export function ReimbursementDetailPage() {
const invoiceHint = resolveInvoiceNextStepText(item);
return (
<div
className="rounded-lg border border-zinc-200 px-4 py-3"
className="detail-evidence-card"
key={item.id}
>
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
@@ -1421,7 +1433,7 @@ export function ReimbursementDetailPage() {
</div>
<div className="flex flex-col items-start gap-2 md:items-end">
<div className="flex items-center gap-2">
<span className="rounded-full bg-zinc-100 px-2 py-1 text-xs text-zinc-600">
<span className="badge-neutral">
{resolveInvoiceStatusLabel(item.status)}
</span>
{canUnbindInvoices ? (
@@ -1455,7 +1467,7 @@ export function ReimbursementDetailPage() {
<SectionPanel title="预算与审核提示">
<div className="space-y-6">
{budgetHint ? (
<div className="rounded-lg border border-zinc-200 p-4 text-sm" data-testid="reimbursement-budget-hint">
<div className="detail-command-panel text-sm" data-testid="reimbursement-budget-hint">
<h3 className="text-base font-semibold text-zinc-900">预算提示</h3>
<div className="mt-3">预算池:{budgetHint.budgetCode} / {budgetHint.budgetName}</div>
<div>
@@ -1519,22 +1531,22 @@ export function ReimbursementDetailPage() {
</p>
) : null}
<div className="grid gap-3 text-sm md:grid-cols-2 xl:grid-cols-4" data-testid="reimbursement-erp-export">
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3">
<div className="text-xs text-zinc-500">导出状态</div>
<div className="mt-1 font-medium text-zinc-900">{erpSyncStatus}</div>
<div className="detail-fact-grid text-sm xl:grid-cols-4" data-testid="reimbursement-erp-export">
<div className="detail-fact-card">
<div className="detail-fact-card__label">导出状态</div>
<div className="detail-fact-card__value">{erpSyncStatus}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3">
<div className="text-xs text-zinc-500">最近导出</div>
<div className="mt-1 font-medium text-zinc-900">{formatDateTime(erpPreviewQuery.data?.exportState.exportedAt ?? null)}</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">最近导出</div>
<div className="detail-fact-card__value">{formatDateTime(erpPreviewQuery.data?.exportState.exportedAt ?? null)}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3">
<div className="text-xs text-zinc-500">导出任务</div>
<div className="mt-1 break-all font-medium text-zinc-900">{erpPreviewQuery.data?.exportState.lastOutboxId ?? "-"}</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">导出任务</div>
<div className="detail-fact-card__value break-all">{erpPreviewQuery.data?.exportState.lastOutboxId ?? "-"}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3">
<div className="text-xs text-zinc-500">外部凭证号</div>
<div className="mt-1 break-all font-medium text-zinc-900">{erpPreviewQuery.data?.exportState.externalId ?? "-"}</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">外部凭证号</div>
<div className="detail-fact-card__value break-all">{erpPreviewQuery.data?.exportState.externalId ?? "-"}</div>
</div>
</div>
@@ -1549,7 +1561,7 @@ export function ReimbursementDetailPage() {
</SectionPanel>
</div>
<div className="space-y-6">
<div className="space-y-5">
<DetailSideMeta title="报销摘要">
<div>报销编号:{displayCode}</div>
<div>报销人:{detail.owner?.name ?? "-"}</div>
@@ -1565,16 +1577,16 @@ export function ReimbursementDetailPage() {
title="审批进度"
description={`当前位于第 ${detail.workflowProgress.currentStepIndex + 1} / ${detail.workflowProgress.totalSteps} 步。`}
>
<div className="space-y-3" data-testid={TEST_IDS.reimbursement.workflowProgress}>
<div className="detail-timeline" data-testid={TEST_IDS.reimbursement.workflowProgress}>
{detail.workflowProgress.steps.map((step, index) => (
<div
key={step.id}
className={
step.status === "current"
? "rounded-lg border border-primary-200 bg-primary-50 px-3 py-2"
? "detail-timeline-item detail-timeline-item--current"
: step.status === "completed"
? "rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2"
: "rounded-lg border border-zinc-200 bg-white px-3 py-2"
? "detail-timeline-item detail-timeline-item--done"
: "detail-timeline-item"
}
>
<div className="flex items-center justify-between gap-3 text-sm">
@@ -1593,9 +1605,9 @@ export function ReimbursementDetailPage() {
<DetailSideMeta title="审批记录">
{detail.approvalHistory.length ? (
<div className="space-y-3" data-testid="reimbursement-approval-history">
<div className="detail-timeline" data-testid="reimbursement-approval-history">
{detail.approvalHistory.map((item) => (
<div key={item.id} className="rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm">
<div key={item.id} className="detail-timeline-item">
<div className="flex items-center justify-between gap-3">
<span className="font-medium text-zinc-900">{workflowActionLabel(item.action)}</span>
<span className="text-xs text-zinc-500">{formatDateTime(item.createdAt)}</span>