修复判断题和填空题的答题问题
## 主要修复 - 修复判断题答案验证:前端提交时将字符串转换为布尔值 - 修复判断题答案显示:支持布尔值和字符串格式的正确显示 - 修复判断题选项顺序:保持"正确"在前、"错误"在后 - 修复填空题答案残留:题目切换时自动清空填空输入框 - 优化填空题验证:去除前后空格,添加详细日志 ## 技术细节 - Question.tsx: 判断题提交前将 "true"/"false" 转为 boolean - AnswerResult.tsx: formatAnswer 函数支持 boolean 类型 - QuestionCard.tsx: 判断题不排序,填空题切换时重置状态 - practice_handler.go: 填空题比较前 TrimSpace,添加调试日志 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
dd2b197516
commit
7c407db78b
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -277,12 +278,18 @@ func checkPracticeAnswer(questionType string, userAnswer, correctAnswer interfac
|
|||||||
userArr, ok1 := toStringArray(userAnswer)
|
userArr, ok1 := toStringArray(userAnswer)
|
||||||
correctArr, ok2 := toStringArray(correctAnswer)
|
correctArr, ok2 := toStringArray(correctAnswer)
|
||||||
if !ok1 || !ok2 || len(userArr) != len(correctArr) {
|
if !ok1 || !ok2 || len(userArr) != len(correctArr) {
|
||||||
|
log.Printf("填空题验证失败 - 数组转换或长度不匹配: ok1=%v, ok2=%v, userLen=%d, correctLen=%d",
|
||||||
|
ok1, ok2, len(userArr), len(correctArr))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 逐个比较填空答案
|
// 逐个比较填空答案(去除前后空格)
|
||||||
for i := range correctArr {
|
for i := range correctArr {
|
||||||
if userArr[i] != correctArr[i] {
|
userTrimmed := strings.TrimSpace(userArr[i])
|
||||||
|
correctTrimmed := strings.TrimSpace(correctArr[i])
|
||||||
|
if userTrimmed != correctTrimmed {
|
||||||
|
log.Printf("填空题验证失败 - 第%d个答案不匹配: user='%s', correct='%s'",
|
||||||
|
i+1, userTrimmed, correctTrimmed)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,7 +342,7 @@ func convertToDTO(question models.PracticeQuestion) models.PracticeQuestionDTO {
|
|||||||
dto.Answer = answer
|
dto.Answer = answer
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断题自动生成选项
|
// 判断题自动生成选项(正确在前,错误在后)
|
||||||
if question.Type == "true-false" {
|
if question.Type == "true-false" {
|
||||||
dto.Options = []models.Option{
|
dto.Options = []models.Option{
|
||||||
{Key: "true", Value: "正确"},
|
{Key: "true", Value: "正确"},
|
||||||
|
|||||||
@ -17,14 +17,23 @@ const AnswerResult: React.FC<AnswerResultProps> = ({
|
|||||||
questionType,
|
questionType,
|
||||||
}) => {
|
}) => {
|
||||||
// 格式化答案显示(判断题特殊处理)
|
// 格式化答案显示(判断题特殊处理)
|
||||||
const formatAnswer = (answer: string | string[]) => {
|
const formatAnswer = (answer: string | string[] | boolean) => {
|
||||||
const answerStr = Array.isArray(answer) ? answer.join(', ') : answer
|
// 处理判断题的布尔值和字符串
|
||||||
|
|
||||||
if (questionType === 'true-false') {
|
if (questionType === 'true-false') {
|
||||||
return answerStr === 'true' ? '正确' : answerStr === 'false' ? '错误' : answerStr
|
if (typeof answer === 'boolean') {
|
||||||
|
return answer ? '正确' : '错误'
|
||||||
|
}
|
||||||
|
if (typeof answer === 'string') {
|
||||||
|
return answer === 'true' ? '正确' : answer === 'false' ? '错误' : answer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return answerStr
|
// 处理数组答案
|
||||||
|
if (Array.isArray(answer)) {
|
||||||
|
return answer.join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import { Card, Space, Tag, Typography, Radio, Checkbox, Input, Button } from 'antd'
|
import { Card, Space, Tag, Typography, Radio, Checkbox, Input, Button } from 'antd'
|
||||||
import type { Question, AnswerResult as AnswerResultType } from '../types/question'
|
import type { Question, AnswerResult as AnswerResultType } from '../types/question'
|
||||||
import AnswerResult from './AnswerResult'
|
import AnswerResult from './AnswerResult'
|
||||||
@ -32,6 +32,13 @@ const QuestionCard: React.FC<QuestionCardProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [fillAnswers, setFillAnswers] = useState<string[]>([])
|
const [fillAnswers, setFillAnswers] = useState<string[]>([])
|
||||||
|
|
||||||
|
// 当题目ID变化时,重置填空题答案
|
||||||
|
useEffect(() => {
|
||||||
|
if (question.type === 'fill-in-blank') {
|
||||||
|
setFillAnswers([])
|
||||||
|
}
|
||||||
|
}, [question.id, question.type])
|
||||||
|
|
||||||
// 渲染填空题内容
|
// 渲染填空题内容
|
||||||
const renderFillContent = () => {
|
const renderFillContent = () => {
|
||||||
const content = question.content
|
const content = question.content
|
||||||
@ -118,7 +125,10 @@ const QuestionCard: React.FC<QuestionCardProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 单选题和判断题
|
// 单选题和判断题
|
||||||
const sortedOptions = [...question.options].sort((a, b) => a.key.localeCompare(b.key))
|
// 判断题不排序,保持后端返回的顺序(正确在前,错误在后)
|
||||||
|
const sortedOptions = question.type === 'true-false'
|
||||||
|
? question.options
|
||||||
|
: [...question.options].sort((a, b) => a.key.localeCompare(b.key))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
|
|||||||
@ -154,9 +154,15 @@ const QuestionPage: React.FC = () => {
|
|||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// 处理判断题答案:将字符串 "true"/"false" 转换为布尔值
|
||||||
|
let answerToSubmit = selectedAnswer;
|
||||||
|
if (currentQuestion.type === "true-false" && typeof selectedAnswer === "string") {
|
||||||
|
answerToSubmit = selectedAnswer === "true";
|
||||||
|
}
|
||||||
|
|
||||||
const res = await questionApi.submitAnswer({
|
const res = await questionApi.submitAnswer({
|
||||||
question_id: currentQuestion.id,
|
question_id: currentQuestion.id,
|
||||||
answer: selectedAnswer,
|
answer: answerToSubmit,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.success && res.data) {
|
if (res.success && res.data) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user