update
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { computed, reactive, ref, watch, nextTick, onMounted, onUnmounted } from 'vue'
|
||||
import { fetchGuideContent, chatGuide, fetchBausteinLernstand } from '../api.js'
|
||||
import { renderMarkdown } from '../markdown.js'
|
||||
import { stufeFuer, schwelle } from '../stufen.js'
|
||||
import { useChat } from '../composables/useChat.js'
|
||||
import BausteinPanel from './BausteinPanel.vue'
|
||||
import BausteinFokus from './BausteinFokus.vue'
|
||||
@@ -83,10 +84,15 @@ async function loadContent() {
|
||||
}
|
||||
|
||||
// --- Baustein-Lernen: Prüfungs-Stand pro Baustein-Titel (lernstand oben deklariert) ---
|
||||
function stufeVon(title) {
|
||||
const l = lernstand.value[title]
|
||||
return l ? stufeFuer(l.gute_antworten || 0, l.cap || 10) : null
|
||||
}
|
||||
function onBausteinStatus(baustein, status) {
|
||||
const warAbsolviert = lernstand.value[baustein]?.absolviert
|
||||
const alt = stufeVon(baustein)?.key || null
|
||||
lernstand.value = { ...lernstand.value, [baustein]: status }
|
||||
if (status.absolviert && !warAbsolviert) emit('progressChanged') // Locks/Stats neu laden
|
||||
const neu = stufeFuer(status.gute_antworten || 0, status.cap || 10)?.key || null
|
||||
if (neu !== alt) emit('progressChanged') // Stufenwechsel → Locks/Stats neu laden
|
||||
}
|
||||
|
||||
// --- Vollbild-Fokus: ein Baustein groß, Guide links + Prüfung rechts ---
|
||||
@@ -116,20 +122,25 @@ function fokusNext() {
|
||||
for (let i = fokusIndex.value + 1; i < bausteine.value.length; i++) if (istPruefbar(bausteine.value[i])) { fokusIndex.value = i; return }
|
||||
}
|
||||
|
||||
// Erfahrungsleiste: Lernstand über alle Bausteine des Guides (sticky, absolviert ⊇ verstanden ⊇ gemeistert).
|
||||
// Erfahrungsleiste: kumulativer Stufen-Stand über alle Bausteine (gold ⊆ lila ⊆ blau ⊆ grün).
|
||||
const fortschritt = computed(() => {
|
||||
let a = 0, v = 0, g = 0
|
||||
const z = { total: bausteine.value.length, anfaenger: 0, fortgeschritten: 0, experte: 0, meister: 0 }
|
||||
for (const s of bausteine.value) {
|
||||
const l = lernstand.value[s.title] || {}
|
||||
if (l.absolviert) a++
|
||||
if (l.verstanden) v++
|
||||
if (l.gemeistert) g++
|
||||
const l = lernstand.value[s.title]
|
||||
if (!l) continue
|
||||
const sc = l.gute_antworten || 0, cp = l.cap || 10
|
||||
if (sc >= schwelle(0.2, cp)) z.anfaenger++
|
||||
if (sc >= schwelle(0.4, cp)) z.fortgeschritten++
|
||||
if (sc >= schwelle(0.6, cp)) z.experte++
|
||||
if (sc >= schwelle(1.0, cp)) z.meister++
|
||||
}
|
||||
return { total: bausteine.value.length, absolviert: a, verstanden: v, gemeistert: g }
|
||||
return z
|
||||
})
|
||||
|
||||
// Prüfung läuft voll 0–30, sobald Guide ODER FullGuide fertig ist (gleicher Baustein-Score).
|
||||
const cap = computed(() => ((props.doneByFormat?.Guide || props.doneByFormat?.FullGuide) ? 30 : 6))
|
||||
// cap je Baustein = 2×relevante Subbausteine (vom Backend in lernstand[title].cap geliefert).
|
||||
function capVon(title) {
|
||||
return lernstand.value[title]?.cap || 10
|
||||
}
|
||||
|
||||
// Section prüfbar? Rest nie. Sonst prüfbar, außer das Feld ist explizit false (FullGuide-Rand).
|
||||
// Fehlt das Feld (alte Guides ohne `pruefbar`) → prüfbar, damit Bestands-Guides weiter funktionieren.
|
||||
@@ -223,7 +234,7 @@ function extractContext() {
|
||||
<header class="guide-head">
|
||||
<h1>{{ previewGuide.topic }}</h1>
|
||||
<span class="guide-format">{{ previewGuide.format }}</span>
|
||||
<span v-if="themaAbgeschlossen" class="thema-done" title="Alle Bausteine gemeistert (18/18)">✓ Thema abgeschlossen</span>
|
||||
<span v-if="themaAbgeschlossen" class="thema-done" title="Alle Bausteine auf Meister">✓ Thema abgeschlossen</span>
|
||||
</header>
|
||||
|
||||
<section
|
||||
@@ -238,14 +249,13 @@ function extractContext() {
|
||||
v-for="s in ch.sections"
|
||||
:key="s.num"
|
||||
:data-num="s.num"
|
||||
:class="['section-card', lernstand[s.title]?.gemeistert ? 'gemeistert' : (lernstand[s.title]?.verstanden ? 'verstanden' : (lernstand[s.title]?.absolviert ? 'absolviert' : ''))]"
|
||||
class="section-card"
|
||||
:style="stufeVon(s.title) ? { borderLeftColor: stufeVon(s.title).farbe } : {}"
|
||||
>
|
||||
<h3 :class="{ 'baustein-klick': istPruefbar(s) }" @click="istPruefbar(s) && openFokus(s, 'pruefung')">
|
||||
{{ s.title }}
|
||||
<template v-if="istPruefbar(s)">
|
||||
<span v-if="lernstand[s.title]?.gemeistert" class="baustein-done gemeistert" title="Gemeistert (25/25)">✓✓✓ Gemeistert</span>
|
||||
<span v-else-if="lernstand[s.title]?.verstanden" class="baustein-done verstanden" title="Vollständig verstanden (10/10)">✓✓ Verstanden</span>
|
||||
<span v-else-if="lernstand[s.title]?.absolviert" class="baustein-done" title="Prüfung bestanden">✓ Absolviert</span>
|
||||
<template v-if="istPruefbar(s) && stufeVon(s.title)">
|
||||
<span class="baustein-done" :style="{ color: stufeVon(s.title).farbe, borderColor: stufeVon(s.title).farbe }" :title="`${stufeVon(s.title).label} (${capVon(s.title)})`">{{ stufeVon(s.title).kurz }} {{ stufeVon(s.title).label }}</span>
|
||||
</template>
|
||||
</h3>
|
||||
<div v-if="sichtbar[s.num]" class="section-body markdown" v-html="htmlFor(s)"></div>
|
||||
@@ -255,7 +265,7 @@ function extractContext() {
|
||||
mode="trigger"
|
||||
:baustein="s.title"
|
||||
:status="lernstand[s.title]"
|
||||
:cap="cap"
|
||||
:cap="capVon(s.title)"
|
||||
:topic="previewGuide.topic"
|
||||
@open-fokus="(tab) => openFokus(s, tab)"
|
||||
/>
|
||||
@@ -280,7 +290,7 @@ function extractContext() {
|
||||
:provider="provider"
|
||||
:fortschritt="fortschritt"
|
||||
:status="lernstand[fokusBaustein.title]"
|
||||
:cap="cap"
|
||||
:cap="capVon(fokusBaustein.title)"
|
||||
:tab="fokusTab"
|
||||
:ansicht="ansichtModus"
|
||||
:has-prev="fokusIndex > 0"
|
||||
|
||||
Reference in New Issue
Block a user