feat(knowledge-testing): add pagination and document filtering to testing interface
This commit is contained in:
249
src/pages/knowledge/components/TestChunkResult.tsx
Normal file
249
src/pages/knowledge/components/TestChunkResult.tsx
Normal file
@@ -0,0 +1,249 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
Typography,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Grid,
|
||||
Card,
|
||||
CardContent,
|
||||
Chip,
|
||||
Stack,
|
||||
Checkbox,
|
||||
ListItemText,
|
||||
OutlinedInput,
|
||||
} from '@mui/material';
|
||||
import type { SelectChangeEvent } from '@mui/material/Select';
|
||||
import { FilterList as FilterListIcon } from '@mui/icons-material';
|
||||
import type { INextTestingResult, ITestingDocument } from '@/interfaces/database/knowledge';
|
||||
|
||||
interface TestChunkResultProps {
|
||||
result: INextTestingResult | null;
|
||||
loading: boolean;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
onDocumentFilter: (docIds: string[]) => void;
|
||||
selectedDocIds: string[];
|
||||
}
|
||||
|
||||
function TestChunkResult({ result, page, pageSize, onDocumentFilter, selectedDocIds }: TestChunkResultProps) {
|
||||
if (!result) {
|
||||
return (
|
||||
<Paper sx={{ p: 3, textAlign: 'center' }}>
|
||||
<Typography variant="h6" color="text.secondary">
|
||||
请输入问题并点击"开始测试"来查看检索结果
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
// 计算分页数据 - 使用服务端分页,直接显示当前页数据
|
||||
const chunks = result.chunks || [];
|
||||
const totalPages = Math.ceil(result.total / pageSize);
|
||||
|
||||
const handleDocumentFilterChange = (event: SelectChangeEvent<string[]>) => {
|
||||
const value = event.target.value as string[];
|
||||
onDocumentFilter(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* 测试结果概览 */}
|
||||
<Paper sx={{ p: 3, mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
测试结果概览
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={{ xs: 12, sm: 4 }}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h4" color="primary">
|
||||
{result.total}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
匹配的文档块
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4 }}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h4" color="secondary">
|
||||
{result.doc_aggs?.length || 0}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
相关文档
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4 }}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h4" color="success.main">
|
||||
{result.chunks?.length || 0}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
返回的块数
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
||||
{/* 文档过滤器 */}
|
||||
{result.doc_aggs && result.doc_aggs.length > 0 && (
|
||||
<Paper sx={{ p: 3, mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<FilterListIcon />
|
||||
文档过滤
|
||||
</Typography>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>选择要显示的文档</InputLabel>
|
||||
<Select
|
||||
multiple
|
||||
value={selectedDocIds}
|
||||
onChange={handleDocumentFilterChange}
|
||||
input={<OutlinedInput label="选择要显示的文档" />}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{selected.map((value) => {
|
||||
const doc = result.doc_aggs?.find(d => d.doc_id === value);
|
||||
return (
|
||||
<Chip key={value} label={doc?.doc_name || value} size="small" />
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
>
|
||||
{result.doc_aggs.map((doc) => (
|
||||
<MenuItem key={doc.doc_id} value={doc.doc_id}>
|
||||
<Checkbox checked={selectedDocIds.indexOf(doc.doc_id) > -1} />
|
||||
<ListItemText
|
||||
primary={doc.doc_name}
|
||||
secondary={`${doc.count} 个匹配块`}
|
||||
/>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
{/* 匹配的文档块 */}
|
||||
{chunks && chunks.length > 0 && (
|
||||
<Paper sx={{ p: 3, mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="h6">
|
||||
匹配的文档块 (第 {page} 页,共 {totalPages} 页)
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
共找到 {result.total} 个匹配块
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Grid container spacing={2}>
|
||||
{chunks.map((chunk, index) => (
|
||||
<Grid size={12} key={chunk.chunk_id}>
|
||||
<Card variant="outlined">
|
||||
<CardContent>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
|
||||
<Typography variant="subtitle1" fontWeight="bold">
|
||||
{chunk.doc_name}
|
||||
</Typography>
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Chip
|
||||
label={`相似度: ${(chunk.similarity * 100).toFixed(1)}%`}
|
||||
size="small"
|
||||
color="primary"
|
||||
/>
|
||||
{chunk.vector_similarity !== undefined && (
|
||||
<Chip
|
||||
label={`向量: ${(chunk.vector_similarity * 100).toFixed(1)}%`}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
{chunk.term_similarity !== undefined && (
|
||||
<Chip
|
||||
label={`词项: ${(chunk.term_similarity * 100).toFixed(1)}%`}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
mb: 2,
|
||||
'& mark': {
|
||||
backgroundColor: 'yellow',
|
||||
padding: '2px 4px',
|
||||
borderRadius: '4px',
|
||||
}
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: chunk.content_with_weight || chunk.content_ltks || '无内容'
|
||||
}}
|
||||
/>
|
||||
|
||||
{chunk.important_kwd && chunk.important_kwd.length > 0 && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
||||
关键词:
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{chunk.important_kwd.map((keyword, kwdIndex) => (
|
||||
<Chip
|
||||
key={kwdIndex}
|
||||
label={keyword}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
{/* 相关文档统计 */}
|
||||
{result.doc_aggs && result.doc_aggs.length > 0 && (
|
||||
<Paper sx={{ p: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
相关文档统计
|
||||
</Typography>
|
||||
<Stack spacing={1}>
|
||||
{result.doc_aggs.map((doc: ITestingDocument, index: number) => (
|
||||
<Box key={doc.doc_id || index} sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="body2">
|
||||
{doc.doc_name}
|
||||
</Typography>
|
||||
<Chip
|
||||
label={`${doc.count} 个匹配块`}
|
||||
size="small"
|
||||
color="default"
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default TestChunkResult;
|
||||
Reference in New Issue
Block a user