From e0d06e4b2941dee6275c66d04c21084fae1a88e3 Mon Sep 17 00:00:00 2001
From: Yann <1319542051@qq.com>
Date: Wed, 15 Apr 2026 16:04:50 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E4=B8=8A=E4=BC=A0?=
=?UTF-8?q?=E5=BC=B9=E7=AA=97=E6=8B=96=E5=8A=A8=E6=96=87=E4=BB=B6=E6=97=A0?=
=?UTF-8?q?=E6=B3=95=E4=B8=8A=E4=BC=A0=E7=9A=84=E9=97=AE=E9=A2=98|?=
=?UTF-8?q?=E4=BC=98=E5=8C=96=E6=80=A7=E8=83=BD=E7=93=B6=E9=A2=88=E5=AF=BC?=
=?UTF-8?q?=E8=87=B4=E7=9A=84=E5=BB=B6=E8=BF=9F=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/pages/files/components/UploadModal.tsx | 158 +++++++++++++--------
1 file changed, 95 insertions(+), 63 deletions(-)
diff --git a/src/pages/files/components/UploadModal.tsx b/src/pages/files/components/UploadModal.tsx
index 717400c..602ea2c 100644
--- a/src/pages/files/components/UploadModal.tsx
+++ b/src/pages/files/components/UploadModal.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react'
+import { useState, useEffect, useCallback, memo } from 'react'
import { useTransferStore } from '@/store/transfer'
import { Upload, X, FileIcon, FolderUp } from 'lucide-react'
import { toast } from 'sonner'
@@ -28,6 +28,43 @@ interface FileWithPath extends File {
webkitRelativePath: string
}
+// 单个文件 item,memo 避免无关重渲染
+const FileItem = memo(
+ ({
+ index,
+ displayName,
+ onRemove,
+ }: {
+ file: FileWithPath
+ index: number
+ displayName: string
+ onRemove: (index: number) => void
+ }) => (
+
+
+
+
+
+
+ {displayName}
+
+
+
+ {displayName}
+
+
+
+
onRemove(index)}
+ />
+
+ )
+)
+
export default function UploadModal({
open,
onOpenChange,
@@ -62,35 +99,39 @@ export default function UploadModal({
}
}
- const handleRemoveFile = (index: number) => {
- setFileList(fileList.filter((_, i) => i !== index))
- }
+ const handleRemoveFile = useCallback((index: number) => {
+ setFileList((prev) => prev.filter((_, i) => i !== index))
+ }, [])
- const handleDragOver = (e: React.DragEvent) => {
+ const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault()
setIsDragging(true)
- }
+ }, [])
- const handleDragLeave = (e: React.DragEvent) => {
+ const handleDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
- }
+ }, [])
- const handleDrop = async (e: React.DragEvent) => {
+ const handleDrop = useCallback(async (e: React.DragEvent) => {
e.preventDefault()
setIsDragging(false)
const items = Array.from(e.dataTransfer.items)
const files: FileWithPath[] = []
- // 递归读取文件夹
- const readEntry = async (entry: any, path = ''): Promise => {
+ // 递归读取文件夹,并行处理同级 entries
+ const readEntry = (entry: any, path = ''): Promise => {
if (entry.isFile) {
return new Promise((resolve) => {
entry.file((file: File) => {
- const fileWithPath = file as FileWithPath
- fileWithPath.webkitRelativePath = path + file.name
- files.push(fileWithPath)
+ // 直接复用原始 File 对象,避免重新构造 blob
+ Object.defineProperty(file, 'webkitRelativePath', {
+ value: path + file.name,
+ writable: false,
+ configurable: true,
+ })
+ files.push(file as FileWithPath)
resolve()
})
})
@@ -98,35 +139,44 @@ export default function UploadModal({
const dirReader = entry.createReader()
return new Promise((resolve) => {
dirReader.readEntries(async (entries: any[]) => {
- for (const childEntry of entries) {
- await readEntry(childEntry, path + entry.name + '/')
- }
+ // 并行处理同级子项
+ await Promise.all(
+ entries.map((childEntry) =>
+ readEntry(childEntry, path + entry.name + '/')
+ )
+ )
resolve()
})
})
}
+ return Promise.resolve()
}
- // 处理拖拽的项目
- for (const item of items) {
- const entry = item.webkitGetAsEntry?.()
- if (entry) {
- await readEntry(entry)
- }
- }
+ // 并行处理所有顶层拖拽项
+ const entries = items
+ .map((item) => item.webkitGetAsEntry?.())
+ .filter(Boolean)
- if (files.length > 0) {
- setFileList([...fileList, ...files])
- } else {
- // 如果没有通过 webkitGetAsEntry 获取到文件,使用传统方式
- const fallbackFiles = Array.from(e.dataTransfer.files) as FileWithPath[]
- if (!isDirectoryMode && fallbackFiles.length + fileList.length > 10) {
- toast.warning('单次最多上传 10 个文件')
+ if (entries.length > 0) {
+ await Promise.all(entries.map((entry) => readEntry(entry)))
+ if (files.length > 0) {
+ setFileList((prev) => [...prev, ...files])
return
}
- setFileList([...fileList, ...fallbackFiles])
}
- }
+
+ // fallback:使用传统方式
+ const fallbackFiles = Array.from(e.dataTransfer.files) as FileWithPath[]
+ if (!isDirectoryMode && fallbackFiles.length > 0) {
+ setFileList((prev) => {
+ if (!isDirectoryMode && fallbackFiles.length + prev.length > 10) {
+ toast.warning('单次最多上传 10 个文件')
+ return prev
+ }
+ return [...prev, ...fallbackFiles]
+ })
+ }
+ }, [isDirectoryMode])
const handleSubmit = async () => {
if (fileList.length === 0) {
@@ -215,37 +265,19 @@ export default function UploadModal({
{fileList.length > 0 && (
-
- {fileList.map((file, index) => (
-
-
-
-
-
-
-
- {getDisplayName(file)}
-
-
-
- {getDisplayName(file)}
-
-
-
-
-
handleRemoveFile(index)}
+
+
+ {fileList.map((file, index) => (
+
-
- ))}
-
+ ))}
+
+
)}
--
Gitee