Frontend start to use backend CRUD api
This commit is contained in:
@@ -1,21 +1,43 @@
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
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 { mockKnowledgeBases } from '../services/mockData';
|
||||
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<KnowledgeBase | null>(null);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [kbs, setKbs] = useState(mockKnowledgeBases);
|
||||
const [kbs, setKbs] = useState<KnowledgeBase[]>([]);
|
||||
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');
|
||||
@@ -25,20 +47,17 @@ export const KnowledgeBasePage: React.FC = () => {
|
||||
setIsUploadOpen(true);
|
||||
};
|
||||
|
||||
const handleCreateKb = () => {
|
||||
const handleCreateKb = async () => {
|
||||
if (!newKbName.trim()) return;
|
||||
|
||||
const newKb: KnowledgeBase = {
|
||||
id: `kb_${Date.now()}`,
|
||||
name: newKbName.trim(),
|
||||
creator: 'Admin User',
|
||||
createdAt: new Date().toISOString().split('T')[0],
|
||||
documents: []
|
||||
};
|
||||
|
||||
setKbs([newKb, ...kbs]);
|
||||
setIsCreateKbOpen(false);
|
||||
setNewKbName('');
|
||||
try {
|
||||
await createKnowledgeBase(newKbName.trim());
|
||||
await refreshKnowledgeBases();
|
||||
setIsCreateKbOpen(false);
|
||||
setNewKbName('');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('新建知识库失败。');
|
||||
}
|
||||
};
|
||||
|
||||
if (view === 'detail' && selectedKb) {
|
||||
@@ -48,8 +67,22 @@ export const KnowledgeBasePage: React.FC = () => {
|
||||
kb={selectedKb}
|
||||
onBack={() => setView('list')}
|
||||
onImport={handleImportClick}
|
||||
onDeleteDocument={async (docId) => {
|
||||
try {
|
||||
await deleteKnowledgeDocument(selectedKb.id, docId);
|
||||
await refreshKnowledgeBases();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert('删除文档失败。');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<UploadModal
|
||||
kbId={selectedKb.id}
|
||||
isOpen={isUploadOpen}
|
||||
onClose={() => setIsUploadOpen(false)}
|
||||
onUploaded={refreshKnowledgeBases}
|
||||
/>
|
||||
<UploadModal isOpen={isUploadOpen} onClose={() => setIsUploadOpen(false)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -103,6 +136,12 @@ export const KnowledgeBasePage: React.FC = () => {
|
||||
<Plus className="h-8 w-8 mb-2 opacity-50" />
|
||||
<span>新建知识库</span>
|
||||
</div>
|
||||
{!isLoading && filteredKbs.length === 0 && (
|
||||
<div className="col-span-full text-center text-muted-foreground py-8">暂无知识库</div>
|
||||
)}
|
||||
{isLoading && (
|
||||
<div className="col-span-full text-center text-muted-foreground py-8">加载中...</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* New Knowledge Base Dialog */}
|
||||
@@ -141,7 +180,8 @@ const KnowledgeBaseDetail: React.FC<{
|
||||
kb: KnowledgeBase;
|
||||
onBack: () => void;
|
||||
onImport: () => void;
|
||||
}> = ({ kb, onBack, onImport }) => {
|
||||
onDeleteDocument: (docId: string) => void;
|
||||
}> = ({ kb, onBack, onImport, onDeleteDocument }) => {
|
||||
const [docSearch, setDocSearch] = useState('');
|
||||
const filteredDocs = kb.documents.filter(d => d.name.toLowerCase().includes(docSearch.toLowerCase()));
|
||||
|
||||
@@ -192,7 +232,14 @@ const KnowledgeBaseDetail: React.FC<{
|
||||
<TableCell className="text-muted-foreground">{doc.size}</TableCell>
|
||||
<TableCell className="text-muted-foreground">{doc.uploadDate}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button variant="ghost" size="sm" className="text-destructive hover:text-destructive/80">删除</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive/80"
|
||||
onClick={() => onDeleteDocument(doc.id)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)) : (
|
||||
@@ -207,7 +254,7 @@ const KnowledgeBaseDetail: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const UploadModal: React.FC<{ isOpen: boolean; onClose: () => void }> = ({ isOpen, onClose }) => {
|
||||
const UploadModal: React.FC<{ kbId: string; isOpen: boolean; onClose: () => void; onUploaded: () => Promise<void> }> = ({ kbId, isOpen, onClose, onUploaded }) => {
|
||||
const [dragActive, setDragActive] = useState(false);
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -242,6 +289,19 @@ const UploadModal: React.FC<{ isOpen: boolean; onClose: () => void }> = ({ isOpe
|
||||
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 (
|
||||
<Dialog
|
||||
isOpen={isOpen}
|
||||
@@ -250,7 +310,7 @@ const UploadModal: React.FC<{ isOpen: boolean; onClose: () => void }> = ({ isOpe
|
||||
footer={
|
||||
<>
|
||||
<Button variant="ghost" onClick={onClose}>取消</Button>
|
||||
<Button onClick={() => { alert('Upload Started!'); onClose(); setFiles([]); }}>确认上传</Button>
|
||||
<Button onClick={handleUpload}>确认上传</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user