Files
tashow-manager/src/components/Upload/UploadImages/index.tsx
2025-10-29 17:10:53 +08:00

148 lines
4.5 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 { PlusOutlined } from '@ant-design/icons';
import type { GetProp, UploadFile, UploadProps } from 'antd';
import { Image, message, Spin, Upload } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import { uploadImage } from '@/services/infra/media';
type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
const getBase64 = (file: FileType): Promise<string> =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
// accept: .doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document image/*,.pdf
const UploadImages: React.FC<{
value?: string;
onChange?: (value: string | string[]) => void;
multiple?: boolean;
accept?: string;
maxCount?: number;
}> = (props) => {
const {
value,
multiple = false,
maxCount = 1,
accept = 'image/png,image/jpeg',
onChange,
} = props;
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState('');
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [uploading, setUploading] = useState(false);
useEffect(() => {
if (value) {
setFileList([{ uid: '-1', url: value, status: 'done', name: value }]);
} else {
setFileList([]);
}
}, [value]);
const beforeUpload = (file: FileType) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('仅支持.jpg .png 格式!');
}
// const isLt2M = file.size / 1024 / 1024 < 2;
// if (!isLt2M) {
// message.error('Image must smaller than 2MB!');
// }
return isJpgOrPng;
};
const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj as FileType);
}
setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
};
const handleRemove = (file: UploadFile): boolean => {
const newFileList = fileList.filter((item) => item.uid !== file.uid);
const newUrl = newFileList.map((item) => item.url) as string[];
onChange?.(newUrl[0]);
return true;
};
const uploadButton = (
<button style={{ border: 0, background: 'none' }} type="button">
<PlusOutlined />
<div style={{ marginTop: 8 }}>Upload</div>
</button>
);
const uploadFile = useCallback(async (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('file', file);
try {
uploadImage(formData).then((res) => {
if (res) {
resolve(res);
} else {
reject(new Error('上传失败未返回有效的URL'));
}
});
} catch (error) {
reject(error);
}
});
}, []);
const handleLoadImage: UploadProps['customRequest'] = async (option) => {
const { file, onSuccess, onError, onProgress } = option;
try {
setUploading(true);
// 模拟进度更新
onProgress?.({ percent: 10 });
// 调用后端接口
const url = await uploadFile(file as File);
onProgress?.({ percent: 100 });
if (url) {
onChange?.(url);
onSuccess?.({ url });
message.success('上传成功');
} else {
throw new Error('上传失败');
}
} catch (error) {
console.error('Upload error:', error);
onError?.(error as Error);
message.error(`上传失败: ${(error as Error).message}`);
} finally {
setUploading(false);
}
};
return (
<Spin spinning={uploading}>
<Upload
listType="picture-card"
fileList={fileList}
onPreview={handlePreview}
onRemove={handleRemove}
beforeUpload={beforeUpload}
customRequest={handleLoadImage}
multiple={multiple}
accept={accept}
>
{fileList.length >= maxCount ? null : uploadButton}
</Upload>
{previewImage && (
<Image
wrapperStyle={{ display: 'none' }}
preview={{
visible: previewOpen,
onVisibleChange: (visible) => setPreviewOpen(visible),
afterOpenChange: (visible) => !visible && setPreviewImage(''),
}}
src={previewImage}
/>
)}
</Spin>
);
};
export default UploadImages;