diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx index 9e72d17e..4b00ee33 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerGrid.tsx @@ -15,7 +15,8 @@ import { ChevronRight, MoreHorizontal, RefreshCw, - ArrowUp + ArrowUp, + FileSymlink } from "lucide-react"; import { useTranslation } from "react-i18next"; import type { FileItem } from "../../../types/index.js"; @@ -64,14 +65,18 @@ interface FileManagerGridProps { onCancelEdit?: () => void; } -const getFileIcon = (fileName: string, isDirectory: boolean, viewMode: 'grid' | 'list' = 'grid') => { +const getFileIcon = (file: FileItem, viewMode: 'grid' | 'list' = 'grid') => { const iconClass = viewMode === 'grid' ? "w-8 h-8" : "w-6 h-6"; - if (isDirectory) { + if (file.type === 'directory') { return ; } - const ext = fileName.split('.').pop()?.toLowerCase(); + if (file.type === 'link') { + return ; + } + + const ext = file.name.split('.').pop()?.toLowerCase(); switch (ext) { case 'txt': @@ -273,6 +278,12 @@ export function FileManagerGrid({ e.stopPropagation(); }, []); + // 滚轮事件处理,确保滚动正常工作 + const handleWheel = useCallback((e: React.WheelEvent) => { + // 不阻止默认滚动行为,让浏览器自己处理滚动 + e.stopPropagation(); + }, []); + const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -394,7 +405,7 @@ export function FileManagerGrid({ } return ( -
+
{/* 工具栏和路径导航 */}
{/* 导航按钮 */} @@ -463,42 +474,44 @@ export function FileManagerGrid({
- {/* 主文件网格 */} -
onContextMenu?.(e)} - tabIndex={0} - > - {isDragging && ( -
-
- -

- {t("fileManager.dragFilesToUpload")} -

+ {/* 主文件网格 - 滚动区域 */} +
+
onContextMenu?.(e)} + tabIndex={0} + > + {isDragging && ( +
+
+ +

+ {t("fileManager.dragFilesToUpload")} +

+
-
- )} + )} - {files.length === 0 ? ( -
-
- -

{t("fileManager.emptyFolder")}

+ {files.length === 0 ? ( +
+
+ +

{t("fileManager.emptyFolder")}

+
-
- ) : viewMode === 'grid' ? ( -
- {files.map((file) => { + ) : viewMode === 'grid' ? ( +
+ {files.map((file) => { const isSelected = selectedFiles.some(f => f.path === file.path); // 详细调试路径比较 @@ -531,11 +544,11 @@ export function FileManagerGrid({
{/* 文件图标 */}
- {getFileIcon(file.name, file.type === 'directory', viewMode)} + {getFileIcon(file, viewMode)}
{/* 文件名 */} -
+
{editingFile?.path === file.path ? ( ) : (

{ // 阻止文件选择事件 @@ -571,6 +584,11 @@ export function FileManagerGrid({ {formatFileSize(file.size)}

)} + {file.type === 'link' && file.linkTarget && ( +

+ → {file.linkTarget} +

+ )}
@@ -600,7 +618,7 @@ export function FileManagerGrid({ > {/* 文件图标 */}
- {getFileIcon(file.name, file.type === 'directory', viewMode)} + {getFileIcon(file, viewMode)}
{/* 文件信息 */} @@ -622,7 +640,7 @@ export function FileManagerGrid({ /> ) : (

{ // 阻止文件选择事件 @@ -635,6 +653,11 @@ export function FileManagerGrid({ {file.name}

)} + {file.type === 'link' && file.linkTarget && ( +

+ → {file.linkTarget} +

+ )} {file.modified && (

{file.modified} @@ -664,6 +687,7 @@ export function FileManagerGrid({ })}

)} +
{/* 状态栏 */} diff --git a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx index 8169aad0..3ec6e2ec 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManagerModern.tsx @@ -30,7 +30,8 @@ import { deleteSSHItem, renameSSHItem, connectSSH, - getSSHStatus + getSSHStatus, + identifySSHSymlink } from "@/ui/main-axios.ts"; @@ -345,9 +346,95 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) { setIsCreatingNewFile(true); } - function handleFileOpen(file: FileItem) { + // Handle symlink resolution + const handleSymlinkClick = async (file: FileItem) => { + if (!currentHost || !sshSessionId) { + toast.error(t("fileManager.noSSHConnection")); + return; + } + + try { + // 确保SSH连接有效 + let currentSessionId = sshSessionId; + try { + const status = await getSSHStatus(currentSessionId); + if (!status.connected) { + const result = await connectSSH(currentSessionId, { + hostId: currentHost.id, + host: currentHost.ip, + port: currentHost.port, + username: currentHost.username, + authType: currentHost.authType, + password: currentHost.password, + key: currentHost.key, + keyPassword: currentHost.keyPassword, + credentialId: currentHost.credentialId + }); + + if (!result.success) { + throw new Error(t("fileManager.failedToReconnectSSH")); + } + } + } catch (sessionErr) { + throw sessionErr; + } + + const symlinkInfo = await identifySSHSymlink(currentSessionId, file.path); + + if (symlinkInfo.type === "directory") { + // 如果软链接指向目录,导航到它 + setCurrentPath(symlinkInfo.target); + } else if (symlinkInfo.type === "file") { + // 如果软链接指向文件,打开文件 + // 计算窗口位置(稍微错开) + const windowCount = Date.now() % 10; + const offsetX = 120 + (windowCount * 30); + const offsetY = 120 + (windowCount * 30); + + // 创建目标文件对象 + const targetFile: FileItem = { + ...file, + path: symlinkInfo.target + }; + + // 创建窗口组件工厂函数 + const createWindowComponent = (windowId: string) => ( + + ); + + openWindow({ + title: file.name, + x: offsetX, + y: offsetY, + width: 800, + height: 600, + isMaximized: false, + isMinimized: false, + component: createWindowComponent + }); + } + } catch (error: any) { + toast.error( + error?.response?.data?.error || + error?.message || + t("fileManager.failedToResolveSymlink") + ); + } + }; + + async function handleFileOpen(file: FileItem) { if (file.type === 'directory') { setCurrentPath(file.path); + } else if (file.type === 'link') { + // 处理软链接 + await handleSymlinkClick(file); } else { // 在新窗口中打开文件 if (!sshSessionId) {