168 lines
4.5 KiB
TypeScript
168 lines
4.5 KiB
TypeScript
import {
|
||
DeleteOutlined,
|
||
FileTextOutlined,
|
||
PlayCircleOutlined,
|
||
} from '@ant-design/icons';
|
||
import { message, Upload } from 'antd';
|
||
import type { RcFile, UploadFile, UploadProps } from 'antd/lib/upload';
|
||
import React, { useState } from 'react';
|
||
import { createSample } from '@/services/ai/sample';
|
||
import styles from './index.module.less';
|
||
|
||
const { Dragger } = Upload;
|
||
interface AudioUploaderProps {
|
||
value?: UploadFile[] | UploadFile | null;
|
||
onChange?: (file: UploadFile[] | UploadFile | null) => void;
|
||
maxSize?: number;
|
||
accept?: string;
|
||
disabled?: boolean;
|
||
maxCount?: number;
|
||
placeholder?: string;
|
||
}
|
||
|
||
const AudioUploader: React.FC<AudioUploaderProps> = ({
|
||
value,
|
||
onChange,
|
||
maxSize = 10,
|
||
accept = '.mp3,.wav,.flac,.aac,.ogg',
|
||
disabled = false,
|
||
maxCount = 10,
|
||
placeholder = '点击或拖拽音频文件到此区域上传',
|
||
}) => {
|
||
const [fileList, setFileList] = useState<UploadFile[]>(() => {
|
||
if (Array.isArray(value)) {
|
||
return value;
|
||
} else if (value) {
|
||
return [value];
|
||
}
|
||
return [];
|
||
});
|
||
const [uploading, setUploading] = useState<boolean>(false);
|
||
|
||
const beforeUpload = (file: RcFile): boolean => {
|
||
const isAudio = file.type.startsWith('audio/');
|
||
if (!isAudio) {
|
||
message.error('只能上传音频文件!');
|
||
return false;
|
||
}
|
||
|
||
// 检查文件大小
|
||
const isLtMaxSize = file.size / 1024 / 1024 < maxSize;
|
||
if (!isLtMaxSize) {
|
||
message.error(`音频文件大小不能超过 ${maxSize}MB!`);
|
||
return false;
|
||
}
|
||
|
||
// 检查文件数量限制
|
||
if (fileList.length >= maxCount) {
|
||
message.error(`最多只能上传 ${maxCount} 个文件!`);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
// 实际的后端接口上传
|
||
const uploadToServer = async (file: File) => {
|
||
const formData = new FormData();
|
||
formData.append('files', file);
|
||
|
||
const response = await createSample(formData);
|
||
message.success('上传成功!');
|
||
return response;
|
||
};
|
||
|
||
const customRequest: UploadProps['customRequest'] = async (options) => {
|
||
const { file, onSuccess, onError, onProgress } = options;
|
||
|
||
try {
|
||
setUploading(true);
|
||
// 模拟进度更新
|
||
onProgress?.({ percent: 10 });
|
||
// 调用后端接口
|
||
const result = await uploadToServer(file as File);
|
||
console.log(result, 'res');
|
||
onProgress?.({ percent: 100 });
|
||
if (result) {
|
||
// 构造返回数据
|
||
const responseData = {
|
||
url: result[0].fileUrl,
|
||
name: result[0].fileName || (file as File).name,
|
||
uid: (file as any).uid,
|
||
status: 'done' as const,
|
||
response: result,
|
||
};
|
||
onSuccess?.(responseData);
|
||
} else {
|
||
throw new Error(result.message || '上传失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('Upload error:', error);
|
||
onError?.(error as Error);
|
||
message.error(`上传失败: ${(error as Error).message}`);
|
||
} finally {
|
||
setUploading(false);
|
||
}
|
||
};
|
||
|
||
// const handleChange: UploadProps["onChange"] = ({
|
||
// fileList: newFileList,
|
||
// file,
|
||
// }) => {
|
||
// setFileList(newFileList);
|
||
// onChange?.(newFileList);
|
||
// };
|
||
|
||
const handleRemove = (file: UploadFile): boolean => {
|
||
const newFileList = fileList.filter((item) => item.uid !== file.uid);
|
||
setFileList(newFileList);
|
||
onChange?.(newFileList);
|
||
return true;
|
||
};
|
||
|
||
const handlePreview = (file: UploadFile): void => {
|
||
const audioUrl = file.url || (file.response as any)?.url;
|
||
if (audioUrl) {
|
||
const audio = new Audio(audioUrl);
|
||
audio.play().catch(() => {
|
||
message.error('音频播放失败');
|
||
});
|
||
}
|
||
};
|
||
|
||
const uploadProps: UploadProps = {
|
||
name: 'file',
|
||
multiple: true,
|
||
fileList,
|
||
accept,
|
||
disabled: disabled || uploading,
|
||
beforeUpload,
|
||
customRequest,
|
||
// onChange: handleChange,
|
||
onRemove: handleRemove,
|
||
onPreview: handlePreview,
|
||
showUploadList: {
|
||
showPreviewIcon: true,
|
||
showRemoveIcon: true,
|
||
previewIcon: <PlayCircleOutlined />,
|
||
removeIcon: <DeleteOutlined />,
|
||
},
|
||
};
|
||
|
||
return (
|
||
<div className={styles['uploader-card']}>
|
||
<Dragger {...uploadProps}>
|
||
<p className="">
|
||
<FileTextOutlined style={{ fontSize: 36 }} />
|
||
</p>
|
||
<p className="ant-upload-text">{placeholder}</p>
|
||
<p className="ant-upload-hint">
|
||
支持格式:{accept},单次最多同时上传 {maxCount} 个文件
|
||
</p>
|
||
</Dragger>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default AudioUploader;
|