'use client'; import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Container, Typography, Box, Paper, Tabs, Tab, CircularProgress, Divider, LinearProgress } from '@mui/material'; import QuestionListView from '@/components/questions/QuestionListView'; import QuestionTreeView from '@/components/questions/QuestionTreeView'; import TabPanel from '@/components/text-split/components/TabPanel'; import useTaskSettings from '@/hooks/useTaskSettings'; import QuestionEditDialog from './components/QuestionEditDialog'; import QuestionsPageHeader from './components/QuestionsPageHeader'; import ConfirmDialog from './components/ConfirmDialog'; import TemplateListView from './components/TemplateListView'; import TemplateFormDialog from './components/template/TemplateFormDialog'; import ExportQuestionsDialog from './components/ExportQuestionsDialog'; import { useQuestionTemplates } from './hooks/useQuestionTemplates'; import { useQuestionEdit } from './hooks/useQuestionEdit'; import { useQuestionDelete } from './hooks/useQuestionDelete'; import { useQuestionsFilter } from './hooks/useQuestionsFilter'; import QuestionsFilter from './components/QuestionsFilter'; import { useQuestionGeneration } from './hooks/useQuestionGeneration'; import useQuestionExport from './hooks/useQuestionExport'; import axios from 'axios'; import { toast } from 'sonner'; import { useAtomValue } from 'jotai/index'; import { selectedModelInfoAtom } from '@/lib/store'; export default function QuestionsPage({ params }) { const { t } = useTranslation(); const { projectId } = params; const [loading, setLoading] = useState(true); const [questions, setQuestions] = useState({}); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); const [tags, setTags] = useState([]); const model = useAtomValue(selectedModelInfoAtom); const [activeTab, setActiveTab] = useState(0); // 模板管理 const { templates, loading: templatesLoading, createTemplate, updateTemplate, deleteTemplate } = useQuestionTemplates(projectId, null); // null 表示获取所有类型的模板 const [templateDialogOpen, setTemplateDialogOpen] = useState(false); const [editingTemplate, setEditingTemplate] = useState(null); const [exportDialogOpen, setExportDialogOpen] = useState(false); // 使用新的过滤和搜索 Hook const { answerFilter, searchTerm, debouncedSearchTerm, searchMatchMode, chunkNameFilter, debouncedChunkNameFilter, sourceTypeFilter, selectedQuestions, setSelectedQuestions, handleSelectQuestion, handleSelectAll, handleSearchChange, handleFilterChange, handleChunkNameFilterChange, handleSourceTypeFilterChange, handleSearchMatchModeChange } = useQuestionsFilter(projectId); const getQuestionList = async () => { try { // 获取问题列表 const questionsResponse = await axios.get( `/api/projects/${projectId}/questions?page=${currentPage}&size=10&status=${answerFilter}&input=${searchTerm}&searchMatchMode=${searchMatchMode}&chunkName=${encodeURIComponent(debouncedChunkNameFilter)}&sourceType=${sourceTypeFilter}` ); if (questionsResponse.status !== 200) { throw new Error(t('common.fetchError')); } setQuestions(questionsResponse.data || {}); // 获取标签树 const tagsResponse = await axios.get(`/api/projects/${projectId}/tags`); if (tagsResponse.status !== 200) { throw new Error(t('common.fetchError')); } setTags(tagsResponse.data.tags || []); setLoading(false); } catch (error) { console.error(t('common.fetchError'), error); toast.error(error.message); } }; // 当筛选条件改变时,重置页码到第1页 useEffect(() => { setCurrentPage(1); }, [answerFilter, debouncedSearchTerm, debouncedChunkNameFilter, sourceTypeFilter, searchMatchMode]); useEffect(() => { getQuestionList(); }, [currentPage, answerFilter, debouncedSearchTerm, debouncedChunkNameFilter, sourceTypeFilter, searchMatchMode]); const { taskSettings } = useTaskSettings(projectId); // 使用新的问题生成 Hook const { processing, progress, handleBatchGenerateAnswers, handleAutoGenerateDatasets, handleAutoGenerateMultiTurnDatasets, handleAutoGenerateImageDatasets } = useQuestionGeneration(projectId, model, taskSettings, getQuestionList); const { editDialogOpen, editMode, editingQuestion, handleOpenCreateDialog, handleOpenEditDialog, handleCloseDialog, handleSubmitQuestion } = useQuestionEdit(projectId, updatedQuestion => { getQuestionList(); toast.success(t('questions.operationSuccess')); }); const { confirmDialog, handleDeleteQuestion, handleBatchDeleteQuestions, closeConfirmDialog, handleConfirmAction } = useQuestionDelete(projectId, () => { getQuestionList(); }); const { exportQuestions } = useQuestionExport(projectId); // 获取所有数据 useEffect(() => { getQuestionList(); }, [projectId]); // 处理标签页切换 const handleTabChange = (event, newValue) => { setActiveTab(newValue); }; // 模板管理函数 const handleOpenCreateTemplateDialog = () => { setEditingTemplate(null); setTemplateDialogOpen(true); }; const handleEditTemplate = template => { setEditingTemplate(template); setTemplateDialogOpen(true); }; const handleCloseTemplateDialog = () => { setTemplateDialogOpen(false); setEditingTemplate(null); }; const handleSubmitTemplate = async data => { try { if (editingTemplate) { await updateTemplate(editingTemplate.id, data); } else { await createTemplate(data); } getQuestionList(); handleCloseTemplateDialog(); } catch (error) { console.error('Failed to save template:', error); } }; const handleDeleteTemplate = async templateId => { const confirmed = window.confirm(t('questions.template.deleteConfirm')); if (confirmed) { try { await deleteTemplate(templateId); } catch (error) { console.error('Failed to delete template:', error); } } }; const handleOpenExportDialog = () => { setExportDialogOpen(true); }; const handleCloseExportDialog = () => { setExportDialogOpen(false); }; const handleExportQuestions = async exportOptions => { const options = { ...exportOptions, selectedIds: selectedQuestions, filters: { searchTerm: debouncedSearchTerm, chunkName: debouncedChunkNameFilter, sourceType: sourceTypeFilter } }; await exportQuestions(options); }; if (loading) { return ( ); } return ( {/* 处理中的进度显示 - 全局蒙版样式 */} {processing && ( {t('datasets.generatingDataset')} {progress.percentage}% {t('questions.generatingProgress', { completed: progress.completed, total: progress.total })} {t('questions.generatedCount', { count: progress.datasetCount })} {t('questions.pleaseWait')} )} handleBatchDeleteQuestions(selectedQuestions, setSelectedQuestions)} onOpenCreateDialog={handleOpenCreateDialog} onOpenCreateTemplateDialog={handleOpenCreateTemplateDialog} onBatchGenerateAnswers={() => handleBatchGenerateAnswers(selectedQuestions)} onAutoGenerateDatasets={handleAutoGenerateDatasets} onAutoGenerateMultiTurnDatasets={handleAutoGenerateMultiTurnDatasets} onAutoGenerateImageDatasets={handleAutoGenerateImageDatasets} onExportQuestions={handleOpenExportDialog} /> 0 && selectedQuestions.length === questions?.total} isIndeterminate={selectedQuestions.length > 0 && selectedQuestions.length < questions?.total} onSelectAll={handleSelectAll} searchTerm={searchTerm} onSearchChange={handleSearchChange} searchMatchMode={searchMatchMode} onSearchMatchModeChange={handleSearchMatchModeChange} answerFilter={answerFilter} onFilterChange={handleFilterChange} chunkNameFilter={chunkNameFilter} onChunkNameFilterChange={handleChunkNameFilterChange} sourceTypeFilter={sourceTypeFilter} onSourceTypeFilterChange={handleSourceTypeFilterChange} activeTab={activeTab} /> setCurrentPage(newPage)} selectedQuestions={selectedQuestions} onSelectQuestion={handleSelectQuestion} onDeleteQuestion={questionId => handleDeleteQuestion(questionId, selectedQuestions, setSelectedQuestions)} onEditQuestion={handleOpenEditDialog} refreshQuestions={getQuestionList} projectId={projectId} /> handleDeleteQuestion(questionId, selectedQuestions, setSelectedQuestions)} onEditQuestion={handleOpenEditDialog} projectId={projectId} searchTerm={searchTerm} /> {/* 确认对话框 */} ); }