'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}
/>
{/* 确认对话框 */}
);
}