fix: 包材列表改用 HTML table 替代 CSS Grid,保证列对齐
This commit is contained in:
parent
8f2875480d
commit
f28e932576
@ -17,7 +17,6 @@ import {
|
||||
Copy,
|
||||
FolderPlus,
|
||||
ImageIcon,
|
||||
MoreHorizontal,
|
||||
Package2,
|
||||
Pencil,
|
||||
Plus,
|
||||
@ -1097,166 +1096,153 @@ export default function PackagingMaterialsClient({
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-[24px] border border-black/5">
|
||||
<div className="grid grid-cols-[48px_1.4fr_1fr_0.9fr_1fr_0.8fr_0.7fr_auto] items-center gap-2 bg-apple-bg-secondary/65 px-4 py-3 text-sm text-apple-label-tertiary">
|
||||
<span>图片</span>
|
||||
<span>名称</span>
|
||||
<span>编码</span>
|
||||
<span>模板类型</span>
|
||||
<span>规格</span>
|
||||
<span>成本</span>
|
||||
<span>状态</span>
|
||||
<span></span>
|
||||
</div>
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="bg-apple-bg-secondary/65 text-left text-sm text-apple-label-tertiary">
|
||||
<th className="w-12 px-4 py-3 font-medium">图片</th>
|
||||
<th className="px-2 py-3 font-medium">名称</th>
|
||||
<th className="px-2 py-3 font-medium">编码</th>
|
||||
<th className="px-2 py-3 font-medium">模板类型</th>
|
||||
<th className="px-2 py-3 font-medium">规格</th>
|
||||
<th className="px-2 py-3 font-medium">成本</th>
|
||||
<th className="px-2 py-3 font-medium">状态</th>
|
||||
<th className="w-28 px-4 py-3 font-medium"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-black/5">
|
||||
{visibleVariants.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={8} className="px-6 py-14 text-center text-sm text-apple-label-tertiary">
|
||||
当前条件下还没有包材记录,可以先新建二级分类,再建包材。
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
visibleVariants.map((variant) => {
|
||||
const family = familyMap.get(variant.assetFamilyId) ?? null
|
||||
const templateType = readPackagingTemplateType(variant, family)
|
||||
const template = templateType ? PACKAGING_TEMPLATE_MAP.get(templateType) ?? null : null
|
||||
const dimensions = (variant.dimensions ?? {}) as Record<string, number>
|
||||
const rowComputedCost = (() => {
|
||||
const businessData = (variant.businessData ?? {}) as Record<string, unknown>
|
||||
const templateFields =
|
||||
typeof businessData.templateFields === 'object' && businessData.templateFields
|
||||
? (businessData.templateFields as Record<string, string>)
|
||||
: {}
|
||||
|
||||
{visibleVariants.length === 0 ? (
|
||||
<div className="px-6 py-14 text-center text-sm text-apple-label-tertiary">
|
||||
当前条件下还没有包材记录,可以先新建二级分类,再建包材。
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y divide-black/5">
|
||||
{visibleVariants.map((variant) => {
|
||||
const family = familyMap.get(variant.assetFamilyId) ?? null
|
||||
const templateType = readPackagingTemplateType(variant, family)
|
||||
const template = templateType ? PACKAGING_TEMPLATE_MAP.get(templateType) ?? null : null
|
||||
const dimensions = (variant.dimensions ?? {}) as Record<string, number>
|
||||
const rowComputedCost = (() => {
|
||||
const businessData = (variant.businessData ?? {}) as Record<string, unknown>
|
||||
const templateFields =
|
||||
typeof businessData.templateFields === 'object' && businessData.templateFields
|
||||
? (businessData.templateFields as Record<string, string>)
|
||||
: {}
|
||||
return templateType
|
||||
? computePackagingMaterialCost(templateType, templateFields)
|
||||
: null
|
||||
})()
|
||||
|
||||
return templateType
|
||||
? computePackagingMaterialCost(templateType, templateFields)
|
||||
: null
|
||||
})()
|
||||
const specParts: string[] = []
|
||||
if (dimensions.length) specParts.push(`${dimensions.length}`)
|
||||
if (dimensions.width) specParts.push(`${dimensions.width}`)
|
||||
if (dimensions.height) specParts.push(`${dimensions.height}`)
|
||||
const specLabel = specParts.length > 0 ? `${specParts.join('×')} mm` : '-'
|
||||
|
||||
const specParts: string[] = []
|
||||
if (dimensions.length) specParts.push(`${dimensions.length}`)
|
||||
if (dimensions.width) specParts.push(`${dimensions.width}`)
|
||||
if (dimensions.height) specParts.push(`${dimensions.height}`)
|
||||
const specLabel = specParts.length > 0 ? `${specParts.join('×')} mm` : '-'
|
||||
|
||||
return (
|
||||
<div
|
||||
key={variant.id}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`查看包材 ${variant.name}`}
|
||||
className={cn(
|
||||
'grid w-full grid-cols-[48px_1.4fr_1fr_0.9fr_1fr_0.8fr_0.7fr_auto] items-center gap-2 px-4 py-3 text-left transition hover:bg-apple-bg-secondary/45',
|
||||
selectedVariantId === variant.id && 'bg-apple-blue/5',
|
||||
)}
|
||||
onClick={() => {
|
||||
setSelectedVariantId(variant.id)
|
||||
setDetailMode('preview')
|
||||
setEditDraft(null)
|
||||
setDetailError(null)
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault()
|
||||
return (
|
||||
<tr
|
||||
key={variant.id}
|
||||
className={cn(
|
||||
'cursor-pointer transition hover:bg-apple-bg-secondary/45',
|
||||
selectedVariantId === variant.id && 'bg-apple-blue/5',
|
||||
)}
|
||||
onClick={() => {
|
||||
setSelectedVariantId(variant.id)
|
||||
setDetailMode('preview')
|
||||
setEditDraft(null)
|
||||
setDetailError(null)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-center"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
}}
|
||||
>
|
||||
<label className="relative cursor-pointer">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/jpeg,image/png,image/webp"
|
||||
className="hidden"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (file) void handleUploadImage(variant, file)
|
||||
event.target.value = ''
|
||||
}}
|
||||
/>
|
||||
{variant.thumbnailUrl ? (
|
||||
<img
|
||||
src={variant.thumbnailUrl}
|
||||
alt={variant.name}
|
||||
className="h-9 w-9 rounded-apple-md border border-black/5 object-cover transition hover:opacity-80"
|
||||
<td
|
||||
className="px-4 py-3"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<label className="relative inline-flex cursor-pointer">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/jpeg,image/png,image/webp"
|
||||
className="hidden"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (file) void handleUploadImage(variant, file)
|
||||
event.target.value = ''
|
||||
}}
|
||||
/>
|
||||
{variant.thumbnailUrl ? (
|
||||
<img
|
||||
src={variant.thumbnailUrl}
|
||||
alt={variant.name}
|
||||
className="h-9 w-9 rounded-apple-md border border-black/5 object-cover transition hover:opacity-80"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-apple-md border border-dashed border-black/15 bg-apple-bg-secondary/60 transition hover:border-apple-blue/40 hover:bg-apple-blue/5">
|
||||
<ImageIcon className="h-4 w-4 text-apple-label-tertiary" />
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
</td>
|
||||
<td className="max-w-0 px-2 py-3 text-sm font-apple-semibold">{variant.name}</td>
|
||||
<td className="max-w-0 px-2 py-3 font-mono text-sm text-apple-label-secondary">{variant.code}</td>
|
||||
<td className="max-w-0 px-2 py-3 text-sm text-apple-label-secondary">{template?.label ?? '未设置'}</td>
|
||||
<td className="max-w-0 px-2 py-3 font-mono text-sm text-apple-label-secondary">{specLabel}</td>
|
||||
<td className="px-2 py-3 text-sm">
|
||||
{variant.costPrice != null ? (
|
||||
<span className="font-apple-medium">¥ {variant.costPrice}</span>
|
||||
) : rowComputedCost != null ? (
|
||||
<span className="text-apple-label-secondary">¥ {rowComputedCost.toFixed(2)}</span>
|
||||
) : (
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-apple-md border border-dashed border-black/15 bg-apple-bg-secondary/60 transition hover:border-apple-blue/40 hover:bg-apple-blue/5">
|
||||
<ImageIcon className="h-4 w-4 text-apple-label-tertiary" />
|
||||
</div>
|
||||
<span className="text-apple-label-tertiary">缺成本</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
<div className="min-w-0 flex items-center">
|
||||
<span className="truncate text-sm font-apple-semibold">{variant.name}</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex items-center text-apple-label-secondary">
|
||||
<span className="truncate font-mono text-sm">{variant.code}</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex items-center text-apple-label-secondary">
|
||||
<span className="truncate text-sm">{template?.label ?? '未设置'}</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex items-center text-apple-label-secondary">
|
||||
<span className="truncate font-mono text-sm">{specLabel}</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex items-center text-sm">
|
||||
{variant.costPrice != null ? (
|
||||
<span className="font-apple-medium">¥ {variant.costPrice}</span>
|
||||
) : rowComputedCost != null ? (
|
||||
<span className="text-apple-label-secondary">¥ {rowComputedCost.toFixed(2)}</span>
|
||||
) : (
|
||||
<span className="text-apple-label-tertiary">缺成本</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Badge type={getStatusTone(variant.status)} variant="subtle" size="small">
|
||||
{getStatusLabel(variant.status)}
|
||||
</Badge>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center justify-end gap-1"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="编辑"
|
||||
className="rounded-apple-md p-1.5 text-apple-label-secondary transition hover:bg-apple-blue/10 hover:text-apple-blue"
|
||||
onClick={() => startEditVariant(variant)}
|
||||
</td>
|
||||
<td className="px-2 py-3">
|
||||
<Badge type={getStatusTone(variant.status)} variant="subtle" size="small">
|
||||
{getStatusLabel(variant.status)}
|
||||
</Badge>
|
||||
</td>
|
||||
<td
|
||||
className="px-4 py-3"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="复制"
|
||||
className="rounded-apple-md p-1.5 text-apple-label-secondary transition hover:bg-apple-blue/10 hover:text-apple-blue"
|
||||
onClick={() => openCreateDrawer(variant)}
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="删除包材记录"
|
||||
className="rounded-apple-md p-1.5 text-apple-label-secondary transition hover:bg-apple-red/10 hover:text-apple-red"
|
||||
onClick={() =>
|
||||
setDeleteTarget({
|
||||
type: 'variant',
|
||||
id: variant.id,
|
||||
name: variant.name,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="编辑"
|
||||
className="rounded-apple-md p-1.5 text-apple-label-secondary transition hover:bg-apple-blue/10 hover:text-apple-blue"
|
||||
onClick={() => startEditVariant(variant)}
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="复制"
|
||||
className="rounded-apple-md p-1.5 text-apple-label-secondary transition hover:bg-apple-blue/10 hover:text-apple-blue"
|
||||
onClick={() => openCreateDrawer(variant)}
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="删除包材记录"
|
||||
className="rounded-apple-md p-1.5 text-apple-label-secondary transition hover:bg-apple-red/10 hover:text-apple-red"
|
||||
onClick={() =>
|
||||
setDeleteTarget({
|
||||
type: 'variant',
|
||||
id: variant.id,
|
||||
name: variant.name,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user