feat: 改進進度條為測驗數量追蹤,更準確反映學習進度
- 新增測驗進度狀態管理 (totalTests, completedTests) - 實現智能測驗數量計算,基於CEFR情境判斷每詞卡測驗數 - 進度條改為基於測驗完成度而非詞卡完成度 - 新增詳細調試日誌,顯示測驗總數計算和分布 - 進度顯示格式:X/Y 測驗 + 詞卡位置 + 答題分數 - 更準確反映不同難度詞彙的實際學習工作量 範例:A1學習者3測驗 + 困難詞彙2測驗 + 適中詞彙3測驗 = 8測驗總數 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6b71ef3b55
commit
c21e9de8e5
|
|
@ -46,6 +46,10 @@ export default function LearnPage() {
|
|||
const [showHint, setShowHint] = useState(false)
|
||||
const [isFlipped, setIsFlipped] = useState(false)
|
||||
|
||||
// 測驗進度狀態
|
||||
const [totalTests, setTotalTests] = useState(0) // 所有測驗總數
|
||||
const [completedTests, setCompletedTests] = useState(0) // 已完成測驗數
|
||||
|
||||
// UI狀態
|
||||
const [modalImage, setModalImage] = useState<string | null>(null)
|
||||
const [showReportModal, setShowReportModal] = useState(false)
|
||||
|
|
@ -121,6 +125,23 @@ export default function LearnPage() {
|
|||
console.log('✅ 載入後端API數據成功:', cardsToUse.length, '張詞卡');
|
||||
console.log('📋 詞卡列表:', cardsToUse.map(c => c.word));
|
||||
|
||||
// 計算所有測驗總數
|
||||
const userCEFR = localStorage.getItem('userEnglishLevel') || 'A2';
|
||||
let totalTestCount = 0;
|
||||
cardsToUse.forEach(card => {
|
||||
const wordCEFR = card.difficultyLevel || 'A2';
|
||||
const testsForCard = calculateTestsForCard(userCEFR, wordCEFR);
|
||||
totalTestCount += testsForCard;
|
||||
});
|
||||
|
||||
console.log('📊 測驗總數計算:', totalTestCount, '個測驗');
|
||||
console.log('📝 詞卡測驗分布:', cardsToUse.map(card => {
|
||||
const wordCEFR = card.difficultyLevel || 'A2';
|
||||
return `${card.word}: ${calculateTestsForCard(userCEFR, wordCEFR)}個測驗`;
|
||||
}));
|
||||
|
||||
setTotalTests(totalTestCount);
|
||||
setCompletedTests(0);
|
||||
setDueCards(cardsToUse);
|
||||
|
||||
// 設置第一張卡片
|
||||
|
|
@ -273,6 +294,23 @@ export default function LearnPage() {
|
|||
return mapping[cefr] || 50;
|
||||
}
|
||||
|
||||
// 計算每張詞卡的測驗數量
|
||||
const calculateTestsForCard = (userCEFR: string, wordCEFR: string): number => {
|
||||
const userLevel = getCEFRToLevel(userCEFR);
|
||||
const wordLevel = getCEFRToLevel(wordCEFR);
|
||||
const difficulty = wordLevel - userLevel;
|
||||
|
||||
if (userCEFR === 'A1') {
|
||||
return 3; // A1學習者:翻卡、選擇、聽力
|
||||
} else if (difficulty < -10) {
|
||||
return 2; // 簡單詞彙:填空、重組
|
||||
} else if (difficulty >= -10 && difficulty <= 10) {
|
||||
return 3; // 適中詞彙:填空、重組、口說
|
||||
} else {
|
||||
return 2; // 困難詞彙:翻卡、選擇
|
||||
}
|
||||
}
|
||||
|
||||
// 取得當前學習情境
|
||||
const getCurrentContext = (userCEFR: string, wordCEFR: string): string => {
|
||||
const userLevel = getCEFRToLevel(userCEFR);
|
||||
|
|
@ -548,7 +586,7 @@ export default function LearnPage() {
|
|||
await submitReviewResult(isCorrect, answer);
|
||||
}
|
||||
|
||||
// 提交復習結果
|
||||
// 提交復習結果並更新測驗進度
|
||||
const submitReviewResult = async (isCorrect: boolean, userAnswer?: string, confidenceLevel?: number) => {
|
||||
if (!currentCard) return;
|
||||
|
||||
|
|
@ -572,9 +610,18 @@ export default function LearnPage() {
|
|||
} else {
|
||||
console.log('復習結果提交失敗,繼續運行');
|
||||
}
|
||||
|
||||
// 更新測驗進度(無論提交成功或失敗)
|
||||
setCompletedTests(prev => {
|
||||
const newCompleted = prev + 1;
|
||||
console.log(`📈 測驗進度更新: ${newCompleted}/${totalTests} (${Math.round((newCompleted/totalTests)*100)}%)`);
|
||||
return newCompleted;
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交復習結果失敗:', error);
|
||||
// 不中斷流程,允許用戶繼續學習
|
||||
// 即使出錯也更新進度,避免卡住
|
||||
setCompletedTests(prev => prev + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -652,6 +699,8 @@ export default function LearnPage() {
|
|||
|
||||
const handleRestart = async () => {
|
||||
setScore({ correct: 0, total: 0 })
|
||||
setCompletedTests(0)
|
||||
setTotalTests(0)
|
||||
setShowComplete(false)
|
||||
setShowNoDueCards(false)
|
||||
await loadDueCards(); // 重新載入到期詞卡
|
||||
|
|
@ -742,7 +791,10 @@ export default function LearnPage() {
|
|||
<span className="text-sm text-gray-600">進度</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600">
|
||||
{currentCardIndex + 1} / {dueCards.length}
|
||||
{completedTests} / {totalTests} 測驗
|
||||
</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
詞卡 {currentCardIndex + 1}/{dueCards.length}
|
||||
</span>
|
||||
<div className="text-sm">
|
||||
<span className="text-green-600 font-semibold">{score.correct}</span>
|
||||
|
|
@ -759,7 +811,7 @@ export default function LearnPage() {
|
|||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-primary h-2 rounded-full transition-all"
|
||||
style={{ width: `${((currentCardIndex + 1) / dueCards.length) * 100}%` }}
|
||||
style={{ width: `${totalTests > 0 ? (completedTests / totalTests) * 100 : 0}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue