diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx index 022f5c8a..b271f24a 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx @@ -43,6 +43,10 @@ interface FileManagerGridProps { onUpload?: (files: FileList) => void; onContextMenu?: (event: React.MouseEvent, file?: FileItem) => void; viewMode?: 'grid' | 'list'; + onRename?: (file: FileItem, newName: string) => void; + editingFile?: FileItem | null; + onStartEdit?: (file: FileItem) => void; + onCancelEdit?: () => void; } const getFileIcon = (fileName: string, isDirectory: boolean, viewMode: 'grid' | 'list' = 'grid') => { @@ -129,12 +133,55 @@ export function FileManagerGrid({ onRefresh, onUpload, onContextMenu, - viewMode = 'grid' + viewMode = 'grid', + onRename, + editingFile, + onStartEdit, + onCancelEdit }: FileManagerGridProps) { const { t } = useTranslation(); const gridRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [dragCounter, setDragCounter] = useState(0); + const [editingName, setEditingName] = useState(''); + const editInputRef = useRef(null); + + // 开始编辑时设置初始名称 + useEffect(() => { + if (editingFile) { + setEditingName(editingFile.name); + // 延迟聚焦以确保DOM已更新 + setTimeout(() => { + editInputRef.current?.focus(); + editInputRef.current?.select(); + }, 0); + } + }, [editingFile]); + + // 处理编辑确认 + const handleEditConfirm = () => { + if (editingFile && onRename && editingName.trim() && editingName !== editingFile.name) { + onRename(editingFile, editingName.trim()); + } + onCancelEdit?.(); + }; + + // 处理编辑取消 + const handleEditCancel = () => { + setEditingName(''); + onCancelEdit?.(); + }; + + // 处理输入框按键 + const handleEditKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleEditConfirm(); + } else if (e.key === 'Escape') { + e.preventDefault(); + handleEditCancel(); + } + }; const [isSelecting, setIsSelecting] = useState(false); const [selectionStart, setSelectionStart] = useState<{ x: number; y: number } | null>(null); const [selectionRect, setSelectionRect] = useState<{ x: number; y: number; width: number; height: number } | null>(null); @@ -480,9 +527,32 @@ export function FileManagerGrid({ {/* 文件名 */}
-

- {file.name} -

+ {editingFile?.path === file.path ? ( + setEditingName(e.target.value)} + onKeyDown={handleEditKeyDown} + onBlur={handleEditConfirm} + className="w-full text-xs bg-white text-black px-1 py-0.5 rounded border-2 border-blue-500 text-center" + onClick={(e) => e.stopPropagation()} + /> + ) : ( +

{ + // 阻止文件选择事件 + if (onStartEdit) { + e.stopPropagation(); + onStartEdit(file); + } + }} + > + {file.name} +

+ )} {file.size && file.type === 'file' && (

{formatFileSize(file.size)} @@ -522,9 +592,32 @@ export function FileManagerGrid({ {/* 文件信息 */}

-

- {file.name} -

+ {editingFile?.path === file.path ? ( + setEditingName(e.target.value)} + onKeyDown={handleEditKeyDown} + onBlur={handleEditConfirm} + className="w-full text-sm bg-white text-black px-2 py-1 rounded border-2 border-blue-500" + onClick={(e) => e.stopPropagation()} + /> + ) : ( +

{ + // 阻止文件选择事件 + if (onStartEdit) { + e.stopPropagation(); + onStartEdit(file); + } + }} + > + {file.name} +

+ )} {file.modified && (

{file.modified} diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx index ea4cf2d3..594e3690 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx @@ -82,6 +82,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { operation: 'copy' | 'cut'; } | null>(null); + // 编辑状态 + const [editingFile, setEditingFile] = useState(null); + const [isCreatingNewFile, setIsCreatingNewFile] = useState(false); + // Hooks const { selectedFiles, @@ -292,17 +296,25 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { async function handleCreateNewFolder() { if (!sshSessionId) return; - const folderName = prompt(t("fileManager.enterFolderName")); - if (!folderName) return; + const defaultName = "New Folder"; + const folderPath = currentPath.endsWith('/') + ? `${currentPath}${defaultName}` + : `${currentPath}/${defaultName}`; try { - const folderPath = currentPath.endsWith('/') - ? `${currentPath}${folderName}` - : `${currentPath}/${folderName}`; - await createSSHFolder(sshSessionId, folderPath); - toast.success(t("fileManager.folderCreatedSuccessfully", { name: folderName })); - loadDirectory(currentPath); + + // 重新加载目录 + await loadDirectory(currentPath); + + // 找到新创建的文件夹并开始编辑 + const newFolder: FileItem = { + name: defaultName, + type: 'directory', + path: folderPath + }; + setEditingFile(newFolder); + setIsCreatingNewFile(true); } catch (error: any) { toast.error(t("fileManager.failedToCreateFolder")); console.error("Create folder failed:", error); @@ -312,17 +324,26 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { async function handleCreateNewFile() { if (!sshSessionId) return; - const fileName = prompt(t("fileManager.enterFileName")); - if (!fileName) return; + const defaultName = "New File.txt"; + const filePath = currentPath.endsWith('/') + ? `${currentPath}${defaultName}` + : `${currentPath}/${defaultName}`; try { - const filePath = currentPath.endsWith('/') - ? `${currentPath}${fileName}` - : `${currentPath}/${fileName}`; - await createSSHFile(sshSessionId, filePath, ""); - toast.success(t("fileManager.fileCreatedSuccessfully", { name: fileName })); - loadDirectory(currentPath); + + // 重新加载目录 + await loadDirectory(currentPath); + + // 找到新创建的文件并开始编辑 + const newFile: FileItem = { + name: defaultName, + type: 'file', + path: filePath, + size: 0 + }; + setEditingFile(newFile); + setIsCreatingNewFile(true); } catch (error: any) { toast.error(t("fileManager.failedToCreateFile")); console.error("Create file failed:", error); @@ -401,13 +422,39 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { } function handleRenameFile(file: FileItem) { + setEditingFile(file); + } + + // 处理重命名确认 + async function handleRenameConfirm(file: FileItem, newName: string) { if (!sshSessionId) return; - const newName = prompt(t("fileManager.enterNewName"), file.name); - if (!newName || newName === file.name) return; + const oldPath = file.path; + const newPath = file.path.replace(file.name, newName); - // TODO: 实现重命名功能 - toast.info("重命名功能正在开发中..."); + try { + await renameSSHItem(sshSessionId, oldPath, newPath); + toast.success(t("fileManager.itemRenamedSuccessfully", { name: newName })); + loadDirectory(currentPath); + } catch (error: any) { + toast.error(t("fileManager.failedToRenameItem")); + console.error("Rename failed:", error); + } + } + + // 开始编辑文件名 + function handleStartEdit(file: FileItem) { + setEditingFile(file); + } + + // 取消编辑 + function handleCancelEdit() { + if (isCreatingNewFile && editingFile) { + // 如果是新建文件/文件夹且取消了编辑,删除刚创建的项目 + handleDeleteFiles([editingFile]); + setIsCreatingNewFile(false); + } + setEditingFile(null); } // 过滤文件 @@ -540,6 +587,10 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { onUpload={handleFilesDropped} onContextMenu={handleContextMenu} viewMode={viewMode} + onRename={handleRenameConfirm} + editingFile={editingFile} + onStartEdit={handleStartEdit} + onCancelEdit={handleCancelEdit} /> {/* 右键菜单 */}