2025-04-02 10:05:43 +08:00
|
|
|
|
import React, { useRef, useState, useEffect } from 'react';
|
|
|
|
|
import { useDispatch } from 'react-redux';
|
|
|
|
|
import { uploadDocument, getKnowledgeBaseById } from '../../../../store/knowledgeBase/knowledgeBase.thunks';
|
2025-03-07 23:59:53 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 文件上传模态框组件
|
|
|
|
|
*/
|
2025-04-02 10:05:43 +08:00
|
|
|
|
const FileUploadModal = ({ show, knowledgeBaseId, onClose }) => {
|
|
|
|
|
const dispatch = useDispatch();
|
2025-03-07 23:59:53 +08:00
|
|
|
|
const fileInputRef = useRef(null);
|
2025-03-22 10:13:42 +08:00
|
|
|
|
const modalRef = useRef(null);
|
2025-04-02 10:05:43 +08:00
|
|
|
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
|
|
|
const [isUploading, setIsUploading] = useState(false);
|
|
|
|
|
const [fileError, setFileError] = useState('');
|
2025-03-22 10:13:42 +08:00
|
|
|
|
|
|
|
|
|
// 处理上传区域点击事件
|
|
|
|
|
const handleUploadAreaClick = () => {
|
|
|
|
|
fileInputRef.current?.click();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理拖拽事件
|
|
|
|
|
const handleDragOver = (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDrop = (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
2025-04-02 10:05:43 +08:00
|
|
|
|
|
|
|
|
|
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
|
|
|
|
|
handleFileSelected(e.dataTransfer.files[0]);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleFileChange = (e) => {
|
|
|
|
|
if (e.target.files && e.target.files.length > 0) {
|
|
|
|
|
handleFileSelected(e.target.files[0]);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleFileSelected = (file) => {
|
|
|
|
|
setFileError('');
|
|
|
|
|
setSelectedFile(file);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const resetFileInput = () => {
|
|
|
|
|
setSelectedFile(null);
|
|
|
|
|
setFileError('');
|
|
|
|
|
if (fileInputRef.current) {
|
|
|
|
|
fileInputRef.current.value = '';
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleUpload = async () => {
|
|
|
|
|
if (!selectedFile) {
|
|
|
|
|
setFileError('请选择要上传的文件');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setIsUploading(true);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await dispatch(
|
|
|
|
|
uploadDocument({
|
|
|
|
|
knowledge_base_id: knowledgeBaseId,
|
|
|
|
|
file: selectedFile,
|
|
|
|
|
})
|
|
|
|
|
).unwrap();
|
|
|
|
|
|
|
|
|
|
// 成功上传后刷新知识库信息以更新文件列表
|
|
|
|
|
dispatch(getKnowledgeBaseById(knowledgeBaseId));
|
|
|
|
|
|
|
|
|
|
// Reset the file input
|
|
|
|
|
resetFileInput();
|
|
|
|
|
|
|
|
|
|
// 不要在上传成功后关闭模态框,允许用户继续上传或手动关闭
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Upload failed:', error);
|
|
|
|
|
setFileError('文件上传失败: ' + (error?.message || '未知错误'));
|
|
|
|
|
|
|
|
|
|
// 清空选中的文件
|
|
|
|
|
resetFileInput();
|
|
|
|
|
} finally {
|
|
|
|
|
setIsUploading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
// 只有在非上传状态才允许关闭
|
|
|
|
|
if (!isUploading) {
|
|
|
|
|
resetFileInput();
|
|
|
|
|
onClose();
|
|
|
|
|
}
|
2025-03-22 10:13:42 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 清理函数
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
return () => {
|
|
|
|
|
// 确保在组件卸载时清理所有引用
|
|
|
|
|
if (fileInputRef.current) {
|
|
|
|
|
fileInputRef.current.value = '';
|
|
|
|
|
}
|
|
|
|
|
if (modalRef.current) {
|
|
|
|
|
modalRef.current = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
2025-03-07 23:59:53 +08:00
|
|
|
|
|
|
|
|
|
if (!show) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
2025-03-22 10:13:42 +08:00
|
|
|
|
ref={modalRef}
|
2025-03-07 23:59:53 +08:00
|
|
|
|
className='modal-backdrop'
|
|
|
|
|
style={{
|
|
|
|
|
position: 'fixed',
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
bottom: 0,
|
|
|
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
zIndex: 1050,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className='modal-content bg-white rounded shadow'
|
|
|
|
|
style={{
|
|
|
|
|
width: '500px',
|
|
|
|
|
maxWidth: '90%',
|
|
|
|
|
padding: '20px',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className='modal-header d-flex justify-content-between align-items-center mb-3'>
|
2025-04-02 10:05:43 +08:00
|
|
|
|
<h5 className='modal-title m-0'>上传文档</h5>
|
|
|
|
|
<button
|
|
|
|
|
type='button'
|
|
|
|
|
className='btn-close'
|
|
|
|
|
onClick={handleClose}
|
|
|
|
|
disabled={isUploading}
|
|
|
|
|
aria-label='Close'
|
|
|
|
|
></button>
|
2025-03-07 23:59:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
<div className='modal-body'>
|
|
|
|
|
<div
|
|
|
|
|
className={`mb-3 p-4 border rounded text-center ${
|
2025-04-02 10:05:43 +08:00
|
|
|
|
fileError ? 'border-danger' : 'border-dashed'
|
2025-03-07 23:59:53 +08:00
|
|
|
|
}`}
|
2025-04-02 10:05:43 +08:00
|
|
|
|
style={{ cursor: isUploading ? 'not-allowed' : 'pointer' }}
|
|
|
|
|
onClick={!isUploading ? handleUploadAreaClick : undefined}
|
|
|
|
|
onDrop={!isUploading ? handleDrop : undefined}
|
2025-03-22 10:13:42 +08:00
|
|
|
|
onDragOver={handleDragOver}
|
2025-03-07 23:59:53 +08:00
|
|
|
|
>
|
2025-03-22 10:13:42 +08:00
|
|
|
|
<input
|
|
|
|
|
type='file'
|
|
|
|
|
ref={fileInputRef}
|
|
|
|
|
className='d-none'
|
2025-04-02 10:05:43 +08:00
|
|
|
|
onChange={handleFileChange}
|
|
|
|
|
accept='.pdf,.doc,.docx,.txt,.md,.csv,.xlsx,.xls'
|
|
|
|
|
disabled={isUploading}
|
2025-03-22 10:13:42 +08:00
|
|
|
|
/>
|
2025-04-02 10:05:43 +08:00
|
|
|
|
{selectedFile ? (
|
2025-03-07 23:59:53 +08:00
|
|
|
|
<div>
|
|
|
|
|
<p className='mb-1'>已选择文件:</p>
|
2025-04-02 10:05:43 +08:00
|
|
|
|
<p className='fw-bold mb-0'>{selectedFile.name}</p>
|
2025-03-07 23:59:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div>
|
|
|
|
|
<p className='mb-1'>点击或拖拽文件到此处上传</p>
|
2025-04-02 10:05:43 +08:00
|
|
|
|
<p className='text-muted small mb-0'>
|
|
|
|
|
支持 PDF, Word, Excel, TXT, Markdown, CSV 等格式
|
|
|
|
|
</p>
|
2025-03-07 23:59:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-04-02 10:05:43 +08:00
|
|
|
|
{fileError && <div className='text-danger mt-2'>{fileError}</div>}
|
2025-03-07 23:59:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-03-13 09:14:25 +08:00
|
|
|
|
<div className='modal-footer gap-2'>
|
2025-04-02 10:05:43 +08:00
|
|
|
|
<button type='button' className='btn btn-secondary' onClick={handleClose} disabled={isUploading}>
|
|
|
|
|
关闭
|
2025-03-07 23:59:53 +08:00
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type='button'
|
2025-04-02 10:05:43 +08:00
|
|
|
|
className='btn btn-primary'
|
|
|
|
|
onClick={handleUpload}
|
|
|
|
|
disabled={!selectedFile || isUploading}
|
2025-03-07 23:59:53 +08:00
|
|
|
|
>
|
2025-04-02 10:05:43 +08:00
|
|
|
|
{isUploading ? (
|
2025-03-07 23:59:53 +08:00
|
|
|
|
<>
|
|
|
|
|
<span
|
|
|
|
|
className='spinner-border spinner-border-sm me-2'
|
|
|
|
|
role='status'
|
|
|
|
|
aria-hidden='true'
|
|
|
|
|
></span>
|
|
|
|
|
上传中...
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
2025-04-02 10:05:43 +08:00
|
|
|
|
'上传文档'
|
2025-03-07 23:59:53 +08:00
|
|
|
|
)}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default FileUploadModal;
|