add new lesson mode and started code refraction
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
<template lang="pug">
|
||||
v-container.fill-height.justify-center.pa-4
|
||||
v-fade-transition(mode="out-in")
|
||||
v-card.pa-6.rounded-xl.elevation-10.border-subtle.d-flex.flex-column.align-center(
|
||||
v-card.pa-6.rounded-xl.elevation-10.d-flex.flex-column.align-center.review-card(
|
||||
v-if="currentItem"
|
||||
color="#1e1e24"
|
||||
width="100%"
|
||||
max-width="420"
|
||||
)
|
||||
.d-flex.align-center.w-100.mb-6
|
||||
v-btn.mr-2(
|
||||
@@ -30,24 +27,25 @@
|
||||
.text-h3.font-weight-bold.text-white.text-shadow
|
||||
| {{ currentItem.meaning }}
|
||||
|
||||
.canvas-wrapper.mb-2
|
||||
.review-canvas-area
|
||||
KanjiCanvas(
|
||||
ref="kanjiCanvasRef"
|
||||
:char="currentItem.char"
|
||||
@complete="handleComplete"
|
||||
@mistake="handleMistake"
|
||||
)
|
||||
|
||||
transition(name="scale")
|
||||
v-btn.next-fab.glow-btn.text-black(
|
||||
v-if="showNext"
|
||||
@click="next"
|
||||
color="#00cec9"
|
||||
color="primary"
|
||||
icon="mdi-arrow-right"
|
||||
size="large"
|
||||
elevation="8"
|
||||
)
|
||||
|
||||
.mb-4
|
||||
.mb-4.d-flex.gap-2
|
||||
v-btn.text-caption.font-weight-bold.opacity-80(
|
||||
variant="text"
|
||||
color="amber-lighten-1"
|
||||
@@ -55,7 +53,16 @@
|
||||
size="small"
|
||||
:disabled="showNext || statusCode === 'hint'"
|
||||
@click="triggerHint"
|
||||
) Show Hint
|
||||
) {{ $t('review.showHint') }}
|
||||
|
||||
v-btn.text-caption.font-weight-bold.opacity-80(
|
||||
variant="text"
|
||||
color="purple-lighten-2"
|
||||
prepend-icon="mdi-school-outline"
|
||||
size="small"
|
||||
:disabled="showNext"
|
||||
@click="redoLesson"
|
||||
) {{ $t('review.redoLesson') }}
|
||||
|
||||
v-sheet.d-flex.align-center.justify-center(
|
||||
width="100%"
|
||||
@@ -68,29 +75,25 @@
|
||||
:class="getStatusClass(statusCode)"
|
||||
) {{ $t('review.' + statusCode) }}
|
||||
|
||||
v-progress-linear.mt-4(
|
||||
v-progress-linear.mt-4.progress-bar(
|
||||
v-model="progressPercent"
|
||||
color="#00cec9"
|
||||
color="primary"
|
||||
height="4"
|
||||
rounded
|
||||
style="opacity: 0.3;"
|
||||
)
|
||||
|
||||
v-card.pa-8.rounded-xl.elevation-10.border-subtle.text-center(
|
||||
v-card.pa-8.rounded-xl.elevation-10.text-center.review-card(
|
||||
v-else-if="sessionTotal > 0 && store.queue.length === 0"
|
||||
color="#1e1e24"
|
||||
width="100%"
|
||||
max-width="400"
|
||||
)
|
||||
.mb-6
|
||||
v-avatar(color="rgba(0, 206, 201, 0.1)" size="80")
|
||||
v-icon(size="40" color="#00cec9") mdi-trophy
|
||||
v-icon(size="40" color="primary") mdi-trophy
|
||||
|
||||
.text-h4.font-weight-bold.mb-2 {{ $t('review.sessionComplete') }}
|
||||
.text-body-1.text-grey.mb-8 {{ $t('review.levelup') }}
|
||||
|
||||
v-row.mb-6
|
||||
v-col.border-r.border-grey-darken-3(cols="6")
|
||||
v-col.border-r.border-subtle(cols="6")
|
||||
.text-h3.font-weight-bold.text-white {{ accuracy }}%
|
||||
.text-caption.text-grey.text-uppercase.mt-1 {{ $t('stats.accuracy') }}
|
||||
v-col(cols="6")
|
||||
@@ -100,7 +103,7 @@
|
||||
v-btn.text-black.font-weight-bold.glow-btn(
|
||||
to="/dashboard"
|
||||
block
|
||||
color="#00cec9"
|
||||
color="primary"
|
||||
height="50"
|
||||
rounded="lg"
|
||||
) {{ $t('review.back') }}
|
||||
@@ -111,19 +114,23 @@
|
||||
.text-grey.mb-6 {{ $t('review.noReviews') }}
|
||||
v-btn.font-weight-bold(
|
||||
to="/dashboard"
|
||||
color="#00cec9"
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
) {{ $t('review.viewCollection') }}
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed, watch } from 'vue';
|
||||
/* eslint-disable no-unused-vars */
|
||||
import {
|
||||
ref, onMounted, computed, watch,
|
||||
} from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useAppStore } from '@/stores/appStore';
|
||||
import { useRoute } from 'vue-router';
|
||||
import KanjiCanvas from '@/components/kanji/KanjiCanvas.vue';
|
||||
|
||||
const store = useAppStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const currentItem = ref(null);
|
||||
const statusCode = ref('draw');
|
||||
@@ -136,103 +143,113 @@ const sessionDone = ref(0);
|
||||
const sessionCorrect = ref(0);
|
||||
|
||||
const accuracy = computed(() => {
|
||||
if (sessionDone.value === 0) return 100;
|
||||
return Math.round((sessionCorrect.value / sessionDone.value) * 100);
|
||||
if (sessionDone.value === 0) return 100;
|
||||
return Math.round((sessionCorrect.value / sessionDone.value) * 100);
|
||||
});
|
||||
|
||||
const progressPercent = computed(() => {
|
||||
if (sessionTotal.value === 0) return 0;
|
||||
return (sessionDone.value / sessionTotal.value) * 100;
|
||||
if (sessionTotal.value === 0) return 0;
|
||||
return (sessionDone.value / sessionTotal.value) * 100;
|
||||
});
|
||||
|
||||
function loadNext() {
|
||||
if (store.queue.length === 0) {
|
||||
currentItem.value = null;
|
||||
return;
|
||||
}
|
||||
const idx = Math.floor(Math.random() * store.queue.length);
|
||||
currentItem.value = store.queue[idx];
|
||||
|
||||
statusCode.value = 'draw';
|
||||
showNext.value = false;
|
||||
isFailure.value = false;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const mode = route.query.mode || 'shuffled';
|
||||
await store.fetchQueue(mode);
|
||||
const mode = route.query.mode || 'shuffled';
|
||||
await store.fetchQueue(mode);
|
||||
|
||||
if (sessionTotal.value === 0 && store.queue.length > 0) {
|
||||
sessionTotal.value = store.queue.length;
|
||||
}
|
||||
loadNext();
|
||||
});
|
||||
|
||||
watch(() => store.batchSize, async () => {
|
||||
resetSession();
|
||||
if (sessionTotal.value === 0 && store.queue.length > 0) {
|
||||
sessionTotal.value = store.queue.length;
|
||||
}
|
||||
loadNext();
|
||||
});
|
||||
|
||||
async function resetSession() {
|
||||
sessionDone.value = 0;
|
||||
sessionCorrect.value = 0;
|
||||
sessionTotal.value = 0;
|
||||
currentItem.value = null;
|
||||
const mode = route.query.mode || 'shuffled';
|
||||
await store.fetchQueue(mode);
|
||||
if (store.queue.length > 0) sessionTotal.value = store.queue.length;
|
||||
loadNext();
|
||||
sessionDone.value = 0;
|
||||
sessionCorrect.value = 0;
|
||||
sessionTotal.value = 0;
|
||||
currentItem.value = null;
|
||||
const mode = route.query.mode || 'shuffled';
|
||||
await store.fetchQueue(mode);
|
||||
if (store.queue.length > 0) sessionTotal.value = store.queue.length;
|
||||
loadNext();
|
||||
}
|
||||
|
||||
function loadNext() {
|
||||
if (store.queue.length === 0) {
|
||||
currentItem.value = null;
|
||||
return;
|
||||
}
|
||||
const idx = Math.floor(Math.random() * store.queue.length);
|
||||
currentItem.value = store.queue[idx];
|
||||
|
||||
statusCode.value = "draw";
|
||||
showNext.value = false;
|
||||
isFailure.value = false;
|
||||
}
|
||||
watch(() => store.batchSize, async () => {
|
||||
resetSession();
|
||||
});
|
||||
|
||||
function triggerHint() {
|
||||
if (!kanjiCanvasRef.value) return;
|
||||
if (!kanjiCanvasRef.value) return;
|
||||
|
||||
isFailure.value = true;
|
||||
statusCode.value = "hint";
|
||||
isFailure.value = true;
|
||||
statusCode.value = 'hint';
|
||||
|
||||
kanjiCanvasRef.value.drawGuide(true);
|
||||
kanjiCanvasRef.value.drawGuide(true);
|
||||
}
|
||||
|
||||
function handleMistake(isHint) {
|
||||
if (isHint) {
|
||||
isFailure.value = true;
|
||||
statusCode.value = "hint";
|
||||
} else {
|
||||
statusCode.value = "tryAgain";
|
||||
}
|
||||
if (isHint) {
|
||||
isFailure.value = true;
|
||||
statusCode.value = 'hint';
|
||||
} else {
|
||||
statusCode.value = 'tryAgain';
|
||||
}
|
||||
}
|
||||
|
||||
function handleComplete() {
|
||||
statusCode.value = "correct";
|
||||
showNext.value = true;
|
||||
statusCode.value = 'correct';
|
||||
showNext.value = true;
|
||||
}
|
||||
|
||||
async function next() {
|
||||
if (!currentItem.value) return;
|
||||
if (!currentItem.value) return;
|
||||
|
||||
await store.submitReview(currentItem.value.wkSubjectId, !isFailure.value);
|
||||
await store.submitReview(currentItem.value.wkSubjectId, !isFailure.value);
|
||||
|
||||
sessionDone.value++;
|
||||
if (!isFailure.value) sessionCorrect.value++;
|
||||
const index = store.queue.findIndex(i => i._id === currentItem.value._id);
|
||||
if (index !== -1) {
|
||||
store.queue.splice(index, 1);
|
||||
}
|
||||
sessionDone.value += 1;
|
||||
if (!isFailure.value) sessionCorrect.value += 1;
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const index = store.queue.findIndex((i) => i._id === currentItem.value._id);
|
||||
if (index !== -1) {
|
||||
store.queue.splice(index, 1);
|
||||
}
|
||||
|
||||
loadNext();
|
||||
loadNext();
|
||||
}
|
||||
|
||||
function redoLesson() {
|
||||
if (!currentItem.value) return;
|
||||
router.push({
|
||||
path: '/lesson',
|
||||
query: { subjectId: currentItem.value.wkSubjectId },
|
||||
state: { item: JSON.parse(JSON.stringify(currentItem.value)) },
|
||||
});
|
||||
}
|
||||
|
||||
const getSRSColor = (lvl) => {
|
||||
const colors = { 1: '#ff7675', 2: '#fdcb6e', 3: '#55efc4', 4: '#0984e3', 5: '#a29bfe', 6: '#6c5ce7' };
|
||||
return colors[lvl] || 'grey';
|
||||
const colors = {
|
||||
1: '#ff7675', 2: '#fdcb6e', 3: '#55efc4', 4: '#0984e3', 5: '#a29bfe', 6: '#6c5ce7',
|
||||
};
|
||||
return colors[lvl] || 'grey';
|
||||
};
|
||||
|
||||
const getStatusClass = (status) => {
|
||||
switch (status) {
|
||||
case 'hint': return 'text-red-lighten-1';
|
||||
case 'correct': return 'text-teal-accent-3';
|
||||
default: return 'text-grey-lighten-1';
|
||||
}
|
||||
switch (status) {
|
||||
case 'hint': return 'text-red-lighten-1';
|
||||
case 'correct': return 'text-teal-accent-3';
|
||||
default: return 'text-grey-lighten-1';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" src="@/styles/pages/_review.scss" scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user