Enhance VocabLessonView with vocabulary trainer and grammar explanations
- Introduced a vocabulary trainer feature allowing users to practice important vocabulary interactively, with options to start and stop the trainer. - Added sections for grammar explanations and lesson descriptions to improve user understanding of the content. - Updated translations in both English and German to reflect changes in vocabulary and exercise terminology. - Enhanced conditional rendering to ensure proper display of vocabulary and grammar information based on lesson data.
This commit is contained in:
@@ -360,21 +360,30 @@
|
||||
"optional": "Optional",
|
||||
"invalidCode": "Ungültiger Code",
|
||||
"courseNotFound": "Kurs nicht gefunden",
|
||||
"grammarExercises": "Grammatik-Übungen",
|
||||
"noExercises": "Keine Übungen verfügbar",
|
||||
"grammarExercises": "Grammatik-Prüfung",
|
||||
"noExercises": "Keine Prüfung verfügbar",
|
||||
"enterAnswer": "Antwort eingeben",
|
||||
"checkAnswer": "Antwort prüfen",
|
||||
"correct": "Richtig!",
|
||||
"wrong": "Falsch",
|
||||
"explanation": "Erklärung",
|
||||
"learn": "Lernen",
|
||||
"exercises": "Übungen",
|
||||
"exercises": "Kapitel-Prüfung",
|
||||
"learnVocabulary": "Vokabeln lernen",
|
||||
"lessonDescription": "Lektions-Beschreibung",
|
||||
"culturalNotes": "Kulturelle Notizen",
|
||||
"grammarExplanations": "Grammatik-Erklärungen",
|
||||
"importantVocab": "Wichtige Begriffe",
|
||||
"vocabInfoText": "Diese Begriffe werden in den Übungen verwendet. Lerne sie hier passiv, bevor du zu den interaktiven Übungen wechselst.",
|
||||
"noVocabInfo": "Lies die Beschreibung oben und die Erklärungen in den Übungen, um die wichtigsten Begriffe zu lernen.",
|
||||
"startExercises": "Zu den Übungen",
|
||||
"vocabInfoText": "Diese Begriffe werden in der Prüfung verwendet. Lerne sie hier passiv, bevor du zur Kapitel-Prüfung wechselst.",
|
||||
"noVocabInfo": "Lies die Beschreibung oben und die Erklärungen in der Prüfung, um die wichtigsten Begriffe zu lernen.",
|
||||
"vocabTrainer": "Vokabeltrainer",
|
||||
"vocabTrainerDescription": "Übe die wichtigsten Begriffe dieser Lektion interaktiv.",
|
||||
"startVocabTrainer": "Vokabeltrainer starten",
|
||||
"stopTrainer": "Trainer beenden",
|
||||
"translateTo": "Übersetze ins Deutsche",
|
||||
"translateFrom": "Übersetze ins Bisaya",
|
||||
"next": "Weiter",
|
||||
"startExercises": "Zur Kapitel-Prüfung",
|
||||
"correctAnswer": "Richtige Antwort",
|
||||
"alternatives": "Alternative Antworten"
|
||||
}
|
||||
|
||||
@@ -360,21 +360,30 @@
|
||||
"optional": "Optional",
|
||||
"invalidCode": "Invalid code",
|
||||
"courseNotFound": "Course not found",
|
||||
"grammarExercises": "Grammar Exercises",
|
||||
"noExercises": "No exercises available",
|
||||
"grammarExercises": "Chapter Test",
|
||||
"noExercises": "No test available",
|
||||
"enterAnswer": "Enter answer",
|
||||
"checkAnswer": "Check Answer",
|
||||
"correct": "Correct!",
|
||||
"wrong": "Wrong",
|
||||
"explanation": "Explanation",
|
||||
"learn": "Learn",
|
||||
"exercises": "Exercises",
|
||||
"exercises": "Chapter Test",
|
||||
"learnVocabulary": "Learn Vocabulary",
|
||||
"lessonDescription": "Lesson Description",
|
||||
"culturalNotes": "Cultural Notes",
|
||||
"grammarExplanations": "Grammar Explanations",
|
||||
"importantVocab": "Important Vocabulary",
|
||||
"vocabInfoText": "These terms are used in the exercises. Learn them here passively before switching to the interactive exercises.",
|
||||
"noVocabInfo": "Read the description above and the explanations in the exercises to learn the most important terms.",
|
||||
"startExercises": "Start Exercises",
|
||||
"vocabInfoText": "These terms are used in the test. Learn them here passively before switching to the chapter test.",
|
||||
"noVocabInfo": "Read the description above and the explanations in the test to learn the most important terms.",
|
||||
"vocabTrainer": "Vocabulary Trainer",
|
||||
"vocabTrainerDescription": "Practice the most important terms of this lesson interactively.",
|
||||
"startVocabTrainer": "Start Vocabulary Trainer",
|
||||
"stopTrainer": "Stop Trainer",
|
||||
"translateTo": "Translate to English",
|
||||
"translateFrom": "Translate to Target Language",
|
||||
"next": "Next",
|
||||
"startExercises": "Start Chapter Test",
|
||||
"correctAnswer": "Correct Answer",
|
||||
"alternatives": "Alternative Answers"
|
||||
}
|
||||
|
||||
@@ -32,14 +32,74 @@
|
||||
<div v-if="activeTab === 'learn'" class="learn-section">
|
||||
<h3>{{ $t('socialnetwork.vocab.courses.learnVocabulary') }}</h3>
|
||||
|
||||
<!-- Lektions-Beschreibung -->
|
||||
<div v-if="lesson && lesson.description" class="lesson-description-box">
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.lessonDescription') }}</h4>
|
||||
<p>{{ lesson.description }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Kulturelle Notizen -->
|
||||
<div v-if="lesson && lesson.culturalNotes" class="cultural-notes">
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.culturalNotes') }}</h4>
|
||||
<p>{{ lesson.culturalNotes }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Wichtige Begriffe aus den Übungen (nur Anzeige, keine Interaktion) -->
|
||||
<div v-if="lesson && importantVocab && importantVocab.length > 0" class="vocab-list">
|
||||
<!-- Grammatik-Erklärungen -->
|
||||
<div v-if="grammarExplanations && grammarExplanations.length > 0" class="grammar-explanations">
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.grammarExplanations') }}</h4>
|
||||
<div v-for="(explanation, index) in grammarExplanations" :key="index" class="grammar-explanation-item">
|
||||
<strong>{{ explanation.title }}</strong>
|
||||
<p>{{ explanation.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vokabeltrainer -->
|
||||
<div v-if="importantVocab && importantVocab.length > 0" class="vocab-trainer-section">
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.vocabTrainer') }}</h4>
|
||||
<div v-if="!vocabTrainerActive" class="vocab-trainer-start">
|
||||
<p>{{ $t('socialnetwork.vocab.courses.vocabTrainerDescription') }}</p>
|
||||
<button @click="startVocabTrainer" class="btn-start-trainer">
|
||||
{{ $t('socialnetwork.vocab.courses.startVocabTrainer') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="vocab-trainer-active">
|
||||
<div class="vocab-trainer-stats">
|
||||
<span>{{ $t('socialnetwork.vocab.courses.correct') }}: {{ vocabTrainerCorrect }}</span>
|
||||
<span>{{ $t('socialnetwork.vocab.courses.wrong') }}: {{ vocabTrainerWrong }}</span>
|
||||
<button @click="stopVocabTrainer" class="btn-stop-trainer">{{ $t('socialnetwork.vocab.courses.stopTrainer') }}</button>
|
||||
</div>
|
||||
<div v-if="currentVocabQuestion" class="vocab-question">
|
||||
<div class="vocab-prompt">
|
||||
<div class="vocab-direction">{{ vocabTrainerDirection === 'L2R' ? $t('socialnetwork.vocab.courses.translateTo') : $t('socialnetwork.vocab.courses.translateFrom') }}</div>
|
||||
<div class="vocab-word">{{ currentVocabQuestion.prompt }}</div>
|
||||
</div>
|
||||
<div v-if="vocabTrainerAnswered" class="vocab-feedback" :class="{ correct: vocabTrainerLastCorrect, wrong: !vocabTrainerLastCorrect }">
|
||||
<div v-if="vocabTrainerLastCorrect">{{ $t('socialnetwork.vocab.courses.correct') }}!</div>
|
||||
<div v-else>
|
||||
{{ $t('socialnetwork.vocab.courses.wrong') }}. {{ $t('socialnetwork.vocab.courses.correctAnswer') }}: {{ currentVocabQuestion.answer }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="vocab-answer-area">
|
||||
<input
|
||||
v-model="vocabTrainerAnswer"
|
||||
@keydown.enter.prevent="checkVocabAnswer"
|
||||
:placeholder="$t('socialnetwork.vocab.courses.enterAnswer')"
|
||||
class="vocab-input"
|
||||
ref="vocabInput"
|
||||
/>
|
||||
<button @click="checkVocabAnswer" :disabled="!vocabTrainerAnswer.trim()">
|
||||
{{ $t('socialnetwork.vocab.courses.checkAnswer') }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="vocabTrainerAnswered" class="vocab-next">
|
||||
<button @click="nextVocabQuestion">{{ $t('socialnetwork.vocab.courses.next') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wichtige Begriffe Liste (nur Anzeige) -->
|
||||
<div v-if="lesson && importantVocab && importantVocab.length > 0 && !vocabTrainerActive" class="vocab-list">
|
||||
<h4>{{ $t('socialnetwork.vocab.courses.importantVocab') }}</h4>
|
||||
<p class="vocab-info-text">{{ $t('socialnetwork.vocab.courses.vocabInfoText') }}</p>
|
||||
<div class="vocab-items">
|
||||
@@ -52,7 +112,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Hinweis wenn keine Vokabeln vorhanden -->
|
||||
<div v-else-if="lesson" class="no-vocab-info">
|
||||
<div v-else-if="lesson && (!importantVocab || importantVocab.length === 0)" class="no-vocab-info">
|
||||
<p>{{ $t('socialnetwork.vocab.courses.noVocabInfo') }}</p>
|
||||
</div>
|
||||
|
||||
@@ -188,7 +248,17 @@ export default {
|
||||
lesson: null,
|
||||
exerciseAnswers: {},
|
||||
exerciseResults: {},
|
||||
activeTab: 'learn' // Standardmäßig "Lernen"-Tab
|
||||
activeTab: 'learn', // Standardmäßig "Lernen"-Tab
|
||||
// Vokabeltrainer
|
||||
vocabTrainerActive: false,
|
||||
vocabTrainerPool: [],
|
||||
vocabTrainerCorrect: 0,
|
||||
vocabTrainerWrong: 0,
|
||||
currentVocabQuestion: null,
|
||||
vocabTrainerAnswer: '',
|
||||
vocabTrainerAnswered: false,
|
||||
vocabTrainerLastCorrect: false,
|
||||
vocabTrainerDirection: 'L2R' // L2R: learning->reference, R2L: reference->learning
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -196,6 +266,32 @@ export default {
|
||||
hasExercises() {
|
||||
return this.lesson && this.lesson.grammarExercises && this.lesson.grammarExercises.length > 0;
|
||||
},
|
||||
grammarExplanations() {
|
||||
// Extrahiere Grammatik-Erklärungen aus den Übungen
|
||||
try {
|
||||
if (!this.lesson || !this.lesson.grammarExercises || !Array.isArray(this.lesson.grammarExercises)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const explanations = [];
|
||||
const seen = new Set();
|
||||
|
||||
this.lesson.grammarExercises.forEach(exercise => {
|
||||
if (exercise.explanation && !seen.has(exercise.explanation)) {
|
||||
seen.add(exercise.explanation);
|
||||
explanations.push({
|
||||
title: exercise.title || '',
|
||||
text: exercise.explanation
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return explanations;
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Extrahieren der Grammatik-Erklärungen:', e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
importantVocab() {
|
||||
// Extrahiere wichtige Begriffe aus den Übungen
|
||||
try {
|
||||
@@ -408,6 +504,58 @@ export default {
|
||||
},
|
||||
back() {
|
||||
this.$router.push(`/socialnetwork/vocab/courses/${this.courseId}`);
|
||||
},
|
||||
// Vokabeltrainer-Methoden
|
||||
startVocabTrainer() {
|
||||
if (!this.importantVocab || this.importantVocab.length === 0) return;
|
||||
this.vocabTrainerActive = true;
|
||||
this.vocabTrainerPool = [...this.importantVocab];
|
||||
this.vocabTrainerCorrect = 0;
|
||||
this.vocabTrainerWrong = 0;
|
||||
this.nextVocabQuestion();
|
||||
this.$nextTick(() => {
|
||||
this.$refs.vocabInput?.focus?.();
|
||||
});
|
||||
},
|
||||
stopVocabTrainer() {
|
||||
this.vocabTrainerActive = false;
|
||||
this.currentVocabQuestion = null;
|
||||
this.vocabTrainerAnswer = '';
|
||||
this.vocabTrainerAnswered = false;
|
||||
},
|
||||
nextVocabQuestion() {
|
||||
if (!this.vocabTrainerPool || this.vocabTrainerPool.length === 0) {
|
||||
this.currentVocabQuestion = null;
|
||||
return;
|
||||
}
|
||||
const randomIndex = Math.floor(Math.random() * this.vocabTrainerPool.length);
|
||||
const vocab = this.vocabTrainerPool[randomIndex];
|
||||
this.vocabTrainerDirection = Math.random() < 0.5 ? 'L2R' : 'R2L';
|
||||
this.currentVocabQuestion = {
|
||||
vocab: vocab,
|
||||
prompt: this.vocabTrainerDirection === 'L2R' ? vocab.learning : vocab.reference,
|
||||
answer: this.vocabTrainerDirection === 'L2R' ? vocab.reference : vocab.learning
|
||||
};
|
||||
this.vocabTrainerAnswer = '';
|
||||
this.vocabTrainerAnswered = false;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.vocabInput?.focus?.();
|
||||
});
|
||||
},
|
||||
normalizeVocab(s) {
|
||||
return String(s || '').trim().toLowerCase().replace(/\s+/g, ' ');
|
||||
},
|
||||
checkVocabAnswer() {
|
||||
if (!this.currentVocabQuestion || !this.vocabTrainerAnswer.trim()) return;
|
||||
const userAnswer = this.normalizeVocab(this.vocabTrainerAnswer);
|
||||
const correctAnswer = this.normalizeVocab(this.currentVocabQuestion.answer);
|
||||
this.vocabTrainerLastCorrect = userAnswer === correctAnswer;
|
||||
if (this.vocabTrainerLastCorrect) {
|
||||
this.vocabTrainerCorrect++;
|
||||
} else {
|
||||
this.vocabTrainerWrong++;
|
||||
}
|
||||
this.vocabTrainerAnswered = true;
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
@@ -673,6 +821,201 @@ export default {
|
||||
|
||||
.separator {
|
||||
color: #999;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.lesson-description-box {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.lesson-description-box h4 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.grammar-explanations {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grammar-explanations h4 {
|
||||
margin-top: 0;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.grammar-explanation-item {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.grammar-explanation-item strong {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.vocab-trainer-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.vocab-trainer-section h4 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.vocab-trainer-start {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-start-trainer {
|
||||
padding: 10px 20px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn-start-trainer:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
.vocab-trainer-stats {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-stop-trainer {
|
||||
padding: 5px 15px;
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.btn-stop-trainer:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.vocab-question {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.vocab-prompt {
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.vocab-direction {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.vocab-word {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.vocab-answer-area {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.vocab-input {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.vocab-answer-area button {
|
||||
padding: 10px 20px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.vocab-answer-area button:hover:not(:disabled) {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
.vocab-answer-area button:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.vocab-feedback {
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.vocab-feedback.correct {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.vocab-feedback.wrong {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.vocab-next {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.vocab-next button {
|
||||
padding: 10px 20px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.vocab-next button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.vocab-info-text {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.no-vocab-info {
|
||||
|
||||
Reference in New Issue
Block a user