diff --git a/src/hooks/team/useTeamMemberships.js b/src/hooks/team/useTeamMemberships.js index dcd4fea..f116f48 100644 --- a/src/hooks/team/useTeamMemberships.js +++ b/src/hooks/team/useTeamMemberships.js @@ -10,7 +10,7 @@ export const useTeamMembership = (teamId) => { setLoading(true); try { - const result = await SupabaseService.select('team_memberships', { + const result = await supabaseService.select('team_memberships', { select: '*', relations: { user: 'id, email, name' diff --git a/src/hooks/team/useTeams.js b/src/hooks/team/useTeams.js index a995438..cbd2482 100644 --- a/src/hooks/team/useTeams.js +++ b/src/hooks/team/useTeams.js @@ -4,7 +4,7 @@ export const useTeams = () => { // 获取团队列表 const fetchTeams = async (params = {}) => { try { - const result = await SupabaseService.select('teams', { + const result = await supabaseService.select('teams', { select: ` id, name, diff --git a/src/pages/notFound/index.jsx b/src/pages/notFound/index.jsx index 2cc6440..323a4c7 100644 --- a/src/pages/notFound/index.jsx +++ b/src/pages/notFound/index.jsx @@ -15,7 +15,7 @@ const NotFound = () => { , diff --git a/src/pages/resource/bucket/index.jsx b/src/pages/resource/bucket/index.jsx index 533f6f0..f0b0d5f 100644 --- a/src/pages/resource/bucket/index.jsx +++ b/src/pages/resource/bucket/index.jsx @@ -24,6 +24,7 @@ import { InboxOutlined, SearchOutlined, EditOutlined, + FolderOutlined, } from "@ant-design/icons"; import MonacoEditor from "@monaco-editor/react"; import InfiniteScroll from 'react-infinite-scroll-component'; @@ -70,6 +71,8 @@ const StorageManager = () => { const LOAD_MORE_SIZE = 100; // 每次加载100条 const [isRenaming, setIsRenaming] = useState(false); const [newFileName, setNewFileName] = useState(""); + const [currentPath, setCurrentPath] = useState(""); // 添加当前路径状态 + const [pathHistory, setPathHistory] = useState([]); // 添加路径历史记录 // 文件图标映射 const getFileIcon = (file) => { @@ -100,19 +103,29 @@ const StorageManager = () => { try { const { data, error } = await supabase.storage .from("file") - .list("", { + .list(currentPath, { // 使用当前路径 limit: isInitial ? INITIAL_LOAD_SIZE : LOAD_MORE_SIZE, offset: isInitial ? 0 : displayFiles.length, sortBy: { column: "created_at", order: "desc" }, }); if (error) throw error; - if (isInitial) { - setDisplayFiles(data || []); - } else { - setDisplayFiles(prev => [...prev, ...(data || [])]); - } + + // 对数据进行排序,文件夹在前 + const sortedData = (data || []).sort((a, b) => { + // 首先按照类型排序(文件夹在前) + if (a.metadata?.isFolder !== b.metadata?.isFolder) { + return a.metadata?.isFolder ? -1 : 1; + } + // 然后按照时间排序 + return new Date(b.created_at) - new Date(a.created_at); + }); + if (isInitial) { + setDisplayFiles(sortedData); + } else { + setDisplayFiles(prev => [...prev, ...sortedData]); + } setHasMore(data?.length === (isInitial ? INITIAL_LOAD_SIZE : LOAD_MORE_SIZE)); } catch (error) { @@ -208,9 +221,11 @@ const StorageManager = () => { if (selectedType === "全部") return matchesSearch; const mimetype = file.metadata?.mimetype || ''; - return matchesType && FILE_TYPES[selectedType]?.some(type => + const matchesType = FILE_TYPES[selectedType]?.some(type => mimetype.startsWith(type) || mimetype === type ); + + return matchesSearch && matchesType; }); }, [displayFiles, searchText, selectedType]); @@ -258,7 +273,7 @@ const StorageManager = () => { fetchAllFiles(); }, []); - // 判断是否是图片 + // 判断是否是��片 const isImage = (file) => { return file.metadata?.mimetype?.startsWith("image/"); }; @@ -349,12 +364,21 @@ const StorageManager = () => { try { const { error } = await supabase.storage.from("file").remove([fileName]); if (error) throw error; + message.success("文件删除成功"); - fetchAllFiles(); + + // 直接从本地状态中移除被删除的文件 + setDisplayFiles(prev => prev.filter(file => file.name !== fileName)); + + // 如果删除的是当前选中的文件,清空预览 if (selectedFile?.name === fileName) { setSelectedFile(null); setFileContent(""); } + + // 更新文件类型统计 + // 注意:typeStats 是通过 useMemo 自动计算的,不需要手动更新 + } catch (error) { message.error(`删除失败: ${error.message}`); } @@ -367,7 +391,7 @@ const StorageManager = () => { // 加载更多数据 const loadMoreFiles = () => { - if (!hasMore || loading || hasFilters) return; // 有过滤条件��不加载更多 + if (!hasMore || loading || hasFilters) return; // 有过滤条件不加载更多 fetchAllFiles(false); }; @@ -406,21 +430,64 @@ const StorageManager = () => { return '其他'; }; - // 渲染文件列表 + // 渲染加载状态 + const LoadingSpinner = () => ( +
+
+
+ ); + + // 处理文件夹点击 + const handleFolderClick = (folderName) => { + const newPath = currentPath ? `${currentPath}/${folderName}` : folderName; + setPathHistory(prev => [...prev, currentPath]); + setCurrentPath(newPath); + setDisplayFiles([]); // 清空当前列表 + setHasMore(true); // 重置加载更多状态 + fetchAllFiles(true); // 重新获取文件列表 + }; + + // 返回上一级 + const handleBack = () => { + const previousPath = pathHistory[pathHistory.length - 1]; + setPathHistory(prev => prev.slice(0, -1)); + setCurrentPath(previousPath); + setDisplayFiles([]); // 清空当前列表 + setHasMore(true); // 重置加载更多状态 + fetchAllFiles(true); // 重新获取文件列表 + }; + + // 修改文件列表渲染 const renderFileList = () => ( -
+
+ {/* 面包屑导航 */} + {currentPath && ( +
+
+ + / + {currentPath} +
+
+ )} + -
-
- } + hasMore={!hasFilters && hasMore} + loader={} scrollableTarget="scrollableDiv" endMessage={ -

+

{displayFiles.length > 0 ? hasFilters ? "已显示所有匹配文件" @@ -430,66 +497,80 @@ const StorageManager = () => { } > ( previewFile(file)} - actions={[ - { - e?.stopPropagation(); - handleDelete(file.name); - }} - okText="确认" - cancelText="取消" - > - - , - ]} + className={` + relative group cursor-pointer transition-colors duration-200 + hover:bg-gray-50 + ${selectedFile?.name === file.name ? "bg-blue-50" : ""} + `} + onClick={() => file.metadata?.isFolder ? handleFolderClick(file.name) : previewFile(file)} > + +

+ ) : isImage(file) ? ( {file.name} +
} /> ) : ( - getFileIcon(file) +
+ {getFileIcon(file)} +
) } - title={file.name} + title={ +
+ + {file.name} + + {!file.metadata?.isFolder && ( + { + e?.stopPropagation(); + handleDelete(currentPath ? `${currentPath}/${file.name}` : file.name); + }} + okText="确认" + cancelText="取消" + > + + + )} +
+ } description={ -
- 类型: {file.metadata?.mimetype} - - 大小: {(file.metadata?.size / 1024).toFixed(2)} KB - - - 创建时间: {new Date(file.created_at).toLocaleString()} - +
+ {file.metadata?.isFolder ? '文件夹' : `类型: ${file.metadata?.mimetype}`} + {!file.metadata?.isFolder && ( + <> + 大小: {(file.metadata?.size / 1024).toFixed(2)} KB + 创建时间: {new Date(file.created_at).toLocaleString()} + + )}
} /> @@ -519,51 +600,68 @@ const StorageManager = () => { return (
{/* 左侧文件列表 */} -
-
+
+ {/* 上传区域 */} +
-

点击或者拖拽文件到此区域上传

-

- 支持单个或批量上传,文件大小不超过50MB -

+
+
+ +
+
+

+ 点击或拖拽文件上传 +

+

+ 支持单个或批量上传,文件大小不超过50MB +

+
+
{/* 搜索和筛选区域 */} -
+
{ - setSearchText(e.target.value); - }} + onChange={(e) => setSearchText(e.target.value)} className="w-full" size="large" /> - {renderTypeTags()} +
+ {renderTypeTags()} +
{/* 文件列表 */} - {renderFileList()} + {loading && displayFiles.length === 0 ? ( +
+ +
+ ) : ( + renderFileList() + )}
{/* 右侧预览区域 */} -
+
{selectedFile ? ( <> -
+
- + {selectedFile.name} )} @@ -588,15 +690,15 @@ const StorageManager = () => { {selectedFile.name} null, // 隐藏底部工具栏 + toolbarRender: () => null, maskClassName: "backdrop-blur-sm", }} placeholder={ -
-
+
+
} /> @@ -605,7 +707,7 @@ const StorageManager = () => { isPreview ? (