From be92ddd55d998ed70372aa0eb606f12f3031f31e Mon Sep 17 00:00:00 2001 From: beibeizi Date: Mon, 16 Mar 2026 16:09:56 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E7=AB=AF=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=9A=E7=BB=88=E7=AB=AF=E6=BB=9A=E5=8A=A8=E4=B8=8E=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/FileBrowser.tsx | 63 ++++++++++++++----- .../src/pages/terminal/TerminalMobile.tsx | 37 ++++++++++- 2 files changed, 85 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/FileBrowser.tsx b/frontend/src/components/FileBrowser.tsx index 2581bd6..6f9149b 100644 --- a/frontend/src/components/FileBrowser.tsx +++ b/frontend/src/components/FileBrowser.tsx @@ -148,6 +148,7 @@ export function FileBrowser({ sessionId, isDark, currentDir, onFileSelect, netwo const [dialogInput, setDialogInput] = React.useState(""); const [chmodRecursive, setChmodRecursive] = React.useState(false); const [chmodLoading, setChmodLoading] = React.useState(false); + const fileInputRef = React.useRef(null); const directoryCacheRef = React.useRef>(new Map()); const treeRef = React.useRef(tree); const selectedPathRef = React.useRef(selectedPath); @@ -517,11 +518,11 @@ export function FileBrowser({ sessionId, isDark, currentDir, onFileSelect, netwo [mapBatchFailures, uploadBatchWithRetry, t] ); - // 处理文件拖拽上传 - const handleDrop = React.useCallback(async (e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - + const handleUploadFiles = React.useCallback(async (allFiles: Array<{ file: File; path: string }>) => { + if (allFiles.length === 0) { + push(t("没有可上传的文件", "No files to upload")); + return; + } setUploading(true); setUploadProgress([]); setLastUploadFailures([]); @@ -530,15 +531,6 @@ export function FileBrowser({ sessionId, isDark, currentDir, onFileSelect, netwo setUploadStats(INITIAL_UPLOAD_STATS); try { - // 读取所有文件(包括文件夹内容) - const dropSnapshot = snapshotDropPayload(e.dataTransfer); - const allFiles = await collectDroppedFiles(dropSnapshot); - - if (allFiles.length === 0) { - push(t("没有可上传的文件", "No files to upload")); - return; - } - const totalBytes = calculateTotalBytes(allFiles); setUploadStats({ current: 0, total: allFiles.length, uploadedBytes: 0, totalBytes }); @@ -639,6 +631,34 @@ export function FileBrowser({ sessionId, isDark, currentDir, onFileSelect, netwo } }, [sessionId, selectedPath, compress, push, loadDirectory, uploadFilesByBatch, t, language]); + // 处理文件拖拽上传 + const handleDrop = React.useCallback(async (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + + try { + // 读取所有文件(包括文件夹内容) + const dropSnapshot = snapshotDropPayload(e.dataTransfer); + const allFiles = await collectDroppedFiles(dropSnapshot); + await handleUploadFiles(allFiles); + } catch (err) { + push(err instanceof Error ? err.message : t("上传失败", "Upload failed")); + } + }, [handleUploadFiles, push, t]); + + const handleFileInputChange = React.useCallback(async (event: React.ChangeEvent) => { + const filesList = event.target.files; + if (!filesList || filesList.length === 0) { + return; + } + const allFiles = Array.from(filesList).map((file) => ({ + file, + path: file.webkitRelativePath || file.name, + })); + await handleUploadFiles(allFiles); + event.target.value = ""; + }, [handleUploadFiles]); + const handleCancelUpload = React.useCallback(() => { uploadCancelledRef.current = true; }, []); @@ -1158,6 +1178,14 @@ export function FileBrowser({ sessionId, isDark, currentDir, onFileSelect, netwo > {t("新建文件夹", "New folder")} + + ) : null} diff --git a/frontend/src/pages/terminal/TerminalMobile.tsx b/frontend/src/pages/terminal/TerminalMobile.tsx index 021a679..0a0c7f3 100644 --- a/frontend/src/pages/terminal/TerminalMobile.tsx +++ b/frontend/src/pages/terminal/TerminalMobile.tsx @@ -27,6 +27,15 @@ export function TerminalMobile({ state, onBack }: TerminalMobileProps) { term.focus(); }, [activeTab, syncTerminalSize, terminalInstance]); + const handleScroll = React.useCallback((direction: "up" | "down") => { + const term = terminalInstance.current; + if (!term) { + return; + } + const lines = direction === "up" ? -8 : 8; + term.scrollLines(lines); + }, [terminalInstance]); + const actions = [ { key: "language", @@ -132,13 +141,39 @@ export function TerminalMobile({ state, onBack }: TerminalMobileProps) {
-
+
state.terminalInstance.current?.focus()} /> +
+ + +