|
|
@@ -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>
|