MUD6 推广移动统一设计
这个提交包含在:
@@ -16,6 +16,7 @@ import { useAuth } from "../auth/AuthContext";
|
||||
import { hasPermission } from "../auth/permissions";
|
||||
import { PageHeader } from "../components/common/PageHeader";
|
||||
import { BarcodeScannerPanel } from "../components/mobile/BarcodeScannerPanel";
|
||||
import { PageScaffold } from "../components/patterns/PageScaffold";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { Input } from "../components/ui/input";
|
||||
import {
|
||||
@@ -394,7 +395,7 @@ export function MobileAssetScanPage() {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<PageScaffold className="space-y-4">
|
||||
<PageHeader
|
||||
title={isInventoryMode ? "移动扫码盘点" : "移动资产扫码"}
|
||||
subtitle={isInventoryMode ? "在盘点任务上下文中扫码定位资产并快捷录入结果。" : "扫码或手工输入资产编号,快速查看资产卡片。"}
|
||||
@@ -410,7 +411,7 @@ export function MobileAssetScanPage() {
|
||||
) : null}
|
||||
|
||||
{isInventoryMode ? (
|
||||
<div className="card-std space-y-3 p-4" data-testid="mobile-asset-scan-inventory-context">
|
||||
<div className="mobile-card space-y-3" data-testid="mobile-asset-scan-inventory-context">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-zinc-900">盘点任务上下文</h3>
|
||||
@@ -436,7 +437,7 @@ export function MobileAssetScanPage() {
|
||||
) : null}
|
||||
|
||||
{selectedInventoryCheck ? (
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-zinc-200 px-3 py-3 text-sm">
|
||||
<div className="mobile-list-card flex flex-wrap items-center justify-between gap-3 text-sm">
|
||||
<div>
|
||||
<div className="font-medium text-zinc-900">{selectedInventoryCheck.checkNo}</div>
|
||||
<div className="text-zinc-500">状态:{inventoryStatusLabel(selectedInventoryCheck.status)} · 范围:{selectedInventoryCheck.scopeType} / {selectedInventoryCheck.scopeRefId ?? "全量"}</div>
|
||||
@@ -459,13 +460,13 @@ export function MobileAssetScanPage() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="card-std space-y-3 p-4" data-testid="mobile-asset-scan-search">
|
||||
<div className="mobile-card space-y-3" data-testid="mobile-asset-scan-search">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h3 className="text-base font-semibold text-zinc-900">手工输入 / 搜索</h3>
|
||||
<span className={`text-sm ${isOnline ? "text-emerald-600" : "text-orange-600"}`}>{isOnline ? "在线" : "离线"}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<div className="grid gap-2 min-[390px]:grid-cols-[minmax(0,1fr)_auto]">
|
||||
<Input
|
||||
data-testid="mobile-asset-scan-input"
|
||||
value={searchInput}
|
||||
@@ -483,14 +484,14 @@ export function MobileAssetScanPage() {
|
||||
</div>
|
||||
|
||||
{filteredSearchResults.length > 1 ? (
|
||||
<div className="card-std space-y-3 p-4" data-testid="mobile-asset-scan-candidates">
|
||||
<div className="mobile-card space-y-3" data-testid="mobile-asset-scan-candidates">
|
||||
<h3 className="text-base font-semibold text-zinc-900">候选资产</h3>
|
||||
<div className="space-y-2">
|
||||
{filteredSearchResults.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
className={`w-full rounded-lg border px-3 py-3 text-left text-sm ${selectedAssetId === item.id ? "border-primary bg-primary-50 text-primary" : "border-zinc-200 bg-white text-zinc-800"}`}
|
||||
className={`mobile-list-card w-full text-left text-sm ${selectedAssetId === item.id ? "border-primary bg-primary-50 text-primary" : "border-zinc-200 bg-white text-zinc-800"}`}
|
||||
onClick={() => setSelectedAssetId(item.id)}
|
||||
data-testid={`mobile-asset-scan-candidate-${item.id}`}
|
||||
>
|
||||
@@ -503,7 +504,7 @@ export function MobileAssetScanPage() {
|
||||
) : null}
|
||||
|
||||
{selectedAsset ? (
|
||||
<div className="card-std space-y-4 p-4" data-testid="mobile-asset-scan-detail">
|
||||
<div className="mobile-card space-y-4" data-testid="mobile-asset-scan-detail">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-zinc-900">{selectedAsset.assetNo}</h3>
|
||||
@@ -514,22 +515,22 @@ export function MobileAssetScanPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">状态:{statusLabel(selectedAsset.status)}</div>
|
||||
<div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">法体:{selectedAsset.legalEntity?.name ?? "-"}</div>
|
||||
<div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">成本中心:{selectedAsset.costCenter?.name ?? "-"}</div>
|
||||
<div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">位置:{selectedAsset.location ?? "-"}</div>
|
||||
<div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">归属人:{selectedAsset.assignee?.name ?? "未分配"}</div>
|
||||
<div className="rounded-lg border border-zinc-200 px-3 py-2 text-sm">序列号:{selectedAsset.serialNo ?? "-"}</div>
|
||||
<div className="mobile-info-grid">
|
||||
<div className="mobile-subcard text-sm">状态:{statusLabel(selectedAsset.status)}</div>
|
||||
<div className="mobile-subcard text-sm">法体:{selectedAsset.legalEntity?.name ?? "-"}</div>
|
||||
<div className="mobile-subcard text-sm">成本中心:{selectedAsset.costCenter?.name ?? "-"}</div>
|
||||
<div className="mobile-subcard text-sm">位置:{selectedAsset.location ?? "-"}</div>
|
||||
<div className="mobile-subcard text-sm">归属人:{selectedAsset.assignee?.name ?? "未分配"}</div>
|
||||
<div className="mobile-subcard text-sm">序列号:{selectedAsset.serialNo ?? "-"}</div>
|
||||
</div>
|
||||
|
||||
{isInventoryMode && canRecordInventory && selectedInventoryCheck ? (
|
||||
<div className="space-y-3 rounded-xl border border-zinc-200 bg-zinc-50 p-4">
|
||||
<div className="mobile-subcard space-y-3 bg-zinc-50">
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold text-zinc-900">盘点录入</h4>
|
||||
<p className="text-xs text-zinc-500">仅在盘点中的任务可提交或离线缓存结果。</p>
|
||||
</div>
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
<div className="mobile-info-grid">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-zinc-700">盘点结果</label>
|
||||
<select className="select-std" value={recordResult} onChange={(event) => setRecordResult(event.target.value as InventoryCheckResult)}>
|
||||
@@ -564,7 +565,7 @@ export function MobileAssetScanPage() {
|
||||
) : null}
|
||||
|
||||
{isInventoryMode && canRecordInventory ? (
|
||||
<div className="card-std space-y-3 p-4" data-testid="mobile-asset-scan-queue">
|
||||
<div className="mobile-card space-y-3" data-testid="mobile-asset-scan-queue">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h3 className="text-base font-semibold text-zinc-900">离线盘点队列</h3>
|
||||
<span className="text-sm text-zinc-500">{currentQueueItems.length ? `待同步 ${currentQueueItems.length} 项` : "无待同步项"}</span>
|
||||
@@ -575,7 +576,7 @@ export function MobileAssetScanPage() {
|
||||
) : null}
|
||||
|
||||
{currentQueueItems.map((item) => (
|
||||
<div key={item.id} className="rounded-lg border border-zinc-200 px-3 py-3 text-sm">
|
||||
<div key={item.id} className="mobile-list-card text-sm">
|
||||
<div className="font-medium text-zinc-900">{item.assetNo}</div>
|
||||
<div className="text-zinc-500">结果:{inventoryResultLabel(item.result)} · 创建时间:{formatDateTime(item.createdAt)}</div>
|
||||
<div className="text-zinc-500">备注:{item.note ?? "-"}</div>
|
||||
@@ -597,6 +598,6 @@ export function MobileAssetScanPage() {
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</PageScaffold>
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户