From 0b0612760c3954f3b70359f506bb5150fafc1366 Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 11:34:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M2-02/03=20admin=20KB=20page=20?= =?UTF-8?q?=E2=80=94=20source=20list=20+=20reference=20tracking=20drawer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- src/pages/KnowledgeBases.tsx | 87 +++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/src/pages/KnowledgeBases.tsx b/src/pages/KnowledgeBases.tsx index 04a40f6..028bbc3 100644 --- a/src/pages/KnowledgeBases.tsx +++ b/src/pages/KnowledgeBases.tsx @@ -1,51 +1,114 @@ import { useState } from 'react' import { useQuery, useQueryClient } from '@tanstack/react-query' -import { Table, Tag, Typography, Button, App } from 'antd' -import { ReloadOutlined, DeleteOutlined } from '@ant-design/icons' -import { getKnowledgeBases, deleteKnowledgeBase, type KnowledgeBase } from '@/services/knowledge-api' +import { Table, Tag, Typography, Button, App, Drawer, Space, Descriptions } from 'antd' +import { ReloadOutlined, DeleteOutlined, EyeOutlined, FileTextOutlined, LinkOutlined } from '@ant-design/icons' +import { api } from '@/services/http-client' import dayjs from 'dayjs' -const { Title } = Typography +const { Title, Text } = Typography function KBPage() { const { modal, message } = App.useApp() const qc = useQueryClient() const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) + const [drawerOpen, setDrawerOpen] = useState(false) + const [selectedKb, setSelectedKb] = useState(null) const { data, isLoading } = useQuery({ queryKey: ['knowledge-bases', page, pageSize], - queryFn: () => getKnowledgeBases(page, pageSize), + queryFn: (): Promise => api.get(`/admin-api/knowledge-bases?page=${page}&limit=${pageSize}`), }) - const handleDelete = (kb: KnowledgeBase) => modal.confirm({ - title: '删除知识库', content: `确定删除「${kb.title}」?`, okType: 'danger', - onOk: async () => { await deleteKnowledgeBase(kb.id); qc.invalidateQueries({ queryKey: ['knowledge-bases'] }); message.success('已删除') }, + const { data: sources } = useQuery({ + queryKey: ['kb-sources', selectedKb?.id], + queryFn: (): Promise => api.get(`/admin-api/knowledge-bases/${selectedKb?.id}/sources`), + enabled: !!selectedKb?.id, }) + const [refSourceId, setRefSourceId] = useState(null) + const { data: references } = useQuery({ + queryKey: ['source-refs', refSourceId], + queryFn: (): Promise => api.get(`/admin-api/knowledge-bases/sources/${refSourceId}/references`), + enabled: !!refSourceId, + }) + + const handleDelete = (kb: any) => modal.confirm({ + title: '删除知识库', content: `确定删除「${kb.title}」?`, okType: 'danger', + onOk: async () => { await api.delete(`/admin-api/knowledge-bases/${kb.id}`); qc.invalidateQueries({ queryKey: ['knowledge-bases'] }); message.success('已删除') }, + }) + + const sourceCols = [ + { title: '标题', dataIndex: 'title', width: 180, ellipsis: true }, + { title: '文件名', dataIndex: 'originalFilename', width: 180, ellipsis: true }, + { title: '类型', dataIndex: 'type', width: 70 }, + { title: '解析', dataIndex: 'parseStatus', width: 70, render: (s: string) => {s} }, + { title: '版本', dataIndex: 'version', width: 60, align: 'center' as const }, + { title: '文本长度', dataIndex: 'textLength', width: 80, render: (v: number) => v?.toLocaleString() || '-' }, + { title: '引用', width: 70, render: (_: any, r: any) => ( + + )}, + ] + + const refCols = [ + { title: '工件类型', dataIndex: 'artifactType', width: 120 }, + { title: '工件ID', dataIndex: 'artifactId', width: 150, ellipsis: true }, + { title: '页码', dataIndex: 'pageNumber', width: 60, align: 'center' as const }, + { title: '章节', dataIndex: 'sectionTitle', width: 150, ellipsis: true }, + { title: '摘录', dataIndex: 'excerptText', ellipsis: true, render: (v: string) => v ? {v.slice(0, 100)} : '-' }, + ] + return (
知识库列表
- + `共 ${t} 条`, onChange: (p, ps) => { setPage(p); setPageSize(ps) } }} columns={[ { title: '名称', dataIndex: 'title', width: 200, ellipsis: true }, - { title: '用户', width: 120, render: (_, r) => r.user?.nickname || r.user?.email || '-' }, + { title: '用户', width: 120, render: (_: any, r: any) => r.user?.nickname || r.user?.email || '-' }, { title: '知识点', dataIndex: 'itemCount', width: 80, align: 'center' }, { title: '状态', dataIndex: 'status', width: 80, render: (s: string) => {s} }, { title: '创建时间', dataIndex: 'createdAt', width: 170, render: (d: string) => dayjs(d).format('YYYY-MM-DD HH:mm') }, { - title: '操作', width: 80, align: 'center', + title: '操作', width: 120, align: 'center', render: (_, r) => ( - +
+ + {refSourceId && ( + <> + <LinkOutlined /> 引用追踪 ({references?.length || 0}) +
+ + )} + + )} + ) }