From 4ce5d905128ce2b1742b8723bb3bfafce337f44c Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:13:03 +0800 Subject: [PATCH] update --- .../frontend/src/components/FileManager.vue | 35 +++++++++++++------ .../file-manager/useFileManagerContextMenu.ts | 25 ++++++++++--- packages/frontend/src/locales/en-US.json | 1 + packages/frontend/src/locales/zh-CN.json | 1 + 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/packages/frontend/src/components/FileManager.vue b/packages/frontend/src/components/FileManager.vue index c136f1a..c292528 100644 --- a/packages/frontend/src/components/FileManager.vue +++ b/packages/frontend/src/components/FileManager.vue @@ -435,7 +435,7 @@ const handlePaste = () => { const triggerFileUpload = () => { fileInputRef.value?.click(); }; // --- 下载触发器 (定义在此处,供 Composable 使用) --- -const triggerDownload = (item: FileListItem) => { // item 已有类型 +const triggerDownload = (items: FileListItem[]) => { // 修改:接受 FileListItem 数组 // 恢复使用 props.wsDeps.isConnected if (!props.wsDeps.isConnected.value) { alert(t('fileManager.errors.notConnected')); @@ -455,15 +455,30 @@ const triggerDownload = (item: FileListItem) => { // item 已有类型 return; } - const downloadPath = currentSftpManager.value.joinPath(currentSftpManager.value.currentPath.value, item.filename); - const downloadUrl = `/api/v1/sftp/download?connectionId=${currentConnectionId}&remotePath=${encodeURIComponent(downloadPath)}`; - console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Triggering download: ${downloadUrl}`); - const link = document.createElement('a'); - link.href = downloadUrl; - link.setAttribute('download', item.filename); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + // 遍历数组中的每个文件项 + items.forEach(item => { + // 确保只下载文件 + if (!item.attrs.isFile) { + console.warn(`[FileManager ${props.sessionId}-${props.instanceId}] Skipping download for non-file item: ${item.filename}`); + return; + } + + const downloadPath = currentSftpManager.value!.joinPath(currentSftpManager.value!.currentPath.value, item.filename); + const downloadUrl = `/api/v1/sftp/download?connectionId=${currentConnectionId}&remotePath=${encodeURIComponent(downloadPath)}`; + console.log(`[FileManager ${props.sessionId}-${props.instanceId}] Triggering download for ${item.filename}: ${downloadUrl}`); + + // 为每个文件创建一个链接并点击 + const link = document.createElement('a'); + link.href = downloadUrl; + link.setAttribute('download', item.filename); // 使用原始文件名 + document.body.appendChild(link); + link.click(); + + // 稍微延迟移除链接,以确保下载开始 + setTimeout(() => { + document.body.removeChild(link); + }, 100); + }); }; diff --git a/packages/frontend/src/composables/file-manager/useFileManagerContextMenu.ts b/packages/frontend/src/composables/file-manager/useFileManagerContextMenu.ts index 60f850a..284592a 100644 --- a/packages/frontend/src/composables/file-manager/useFileManagerContextMenu.ts +++ b/packages/frontend/src/composables/file-manager/useFileManagerContextMenu.ts @@ -30,7 +30,7 @@ export interface UseFileManagerContextMenuOptions { // --- 回调函数 --- onRefresh: () => void; onUpload: () => void; - onDownload: (item: FileListItem) => void; + onDownload: (items: FileListItem[]) => void; // 修改:接受 FileListItem 数组 onDelete: () => void; // 删除操作现在由外部处理 onRename: (item: FileListItem) => void; onChangePermissions: (item: FileListItem) => void; @@ -97,22 +97,37 @@ export function useFileManagerContextMenu(options: UseFileManagerContextMenuOpti // Build context menu items (使用传入的回调) if (selectionSize > 1 && clickedItemIsSelected) { // Multi-selection menu + const selectedFileItems = Array.from(selectedItems.value) + .map(filename => fileList.value.find(f => f.filename === filename)) + .filter((item): item is FileListItem => !!item); // 过滤掉未找到的项并确保类型 + + const allFilesSelected = selectedFileItems.length === selectionSize && selectedFileItems.every(item => item.attrs.isFile); + menu = [ // 调整顺序:剪切、复制优先 { label: t('fileManager.actions.cut'), action: onCut, disabled: !canPerformActions }, { label: t('fileManager.actions.copy'), action: onCopy, disabled: !canPerformActions }, - // --- 分隔符 (视觉) --- + ]; + + // --- 新增:多选下载 --- + if (allFilesSelected) { + menu.push({ label: t('fileManager.actions.downloadMultiple', { count: selectionSize }), action: () => onDownload(selectedFileItems), disabled: !canPerformActions }); + } + + menu.push( + // --- 分隔符 (视觉) --- { label: t('fileManager.actions.deleteMultiple', { count: selectionSize }), action: onDelete, disabled: !canPerformActions }, // --- 分隔符 (视觉) --- - { label: t('fileManager.actions.refresh'), action: onRefresh, disabled: !canPerformActions }, - ]; + { label: t('fileManager.actions.refresh'), action: onRefresh, disabled: !canPerformActions } + ); } else if (targetItem && targetItem.filename !== '..') { // Single item (not '..') menu + // --- 修改:单选下载也调用接收数组的回调 --- menu = []; // 1. 主要操作 (下载 - 如果是文件) if (targetItem.attrs.isFile) { - menu.push({ label: t('fileManager.actions.download', { name: targetItem.filename }), action: () => onDownload(targetItem), disabled: !canPerformActions }); + menu.push({ label: t('fileManager.actions.download', { name: targetItem.filename }), action: () => onDownload([targetItem]), disabled: !canPerformActions }); // 传递包含单个项的数组 } // 2. 剪切、复制、粘贴 (粘贴 - 如果是文件夹) diff --git a/packages/frontend/src/locales/en-US.json b/packages/frontend/src/locales/en-US.json index 4f65d45..96a79a5 100644 --- a/packages/frontend/src/locales/en-US.json +++ b/packages/frontend/src/locales/en-US.json @@ -250,6 +250,7 @@ "delete": "Delete", "deleteMultiple": "Delete {count} items", "download": "Download", + "downloadMultiple": "Download {count} items", "cancel": "Cancel", "save": "Save", "closeTab": "Close Tab", diff --git a/packages/frontend/src/locales/zh-CN.json b/packages/frontend/src/locales/zh-CN.json index ffe4e5d..fc78d4f 100644 --- a/packages/frontend/src/locales/zh-CN.json +++ b/packages/frontend/src/locales/zh-CN.json @@ -250,6 +250,7 @@ "delete": "删除", "deleteMultiple": "删除 {count} 个项目", "download": "下载", + "downloadMultiple": "下载 {count} 个项目", "cancel": "取消", "save": "保存", "closeTab": "关闭标签页",