备份:修复前完整项目快照 2026-04-19

This commit is contained in:
root
2026-04-19 19:15:01 +08:00
parent 5266d7732b
commit d00f41a120
449 changed files with 89577 additions and 23251 deletions
@@ -0,0 +1,129 @@
import React, { useState } from 'react';
import { Upload, Button, message, Image, Spin } from 'antd';
import { UploadOutlined, FileOutlined, DeleteOutlined } from '@ant-design/icons';
import type { UploadFile } from 'antd/es/upload/interface';
interface FileUploadProps {
value?: string;
onChange?: (url: string) => void;
accept?: string;
maxSize?: number; // MB
disabled?: boolean;
}
const FileUpload: React.FC<FileUploadProps> = ({
value,
onChange,
accept = '.pdf,.doc,.docx,.jpg,.jpeg,.png,.xlsx,.xls',
maxSize = 10,
disabled = false,
}) => {
const [loading, setLoading] = useState(false);
const [fileList, setFileList] = useState<UploadFile[]>([]);
const beforeUpload = (file: File) => {
const isLt = file.size / 1024 / 1024 < maxSize;
if (!isLt) {
message.error(`文件大小不能超过 ${maxSize}MB`);
return false;
}
return true;
};
const handleUpload = async (options: any) => {
const { file, onSuccess, onError } = options;
setLoading(true);
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const result = await response.json();
if (result.success) {
message.success('上传成功');
onChange?.(result.data.url);
onSuccess(result.data, file);
} else {
message.error(result.error || '上传失败');
onError?.(new Error(result.error));
}
} catch (error: any) {
message.error('上传失败');
onError?.(error);
} finally {
setLoading(false);
}
};
const handleRemove = () => {
onChange?.('');
setFileList([]);
};
// 判断文件类型
const getFileType = (url: string) => {
const ext = url.split('.').pop()?.toLowerCase();
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext || '')) {
return 'image';
}
return 'file';
};
return (
<div>
{value ? (
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{getFileType(value) === 'image' ? (
<Image src={value} width={100} height={100} style={{ objectFit: 'cover' }} />
) : (
<div
style={{
width: 100,
height: 100,
border: '1px solid #d9d9d9',
borderRadius: 4,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#fafafa',
}}
>
<FileOutlined style={{ fontSize: 32, color: '#1890ff' }} />
</div>
)}
<div style={{ flex: 1 }}>
<a href={value} target="_blank" rel="noopener noreferrer">
</a>
</div>
{!disabled && (
<Button danger icon={<DeleteOutlined />} onClick={handleRemove}>
</Button>
)}
</div>
) : (
<Upload
accept={accept}
beforeUpload={beforeUpload}
customRequest={handleUpload}
fileList={fileList}
showUploadList={false}
disabled={disabled || loading}
>
<Button icon={<UploadOutlined />} disabled={disabled}>
{loading ? <Spin size="small" /> : '选择文件'}
</Button>
</Upload>
)}
</div>
);
};
export default FileUpload;