Enhance VocabService and VocabLessonView for improved vocabulary review: Updated VocabService to load previous lesson vocabulary for both review and mixed training modes. Added new computed properties and methods in VocabLessonView to manage vocabulary from previous lessons, including a mixed pool for enhanced training. Updated UI to reflect current lesson and mixed review status in the vocabulary trainer.
This commit is contained in:
@@ -885,10 +885,14 @@ export default class VocabService {
|
|||||||
|
|
||||||
const plainLesson = lesson.get({ plain: true });
|
const plainLesson = lesson.get({ plain: true });
|
||||||
|
|
||||||
// Bei Wiederholungslektionen: Lade Vokabeln aus vorherigen Lektionen
|
// Lade Vokabeln aus vorherigen Lektionen (für Wiederholung UND für gemischten Vokabeltrainer)
|
||||||
|
if (plainLesson.lessonNumber > 1) {
|
||||||
|
plainLesson.previousLessonExercises = await this._getReviewVocabExercises(plainLesson.courseId, plainLesson.lessonNumber);
|
||||||
|
}
|
||||||
|
// Bei Wiederholungslektionen: Auch Lektions-Liste für Anzeige
|
||||||
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') {
|
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') {
|
||||||
plainLesson.reviewLessons = await this._getReviewLessons(plainLesson.courseId, plainLesson.lessonNumber);
|
plainLesson.reviewLessons = await this._getReviewLessons(plainLesson.courseId, plainLesson.lessonNumber);
|
||||||
plainLesson.reviewVocabExercises = await this._getReviewVocabExercises(plainLesson.courseId, plainLesson.lessonNumber);
|
plainLesson.reviewVocabExercises = plainLesson.previousLessonExercises || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[getLesson] Lektion ${lessonId} geladen:`, {
|
console.log(`[getLesson] Lektion ${lessonId} geladen:`, {
|
||||||
@@ -897,7 +901,8 @@ export default class VocabService {
|
|||||||
lessonType: plainLesson.lessonType,
|
lessonType: plainLesson.lessonType,
|
||||||
exerciseCount: plainLesson.grammarExercises ? plainLesson.grammarExercises.length : 0,
|
exerciseCount: plainLesson.grammarExercises ? plainLesson.grammarExercises.length : 0,
|
||||||
reviewLessonsCount: plainLesson.reviewLessons ? plainLesson.reviewLessons.length : 0,
|
reviewLessonsCount: plainLesson.reviewLessons ? plainLesson.reviewLessons.length : 0,
|
||||||
reviewVocabExercisesCount: plainLesson.reviewVocabExercises ? plainLesson.reviewVocabExercises.length : 0
|
reviewVocabExercisesCount: plainLesson.reviewVocabExercises ? plainLesson.reviewVocabExercises.length : 0,
|
||||||
|
previousLessonExercisesCount: plainLesson.previousLessonExercises ? plainLesson.previousLessonExercises.length : 0
|
||||||
});
|
});
|
||||||
return plainLesson;
|
return plainLesson;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,6 +387,8 @@
|
|||||||
"successRate": "Erfolgsrate",
|
"successRate": "Erfolgsrate",
|
||||||
"modeMultipleChoice": "Multiple Choice",
|
"modeMultipleChoice": "Multiple Choice",
|
||||||
"modeTyping": "Texteingabe",
|
"modeTyping": "Texteingabe",
|
||||||
|
"currentLesson": "Aktuelle Lektion",
|
||||||
|
"mixedReview": "Wiederholung",
|
||||||
"lessonCompleted": "Lektion abgeschlossen!",
|
"lessonCompleted": "Lektion abgeschlossen!",
|
||||||
"goToNextLesson": "Zur nächsten Lektion wechseln?",
|
"goToNextLesson": "Zur nächsten Lektion wechseln?",
|
||||||
"allLessonsCompleted": "Alle Lektionen abgeschlossen!",
|
"allLessonsCompleted": "Alle Lektionen abgeschlossen!",
|
||||||
|
|||||||
@@ -387,6 +387,8 @@
|
|||||||
"successRate": "Success Rate",
|
"successRate": "Success Rate",
|
||||||
"modeMultipleChoice": "Multiple Choice",
|
"modeMultipleChoice": "Multiple Choice",
|
||||||
"modeTyping": "Text Input",
|
"modeTyping": "Text Input",
|
||||||
|
"currentLesson": "Current Lesson",
|
||||||
|
"mixedReview": "Review",
|
||||||
"lessonCompleted": "Lesson completed!",
|
"lessonCompleted": "Lesson completed!",
|
||||||
"goToNextLesson": "Go to next lesson?",
|
"goToNextLesson": "Go to next lesson?",
|
||||||
"allLessonsCompleted": "All lessons completed!",
|
"allLessonsCompleted": "All lessons completed!",
|
||||||
|
|||||||
@@ -73,6 +73,12 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-row">
|
<div class="stats-row">
|
||||||
|
<span class="mode-badge" :class="{ 'mode-active': vocabTrainerPhase === 'current' }">
|
||||||
|
{{ $t('socialnetwork.vocab.courses.currentLesson') || 'Aktuelle Lektion' }}
|
||||||
|
</span>
|
||||||
|
<span v-if="previousVocab && previousVocab.length > 0" class="mode-badge" :class="{ 'mode-active': vocabTrainerPhase === 'mixed' }">
|
||||||
|
{{ $t('socialnetwork.vocab.courses.mixedReview') || 'Wiederholung' }}
|
||||||
|
</span>
|
||||||
<span class="mode-badge" :class="{ 'mode-active': vocabTrainerMode === 'multiple_choice', 'mode-completed': vocabTrainerMode === 'typing' }">
|
<span class="mode-badge" :class="{ 'mode-active': vocabTrainerMode === 'multiple_choice', 'mode-completed': vocabTrainerMode === 'typing' }">
|
||||||
{{ $t('socialnetwork.vocab.courses.modeMultipleChoice') }}
|
{{ $t('socialnetwork.vocab.courses.modeMultipleChoice') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -441,6 +447,9 @@ export default {
|
|||||||
vocabTrainerTotalAttempts: 0,
|
vocabTrainerTotalAttempts: 0,
|
||||||
vocabTrainerStats: {}, // { [vocabKey]: { attempts: 0, correct: 0, wrong: 0 } }
|
vocabTrainerStats: {}, // { [vocabKey]: { attempts: 0, correct: 0, wrong: 0 } }
|
||||||
vocabTrainerChoiceOptions: [],
|
vocabTrainerChoiceOptions: [],
|
||||||
|
vocabTrainerPhase: 'current', // 'current' = aktuelle Lektion, 'mixed' = gemischt mit alten
|
||||||
|
vocabTrainerMixedPool: [], // Pool aus alten Lektionsvokabeln
|
||||||
|
vocabTrainerMixedAttempts: 0, // Zähler für Mixed-Phase
|
||||||
currentVocabQuestion: null,
|
currentVocabQuestion: null,
|
||||||
vocabTrainerAnswer: '',
|
vocabTrainerAnswer: '',
|
||||||
vocabTrainerSelectedChoice: null,
|
vocabTrainerSelectedChoice: null,
|
||||||
@@ -507,6 +516,16 @@ export default {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/** Vokabeln aus vorherigen Lektionen (für gemischte Wiederholung im Vokabeltrainer) */
|
||||||
|
previousVocab() {
|
||||||
|
try {
|
||||||
|
if (!this.lesson || !this.lesson.previousLessonExercises) return [];
|
||||||
|
return this._extractVocabFromExercises(this.lesson.previousLessonExercises);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Fehler in previousVocab:', e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
importantVocab() {
|
importantVocab() {
|
||||||
// Extrahiere wichtige Begriffe aus den Übungen
|
// Extrahiere wichtige Begriffe aus den Übungen
|
||||||
try {
|
try {
|
||||||
@@ -998,6 +1017,7 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('[VocabLessonView] Vokabeln gefunden:', this.importantVocab.length);
|
console.log('[VocabLessonView] Vokabeln gefunden:', this.importantVocab.length);
|
||||||
|
console.log('[VocabLessonView] Alte Vokabeln:', this.previousVocab?.length || 0);
|
||||||
this.vocabTrainerActive = true;
|
this.vocabTrainerActive = true;
|
||||||
this.vocabTrainerPool = [...this.importantVocab];
|
this.vocabTrainerPool = [...this.importantVocab];
|
||||||
this.vocabTrainerMode = 'multiple_choice';
|
this.vocabTrainerMode = 'multiple_choice';
|
||||||
@@ -1006,6 +1026,11 @@ export default {
|
|||||||
this.vocabTrainerWrong = 0;
|
this.vocabTrainerWrong = 0;
|
||||||
this.vocabTrainerTotalAttempts = 0;
|
this.vocabTrainerTotalAttempts = 0;
|
||||||
this.vocabTrainerStats = {};
|
this.vocabTrainerStats = {};
|
||||||
|
this.vocabTrainerPhase = 'current';
|
||||||
|
this.vocabTrainerMixedAttempts = 0;
|
||||||
|
// Bereite Mixed-Pool aus alten Vokabeln vor (ohne Duplikate aus aktueller Lektion)
|
||||||
|
this.vocabTrainerMixedPool = this._buildMixedPool();
|
||||||
|
console.log('[VocabLessonView] Mixed-Pool:', this.vocabTrainerMixedPool.length, 'Vokabeln');
|
||||||
console.log('[VocabLessonView] Rufe nextVocabQuestion auf');
|
console.log('[VocabLessonView] Rufe nextVocabQuestion auf');
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.nextVocabQuestion();
|
this.nextVocabQuestion();
|
||||||
@@ -1015,11 +1040,23 @@ export default {
|
|||||||
this.vocabTrainerActive = false;
|
this.vocabTrainerActive = false;
|
||||||
this.vocabTrainerMode = 'multiple_choice';
|
this.vocabTrainerMode = 'multiple_choice';
|
||||||
this.vocabTrainerAutoSwitchedToTyping = false;
|
this.vocabTrainerAutoSwitchedToTyping = false;
|
||||||
|
this.vocabTrainerPhase = 'current';
|
||||||
|
this.vocabTrainerMixedAttempts = 0;
|
||||||
|
this.vocabTrainerMixedPool = [];
|
||||||
this.currentVocabQuestion = null;
|
this.currentVocabQuestion = null;
|
||||||
this.vocabTrainerAnswer = '';
|
this.vocabTrainerAnswer = '';
|
||||||
this.vocabTrainerSelectedChoice = null;
|
this.vocabTrainerSelectedChoice = null;
|
||||||
this.vocabTrainerAnswered = false;
|
this.vocabTrainerAnswered = false;
|
||||||
},
|
},
|
||||||
|
/** Erstellt den Mixed-Pool aus vorherigen Lektions-Vokabeln (ohne Duplikate der aktuellen Lektion) */
|
||||||
|
_buildMixedPool() {
|
||||||
|
if (!this.previousVocab || this.previousVocab.length === 0) return [];
|
||||||
|
const currentKeys = new Set(this.importantVocab.map(v => this.getVocabKey(v)));
|
||||||
|
const filtered = this.previousVocab.filter(v => !currentKeys.has(this.getVocabKey(v)));
|
||||||
|
// Zufällig mischen und auf 40 begrenzen
|
||||||
|
const shuffled = [...filtered].sort(() => Math.random() - 0.5);
|
||||||
|
return shuffled.slice(0, 40);
|
||||||
|
},
|
||||||
getVocabKey(vocab) {
|
getVocabKey(vocab) {
|
||||||
return `${vocab.learning}|${vocab.reference}`;
|
return `${vocab.learning}|${vocab.reference}`;
|
||||||
},
|
},
|
||||||
@@ -1031,13 +1068,43 @@ export default {
|
|||||||
return this.vocabTrainerStats[key];
|
return this.vocabTrainerStats[key];
|
||||||
},
|
},
|
||||||
checkVocabModeSwitch() {
|
checkVocabModeSwitch() {
|
||||||
// Wechsle zu Texteingabe wenn 80% erreicht und mindestens 20 Versuche
|
const MC_THRESHOLD = 60; // Multiple Choice Versuche pro Phase
|
||||||
if (this.vocabTrainerMode === 'multiple_choice' && this.vocabTrainerTotalAttempts >= 20) {
|
const MIXED_LIMIT = 40; // Anzahl gemischter Vokabeln aus alten Lektionen
|
||||||
|
|
||||||
|
if (this.vocabTrainerPhase === 'current') {
|
||||||
|
// Phase 1: Aktuelle Lektion - nach MC_THRESHOLD Versuchen mit 80% → Wechsel zu Mixed oder Typing
|
||||||
|
if (this.vocabTrainerMode === 'multiple_choice' && this.vocabTrainerTotalAttempts >= MC_THRESHOLD) {
|
||||||
const successRate = (this.vocabTrainerCorrect / this.vocabTrainerTotalAttempts) * 100;
|
const successRate = (this.vocabTrainerCorrect / this.vocabTrainerTotalAttempts) * 100;
|
||||||
if (successRate >= 80) {
|
if (successRate >= 80) {
|
||||||
|
// Wechsel zur Mixed-Phase (falls alte Vokabeln vorhanden)
|
||||||
|
if (this.vocabTrainerMixedPool.length > 0) {
|
||||||
|
console.log('[VocabLessonView] Wechsel zu Mixed-Phase mit', this.vocabTrainerMixedPool.length, 'alten Vokabeln');
|
||||||
|
this.vocabTrainerPhase = 'mixed';
|
||||||
|
this.vocabTrainerPool = [...this.vocabTrainerMixedPool];
|
||||||
|
this.vocabTrainerMixedAttempts = 0;
|
||||||
|
// Stats zurücksetzen für neue Phase
|
||||||
|
this.vocabTrainerCorrect = 0;
|
||||||
|
this.vocabTrainerWrong = 0;
|
||||||
|
this.vocabTrainerTotalAttempts = 0;
|
||||||
|
} else {
|
||||||
|
// Kein Mixed-Pool → direkt zu Typing
|
||||||
this.vocabTrainerMode = 'typing';
|
this.vocabTrainerMode = 'typing';
|
||||||
this.vocabTrainerAutoSwitchedToTyping = true; // Markiere als automatisch gewechselt
|
this.vocabTrainerAutoSwitchedToTyping = true;
|
||||||
// Reset Stats für Texteingabe-Modus
|
this.vocabTrainerPool = [...this.importantVocab];
|
||||||
|
this.vocabTrainerCorrect = 0;
|
||||||
|
this.vocabTrainerWrong = 0;
|
||||||
|
this.vocabTrainerTotalAttempts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (this.vocabTrainerPhase === 'mixed') {
|
||||||
|
// Phase 2: Gemischte Wiederholung - nach MIXED_LIMIT Versuchen → Wechsel zu Typing mit allen Vokabeln
|
||||||
|
if (this.vocabTrainerMode === 'multiple_choice' && this.vocabTrainerTotalAttempts >= MIXED_LIMIT) {
|
||||||
|
console.log('[VocabLessonView] Mixed-Phase abgeschlossen, wechsle zu Typing');
|
||||||
|
this.vocabTrainerMode = 'typing';
|
||||||
|
this.vocabTrainerAutoSwitchedToTyping = true;
|
||||||
|
// Im Typing: Pool aus aktuellen + alten Vokabeln kombinieren
|
||||||
|
this.vocabTrainerPool = [...this.importantVocab, ...this.vocabTrainerMixedPool];
|
||||||
this.vocabTrainerCorrect = 0;
|
this.vocabTrainerCorrect = 0;
|
||||||
this.vocabTrainerWrong = 0;
|
this.vocabTrainerWrong = 0;
|
||||||
this.vocabTrainerTotalAttempts = 0;
|
this.vocabTrainerTotalAttempts = 0;
|
||||||
@@ -1048,6 +1115,12 @@ export default {
|
|||||||
// Wechsle zurück zu Multiple Choice
|
// Wechsle zurück zu Multiple Choice
|
||||||
this.vocabTrainerMode = 'multiple_choice';
|
this.vocabTrainerMode = 'multiple_choice';
|
||||||
this.vocabTrainerAutoSwitchedToTyping = false;
|
this.vocabTrainerAutoSwitchedToTyping = false;
|
||||||
|
// Zurück zur aktuellen Phase mit passendem Pool
|
||||||
|
if (this.vocabTrainerPhase === 'mixed') {
|
||||||
|
this.vocabTrainerPool = [...this.vocabTrainerMixedPool];
|
||||||
|
} else {
|
||||||
|
this.vocabTrainerPool = [...this.importantVocab];
|
||||||
|
}
|
||||||
// Reset Stats für Multiple Choice Modus
|
// Reset Stats für Multiple Choice Modus
|
||||||
this.vocabTrainerCorrect = 0;
|
this.vocabTrainerCorrect = 0;
|
||||||
this.vocabTrainerWrong = 0;
|
this.vocabTrainerWrong = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user