question timers, little fixes to nav/store

This commit is contained in:
NetMan 2025-12-15 20:14:54 +01:00
parent 63121da4b7
commit 97b8d5dab9
12 changed files with 242 additions and 107 deletions

View file

@ -23,8 +23,8 @@ You also need the exam media files from the (Ministry of Infrasture)[https://www
- [x] beautify website (good for now) - [x] beautify website (good for now)
- [x] <b>Fixed?</b> Needs testing, but should be fine question-mark? - <b>fix pinia middleware between pages, MAJOR ISSUE - finishing exam sometimes redirects to homepage instead of results, help appreciated</b> - [x] <b>Fixed?</b> Needs testing, but should be fine question-mark? - <b>fix pinia middleware between pages, MAJOR ISSUE - finishing exam sometimes redirects to homepage instead of results, help appreciated</b>
- [x] (scrapped - lazy loading) - [x] (scrapped - lazy loading)
- [x] question timers
- [ ] exam (& results?) warning leave message on exit and timer end (and definitely on refresh) - [ ] exam (& results?) warning leave message on exit and timer end (and definitely on refresh)
- [ ] question timers
- [ ] i18n - pl, en, de, ua (not all questions are available in ua, api handle) - [ ] i18n - pl, en, de, ua (not all questions are available in ua, api handle)
- UI i18n - UI i18n
- db: examstore add language field, api handle languages - db: examstore add language field, api handle languages

View file

@ -6,8 +6,11 @@ const cdnUrl = runtimeConfig.public.cdn_url;
const route = useRoute(); const route = useRoute();
const emit = defineEmits(['mediaload']);
const props = defineProps<{ const props = defineProps<{
mediaPath: string | null | undefined; mediaPath: string | null | undefined;
phase: string;
}>(); }>();
const media = computed(() => { const media = computed(() => {
@ -21,6 +24,21 @@ const media = computed(() => {
} }
return { name: dotSplit?.join('.'), type }; return { name: dotSplit?.join('.'), type };
}); });
const video = ref();
function onVideoLoad() {
if (route.path === '/exam') {
video.value.play();
let duration = video.value.duration;
if (isNaN(duration) || duration == Infinity) {
duration = 0;
}
setTimeout(() => {
emit('mediaload');
}, duration * 1000);
}
}
</script> </script>
<template> <template>
@ -28,23 +46,26 @@ const media = computed(() => {
class="select-none flex-auto w-full *:object-contain *:w-full *:h-full *:max-h-full relative *:absolute" class="select-none flex-auto w-full *:object-contain *:w-full *:h-full *:max-h-full relative *:absolute"
:class="route.path === '/exam' ? 'z-[-1]' : ''" :class="route.path === '/exam' ? 'z-[-1]' : ''"
> >
<img v-if="phase == 'set-basic'" src="/placeholder.svg" alt="placeholder" />
<NuxtImg <NuxtImg
v-if="media.type === 'image'" v-else-if="media.type === 'image'"
:key="`${mediaPath}-image`" :key="`${mediaPath}-image`"
provider="selfhost" provider="selfhost"
:src="'/' + mediaPath" :src="'/' + mediaPath"
:alt="mediaPath ?? ''" :alt="mediaPath ?? ''"
@load="emit('mediaload')"
/> />
<video <video
v-else-if="media.type === 'video'" v-else-if="media.type === 'video'"
:key="`${mediaPath}-video`" :key="`${mediaPath}-video`"
:autoplay="route.path === '/exam'" ref="video"
:controls="route.path === '/result'" :controls="route.path === '/result'"
@canplaythrough="onVideoLoad()"
> >
<source :src="joinURL(cdnUrl, media.name + '.mp4')" type="video/mp4" /> <source :src="joinURL(cdnUrl, media.name + '.mp4')" type="video/mp4" />
</video> </video>
<span v-else class="text-5xl font-bold flex items-center justify-center" <span v-else class="text-5xl font-bold flex items-center justify-center"
>Brak mediów</span >Pytanie bez wizualizacji</span
> >
</div> </div>
</template> </template>

View file

@ -5,7 +5,7 @@ onMounted(() => {
myModal.value?.showModal(); myModal.value?.showModal();
}); });
defineEmits(['again']); defineEmits(['again', 'home']);
</script> </script>
<template> <template>
@ -21,7 +21,9 @@ defineEmits(['again']);
<div class="*:inline">Punkty: <slot name="points" /> / 74</div> <div class="*:inline">Punkty: <slot name="points" /> / 74</div>
<div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div> <div class="*:inline">Wynik: <slot name="resultTrueFalse" /></div>
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<NuxtLink to="/" class="btn btn-soft">Wróć na stronę główną</NuxtLink> <div class="btn btn-soft" @click="$emit('home')">
Wróć na stronę główną
</div>
<div class="btn btn-outline" @click="$emit('again')"> <div class="btn btn-outline" @click="$emit('again')">
Rozpocznij jeszcze raz Rozpocznij jeszcze raz
</div> </div>

View file

@ -4,11 +4,15 @@ defineProps<{
countAdvanced: number; countAdvanced: number;
now: string | null | undefined; now: string | null | undefined;
ending: boolean; ending: boolean;
setBasic: boolean;
time: number;
phase: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
endExam: []; endExam: [];
nextQuestion: []; nextQuestion: [];
nextTime: [];
}>(); }>();
</script> </script>
@ -36,38 +40,45 @@ const emit = defineEmits<{
</CurrentQuestionCount> </CurrentQuestionCount>
</div> </div>
<div class="text-center text-xl flex flex-col gap-2"> <div
v-if="phase == 'set-basic'"
class="text-center text-xl flex flex-col gap-2"
>
<span>Czas na zapoznanie się z pytaniem</span> <span>Czas na zapoznanie się z pytaniem</span>
<div class="flex flex-row items-stretch gap-2"> <div class="flex flex-row items-stretch gap-2">
<div class="btn btn-primary">START</div> <div class="btn btn-primary" @click="emit('nextTime')">START</div>
<div class="h-full flex-1 relative"> <div class="h-full flex-1 relative">
<progress <progress
class="progress progress-warning w-full h-full" class="progress progress-warning w-full h-full"
value="50" :value="time"
max="100" max="20"
/> ></progress>
<span class="block set-translate z-10 text-black text-2xl">20s</span> <span class="block set-translate z-10 text-black text-2xl">
{{ time >= 0 ? time : 0 }}s
</span>
</div> </div>
</div> </div>
</div> </div>
<div class="text-center text-xl flex flex-col gap-2"> <div v-else class="text-center text-xl flex flex-col gap-2">
<span>Czas na udzielenie odpowiedzi</span> <span>Czas na udzielenie odpowiedzi</span>
<div class="h-9 relative"> <div class="h-9 relative">
<progress <progress
class="progress progress-warning w-full h-full" class="progress progress-warning w-full h-full"
value="50" :value="time"
max="100" :max="phase == 'start-basic' ? 15 : 45"
/> ></progress>
<span class="block set-translate z-10 text-black text-2xl">15s</span> <span class="block set-translate z-10 text-black text-2xl">
{{ time >= 0 ? time : 0 }}s
</span>
</div> </div>
</div> </div>
<div class="flex-1" /> <div class="flex-1"></div>
<button <button
class="btn btn-warning btn-xl" class="btn btn-warning btn-xl"
:disabled="ending" :disabled="ending || setBasic"
@click="emit('nextQuestion')" @click="emit('nextQuestion')"
> >
Następne pytanie Następne pytanie
@ -76,16 +87,12 @@ const emit = defineEmits<{
</template> </template>
<style> <style>
/*.progressive { .progressive {
animation: progressZapoznanie 20s linear; width: calc(100% / v-bind('time'));
transition: all 1s linear;
} }
@keyframes progressZapoznanie { progress[value]::-webkit-progress-value {
0% { transition: width 0.5s;
width: 0;
} }
100% {
width: 100%;
}
}*/
</style> </style>

View file

@ -12,6 +12,7 @@ const emit = defineEmits<{
changeNow: [value: string]; changeNow: [value: string];
changeCount: [num: number]; changeCount: [num: number];
again: []; again: [];
home: [];
}>(); }>();
</script> </script>
@ -19,9 +20,9 @@ const emit = defineEmits<{
<div <div
class="flex flex-col items-stretch p-4 gap-6 border-l border-base-300 bg-base-100" class="flex flex-col items-stretch p-4 gap-6 border-l border-base-300 bg-base-100"
> >
<NuxtLink to="/" class="btn btn-warning btn-xl"> <div class="btn btn-warning btn-xl" @click="$emit('home')">
Wróć na stronę główną Wróć na stronę główną
</NuxtLink> </div>
<button class="btn btn-info btn-lg" @click="emit('changeNow', 'basic')"> <button class="btn btn-info btn-lg" @click="emit('changeNow', 'basic')">
Pytania podstawowe Pytania podstawowe
@ -38,7 +39,9 @@ const emit = defineEmits<{
class="btn btn-md" class="btn btn-md"
name="chooser" name="chooser"
:class="`${ :class="`${
result.basic[num].question?.correct_answer === result.basic[num].chosen_answer == ''
? 'btn-warning'
: result.basic[num].question?.correct_answer ===
result.basic[num].chosen_answer result.basic[num].chosen_answer
? 'btn-success' ? 'btn-success'
: 'btn-error' : 'btn-error'
@ -66,7 +69,9 @@ const emit = defineEmits<{
class="btn btn-md" class="btn btn-md"
name="chooser" name="chooser"
:class="`${ :class="`${
result.advanced[num].question?.correct_answer === result.advanced[num].chosen_answer == ''
? 'btn-warning'
: result.advanced[num].question?.correct_answer ===
result.advanced[num].chosen_answer result.advanced[num].chosen_answer
? 'btn-success' ? 'btn-success'
: 'btn-error' : 'btn-error'

View file

@ -19,6 +19,7 @@
"@nuxt/image": "1.10.0", "@nuxt/image": "1.10.0",
"@nuxtjs/tailwindcss": "6.13.2", "@nuxtjs/tailwindcss": "6.13.2",
"@pinia/nuxt": "0.11.0", "@pinia/nuxt": "0.11.0",
"@vueuse/core": "^14.1.0",
"array-shuffle": "^3.0.0", "array-shuffle": "^3.0.0",
"daisyui": "^5.0.27", "daisyui": "^5.0.27",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",

View file

@ -8,10 +8,15 @@ const advancedStore = useAdvancedStore();
<div class="flex flex-col gap-2 items-start m-2"> <div class="flex flex-col gap-2 items-start m-2">
<h1 class="text-2xl">Nastąpiła anomalia</h1> <h1 class="text-2xl">Nastąpiła anomalia</h1>
<br /> <br />
Kategoria: {{ examStore.category != '' ? examStore.category : '""' }} <br /> Przekierowanie z: {{ useRoute().redirectedFrom ?? '""' }} <br />
Kategoria:
{{ examStore.category != '' ? examStore.category : '""' }}
<br />
Koniec: {{ examStore.end }} <br /> Koniec: {{ examStore.end }} <br />
Pytania podstawowe: {{ basicStore.basic }} <br /> Pytania podstawowe: <code class="text-xs">{{ basicStore.basic }}</code>
Pytania specjalistyczne: {{ advancedStore.advanced }} <br /> <br />
Pytania specjalistyczne:
<code class="text-xs">{{ advancedStore.advanced }}</code> <br />
<NuxtLink to="/" class="btn btn-primary"> Wróć na stronę główną </NuxtLink> <NuxtLink to="/" class="btn btn-primary"> Wróć na stronę główną </NuxtLink>
</div> </div>
</template> </template>

View file

@ -1,21 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { import { intervalToDuration, addMinutes, addSeconds, isAfter } from 'date-fns';
intervalToDuration, import { useNow } from '@vueuse/core';
addMinutes,
addSeconds,
isEqual,
isAfter,
} from 'date-fns';
import { useExamStore } from '~/store/examStore'; import { useExamStore } from '~/store/examStore';
definePageMeta({ definePageMeta({ middleware: 'exam' });
middleware: 'exam',
});
const examStore = useExamStore(); const examStore = useExamStore();
const basicStore = useBasicStore(); const basicStore = useBasicStore();
const advancedStore = useAdvancedStore(); const advancedStore = useAdvancedStore();
// await callOnce(() => examStore.mildReset(), { mode: 'navigation' }); await callOnce(() => examStore.mildReset(), { mode: 'navigation' });
useHead({ useHead({
title: 'Pytanie 1/20', title: 'Pytanie 1/20',
@ -23,25 +16,70 @@ useHead({
const now = ref('basic'); const now = ref('basic');
const nowTime = ref(new Date()); const nowTime = useNow();
const timeEnd = addMinutes(new Date(), 25); const timeEnd = addMinutes(new Date(), 25);
const timeRemainingTotal = computed(() => const questionEnd = ref(addSeconds(new Date(), 20));
const mediaLoaded = ref(false);
const time = ref({
total: computed(() =>
intervalToDuration({ intervalToDuration({
start: nowTime.value, start: nowTime.value,
end: timeEnd, end: timeEnd,
}), }),
); ),
// const timeRemainingQuestion - to implement question: computed(() => {
const interval = intervalToDuration({
start: nowTime.value,
end: questionEnd.value,
});
if (Object.hasOwn(interval, 'seconds') && interval.seconds != undefined) {
return interval.seconds;
} else {
return 0;
}
}),
phase: 'set-basic',
});
onMounted(() => { function changeQuestionTimeAfterNext() {
const endInterval = setInterval(() => { if (time.value.phase == 'set-basic') {
nowTime.value = addSeconds(nowTime.value, 1); time.value.phase = 'start-basic';
if (isEqual(nowTime.value, timeEnd) || isAfter(nowTime.value, timeEnd)) { questionEnd.value = addSeconds(new Date(), 15);
clearInterval(endInterval); } else if (time.value.phase == 'start-basic') {
if (now.value == 'basic') {
time.value.phase = 'set-basic';
mediaLoaded.value = false;
questionEnd.value = addSeconds(new Date(), 20);
} else {
time.value.phase = 'set-advanced';
questionEnd.value = addSeconds(new Date(), 50);
}
} else if (time.value.phase == 'set-advanced') {
questionEnd.value = addSeconds(new Date(), 50);
}
}
function onMediaLoad() {
mediaLoaded.value = true;
}
function clickNext() {
if (ending.value) {
endExam(); endExam();
} }
}, 999); if (time.value.phase != 'set-basic') {
next();
}
changeQuestionTimeAfterNext();
}
onMounted(() => {
watchEffect(() => {
if (isAfter(nowTime.value, timeEnd)) endExam();
});
watchEffect(() => { watchEffect(() => {
if (now.value === 'basic') if (now.value === 'basic')
@ -49,6 +87,15 @@ onMounted(() => {
if (now.value === 'advanced') if (now.value === 'advanced')
useHead({ title: `Pytanie ${countAdvanced.value + 1}/12` }); useHead({ title: `Pytanie ${countAdvanced.value + 1}/12` });
}); });
watchEffect(() => {
if (mediaLoaded.value == false && time.value.phase == 'start-basic') {
questionEnd.value = addSeconds(new Date(), 15);
}
if (time.value.question < 0 && ending.value == false) {
clickNext();
}
});
}); });
const { const {
@ -97,6 +144,7 @@ const result: Ref<ResultEndType> = ref({
}); });
function next() { function next() {
if (examStore.end == false) {
result.value[now.value as keyof ResultEndType].push({ result.value[now.value as keyof ResultEndType].push({
question: question.value, question: question.value,
chosen_answer: answer.value, chosen_answer: answer.value,
@ -104,7 +152,9 @@ function next() {
chosen_is_correct: answer.value == question.value?.correct_answer, chosen_is_correct: answer.value == question.value?.correct_answer,
}); });
answer.value = ''; answer.value = '';
} else {
console.error('next() incorrectly executed - exam has already ended.');
}
if (now.value === 'basic') { if (now.value === 'basic') {
if (countBasic.value + 1 <= 19) { if (countBasic.value + 1 <= 19) {
countBasic.value++; countBasic.value++;
@ -115,28 +165,33 @@ function next() {
} else if (now.value === 'advanced') { } else if (now.value === 'advanced') {
if (countAdvanced.value + 1 <= 11) { if (countAdvanced.value + 1 <= 11) {
countAdvanced.value++; countAdvanced.value++;
} else if (countAdvanced.value + 1 >= 12) { }
if (countAdvanced.value + 1 >= 12) {
ending.value = true; ending.value = true;
} else {
// error?
} }
} }
} }
function endExam() { function endExam() {
loading.value = true; loading.value = true;
while (ending.value == false) next(); while (ending.value == false) {
examStore.setBasic(result.value.basic); if (time.value.phase != 'set-basic') {
examStore.setAdvanced(result.value.advanced); next();
}
changeQuestionTimeAfterNext();
}
next();
basicStore.set(result.value.basic);
advancedStore.set(result.value.advanced);
examStore.setEnd(true); examStore.setEnd(true);
while (true) {
if ( if (
basicStore.basic == result.value.basic && basicStore.basic == result.value.basic &&
advancedStore.advanced == result.value.advanced && advancedStore.advanced == result.value.advanced &&
examStore.end examStore.end
) { ) {
return navigateTo(`/result`, { replace: true }); return navigateTo(`/result`, { replace: true });
} } else {
return navigateTo(`/anomaly`);
} }
} }
@ -153,9 +208,13 @@ const loading = ref(false);
<BarTop <BarTop
:points="question?.weight" :points="question?.weight"
:category="examStore.category" :category="examStore.category"
:time-remaining="timeRemainingTotal" :time-remaining="time.total"
/>
<MediaBox
:media-path="question?.media_url"
:phase="time.phase"
@mediaload="onMediaLoad()"
/> />
<MediaBox :media-path="question?.media_url" />
<QuestionBasic <QuestionBasic
v-if="now === 'basic'" v-if="now === 'basic'"
v-model="answer" v-model="answer"
@ -174,8 +233,12 @@ const loading = ref(false);
:count-advanced="countAdvanced" :count-advanced="countAdvanced"
:now="now" :now="now"
:ending="ending" :ending="ending"
@next-question="next()" :set-basic="time.phase == 'set-basic'"
:time="time.question"
:phase="time.phase"
@next-question="clickNext()"
@end-exam="endExam()" @end-exam="endExam()"
@next-time="changeQuestionTimeAfterNext()"
/> />
</div> </div>
</div> </div>

View file

@ -1,7 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import categories from '~/categories'; import categories, { opis, wiek } from '~/categories';
import { opis } from '~/categories';
import { wiek } from '~/categories';
onMounted(() => { onMounted(() => {
useHead({ useHead({
@ -12,8 +10,6 @@ onMounted(() => {
const loading = ref(false); const loading = ref(false);
const examStore = useExamStore(); const examStore = useExamStore();
const basicStore = useBasicStore();
const advancedStore = useAdvancedStore();
await callOnce(() => examStore.resetExam(), { mode: 'navigation' }); await callOnce(() => examStore.resetExam(), { mode: 'navigation' });
function setAndGo(category: string) { function setAndGo(category: string) {

View file

@ -92,13 +92,19 @@ async function again() {
await examStore.mildReset(); await examStore.mildReset();
return await navigateTo('/exam'); return await navigateTo('/exam');
} }
async function home() {
loading.value = true;
await examStore.resetExam();
return await navigateTo('/');
}
</script> </script>
<template> <template>
<div> <div>
<LoadingScreen v-if="loading" /> <LoadingScreen v-if="loading" />
<div v-else> <div v-else>
<ResultModal @again="again"> <ResultModal @again="again" @home="home">
<template #title>Egzamin teorytyczny</template> <template #title>Egzamin teorytyczny</template>
<template #category>{{ examStore.category }}</template> <template #category>{{ examStore.category }}</template>
<template #points>{{ points }}</template> <template #points>{{ points }}</template>
@ -108,7 +114,7 @@ async function again() {
<div class="grid grid-cols-4 min-h-dvh"> <div class="grid grid-cols-4 min-h-dvh">
<div class="col-span-3 flex flex-col"> <div class="col-span-3 flex flex-col">
<BarTop :points="question?.weight" :category="examStore.category" /> <BarTop :points="question?.weight" :category="examStore.category" />
<MediaBox :media-path="question?.media_url" /> <MediaBox :media-path="question?.media_url" phase="" />
<QuestionBasic <QuestionBasic
v-if="now === 'basic'" v-if="now === 'basic'"
v-model="answer" v-model="answer"
@ -134,6 +140,7 @@ async function again() {
:now="now" :now="now"
@change-now="changeNow" @change-now="changeNow"
@change-count="changeCount" @change-count="changeCount"
@home="home"
@again="again" @again="again"
> >
<template #points>{{ points }}</template> <template #points>{{ points }}</template>

34
pnpm-lock.yaml generated
View file

@ -23,6 +23,9 @@ importers:
'@pinia/nuxt': '@pinia/nuxt':
specifier: 0.11.0 specifier: 0.11.0
version: 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))) version: 0.11.0(magicast@0.3.5)(pinia@3.0.2(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)))
'@vueuse/core':
specifier: ^14.1.0
version: 14.1.0(vue@3.5.13(typescript@5.8.3))
array-shuffle: array-shuffle:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
@ -1273,6 +1276,9 @@ packages:
'@types/resolve@1.20.2': '@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
'@types/web-bluetooth@0.0.21':
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
'@types/ws@8.18.1': '@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
@ -1498,6 +1504,19 @@ packages:
'@vue/shared@3.5.13': '@vue/shared@3.5.13':
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
'@vueuse/core@14.1.0':
resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==}
peerDependencies:
vue: ^3.5.0
'@vueuse/metadata@14.1.0':
resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==}
'@vueuse/shared@14.1.0':
resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==}
peerDependencies:
vue: ^3.5.0
abbrev@3.0.1: abbrev@3.0.1:
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
engines: {node: ^18.17.0 || >=20.5.0} engines: {node: ^18.17.0 || >=20.5.0}
@ -6187,6 +6206,8 @@ snapshots:
'@types/resolve@1.20.2': {} '@types/resolve@1.20.2': {}
'@types/web-bluetooth@0.0.21': {}
'@types/ws@8.18.1': '@types/ws@8.18.1':
dependencies: dependencies:
'@types/node': 22.14.1 '@types/node': 22.14.1
@ -6484,6 +6505,19 @@ snapshots:
'@vue/shared@3.5.13': {} '@vue/shared@3.5.13': {}
'@vueuse/core@14.1.0(vue@3.5.13(typescript@5.8.3))':
dependencies:
'@types/web-bluetooth': 0.0.21
'@vueuse/metadata': 14.1.0
'@vueuse/shared': 14.1.0(vue@3.5.13(typescript@5.8.3))
vue: 3.5.13(typescript@5.8.3)
'@vueuse/metadata@14.1.0': {}
'@vueuse/shared@14.1.0(vue@3.5.13(typescript@5.8.3))':
dependencies:
vue: 3.5.13(typescript@5.8.3)
abbrev@3.0.1: {} abbrev@3.0.1: {}
abort-controller@3.0.0: abort-controller@3.0.0:

View file

@ -38,16 +38,10 @@ export const useExamStore = defineStore('examStore', {
async setEnd(end: boolean) { async setEnd(end: boolean) {
this.end = end; this.end = end;
}, },
async setBasic(basic: ResultType[]) {
useBasicStore().set(basic);
},
async setAdvanced(advanced: ResultType[]) {
useAdvancedStore().set(advanced);
},
async mildReset() { async mildReset() {
this.end = false; this.end = false;
this.setBasic([]); useBasicStore().set([]);
this.setAdvanced([]); useAdvancedStore().set([]);
}, },
async resetExam() { async resetExam() {
this.category = ''; this.category = '';