重构资产盘点与移动扫码页面(05-30-asset-inventory-mobile-redesign)

这个提交包含在:
2026-05-30 13:23:47 +08:00
父节点 aa8f65ce2a
当前提交 ab0d57bd25
共修改 7 个文件,包含 321 行新增和 84 行删除
+52 -4
查看文件
@@ -15,8 +15,10 @@ import {
} from "../api/inventoryChecks";
import { fetchCostCenters, fetchLegalEntities } from "../api/masterData";
import { isApiError } from "../api/types";
import { ActionConfirmDialog } from "../components/common/ActionConfirmDialog";
import { PageHeader } from "../components/common/PageHeader";
import { TableSkeleton } from "../components/common/TableSkeleton";
import { PageScaffold } from "../components/patterns/PageScaffold";
import { Button } from "../components/ui/button";
import { Input } from "../components/ui/input";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table";
@@ -84,6 +86,7 @@ export function InventoryChecksPage() {
const [recordAssetId, setRecordAssetId] = useState("");
const [recordResult, setRecordResult] = useState<InventoryCheckResult>("matched");
const [recordNote, setRecordNote] = useState("");
const [confirmCompleteCheck, setConfirmCompleteCheck] = useState(false);
const inventoryChecksQuery = useQuery({
queryKey: ["inventory-checks"],
@@ -122,6 +125,19 @@ export function InventoryChecksPage() {
() => (inventoryChecksQuery.data?.items ?? []).find((item) => item.id === selectedCheckId) ?? null,
[inventoryChecksQuery.data?.items, selectedCheckId],
);
const inventoryStats = useMemo(() => {
const items = inventoryChecksQuery.data?.items ?? [];
return items.reduce(
(acc, item) => {
acc.total += 1;
if (item.status === "draft") acc.draft += 1;
if (item.status === "counting") acc.counting += 1;
if (item.status === "completed") acc.completed += 1;
return acc;
},
{ total: 0, draft: 0, counting: 0, completed: 0 },
);
}, [inventoryChecksQuery.data?.items]);
const scopedAssets = useMemo(() => {
const items = assetsQuery.data?.items ?? [];
@@ -228,10 +244,10 @@ export function InventoryChecksPage() {
}, [costCentersQuery.data?.items, legalEntitiesQuery.data?.items, scopeType]);
return (
<div className="space-y-6">
<PageScaffold className="space-y-6">
<PageHeader
title="资产盘点"
subtitle="创建、启动、记录并完成资产盘点任务。"
subtitle="按任务执行盘点:先确定范围,再启动、扫码/录入、核对结果并确认完成。"
actions={
<Button
type="button"
@@ -244,6 +260,29 @@ export function InventoryChecksPage() {
}
/>
<section className="ops-status-strip grid gap-4 md:grid-cols-4" data-testid="inventory-check-summary">
<div className="finance-metric-card">
<div className="text-sm text-zinc-500">盘点任务</div>
<div className="mt-1 text-2xl font-semibold text-zinc-900">{inventoryStats.total}</div>
<div className="mt-1 text-xs text-zinc-500">按季度或事件触发</div>
</div>
<div className="finance-metric-card">
<div className="text-sm text-zinc-500">草稿</div>
<div className="mt-1 text-2xl font-semibold text-zinc-900">{inventoryStats.draft}</div>
<div className="mt-1 text-xs text-zinc-500">待启动后录入</div>
</div>
<div className="finance-metric-card">
<div className="text-sm text-zinc-500">盘点中</div>
<div className="mt-1 text-2xl font-semibold text-zinc-900">{inventoryStats.counting}</div>
<div className="mt-1 text-xs text-zinc-500">可扫码或手工录入</div>
</div>
<div className="finance-metric-card">
<div className="text-sm text-zinc-500">已完成</div>
<div className="mt-1 text-2xl font-semibold text-zinc-900">{inventoryStats.completed}</div>
<div className="mt-1 text-xs text-zinc-500">保留差异和备注</div>
</div>
</section>
<div className="grid gap-6 xl:grid-cols-[360px_minmax(0,1fr)]">
<div className="card-std space-y-4 p-6">
<h3 className="text-base font-semibold text-zinc-900">新建盘点任务</h3>
@@ -347,7 +386,7 @@ export function InventoryChecksPage() {
</Button>
) : null}
{selectedCheck.status === "counting" ? (
<Button data-testid="inventory-complete" type="button" variant="secondary" onClick={() => completeCheckMutation.mutate()} disabled={completeCheckMutation.isPending}>
<Button data-testid="inventory-complete" type="button" variant="secondary" onClick={() => setConfirmCompleteCheck(true)} disabled={completeCheckMutation.isPending}>
{completeCheckMutation.isPending ? "完成中..." : "完成盘点"}
</Button>
) : null}
@@ -426,6 +465,15 @@ export function InventoryChecksPage() {
) : null}
</div>
</div>
</div>
<ActionConfirmDialog
open={confirmCompleteCheck}
onOpenChange={setConfirmCompleteCheck}
kind="complete"
targetName={selectedCheck?.checkNo}
description={`完成后该盘点任务将停止继续录入。当前已记录 ${selectedCheck?.items.length ?? 0} 条结果,请确认差异和备注已核对。`}
confirmLabel="确认完成盘点"
onConfirm={() => completeCheckMutation.mutate()}
/>
</PageScaffold>
);
}