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 });
|
||||
|
||||
// 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') {
|
||||
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:`, {
|
||||
@@ -897,7 +901,8 @@ export default class VocabService {
|
||||
lessonType: plainLesson.lessonType,
|
||||
exerciseCount: plainLesson.grammarExercises ? plainLesson.grammarExercises.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;
|
||||
}
|
||||
|
||||
@@ -387,6 +387,8 @@
|
||||
"successRate": "Erfolgsrate",
|
||||
"modeMultipleChoice": "Multiple Choice",
|
||||
"modeTyping": "Texteingabe",
|
||||
"currentLesson": "Aktuelle Lektion",
|
||||
"mixedReview": "Wiederholung",
|
||||
"lessonCompleted": "Lektion abgeschlossen!",
|
||||
"goToNextLesson": "Zur nächsten Lektion wechseln?",
|
||||
"allLessonsCompleted": "Alle Lektionen abgeschlossen!",
|
||||
|
||||
@@ -387,6 +387,8 @@
|
||||
"successRate": "Success Rate",
|
||||
"modeMultipleChoice": "Multiple Choice",
|
||||
"modeTyping": "Text Input",
|
||||
"currentLesson": "Current Lesson",
|
||||
"mixedReview": "Review",
|
||||
"lessonCompleted": "Lesson completed!",
|
||||
"goToNextLesson": "Go to next lesson?",
|
||||
"allLessonsCompleted": "All lessons completed!",
|
||||
|
||||
@@ -73,6 +73,12 @@
|
||||
</span>
|
||||
</div>
|
||||
<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' }">
|
||||
{{ $t('socialnetwork.vocab.courses.modeMultipleChoice') }}
|
||||
</span>
|
||||
@@ -441,6 +447,9 @@ export default {
|
||||
vocabTrainerTotalAttempts: 0,
|
||||
vocabTrainerStats: {}, // { [vocabKey]: { attempts: 0, correct: 0, wrong: 0 } }
|
||||
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,
|
||||
vocabTrainerAnswer: '',
|
||||
vocabTrainerSelectedChoice: null,
|
||||
@@ -507,6 +516,16 @@ export default {
|
||||
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() {
|
||||
// Extrahiere wichtige Begriffe aus den Übungen
|
||||
try {
|
||||
@@ -998,6 +1017,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
console.log('[VocabLessonView] Vokabeln gefunden:', this.importantVocab.length);
|
||||
console.log('[VocabLessonView] Alte Vokabeln:', this.previousVocab?.length || 0);
|
||||
this.vocabTrainerActive = true;
|
||||
this.vocabTrainerPool = [...this.importantVocab];
|
||||
this.vocabTrainerMode = 'multiple_choice';
|
||||
@@ -1006,6 +1026,11 @@ export default {
|
||||
this.vocabTrainerWrong = 0;
|
||||
this.vocabTrainerTotalAttempts = 0;
|
||||
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');
|
||||
this.$nextTick(() => {
|
||||
this.nextVocabQuestion();
|
||||
@@ -1015,11 +1040,23 @@ export default {
|
||||
this.vocabTrainerActive = false;
|
||||
this.vocabTrainerMode = 'multiple_choice';
|
||||
this.vocabTrainerAutoSwitchedToTyping = false;
|
||||
this.vocabTrainerPhase = 'current';
|
||||
this.vocabTrainerMixedAttempts = 0;
|
||||
this.vocabTrainerMixedPool = [];
|
||||
this.currentVocabQuestion = null;
|
||||
this.vocabTrainerAnswer = '';
|
||||
this.vocabTrainerSelectedChoice = null;
|
||||
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) {
|
||||
return `${vocab.learning}|${vocab.reference}`;
|
||||
},
|
||||
@@ -1031,13 +1068,43 @@ export default {
|
||||
return this.vocabTrainerStats[key];
|
||||
},
|
||||
checkVocabModeSwitch() {
|
||||
// Wechsle zu Texteingabe wenn 80% erreicht und mindestens 20 Versuche
|
||||
if (this.vocabTrainerMode === 'multiple_choice' && this.vocabTrainerTotalAttempts >= 20) {
|
||||
const successRate = (this.vocabTrainerCorrect / this.vocabTrainerTotalAttempts) * 100;
|
||||
if (successRate >= 80) {
|
||||
const MC_THRESHOLD = 60; // Multiple Choice Versuche pro Phase
|
||||
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;
|
||||
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.vocabTrainerAutoSwitchedToTyping = true;
|
||||
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; // Markiere als automatisch gewechselt
|
||||
// Reset Stats für Texteingabe-Modus
|
||||
this.vocabTrainerAutoSwitchedToTyping = true;
|
||||
// Im Typing: Pool aus aktuellen + alten Vokabeln kombinieren
|
||||
this.vocabTrainerPool = [...this.importantVocab, ...this.vocabTrainerMixedPool];
|
||||
this.vocabTrainerCorrect = 0;
|
||||
this.vocabTrainerWrong = 0;
|
||||
this.vocabTrainerTotalAttempts = 0;
|
||||
@@ -1048,6 +1115,12 @@ export default {
|
||||
// Wechsle zurück zu Multiple Choice
|
||||
this.vocabTrainerMode = 'multiple_choice';
|
||||
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
|
||||
this.vocabTrainerCorrect = 0;
|
||||
this.vocabTrainerWrong = 0;
|
||||
|
||||
Reference in New Issue
Block a user