<script lang="ts" setup>
|
import type { SelfTestPaper, SelfTestQuestionItem } from './types';
|
|
import { computed, onMounted, ref } from 'vue';
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { useTabbarStore } from '@vben/stores';
|
|
import { getSelfTestInfo } from '#/api/x/tms/selfTest';
|
import { labelOfType } from '#/views/x/tms/question/constants';
|
|
defineOptions({ name: 'TmsSelfTestDetail' });
|
|
const route = useRoute();
|
const router = useRouter();
|
const { createMessage } = useMessage();
|
const tabbarStore = useTabbarStore();
|
|
const loading = ref(false);
|
const detail = ref<SelfTestPaper | null>(null);
|
/** all | wrong */
|
const filterMode = ref<'all' | 'wrong'>('all');
|
|
const wrongCount = computed(
|
() => detail.value?.questions?.filter((q) => q.isRight !== '1').length || 0,
|
);
|
|
const displayQuestions = computed(() => {
|
const list = detail.value?.questions || [];
|
if (filterMode.value === 'wrong') {
|
return list.filter((q) => q.isRight !== '1');
|
}
|
return list;
|
});
|
|
function stripHtml(html?: string) {
|
if (!html) return '';
|
return html.replace(/<[^>]+>/g, '').replace(/ /g, ' ').trim();
|
}
|
|
function formatAnswer(ans?: string) {
|
if (!ans) return '未作答';
|
return ans.split(',').filter(Boolean).join('、');
|
}
|
|
function optionClass(q: SelfTestQuestionItem, label: string) {
|
const correctSet = new Set((q.correctAnswer || '').split(',').filter(Boolean));
|
const userSet = new Set((q.userAnswer || '').split(',').filter(Boolean));
|
const isCorrectOpt = correctSet.has(label);
|
const isUserOpt = userSet.has(label);
|
if (isCorrectOpt) return 'opt-correct';
|
if (isUserOpt && !isCorrectOpt) return 'opt-wrong';
|
return '';
|
}
|
|
async function loadDetail() {
|
const id = String(route.params.id || '');
|
if (!id) {
|
router.replace('/tms/selfTest/records');
|
return;
|
}
|
loading.value = true;
|
try {
|
detail.value = await getSelfTestInfo(id);
|
} catch (e: any) {
|
createMessage.error(e?.message || '加载检测详情失败');
|
router.replace('/tms/selfTest/records');
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
onMounted(() => {
|
tabbarStore.renderRouteView = true;
|
loadDetail();
|
});
|
|
function goBack() {
|
tabbarStore.renderRouteView = true;
|
router.push('/tms/selfTest/records');
|
}
|
</script>
|
|
<template>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-center">
|
<div class="jnpf-content-wrapper-content tms-self-test-detail" v-loading="loading">
|
<div class="page-header" v-if="detail">
|
<div>
|
<div class="text-base font-medium">检测详情 · {{ detail.bankName }}</div>
|
<div class="mt-1 text-gray-400 text-sm">
|
得分:{{ detail.correctCount ?? 0 }} / {{ detail.totalCount ?? detail.questions?.length ?? 0 }}
|
<span v-if="detail.scoreRate != null" class="ml-2">正确率 {{ detail.scoreRate }}%</span>
|
<span v-if="detail.submitTime" class="ml-3">交卷时间:{{ detail.submitTime }}</span>
|
</div>
|
</div>
|
<a-space>
|
<a-radio-group v-model:value="filterMode" button-style="solid" size="small">
|
<a-radio-button value="all">全部题目</a-radio-button>
|
<a-radio-button value="wrong">仅错题({{ wrongCount }})</a-radio-button>
|
</a-radio-group>
|
<a-button @click="goBack">返回记录</a-button>
|
</a-space>
|
</div>
|
|
<a-empty v-if="!loading && detail && !displayQuestions.length" description="暂无题目" />
|
|
<div v-for="(q, idx) in displayQuestions" :key="`${q.id}_${idx}`" class="question-card">
|
<div class="q-title">
|
<span class="q-index">{{ q.sortNo || idx + 1 }}.</span>
|
<span class="q-type">{{ labelOfType(q.questionType) }}</span>
|
<a-tag :color="q.isRight === '1' ? 'success' : 'error'" class="!ml-2">
|
{{ q.isRight === '1' ? '正确' : '错误' }}
|
</a-tag>
|
</div>
|
<div class="q-stem">{{ stripHtml(q.stem) }}</div>
|
<div class="q-options">
|
<div
|
v-for="opt in q.options"
|
:key="opt.optionLabel"
|
class="q-option"
|
:class="optionClass(q, opt.optionLabel)"
|
>
|
{{ opt.optionLabel }}. {{ opt.optionContent }}
|
</div>
|
</div>
|
<div class="q-answer">
|
<span>你的答案:{{ formatAnswer(q.userAnswer) }}</span>
|
<span class="ml-4">正确答案:{{ formatAnswer(q.correctAnswer) }}</span>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<style scoped>
|
.tms-self-test-detail {
|
background: #fff;
|
padding: 20px 24px;
|
height: 100%;
|
overflow: auto;
|
}
|
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: flex-start;
|
gap: 16px;
|
margin-bottom: 20px;
|
padding-bottom: 12px;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
.question-card {
|
padding: 16px 0;
|
border-bottom: 1px solid #f5f5f5;
|
}
|
|
.q-title {
|
display: flex;
|
align-items: center;
|
margin-bottom: 8px;
|
}
|
|
.q-index {
|
font-weight: 600;
|
margin-right: 6px;
|
}
|
|
.q-type {
|
color: rgba(0, 0, 0, 0.45);
|
font-size: 13px;
|
}
|
|
.q-stem {
|
font-size: 15px;
|
line-height: 1.7;
|
margin-bottom: 12px;
|
color: rgba(0, 0, 0, 0.88);
|
}
|
|
.q-options {
|
display: flex;
|
flex-direction: column;
|
gap: 8px;
|
margin-bottom: 10px;
|
}
|
|
.q-option {
|
padding: 6px 10px;
|
border-radius: 4px;
|
background: #fafafa;
|
color: rgba(0, 0, 0, 0.75);
|
}
|
|
.q-option.opt-correct {
|
background: #f6ffed;
|
color: #389e0d;
|
border: 1px solid #b7eb8f;
|
}
|
|
.q-option.opt-wrong {
|
background: #fff2f0;
|
color: #cf1322;
|
border: 1px solid #ffccc7;
|
}
|
|
.q-answer {
|
font-size: 13px;
|
color: rgba(0, 0, 0, 0.55);
|
}
|
</style>
|