统一业务列表 Ant Design 风格(05-31-unify-business-list-ant-design)

这个提交包含在:
2026-05-31 11:39:46 +08:00
父节点 59c942da54
当前提交 40c23c5cf3
共修改 19 个文件,包含 905 行新增和 578 行删除
+103 -119
查看文件
@@ -45,7 +45,12 @@ import { Button } from "../components/ui/button";
import { Input } from "../components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table";
import { Textarea } from "../components/ui/textarea";
import { FilterBar, ListWorkspace, PrimaryTable, WorkspaceHeader } from "../components/patterns/ListWorkspace";
import { ListWorkspace, WorkspaceHeader } from "../components/patterns/ListWorkspace";
import {
BusinessListToolbar,
BusinessMobileList,
BusinessTableFrame,
} from "../components/patterns/BusinessListTemplate";
import { SectionPanel } from "../components/patterns/SectionPanel";
import { Tabs, TabsList, TabsTrigger } from "../components/ui/tabs";
import type { AttachmentGovernanceFields } from "../lib/attachmentGovernance";
@@ -842,9 +847,97 @@ export function PurchaseRequestsPage() {
{!isPurchaseDetailMode ? (
<>
<FilterBar className="ops-command-bar space-y-3 !p-3 md:!p-3.5">
<div className="ops-command-bar__row">
<div className="min-w-0">
<BusinessListToolbar
actions={
<span className="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-medium text-slate-600">
{mine ? "我相关" : "全范围"}
</span>
}
advanced={showAdvancedTools ? (
<div className="grid gap-3 md:grid-cols-3 xl:grid-cols-4" data-testid="purchase-ledger-tools-panel">
<select
className="select-std"
value={supplierFilter}
onChange={(event) => {
setSupplierFilter(event.target.value);
setPage(1);
}}
>
<option value="">全部供应商</option>
{(suppliersQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
<select
className="select-std"
value={legalEntityFilter}
onChange={(event) => {
const nextValue = event.target.value;
setLegalEntityFilter(nextValue);
if (costCenterFilter && !filterCostCenters.some((item) => item.id === costCenterFilter && item.legalEntityId === nextValue)) {
setCostCenterFilter("");
}
setPage(1);
}}
>
<option value="">全部法体</option>
{(legalEntitiesQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
<select
className="select-std"
value={costCenterFilter}
onChange={(event) => {
setCostCenterFilter(event.target.value);
setPage(1);
}}
>
<option value="">全部成本中心</option>
{filterCostCenters.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
{canViewAll ? (
<div className="flex items-center gap-2">
<Button
data-testid="purchase-scope-mine"
size="sm"
type="button"
variant={mine ? "default" : "secondary"}
onClick={() => {
setMine(true);
setPage(1);
}}
>
我相关
</Button>
<Button
data-testid="purchase-scope-all"
size="sm"
type="button"
variant={!mine ? "default" : "secondary"}
onClick={() => {
setMine(false);
setPage(1);
}}
>
全范围
</Button>
</div>
) : null}
</div>
) : null}
>
<Tabs
data-testid="purchase-status-tabs"
value={status}
@@ -865,113 +958,7 @@ export function PurchaseRequestsPage() {
))}
</TabsList>
</Tabs>
</div>
<div className="ops-command-bar__actions">
<span className="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-medium text-slate-600">
{mine ? "我相关" : "全范围"}
</span>
</div>
</div>
{showAdvancedTools ? (
<div className="rounded-lg border border-slate-200 bg-slate-50/70 p-3" data-testid="purchase-ledger-tools-panel">
<div className="grid gap-3 md:grid-cols-3 xl:grid-cols-4">
<select
className="select-std"
value={supplierFilter}
onChange={(event) => {
setSupplierFilter(event.target.value);
setPage(1);
}}
>
<option value="">全部供应商</option>
{(suppliersQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
<select
className="select-std"
value={legalEntityFilter}
onChange={(event) => {
const nextValue = event.target.value;
setLegalEntityFilter(nextValue);
if (costCenterFilter && !filterCostCenters.some((item) => item.id === costCenterFilter && item.legalEntityId === nextValue)) {
setCostCenterFilter("");
}
setPage(1);
}}
>
<option value="">全部法体</option>
{(legalEntitiesQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
<select
className="select-std"
value={costCenterFilter}
onChange={(event) => {
setCostCenterFilter(event.target.value);
setPage(1);
}}
>
<option value="">全部成本中心</option>
{filterCostCenters.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
{canViewAll ? (
<div className="flex items-center gap-2">
<Button
data-testid="purchase-scope-mine"
size="sm"
type="button"
variant={mine ? "default" : "secondary"}
onClick={() => {
setMine(true);
setPage(1);
}}
>
我相关
</Button>
<Button
data-testid="purchase-scope-all"
size="sm"
type="button"
variant={!mine ? "default" : "secondary"}
onClick={() => {
setMine(false);
setPage(1);
}}
>
全范围
</Button>
</div>
) : (
<div className="rounded-lg bg-white px-3 py-2 text-sm text-zinc-500">
当前仅显示与你相关的采购。
</div>
)}
</div>
</div>
) : null}
</FilterBar>
<div className="grid gap-6">
<div className="card-std space-y-4 p-4 md:p-5">
<div className="flex items-center justify-between">
<h3 className="text-base font-semibold text-zinc-900">采购单列表</h3>
<span className="text-sm text-zinc-500">共 {listQuery.data?.total ?? 0} 条</span>
</div>
</BusinessListToolbar>
{listQuery.isLoading ? <TableSkeleton columns={4} rows={6} /> : null}
@@ -983,7 +970,7 @@ export function PurchaseRequestsPage() {
{!listQuery.isLoading && !listQuery.isError ? (
<>
<div className="space-y-3 md:hidden">
<BusinessMobileList>
{purchaseItems.length ? (
purchaseItems.map((item) => {
const evidenceHint = resolvePurchaseEvidenceHint(item);
@@ -1033,13 +1020,13 @@ export function PurchaseRequestsPage() {
);
})
) : (
<div className="rounded-lg border border-zinc-200 p-4 text-sm text-zinc-500">
<div className="rounded-lg border border-zinc-200 bg-white p-4 text-sm text-zinc-500">
<div className="font-semibold text-zinc-900">{emptyState.title}</div>
<div className="mt-1 leading-6">{emptyState.description}</div>
</div>
)}
</div>
<PrimaryTable className="hidden md:block">
</BusinessMobileList>
<BusinessTableFrame>
<Table data-testid="purchase-requests-table">
<TableHeader>
<tr>
@@ -1087,7 +1074,6 @@ export function PurchaseRequestsPage() {
) : (
<span className="text-zinc-500">未关联项目</span>
)}
<div className="mt-1 text-xs text-zinc-500">合同:详情页登记归档摘要</div>
</TableCell>
<TableCell>
<StatusBadge status={item.status} />
@@ -1122,7 +1108,7 @@ export function PurchaseRequestsPage() {
)}
</TableBody>
</Table>
</PrimaryTable>
</BusinessTableFrame>
</>
) : null}
@@ -1132,8 +1118,6 @@ export function PurchaseRequestsPage() {
onPageChange={setPage}
testIdPrefix="purchase-requests"
/>
</div>
</div>
</>
) : (
<div className="card-std space-y-5 p-4 md:p-5">