training_data/apps/web/src/components/common/uploader/MultiImageUploader.tsx

174 lines
5.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 React, { useState } from 'react';
import { Upload, Modal, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { useTusUpload } from "@web/src/hooks/useTusUpload";
import toast from "react-hot-toast";
export interface MultiImageUploadProps {
value?: string;
placeholder?: string;
className?: string;
onChange?: (value: string) => void;
compressed?: boolean;
style?: React.CSSProperties; // 添加style属性
}
interface UploadFile {
name: string;
progress: number;
status: "uploading" | "done" | "error";
url?: string;
fileKey?: string;
uid: string;
}
const MultiImageUpload: React.FC<MultiImageUploadProps> = ({
value,
onChange,
compressed = false,
className,
placeholder = "点击上传",
style, // 解构style属性
}) => {
const [fileList, setFileList] = useState<UploadFile[] | null>(null); // 存储已上传的文件列表
const [previewVisible, setPreviewVisible] = useState(false); // 控制预览模态框的显示
const [previewImage, setPreviewImage] = useState(''); // 当前预览的图片URL
const { handleFileUpload, uploadProgress } = useTusUpload();
const [compressedUrl, setCompressedUrl] = useState<string>(value || "");
const [url, setUrl] = useState<string>(value || "");
const [uploading, setUploading] = useState(false);
// 处理文件上传前的校验
const beforeUpload = (file) => {
const isImage = file.type.startsWith('image/');
if (!isImage) {
message.error('只能上传图片文件!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('图片大小不能超过10MB');
}
return isImage && isLt10M;
};
// 处理文件列表变化
const handleChange = async ({ fileList }) => {
//setFileList(fileList);
console.log(fileList);
const imageUrls = fileList.map(file => {
return URL.createObjectURL(file.originFileObj)
});
console.log("imageUrls", imageUrls);
const newFileList = fileList.map(file => {
return {
name: file.name,
progress: 0,
status: "uploading",
//uid: file.uid,
fileKey: `${file.name}-${Date.now()}`,
//url: file.url,
}
});
console.log("newFileList", newFileList);
setUploading(true);
try {
const resFileList = newFileList.map(async (file, index) => {
const uploadedUrl = await new Promise<string>((resolve, reject) => {
handleFileUpload(
fileList[index].originFileObj,
(result) => {
() => {
return {
...newFileList[index],
progress: 100,
status: "done",
fileId: result.fileId,
url: result.url,
compressedUrl: result.compressedUrl,
}
}
// setFile((prev) => ({
// ...prev!,
// progress: 100,
// status: "done",
// fileId: result.fileId,
// url: result.url,
// compressedUrl: result.compressedUrl,
// }));
setUrl(result.url);
setCompressedUrl(result.compressedUrl);
// 直接使用 result 中的最新值
resolve(compressed ? result.compressedUrl : result.url);
},
(error) => {
reject(error);
},
newFileList[index]?.fileKey
);
});
})
// await new Promise((resolve) => setTimeout(resolve,4999)); // 方法1使用 await 暂停执行
// 使用 resolved 的最新值调用 onChange
// 强制刷新 Avatar 组件
// setAvatarKey((prev) => prev + 1); // 修改 key 强制重新挂载
onChange?.(resFileList);
console.log(resFileList);
toast.success("图片上传成功");
} catch (error) {
console.error("上传错误:", error);
toast.error("图片上传失败");
// setFile((prev) => ({ ...prev!, status: "error" }));
} finally {
setUploading(false);
}
};uploadProgress
// 处理预览
const handlePreview = async (file) => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setPreviewImage(file.url || file.preview);
setPreviewVisible(true);
};
// 关闭预览模态框
const handleCancel = () => setPreviewVisible(false);
// 将文件转换为Base64格式用于预览
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
return (
<div>
<Upload.Dragger
listType="picture-card" // 卡片样式
fileList={fileList} // 已上传的文件列表
beforeUpload={beforeUpload} // 上传前的校验
onChange={handleChange} // 文件列表变化时的回调
onPreview={handlePreview} // 预览回调
multiple // 支持多选
// style={{ width: '200px' }}
>
<p className="ant-upload-drag-icon">
</p>
<p className="ant-upload-text"></p>
<p className="ant-upload-hint"></p>
</Upload.Dragger>
<Modal visible={previewVisible} footer={null} onCancel={handleCancel}>
<img alt="预览" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
};
export default MultiImageUpload;