diff --git a/src/components/admin/ExamComponents/ExamSettingsModal.vue b/src/components/admin/ExamComponents/ExamSettingsModal.vue
index 564ad66..14fb494 100644
--- a/src/components/admin/ExamComponents/ExamSettingsModal.vue
+++ b/src/components/admin/ExamComponents/ExamSettingsModal.vue
@@ -289,7 +289,8 @@ import {
NCheckbox,
NButton,
NDivider,
- NSwitch
+ NSwitch,
+ NFlex
} from 'naive-ui';
// 创建独立的 message API
diff --git a/src/components/admin/ExamComponents/QuestionBankModal.vue b/src/components/admin/ExamComponents/QuestionBankModal.vue
index 72a466d..dd0feb3 100644
--- a/src/components/admin/ExamComponents/QuestionBankModal.vue
+++ b/src/components/admin/ExamComponents/QuestionBankModal.vue
@@ -33,7 +33,7 @@
- {{ selectedRepo ? `已加载题库题目,共${pagination.itemCount}试题` : '请先选择题库' }}
+ {{ selectedRepo ? `已加载题库题目,共${paginationItemCount}试题` : '请先选择题库' }}
@@ -93,8 +93,8 @@
-
@@ -119,6 +119,14 @@
+
+
+
@@ -248,7 +256,7 @@ const filteredQuestions = computed(() => {
// 按分类筛选
if (filters.value.category) {
- filtered = filtered.filter(q => q.category === filters.value.category);
+ filtered = filtered.filter((q: QuestionItem) => q.category === filters.value.category);
}
// 按难度筛选
@@ -259,7 +267,7 @@ const filteredQuestions = computed(() => {
'3': '困难'
};
const targetDifficulty = difficultyMap[filters.value.difficulty];
- filtered = filtered.filter(q => q.difficulty === targetDifficulty);
+ filtered = filtered.filter((q: QuestionItem) => q.difficulty === targetDifficulty);
}
// 按题型筛选
@@ -273,13 +281,13 @@ const filteredQuestions = computed(() => {
'5': '复合题'
};
const targetType = typeMap[filters.value.type];
- filtered = filtered.filter(q => q.type === targetType);
+ filtered = filtered.filter((q: QuestionItem) => q.type === targetType);
}
// 按关键词筛选
if (filters.value.keyword) {
const keyword = filters.value.keyword.toLowerCase();
- filtered = filtered.filter(q =>
+ filtered = filtered.filter((q: QuestionItem) =>
q.title.toLowerCase().includes(keyword) ||
q.creator.toLowerCase().includes(keyword)
);
@@ -369,60 +377,54 @@ const columns = [
}
];
-// 分页配置
-const pagination = ref({
- page: 1,
- pageSize: 10,
- showSizePicker: true,
- pageSizes: [10, 20, 50],
- itemCount: 0,
- showQuickJumper: true,
- displayOrder: ['size-picker', 'pages', 'quick-jumper'],
- onChange: (page: number) => {
- pagination.value.page = page;
- updateCurrentPageQuestions();
- },
- onUpdatePageSize: (pageSize: number) => {
- pagination.value.pageSize = pageSize;
- pagination.value.page = 1;
- updateCurrentPageQuestions();
- },
- prefix: ({ itemCount }: { itemCount: number }) => `共${itemCount}题`
-});
+// 分页器状态
+const paginationPage = ref(1);
+const paginationPageSize = ref(10);
+const paginationItemCount = ref(0);
// 处理复选框选择
const handleCheck = (rowKeys: string[]) => {
selectedRowKeys.value = rowKeys;
};
+// 分页器事件处理
+const handlePageChange = (page: number) => {
+ paginationPage.value = page;
+ updateCurrentPageQuestions();
+};
+
+const handlePageSizeChange = (pageSize: number) => {
+ paginationPageSize.value = pageSize;
+ paginationPage.value = 1;
+ updateCurrentPageQuestions();
+};
+
// 更新当前页的题目
const updateCurrentPageQuestions = () => {
const filtered = filteredQuestions.value;
- const startIndex = (pagination.value.page - 1) * pagination.value.pageSize;
- const endIndex = startIndex + pagination.value.pageSize;
+ const startIndex = (paginationPage.value - 1) * paginationPageSize.value;
+ const endIndex = startIndex + paginationPageSize.value;
questionList.value = filtered.slice(startIndex, endIndex);
- console.log('📄 更新当前页题目 - 页码:', pagination.value.page, '每页:', pagination.value.pageSize, '显示题目数:', questionList.value.length);
+
+ // 更新分页器的总数
+ paginationItemCount.value = filtered.length;
+
+ // 加载当前页题目的详细信息
+ loadCurrentPageDetails();
};
// 处理题库选择变化
const handleRepoChange = (repoId: string) => {
selectedRepo.value = repoId;
filters.value.repoId = repoId;
+ paginationPage.value = 1; // 重置到第一页
loadQuestions();
};
// 筛选条件变化处理
const handleFilterChange = () => {
- const filtered = filteredQuestions.value;
- pagination.value.itemCount = filtered.length;
- pagination.value.page = 1; // 重置到第一页
-
- // 计算当前页的题目
- const startIndex = (pagination.value.page - 1) * pagination.value.pageSize;
- const endIndex = startIndex + pagination.value.pageSize;
- questionList.value = filtered.slice(startIndex, endIndex);
-
- console.log('📊 筛选后分页器更新 - 题目总数:', pagination.value.itemCount);
+ paginationPage.value = 1; // 重置到第一页
+ updateCurrentPageQuestions();
};
// 重置筛选条件
@@ -450,7 +452,6 @@ const loadCategories = async () => {
value: category.name
}))
];
- console.log('✅ 加载分类选项成功:', categoryOptions.value);
}
} catch (error) {
console.error('加载分类选项失败:', error);
@@ -476,7 +477,6 @@ const loadDifficulties = async () => {
value: difficulty.id
}))
];
- console.log('✅ 加载难度选项成功:', difficultyOptions.value);
}
} catch (error) {
console.error('加载难度选项失败:', error);
@@ -496,12 +496,10 @@ const loadRepos = async () => {
const response = await ExamApi.getCourseRepoList();
if (response.data && response.data.result) {
repoList.value = response.data.result;
- console.log('✅ 加载题库列表成功:', repoList.value);
// 如果还没有选择题库,自动选择第一个题库
if (!selectedRepo.value && repoList.value.length > 0) {
selectedRepo.value = repoList.value[0].id;
- console.log('🔄 自动选择第一个题库:', selectedRepo.value);
// 自动加载该题库的题目
await loadQuestions();
}
@@ -518,12 +516,11 @@ const loadQuestions = async () => {
try {
if (selectedRepo.value) {
// 根据选择的题库加载题目
- console.log('🔍 正在加载题库题目:', selectedRepo.value);
const response = await ExamApi.getQuestionsByRepo(selectedRepo.value);
- console.log('✅ 题库题目响应:', response);
if (response.data && response.data.result && response.data.result.length > 0) {
- const questions = response.data.result.map((q: Question, index: number) => ({
+ // 优化:先处理基础题目信息,不立即获取选项和答案
+ const questionsWithBasicInfo = response.data.result.map((q: Question, index: number) => ({
id: q.id,
number: index + 1,
title: q.content || '无标题',
@@ -533,43 +530,133 @@ const loadQuestions = async () => {
score: q.score || 5,
creator: q.createBy || '未知',
createTime: q.createTime ? new Date(q.createTime).toLocaleString() : '未知时间',
- originalData: q
+ originalData: {
+ ...q,
+ options: [], // 初始化为空数组
+ answers: [] // 初始化为空数组
+ }
}));
- allQuestions.value = questions;
+
+ allQuestions.value = questionsWithBasicInfo;
// 应用筛选条件并更新当前页
- const filtered = filteredQuestions.value;
- pagination.value.itemCount = filtered.length;
+ updateCurrentPageQuestions();
- // 计算当前页的题目
- const startIndex = (pagination.value.page - 1) * pagination.value.pageSize;
- const endIndex = startIndex + pagination.value.pageSize;
- questionList.value = filtered.slice(startIndex, endIndex);
+ // 优化:延迟加载选项和答案数据,避免阻塞UI
+ // 只对当前页的题目加载详细数据
+ loadCurrentPageDetails();
- console.log('✅ 题目列表加载成功:', questionList.value);
- console.log('📊 筛选后题目总数:', filtered.length);
} else {
// 如果题库没有题目,显示空列表
allQuestions.value = [];
questionList.value = [];
- console.log('⚠️ 该题库暂无题目');
+ paginationItemCount.value = 0; // 重置分页器总数
}
} else {
// 如果没有选择题库,显示提示信息
+ allQuestions.value = [];
questionList.value = [];
- console.log('ℹ️ 请先选择题库');
+ paginationItemCount.value = 0; // 重置分页器总数
}
- console.log('📊 分页器更新 - 题目总数:', pagination.value.itemCount);
- } catch (error) {
- console.error('加载题目失败:', error);
- message.error('加载题目失败');
- questionList.value = [];
+ } catch (error: any) {
+ // 检查是否是"没有题目"的错误
+ if (error?.message?.includes('该题库下没有题目') || error?.message?.includes('该题库不存在')) {
+ // 没有题目不是错误,正常处理
+ allQuestions.value = [];
+ questionList.value = [];
+ paginationItemCount.value = 0;
+ } else {
+ // 真正的错误才显示错误信息
+ console.error('加载题目失败:', error);
+ message.error('加载题目失败');
+ allQuestions.value = [];
+ questionList.value = [];
+ paginationItemCount.value = 0;
+ }
} finally {
loading.value = false;
}
};
+// 新增:加载当前页题目的详细信息(选项和答案)
+const loadCurrentPageDetails = async () => {
+ if (!questionList.value || questionList.value.length === 0) return;
+
+ try {
+ // 只对当前页的题目加载选项和答案数据
+ const currentPageQuestions = questionList.value;
+
+ // 使用 Promise.allSettled 避免单个请求失败影响整体
+ const detailPromises = currentPageQuestions.map(async (question: QuestionItem) => {
+ const q = question.originalData;
+ if (!q || !q.id) return question;
+
+ let options: any[] = [];
+ let answers: any[] = [];
+
+ // 如果是选择题或判断题,获取选项数据
+ if (q.type === 0 || q.type === 1 || q.type === 2) {
+ try {
+ const optionsResponse = await ExamApi.getQuestionOptions(q.id);
+ options = processApiResponse(optionsResponse.data);
+ } catch (error) {
+ console.warn(`获取题目 ${q.id} 选项失败:`, error);
+ }
+ }
+
+ // 如果是填空题或简答题,获取答案数据
+ if (q.type === 3 || q.type === 4) {
+ try {
+ const answersResponse = await ExamApi.getQuestionAnswers(q.id);
+ answers = processApiResponse(answersResponse.data);
+ } catch (error) {
+ console.warn(`获取题目 ${q.id} 答案失败:`, error);
+ }
+ }
+
+ // 更新题目数据
+ question.originalData = {
+ ...q,
+ options: options,
+ answers: answers
+ } as Question;
+
+ return question;
+ });
+
+ // 等待所有详情加载完成
+ await Promise.allSettled(detailPromises);
+
+ // 触发响应式更新
+ allQuestions.value = [...allQuestions.value];
+
+ } catch (error) {
+ console.warn('加载题目详情失败:', error);
+ }
+};
+
+// 新增:处理API响应的通用方法
+const processApiResponse = (data: any): any[] => {
+ if (!data) return [];
+
+ if (Array.isArray(data)) {
+ return data;
+ } else if (data && Array.isArray(data.result)) {
+ return data.result;
+ } else if (data && data.success && data.result) {
+ if (Array.isArray(data.result)) {
+ return data.result;
+ } else if (data.result && data.result.records) {
+ return data.result.records || [];
+ } else {
+ return [data.result];
+ }
+ }
+
+ return [];
+};
+
// 导入试题
const addNewQuestion = () => {
// 创建文件输入元素
@@ -685,7 +772,7 @@ const exportQuestions = async () => {
link.href = url;
// 获取题库名称作为文件名
- const repo = repoList.value.find(r => r.id === selectedRepo.value);
+ const repo = repoList.value.find((r: Repo) => r.id === selectedRepo.value);
const fileName = repo ? `${repo.title}_题目模板.xlsx` : '题目模板.xlsx';
link.download = fileName;
@@ -765,8 +852,8 @@ onMounted(() => {
display: flex;
flex-direction: column;
gap: 16px;
- max-height: 60vh;
- overflow: hidden;
+ max-height: 80vh;
+ overflow-y: auto;
}
.filter-section {
@@ -807,7 +894,9 @@ onMounted(() => {
.question-list-section {
flex: 1;
- overflow: hidden;
+ overflow: visible;
+ display: flex;
+ flex-direction: column;
}
.selected-info {
@@ -817,6 +906,11 @@ onMounted(() => {
font-size: 14px;
}
+.pagination-wrapper {
+ margin-top: 16px;
+ display: flex;
+ justify-content: flex-end;
+}
.modal-actions {
display: flex;
justify-content: flex-end;
diff --git a/src/components/teacher/FillBlankQuestion.vue b/src/components/teacher/FillBlankQuestion.vue
index 251e3d0..12bb3ca 100644
--- a/src/components/teacher/FillBlankQuestion.vue
+++ b/src/components/teacher/FillBlankQuestion.vue
@@ -84,7 +84,7 @@