This commit is contained in:
8
src/components/Upload/UploadCard/index.module.less
Normal file
8
src/components/Upload/UploadCard/index.module.less
Normal file
@@ -0,0 +1,8 @@
|
||||
.uploader-card {
|
||||
background-color: #fff;
|
||||
:global {
|
||||
.ant-upload-drag {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
src/components/Upload/UploadCard/index.tsx
Normal file
176
src/components/Upload/UploadCard/index.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
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, getSample } 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('file', file);
|
||||
// formData.append("type", "audio"); // 可以添加额外参数
|
||||
|
||||
const response = await createSample(formData);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
onProgress?.({ percent: 100 });
|
||||
|
||||
if (result.success && result.data) {
|
||||
// 构造返回数据
|
||||
const responseData = {
|
||||
url: result.data.url,
|
||||
name: result.data.filename || (file as File).name,
|
||||
uid: (file as any).uid,
|
||||
status: 'done' as const,
|
||||
response: result.data,
|
||||
};
|
||||
|
||||
onSuccess?.(responseData);
|
||||
message.success('音频上传成功!');
|
||||
} 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;
|
||||
Reference in New Issue
Block a user