1
Some checks failed
CI / verify (push) Has been cancelled
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-05-13 03:11:00 +08:00
parent e4a8bd42bb
commit b13870f71b
8 changed files with 273 additions and 55 deletions

View File

@@ -229,7 +229,8 @@ function getMatch3DDifficultyOption(optionId: Match3DDifficultyOptionId) {
function getMatch3DReadyItemTypeCount(
generatedItemAssets: readonly Match3DGeneratedItemAsset[],
) {
return generatedItemAssets.filter(hasMatch3DGeneratedImageSource).length;
return generatedItemAssets.filter(hasMatch3DGeneratedFiveViewImageSource)
.length;
}
function getMatch3DPlayableItemTypeCount(
@@ -410,12 +411,44 @@ function hasMatch3DGeneratedImageSource(asset: Match3DGeneratedItemAsset) {
return getMatch3DGeneratedImageViewSources(asset).length > 0;
}
function hasMatch3DGeneratedFiveViewImageSource(
asset: Match3DGeneratedItemAsset,
) {
return (
(asset.imageViews ?? []).filter(
(view) =>
Boolean(view.imageSrc?.trim()) || Boolean(view.imageObjectKey?.trim()),
).length >= 5
);
}
function resolveMatch3DGeneratedImageViewSourceFromDraft(
view: NonNullable<Match3DGeneratedItemAsset['imageViews']>[number],
) {
return view.imageObjectKey?.trim() || view.imageSrc?.trim() || '';
}
function resolveMatch3DAssetDraftImageViewSources(
asset: Match3DItemAssetDraft,
) {
return [
...new Set(
(asset.imageViews ?? [])
.map(resolveMatch3DGeneratedImageViewSourceFromDraft)
.filter(Boolean),
),
];
}
function resolveMatch3DAssetDraftPreviewSources(asset: Match3DItemAssetDraft) {
const imageViewSources = resolveMatch3DAssetDraftImageViewSources(asset);
if (imageViewSources.length > 0) {
return imageViewSources.slice(0, 5);
}
const fallbackSource = asset.referenceImageSrc.trim();
return fallbackSource ? [fallbackSource] : [];
}
function hasPersistableMatch3DGeneratedItemAsset(
asset: Match3DGeneratedItemAsset,
) {
@@ -1703,7 +1736,7 @@ function Match3DItemAssetListCard({
onDelete: () => void;
}) {
const pillClass = getMatch3DAssetStatusPillClass(asset.status);
const imageViews = asset.imageViews ?? [];
const previewSources = resolveMatch3DAssetDraftPreviewSources(asset);
return (
<div
@@ -1746,7 +1779,7 @@ function Match3DItemAssetListCard({
</div>
<div className="mt-2 flex flex-wrap gap-2">
<span className="platform-pill platform-pill--neutral px-2.5 py-1 text-[10px]">
{Math.max(imageViews.length, asset.referenceImageSrc ? 1 : 0)}
{previewSources.length}
</span>
<span className="platform-pill platform-pill--neutral px-2.5 py-1 text-[10px]">
2D素材
@@ -1783,33 +1816,25 @@ function Match3DItemAssetDetail({
onChange: (asset: Match3DItemAssetDraft) => void;
onGenerateClickSound: (asset: Match3DItemAssetDraft) => void;
}) {
const imageViews = asset.imageViews ?? [];
const previewSources = resolveMatch3DAssetDraftPreviewSources(asset);
return (
<section className="platform-subpanel min-h-0 rounded-[1.5rem] p-4 sm:p-5">
<div className="grid min-h-0 gap-4 lg:grid-cols-[minmax(18rem,0.95fr)_minmax(14rem,0.62fr)]">
<div className="grid aspect-square min-h-[18rem] grid-cols-2 gap-2 overflow-hidden rounded-[1.25rem] border border-[var(--platform-subpanel-border)] bg-white/70 p-3">
{[
asset.referenceImageSrc,
...imageViews
.map(resolveMatch3DGeneratedImageViewSourceFromDraft)
.filter(Boolean),
]
.filter((source, index, list) => source && list.indexOf(source) === index)
.slice(0, 5)
.map((source, index) => (
<div
key={`${source}-${index}`}
className="grid min-h-0 place-items-center overflow-hidden rounded-[0.9rem] bg-white/80"
>
<ResolvedAssetImage
src={source}
alt=""
aria-hidden="true"
className="h-full w-full object-contain"
/>
</div>
))}
{previewSources.map((source, index) => (
<div
key={`${source}-${index}`}
className="grid min-h-0 place-items-center overflow-hidden rounded-[0.9rem] bg-white/80"
>
<ResolvedAssetImage
src={source}
alt=""
aria-hidden="true"
className="h-full w-full object-contain"
/>
</div>
))}
</div>
<div className="min-h-0 space-y-3">