"use client"; import { useState, useCallback } from "react"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { BookOpen, Plus, Trash2, Search, FileText, Link, Type, AlertCircle, Loader2, } from "lucide-react"; import { knowledgeApi, type KnowledgeBase, type KnowledgeDocument, } from "@/lib/api"; import { useApi } from "@/lib/hooks/use-api"; import { LoadingState, ErrorState } from "@/components/ui/api-states"; // ── 状态 Badge ───────────────────────────────────────────────────────────────── function StatusBadge({ status }: { status: string }) { switch (status) { case "processing": return 处理中; case "ready": return 就绪; case "failed": return 失败; case "active": return 活跃; default: return {status}; } } function SourceTypeBadge({ type }: { type: string }) { switch (type) { case "text": return ( 文本 ); case "url": return ( URL ); case "markdown": return ( Markdown ); default: return {type}; } } // ── 空状态组件 ───────────────────────────────────────────────────────────────── function EmptyState({ onCreateClick }: { onCreateClick: () => void }) { return (

还没有知识库

创建您的第一个知识库,为AI内容生产提供精准的知识支撑

); } // ── 知识库卡片 + 展开文档列表 ──────────────────────────────────────────────────── function KnowledgeBaseCard({ kb, isExpanded, onToggle, onDelete, onUpload, }: { kb: KnowledgeBase; isExpanded: boolean; onToggle: () => void; onDelete: (id: string) => void; onUpload: (id: string) => void; }) { const docsUrl = isExpanded ? `/api/v1/knowledge/bases/${kb.id}/documents` : null; const { data: documents = [], isLoading: docsLoading, error: docsApiError, refresh: refreshDocs } = useApi(docsUrl); const docsError = docsApiError?.message ?? null; const handleDeleteDoc = async (docId: string) => { try { await knowledgeApi.deleteDocument(undefined, kb.id, docId); refreshDocs(); } catch (err) { console.error("Delete doc error:", err); } }; return (
{kb.name} {kb.description && ( {kb.description} )}
{kb.document_count} 文档
{isExpanded && (

文档列表

{docsLoading ? (
加载中...
) : docsError ? (
{docsError}
) : documents.length === 0 ? (

暂无文档,点击上方按钮上传

) : ( 标题 来源 状态 分块数 操作 {documents.map((doc) => ( {doc.title} {doc.chunk_count} ))}
)}
)}
); } // ── 主页面 ───────────────────────────────────────────────────────────────────── export default function KnowledgePage() { const [activeTab, setActiveTab] = useState("enterprise"); const [searchQuery, setSearchQuery] = useState(""); const [expandedKbId, setExpandedKbId] = useState(null); // SWR data fetching const { data: enterpriseBases = [], isLoading: enterpriseLoading, error: enterpriseError, mutate: mutateEnterprise, } = useApi("/api/v1/knowledge/bases/?type=enterprise"); const { data: industryBases = [], isLoading: industryLoading, error: industryError, } = useApi("/api/v1/knowledge/bases/?type=industry"); const loading = enterpriseLoading || industryLoading; const error = enterpriseError?.message || industryError?.message || null; // Create KB dialog const [createDialogOpen, setCreateDialogOpen] = useState(false); const [newKbName, setNewKbName] = useState(""); const [newKbDescription, setNewKbDescription] = useState(""); const [createLoading, setCreateLoading] = useState(false); const [createError, setCreateError] = useState(null); // Upload doc dialog const [uploadDialogOpen, setUploadDialogOpen] = useState(false); const [uploadKbId, setUploadKbId] = useState(null); const [docTitle, setDocTitle] = useState(""); const [docSourceType, setDocSourceType] = useState<"text" | "url" | "markdown">("text"); const [docContent, setDocContent] = useState(""); const [docUrl, setDocUrl] = useState(""); const [uploadLoading, setUploadLoading] = useState(false); const [uploadError, setUploadError] = useState(null); const fetchBases = useCallback(() => { mutateEnterprise(); }, [mutateEnterprise]); const currentBases = activeTab === "enterprise" ? enterpriseBases : industryBases; const filteredBases = searchQuery ? currentBases.filter( (kb) => kb.name.toLowerCase().includes(searchQuery.toLowerCase()) || kb.description?.toLowerCase().includes(searchQuery.toLowerCase()) ) : currentBases; const handleCreateKb = async () => { if (!newKbName.trim()) return; try { setCreateLoading(true); setCreateError(null); await knowledgeApi.createBase(undefined, { name: newKbName.trim(), type: "enterprise", description: newKbDescription.trim() || undefined, }); mutateEnterprise(); setCreateDialogOpen(false); setNewKbName(""); setNewKbDescription(""); } catch (err) { setCreateError(err instanceof Error ? err.message : "创建失败"); } finally { setCreateLoading(false); } }; const handleDeleteKb = async (kbId: string) => { try { await knowledgeApi.deleteBase(undefined, kbId); mutateEnterprise(); if (expandedKbId === kbId) setExpandedKbId(null); } catch (err) { console.error("Delete KB error:", err); } }; const handleUploadDoc = async () => { if (!uploadKbId || !docTitle.trim()) return; try { setUploadLoading(true); setUploadError(null); await knowledgeApi.uploadDocument(undefined, uploadKbId, { title: docTitle.trim(), source_type: docSourceType, content: docSourceType !== "url" ? docContent : undefined, source_url: docSourceType === "url" ? docUrl : undefined, }); mutateEnterprise(); setUploadDialogOpen(false); setDocTitle(""); setDocContent(""); setDocUrl(""); setDocSourceType("text"); } catch (err) { setUploadError(err instanceof Error ? err.message : "上传失败"); } finally { setUploadLoading(false); } }; const openUploadDialog = (kbId: string) => { setUploadKbId(kbId); setUploadError(null); setUploadDialogOpen(true); }; if (loading) { return (

知识库

管理行业和企业知识,为AI内容生产提供智能支撑

); } return (
{/* 页面标题 */}

知识库

管理行业和企业知识,为AI内容生产提供智能支撑

{/* Error Banner */} {error && ( )} {/* Tab 切换 */} 企业知识库 ({enterpriseBases.length}) 行业知识库 ({industryBases.length}) {/* ── 企业知识库 ── */}
setSearchQuery(e.target.value)} className="pl-9" />
{filteredBases.length === 0 ? ( setCreateDialogOpen(true)} /> ) : (
{filteredBases.map((kb) => ( setExpandedKbId(expandedKbId === kb.id ? null : kb.id)} onDelete={handleDeleteKb} onUpload={openUploadDialog} /> ))}
)}
{/* ── 行业知识库 ── */}

行业知识库由平台统一维护,提供行业级知识支撑

{filteredBases.length === 0 ? (

暂无行业知识库

) : (
{filteredBases.map((kb) => (
{kb.name} 平台维护
{kb.description && ( {kb.description} )}
{kb.document_count} 文档
))}
)}
{/* ── 创建知识库 Dialog ── */} 创建知识库 创建企业专属知识库,为AI内容生产提供精准知识支撑
{createError && (
{createError}
)}
setNewKbName(e.target.value)} />