Files
fe_supplier_frontend/src/components/FileUpload/FileUpload.tsx
2025-08-05 15:45:52 +08:00

232 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import { Upload, Button, message, Tooltip } from 'antd';
import { UploadOutlined, InboxOutlined } from '@ant-design/icons';
import type { UploadFile, UploadProps } from 'antd/es/upload/interface';
import { validateFileSize } from '@/utils/utils';
import { useIntl } from 'umi';
import './FileUpload.less';
export interface FileUploadProps {
value?: UploadFile[];
onChange?: (fileList: UploadFile[]) => void;
maxCount?: number;
maxSize?: number;
allowedTypes?: string[];
listType?: 'text' | 'picture' | 'picture-card';
buttonText?: string;
disabled?: boolean;
accept?: string;
showUploadList?:
| boolean
| { showPreviewIcon?: boolean; showRemoveIcon?: boolean; showDownloadIcon?: boolean };
isDragger?: boolean;
tip?: string;
action?: string;
}
// 上传接口返回数据格式
interface UploadResponseData {
fileName: string;
fileSize: string;
filePath: string;
fileType: string;
url: string;
}
// API响应格式
interface ApiResponse<T> {
code: number;
success: boolean;
message: string;
data: T;
}
const FileUpload: React.FC<FileUploadProps> = ({
value,
onChange,
maxCount = 1,
maxSize = 5,
allowedTypes = ['*'],
listType = 'text',
buttonText,
disabled = false,
accept = allowedTypes.map((type) => `.${type}`).join(','),
showUploadList = true,
isDragger = false,
tip,
action,
}) => {
const actionUrl = action ? `${UPLOAD_URL}${action}` : `${UPLOAD_URL}/fileConfig/files/upload`;
const intl = useIntl();
const [fileList, setFileList] = useState<UploadFile[]>([]);
// 监听value变化
useEffect(() => {
// 处理字符串URL值这是关键修复
if (typeof value === 'string' && value) {
const file: Partial<UploadFile> = {
uid: '-1',
name: 'image.jpg',
status: 'done',
url: value,
size: 0,
type: 'image/jpeg',
};
setFileList([file as UploadFile]);
return;
}
// 处理空值
if (!value || (Array.isArray(value) && value.length === 0)) {
setFileList([]);
return;
}
// 处理正常的文件数组
if (Array.isArray(value)) {
setFileList(value);
return;
}
// 处理对象格式
if (typeof value === 'object') {
// 处理fileList属性
if ('fileList' in value && Array.isArray((value as any).fileList)) {
const files = (value as { fileList: UploadFile[] }).fileList;
setFileList(files);
return;
}
// 尝试作为单个文件对象处理
if ('uid' in value && 'status' in value) {
setFileList([value as unknown as UploadFile]);
return;
}
console.warn('FileUpload: Unrecognized object format', value);
setFileList([]);
return;
}
// 无法识别的格式
console.warn('FileUpload component: unrecognized value format:', value);
setFileList([]);
}, [value]);
const defaultButtonText = intl.formatMessage({
id: 'component.fileUpload.buttonText',
defaultMessage: '上传文件',
});
// 处理上传接口返回的数据格式
const getFileUrl = (file: UploadFile) => {
// 如果response是API的标准响应格式
if (file.response && typeof file.response === 'object') {
// 首先检查是否是标准API响应格式
if ('success' in file.response && 'data' in file.response) {
const response = file.response as ApiResponse<UploadResponseData>;
if (response.success && response.data) {
return response.data.url;
}
}
// 如果response直接包含url属性
else if ('url' in file.response) {
return (file.response as UploadResponseData).url;
}
}
// 回退到文件的url或thumbUrl
return file.url || file.thumbUrl;
};
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
const filteredList = newFileList.filter((file) => file.status !== 'error');
// 为每个文件添加正确的url
const processedList = filteredList.map((file) => {
if (file.status === 'done' && file.response && !file.url) {
return {
...file,
url: getFileUrl(file),
filePath: file?.response?.filePath || 'filePath not found',
};
}
return file;
});
setFileList(processedList);
if (onChange) {
onChange(processedList);
}
};
const beforeUpload = (file: File) => {
return validateFileSize(file, maxSize, allowedTypes);
};
const UploadComponent = isDragger ? Upload.Dragger : Upload;
const uploadProps = {
fileList,
onChange: handleChange,
beforeUpload,
listType,
maxCount,
disabled,
accept,
showUploadList,
action: actionUrl,
};
const renderUploadButton = () => {
if (fileList.length >= maxCount) {
return null;
}
if (isDragger) {
return (
<>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">
{buttonText ||
intl.formatMessage({
id: 'component.fileUpload.dragText',
defaultMessage: '点击或拖拽文件到此区域上传',
})}
</p>
{tip && <p className="ant-upload-hint">{tip}</p>}
</>
);
}
return (
<Button
type="link"
style={{ padding: 0, height: 'auto', marginTop: 6 }}
icon={<UploadOutlined />}
disabled={disabled}
>
{buttonText || defaultButtonText}
</Button>
);
};
return (
<div className="file-upload-wrapper">
<UploadComponent {...uploadProps}>{renderUploadButton()}</UploadComponent>
{!isDragger && tip && (
<div className="file-upload-tip">
<Tooltip title={tip}>
<span>{tip}</span>
</Tooltip>
</div>
)}
</div>
);
};
export default FileUpload;