Skip to content

Commit e73d08c

Browse files
committed
feat(frontend): improve task file download dialog
1 parent b942f37 commit e73d08c

File tree

3 files changed

+380
-22
lines changed

3 files changed

+380
-22
lines changed

frontend/src/components/console/task/file-actions-dropdown.tsx

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
AlertDialogTitle,
1919
} from "@/components/ui/alert-dialog"
2020
import { IconCopy, IconDotsVertical, IconDownload, IconFile, IconFolder, IconReload, IconTrash, IconTransfer, IconUpload } from "@tabler/icons-react"
21-
import { normalizePath, downloadFile } from "@/utils/common"
21+
import { normalizePath } from "@/utils/common"
2222
import { apiRequest } from "@/utils/requestUtils"
2323
import { toast } from "sonner"
2424
import { RepoFileEntryMode, type RepoFileStatus } from "./task-shared"
@@ -27,6 +27,13 @@ import CreateFileDialog from "@/components/console/files/create-file"
2727
import UploadFileDialog from "@/components/console/files/upload-file"
2828
import CopyFileDialog from "@/components/console/files/copy"
2929
import MoveFileDialog from "@/components/console/files/move"
30+
import { FileDownloadDialog } from "./file-download-dialog"
31+
32+
type WindowWithSaveFilePicker = Window & {
33+
showSaveFilePicker?: (options?: {
34+
suggestedName?: string
35+
}) => Promise<FileSystemFileHandle>
36+
}
3037

3138
interface FileActionsDropdownProps {
3239
file: RepoFileStatus
@@ -48,12 +55,14 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
4855
const [uploadFileDialogOpen, setUploadFileDialogOpen] = useState(false)
4956
const [copyFileDialogOpen, setCopyFileDialogOpen] = useState(false)
5057
const [moveFileDialogOpen, setMoveFileDialogOpen] = useState(false)
58+
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false)
59+
const [downloadFileHandle, setDownloadFileHandle] = useState<FileSystemFileHandle | null>(null)
5160

5261
const isDirectory = file.entry_mode === RepoFileEntryMode.RepoEntryModeTree
5362
// 完整路径用于 API 调用
5463
const filePath = normalizePath('/workspace/' + file.path)
55-
// 显示路径用于界面展示(不含 /workspace 前缀)
56-
const displayPath = file.path
64+
// 展示路径统一使用完整工作区路径
65+
const displayPath = filePath
5766
const downloadFilename = isDirectory ? `${file.name}.zip` : file.name
5867
const fileType = isDirectory ? '目录' : '文件'
5968

@@ -74,6 +83,26 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
7483
setDeleteDialogOpen(false)
7584
}
7685

86+
const handleDownloadClick = async () => {
87+
let nextFileHandle: FileSystemFileHandle | null = null
88+
const typedWindow = window as WindowWithSaveFilePicker
89+
90+
if (typeof typedWindow.showSaveFilePicker === "function") {
91+
try {
92+
nextFileHandle = await typedWindow.showSaveFilePicker({
93+
suggestedName: downloadFilename,
94+
})
95+
} catch (error) {
96+
if (error instanceof DOMException && error.name === "AbortError") {
97+
return
98+
}
99+
}
100+
}
101+
102+
setDownloadFileHandle(nextFileHandle)
103+
setDownloadDialogOpen(true)
104+
}
105+
77106
return (
78107
<>
79108
<DropdownMenu>
@@ -128,13 +157,8 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
128157
移动
129158
</DropdownMenuItem>
130159
)}
131-
<DropdownMenuItem onClick={async () => {
132-
if (!envid) return
133-
try {
134-
await downloadFile(envid, filePath, downloadFilename)
135-
} catch (error) {
136-
toast.error('下载失败:' + (error instanceof Error ? error.message : '未知错误'))
137-
}
160+
<DropdownMenuItem onClick={() => {
161+
void handleDownloadClick()
138162
}}>
139163
<IconDownload className="h-4 w-4" />
140164
下载
@@ -170,14 +194,32 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
170194
</AlertDialogContent>
171195
</AlertDialog>
172196

197+
{envid && (
198+
<FileDownloadDialog
199+
open={downloadDialogOpen}
200+
onOpenChange={(open) => {
201+
setDownloadDialogOpen(open)
202+
if (!open) {
203+
setDownloadFileHandle(null)
204+
}
205+
}}
206+
envid={envid}
207+
filePath={filePath}
208+
displayPath={displayPath}
209+
downloadFilename={downloadFilename}
210+
fileName={file.name}
211+
fileType={fileType}
212+
fileHandle={downloadFileHandle}
213+
/>
214+
)}
215+
173216
{/* 创建文件夹对话框 */}
174217
{envid && (
175218
<CreateFolderDialog
176219
open={createFolderDialogOpen}
177220
onOpenChange={setCreateFolderDialogOpen}
178221
targetDir={displayPath}
179222
envid={envid}
180-
baseDir="/workspace"
181223
onSuccess={onSuccess}
182224
/>
183225
)}
@@ -189,7 +231,6 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
189231
onOpenChange={setCreateFileDialogOpen}
190232
targetDir={displayPath}
191233
envid={envid}
192-
baseDir="/workspace"
193234
onSuccess={onSuccess}
194235
/>
195236
)}
@@ -201,7 +242,6 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
201242
onOpenChange={setUploadFileDialogOpen}
202243
targetDir={displayPath}
203244
envid={envid}
204-
baseDir="/workspace"
205245
onSuccess={onSuccess}
206246
/>
207247
)}
@@ -213,7 +253,6 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
213253
onOpenChange={setCopyFileDialogOpen}
214254
sourcePath={displayPath}
215255
envid={envid}
216-
baseDir="/workspace"
217256
onSuccess={onSuccess}
218257
/>
219258
)}
@@ -225,7 +264,6 @@ export function FileActionsDropdown({ file, envid, onRefresh, onSuccess, alwaysV
225264
onOpenChange={setMoveFileDialogOpen}
226265
sourcePath={displayPath}
227266
envid={envid}
228-
baseDir="/workspace"
229267
onSuccess={onSuccess}
230268
/>
231269
)}

0 commit comments

Comments
 (0)