useFileSelector.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { useState, useCallback } from 'react';
  2. import { useQuery } from '@tanstack/react-query';
  3. import { fileClient } from '../api/fileClient';
  4. import type { FileType } from '../types/file';
  5. // 文件选择器钩子
  6. export interface UseFileSelectorOptions {
  7. filterType?: 'image' | 'all' | string;
  8. pageSize?: number;
  9. }
  10. export const useFileSelector = (options: UseFileSelectorOptions = {}) => {
  11. const { filterType = 'all', pageSize = 50 } = options;
  12. const [isOpen, setIsOpen] = useState(false);
  13. const [selectedFiles, setSelectedFiles] = useState<FileType[]>([]);
  14. const [searchText, setSearchText] = useState('');
  15. // 获取文件列表
  16. const {
  17. data: filesData,
  18. isLoading,
  19. error,
  20. refetch
  21. } = useQuery({
  22. queryKey: ['files-for-selection', filterType, searchText],
  23. queryFn: async () => {
  24. const response = await fileClient.$get({
  25. query: {
  26. page: 1,
  27. pageSize,
  28. keyword: searchText || undefined,
  29. },
  30. });
  31. if (!response.ok) {
  32. throw new Error('获取文件列表失败');
  33. }
  34. return await response.json();
  35. },
  36. enabled: isOpen,
  37. });
  38. // 过滤文件
  39. const files = filesData?.data?.filter((file) => {
  40. if (filterType === 'all') return true;
  41. if (filterType === 'image') return file?.type?.startsWith('image/');
  42. return file?.type?.includes(filterType);
  43. }) || [];
  44. // 选择文件
  45. const handleSelectFile = useCallback((file: FileType) => {
  46. setSelectedFiles(prev => {
  47. const isSelected = prev.some(f => f.id === file.id);
  48. if (isSelected) {
  49. return prev.filter(f => f.id !== file.id);
  50. } else {
  51. return [...prev, file];
  52. }
  53. });
  54. }, []);
  55. // 批量选择文件
  56. const handleSelectFiles = useCallback((files: FileType[]) => {
  57. setSelectedFiles(files);
  58. }, []);
  59. // 清除选择
  60. const handleClearSelection = useCallback(() => {
  61. setSelectedFiles([]);
  62. }, []);
  63. // 打开选择器
  64. const handleOpen = useCallback(() => {
  65. setIsOpen(true);
  66. }, []);
  67. // 关闭选择器
  68. const handleClose = useCallback(() => {
  69. setIsOpen(false);
  70. setSearchText('');
  71. }, []);
  72. // 确认选择
  73. const handleConfirm = useCallback(() => {
  74. setIsOpen(false);
  75. setSearchText('');
  76. return selectedFiles;
  77. }, [selectedFiles]);
  78. // 搜索文件
  79. const handleSearch = useCallback((text: string) => {
  80. setSearchText(text);
  81. }, []);
  82. // 检查文件是否被选中
  83. const isFileSelected = useCallback((fileId: number) => {
  84. return selectedFiles.some(file => file.id === fileId);
  85. }, [selectedFiles]);
  86. return {
  87. // 数据
  88. files,
  89. selectedFiles,
  90. // 状态
  91. isOpen,
  92. isLoading,
  93. error,
  94. searchText,
  95. // 操作
  96. handleOpen,
  97. handleClose,
  98. handleConfirm,
  99. handleSelectFile,
  100. handleSelectFiles,
  101. handleClearSelection,
  102. handleSearch,
  103. refetch,
  104. // 工具函数
  105. isFileSelected,
  106. };
  107. };