import React, { useState, useRef, useCallback, useEffect } from 'react'; import { Image, X, Navigation, ChevronDown, Copy } from 'lucide-react'; import { convertFileSrc } from '@tauri-apps/api/core'; import { Core } from '@esengine/ecs-framework'; import { ProjectService } from '@esengine/editor-core'; import { AssetPickerDialog } from '../../../components/dialogs/AssetPickerDialog'; import './AssetField.css'; interface AssetFieldProps { label?: string; value: string | null; onChange: (value: string | null) => void; fileExtension?: string; placeholder?: string; readonly?: boolean; onNavigate?: (path: string) => void; onCreate?: () => void; } export function AssetField({ label, value, onChange, fileExtension = '', placeholder = 'None', readonly = false, onNavigate, onCreate }: AssetFieldProps) { const [isDragging, setIsDragging] = useState(false); const [showPicker, setShowPicker] = useState(false); const [thumbnailUrl, setThumbnailUrl] = useState(null); const inputRef = useRef(null); // 检测是否是图片资源 const isImageAsset = useCallback((path: string | null) => { if (!path) return false; return ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp'].some(ext => path.toLowerCase().endsWith(ext) ); }, []); // 加载缩略图 useEffect(() => { if (value && isImageAsset(value)) { // 获取项目路径并构建完整路径 const projectService = Core.services.tryResolve(ProjectService); const projectPath = projectService?.getCurrentProject()?.path; if (projectPath) { // 构建完整的文件路径 const fullPath = value.startsWith('/') || value.includes(':') ? value : `${projectPath}/${value}`; try { const url = convertFileSrc(fullPath); setThumbnailUrl(url); } catch { setThumbnailUrl(null); } } else { // 没有项目路径时,尝试直接使用 value try { const url = convertFileSrc(value); setThumbnailUrl(url); } catch { setThumbnailUrl(null); } } } else { setThumbnailUrl(null); } }, [value, isImageAsset]); const handleDragEnter = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (!readonly) { setIsDragging(true); } }, [readonly]); const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); }, []); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); if (readonly) return; const files = Array.from(e.dataTransfer.files); const file = files.find((f) => !fileExtension || f.name.endsWith(fileExtension) ); if (file) { onChange(file.name); return; } const assetPath = e.dataTransfer.getData('asset-path'); if (assetPath && (!fileExtension || assetPath.endsWith(fileExtension))) { onChange(assetPath); return; } const text = e.dataTransfer.getData('text/plain'); if (text && (!fileExtension || text.endsWith(fileExtension))) { onChange(text); } }, [onChange, fileExtension, readonly]); const handleBrowse = useCallback(() => { if (readonly) return; setShowPicker(true); }, [readonly]); const handlePickerSelect = useCallback((path: string) => { onChange(path); setShowPicker(false); }, [onChange]); const handleClear = useCallback(() => { if (!readonly) { onChange(null); } }, [onChange, readonly]); const getFileName = (path: string) => { const parts = path.split(/[\\/]/); return parts[parts.length - 1]; }; return (
{label && }
{/* 缩略图预览 */}
{thumbnailUrl ? ( ) : ( )}
{/* 右侧区域 */}
{/* 下拉选择框 */}
{value ? getFileName(value) : placeholder}
{/* 操作按钮行 */}
{/* 定位按钮 */} {value && onNavigate && ( )} {/* 复制路径按钮 */} {value && ( )} {/* 清除按钮 */} {value && !readonly && ( )}
setShowPicker(false)} onSelect={handlePickerSelect} title="Select Asset" fileExtensions={fileExtension ? [fileExtension] : []} />
); }