Implement proper file/folder creation workflow with inline editing
- Fix delete API by adding isDirectory parameter for proper directory deletion - Redesign creation logic: edit first, then create on confirm instead of create-then-rename - Remove spaces from default names (NewFile.txt, NewFolder) to avoid path issues - Add temporary items to file list display during editing for immediate visual feedback - Simplify cancel operation: just exit edit mode without server deletion - Reduce API calls from 2 (create+rename) to 1 (direct create with final name) - Fix editing state display issues where new items weren't visible in the interface 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -299,7 +299,13 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
|
|||||||
await ensureSSHConnection();
|
await ensureSSHConnection();
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
await deleteSSHItem(sshSessionId, file.path);
|
await deleteSSHItem(
|
||||||
|
sshSessionId,
|
||||||
|
file.path,
|
||||||
|
file.type === 'directory', // isDirectory
|
||||||
|
currentHost?.id,
|
||||||
|
currentHost?.userId?.toString()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
toast.success(t("fileManager.itemsDeletedSuccessfully", { count: files.length }));
|
toast.success(t("fileManager.itemsDeletedSuccessfully", { count: files.length }));
|
||||||
loadDirectory(currentPath);
|
loadDirectory(currentPath);
|
||||||
@@ -314,90 +320,43 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCreateNewFolder() {
|
function handleCreateNewFolder() {
|
||||||
if (!sshSessionId) return;
|
const defaultName = "NewFolder"; // 移除空格避免路径问题
|
||||||
|
|
||||||
const defaultName = "New Folder";
|
|
||||||
const folderPath = currentPath.endsWith('/')
|
const folderPath = currentPath.endsWith('/')
|
||||||
? `${currentPath}${defaultName}`
|
? `${currentPath}${defaultName}`
|
||||||
: `${currentPath}/${defaultName}`;
|
: `${currentPath}/${defaultName}`;
|
||||||
|
|
||||||
try {
|
// 直接进入编辑模式,不在服务器创建文件夹
|
||||||
// 确保SSH连接有效
|
|
||||||
await ensureSSHConnection();
|
|
||||||
|
|
||||||
// API需要分离的path和folderName参数
|
|
||||||
await createSSHFolder(
|
|
||||||
sshSessionId,
|
|
||||||
currentPath,
|
|
||||||
defaultName,
|
|
||||||
currentHost?.id,
|
|
||||||
currentHost?.userId?.toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 重新加载目录
|
|
||||||
await loadDirectory(currentPath);
|
|
||||||
|
|
||||||
// 找到新创建的文件夹并开始编辑
|
|
||||||
const newFolder: FileItem = {
|
const newFolder: FileItem = {
|
||||||
name: defaultName,
|
name: defaultName,
|
||||||
type: 'directory',
|
type: 'directory',
|
||||||
path: folderPath
|
path: folderPath
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('Starting edit for new folder (not created yet):', newFolder);
|
||||||
setEditingFile(newFolder);
|
setEditingFile(newFolder);
|
||||||
setIsCreatingNewFile(true);
|
setIsCreatingNewFile(true);
|
||||||
} catch (error: any) {
|
console.log('Edit state set:', { editingFile: newFolder, isCreatingNewFile: true });
|
||||||
if (error.message?.includes('connection') || error.message?.includes('established')) {
|
|
||||||
toast.error(`SSH connection failed. Please check your connection to ${currentHost?.name} (${currentHost?.ip}:${currentHost?.port})`);
|
|
||||||
} else {
|
|
||||||
toast.error(t("fileManager.failedToCreateFolder"));
|
|
||||||
}
|
|
||||||
console.error("Create folder failed:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCreateNewFile() {
|
function handleCreateNewFile() {
|
||||||
if (!sshSessionId) return;
|
const defaultName = "NewFile.txt"; // 移除空格避免路径问题
|
||||||
|
|
||||||
const defaultName = "New File.txt";
|
|
||||||
const filePath = currentPath.endsWith('/')
|
const filePath = currentPath.endsWith('/')
|
||||||
? `${currentPath}${defaultName}`
|
? `${currentPath}${defaultName}`
|
||||||
: `${currentPath}/${defaultName}`;
|
: `${currentPath}/${defaultName}`;
|
||||||
|
|
||||||
try {
|
// 直接进入编辑模式,不在服务器创建文件
|
||||||
// 确保SSH连接有效
|
|
||||||
await ensureSSHConnection();
|
|
||||||
|
|
||||||
// API需要分离的path、fileName和content参数
|
|
||||||
await createSSHFile(
|
|
||||||
sshSessionId,
|
|
||||||
currentPath,
|
|
||||||
defaultName,
|
|
||||||
"",
|
|
||||||
currentHost?.id,
|
|
||||||
currentHost?.userId?.toString()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 重新加载目录
|
|
||||||
await loadDirectory(currentPath);
|
|
||||||
|
|
||||||
// 找到新创建的文件并开始编辑
|
|
||||||
const newFile: FileItem = {
|
const newFile: FileItem = {
|
||||||
name: defaultName,
|
name: defaultName,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
path: filePath,
|
path: filePath,
|
||||||
size: 0
|
size: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('Starting edit for new file (not created yet):', newFile);
|
||||||
setEditingFile(newFile);
|
setEditingFile(newFile);
|
||||||
setIsCreatingNewFile(true);
|
setIsCreatingNewFile(true);
|
||||||
} catch (error: any) {
|
console.log('Edit state set:', { editingFile: newFile, isCreatingNewFile: true });
|
||||||
if (error.message?.includes('connection') || error.message?.includes('established')) {
|
|
||||||
toast.error(`SSH connection failed. Please check your connection to ${currentHost?.name} (${currentHost?.ip}:${currentHost?.port})`);
|
|
||||||
} else {
|
|
||||||
toast.error(t("fileManager.failedToCreateFile"));
|
|
||||||
}
|
|
||||||
console.error("Create file failed:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFileOpen(file: FileItem) {
|
function handleFileOpen(file: FileItem) {
|
||||||
@@ -507,27 +466,81 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理重命名确认
|
// 处理重命名/创建确认
|
||||||
async function handleRenameConfirm(file: FileItem, newName: string) {
|
async function handleRenameConfirm(file: FileItem, newName: string) {
|
||||||
if (!sshSessionId) return;
|
if (!sshSessionId) return;
|
||||||
|
|
||||||
const oldPath = file.path;
|
|
||||||
const newPath = file.path.replace(file.name, newName);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 确保SSH连接有效
|
// 确保SSH连接有效
|
||||||
await ensureSSHConnection();
|
await ensureSSHConnection();
|
||||||
|
|
||||||
await renameSSHItem(sshSessionId, oldPath, newPath);
|
if (isCreatingNewFile) {
|
||||||
|
// 新建项目:直接创建最终名字
|
||||||
|
console.log('Creating new item:', {
|
||||||
|
type: file.type,
|
||||||
|
name: newName,
|
||||||
|
path: currentPath,
|
||||||
|
hostId: currentHost?.id,
|
||||||
|
userId: currentHost?.userId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file.type === 'file') {
|
||||||
|
await createSSHFile(
|
||||||
|
sshSessionId,
|
||||||
|
currentPath,
|
||||||
|
newName,
|
||||||
|
"",
|
||||||
|
currentHost?.id,
|
||||||
|
currentHost?.userId?.toString()
|
||||||
|
);
|
||||||
|
toast.success(t("fileManager.fileCreatedSuccessfully", { name: newName }));
|
||||||
|
} else if (file.type === 'directory') {
|
||||||
|
await createSSHFolder(
|
||||||
|
sshSessionId,
|
||||||
|
currentPath,
|
||||||
|
newName,
|
||||||
|
currentHost?.id,
|
||||||
|
currentHost?.userId?.toString()
|
||||||
|
);
|
||||||
|
toast.success(t("fileManager.folderCreatedSuccessfully", { name: newName }));
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsCreatingNewFile(false);
|
||||||
|
} else {
|
||||||
|
// 现有项目:重命名
|
||||||
|
console.log('Renaming existing item:', {
|
||||||
|
from: file.path,
|
||||||
|
to: newName,
|
||||||
|
hostId: currentHost?.id,
|
||||||
|
userId: currentHost?.userId
|
||||||
|
});
|
||||||
|
|
||||||
|
await renameSSHItem(
|
||||||
|
sshSessionId,
|
||||||
|
file.path,
|
||||||
|
newName,
|
||||||
|
currentHost?.id,
|
||||||
|
currentHost?.userId?.toString()
|
||||||
|
);
|
||||||
toast.success(t("fileManager.itemRenamedSuccessfully", { name: newName }));
|
toast.success(t("fileManager.itemRenamedSuccessfully", { name: newName }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除编辑状态
|
||||||
|
setEditingFile(null);
|
||||||
loadDirectory(currentPath);
|
loadDirectory(currentPath);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.error("Rename failed with error:", {
|
||||||
|
error,
|
||||||
|
oldPath,
|
||||||
|
newName,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
|
||||||
if (error.message?.includes('connection') || error.message?.includes('established')) {
|
if (error.message?.includes('connection') || error.message?.includes('established')) {
|
||||||
toast.error(`SSH connection failed. Please check your connection to ${currentHost?.name} (${currentHost?.ip}:${currentHost?.port})`);
|
toast.error(`SSH connection failed. Please check your connection to ${currentHost?.name} (${currentHost?.ip}:${currentHost?.port})`);
|
||||||
} else {
|
} else {
|
||||||
toast.error(t("fileManager.failedToRenameItem"));
|
toast.error(t("fileManager.failedToRenameItem"));
|
||||||
}
|
}
|
||||||
console.error("Rename failed:", error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,11 +559,20 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
|
|||||||
setEditingFile(null);
|
setEditingFile(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过滤文件
|
// 过滤文件并添加新建的临时项目
|
||||||
const filteredFiles = files.filter(file =>
|
let filteredFiles = files.filter(file =>
|
||||||
file.name.toLowerCase().includes(searchQuery.toLowerCase())
|
file.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 如果正在创建新项目,将其添加到列表中
|
||||||
|
if (isCreatingNewFile && editingFile) {
|
||||||
|
// 检查是否已经存在同名项目,避免重复
|
||||||
|
const exists = filteredFiles.some(f => f.path === editingFile.path);
|
||||||
|
if (!exists && editingFile.name.toLowerCase().includes(searchQuery.toLowerCase())) {
|
||||||
|
filteredFiles = [editingFile, ...filteredFiles]; // 将新项目放在前面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentHost) {
|
if (!currentHost) {
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex items-center justify-center">
|
<div className="h-full flex items-center justify-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user