Browse Source

✨ feat(files): 重构文件上传功能,优化用户体验

- 移除MinioUploader组件,实现直接上传功能
- 替换"添加文件"按钮为"上传文件"按钮,点击后直接打开文件选择对话框
- 优化文件编辑模态框,仅保留可编辑字段
- 添加文件上传loading状态提示
- 简化表单字段,隐藏路径、类型和大小等系统自动生成的字段

♻️ refactor(files): 清理冗余代码,优化组件结构

- 移除createFile相关代码和功能
- 删除未使用的CreateFileRequest类型定义
- 简化showModal函数,仅处理编辑功能
- 优化handleSubmit函数,只保留更新文件信息功能
- 调整表单验证逻辑,移除不必要的日期处理

🐛 fix(files): 修复文件编辑功能问题

- 确保编辑时正确填充文件信息到表单
- 修复更新文件信息时的参数传递问题
- 统一错误处理机制,提供更友好的错误提示
yourname 8 months ago
parent
commit
dec3e397b3
1 changed files with 57 additions and 98 deletions
  1. 57 98
      src/client/admin/pages/Files.tsx

+ 57 - 98
src/client/admin/pages/Files.tsx

@@ -1,18 +1,17 @@
 import React, { useState, useEffect } from 'react';
-import { Table, Button, Space, Input, Modal, Form, Select, DatePicker } from 'antd';
-import MinioUploader from '@/client/admin/components/MinioUploader';
+import { Table, Button, Space, Input, Modal, Form, Select, DatePicker, Upload } from 'antd';
 import { App } from 'antd';
 import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, UploadOutlined } from '@ant-design/icons';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import { fileClient, clientClient } from '@/client/api';
 import type { InferResponseType, InferRequestType } from 'hono/client';
 import dayjs from 'dayjs';
+import { uploadMinIOWithPolicy } from '@/client/utils/minio';
 
 // 定义类型
 type FileItem = InferResponseType<typeof fileClient.$get, 200>['data'][0];
 type FileListResponse = InferResponseType<typeof fileClient.$get, 200>;
 type ClientItem = InferResponseType<typeof clientClient.$get, 200>['data'][0];
-type CreateFileRequest = InferRequestType<typeof fileClient.$post>['json'];
 type UpdateFileRequest = InferRequestType<typeof fileClient[':id']['$put']>['json'];
 
 const Files: React.FC = () => {
@@ -60,47 +59,24 @@ const Files: React.FC = () => {
     setPagination(newPagination);
   };
   
-  // 显示添加/编辑弹窗
-  const showModal = (record?: FileItem) => {
+  // 显示编辑弹窗
+  const showModal = (record: FileItem) => {
     setModalVisible(true);
-    if (record) {
-      setEditingKey(record.id);
-      form.setFieldsValue({
-        id: record.id,
-        name: record.name,
-        type: record.type,
-        size: record.size,
-        path: record.path,
-        description: record.description,
-        uploadUserId: record.uploadUserId,
-        uploadTime: record.uploadTime ? dayjs(record.uploadTime) : null,
-        lastUpdated: record.lastUpdated ? dayjs(record.lastUpdated) : null,
-      });
-    } else {
-      setEditingKey(null);
-      form.resetFields();
-    }
+    setEditingKey(record.id);
+    form.setFieldsValue({
+      name: record.name,
+      description: record.description,
+      type: record.type,
+      size: record.size,
+    });
   };
-  
+
   // 关闭弹窗
   const handleCancel = () => {
     setModalVisible(false);
     form.resetFields();
   };
   
-  // 创建文件记录
-  const createFile = useMutation({
-    mutationFn: (data: CreateFileRequest) => fileClient.$post({ json: data }),
-    onSuccess: () => {
-      message.success('文件记录创建成功');
-      queryClient.invalidateQueries({ queryKey: ['files'] });
-      setModalVisible(false);
-    },
-    onError: (error: Error) => {
-      message.error(`操作失败: ${error.message}`);
-    }
-  });
-  
   // 更新文件记录
   const updateFile = useMutation({
     mutationFn: ({ id, data }: { id: number; data: UpdateFileRequest }) =>
@@ -127,25 +103,40 @@ const Files: React.FC = () => {
     }
   });
   
-  // 提交表单
+  // 直接上传文件
+  const handleDirectUpload = async () => {
+    const input = document.createElement('input');
+    input.type = 'file';
+    input.multiple = false;
+    
+    input.onchange = async (e) => {
+      const file = (e.target as HTMLInputElement).files?.[0];
+      if (!file) return;
+      
+      try {
+        message.loading('正在上传文件...');
+        await uploadMinIOWithPolicy('/files', file, file.name);
+        message.success('文件上传成功');
+        queryClient.invalidateQueries({ queryKey: ['files'] });
+      } catch (error) {
+        message.error(`上传失败: ${error instanceof Error ? error.message : '未知错误'}`);
+      }
+    };
+    
+    input.click();
+  };
+  
+  // 提交表单(仅用于编辑已上传文件)
   const handleSubmit = async () => {
     try {
       const values = await form.validateFields();
       
-      // 处理日期字段
       const payload = {
-        ...values,
-        uploadTime: values.uploadTime?.format('YYYY-MM-DD HH:mm:ss'),
-        lastUpdated: values.lastUpdated?.format('YYYY-MM-DD HH:mm:ss'),
+        name: values.name,
+        description: values.description,
       };
       
-      if (editingKey) {
-        // 更新操作
-        await updateFile.mutateAsync({ id: editingKey, data: payload });
-      } else {
-        // 创建操作
-        await createFile.mutateAsync(payload);
-      }
+      await updateFile.mutateAsync({ id: editingKey, data: payload });
     } catch (error) {
       message.error('表单验证失败,请检查输入');
     }
@@ -214,12 +205,12 @@ const Files: React.FC = () => {
     <div className="p-4">
       <div className="flex justify-between items-center mb-4">
         <h2 className="text-xl font-bold">文件管理</h2>
-        <Button 
-          type="primary" 
-          icon={<PlusOutlined />} 
-          onClick={() => showModal()}
+        <Button
+          type="primary"
+          icon={<UploadOutlined />}
+          onClick={handleDirectUpload}
         >
-          添加文件
+          上传文件
         </Button>
       </div>
       
@@ -248,18 +239,18 @@ const Files: React.FC = () => {
       />
       
       <Modal
-        title={editingKey ? "编辑文件记录" : "添加文件记录"}
+        title="编辑文件信息"
         open={modalVisible}
         onCancel={handleCancel}
         footer={[
           <Button key="cancel" onClick={handleCancel}>
             取消
           </Button>,
-          <Button 
-            key="submit" 
-            type="primary" 
+          <Button
+            key="submit"
+            type="primary"
             onClick={handleSubmit}
-            loading={createFile.isPending || updateFile.isPending}
+            loading={updateFile.isPending}
           >
             确定
           </Button>,
@@ -267,52 +258,20 @@ const Files: React.FC = () => {
         width={600}
       >
         <Form form={form} layout="vertical">
-          
-          
-          
-          <Form.Item
-            name="path"
-            label="文件路径"
-            rules={[{ required: true, message: '请上传文件获取路径' }]}
-            hidden
-          >
-            <Input placeholder="文件路径将在上传后自动填充" />
+          <Form.Item name="name" label="文件名称">
+            <Input />
           </Form.Item>
           
-          <Form.Item
-            label="文件上传"
-            extra="支持单文件上传,单个文件大小不超过500MB"
-          >
-            <MinioUploader
-              uploadPath="/files"
-              onUploadSuccess={(fileKey, fileUrl, file) => {
-                // 从文件对象中提取信息并自动填充表单
-                form.setFieldsValue({
-                  path: fileUrl,
-                  name: file.name,
-                  type: file.type || file.name.split('.').pop() || '',
-                  size: file.size
-                });
-              }}
-              onUploadError={(error) => {
-                message.error(`上传失败: ${error.message}`);
-              }}
-            />
+          <Form.Item name="description" label="文件描述">
+            <Input.TextArea rows={4} placeholder="请输入文件描述" />
           </Form.Item>
           
-          <Form.Item
-            name="uploadTime"
-            label="上传时间"
-            initialValue={dayjs()}
-          >
-            <DatePicker showTime format="YYYY-MM-DD HH:mm:ss" />
+          <Form.Item name="type" label="文件类型" hidden>
+            <Input />
           </Form.Item>
           
-          <Form.Item
-            name="description"
-            label="文件描述"
-          >
-            <Input.TextArea rows={4} placeholder="请输入文件描述" />
+          <Form.Item name="size" label="文件大小" hidden>
+            <Input />
           </Form.Item>
         </Form>
       </Modal>