|
|
|
@ -0,0 +1,212 @@
|
|
|
|
|
/*
|
|
|
|
|
* @Author: donghao donghao@supervision.ltd
|
|
|
|
|
* @Date: 2024-07-16 13:02:35
|
|
|
|
|
* @LastEditors: donghao donghao@supervision.ltd
|
|
|
|
|
* @LastEditTime: 2024-07-16 17:10:10
|
|
|
|
|
* @FilePath: \general-ai-platform-web\src\components\UploadFile\src\draggerUpload.tsx
|
|
|
|
|
* @Description: 拖拽上传
|
|
|
|
|
*
|
|
|
|
|
* @交互说明
|
|
|
|
|
* 1. 拖拽上传
|
|
|
|
|
* 2. 文件格式、大小限制
|
|
|
|
|
* 3. 上传、删除
|
|
|
|
|
* 4. 进度显示、文件展示
|
|
|
|
|
* 5. 多个文件上传(拓展)
|
|
|
|
|
* 待验证
|
|
|
|
|
*/
|
|
|
|
|
import { apiFileDelete, apiFileUpload } from '@/services/business/file';
|
|
|
|
|
import { useIntl } from '@umijs/max';
|
|
|
|
|
|
|
|
|
|
import { isSuccessApi } from '@/utils/forApi';
|
|
|
|
|
import { ProFormItem } from '@ant-design/pro-components';
|
|
|
|
|
import { message, Upload } from 'antd';
|
|
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
import UploadFileForView from './uploadFileForView';
|
|
|
|
|
|
|
|
|
|
// export const fileType = [
|
|
|
|
|
// // 压缩包
|
|
|
|
|
// { type: 'ZIP', value: 'application/zip' },
|
|
|
|
|
// { type: 'GZIP', value: 'application/gzip' },
|
|
|
|
|
// { type: 'TAR', value: 'application/x-tar' },
|
|
|
|
|
// { type: 'BZIP2', value: 'application/x-bzip2' },
|
|
|
|
|
// { type: '7Z', value: 'application/x-7z-compressed' },
|
|
|
|
|
// { type: 'RAR', value: 'application/vnd.rar' },
|
|
|
|
|
// { type: 'XZ', value: 'application/x-xz' },
|
|
|
|
|
// { type: 'LZMA', value: 'application/x-lzma' },
|
|
|
|
|
// { type: 'ISO', value: 'application/x-iso9660-image' },
|
|
|
|
|
// { type: 'CAB', value: 'application/vnd.ms-cab-compressed' },
|
|
|
|
|
// { type: 'Z', value: 'application/x-compress' },
|
|
|
|
|
// // JSON 或者 YAML
|
|
|
|
|
// { type: 'JSON', value: 'application/json' },
|
|
|
|
|
// { type: 'YAML', value: 'application/x-yaml' },
|
|
|
|
|
// { type: 'YML', value: 'application/x-yaml' },
|
|
|
|
|
// ];
|
|
|
|
|
// const { Dragger } = Upload;
|
|
|
|
|
|
|
|
|
|
const DraggerUpload: React.FC = (props) => {
|
|
|
|
|
const intl = useIntl();
|
|
|
|
|
|
|
|
|
|
const [fileList, setFileList] = useState<UploadFile[]>(props?.proFieldProps?.initialValue || []);
|
|
|
|
|
const [dragOver, setDragOver] = useState(false);
|
|
|
|
|
const [previewFile, setPreviewFile] = useState(null);
|
|
|
|
|
|
|
|
|
|
const defualtOptions = {
|
|
|
|
|
accept: props?.proFieldProps?.accept || 'application/json, application/x-yaml', // 默认使用 JSON 或者 yaml 文件类型
|
|
|
|
|
max: props?.proFieldProps?.max || 1,
|
|
|
|
|
size: props?.proFieldProps?.size || 1024 * 1024 * 200,
|
|
|
|
|
};
|
|
|
|
|
// const defalutOptions: UploadProps = {
|
|
|
|
|
// name: 'file',
|
|
|
|
|
// multiple: true,
|
|
|
|
|
// action: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload',
|
|
|
|
|
// onChange(info) {
|
|
|
|
|
// const { status } = info.file;
|
|
|
|
|
// if (status !== 'uploading') {
|
|
|
|
|
// console.log(info.file, info.fileList);
|
|
|
|
|
// }
|
|
|
|
|
// if (status === 'done') {
|
|
|
|
|
// message.success(`${info.file.name} file uploaded successfully.`);
|
|
|
|
|
// } else if (status === 'error') {
|
|
|
|
|
// message.error(`${info.file.name} file upload failed.`);
|
|
|
|
|
// }
|
|
|
|
|
// },
|
|
|
|
|
// onDrop(e) {
|
|
|
|
|
// console.log('Dropped files', e.dataTransfer.files);
|
|
|
|
|
// },
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
// 文件格式和大小校验
|
|
|
|
|
const validateFile = (file: UploadFile) => {
|
|
|
|
|
const isValidType = defualtOptions.accept.split(',').includes(file.type);
|
|
|
|
|
const isLt2M = file.size < defualtOptions.size;
|
|
|
|
|
if (!isValidType) {
|
|
|
|
|
message.error('文件格式不对!');
|
|
|
|
|
}
|
|
|
|
|
if (!isLt2M) {
|
|
|
|
|
// TODO 暂时写死200M
|
|
|
|
|
message.error('文件大小不得超过200M');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return isValidType && isLt2M;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setDragOver(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDragLeave = () => {
|
|
|
|
|
setDragOver(false);
|
|
|
|
|
};
|
|
|
|
|
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
setDragOver(false);
|
|
|
|
|
|
|
|
|
|
const files = Array.from(e.dataTransfer.files);
|
|
|
|
|
files.forEach((file) => {
|
|
|
|
|
const isValid = validateFile(file);
|
|
|
|
|
if (isValid && fileList.length < defualtOptions.max) {
|
|
|
|
|
setFileList([...fileList, file]);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleBeforeUpload = async (file: UploadFile) => {
|
|
|
|
|
const isValid = validateFile(file);
|
|
|
|
|
if (isValid) {
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append('file', file);
|
|
|
|
|
const resp = await apiFileUpload(formData);
|
|
|
|
|
if (isSuccessApi(resp)) {
|
|
|
|
|
setFileList([
|
|
|
|
|
{
|
|
|
|
|
uid: file?.uid,
|
|
|
|
|
name: file?.name,
|
|
|
|
|
status: 'done',
|
|
|
|
|
url: '/file/' + resp?.data?.result,
|
|
|
|
|
fileId: resp?.data?.result,
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
setPreviewFile(file);
|
|
|
|
|
props.afterUploadFile({ resp, file, fileList });
|
|
|
|
|
}
|
|
|
|
|
console.log(resp, 'apiFileUpload_resp');
|
|
|
|
|
}
|
|
|
|
|
return false; // Prevent auto-upload
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRemove = async (file: UploadFile<R<string>>) => {
|
|
|
|
|
console.log(file, 'onRemoveFile_file', fileList);
|
|
|
|
|
const currFile = fileList.find((item) => item.uid === file.uid);
|
|
|
|
|
const resp = await apiFileDelete({ file_md5: currFile?.fileId || file?.uid });
|
|
|
|
|
console.log(resp, 'apiFileDelete_resp');
|
|
|
|
|
props.afterRemoveFile(resp, file);
|
|
|
|
|
setPreviewFile(null);
|
|
|
|
|
if (isSuccessApi(resp)) {
|
|
|
|
|
message.success(`${file.name} 删除成功`);
|
|
|
|
|
setFileList(fileList.filter((item) => item.uid !== file.uid));
|
|
|
|
|
} else {
|
|
|
|
|
message.error(
|
|
|
|
|
resp?.meta?.message ||
|
|
|
|
|
intl.formatMessage({ id: 'common.action.failure', defaultMessage: '$$$' }),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const customRequest = async ({ file, onSuccess, onError }) => {
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append('file', file);
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch('https://your-upload-api-endpoint.com/upload', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: formData,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
onSuccess('ok');
|
|
|
|
|
message.success('File uploaded successfully');
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('Upload failed');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
onError(error);
|
|
|
|
|
message.error('Upload failed');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ProFormItem name="upload" label={props?.proFieldProps?.label} valuePropName="fileList">
|
|
|
|
|
<div
|
|
|
|
|
className={`custom-dragger gn_dragger_formItem ${dragOver ? 'drag-over' : ''}`}
|
|
|
|
|
onDragOver={handleDragOver}
|
|
|
|
|
onDragLeave={handleDragLeave}
|
|
|
|
|
onDrop={handleDrop}
|
|
|
|
|
>
|
|
|
|
|
<Upload
|
|
|
|
|
fileList={fileList}
|
|
|
|
|
beforeUpload={handleBeforeUpload}
|
|
|
|
|
onRemove={handleRemove}
|
|
|
|
|
customRequest={customRequest}
|
|
|
|
|
>
|
|
|
|
|
{fileList.length < defualtOptions.max && (
|
|
|
|
|
<span className="gn_proFormUploadDragger_formItem">
|
|
|
|
|
<div className="ant-upload ant-upload-drag">
|
|
|
|
|
<span className="ant-upload ant-upload-btn">
|
|
|
|
|
<div className="ant-upload-drag-container">
|
|
|
|
|
<div className="ant-upload-drag-icon">{props?.proFieldProps?.icon}</div>
|
|
|
|
|
<p className="ant-upload-text">{props?.proFieldProps?.title}</p>
|
|
|
|
|
<p className="ant-upload-hint">{props?.proFieldProps?.description}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</Upload>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{props?.openPreviewFile && <UploadFileForView file={previewFile} />}
|
|
|
|
|
</ProFormItem>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default DraggerUpload;
|