'use client'; import { useState, useEffect } from 'react'; import { useParams } from 'next/navigation'; import { Box, Container, Grid, Paper, Chip, Typography, CircularProgress, Alert, Card, CardContent, Divider, Stack, RadioGroup, FormControlLabel, Radio, FormGroup, Checkbox as MuiCheckbox } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { useTheme, alpha } from '@mui/material/styles'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import ShortTextIcon from '@mui/icons-material/ShortText'; import NotesIcon from '@mui/icons-material/Notes'; import AccessTimeIcon from '@mui/icons-material/AccessTime'; import TagIcon from '@mui/icons-material/Tag'; import DescriptionIcon from '@mui/icons-material/Description'; import useEvalDatasetDetails from './useEvalDatasetDetails'; import EvalDatasetHeader from '../components/EvalDatasetHeader'; import EvalEditableField from '../components/EvalEditableField'; import TagSelector from '@/components/datasets/TagSelector'; // 题型图标和颜色映射 const QUESTION_TYPE_CONFIG = { true_false: { icon: CheckCircleIcon, color: 'success', bgColor: 'success.light' }, single_choice: { icon: RadioButtonCheckedIcon, color: 'primary', bgColor: 'primary.light' }, multiple_choice: { icon: CheckBoxIcon, color: 'secondary', bgColor: 'secondary.light' }, short_answer: { icon: ShortTextIcon, color: 'warning', bgColor: 'warning.light' }, open_ended: { icon: NotesIcon, color: 'info', bgColor: 'info.light' } }; export default function EvalDatasetDetailPage() { const { projectId, evalId } = useParams(); const { t } = useTranslation(); const theme = useTheme(); const [availableTags, setAvailableTags] = useState([]); const { data, loading, error, handleNavigate, handleSave, handleDelete } = useEvalDatasetDetails(projectId, evalId); // 获取项目中已使用的标签 useEffect(() => { const fetchAvailableTags = async () => { try { const response = await fetch(`/api/projects/${projectId}/eval-datasets/tags`); if (response.ok) { const result = await response.json(); setAvailableTags(result.tags || []); } } catch (error) { console.error('获取可用标签失败:', error); } }; if (projectId && !loading) { fetchAvailableTags(); } }, [projectId, loading]); if (loading) { return ( ); } if (error || !data) { return ( {error || t('eval.notFound')} ); } const typeConfig = QUESTION_TYPE_CONFIG[data.questionType] || QUESTION_TYPE_CONFIG.short_answer; const TypeIcon = typeConfig.icon; // 解析选项 let options = []; try { options = data.options ? (typeof data.options === 'string' ? JSON.parse(data.options) : data.options) : []; } catch (e) { options = []; } // 渲染选项预览 const renderOptionsPreview = value => { let opts = []; try { opts = value ? (typeof value === 'string' ? JSON.parse(value) : value) : []; } catch (e) { return Invalid JSON format; } if (!Array.isArray(opts) || opts.length === 0) { return {t('common.noData')}; } return ( {opts.map((option, index) => { const optionLabel = String.fromCharCode(65 + index); const isCorrect = data.questionType === 'multiple_choice' ? (Array.isArray(data.correctAnswer) ? data.correctAnswer : JSON.parse(data.correctAnswer || '[]') ).includes(optionLabel) : data.correctAnswer === optionLabel; return ( {optionLabel}. {option} {isCorrect && ( )} ); })} ); }; // 渲染答案编辑组件 const renderAnswerEditor = (currentValue, onChange) => { if (data.questionType === 'true_false') { return ( onChange(e.target.value)} row> } label={t('eval.correct')} /> } label={t('eval.wrong')} /> ); } if (data.questionType === 'single_choice') { return ( onChange(e.target.value)}> {options.map((_, index) => { const label = String.fromCharCode(65 + index); return ( } label={`${label}. ${options[index]}`} /> ); })} ); } if (data.questionType === 'multiple_choice') { const selected = Array.isArray(currentValue) ? currentValue : JSON.parse(currentValue || '[]'); const handleChange = label => { const newSelected = selected.includes(label) ? selected.filter(i => i !== label) : [...selected, label].sort(); onChange(JSON.stringify(newSelected)); }; return ( {options.map((_, index) => { const label = String.fromCharCode(65 + index); return ( handleChange(label)} />} label={`${label}. ${options[index]}`} /> ); })} ); } return null; // 简答题和开放题保持默认文本框 }; return ( {/* 左侧主要内容 */} {/* 题型标识 */} } label={t(`eval.questionTypes.${data.questionType}`)} color={typeConfig.color} sx={{ fontWeight: 600, fontSize: '0.9rem', py: 0.5, height: 32 }} /> {new Date(data.createAt).toLocaleString()} {/* 问题 */} handleSave('question', val)} placeholder={t('eval.questionPlaceholder')} /> {/* 选项 (仅选择题) */} {(data.questionType === 'single_choice' || data.questionType === 'multiple_choice') && ( handleSave('options', val)} placeholder={'["Option A", "Option B", ...]'} renderPreview={() => renderOptionsPreview(data.options)} /> )} {/* 答案 */} handleSave('correctAnswer', val)} placeholder={t('eval.answerPlaceholder')} renderEditor={(val, setVal) => renderAnswerEditor(val, setVal)} /> {/* 右侧侧边栏 */} {/* 来源信息 */} {t('eval.sourceChunk')} {data.chunks ? ( <> {data.chunks.content && ( {data.chunks.content} )} ) : ( {t('common.noData')} )} {/* 标签和备注 */} {t('eval.tags')} t.trim()) .filter(Boolean) : [] : [] } onChange={newTags => handleSave('tags', newTags.join(', '))} availableTags={availableTags} placeholder={t('eval.tagsPlaceholder')} /> {t('eval.note')} } value={data.note} onSave={val => handleSave('note', val)} placeholder={t('eval.notePlaceholder')} /> ); }