admin-projects/src/pages/VectorAdmin.tsx

83 lines
3.4 KiB
TypeScript
Raw Normal View History

import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Card, Row, Col, Statistic, Button, Typography, Descriptions, App } from 'antd'
import { ReloadOutlined, DatabaseOutlined, NodeIndexOutlined } from '@ant-design/icons'
import { api } from '@/services/http-client'
const { Title } = Typography
export default function VectorAdminPage() {
const qc = useQueryClient()
const { message } = App.useApp()
const { data: coll, isLoading: collLoading } = useQuery({
queryKey: ['vector', 'collection'],
queryFn: (): Promise<any> => api.get('/admin-api/vector/collection'),
staleTime: 30_000,
})
const { data: count } = useQuery({
queryKey: ['vector', 'count'],
queryFn: (): Promise<any> => api.get('/admin-api/vector/count'),
staleTime: 30_000,
})
const handleReindex = async () => {
try {
await api.post('/admin-api/vector/reindex')
message.success('索引重建已提交')
qc.invalidateQueries({ queryKey: ['vector'] })
} catch {
message.error('操作失败')
}
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<Title level={5} style={{ margin: 0 }}><DatabaseOutlined /> </Title>
<Button icon={<ReloadOutlined />} onClick={() => qc.invalidateQueries({ queryKey: ['vector'] })}></Button>
</div>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={6}>
<Card size="small"><Statistic title="向量总数" value={count?.count ?? 0} loading={collLoading} suffix="条" /></Card>
</Col>
<Col span={6}>
<Card size="small"><Statistic title="向量维度" value={coll?.vectorSize || 1024} /></Card>
</Col>
<Col span={6}>
<Card size="small"><Statistic title="距离度量" value={coll?.distance || 'Cosine'} /></Card>
</Col>
<Col span={6}>
<Card size="small"><Statistic title="状态" value={coll?.status || '-'} valueStyle={{ color: coll?.status === 'green' ? '#52c41a' : '#faad14' }} /></Card>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={16}>
<Card size="small" title="Collection 信息">
<Descriptions column={2} size="small" bordered>
<Descriptions.Item label="名称">{coll?.name || 'zhixi_chunks'}</Descriptions.Item>
<Descriptions.Item label="向量维度">{coll?.vectorSize || 1024}</Descriptions.Item>
<Descriptions.Item label="距离度量">{coll?.distance || 'Cosine'}</Descriptions.Item>
<Descriptions.Item label="向量总数">{count?.count ?? 0}</Descriptions.Item>
<Descriptions.Item label="索引算法">HNSW (m=16, ef_construct=100)</Descriptions.Item>
<Descriptions.Item label="Payload 索引">userId (keyword), knowledgeBaseId (keyword), deleted (bool)</Descriptions.Item>
</Descriptions>
</Card>
</Col>
<Col span={8}>
<Card size="small" title="索引操作">
<Button icon={<NodeIndexOutlined />} block onClick={handleReindex} style={{ marginBottom: 8 }}>
</Button>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
线
</Typography.Text>
</Card>
</Col>
</Row>
</div>
)
}