DG9 统一全站 UI 布局样板迁移

关联 Issue: DG9-0..DG9-12
这个提交包含在:
2026-05-14 00:52:59 +08:00
父节点 3ac5c785b1
当前提交 4b14c925b6
共修改 30 个文件,包含 743 行新增和 437 行删除
+18 -33
查看文件
@@ -22,7 +22,6 @@ import {
withdrawReimbursement,
linkReimbursementProject,
type ReimbursementItem,
type ReimbursementStatus,
} from "../api/reimbursements";
import { remindWorkflowTask } from "../api/workflow";
import {
@@ -51,6 +50,8 @@ import { Input } from "../components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table";
import { DetailSideMeta } from "../components/patterns/DetailSideMeta";
import { EntityOverviewHero } from "../components/patterns/EntityOverviewHero";
import { StatStrip } from "../components/patterns/ListWorkspace";
import { MetricCard } from "../components/patterns/MetricCard";
import { PageScaffold } from "../components/patterns/PageScaffold";
import { SectionPanel } from "../components/patterns/SectionPanel";
import {
@@ -73,15 +74,6 @@ import { buildPrecheckDialogMessage } from "../lib/precheck";
type ItemEditorState = { mode: "create" } | { mode: "edit"; itemId: string } | null;
type SelectedInvoiceMap = Record<string, string>;
const REIMBURSEMENT_STATUS_LABEL: Record<ReimbursementStatus, string> = {
draft: "草稿",
reviewing: "审批中",
approved: "已通过",
paid: "已支付",
rejected: "已驳回",
cancelled: "已取消",
};
function formatDateTime(value: string | null) {
if (!value) return "-";
return value.slice(0, 19).replace("T", " ");
@@ -99,10 +91,6 @@ function buildActionErrorMessage(error: unknown, fallbackMessage: string) {
return fallbackMessage;
}
function reimbursementStatusLabel(status: ReimbursementStatus) {
return REIMBURSEMENT_STATUS_LABEL[status] ?? "未知状态";
}
function workflowActionLabel(action: string) {
const labels: Record<string, string> = {
start: "提交",
@@ -1009,27 +997,24 @@ export function ReimbursementDetailPage() {
<div className="space-y-6">
<SectionPanel title="概览与关联">
<div className="space-y-6" data-testid="reimbursement-overview">
<div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
<div className="rounded-lg border border-zinc-200 px-4 py-3 text-sm">
<div className="text-zinc-500">当前状态</div>
<div className="mt-2"><StatusBadge status={detail.status} /></div>
</div>
<div className="rounded-lg border border-zinc-200 px-4 py-3 text-sm">
<div className="text-zinc-500">关联项目</div>
<div className="mt-2 font-medium text-zinc-900">{detail.project ? `${detail.project.projectNo} · ${detail.project.name}` : "未关联"}</div>
</div>
<div className="rounded-lg border border-zinc-200 px-4 py-3 text-sm">
<div className="text-zinc-500">明细数</div>
<div className="mt-2 font-medium text-zinc-900">{detail.items.length}</div>
</div>
<div className="rounded-lg border border-zinc-200 px-4 py-3 text-sm">
<div className="text-zinc-500">发票</div>
<div className="mt-2 font-medium text-zinc-900">{detail.invoices.length}</div>
</div>
</div>
<StatStrip columns={4}>
<MetricCard
title="关联项目"
value={
<span className="text-base">
{detail.project ? `${detail.project.projectNo} · ${detail.project.name}` : "未关联"}
</span>
}
/>
<MetricCard title="明细数" value={detail.items.length} />
<MetricCard title="发票" value={detail.invoices.length} />
<MetricCard
title="付款状态"
value={<span className="text-base">{isPaidReimbursement ? "已付款" : "未付款"}</span>}
/>
</StatStrip>
<div className="rounded-lg border border-zinc-200 p-4 text-sm" data-testid="reimbursement-summary">
<div>状态:{reimbursementStatusLabel(detail.status)}</div>
<div>报销编号:{displayCode}</div>
<div>报销日期:{detail.reimbursementDate ?? "-"}</div>
<div>金额:{detail.totalAmount ?? "-"}</div>