feat: multi-module iteration
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { useState } from 'react';
|
||||
import { Button, Image, Input, Space, Upload, message } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import type { UploadProps } from 'antd';
|
||||
import { uploadFileToOss, type OssMediaType, type UploadFileResult } from '../lib/upload';
|
||||
|
||||
type OssUploadProps = {
|
||||
value?: string;
|
||||
onChange?: (url: string) => void;
|
||||
onUploaded?: (result: UploadFileResult) => void;
|
||||
bizType: string;
|
||||
mediaType?: OssMediaType;
|
||||
accept?: string;
|
||||
maxSizeMb?: number;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
const DEFAULT_MAX_MB = 10;
|
||||
|
||||
export default function OssUpload({
|
||||
value,
|
||||
onChange,
|
||||
onUploaded,
|
||||
bizType,
|
||||
mediaType = 'IMAGE',
|
||||
accept,
|
||||
maxSizeMb = DEFAULT_MAX_MB,
|
||||
placeholder = '上传后自动填入,或手动粘贴 URL',
|
||||
}: OssUploadProps) {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const resolvedAccept =
|
||||
accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? undefined : 'image/*');
|
||||
|
||||
const customRequest: UploadProps['customRequest'] = async ({ file, onSuccess, onError }) => {
|
||||
const raw = file as File;
|
||||
if (raw.size > maxSizeMb * 1024 * 1024) {
|
||||
const err = new Error(`文件不能超过 ${maxSizeMb}MB`);
|
||||
message.error(err.message);
|
||||
onError?.(err);
|
||||
return;
|
||||
}
|
||||
setUploading(true);
|
||||
try {
|
||||
const result = await uploadFileToOss(raw, { bizType, mediaType });
|
||||
onChange?.(result.url);
|
||||
onUploaded?.(result);
|
||||
message.success('上传成功');
|
||||
onSuccess?.(result);
|
||||
} catch (e) {
|
||||
const err = e instanceof Error ? e : new Error('上传失败');
|
||||
message.error(err.message);
|
||||
onError?.(err);
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="small">
|
||||
{value && mediaType === 'IMAGE' && (
|
||||
<Image src={value} width={120} height={120} style={{ objectFit: 'cover', borderRadius: 4 }} />
|
||||
)}
|
||||
{value && mediaType === 'VIDEO' && (
|
||||
<video src={value} controls style={{ maxWidth: '100%', maxHeight: 160, borderRadius: 4 }} />
|
||||
)}
|
||||
<Space wrap>
|
||||
<Upload
|
||||
accept={resolvedAccept}
|
||||
showUploadList={false}
|
||||
customRequest={customRequest}
|
||||
disabled={uploading}
|
||||
>
|
||||
<Button icon={<UploadOutlined />} loading={uploading}>
|
||||
{mediaType === 'VIDEO' ? '上传视频' : mediaType === 'FILE' ? '上传文件' : '上传图片'}
|
||||
</Button>
|
||||
</Upload>
|
||||
</Space>
|
||||
<Input
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => onChange?.(e.target.value)}
|
||||
allowClear
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user