fixed some bugs

This commit is contained in:
2026-05-21 17:16:44 +08:00
parent 7f44194416
commit 2f76e6a3ca
2 changed files with 67 additions and 13 deletions

View File

@@ -879,7 +879,6 @@ router.post('/test-record', authMiddleware, async (req, res) => {
message: '测试记录已保存', message: '测试记录已保存',
attemptId: attempt._id.toString(), attemptId: attempt._id.toString(),
stats: verifiedStats, stats: verifiedStats,
results: evaluatedResults,
submittedAt: new Date(lastSubmittedAtMs) submittedAt: new Date(lastSubmittedAtMs)
}); });
} catch (error) { } catch (error) {

View File

@@ -93,7 +93,6 @@ interface SavedVocabularyTestRecordResponse {
endTime: string | Date; endTime: string | Date;
duration: number; duration: number;
}; };
results: TestResult[];
} }
interface TestAttemptResponse { interface TestAttemptResponse {
@@ -125,6 +124,7 @@ const VocabularyStudy: React.FC = () => {
const [testStarted, setTestStarted] = useState(false); const [testStarted, setTestStarted] = useState(false);
const [testFinished, setTestFinished] = useState(false); const [testFinished, setTestFinished] = useState(false);
const [testResults, setTestResults] = useState<TestResult[]>([]); const [testResults, setTestResults] = useState<TestResult[]>([]);
const [answerFeedback, setAnswerFeedback] = useState<TestResult | null>(null);
const [testAttemptId, setTestAttemptId] = useState<string>(''); const [testAttemptId, setTestAttemptId] = useState<string>('');
const [pendingAnswers, setPendingAnswers] = useState<PendingAnswer[]>([]); const [pendingAnswers, setPendingAnswers] = useState<PendingAnswer[]>([]);
const [testAttemptLoading, setTestAttemptLoading] = useState(false); const [testAttemptLoading, setTestAttemptLoading] = useState(false);
@@ -429,6 +429,7 @@ const VocabularyStudy: React.FC = () => {
setTestAttemptId(''); setTestAttemptId('');
setPendingAnswers([]); setPendingAnswers([]);
setTestResults([]); setTestResults([]);
setAnswerFeedback(null);
const wordIds = studyWords.map(getWordId).filter(Boolean); const wordIds = studyWords.map(getWordId).filter(Boolean);
if (wordIds.length === 0) { if (wordIds.length === 0) {
message.error('单词数据不完整,请重新加载后再试'); message.error('单词数据不完整,请重新加载后再试');
@@ -506,6 +507,30 @@ const VocabularyStudy: React.FC = () => {
.toLowerCase(); .toLowerCase();
} }
const getQuestionWord = (question: TestQuestion): Word | undefined => {
return studyWords.find(word => getWordId(word) === question.wordId);
};
const buildLocalTestResult = (question: TestQuestion, rawUserAnswer: string): TestResult => {
const matchedWord = getQuestionWord(question);
const userAnswer = rawUserAnswer.trim();
const correctAnswer = testType === 'multiple-choice'
? String(matchedWord?.translation ?? question.translation ?? '').trim()
: String(matchedWord?.word ?? question.word ?? '').trim();
const isCorrect = testType === 'multiple-choice'
? userAnswer === correctAnswer
: normalizeAnswer(userAnswer) === normalizeAnswer(correctAnswer);
return {
questionToken: question.questionToken,
wordId: question.wordId,
word: String(matchedWord?.word ?? question.word ?? question.translation ?? '').trim(),
userAnswer,
correctAnswer,
isCorrect
};
};
// 提交答案 // 提交答案
const submitAnswer = async () => { const submitAnswer = async () => {
if (!testStarted || testWords.length === 0 || !testAttemptId || answerSubmittingRef.current) return; if (!testStarted || testWords.length === 0 || !testAttemptId || answerSubmittingRef.current) return;
@@ -518,6 +543,7 @@ const VocabularyStudy: React.FC = () => {
} }
const submittedAt = Date.now(); const submittedAt = Date.now();
const normalizedUserAnswer = userAnswer.trim(); const normalizedUserAnswer = userAnswer.trim();
const localResult = buildLocalTestResult(currentQuestion, normalizedUserAnswer);
const answerProof = buildAnswerProof( const answerProof = buildAnswerProof(
testAttemptId, testAttemptId,
currentQuestion.questionToken, currentQuestion.questionToken,
@@ -534,34 +560,57 @@ const VocabularyStudy: React.FC = () => {
answerProof answerProof
} }
]; ];
const updatedTestResults = [
...testResults,
localResult
];
setPendingAnswers(updatedPendingAnswers); setPendingAnswers(updatedPendingAnswers);
setTestResults(updatedTestResults);
setAnswerFeedback(localResult);
setShowAnswer(true); setShowAnswer(true);
setUserAnswer(''); setUserAnswer('');
setTimeout(() => { setTimeout(() => {
if (testIndex < testWords.length - 1) { if (testIndex < testWords.length - 1) {
setTestIndex(testIndex + 1); const nextIndex = testIndex + 1;
setTestIndex(nextIndex);
setShowAnswer(false); setShowAnswer(false);
setAnswerFeedback(null);
answerSubmittingRef.current = false; answerSubmittingRef.current = false;
if (testType === 'audio-to-english') { if (testType === 'audio-to-english') {
setTimeout(() => { setTimeout(() => {
playWordSound(testWords[testIndex + 1]?.word || ''); playWordSound(testWords[nextIndex]?.word || '');
}, 500); }, 500);
} }
if (testType === 'multiple-choice') { if (testType === 'multiple-choice') {
setOptions(testWords[testIndex + 1]?.options || []); setOptions(testWords[nextIndex]?.options || []);
} }
} else { } else {
answerSubmittingRef.current = false; answerSubmittingRef.current = false;
finishTestWithResults(updatedPendingAnswers); finishTestWithResults(updatedPendingAnswers, updatedTestResults);
} }
}, 1500); }, 1500);
}; };
// 使用指定结果完成测试 // 使用指定结果完成测试
const finishTestWithResults = async (finalAnswers: PendingAnswer[]) => { const finishTestWithResults = async (finalAnswers: PendingAnswer[], finalResults: TestResult[]) => {
try { try {
const lastSubmittedAtMs = finalAnswers.length > 0
? Math.max(...finalAnswers.map(item => item.submittedAt))
: Date.now();
const localCorrectWords = finalResults.filter(result => result.isCorrect).length;
const localStats = {
totalWords: finalResults.length,
correctWords: localCorrectWords,
accuracy: finalResults.length > 0 ? (localCorrectWords / finalResults.length) * 100 : 0,
startTime: studyStats.startTime,
endTime: new Date(lastSubmittedAtMs),
duration: Math.max(0, (lastSubmittedAtMs - studyStats.startTime.getTime()) / 1000)
};
setTestResults(finalResults);
setStudyStats(localStats);
setTestFinished(true); setTestFinished(true);
// 添加调试信息 // 添加调试信息
@@ -591,7 +640,6 @@ const VocabularyStudy: React.FC = () => {
endTime: new Date(savedRecord.stats.endTime), endTime: new Date(savedRecord.stats.endTime),
duration: savedRecord.stats.duration duration: savedRecord.stats.duration
}); });
setTestResults(savedRecord.results);
setPendingAnswers([]); setPendingAnswers([]);
setTestAttemptId(''); setTestAttemptId('');
@@ -609,7 +657,7 @@ const VocabularyStudy: React.FC = () => {
// 完成测试 - 兼容原来的调用方式 // 完成测试 - 兼容原来的调用方式
const finishTest = async () => { const finishTest = async () => {
// 使用当前的测试结果完成测试 // 使用当前的测试结果完成测试
await finishTestWithResults([...pendingAnswers]); await finishTestWithResults([...pendingAnswers], [...testResults]);
}; };
// 重新开始测试 // 重新开始测试
@@ -622,6 +670,7 @@ const VocabularyStudy: React.FC = () => {
setUserAnswer(''); setUserAnswer('');
setShowAnswer(false); setShowAnswer(false);
setTestResults([]); setTestResults([]);
setAnswerFeedback(null);
setPendingAnswers([]); setPendingAnswers([]);
setOptions([]); setOptions([]);
answerSubmittingRef.current = false; answerSubmittingRef.current = false;
@@ -1222,18 +1271,24 @@ const VocabularyStudy: React.FC = () => {
{showAnswer && ( {showAnswer && (
<div style={{ <div style={{
padding: 15, padding: 15,
backgroundColor: '#f6f8fa', backgroundColor: answerFeedback?.isCorrect ? '#f6ffed' : '#fff2f0',
borderRadius: 4, borderRadius: 4,
marginBottom: 15, marginBottom: 15,
border: '1px solid #d9d9d9' border: `1px solid ${answerFeedback?.isCorrect ? '#b7eb8f' : '#ffccc7'}`
}}> }}>
<div style={{ <div style={{
fontWeight: 'bold', fontWeight: 'bold',
color: '#1677ff', color: answerFeedback?.isCorrect ? '#389e0d' : '#cf1322',
marginBottom: 5 marginBottom: 5
}}> }}>
{answerFeedback?.isCorrect ? '回答正确' : '回答错误'}
</div> </div>
{answerFeedback && (
<>
<div>: {answerFeedback.correctAnswer}</div>
<div>: {answerFeedback.userAnswer || '(空)'}</div>
</>
)}
<div>...</div> <div>...</div>
</div> </div>
)} )}