统一详情页操作区模板(05-31-unify-business-detail-ant-design)
这个提交包含在:
@@ -29,14 +29,20 @@ import { hasAnyPermission, hasPermission } from "../auth/permissions";
|
||||
import { DateTextInput } from "../components/common/DateTextInput";
|
||||
import { StatusBadge } from "../components/common/StatusBadge";
|
||||
import { TableSkeleton } from "../components/common/TableSkeleton";
|
||||
import { DetailSideMeta } from "../components/patterns/DetailSideMeta";
|
||||
import {
|
||||
BusinessDetailActionPanel,
|
||||
BusinessDetailAside,
|
||||
BusinessDetailFact,
|
||||
BusinessDetailFactGrid,
|
||||
BusinessDetailMain,
|
||||
BusinessDetailWorkspace,
|
||||
} from "../components/patterns/BusinessDetailTemplate";
|
||||
import { ListWorkspace, PrimaryTable, WorkspaceHeader } from "../components/patterns/ListWorkspace";
|
||||
import {
|
||||
BusinessListToolbar,
|
||||
BusinessMobileList,
|
||||
BusinessTable,
|
||||
} from "../components/patterns/BusinessListTemplate";
|
||||
import { SectionPanel } from "../components/patterns/SectionPanel";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { Input } from "../components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table";
|
||||
@@ -1044,7 +1050,8 @@ export function AssetRegistryPage() {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||
<BusinessDetailWorkspace>
|
||||
<BusinessDetailMain>
|
||||
<div className="card-std space-y-4 p-4 md:p-5">
|
||||
{selectedAssetId && !isCreatingAsset && assetDetailQuery.isLoading ? <TableSkeleton columns={2} rows={8} /> : null}
|
||||
{selectedAssetId && !isCreatingAsset && assetDetailQuery.isError ? (
|
||||
@@ -1078,24 +1085,20 @@ export function AssetRegistryPage() {
|
||||
) : null}
|
||||
|
||||
{!isCreatingAsset && currentAsset ? (
|
||||
<div className="grid gap-3 md:grid-cols-4" data-testid="asset-detail-summary">
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3 text-sm">
|
||||
<div className="text-xs text-zinc-500">责任人</div>
|
||||
<div className="mt-1 font-medium text-zinc-900">{formatEmployeeSummary(currentAsset.owner ?? currentAsset.assignee)}</div>
|
||||
</div>
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3 text-sm">
|
||||
<div className="text-xs text-zinc-500">保管/位置</div>
|
||||
<div className="mt-1 font-medium text-zinc-900">{currentAsset.keeper ? formatEmployeeSummary(currentAsset.keeper) : currentAsset.location ?? "待确认"}</div>
|
||||
</div>
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3 text-sm">
|
||||
<div className="text-xs text-zinc-500">{isElectronicAssetType(currentAsset.assetType) ? "最近验证" : "最近盘点"}</div>
|
||||
<div className="mt-1 font-medium text-zinc-900">{currentAsset.lastVerifiedAt ? formatDisplayDateTime(currentAsset.lastVerifiedAt) : "未登记"}</div>
|
||||
</div>
|
||||
<div className="rounded-lg border border-zinc-200 bg-zinc-50 p-3 text-sm">
|
||||
<div className="text-xs text-zinc-500">状态</div>
|
||||
<div className="mt-1"><StatusBadge status={currentAsset.status} /></div>
|
||||
</div>
|
||||
</div>
|
||||
<BusinessDetailFactGrid className="xl:grid-cols-4" testId="asset-detail-summary">
|
||||
<BusinessDetailFact label="责任人">
|
||||
{formatEmployeeSummary(currentAsset.owner ?? currentAsset.assignee)}
|
||||
</BusinessDetailFact>
|
||||
<BusinessDetailFact label="保管/位置">
|
||||
{currentAsset.keeper ? formatEmployeeSummary(currentAsset.keeper) : currentAsset.location ?? "待确认"}
|
||||
</BusinessDetailFact>
|
||||
<BusinessDetailFact label={isElectronicAssetType(currentAsset.assetType) ? "最近验证" : "最近盘点"}>
|
||||
{currentAsset.lastVerifiedAt ? formatDisplayDateTime(currentAsset.lastVerifiedAt) : "未登记"}
|
||||
</BusinessDetailFact>
|
||||
<BusinessDetailFact label="状态">
|
||||
<StatusBadge status={currentAsset.status} />
|
||||
</BusinessDetailFact>
|
||||
</BusinessDetailFactGrid>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-6">
|
||||
@@ -1302,31 +1305,54 @@ export function AssetRegistryPage() {
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</BusinessDetailMain>
|
||||
{(isCreatingAsset || currentAsset) && !assetDetailQuery.isLoading ? (
|
||||
<div className="space-y-5">
|
||||
<SectionPanel title="操作" className="xl:sticky xl:top-24">
|
||||
<div className="space-y-4">
|
||||
{canManage ? (
|
||||
<Button
|
||||
type="button"
|
||||
data-testid="asset-save"
|
||||
disabled={saveAssetMutation.isPending || !canEditCurrent}
|
||||
onClick={() => saveAssetMutation.mutate()}
|
||||
>
|
||||
{saveAssetMutation.isPending ? "保存中..." : isCreatingAsset ? "创建资产卡片" : "保存资产卡片"}
|
||||
</Button>
|
||||
) : null}
|
||||
<BusinessDetailAside>
|
||||
<BusinessDetailActionPanel
|
||||
status={!isCreatingAsset && currentAsset ? <StatusBadge status={currentAsset.status} /> : null}
|
||||
primaryAction={canManage ? (
|
||||
<Button
|
||||
className="w-full justify-center"
|
||||
type="button"
|
||||
data-testid="asset-save"
|
||||
disabled={saveAssetMutation.isPending || !canEditCurrent}
|
||||
onClick={() => saveAssetMutation.mutate()}
|
||||
>
|
||||
{saveAssetMutation.isPending ? "保存中..." : isCreatingAsset ? "创建资产卡片" : "保存资产卡片"}
|
||||
</Button>
|
||||
) : null}
|
||||
summary={(
|
||||
<>
|
||||
<div>状态:{isCreatingAsset ? "新建中" : assetStatusLabel(currentAsset?.status)}</div>
|
||||
<div>类型:{assetTypeLabel(assetForm.assetType)}</div>
|
||||
<div>责任人:{formatEmployeeSummary(currentAsset?.owner ?? currentAsset?.assignee)}</div>
|
||||
<div>归属人:{formatEmployeeSummary(currentAsset?.assignee)}</div>
|
||||
<div>保管人:{formatEmployeeSummary(currentAsset?.keeper)}</div>
|
||||
<div>位置:{(currentAsset?.location ?? assetForm.location) || "-"}</div>
|
||||
<div>
|
||||
{isElectronicAssetType(assetForm.assetType) ? "最近验证" : "最近盘点"}:
|
||||
{currentAsset?.lastVerifiedAt ? formatDisplayDateTime(currentAsset.lastVerifiedAt) : assetForm.lastVerifiedAt || "-"}
|
||||
</div>
|
||||
<div>
|
||||
采购来源:
|
||||
{currentAsset?.purchaseRequest
|
||||
? formatTextWithCompactLongNumbers(currentAsset.purchaseRequest.requestNo ?? currentAsset.purchaseRequest.id)
|
||||
: "-"}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
testId="asset-detail-action-panel"
|
||||
>
|
||||
{currentAsset && canTransfer && !isCreatingAsset && currentAsset.assetType === "fixed_asset" ? (
|
||||
<div className="space-y-3">
|
||||
<div className="text-sm font-semibold text-zinc-900">资产流转</div>
|
||||
<div className="rounded-lg bg-zinc-50 px-3 py-2 text-xs text-zinc-600">
|
||||
归属人:{formatEmployeeSummary(currentAsset.assignee)} · 位置:{currentAsset.location ?? "未设置"}
|
||||
</div>
|
||||
|
||||
{currentAsset && canTransfer && !isCreatingAsset && currentAsset.assetType === "fixed_asset" ? (
|
||||
<div className="space-y-3 border-t border-zinc-200 pt-4">
|
||||
<div className="text-sm font-semibold text-zinc-900">资产流转</div>
|
||||
<div className="rounded-lg bg-zinc-50 px-3 py-2 text-xs text-zinc-600">
|
||||
归属人:{formatEmployeeSummary(currentAsset.assignee)} · 位置:{currentAsset.location ?? "未设置"}
|
||||
</div>
|
||||
|
||||
{movementError ? (
|
||||
<p className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">{movementError}</p>
|
||||
) : null}
|
||||
{movementError ? (
|
||||
<p className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">{movementError}</p>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
@@ -1397,32 +1423,12 @@ export function AssetRegistryPage() {
|
||||
{transferAssetMutation.isPending ? "调拨中..." : "调拨"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</SectionPanel>
|
||||
|
||||
<DetailSideMeta title="资产摘要">
|
||||
<div>状态:{isCreatingAsset ? "新建中" : assetStatusLabel(currentAsset?.status)}</div>
|
||||
<div>类型:{assetTypeLabel(assetForm.assetType)}</div>
|
||||
<div>责任人:{formatEmployeeSummary(currentAsset?.owner ?? currentAsset?.assignee)}</div>
|
||||
<div>归属人:{formatEmployeeSummary(currentAsset?.assignee)}</div>
|
||||
<div>保管人:{formatEmployeeSummary(currentAsset?.keeper)}</div>
|
||||
<div>位置:{(currentAsset?.location ?? assetForm.location) || "-"}</div>
|
||||
<div>
|
||||
{isElectronicAssetType(assetForm.assetType) ? "最近验证" : "最近盘点"}:
|
||||
{currentAsset?.lastVerifiedAt ? formatDisplayDateTime(currentAsset.lastVerifiedAt) : assetForm.lastVerifiedAt || "-"}
|
||||
</div>
|
||||
<div>
|
||||
采购来源:
|
||||
{currentAsset?.purchaseRequest
|
||||
? formatTextWithCompactLongNumbers(currentAsset.purchaseRequest.requestNo ?? currentAsset.purchaseRequest.id)
|
||||
: "-"}
|
||||
</div>
|
||||
</DetailSideMeta>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</BusinessDetailActionPanel>
|
||||
</BusinessDetailAside>
|
||||
) : null}
|
||||
</div>
|
||||
</BusinessDetailWorkspace>
|
||||
)}
|
||||
</ListWorkspace>
|
||||
);
|
||||
|
||||
在新工单中引用
屏蔽一个用户