import type React from "react"; import { useEffect, useState } from "react"; import { Table, Badge, Button, Breadcrumb, Tooltip, App, Card, Input, Empty, Spin } from "antd"; import { DeleteOutlined, EditOutlined, ReloadOutlined, } from "@ant-design/icons"; import { useNavigate, useParams } from "react-router"; import DetailHeader from "@/components/DetailHeader"; import { SearchControls } from "@/components/SearchControls"; import { KBFile, KnowledgeBaseItem } from "../knowledge-base.model"; import { mapFileData, mapKnowledgeBase } from "../knowledge-base.const"; import { deleteKnowledgeBaseByIdUsingDelete, deleteKnowledgeBaseFileByIdUsingDelete, queryKnowledgeBaseByIdUsingGet, queryKnowledgeBaseFilesUsingGet, retrieveKnowledgeBaseContent, } from "../knowledge-base.api"; import useFetchData from "@/hooks/useFetchData"; import AddDataDialog from "../components/AddDataDialog"; import CreateKnowledgeBase from "../components/CreateKnowledgeBase"; interface StatisticItem { icon?: React.ReactNode; label: string; value: string | number; } interface RagChunk { id: string; text: string; metadata: string; } interface RecallResult { score: number; entity: RagChunk; id?: string | object; primaryKey?: string; } const KnowledgeBaseDetailPage: React.FC = () => { const navigate = useNavigate(); const { message } = App.useApp(); const { id } = useParams<{ id: string }>(); const [knowledgeBase, setKnowledgeBase] = useState(undefined); const [showEdit, setShowEdit] = useState(false); const [activeTab, setActiveTab] = useState<'fileList' | 'recallTest'>('fileList'); const [recallLoading, setRecallLoading] = useState(false); const [recallResults, setRecallResults] = useState([]); const [recallQuery, setRecallQuery] = useState(""); const fetchKnowledgeBaseDetails = async (id: string) => { const { data } = await queryKnowledgeBaseByIdUsingGet(id); setKnowledgeBase(mapKnowledgeBase(data)); }; useEffect(() => { if (id) { fetchKnowledgeBaseDetails(id); } }, [id]); const { loading, tableData: files, searchParams, pagination, fetchData: fetchFiles, setSearchParams, handleFiltersChange, handleKeywordChange, } = useFetchData( (params) => id ? queryKnowledgeBaseFilesUsingGet(id, params) : Promise.resolve({ data: [] }), mapFileData ); // File table logic const handleDeleteFile = async (file: KBFile) => { try { await deleteKnowledgeBaseFileByIdUsingDelete(knowledgeBase!.id, { ids: [file.id] }); message.success("文件已删除"); fetchFiles(); } catch { message.error("文件删除失败"); } }; const handleDeleteKB = async (kb: KnowledgeBaseItem) => { await deleteKnowledgeBaseByIdUsingDelete(kb.id); message.success("知识库已删除"); navigate("/data/knowledge-base"); }; const handleRefreshPage = () => { if (knowledgeBase) { fetchKnowledgeBaseDetails(knowledgeBase.id); } fetchFiles(); setShowEdit(false); }; const handleRecallTest = async () => { if (!recallQuery || !knowledgeBase?.id) return; setRecallLoading(true); try { const result = await retrieveKnowledgeBaseContent({ query: recallQuery, topK: 10, threshold: 0.2, knowledgeBaseIds: [knowledgeBase.id], }); setRecallResults(result?.data || []); } catch { setRecallResults([]); } setRecallLoading(false); }; const operations = [ { key: "edit", label: "编辑知识库", icon: , onClick: () => { setShowEdit(true); }, }, { key: "refresh", label: "刷新知识库", icon: , onClick: () => { handleRefreshPage(); }, }, { key: "delete", label: "删除知识库", danger: true, confirm: { title: "确认删除该知识库吗?", description: "删除后将无法恢复,请谨慎操作。", cancelText: "取消", okText: "删除", okType: "danger", onConfirm: () => knowledgeBase && handleDeleteKB(knowledgeBase), }, icon: , }, ]; const fileOps = [ { key: "delete", label: "删除文件", icon: , danger: true, onClick: handleDeleteFile, }, ]; const fileColumns = [ { title: "文件名", dataIndex: "name", key: "name", width: 200, ellipsis: true, fixed: "left" as const, }, { title: "状态", dataIndex: "status", key: "vectorizationStatus", width: 120, render: (status: unknown) => { if (typeof status === 'object' && status !== null) { const s = status as { color?: string; label?: string }; return ; } return ; }, }, { title: "分块数", dataIndex: "chunkCount", key: "chunkCount", width: 100, ellipsis: true, }, { title: "创建时间", dataIndex: "createdAt", key: "createdAt", ellipsis: true, width: 180, }, { title: "更新时间", dataIndex: "updatedAt", key: "updatedAt", ellipsis: true, width: 180, }, { title: "操作", key: "actions", align: "right" as const, width: 100, render: (_: unknown, file: KBFile) => (
{fileOps.map((op) => (
), }, ]; return (
setShowEdit(false)} />
{activeTab === 'fileList' && ( <>
setSearchParams({ ...searchParams, filter: { type: [], status: [], tags: [] } })} showViewToggle={false} showReload={false} />
)}
{activeTab === 'fileList' ? ( ) : (
基于语义文本检索和全文检索后的加权平均结果
setRecallQuery(e.target.value)} onSearch={handleRecallTest} placeholder="请输入召回测试问题" enterButton="检索" loading={recallLoading} style={{ width: "100%", fontSize: 18, height: 48 }} />
{recallLoading ? ( ) : recallResults.length === 0 ? ( ) : (
{recallResults.map((item, idx) => ( ID: {item.entity?.id ?? "-"}} style={{ wordBreak: "break-all" }} >
{item.entity?.text ?? ""}
metadata:
{item.entity?.metadata}
))}
)}
)} ); }; export default KnowledgeBaseDetailPage;