<script lang="ts" setup>
|
import type { SelfTestPaper, SelfTestQuestionItem } from './types';
|
|
import { computed, onMounted, reactive, ref } from 'vue';
|
import { useRouter } from 'vue-router';
|
|
import { useMessage } from '@jnpf/hooks';
|
|
import { labelOfType } from '#/views/x/tms/question/constants';
|
|
defineOptions({ name: 'TmsSelfTestExam' });
|
|
const router = useRouter();
|
const { createMessage } = useMessage();
|
|
const paper = ref<SelfTestPaper | null>(null);
|
const currentIndex = ref(0);
|
/** questionId -> answer: single/judge 存 optionLabel;multi 存 label[] */
|
const answers = reactive<Record<string, string | string[]>>({});
|
const submitted = ref(false);
|
const scoreText = ref('');
|
|
const current = computed(() => paper.value?.questions?.[currentIndex.value]);
|
const total = computed(() => paper.value?.questions?.length || 0);
|
|
onMounted(() => {
|
const raw = sessionStorage.getItem('tms_self_test_paper');
|
if (!raw) {
|
createMessage.warning('请先设置抽题条件');
|
router.replace('/tms/selfTest');
|
return;
|
}
|
try {
|
paper.value = JSON.parse(raw);
|
} catch {
|
router.replace('/tms/selfTest');
|
}
|
});
|
|
function stripHtml(html?: string) {
|
if (!html) return '';
|
return html.replace(/<[^>]+>/g, '').replace(/ /g, ' ').trim();
|
}
|
|
function goPrev() {
|
if (currentIndex.value > 0) currentIndex.value -= 1;
|
}
|
|
function goNext() {
|
if (currentIndex.value < total.value - 1) currentIndex.value += 1;
|
}
|
|
function goBack() {
|
router.push('/tms/selfTest');
|
}
|
|
function isCorrect(q: SelfTestQuestionItem): boolean {
|
const ans = answers[q.id];
|
const correctLabels = q.options.filter((o) => o.isCorrect === '1').map((o) => o.optionLabel);
|
if (q.questionType === 'multi') {
|
const selected = Array.isArray(ans) ? [...ans].sort() : [];
|
return selected.join(',') === [...correctLabels].sort().join(',');
|
}
|
return String(ans || '') === String(correctLabels[0] || '');
|
}
|
|
function handleSubmit() {
|
if (!paper.value) return;
|
const qs = paper.value.questions;
|
let right = 0;
|
qs.forEach((q) => {
|
if (isCorrect(q)) right += 1;
|
});
|
submitted.value = true;
|
scoreText.value = `${right} / ${qs.length}`;
|
createMessage.success(`检测完成,正确 ${right} 题,共 ${qs.length} 题`);
|
}
|
</script>
|
|
<template>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-center">
|
<div class="jnpf-content-wrapper-content tms-exam-page" v-if="paper">
|
<div class="tms-exam-header">
|
<div>
|
<div class="text-base font-medium">自我检测 · {{ paper.bankName }}</div>
|
<div class="mt-1 text-gray-400 text-sm">
|
第 {{ currentIndex + 1 }} / {{ total }} 题
|
<span v-if="submitted" class="ml-3 text-primary">得分:{{ scoreText }}</span>
|
</div>
|
</div>
|
<a-space>
|
<a-button @click="goBack">返回设置</a-button>
|
<a-button type="primary" :disabled="submitted" @click="handleSubmit">交卷</a-button>
|
</a-space>
|
</div>
|
|
<div v-if="current" class="tms-exam-body">
|
<div class="mb-3 text-sm text-gray-500">
|
{{ labelOfType(current.questionType) }}
|
</div>
|
<div class="stem mb-4">{{ stripHtml(current.stem) }}</div>
|
|
<!-- 单选 / 判断 -->
|
<a-radio-group
|
v-if="current.questionType === 'single' || current.questionType === 'judge'"
|
v-model:value="answers[current.id]"
|
class="!flex !flex-col gap-3"
|
:disabled="submitted"
|
>
|
<a-radio v-for="opt in current.options" :key="opt.optionLabel" :value="opt.optionLabel">
|
{{ opt.optionLabel }}. {{ opt.optionContent }}
|
</a-radio>
|
</a-radio-group>
|
|
<!-- 多选 -->
|
<a-checkbox-group
|
v-else-if="current.questionType === 'multi'"
|
v-model:value="answers[current.id]"
|
class="!flex !flex-col gap-3"
|
:disabled="submitted"
|
>
|
<a-checkbox v-for="opt in current.options" :key="opt.optionLabel" :value="opt.optionLabel">
|
{{ opt.optionLabel }}. {{ opt.optionContent }}
|
</a-checkbox>
|
</a-checkbox-group>
|
|
<div v-if="submitted" class="mt-4 text-sm" :class="isCorrect(current) ? 'text-green-600' : 'text-red-500'">
|
{{ isCorrect(current) ? '回答正确' : '回答错误' }}
|
· 正确答案:
|
{{ current.options.filter((o) => o.isCorrect === '1').map((o) => o.optionLabel).join('、') }}
|
</div>
|
</div>
|
|
<div class="tms-exam-footer">
|
<a-button :disabled="currentIndex <= 0" @click="goPrev">上一题</a-button>
|
<a-button :disabled="currentIndex >= total - 1" @click="goNext">下一题</a-button>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<style scoped>
|
.tms-exam-page {
|
background: #fff;
|
padding: 20px 24px;
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
min-height: 0;
|
}
|
|
.tms-exam-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding-bottom: 12px;
|
margin-bottom: 16px;
|
border-bottom: 1px solid #f0f0f0;
|
flex-shrink: 0;
|
}
|
|
.tms-exam-body {
|
flex: 1;
|
min-height: 0;
|
overflow: auto;
|
}
|
|
.stem {
|
font-size: 15px;
|
line-height: 1.7;
|
color: rgba(0, 0, 0, 0.88);
|
}
|
|
.tms-exam-footer {
|
flex-shrink: 0;
|
display: flex;
|
gap: 12px;
|
justify-content: center;
|
padding-top: 16px;
|
border-top: 1px solid #f0f0f0;
|
}
|
</style>
|