fix: 修复题目管理页面的 TypeScript 类型错误
This commit is contained in:
parent
3d5d34b660
commit
e1b32c2c3c
@ -500,7 +500,7 @@ const createNewQuestion = async (bankId: string) => {
|
||||
try {
|
||||
// 只调用一次API创建题目
|
||||
const questionData = {
|
||||
parentId: null, // 父题目ID,普通题目为null
|
||||
parentId: undefined, // 父题目ID,普通题目为undefined
|
||||
type: getQuestionTypeNumber(questionForm.type),
|
||||
content: questionForm.title,
|
||||
analysis: questionForm.explanation || '',
|
||||
@ -547,43 +547,6 @@ const createNewQuestion = async (bankId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 处理单选题的选项和答案
|
||||
const handleSingleChoiceQuestion = async (questionId: string) => {
|
||||
try {
|
||||
// 添加选项
|
||||
const optionPromises = questionForm.options.map((option, index) => {
|
||||
const isCorrect = questionForm.correctAnswer === index ? 1 : 0;
|
||||
return ExamApi.createQuestionOption({
|
||||
questionId,
|
||||
content: option.content,
|
||||
izCorrent: isCorrect,
|
||||
orderNo: index + 1
|
||||
});
|
||||
});
|
||||
|
||||
console.log('🚀 第二步:添加选项,选项数量:', questionForm.options.length);
|
||||
await Promise.all(optionPromises);
|
||||
console.log('✅ 选项添加成功');
|
||||
|
||||
// 添加正确答案
|
||||
if (questionForm.correctAnswer !== null) {
|
||||
const correctOption = questionForm.options[questionForm.correctAnswer];
|
||||
const answerData = {
|
||||
questionId,
|
||||
answerText: correctOption.content,
|
||||
orderNo: 1
|
||||
};
|
||||
|
||||
console.log('🚀 第三步:添加答案:', answerData);
|
||||
await ExamApi.createQuestionAnswer(answerData);
|
||||
console.log('✅ 答案添加成功');
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('处理单选题失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 验证答案设置
|
||||
const validateAnswers = (): boolean => {
|
||||
@ -600,7 +563,7 @@ const validateAnswers = (): boolean => {
|
||||
message.error('单选题至少需要2个选项');
|
||||
return false;
|
||||
}
|
||||
if (questionForm.options.some(option => !option.content.trim())) {
|
||||
if (questionForm.options.some((option: any) => !option.content.trim())) {
|
||||
message.error('请填写所有选项的内容');
|
||||
return false;
|
||||
}
|
||||
@ -615,7 +578,7 @@ const validateAnswers = (): boolean => {
|
||||
message.error('多选题至少需要2个选项');
|
||||
return false;
|
||||
}
|
||||
if (questionForm.options.some(option => !option.content.trim())) {
|
||||
if (questionForm.options.some((option: any) => !option.content.trim())) {
|
||||
message.error('请填写所有选项的内容');
|
||||
return false;
|
||||
}
|
||||
@ -631,7 +594,7 @@ const validateAnswers = (): boolean => {
|
||||
}
|
||||
break;
|
||||
case 'fill_blank':
|
||||
if (questionForm.fillBlankAnswers.length === 0 || questionForm.fillBlankAnswers.every(answer => !answer.content.trim())) {
|
||||
if (questionForm.fillBlankAnswers.length === 0 || questionForm.fillBlankAnswers.every((answer: any) => !answer.content.trim())) {
|
||||
message.error('请设置填空题的参考答案');
|
||||
return false;
|
||||
}
|
||||
|
@ -3,94 +3,61 @@
|
||||
<div class="breadcrumb-section">
|
||||
<n-breadcrumb>
|
||||
<n-breadcrumb-item @click="goToQuestionBank">
|
||||
<n-icon><ChevronBackOutline /></n-icon>
|
||||
<n-icon>
|
||||
<ChevronBackOutline />
|
||||
</n-icon>
|
||||
题库管理
|
||||
</n-breadcrumb-item>
|
||||
<n-breadcrumb-item>{{ currentBankTitle }}</n-breadcrumb-item>
|
||||
</n-breadcrumb>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="header-section">
|
||||
<h1 class="title">{{ currentBankTitle }}</h1>
|
||||
<n-space class="actions-group">
|
||||
<n-button type="primary" @click="addQuestion">添加试题</n-button>
|
||||
<n-button ghost @click="importQuestions">导入</n-button>
|
||||
<n-button ghost @click="exportQuestions">导出</n-button>
|
||||
<n-button type="error" ghost @click="deleteSelected" :disabled="selectedRowKeys.length === 0">删除</n-button>
|
||||
<n-button type="error" ghost @click="deleteSelected"
|
||||
:disabled="selectedRowKeys.length === 0">删除</n-button>
|
||||
<n-button @click="setCategoryForSelected" :disabled="selectedRowKeys.length === 0">分类设置</n-button>
|
||||
<n-select
|
||||
v-model:value="filters.category"
|
||||
placeholder="分类"
|
||||
:options="[{ label: '全部', value: '' }, ...allCategoryOptions]"
|
||||
style="width: 120px"
|
||||
@update:value="handleFilterChange"
|
||||
/>
|
||||
<n-input
|
||||
v-model:value="filters.keyword"
|
||||
placeholder="请输入想要搜索的内容"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
/>
|
||||
<n-select v-model:value="filters.category" placeholder="分类"
|
||||
:options="[{ label: '全部', value: '' }, ...allCategoryOptions]" style="width: 120px"
|
||||
@update:value="handleFilterChange" />
|
||||
<n-input v-model:value="filters.keyword" placeholder="请输入想要搜索的内容" style="width: 200px" clearable />
|
||||
<n-button type="primary" @click="searchQuestions">搜索</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
|
||||
<n-data-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:data="questionList"
|
||||
:loading="loading"
|
||||
:pagination="paginationConfig"
|
||||
:row-key="(row: Question) => row.id"
|
||||
:checked-row-keys="selectedRowKeys"
|
||||
@update:checked-row-keys="handleCheck"
|
||||
class="question-table"
|
||||
:single-line="false"
|
||||
/>
|
||||
<n-data-table ref="tableRef" :columns="columns" :data="questionList" :loading="loading"
|
||||
:pagination="paginationConfig" :row-key="(row: any) => row.id" :checked-row-keys="selectedRowKeys"
|
||||
@update:checked-row-keys="handleCheck" class="question-table" :single-line="false" />
|
||||
|
||||
<!-- 导入弹窗 -->
|
||||
<ImportModal
|
||||
v-model:show="showImportModal"
|
||||
template-name="question_template.xlsx"
|
||||
import-type="question"
|
||||
@success="handleImportSuccess"
|
||||
@template-download="handleTemplateDownload"
|
||||
/>
|
||||
<ImportModal v-model:show="showImportModal" template-name="question_template.xlsx" import-type="question"
|
||||
@success="handleImportSuccess" @template-download="handleTemplateDownload" />
|
||||
|
||||
<!-- 分类设置弹窗 -->
|
||||
<n-modal
|
||||
v-model:show="showCategoryModal"
|
||||
preset="dialog"
|
||||
title="分类设置"
|
||||
style="width: 500px;"
|
||||
>
|
||||
<n-modal v-model:show="showCategoryModal" preset="dialog" title="分类设置" style="width: 500px;">
|
||||
<div class="category-modal-content">
|
||||
<div class="selected-info">
|
||||
<n-alert type="info" :show-icon="false" style="margin-bottom: 16px;">
|
||||
已选择 {{ selectedRowKeys.length }} 个试题
|
||||
</n-alert>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="category-selection">
|
||||
<div class="form-item">
|
||||
<label>选择分类:</label>
|
||||
<n-select
|
||||
v-model:value="selectedCategory"
|
||||
:options="allCategoryOptions"
|
||||
placeholder="请选择分类"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
<n-select v-model:value="selectedCategory" :options="allCategoryOptions" placeholder="请选择分类"
|
||||
style="width: 100%;" />
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="showAddCategoryInput" class="form-item">
|
||||
<label>新分类名称:</label>
|
||||
<n-space>
|
||||
<n-input
|
||||
v-model:value="newCategoryName"
|
||||
placeholder="请输入新分类名称"
|
||||
style="width: 200px;"
|
||||
@keyup.enter="addNewCategory"
|
||||
/>
|
||||
<n-input v-model:value="newCategoryName" placeholder="请输入新分类名称" style="width: 200px;"
|
||||
@keyup.enter="addNewCategory" />
|
||||
<n-button type="primary" @click="addNewCategory" :disabled="!newCategoryName.trim()">
|
||||
添加
|
||||
</n-button>
|
||||
@ -98,7 +65,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="showAddCategoryInput = !showAddCategoryInput" secondary type="info">
|
||||
@ -113,12 +80,7 @@
|
||||
</n-modal>
|
||||
|
||||
<!-- 分类管理弹窗 -->
|
||||
<n-modal
|
||||
v-model:show="showCategoryManageModal"
|
||||
preset="dialog"
|
||||
title="分类管理"
|
||||
style="width: 600px;"
|
||||
>
|
||||
<n-modal v-model:show="showCategoryManageModal" preset="dialog" title="分类管理" style="width: 600px;">
|
||||
<div class="category-manage-content">
|
||||
<div class="category-header">
|
||||
<n-space>
|
||||
@ -127,15 +89,11 @@
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="showAddCategoryInManage" class="add-category-section">
|
||||
<n-space>
|
||||
<n-input
|
||||
v-model:value="newCategoryInManage"
|
||||
placeholder="请输入分类名称"
|
||||
style="width: 200px;"
|
||||
@keyup.enter="addCategoryInManage"
|
||||
/>
|
||||
<n-input v-model:value="newCategoryInManage" placeholder="请输入分类名称" style="width: 200px;"
|
||||
@keyup.enter="addCategoryInManage" />
|
||||
<n-button type="primary" @click="addCategoryInManage" :disabled="!newCategoryInManage.trim()">
|
||||
添加
|
||||
</n-button>
|
||||
@ -144,7 +102,7 @@
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="category-list">
|
||||
<n-list>
|
||||
<n-list-item v-for="category in customCategories" :key="category.value">
|
||||
@ -163,7 +121,7 @@
|
||||
</n-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<template #action>
|
||||
<n-space>
|
||||
<n-button @click="closeCategoryManageModal">关闭</n-button>
|
||||
@ -210,6 +168,8 @@ interface Question {
|
||||
score: number;
|
||||
creator: string;
|
||||
createTime: string;
|
||||
parentId?: string; // 添加可选的 parentId 字段
|
||||
analysis?: string; // 添加可选的 analysis 字段
|
||||
}
|
||||
|
||||
// 筛选条件
|
||||
@ -333,7 +293,7 @@ const createColumns = ({
|
||||
width: 100,
|
||||
align: 'center' as const,
|
||||
render(row: Question) {
|
||||
const categoryInfo = allCategoryOptions.value.find(cat => cat.value === row.category);
|
||||
const categoryInfo = allCategoryOptions.value.find((cat: any) => cat.value === row.category);
|
||||
return categoryInfo ? categoryInfo.label : row.category;
|
||||
}
|
||||
},
|
||||
@ -377,27 +337,27 @@ const createColumns = ({
|
||||
align: 'center' as const,
|
||||
render(row: Question) {
|
||||
const buttons: VNode[] = [];
|
||||
|
||||
|
||||
buttons.push(
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('编辑', row)
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('编辑', row)
|
||||
}, { default: () => '编辑' })
|
||||
);
|
||||
|
||||
|
||||
buttons.push(
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('删除', row)
|
||||
h(NButton, {
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
ghost: true,
|
||||
style: 'margin: 0 3px;',
|
||||
onClick: () => handleAction('删除', row)
|
||||
}, { default: () => '删除' })
|
||||
);
|
||||
|
||||
|
||||
return h(NSpace, {}, { default: () => buttons });
|
||||
}
|
||||
}
|
||||
@ -420,7 +380,7 @@ const generateMockData = (): Question[] => {
|
||||
const mockData: Question[] = [];
|
||||
const types = ['single_choice', 'multiple_choice', 'true_false', 'fill_blank', 'short_answer'];
|
||||
const difficulties = ['easy', 'medium', 'hard'];
|
||||
const categories = customCategories.value.map(cat => cat.value);
|
||||
const categories = customCategories.value.map((cat: any) => cat.value);
|
||||
const creators = ['王建国', '李明', '张三', '刘老师'];
|
||||
|
||||
for (let i = 1; i <= 50; i++) {
|
||||
@ -492,7 +452,7 @@ const loadQuestions = async () => {
|
||||
let allData: Question[] = [];
|
||||
|
||||
// 处理API响应数据
|
||||
if (response.data && (response.data.code === 200 || response.data.code === 0) && response.data.result) {
|
||||
if (response.data && typeof response.data === 'object' && 'code' in response.data && (response.data.code === 200 || response.data.code === 0) && 'result' in response.data && response.data.result && Array.isArray(response.data.result)) {
|
||||
// 将API返回的数据转换为前端格式
|
||||
allData = response.data.result.map((item: any, index: number) => ({
|
||||
id: item.id || `question_${index}`,
|
||||
@ -746,7 +706,7 @@ const addNewCategory = () => {
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
const exists = customCategories.value.some(cat => cat.label === trimmedName);
|
||||
const exists = customCategories.value.some((cat: any) => cat.label === trimmedName);
|
||||
if (exists) {
|
||||
message.warning('该分类已存在');
|
||||
return;
|
||||
@ -775,8 +735,8 @@ const applyCategoryChange = async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
||||
// 更新本地数据
|
||||
const selectedCategoryLabel = allCategoryOptions.value.find(cat => cat.value === selectedCategory.value)?.label || selectedCategory.value;
|
||||
questionList.value.forEach(question => {
|
||||
const selectedCategoryLabel = allCategoryOptions.value.find((cat: any) => cat.value === selectedCategory.value)?.label || selectedCategory.value;
|
||||
questionList.value.forEach((question: any) => {
|
||||
if (selectedRowKeys.value.includes(question.id)) {
|
||||
question.category = selectedCategory.value;
|
||||
}
|
||||
@ -811,7 +771,7 @@ const addCategoryInManage = () => {
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
const exists = customCategories.value.some(cat => cat.label === trimmedName);
|
||||
const exists = customCategories.value.some((cat: any) => cat.label === trimmedName);
|
||||
if (exists) {
|
||||
message.warning('该分类已存在');
|
||||
return;
|
||||
@ -841,13 +801,13 @@ const editCategory = (category: { label: string; value: string }) => {
|
||||
|
||||
const deleteCategory = (categoryValue: string) => {
|
||||
// 检查是否有试题使用该分类
|
||||
const hasQuestions = questionList.value.some(q => q.category === categoryValue);
|
||||
const hasQuestions = questionList.value.some((q: any) => q.category === categoryValue);
|
||||
if (hasQuestions) {
|
||||
message.warning('该分类下还有试题,不能删除');
|
||||
return;
|
||||
}
|
||||
|
||||
const index = customCategories.value.findIndex(cat => cat.value === categoryValue);
|
||||
const index = customCategories.value.findIndex((cat: any) => cat.value === categoryValue);
|
||||
if (index > -1) {
|
||||
const categoryName = customCategories.value[index].label;
|
||||
customCategories.value.splice(index, 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user