import { useState, useEffect } from 'react'; import { X, FileJson, Binary, Info, File, FolderTree, FolderOpen, Code } from 'lucide-react'; import { open } from '@tauri-apps/plugin-dialog'; import '../styles/ExportRuntimeDialog.css'; interface ExportRuntimeDialogProps { isOpen: boolean; onClose: () => void; onExport: (options: ExportOptions) => void; hasProject: boolean; availableFiles: string[]; currentFileName?: string; projectPath?: string; } export interface ExportOptions { mode: 'single' | 'workspace'; assetOutputPath: string; typeOutputPath: string; selectedFiles: string[]; fileFormats: Map; } /** * 导出运行时资产对话框 */ export const ExportRuntimeDialog: React.FC = ({ isOpen, onClose, onExport, hasProject, availableFiles, currentFileName, projectPath }) => { const [selectedMode, setSelectedMode] = useState<'single' | 'workspace'>('workspace'); const [assetOutputPath, setAssetOutputPath] = useState(''); const [typeOutputPath, setTypeOutputPath] = useState(''); const [selectedFiles, setSelectedFiles] = useState>(new Set()); const [fileFormats, setFileFormats] = useState>(new Map()); const [selectAll, setSelectAll] = useState(true); const [isExporting, setIsExporting] = useState(false); const [exportProgress, setExportProgress] = useState(0); const [exportMessage, setExportMessage] = useState(''); // 从 localStorage 加载上次的路径 useEffect(() => { if (isOpen && projectPath) { const savedAssetPath = localStorage.getItem('export-asset-path'); const savedTypePath = localStorage.getItem('export-type-path'); if (savedAssetPath) { setAssetOutputPath(savedAssetPath); } if (savedTypePath) { setTypeOutputPath(savedTypePath); } } }, [isOpen, projectPath]); useEffect(() => { if (isOpen) { if (selectedMode === 'workspace') { const newSelectedFiles = new Set(availableFiles); setSelectedFiles(newSelectedFiles); setSelectAll(true); const newFormats = new Map(); availableFiles.forEach(file => { newFormats.set(file, 'binary'); }); setFileFormats(newFormats); } else { setSelectedFiles(new Set()); setSelectAll(false); if (currentFileName) { const newFormats = new Map(); newFormats.set(currentFileName, 'binary'); setFileFormats(newFormats); } } } }, [isOpen, selectedMode, availableFiles, currentFileName]); if (!isOpen) return null; const handleSelectAll = () => { if (selectAll) { setSelectedFiles(new Set()); setSelectAll(false); } else { setSelectedFiles(new Set(availableFiles)); setSelectAll(true); } }; const handleToggleFile = (file: string) => { const newSelected = new Set(selectedFiles); if (newSelected.has(file)) { newSelected.delete(file); } else { newSelected.add(file); } setSelectedFiles(newSelected); setSelectAll(newSelected.size === availableFiles.length); }; const handleFileFormatChange = (file: string, format: 'json' | 'binary') => { const newFormats = new Map(fileFormats); newFormats.set(file, format); setFileFormats(newFormats); }; const handleBrowseAssetPath = async () => { try { const selected = await open({ directory: true, multiple: false, title: '选择资产输出目录', defaultPath: assetOutputPath || projectPath }); if (selected) { const path = selected as string; setAssetOutputPath(path); localStorage.setItem('export-asset-path', path); } } catch (error) { console.error('Failed to browse asset path:', error); } }; const handleBrowseTypePath = async () => { try { const selected = await open({ directory: true, multiple: false, title: '选择类型定义输出目录', defaultPath: typeOutputPath || projectPath }); if (selected) { const path = selected as string; setTypeOutputPath(path); localStorage.setItem('export-type-path', path); } } catch (error) { console.error('Failed to browse type path:', error); } }; const handleExport = async () => { if (!assetOutputPath) { setExportMessage('错误:请选择资产输出路径'); return; } if (!typeOutputPath) { setExportMessage('错误:请选择类型定义输出路径'); return; } if (selectedMode === 'workspace' && selectedFiles.size === 0) { setExportMessage('错误:请至少选择一个文件'); return; } if (selectedMode === 'single' && !currentFileName) { setExportMessage('错误:没有可导出的当前文件'); return; } // 保存路径到 localStorage localStorage.setItem('export-asset-path', assetOutputPath); localStorage.setItem('export-type-path', typeOutputPath); setIsExporting(true); setExportProgress(0); setExportMessage('正在导出...'); try { await onExport({ mode: selectedMode, assetOutputPath, typeOutputPath, selectedFiles: selectedMode === 'workspace' ? Array.from(selectedFiles) : [currentFileName!], fileFormats }); setExportProgress(100); setExportMessage('导出成功!'); } catch (error) { setExportMessage(`导出失败:${error}`); } finally { setIsExporting(false); } }; return (

导出运行时资产

{/* Tab 页签 */}
{/* 资产输出路径 */}

资产输出路径

setAssetOutputPath(e.target.value)} placeholder="选择资产输出目录(.btree.bin / .btree.json)..." style={{ flex: 1, padding: '8px 12px', backgroundColor: '#2d2d2d', border: '1px solid #3a3a3a', borderRadius: '4px', color: '#cccccc', fontSize: '12px' }} />
{/* TypeScript 类型定义输出路径 */}

TypeScript 类型定义输出路径

{selectedMode === 'workspace' ? ( <> 将导出以下类型定义:
• 每个行为树的黑板变量类型(.d.ts)
• 全局黑板变量类型(GlobalBlackboard.ts) ) : ( '将导出当前行为树的黑板变量类型(.d.ts)' )}
setTypeOutputPath(e.target.value)} placeholder="选择类型定义输出目录..." style={{ flex: 1, padding: '8px 12px', backgroundColor: '#2d2d2d', border: '1px solid #3a3a3a', borderRadius: '4px', color: '#cccccc', fontSize: '12px' }} />
{/* 文件列表 */} {selectedMode === 'workspace' && availableFiles.length > 0 && (

选择要导出的文件 ({selectedFiles.size}/{availableFiles.length})

{availableFiles.map((file) => (
handleToggleFile(file)} />
{file}.btree
))}
)} {/* 单文件模式 */} {selectedMode === 'single' && (

当前文件

{currentFileName ? (
{currentFileName}.btree
) : (
没有打开的行为树文件
请先在编辑器中打开一个行为树文件
)}
)}
{exportMessage && (
{exportMessage}
)} {isExporting && (
)}
); };