import React, { useEffect, useState, useRef } from 'react'; import { Search, Plus, FileText, Upload, ArrowLeft, CloudUpload, File as FileIcon, X } from 'lucide-react'; import { Button, Input, TableHeader, TableRow, TableHead, TableCell, Card, Dialog } from '../components/UI'; import { KnowledgeBase } from '../types'; import { createKnowledgeBase, deleteKnowledgeDocument, fetchKnowledgeBases, uploadKnowledgeDocument } from '../services/backendApi'; export const KnowledgeBasePage: React.FC = () => { const [view, setView] = useState<'list' | 'detail'>('list'); const [selectedKb, setSelectedKb] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [kbs, setKbs] = useState([]); const [isUploadOpen, setIsUploadOpen] = useState(false); const [isCreateKbOpen, setIsCreateKbOpen] = useState(false); const [newKbName, setNewKbName] = useState(''); const [isLoading, setIsLoading] = useState(true); const filteredKbs = kbs.filter(kb => kb.name.toLowerCase().includes(searchTerm.toLowerCase())); const refreshKnowledgeBases = async () => { setIsLoading(true); try { const list = await fetchKnowledgeBases(); setKbs(list); if (selectedKb) { const nextSelected = list.find((item) => item.id === selectedKb.id) || null; setSelectedKb(nextSelected); } } catch (error) { console.error(error); alert('加载知识库失败,请检查后端服务。'); } finally { setIsLoading(false); } }; useEffect(() => { refreshKnowledgeBases(); }, []); const handleSelect = (kb: KnowledgeBase) => { setSelectedKb(kb); setView('detail'); }; const handleImportClick = () => { setIsUploadOpen(true); }; const handleCreateKb = async () => { if (!newKbName.trim()) return; try { await createKnowledgeBase(newKbName.trim()); await refreshKnowledgeBases(); setIsCreateKbOpen(false); setNewKbName(''); } catch (error) { console.error(error); alert('新建知识库失败。'); } }; if (view === 'detail' && selectedKb) { return (
setView('list')} onImport={handleImportClick} onDeleteDocument={async (docId) => { try { await deleteKnowledgeDocument(selectedKb.id, docId); await refreshKnowledgeBases(); } catch (error) { console.error(error); alert('删除文档失败。'); } }} /> setIsUploadOpen(false)} onUploaded={refreshKnowledgeBases} />
); } return (

知识库

{/* Search Bar - Layout aligned with History Page and width filled */}
setSearchTerm(e.target.value)} />
{filteredKbs.map(kb => (
handleSelect(kb)}>

{kb.name}

文档数量: {kb.documents.length}

创建人: {kb.creator}

创建时间: {kb.createdAt}

))} {/* Add New Placeholder */}
setIsCreateKbOpen(true)} className="border border-dashed border-white/10 rounded-xl p-6 flex flex-col items-center justify-center text-muted-foreground hover:bg-white/5 hover:border-primary/30 transition-all cursor-pointer min-h-[200px]" > 新建知识库
{!isLoading && filteredKbs.length === 0 && (
暂无知识库
)} {isLoading && (
加载中...
)}
{/* New Knowledge Base Dialog */} setIsCreateKbOpen(false)} title="新建知识库" footer={ <> } >
setNewKbName(e.target.value)} placeholder="请输入知识库名称..." autoFocus onKeyDown={(e) => e.key === 'Enter' && handleCreateKb()} />

知识库用于存储私域文档,AI 小助手在回答问题时会优先检索绑定的知识库内容。

); }; const KnowledgeBaseDetail: React.FC<{ kb: KnowledgeBase; onBack: () => void; onImport: () => void; onDeleteDocument: (docId: string) => void; }> = ({ kb, onBack, onImport, onDeleteDocument }) => { const [docSearch, setDocSearch] = useState(''); const filteredDocs = kb.documents.filter(d => d.name.toLowerCase().includes(docSearch.toLowerCase())); return (

{kb.name}

创建于 {kb.createdAt} · by {kb.creator}

文档列表

setDocSearch(e.target.value)} className="bg-black/20 border-transparent focus:bg-black/40" />
文档名称 大小 上传时间 操作 {filteredDocs.length > 0 ? filteredDocs.map(doc => ( {doc.name} {doc.size} {doc.uploadDate} )) : ( 暂无文档 )}
); }; const UploadModal: React.FC<{ kbId: string; isOpen: boolean; onClose: () => void; onUploaded: () => Promise }> = ({ kbId, isOpen, onClose, onUploaded }) => { const [dragActive, setDragActive] = useState(false); const [files, setFiles] = useState([]); const inputRef = useRef(null); const handleDrag = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); if (e.type === "dragenter" || e.type === "dragover") { setDragActive(true); } else if (e.type === "dragleave") { setDragActive(false); } }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { setFiles(prev => [...prev, ...Array.from(e.dataTransfer.files)]); } }; const handleChange = (e: React.ChangeEvent) => { e.preventDefault(); if (e.target.files && e.target.files[0]) { setFiles(prev => [...prev, ...Array.from(e.target.files || [])]); } }; const removeFile = (idx: number) => { setFiles(prev => prev.filter((_, i) => i !== idx)); }; const handleUpload = async () => { if (files.length === 0) return; try { await Promise.all(files.map((file) => uploadKnowledgeDocument(kbId, file))); await onUploaded(); onClose(); setFiles([]); } catch (error) { console.error(error); alert('上传失败,请稍后重试。'); } }; return ( } >
inputRef.current?.click()} >

点击上传 或将文件拖拽到此处

支持 PDF, DOCX, TXT (Max 10MB)

{files.length > 0 && (
{files.map((file, idx) => (
{file.name} ({(file.size / 1024).toFixed(1)} KB)
))}
)}
); };