feat(knowledge-testing): add pagination and document filtering to testing interface

This commit is contained in:
2025-10-16 11:48:00 +08:00
parent fd4309582d
commit 4f956e79ba
4 changed files with 537 additions and 281 deletions

View 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;