{fileList.map((file) => {
- const isSelected = selectedKeys.includes(file.id)
+ const isSelected = selectedSet.has(file.id)
const isDragging = dragState.draggedItems.some(
(f) => f.id === file.id
)
const isDropTarget = file.isDir && dragState.dropTargetId === file.id
const isMultiSelected = selectedKeys.length > 1 && isSelected
- const selectedFiles = fileList.filter((f) =>
- selectedKeys.includes(f.id)
- )
- const hasUnfavorited = selectedFiles.some((f) => !f.isFavorite)
- const downloadableFiles = selectedFiles.filter((f) => !f.isDir)
return (
diff --git a/src/pages/files/hooks/useFileList.ts b/src/pages/files/hooks/useFileList.ts
index a37358f..a158b69 100644
--- a/src/pages/files/hooks/useFileList.ts
+++ b/src/pages/files/hooks/useFileList.ts
@@ -209,6 +209,16 @@ export function useFileList() {
fetchInitial()
}, [fetchInitial])
+ /** 本地更新部分文件字段,避免不必要的列表刷新 */
+ const updateFileItems = useCallback(
+ (ids: string[], patch: Partial) => {
+ setFileList((prev) =>
+ prev.map((f) => (ids.includes(f.id) ? { ...f, ...patch } : f))
+ )
+ },
+ []
+ )
+
const handleSortChange = useCallback(
(field: string, direction: SortOrder) => {
setOrderBy(field)
@@ -263,6 +273,7 @@ export function useFileList() {
enterFolder,
navigateToFolder,
refresh,
+ updateFileItems,
commitSearch,
handleSortChange,
}
diff --git a/src/pages/files/hooks/useFileOperations.ts b/src/pages/files/hooks/useFileOperations.ts
index c401e88..256ccb3 100644
--- a/src/pages/files/hooks/useFileOperations.ts
+++ b/src/pages/files/hooks/useFileOperations.ts
@@ -14,7 +14,8 @@ import { openFilePreviewWithToken } from '@/utils/preview'
export function useFileOperations(
refreshCallback: () => void,
clearSelectionCallback?: () => void,
- onCreateFolderSuccess?: () => void
+ onCreateFolderSuccess?: () => void,
+ updateFileItemsCallback?: (ids: string[], patch: Partial) => void
) {
// 模态框状态
const [createFolderModalVisible, setCreateFolderModalVisible] =
@@ -213,6 +214,7 @@ export function useFileOperations(
/**
* 收藏/取消收藏
+ * 使用乐观更新:直接修改本地状态,失败时回滚刷新列表
*/
const handleFavorite = useCallback(
async (files: FileItem | FileItem[]) => {
@@ -221,6 +223,11 @@ export function useFileOperations(
// 判断是收藏还是取消收藏(如果有任何一个未收藏,就执行收藏操作)
const hasUnfavorited = fileArray.some((f) => !f.isFavorite)
+ const newFavoriteState = hasUnfavorited
+
+ // 乐观更新本地状态
+ updateFileItemsCallback?.(fileIds, { isFavorite: newFavoriteState })
+ clearSelectionCallback?.()
try {
if (hasUnfavorited) {
@@ -230,13 +237,13 @@ export function useFileOperations(
await unfavoriteFile(fileIds)
toast.success('取消收藏成功')
}
- clearSelectionCallback?.()
- refreshCallback()
} catch (error) {
+ // 失败时回滚:刷新列表恢复真实状态
+ updateFileItemsCallback?.(fileIds, { isFavorite: !newFavoriteState })
toast.error(hasUnfavorited ? '收藏失败' : '取消收藏失败')
}
},
- [refreshCallback, clearSelectionCallback]
+ [clearSelectionCallback, updateFileItemsCallback]
)
/**
diff --git a/src/pages/files/index.tsx b/src/pages/files/index.tsx
index 21c6745..f58ff33 100644
--- a/src/pages/files/index.tsx
+++ b/src/pages/files/index.tsx
@@ -89,7 +89,7 @@ export default function FilesPage() {
if (isFavoritesView || isRecentsView || isTypeFilter || isDirFilter) {
navigate(`/files?viewMode=${viewMode}`)
}
- })
+ }, fileList.updateFileItems)
// 计算当前视图类型
const viewType = searchParams.get('view')
--
Gitee
From 5b1aec2604d4d0161a8ea1df7dcadbc38c6927eb Mon Sep 17 00:00:00 2001
From: Yann <1319542051@qq.com>
Date: Thu, 16 Apr 2026 00:08:01 +0800
Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E9=A1=B5=E9=9D=A2?=
=?UTF-8?q?=E8=AD=A6=E5=91=8A=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/ui/dialog.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
index c6e95c2..6ece881 100644
--- a/src/components/ui/dialog.tsx
+++ b/src/components/ui/dialog.tsx
@@ -36,11 +36,13 @@ type DialogContentProps = React.ComponentPropsWithoutRef<
const DialogContent = React.forwardRef<
React.ElementRef,
DialogContentProps
->(({ className, children, hideClose, ...props }, ref) => (
+>(({ className, children, hideClose, 'aria-describedby': ariaDescribedby, ...props }, ref) => (