refactor(ui): [UI7-1] 为其余配置与边缘页面应用新版空壳布局

这个提交包含在:
2026-03-06 09:48:11 +08:00
父节点 c632648e16
当前提交 62fd2a5398
共修改 12 个文件,包含 229 行新增和 234 行删除
+83 -81
查看文件
@@ -10,6 +10,11 @@ import { PageHeader } from "../components/common/PageHeader";
import { Button } from "../components/ui/button";
import { Card } from "../components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table";
import { toast } from "sonner";
import { TableSkeleton } from "../components/common/TableSkeleton";
import { StatusBadge } from "../components/common/StatusBadge";
import { ConfirmDialog } from "../components/common/ConfirmDialog";
import { Pagination } from "../components/common/Pagination";
const PAGE_SIZE = 20;
@@ -42,6 +47,7 @@ export function BudgetsPage() {
const [warningThresholdPercent, setWarningThresholdPercent] = useState("80");
const [formDepartmentId, setFormDepartmentId] = useState("");
const [error, setError] = useState("");
const [confirmArchiveId, setConfirmArchiveId] = useState<{ id: string; code: string } | null>(null);
const queryInput = useMemo<BudgetListQuery>(
() => ({
@@ -87,9 +93,11 @@ export function BudgetsPage() {
setWarningThresholdPercent("80");
setFormDepartmentId("");
setError("");
toast.success("预算池已创建");
},
onError: (mutationError) => {
setError(resolveError(mutationError));
toast.error(resolveError(mutationError));
},
});
@@ -98,9 +106,11 @@ export function BudgetsPage() {
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["budgets"] });
setError("");
toast.success("已归档");
},
onError: (mutationError) => {
setError(resolveError(mutationError));
toast.error(resolveError(mutationError));
},
});
@@ -246,89 +256,81 @@ export function BudgetsPage() {
<Card className="space-y-3 p-6">
<h3 className="text-base font-semibold text-slate-900">预算池列表</h3>
<div className="rounded-lg border border-slate-200">
<Table>
<TableHeader>
<TableRow>
<TableHead>编码</TableHead>
<TableHead>名称</TableHead>
<TableHead>状态</TableHead>
<TableHead>期间</TableHead>
<TableHead>额度</TableHead>
<TableHead>预警阈值</TableHead>
<TableHead className="w-28">操作</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{(budgetsQuery.data?.items ?? []).length ? (
(budgetsQuery.data?.items ?? []).map((item) => (
<TableRow key={item.id}>
<TableCell className="font-medium text-slate-900">{item.code}</TableCell>
<TableCell>{item.name}</TableCell>
<TableCell>{item.status}</TableCell>
<TableCell>
{(item.periodStart ?? "-")} ~ {(item.periodEnd ?? "-")}
</TableCell>
<TableCell>
{item.usedAmount} / {item.allocatedAmount}
<div className="text-xs text-slate-500">剩余 {item.remainingAmount}</div>
</TableCell>
<TableCell>{item.warningThresholdPercent}%</TableCell>
<TableCell>
{canManageBudget && item.status !== "archived" ? (
<Button
size="sm"
type="button"
variant="secondary"
disabled={archiveMutation.isPending}
onClick={() => {
if (!window.confirm(`确认归档预算池 ${item.code} ?`)) return;
archiveMutation.mutate(item.id);
}}
>
归档
</Button>
) : (
<span className="text-xs text-slate-400">-</span>
)}
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell className="py-3 text-slate-500" colSpan={7}>
暂无预算池
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{budgetsQuery.isLoading ? <TableSkeleton columns={7} rows={4} /> : null}
<div className="flex items-center justify-between text-sm">
<span>
第 {page} / {totalPages} 页
</span>
<div className="flex gap-2">
<Button
variant="secondary"
type="button"
disabled={page <= 1}
onClick={() => setPage((current) => Math.max(1, current - 1))}
>
上一页
</Button>
<Button
variant="secondary"
type="button"
disabled={page >= totalPages}
onClick={() => setPage((current) => Math.min(totalPages, current + 1))}
>
下一页
</Button>
</div>
</div>
{!budgetsQuery.isLoading ? (
<>
<div className="rounded-lg border border-slate-200">
<Table>
<TableHeader>
<TableRow>
<TableHead>编码</TableHead>
<TableHead>名称</TableHead>
<TableHead>状态</TableHead>
<TableHead>期间</TableHead>
<TableHead>额度</TableHead>
<TableHead>预警阈值</TableHead>
<TableHead className="w-28">操作</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{(budgetsQuery.data?.items ?? []).length ? (
(budgetsQuery.data?.items ?? []).map((item) => (
<TableRow key={item.id}>
<TableCell className="font-medium text-slate-900">{item.code}</TableCell>
<TableCell>{item.name}</TableCell>
<TableCell><StatusBadge status={item.status} /></TableCell>
<TableCell>
{(item.periodStart ?? "-")} ~ {(item.periodEnd ?? "-")}
</TableCell>
<TableCell>
{item.usedAmount} / {item.allocatedAmount}
<div className="text-xs text-slate-500">剩余 {item.remainingAmount}</div>
</TableCell>
<TableCell>{item.warningThresholdPercent}%</TableCell>
<TableCell>
{canManageBudget && item.status !== "archived" ? (
<Button
size="sm"
type="button"
variant="secondary"
disabled={archiveMutation.isPending}
onClick={() => setConfirmArchiveId({ id: item.id, code: item.code })}
>
归档
</Button>
) : (
<span className="text-xs text-slate-400">-</span>
)}
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell className="py-3 text-slate-500" colSpan={7}>
暂无预算池
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<Pagination page={page} totalPages={totalPages} onPageChange={setPage} />
</>
) : null}
</Card>
<ConfirmDialog
open={Boolean(confirmArchiveId)}
title="确认归档"
description={`确认归档预算池 ${confirmArchiveId?.code ?? ""} ?`}
variant="destructive"
onConfirm={async () => {
if (confirmArchiveId) archiveMutation.mutate(confirmArchiveId.id);
}}
onOpenChange={(open) => { if (!open) setConfirmArchiveId(null); }}
/>
</div>
);
}