Files
MemoryBear/web/src/views/KnowledgeBase/components/CreateImageDataset.tsx
yujiangping c00e164567 feat(knowledgeBase): add media dataset support and improve file handling
- Add media dataset translations in English and Chinese locales
- Add "mediaDataSet" and "uploadMedia" i18n keys for UI labels
- Enable media dataset creation option in Private component by uncommenting menu item
- Import and display image icon for media dataset menu option
- Refactor file ID handling in CreateDataset to support both string and array types
- Improve fileIds initialization logic to handle mixed input types
- Update CreateImageDataset component to use file chunking workflow
- Add navigation to parameter settings step after file upload
- Pass file IDs to dataset creation flow for media processing
- Add message API and navigate hook for improved UX feedback
2025-12-25 20:17:58 +08:00

223 lines
7.1 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 { forwardRef, useImperativeHandle, useState, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { Form, Input, message } from 'antd';
import { useTranslation } from 'react-i18next';
import type { UploadFile } from 'antd';
import type { CreateSetModalRef, CreateSetMoealRefProps, UploadFileResponse } from '@/views/KnowledgeBase/types';
import type { UploadRequestOption } from 'rc-upload/lib/interface';
import RbModal from '@/components/RbModal';
import UploadFiles from '@/components/Upload/UploadFiles';
import { uploadFile } from '@/api/knowledgeBase';
interface ImageDatasetFormData {
name: string;
images: UploadFile[];
}
const CreateImageDataset = forwardRef<CreateSetModalRef, CreateSetMoealRefProps>(
({ refreshTable }, ref) => {
const { t } = useTranslation();
const navigate = useNavigate();
const [visible, setVisible] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const [form] = Form.useForm<ImageDatasetFormData>();
const [loading, setLoading] = useState(false);
const [kbId, setKbId] = useState<string>('');
const [parentId, setParentId] = useState<string>('');
const uploadRef = useRef<{ fileList: UploadFile[]; clearFiles: () => void }>(null);
// const fileIds = [];
const handleClose = () => {
form.resetFields();
uploadRef.current?.clearFiles();
setLoading(false);
setVisible(false);
setKbId('');
setParentId('');
};
const handleOpen = (kb_id: string, parent_id: string) => {
setKbId(kb_id);
setParentId(parent_id);
form.resetFields();
uploadRef.current?.clearFiles();
setVisible(true);
};
const handleSave = async () => {
try {
await form.validateFields();
setLoading(true);
const fileList = uploadRef.current?.fileList || [];
if (fileList.length === 0) {
throw new Error(t('knowledgeBase.pleaseUploadImages'));
}
const ids = fileList.map((file) => file.response?.id);
handleChunking(kbId, parentId, ids)
// // 上传所有图片
// const uploadPromises = fileList.map(async (file) => {
// if (file.originFileObj) {
// const formData = new FormData();
// formData.append('file', file.originFileObj);
// return uploadFile(formData, {
// kb_id: kbId,
// parent_id: parentId,
// });
// }
// return null;
// });
// await Promise.all(uploadPromises);
if (refreshTable) {
await refreshTable();
}
handleClose();
} catch (err) {
console.error('创建图片数据集失败:', err);
} finally {
setLoading(false);
}
};
const handleChunking = (kb_id: string, parent_id: string, file_id: Array<string>) => {
if (!kb_id) return;
const targetFileId = file_id
navigate(`/knowledge-base/${kb_id}/create-dataset`, {
state: {
source: 'local',
knowledgeBaseId: kb_id,
parentId: parent_id ?? kb_id,
startStep: 'parameterSettings',
fileId: targetFileId,
},
});
}
useImperativeHandle(ref, () => ({
handleOpen,
}));
// 检查媒体文件时长的辅助函数
const checkMediaDuration = (file: File): Promise<number> => {
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(file);
const media = document.createElement(file.type.startsWith('video/') ? 'video' : 'audio');
media.onloadedmetadata = () => {
URL.revokeObjectURL(url);
resolve(media.duration);
};
media.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error('无法读取媒体文件'));
};
media.src = url;
});
};
// 上传文件
const handleUpload = async (options: UploadRequestOption) => {
const { file, onSuccess, onError, onProgress, filename = 'file' } = options;
// 获取文件扩展名
const fileExtension = (file as File).name.split('.').pop()?.toLowerCase();
const mediaExtensions = ['mp3', 'mp4', 'mov', 'wav'];
// 如果是媒体文件,进行大小和时长检查
if (fileExtension && mediaExtensions.includes(fileExtension)) {
const fileSizeInMB = (file as File).size / (1024 * 1024);
// 检查文件大小256MB限制
if (fileSizeInMB > 256) {
messageApi.error(`${t('knowledgeBase.sizeLimitError')}${fileSizeInMB.toFixed(2)}MB`);
onError?.(new Error(`${t('knowledgeBase.fileSizeExceeds')}`));
return;
}
try {
// 检查媒体时长150秒限制
const duration = await checkMediaDuration(file as File);
if (duration > 150) {
messageApi.error(`${t('knowledgeBase.fileDurationLimitError')}${Math.round(duration)}`);
onError?.(new Error(`${t('knowledgeBase.fileDurationExceeds')}`));
return;
}
} catch (error) {
messageApi.error(`${t('knowledgeBase.unableReadFile')}`);
onError?.(error as Error);
return;
}
}
const formData = new FormData();
formData.append(filename, file as File);
if (kbId) {
formData.append('kb_id', kbId);
}
if (parentId) {
formData.append('parent_id', parentId);
}
uploadFile(formData, {
kb_id: kbId,
parent_id: parentId,
onUploadProgress: (event) => {
if (!event.total) return;
const percent = Math.round((event.loaded / event.total) * 100);
onProgress?.({ percent }, file);
},
})
.then((res: UploadFileResponse) => {
onSuccess?.(res, new XMLHttpRequest());
if (res?.id) {
// 上传成功
// fileIds.push(res.id)
}
})
.catch((error) => {
onError?.(error as Error);
});
};
return (
<>
{contextHolder}
<RbModal
title={`${t('knowledgeBase.createA')} ${t('knowledgeBase.imageDataSet')}`}
open={visible}
onCancel={handleClose}
okText={t('common.create')}
onOk={handleSave}
confirmLoading={loading}
>
<Form form={form} layout="vertical">
{/* <Form.Item
name="name"
label={t('knowledgeBase.datasetName')}
rules={[{ required: true, message: t('knowledgeBase.pleaseEnterDatasetName') }]}
>
<Input placeholder={t('knowledgeBase.pleaseEnterDatasetName')} />
</Form.Item> */}
<Form.Item label={t('knowledgeBase.uploadMedia')}>
<UploadFiles
ref={uploadRef}
isCanDrag={true}
fileSize={100}
multiple={true}
maxCount={99}
fileType={['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp3', 'mp4', 'mov', 'wav']}
customRequest={handleUpload}
/>
</Form.Item>
</Form>
</RbModal>
</>);
}
);
export default CreateImageDataset;