feat:讨论模式页面框架搭建

This commit is contained in:
小张 2025-09-18 02:18:39 +08:00
parent 8de56bd07c
commit b20cc50f44
5 changed files with 309 additions and 51 deletions

View File

@ -16,7 +16,7 @@ import type {
UpdateQuestionAnswerRequest,
CreateQuestionRepoRequest,
UpdateQuestionRepoRequest,
ExamInfo,
ExamInfo
} from '../types'
/**

View File

@ -890,3 +890,65 @@ export interface PaperInfo {
updateBy: string
updateTime: string
}
// 试卷创建请求类型
export interface CreatePaperRequest {
/**
* 01
*/
generateMode?: number;
/**
*
*/
passScore?: number;
/**
* id
*/
repoId?: string;
/**
* 0 1
*/
requireReview?: number;
/**
*
* : {
* "type0_count": 1, "type0_score": 10,
* "type1_count": 20, "type1_score": 20,
* "type2_count": 10, "type2_score": 10,
* "type3_count": 10, "type3_score": 10,
* "type4_count": 5, "type4_score": 5,
* "type5_count": 1, "type5_score": 1
* }
*/
rules?: string;
/**
*
*/
title?: string;
/**
*
*/
totalScore?: number;
[property: string]: any;
}
// 试卷题目关联请求类型
export interface AddPaperQuestionRequest {
/**
*
*/
orderNo?: number;
/**
* id
*/
paperId?: string;
/**
* id
*/
questionId?: string;
/**
*
*/
score?: number;
[property: string]: any;
}

View File

@ -30,20 +30,22 @@
:class="{ 'is-selected': correctAnswer === index }">
<div class="option-content">
<div class="option-left">
<n-radio :checked="correctAnswer === index"
@update:checked="() => handleSetCorrectAnswer(index)" name="correct-answer"
class="correct-radio" />
<input type="radio"
name="correct-answer"
:checked="correctAnswer === index"
@change="() => handleSetCorrectAnswer((index + 1).toString())"
class="correct-radio-input" />
<span class="option-label">{{ String.fromCharCode(65 + index) }}</span>
</div>
<n-input :value="option.content"
@update:value="(value: string) => handleUpdateOption(index, value)" placeholder="请输入内容"
show-count maxlength="200" class="option-input" />
<n-button v-if="modelValue.length > 2" @click="handleRemoveOption(index)" type="error" ghost
size="small" class="delete-option-btn">
删除
</n-button>
<n-input :value="option.content"
@update:value="(value: string) => handleUpdateOption(index, value)" placeholder="请输入内容"
show-count maxlength="200" class="option-input" />
<n-button v-if="modelValue.length > 2" @click="handleRemoveOption(index)" type="error" ghost
size="small" class="delete-option-btn">
删除
</n-button>
</div>
</div>
</div>
<n-button v-if="modelValue.length < 6" @click="handleAddOption" dashed block class="add-option-btn">
+ 添加选项
</n-button>
@ -128,8 +130,25 @@ const handleUpdateOption = (index: number, content: string) => {
};
//
const handleSetCorrectAnswer = (index: number) => {
emit('update:correctAnswer', index);
const handleSetCorrectAnswer = (value: string | null) => {
console.log('🔍 设置正确答案,偏移值:', value, '当前正确答案:', props.correctAnswer);
try {
// 1
const actualIndex = value !== null ? parseInt(value, 10) - 1 : null;
console.log('🔍 实际索引:', actualIndex);
if (actualIndex !== null && !isNaN(actualIndex) && actualIndex >= 0) {
console.log('🔍 当前选项内容:', props.modelValue[actualIndex]?.content);
console.log('🔍 所有选项:', props.modelValue.map((opt, i) => `${String.fromCharCode(65 + i)}: ${opt.content}`));
}
//
emit('update:correctAnswer', actualIndex);
} catch (error) {
console.error('🚨 设置正确答案时出错:', error);
emit('update:correctAnswer', null);
}
};
</script>
@ -236,4 +255,5 @@ const handleSetCorrectAnswer = (index: number) => {
.correct-radio {
color: #52c41a;
}
</style>

View File

@ -139,7 +139,10 @@
<!-- 单选题 -->
<SingleChoiceQuestion v-if="subQuestion.type === 'single_choice'"
v-model="subQuestion.options!" :correctAnswer="subQuestion.correctAnswer || null"
@update:correctAnswer="(val: number | null) => subQuestion.correctAnswer = val"
@update:correctAnswer="(val: number | null) => {
console.log('🎯 AddExam: 更新正确答案', val, '选项内容:', subQuestion.options?.[val as number]?.content);
subQuestion.correctAnswer = val;
}"
v-model:title="subQuestion.title" v-model:explanation="subQuestion.explanation" />
@ -1330,8 +1333,8 @@ const saveExam = async () => {
const apiData = {
title: examForm.title,
generateMode: examForm.type === 1 ? 0 : 1, // 0: , 1:
rules: '', //
repoId: '', // ID
rules: examForm.type === 1 ? '' : generateRandomRules(), //
repoId: examForm.type === 1 ? '' : '', // IDID
totalScore: examForm.totalScore,
passScore: examForm.passScore || Math.floor(examForm.totalScore * 0.6), //
requireReview: examForm.useAIGrading ? 1 : 0 //
@ -1453,6 +1456,36 @@ const convertNumberToQuestionType = (type: number): QuestionType => {
return typeMap[type] || QuestionType.SINGLE_CHOICE;
};
// API
const getDifficultyNumber = (difficulty: string): number => {
const difficultyMap: Record<string, number> = {
'easy': 1, //
'medium': 2, //
'hard': 3 //
};
return difficultyMap[difficulty] || 1;
};
//
const generateRandomRules = (): string => {
//
// {
// "type0_count": 1, //
// "type0_score": 10, //
// "type1_count": 20, //
// "type1_score": 20, //
// "type2_count": 10, //
// "type2_score": 10, //
// "type3_count": 10, //
// "type3_score": 10, //
// "type4_count": 5, //
// "type4_score": 5, //
// "type5_count": 1, //
// "type5_score": 1 //
// }
return '';
};
//
const saveExamQuestions = async (paperId: string) => {
try {
@ -1484,12 +1517,12 @@ const saveExamQuestions = async (paperId: string) => {
try {
//
const questionData = {
repoId: '1958492351656955905', // 使ID
repoId: '', // ID
parentId: '', // ID
type: convertQuestionTypeToNumber(subQuestion.type), //
content: subQuestion.title || '', //
analysis: '', //
difficulty: 1, // 1
analysis: subQuestion.explanation || '', //
difficulty: getDifficultyNumber(subQuestion.difficulty), //
score: subQuestion.score || 0, //
degree: 1, // 1
ability: 1 // 1
@ -1512,39 +1545,182 @@ const saveExamQuestions = async (paperId: string) => {
console.log('✅ 题目创建成功ID:', questionId);
console.log('📊 题目响应详情:', questionResponse);
//
if (subQuestion.options && subQuestion.options.length > 0) {
console.log('📊 题目选项数据:', subQuestion.options);
for (let i = 0; i < subQuestion.options.length; i++) {
const option = subQuestion.options[i];
console.log('📊 处理选项:', option);
//
const questionType = convertQuestionTypeToNumber(subQuestion.type);
const optionData = {
if (questionType === 0 || questionType === 1 || questionType === 2) {
// -
console.log('📊 处理选择题选项,题目类型:', questionType);
if (questionType === 0) {
//
if (subQuestion.options && subQuestion.options.length > 0) {
console.log('📊 单选题选项数据:', subQuestion.options);
for (let i = 0; i < subQuestion.options.length; i++) {
const option = subQuestion.options[i];
const optionData = {
questionId: questionId,
content: option.content || '',
izCorrent: subQuestion.correctAnswer === i ? 1 : 0,
orderNo: i
};
console.log('📝 创建单选题选项:', optionData);
await ExamApi.createQuestionOption(optionData);
}
}
} else if (questionType === 1) {
//
if (subQuestion.options && subQuestion.options.length > 0) {
console.log('📊 多选题选项数据:', subQuestion.options);
for (let i = 0; i < subQuestion.options.length; i++) {
const option = subQuestion.options[i];
const isCorrect = subQuestion.correctAnswers && subQuestion.correctAnswers.includes(i);
const optionData = {
questionId: questionId,
content: option.content || '',
izCorrent: isCorrect ? 1 : 0,
orderNo: i
};
console.log('📝 创建多选题选项:', optionData);
await ExamApi.createQuestionOption(optionData);
}
}
} else if (questionType === 2) {
//
const optionData1 = {
questionId: questionId,
content: (option as any).text || option.content || '',
izCorrent: (option as any).isCorrect ? 1 : 0,
orderNo: i + 1
content: '正确',
izCorrent: subQuestion.trueFalseAnswer === true ? 1 : 0,
orderNo: 0
};
const optionData2 = {
questionId: questionId,
content: '错误',
izCorrent: subQuestion.trueFalseAnswer === false ? 1 : 0,
orderNo: 1
};
console.log('📝 创建判断题选项:', optionData1, optionData2);
await ExamApi.createQuestionOption(optionData1);
await ExamApi.createQuestionOption(optionData2);
}
} else if (questionType === 3 || questionType === 4) {
// -
console.log('📊 处理主观题答案,题目类型:', questionType);
console.log('📝 创建题目选项:', optionData);
await ExamApi.createQuestionOption(optionData);
if (questionType === 3) {
//
if (subQuestion.fillBlanks && subQuestion.fillBlanks.length > 0) {
console.log('📊 填空题答案数据:', subQuestion.fillBlanks);
for (let i = 0; i < subQuestion.fillBlanks.length; i++) {
const blank = subQuestion.fillBlanks[i];
const answerData = {
questionId: questionId,
answerText: blank.content || '',
orderNo: i
};
console.log('📝 创建填空题答案:', answerData);
await ExamApi.createQuestionAnswer(answerData);
}
}
} else if (questionType === 4) {
//
if (subQuestion.textAnswer) {
const answerData = {
questionId: questionId,
answerText: subQuestion.textAnswer,
orderNo: 0
};
console.log('📝 创建简答题答案:', answerData);
await ExamApi.createQuestionAnswer(answerData);
}
}
} else if (questionType === 5) {
// -
console.log('📊 处理复合题,题目类型:', questionType);
if (subQuestion.subQuestions && subQuestion.subQuestions.length > 0) {
console.log('📊 复合题子题目数据:', subQuestion.subQuestions);
// /
for (let subIndex = 0; subIndex < subQuestion.subQuestions.length; subIndex++) {
const compositeSubQuestion = subQuestion.subQuestions[subIndex];
console.log('📊 处理复合题子题目:', compositeSubQuestion.title, '类型:', compositeSubQuestion.type);
//
const subQuestionData = {
repoId: '',
parentId: questionId, // IDID
type: convertQuestionTypeToNumber(compositeSubQuestion.type),
content: compositeSubQuestion.title || '',
analysis: compositeSubQuestion.explanation || '',
difficulty: getDifficultyNumber(compositeSubQuestion.difficulty),
score: compositeSubQuestion.score || 0,
degree: 1,
ability: 1
};
console.log('📝 创建复合题子题目:', subQuestionData);
const subQuestionResponse = await ExamApi.createQuestion(subQuestionData);
if (subQuestionResponse.data) {
const subQuestionId = subQuestionResponse.data;
console.log('✅ 复合题子题目创建成功ID:', subQuestionId);
//
const subQuestionType = convertQuestionTypeToNumber(compositeSubQuestion.type);
if (subQuestionType === 0 || subQuestionType === 1 || subQuestionType === 2) {
// -
if (compositeSubQuestion.options && compositeSubQuestion.options.length > 0) {
for (let i = 0; i < compositeSubQuestion.options.length; i++) {
const option = compositeSubQuestion.options[i];
const isCorrect = subQuestionType === 0 ?
compositeSubQuestion.correctAnswer === i :
subQuestionType === 1 ?
compositeSubQuestion.correctAnswers && compositeSubQuestion.correctAnswers.includes(i) :
compositeSubQuestion.trueFalseAnswer === (i === 0);
const optionData = {
questionId: subQuestionId,
content: option.content || '',
izCorrent: isCorrect ? 1 : 0,
orderNo: i
};
console.log('📝 创建复合题子题目选项:', optionData);
await ExamApi.createQuestionOption(optionData);
}
}
} else if (subQuestionType === 3 || subQuestionType === 4) {
// -
if (subQuestionType === 3 && compositeSubQuestion.fillBlanks) {
//
for (let i = 0; i < compositeSubQuestion.fillBlanks.length; i++) {
const blank = compositeSubQuestion.fillBlanks[i];
const answerData = {
questionId: subQuestionId,
answerText: blank.content || '',
orderNo: i
};
console.log('📝 创建复合题子题目填空答案:', answerData);
await ExamApi.createQuestionAnswer(answerData);
}
} else if (subQuestionType === 4 && compositeSubQuestion.textAnswer) {
//
const answerData = {
questionId: subQuestionId,
answerText: compositeSubQuestion.textAnswer,
orderNo: 0
};
console.log('📝 创建复合题子题目简答答案:', answerData);
await ExamApi.createQuestionAnswer(answerData);
}
}
}
}
}
}
//
if (subQuestion.correctAnswer !== null && subQuestion.correctAnswer !== undefined) {
console.log('📊 题目答案数据:', subQuestion.correctAnswer);
const answerData = {
questionId: questionId,
answerText: String(subQuestion.correctAnswer),
orderNo: 1
};
console.log('📝 创建题目答案:', answerData);
await ExamApi.createQuestionAnswer(answerData);
} else {
console.log('📝 题目无答案,跳过答案创建');
}
//
const paperQuestionData = {

View File

@ -828,7 +828,7 @@ const updateQuestionOptions = async (questionId: string, questionType: number) =
id: existingOptionsIds.value[index] || '',
content: option.content || '',
izCorrent: index === correctAnswer ? 1 : 0,
orderNo: index
orderNo: index + 1 // orderNo1
})).filter(option => option.id); // ID
} else if (questionType === 1) { //
@ -839,7 +839,7 @@ const updateQuestionOptions = async (questionId: string, questionType: number) =
id: existingOptionsIds.value[index] || '',
content: option.content || '',
izCorrent: correctAnswers.includes(index) ? 1 : 0,
orderNo: index
orderNo: index + 1 // orderNo1
})).filter(option => option.id); // ID
} else if (questionType === 2) { //
@ -1249,10 +1249,10 @@ const renderSingleChoiceData = (options: any[]) => {
content: option.content || ''
}));
//
// - orderNoorderNo10
const correctOption = sortedOptions.find(option => option.izCorrent === 1);
if (correctOption) {
questionForm.correctAnswer = correctOption.orderNo;
questionForm.correctAnswer = correctOption.orderNo - 1; //
}
console.log('✅ 单选题渲染完成:', {
@ -1279,10 +1279,10 @@ const renderMultipleChoiceData = (options: any[]) => {
content: option.content || ''
}));
//
// - orderNo
questionForm.correctAnswers = sortedOptions
.filter(option => option.izCorrent === 1)
.map(option => option.orderNo);
.map(option => option.orderNo - 1); //
console.log('✅ 多选题渲染完成:', {
options: questionForm.options,