admin-projects/src/pages/KnowledgeOps.tsx
wangdl 8663ceba66
Some checks failed
Deploy Admin Frontend / build-and-deploy (push) Failing after 10s
feat: HTTP 层重构 + API 抽离 + 状态管理优化 + 路由修复
- HTTP 层:安装 axios,创建 lib/api-client.ts 统一拦截器
- API 层:创建 33 个 api/*.ts 文件,所有页面迁移完成
- DataPages.tsx 拆分为 10 个独立文件
- message 导入统一为 App.useApp()
- 新增 StaticAntdProvider 全局消息/通知
- 全局 HTTP 错误/成功提示(notification.error/success)
- learning 路由 /learning → /learning/dashboard 修复选中 bug
- 学习会话页面优化(移除 ID 列、加批量删除、后端排序)
- 文档导入页面重构(知识库筛选、批量重新解析)
- 删除旧 service 文件 10 个(admin-api、billing-api 等)
- antd App 包裹根组件

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-01 22:44:51 +08:00

92 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Table, Tag, Typography, Button, App, Tabs, Input, Card, Space } from 'antd'
import { ReloadOutlined, SearchOutlined, ExperimentOutlined } from '@ant-design/icons'
import { knowledgeAPI, vectorAPI } from '@/api'
import { candidateStatusLabels } from '@/constants/labels'
import dayjs from 'dayjs'
const { Title, Text } = Typography
export default function KnowledgeOpsPage() {
const qc = useQueryClient()
const { message } = App.useApp()
const [sourceId, setSourceId] = useState('')
const [debugQuery, setDebugQuery] = useState('')
const { data: candidates } = useQuery({
queryKey: ['ops', 'candidates'],
queryFn: (): Promise<any> => knowledgeAPI.candidates(),
})
const { data: chunks } = useQuery({
queryKey: ['ops', 'chunks', sourceId],
queryFn: (): Promise<any> => knowledgeAPI.chunks(sourceId),
enabled: !!sourceId,
})
const handleDebugSearch = async () => {
if (!debugQuery) return
const res: any = await vectorAPI.debugSearch({ query: debugQuery })
message.info(JSON.stringify(res.data))
}
const candidateCols = [
{ title: '标题', dataIndex: 'title', width: 200, ellipsis: true },
{ title: '用户', dataIndex: 'userId', width: 120, ellipsis: true, render: (_: any, r: any) => r.userName || r.userId },
{ title: '状态', dataIndex: 'status', width: 80, render: (s: string) => <Tag color={s==='PENDING'?'gold':s==='ACCEPTED'?'green':'red'}>{candidateStatusLabels[s] || s}</Tag> },
{ title: '置信度', dataIndex: 'confidence', width: 80, align: 'center' as const, render: (v: any) => v ? `${(Number(v)*100).toFixed(0)}%` : '-' },
{ title: '难度', dataIndex: 'difficulty', width: 70 },
{ title: '时间', dataIndex: 'createdAt', width: 130, render: (d: string) => dayjs(d).format('MM-DD HH:mm') },
]
const chunkCols = [
{ title: '#', dataIndex: 'chunkIndex', width: 50 },
{ title: '内容', dataIndex: 'content', ellipsis: true, render: (v: string) => <Text style={{ fontSize: 12 }}>{v?.slice(0, 150)}</Text> },
{ title: '页', dataIndex: 'pageNumber', width: 50 },
{ title: '章节', dataIndex: 'sectionTitle', width: 150, ellipsis: true },
{ title: 'Tokens', dataIndex: 'tokenCount', width: 70, align: 'center' as const },
{ title: 'Embedding', dataIndex: 'embeddingStatus', width: 80, render: (s: string) => <Tag color={s==='done'?'green':'default'}>{s}</Tag> },
]
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<Title level={5} style={{ margin: 0 }}><ExperimentOutlined /> </Title>
<Button icon={<ReloadOutlined />} onClick={() => qc.invalidateQueries({ queryKey: ['ops'] })}></Button>
</div>
<Tabs items={[
{
key: 'candidates', label: '候选巡检',
children: <Table dataSource={candidates || []} columns={candidateCols} rowKey="id" pagination={{ pageSize: 20 }} size="small" />,
},
{
key: 'chunks', label: 'Chunk 管理',
children: (
<>
<Space style={{ marginBottom: 16 }}>
<Input.Search placeholder="输入 Source ID" value={sourceId} onChange={e => setSourceId(e.target.value)} onSearch={v => setSourceId(v)} enterButton={<SearchOutlined />} style={{ width: 300 }} />
</Space>
{sourceId && <Table dataSource={chunks || []} columns={chunkCols} rowKey="id" pagination={{ pageSize: 20 }} size="small" />}
</>
),
},
{
key: 'rag-debug', label: 'RAG 调试',
children: (
<Card size="small" title="检索调试">
<Space>
<Input.Search placeholder="输入查询文本" value={debugQuery} onChange={e => setDebugQuery(e.target.value)} onSearch={handleDebugSearch} enterButton="检索" style={{ width: 400 }} />
</Space>
<div style={{ marginTop: 8 }}>
<Text type="secondary"> RAG M3 </Text>
</div>
</Card>
),
},
]} />
</div>
)
}