统一业务列表 Ant Design 风格(05-31-unify-business-list-ant-design)
这个提交包含在:
@@ -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">
|
||||
|
||||
在新工单中引用
屏蔽一个用户