From f6f9ab2ef02d395f55acf6d02906d773f7ce444b Mon Sep 17 00:00:00 2001 From: wangdl Date: Fri, 19 Jun 2026 15:32:05 +0800 Subject: [PATCH] feat: implement knowledge sources admin page --- src/App.tsx | 3 +- src/pages/KnowledgeSources.tsx | 88 ++++++++++++++++++++++++++++++++++ src/routes/index.tsx | 3 +- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/pages/KnowledgeSources.tsx diff --git a/src/App.tsx b/src/App.tsx index f0b156e..cf83898 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -56,6 +56,7 @@ const UserDiagnosePage = lazy(() => import('./pages/learning/DataPages').then(m const MaterialDiagnosePage = lazy(() => import('./pages/learning/DataPages').then(m => ({ default: m.MaterialDiagnosePage }))) const LearningReplay = lazy(() => import('./pages/learning/ReplayPage')) const BackupAdmin = lazy(() => import('./pages/BackupAdmin')) +const KnowledgeSourcesPage = lazy(() => import('./pages/KnowledgeSources')) const ReportingAdmin = lazy(() => import('./pages/ReportingAdmin')) const queryClient = new QueryClient() @@ -120,7 +121,7 @@ function App() { } /> }>} /> - } /> + }>} /> }>} /> }>} /> }>} /> diff --git a/src/pages/KnowledgeSources.tsx b/src/pages/KnowledgeSources.tsx new file mode 100644 index 0000000..85e59cd --- /dev/null +++ b/src/pages/KnowledgeSources.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react' +import { Table, Tag, Space, Input, Select, Typography } from 'antd' +import { SearchOutlined } from '@ant-design/icons' +import { useQuery } from '@tanstack/react-query' +import { api } from '@/services/http-client' +import { parseStatusLabels } from '@/constants/labels' +import dayjs from 'dayjs' + +const { Title } = Typography + +const mimeLabels: Record = { + 'text/plain': '文本', 'text/markdown': 'Markdown', + 'application/pdf': 'PDF', 'application/epub+zip': 'EPUB', + 'image/jpeg': '图片', 'image/png': '图片', 'image/webp': '图片', +} + +const indexStatusLabels: Record = { + pending: '待索引', indexing: '索引中', completed: '已索引', failed: '索引失败', +} + +const parseStatusColors: Record = { + pending: 'default', processing: 'processing', completed: 'green', failed: 'red', +} +const indexStatusColors: Record = { + pending: 'default', indexing: 'processing', completed: 'green', failed: 'red', +} + +export default function KnowledgeSourcesPage() { + const [page, setPage] = useState(1) + const [kbFilter, setKbFilter] = useState() + const [statusFilter, setStatusFilter] = useState() + const [search, setSearch] = useState('') + + const { data, isLoading } = useQuery({ + queryKey: ['admin', 'knowledge-sources', page, kbFilter, statusFilter, search], + queryFn: () => { + const p = new URLSearchParams({ page: String(page), limit: '20' }) + if (kbFilter) p.set('knowledgeBaseId', kbFilter) + if (statusFilter) p.set('parseStatus', statusFilter) + if (search) p.set('search', search) + return api.get(`/admin-api/knowledge-bases/all-sources?${p.toString()}`) + }, + staleTime: 10_000, + }) + + // KB list for filter dropdown + const { data: kbList } = useQuery({ + queryKey: ['admin', 'kb-list'], + queryFn: () => api.get('/admin-api/knowledge-bases?limit=200').then(d => d?.items || []), + staleTime: 60_000, + }) + + const columns = [ + { title: '文件名', dataIndex: 'originalFilename', width: 220, ellipsis: true, render: (v: string, r: any) => v || r.title || '-' }, + { title: '知识库', dataIndex: 'kbName', width: 140, ellipsis: true, render: (v: string, r: any) => v || r.knowledgeBaseId }, + { title: '类型', dataIndex: 'mimeType', width: 80, render: (v: string) => mimeLabels[v] || v?.split('/').pop() || '-' }, + { title: '大小', dataIndex: 'sizeBytes', width: 80, render: (v: number) => v ? (v / 1024).toFixed(1) + 'KB' : '-' }, + { title: '字数', dataIndex: 'textLength', width: 70, render: (v: number) => v || '-' }, + { + title: '解析', dataIndex: 'parseStatus', width: 90, + render: (v: string) => {parseStatusLabels[v] || v}, + }, + { + title: '索引', dataIndex: 'indexStatus', width: 90, + render: (v: string) => {indexStatusLabels[v] || v}, + }, + { title: '上传时间', dataIndex: 'createdAt', width: 140, render: (d: string) => d ? dayjs(d).format('MM-DD HH:mm') : '-' }, + ] + + return ( +
+ 知识源列表 + + } placeholder="搜索文件名" allowClear value={search} + onChange={e => { setSearch(e.target.value); setPage(1) }} style={{ width: 200 }} /> + { setStatusFilter(v); setPage(1) }} style={{ width: 130 }} + options={Object.entries(parseStatusLabels).map(([v, l]) => ({ value: v, label: l }))} /> + + + + ) +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 9150b97..485dd47 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -15,6 +15,7 @@ const AnomalyPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ const UserTimelinePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.UserTimelinePage }))) const UserDiagnosePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.UserDiagnosePage }))) const MaterialDiagnosePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.MaterialDiagnosePage }))) +const KnowledgeSourcesPage = lazy(() => import('@/pages/KnowledgeSources')) const LearningReplay = lazy(() => import('@/pages/learning/ReplayPage')) export interface RouteConfig { @@ -31,7 +32,7 @@ export const routeConfig: RouteConfig[] = [ { path: '/users/members', title: '普通用户', element: MemberManagement }, { path: '/membership', title: '会员与额度', element: UserManagement, requiredRole: 'ADMIN' }, { path: '/knowledge/bases', title: '知识库列表', element: UserManagement }, - { path: '/knowledge/sources', title: '知识源列表', element: UserManagement }, + { path: '/knowledge/sources', title: '知识源列表', element: KnowledgeSourcesPage }, { path: '/imports', title: '文档导入', element: UserManagement }, { path: '/ai-costs', title: 'AI 调用与成本', element: UserManagement }, { path: '/files', title: '文件与 COS', element: UserManagement },