diff --git a/frontend/src/components/RenameFolderModal.tsx b/frontend/src/components/RenameFolderModal.tsx new file mode 100644 index 0000000..53c6347 --- /dev/null +++ b/frontend/src/components/RenameFolderModal.tsx @@ -0,0 +1,96 @@ +import { useEffect, useState } from 'react' +import Modal from './Modal' + +export default function RenameFolderModal({ + open, + initialName, + onClose, + onSubmit, +}: { + open: boolean + initialName: string + onClose: () => void + onSubmit: (name: string) => Promise +}) { + const [name, setName] = useState('') + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState(null) + + useEffect(() => { + if (!open) return + setName(initialName) + setSubmitting(false) + setError(null) + }, [initialName, open]) + + if (!open) return null + + async function handleSave() { + const trimmedName = name.trim() + if (!trimmedName) { + setError('请输入文件夹名称') + return + } + + if (trimmedName === initialName) { + onClose() + return + } + + setSubmitting(true) + setError(null) + try { + await onSubmit(trimmedName) + onClose() + } catch (err) { + const message = + (err as { response?: { data?: { message?: string; error?: string } } }).response?.data?.message || '重命名文件夹失败' + setError(message) + } finally { + setSubmitting(false) + } + } + + return ( + + + + + } + > +
+ + + {error ?
{error}
: null} +
+
+ ) +} diff --git a/frontend/src/pages/WorkspacePage.tsx b/frontend/src/pages/WorkspacePage.tsx index 6f78a8d..ae777d1 100644 --- a/frontend/src/pages/WorkspacePage.tsx +++ b/frontend/src/pages/WorkspacePage.tsx @@ -52,6 +52,7 @@ import BatchCommandModal from '../components/BatchCommandModal' import ChangePasswordModal from '../components/ChangePasswordModal' import ConnectionModal from '../components/ConnectionModal' import FolderModal from '../components/FolderModal' +import RenameFolderModal from '../components/RenameFolderModal' import SessionTree from '../components/SessionTree' import SettingsModal from '../components/SettingsModal' import SftpPane from '../components/SftpPane' @@ -119,6 +120,7 @@ export default function WorkspacePage({ const [selectedFiles, setSelectedFiles] = useState([]) const [showConnectionModal, setShowConnectionModal] = useState(false) const [showFolderModal, setShowFolderModal] = useState(false) + const [renameFolderNodeId, setRenameFolderNodeId] = useState(null) const [editingConnection, setEditingConnection] = useState(null) const [showBatchModal, setShowBatchModal] = useState(false) const [showTransferModal, setShowTransferModal] = useState(initialTool === 'transfers') @@ -366,6 +368,13 @@ export default function WorkspacePage({ await persistTreeLayout(nextLayout) } + async function handleRenameFolderSubmit(newName: string) { + if (!renameFolderNodeId) return + const nextLayout = renameSessionTreeNode(getCurrentTreeLayout(), renameFolderNodeId, newName) + if (!nextLayout) return + await persistTreeLayout(nextLayout) + } + function handleOpenTreeContextMenu(payload: { nodeId: string; nodeType: TreeContextMenuTargetType; x: number; y: number }) { const position = getClampedContextMenuPosition(payload.x, payload.y, payload.nodeType === 'folder' ? 4 : 2) setSelectedNodeId(payload.nodeId) @@ -429,13 +438,7 @@ export default function WorkspacePage({ const targetNode = findTreeNode(targetId) if (!targetNode || targetNode.type !== 'folder') return - const nextName = window.prompt('重命名文件夹', targetNode.name) - const trimmedName = nextName?.trim() - if (!trimmedName || trimmedName === targetNode.name) return - - const nextLayout = renameSessionTreeNode(getCurrentTreeLayout(), targetId, trimmedName) - if (!nextLayout) return - await persistTreeLayout(nextLayout) + setRenameFolderNodeId(targetId) return } @@ -582,15 +585,6 @@ export default function WorkspacePage({
-
- - setSearch(event.target.value)} - /> -
+
+ + setSearch(event.target.value)} + /> +
setShowFolderModal(false)} onSubmit={handleCreateFolder} /> + setRenameFolderNodeId(null)} + onSubmit={handleRenameFolderSubmit} + />