useFileManagement.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { useState, useCallback } from 'react';
  2. import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
  3. import { toast } from 'sonner';
  4. import { fileClient } from '../api/fileClient';
  5. import type { FileType } from '../types/file';
  6. // 文件管理钩子
  7. export interface UseFileManagementOptions {
  8. defaultPageSize?: number;
  9. }
  10. export const useFileManagement = (options: UseFileManagementOptions = {}) => {
  11. const { defaultPageSize = 10 } = options;
  12. const [pagination, setPagination] = useState({
  13. current: 1,
  14. pageSize: defaultPageSize,
  15. total: 0,
  16. });
  17. const [searchText, setSearchText] = useState('');
  18. const queryClient = useQueryClient();
  19. // 获取文件列表
  20. const {
  21. data: filesData,
  22. isLoading,
  23. error,
  24. refetch
  25. } = useQuery({
  26. queryKey: ['files', pagination.current, pagination.pageSize, searchText],
  27. queryFn: async () => {
  28. const response = await fileClient.$get({
  29. query: {
  30. page: pagination.current,
  31. pageSize: pagination.pageSize,
  32. keyword: searchText || undefined,
  33. },
  34. });
  35. if (!response.ok) {
  36. throw new Error('获取文件列表失败');
  37. }
  38. return await response.json();
  39. },
  40. });
  41. // 更新文件信息
  42. const updateFileMutation = useMutation({
  43. mutationFn: async ({ id, data }: { id: number; data: { name: string; description?: string } }) => {
  44. const response = await fileClient[':id'].$put({
  45. param: { id: Number(id) },
  46. json: data,
  47. });
  48. if (!response.ok) {
  49. throw new Error('更新文件失败');
  50. }
  51. return await response.json();
  52. },
  53. onSuccess: () => {
  54. toast.success('文件信息更新成功');
  55. queryClient.invalidateQueries({ queryKey: ['files'] });
  56. },
  57. onError: (error: Error) => {
  58. toast.error(`更新失败: ${error.message}`);
  59. },
  60. });
  61. // 删除文件
  62. const deleteFileMutation = useMutation({
  63. mutationFn: async (id: number) => {
  64. const response = await fileClient[':id'].$delete({
  65. param: { id: Number(id) },
  66. });
  67. if (!response.ok) {
  68. throw new Error('删除文件失败');
  69. }
  70. },
  71. onSuccess: () => {
  72. toast.success('文件删除成功');
  73. queryClient.invalidateQueries({ queryKey: ['files'] });
  74. },
  75. onError: (error: Error) => {
  76. toast.error(`删除失败: ${error.message}`);
  77. },
  78. });
  79. // 搜索文件
  80. const handleSearch = useCallback((text: string) => {
  81. setSearchText(text);
  82. setPagination(prev => ({ ...prev, current: 1 }));
  83. }, []);
  84. // 分页处理
  85. const handlePageChange = useCallback((page: number, pageSize: number) => {
  86. setPagination(prev => ({ ...prev, current: page, pageSize }));
  87. }, []);
  88. // 文件预览
  89. const handlePreview = useCallback((file: FileType) => {
  90. if (isPreviewable(file.type)) {
  91. window.open(file.fullUrl, '_blank');
  92. } else {
  93. toast.warning('该文件类型不支持预览');
  94. }
  95. }, []);
  96. // 文件下载
  97. const handleDownload = useCallback((file: FileType) => {
  98. const a = document.createElement('a');
  99. a.href = file.fullUrl;
  100. a.download = file.name;
  101. document.body.appendChild(a);
  102. a.click();
  103. document.body.removeChild(a);
  104. }, []);
  105. // 检查是否为可预览的文件类型
  106. const isPreviewable = (fileType: string | null) => {
  107. if (!fileType) return false;
  108. return fileType.startsWith('image/') || fileType.startsWith('video/');
  109. };
  110. return {
  111. // 数据
  112. files: filesData?.data || [],
  113. pagination: filesData?.pagination || pagination,
  114. // 状态
  115. isLoading,
  116. error,
  117. searchText,
  118. // 操作
  119. handleSearch,
  120. handlePageChange,
  121. handlePreview,
  122. handleDownload,
  123. updateFile: updateFileMutation.mutateAsync,
  124. deleteFile: deleteFileMutation.mutateAsync,
  125. refetch,
  126. // 工具函数
  127. isPreviewable,
  128. };
  129. };