diff --git a/src/pages/files/components/UploadModal.tsx b/src/pages/files/components/UploadModal.tsx
index 717400c391e053fbfd43f6fb7fda26f5ea2e91c2..602ea2caf00a4fe923230e12c588674e56c43229 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) => (
+
-
- ))}
-
+ ))}
+
+
)}