From ed4e4af646d67e5eb9cc15c9cdf9a84ad02098eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 3 Sep 2026 13:49:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=BE=E7=89=87=E7=B4=A0?= =?UTF-8?q?=E6=9D=90=E9=A2=84=E8=A7=88=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用 ThemedModal 网格选择器替换图片组件原生素材下拉。 支持预览占位、失效引用、清除绑定、键盘操作与只读状态。 --- .../Components/ImageAssetSelector.tsx | 161 ++++++++++++++++++ .../Inspector/Components/ImagePanel.tsx | 27 +-- 2 files changed, 170 insertions(+), 18 deletions(-) create mode 100644 apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImageAssetSelector.tsx diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImageAssetSelector.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImageAssetSelector.tsx new file mode 100644 index 000000000..0e44b2406 --- /dev/null +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImageAssetSelector.tsx @@ -0,0 +1,161 @@ +import { Check, ImageIcon, X } from 'lucide-react'; +import { useState } from 'react'; + +import { ThemedModal } from '../../../../../components/modal/ThemedModal'; +import type { SpriteAsset } from '../../../../../features/ui-editor/types/SpriteAsset'; +import { ComponentField } from './ComponentField'; + +export function ImageAssetSelector({ + value, + sprites, + previewUrls, + readOnly, + onChange, +}: { + value: string | null; + sprites: Record; + previewUrls: Record; + readOnly: boolean; + onChange: (value: string | null) => void; +}) { + const [open, setOpen] = useState(false); + const [failedPreviewIds, setFailedPreviewIds] = useState>( + () => new Set(), + ); + const selectedSprite = value ? sprites[value] : undefined; + const selectedLabel = selectedSprite + ? selectedSprite.metadata.name || value || '未绑定' + : value + ? `素材不存在(${value})` + : '未绑定'; + const buttonLabel = value ? '更换素材' : '选择素材'; + + function close() { + setOpen(false); + } + + function select(nextValue: string | null) { + onChange(nextValue); + close(); + } + + function markPreviewFailed(id: string) { + setFailedPreviewIds((current) => { + if (current.has(id)) return current; + const next = new Set(current); + next.add(id); + return next; + }); + } + + return ( + <> + +
+
+ {selectedLabel} +
+ +
+
+ + +
+

选择图片素材

+ +
+ +
+
+ + + {value && !selectedSprite ? ( +
+
+ ) : null} + + {Object.entries(sprites).map(([id, sprite]) => { + const previewUrl = previewUrls[id]; + const hasPreview = + Boolean(previewUrl) && !failedPreviewIds.has(id); + const selected = value === id; + return ( + + ); + })} +
+ {Object.keys(sprites).length === 0 ? ( +

+ 暂无可用素材 +

+ ) : null} +
+
+ + ); +} diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImagePanel.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImagePanel.tsx index 176021783..06b3e8b53 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImagePanel.tsx +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/Components/ImagePanel.tsx @@ -4,10 +4,12 @@ import type { ImageType } from '../../../../../features/ui-editor/types/ImageTyp import type { UiEditorOperationResult } from '../../../../../features/ui-editor/useUiEditorState'; import type { ImageEditorProps } from './componentEditorTypes'; import { ComponentNumberInput, ComponentSelect } from './ComponentField'; +import { ImageAssetSelector } from './ImageAssetSelector'; export function ImagePanel({ component, sprites, + previewUrls, readOnly, onChange, }: ImageEditorProps) { @@ -17,24 +19,13 @@ export function ImagePanel({ return (
- - update({ - ...component, - target_graphic: event.target.value || null, - }) - } - > - - {Object.entries(sprites).map(([id, sprite]) => ( - - ))} - + update({ ...component, target_graphic })} + />