"use client"; import { useState, useRef, useCallback } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import api from "@/lib/api"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { toast } from "sonner"; import { useAuthStore } from "@/stores/auth"; import { BookOpen, Upload, FileText, Trash2, Plus, Database, Search, } from "lucide-react"; interface KnowledgeBase { id: string; name: string; description: string; visibility: string; document_count: number; total_chars: number; status: string; created_at: string; updated_at: string; } interface KBDocument { id: string; filename: string; file_size: number; file_type: string; status: string; created_at: string; } function formatFileSize(bytes: number): string { if (bytes < 1024) return bytes + " B"; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB"; return (bytes / (1024 * 1024)).toFixed(1) + " MB"; } function getStatusBadge(status: string) { switch (status) { case "completed": return 已完成; case "indexing": return 索引中; case "failed": return 失败; default: return {status}; } } function getVisibilityLabel(v: string) { switch (v) { case "public": return "全单位"; case "department": return "本科室"; default: return "私有"; } } export default function KnowledgePage() { const queryClient = useQueryClient(); const user = useAuthStore((s) => s.user); const orgId = user?.org_id; const [showCreate, setShowCreate] = useState(false); const [selectedKB, setSelectedKB] = useState(null); const [form, setForm] = useState({ name: "", description: "", visibility: "private" }); const [searchTerm, setSearchTerm] = useState(""); const fileInputRef = useRef(null); const { data: knowledgeBases, isLoading: kbLoading } = useQuery({ queryKey: ["knowledgeBases", orgId], queryFn: () => api.get(`/api/v1/knowledge/${orgId ? `?org_id=${orgId}` : ""}`), }); const { data: documents } = useQuery({ queryKey: ["kbDocuments", selectedKB?.id], queryFn: () => api.get(`/api/v1/knowledge/${selectedKB!.id}/documents`), enabled: !!selectedKB, }); const createKB = useMutation({ mutationFn: () => api.post("/api/v1/knowledge/", form), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["knowledgeBases"] }); setShowCreate(false); setForm({ name: "", description: "", visibility: "private" }); toast.success("知识库创建成功"); }, onError: (err: Error) => toast.error(err.message), }); const deleteKB = useMutation({ mutationFn: (id: string) => api.delete(`/api/v1/knowledge/${id}`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["knowledgeBases"] }); if (selectedKB) setSelectedKB(null); toast.success("知识库已删除"); }, onError: (err: Error) => toast.error(err.message), }); const uploadDoc = useMutation({ mutationFn: async (file: File) => { const formData = new FormData(); formData.append("file", file); const res = await fetch(`/api/v1/knowledge/${selectedKB!.id}/documents`, { method: "POST", headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, body: formData, }); if (!res.ok) throw new Error("上传失败"); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["kbDocuments"] }); queryClient.invalidateQueries({ queryKey: ["knowledgeBases"] }); toast.success("文档上传成功"); }, onError: (err: Error) => toast.error(err.message), }); const deleteDoc = useMutation({ mutationFn: (docId: string) => api.delete(`/api/v1/knowledge/${selectedKB!.id}/documents/${docId}`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["kbDocuments"] }); queryClient.invalidateQueries({ queryKey: ["knowledgeBases"] }); toast.success("文档已删除"); }, onError: (err: Error) => toast.error(err.message), }); const handleFileUpload = useCallback(() => { fileInputRef.current?.click(); }, []); const onFileChange = useCallback((e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { uploadDoc.mutate(file); e.target.value = ""; } }, [uploadDoc]); const filteredKBs = knowledgeBases?.filter( (kb) => !searchTerm || kb.name.includes(searchTerm) || kb.description?.includes(searchTerm) ); return (

知识库管理

管理政策法规、制度文件等知识资源

setSearchTerm(e.target.value)} className="pl-9" />
{kbLoading && (
{[1, 2].map((i) => (
))}
)} {!kbLoading && filteredKBs?.length === 0 && (

{searchTerm ? "未找到匹配的知识库" : "暂无知识库,点击上方按钮创建"}

)} {filteredKBs?.map((kb) => ( setSelectedKB(kb)} >
{kb.name} {kb.document_count} 文档

{kb.description || "暂无描述"}

{getVisibilityLabel(kb.visibility)} {new Date(kb.updated_at).toLocaleDateString("zh-CN")}
))}
{selectedKB ? (
{selectedKB.name}

{selectedKB.description}

{getVisibilityLabel(selectedKB.visibility)} {selectedKB.document_count} 个文档
{documents?.length === 0 ? (

暂无文档

支持上传 PDF、DOCX、TXT、MD、CSV、XLSX 格式

) : (
{documents?.map((doc) => ( ))}
文件名 类型 大小 状态 上传时间 操作
{doc.filename} {doc.file_type || "-"} {formatFileSize(doc.file_size)} {getStatusBadge(doc.status)} {new Date(doc.created_at).toLocaleString("zh-CN")}
)}
) : (

请从左侧选择一个知识库查看文档

或点击"新建知识库"创建政策法规资源库

)}
新建知识库
setForm({ ...form, name: e.target.value })} placeholder="例如:科技局政策法规库" />