You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
827 lines
21 KiB
Vue
827 lines
21 KiB
Vue
|
2 months ago
|
<template>
|
||
|
|
<div class="resultados-container">
|
||
|
|
<!-- Header de resultados -->
|
||
|
|
<a-card class="resultados-header">
|
||
|
|
<a-result
|
||
|
|
:status="resultadoStatus"
|
||
|
|
:title="resultadoTitulo"
|
||
|
|
:sub-title="resultadoSubtitulo"
|
||
|
|
>
|
||
|
|
<template #extra>
|
||
|
|
<a-space size="large">
|
||
|
|
<a-button type="primary" @click="verDetalles">
|
||
|
|
<eye-outlined />
|
||
|
|
Ver detalles del examen
|
||
|
|
</a-button>
|
||
|
|
<a-button @click="volverADashboard">
|
||
|
|
<home-outlined />
|
||
|
|
Volver al inicio
|
||
|
|
</a-button>
|
||
|
|
</a-space>
|
||
|
|
</template>
|
||
|
|
</a-result>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<!-- Estadísticas principales -->
|
||
|
|
<div class="stats-grid">
|
||
|
|
<a-card class="stat-card">
|
||
|
|
<div class="stat-content">
|
||
|
|
<check-circle-outlined class="stat-icon" style="color: #52c41a;" />
|
||
|
|
<div class="stat-info">
|
||
|
|
<div class="stat-value">{{ examenStore.progreso.correctas }}</div>
|
||
|
|
<div class="stat-label">Respuestas correctas</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<a-card class="stat-card">
|
||
|
|
<div class="stat-content">
|
||
|
|
<close-circle-outlined class="stat-icon" style="color: #f5222d;" />
|
||
|
|
<div class="stat-info">
|
||
|
|
<div class="stat-value">{{ respuestasIncorrectas }}</div>
|
||
|
|
<div class="stat-label">Respuestas incorrectas</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<a-card class="stat-card">
|
||
|
|
<div class="stat-content">
|
||
|
|
<star-outlined class="stat-icon" style="color: #faad14;" />
|
||
|
|
<div class="stat-info">
|
||
|
|
<div class="stat-value">{{ examenStore.progreso.puntaje_total }}</div>
|
||
|
|
<div class="stat-label">Puntaje total</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<a-card class="stat-card">
|
||
|
|
<div class="stat-content">
|
||
|
|
<percentage-outlined class="stat-icon" style="color: #1890ff;" />
|
||
|
|
<div class="stat-info">
|
||
|
|
<div class="stat-value">{{ porcentajeCorrectas }}%</div>
|
||
|
|
<div class="stat-label">Porcentaje de acierto</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</a-card>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Progreso por áreas/temas -->
|
||
|
|
<a-card class="progreso-card" title="Progreso por área">
|
||
|
|
<a-progress
|
||
|
|
:percent="porcentajeCorrectas"
|
||
|
|
:stroke-color="{
|
||
|
|
'0%': '#f5222d',
|
||
|
|
'100%': '#52c41a',
|
||
|
|
}"
|
||
|
|
:show-info="false"
|
||
|
|
/>
|
||
|
|
<div class="progreso-labels">
|
||
|
|
<span>0%</span>
|
||
|
|
<span>50%</span>
|
||
|
|
<span>100%</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="estado-final">
|
||
|
|
<a-tag :color="getEstadoColor()" size="large">
|
||
|
|
{{ getEstadoTexto() }}
|
||
|
|
</a-tag>
|
||
|
|
<span class="recomendacion">
|
||
|
|
{{ recomendacionTexto }}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<!-- Detalle de preguntas -->
|
||
|
|
<a-card class="preguntas-detalle-card" title="Detalle de respuestas">
|
||
|
|
<a-collapse v-model:activeKey="activeKeys" accordion>
|
||
|
|
<a-collapse-panel
|
||
|
|
v-for="pregunta in examenStore.preguntas"
|
||
|
|
:key="pregunta.id"
|
||
|
|
:header="`Pregunta ${pregunta.orden}: ${getResumenPregunta(pregunta)}`"
|
||
|
|
>
|
||
|
|
<!-- Enunciado -->
|
||
|
|
<div class="pregunta-enunciado">
|
||
|
|
<h4>Enunciado:</h4>
|
||
|
|
<div v-html="formatearEnunciado(pregunta.enunciado || '')"></div>
|
||
|
|
|
||
|
|
<!-- Imágenes -->
|
||
|
|
<div v-if="pregunta.imagenes && pregunta.imagenes.length > 0" class="pregunta-images">
|
||
|
|
<a-image
|
||
|
|
v-for="(imagen, idx) in pregunta.imagenes"
|
||
|
|
:key="idx"
|
||
|
|
:src="imagen.url"
|
||
|
|
:alt="`Imagen ${idx + 1}`"
|
||
|
|
class="detalle-image"
|
||
|
|
:preview="true"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Opciones -->
|
||
|
|
<div class="pregunta-opciones">
|
||
|
|
<h4>Opciones:</h4>
|
||
|
|
<div
|
||
|
|
v-for="opcion in pregunta.opciones || []"
|
||
|
|
:key="opcion.key"
|
||
|
|
class="opcion-detalle"
|
||
|
|
:class="{
|
||
|
|
'correcta': esOpcionCorrecta(pregunta, opcion.key),
|
||
|
|
'seleccionada': pregunta.respuesta_usuario == opcion.key,
|
||
|
|
'incorrecta-seleccionada': pregunta.respuesta_usuario == opcion.key && !esOpcionCorrecta(pregunta, opcion.key)
|
||
|
|
}"
|
||
|
|
>
|
||
|
|
<div class="opcion-detalle-content">
|
||
|
|
<div class="opcion-detalle-letra">
|
||
|
|
{{ obtenerLetraOpcion(opcion.key) }}
|
||
|
|
<span v-if="esOpcionCorrecta(pregunta, opcion.key)" class="correct-badge">
|
||
|
|
✓
|
||
|
|
</span>
|
||
|
|
<span v-if="pregunta.respuesta_usuario == opcion.key && !esOpcionCorrecta(pregunta, opcion.key)"
|
||
|
|
class="incorrect-badge">
|
||
|
|
✗
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<div class="opcion-detalle-texto">
|
||
|
|
{{ opcion.texto }}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Resultado de la pregunta -->
|
||
|
|
<div class="pregunta-resultado">
|
||
|
|
<a-alert
|
||
|
|
:type="pregunta.es_correcta ? 'success' : 'error'"
|
||
|
|
:message="pregunta.es_correcta ? '¡Respuesta correcta!' : 'Respuesta incorrecta'"
|
||
|
|
:description="getDescripcionResultado(pregunta)"
|
||
|
|
show-icon
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Explicación (si existe) -->
|
||
|
|
<div v-if="pregunta.explicacion" class="pregunta-explicacion">
|
||
|
|
<h4>Explicación:</h4>
|
||
|
|
<p>{{ pregunta.explicacion }}</p>
|
||
|
|
</div>
|
||
|
|
</a-collapse-panel>
|
||
|
|
</a-collapse>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<!-- Recomendaciones -->
|
||
|
|
<a-card class="recomendaciones-card" title="Recomendaciones">
|
||
|
|
<a-list :data-source="recomendaciones" bordered>
|
||
|
|
<template #renderItem="{ item, index }">
|
||
|
|
<a-list-item>
|
||
|
|
<a-list-item-meta>
|
||
|
|
<template #title>
|
||
|
|
<a-tag :color="item.tipo === 'fortaleza' ? 'green' : 'orange'">
|
||
|
|
{{ item.tipo === 'fortaleza' ? 'Fortaleza' : 'Área a mejorar' }}
|
||
|
|
</a-tag>
|
||
|
|
{{ item.titulo }}
|
||
|
|
</template>
|
||
|
|
<template #description>
|
||
|
|
{{ item.descripcion }}
|
||
|
|
</template>
|
||
|
|
</a-list-item-meta>
|
||
|
|
<div v-if="item.accion">
|
||
|
|
<a-button type="link" size="small">
|
||
|
|
{{ item.accion }}
|
||
|
|
</a-button>
|
||
|
|
</div>
|
||
|
|
</a-list-item>
|
||
|
|
</template>
|
||
|
|
</a-list>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<!-- Acciones finales -->
|
||
|
|
<a-card class="acciones-card">
|
||
|
|
<div class="acciones-content">
|
||
|
|
<a-space size="large">
|
||
|
|
<a-button type="primary" size="large" @click="crearNuevoExamen">
|
||
|
|
<plus-outlined />
|
||
|
|
Realizar nuevo examen
|
||
|
|
</a-button>
|
||
|
|
|
||
|
|
<a-button size="large" @click="imprimirResultados">
|
||
|
|
<printer-outlined />
|
||
|
|
Imprimir resultados
|
||
|
|
</a-button>
|
||
|
|
|
||
|
|
<a-button size="large" @click="compartirResultados">
|
||
|
|
<share-alt-outlined />
|
||
|
|
Compartir resultados
|
||
|
|
</a-button>
|
||
|
|
</a-space>
|
||
|
|
</div>
|
||
|
|
</a-card>
|
||
|
|
|
||
|
|
<!-- Modal para ver detalles completos -->
|
||
|
|
<a-modal
|
||
|
|
v-model:open="modalDetallesVisible"
|
||
|
|
title="Detalles completos del examen"
|
||
|
|
width="800px"
|
||
|
|
:footer="null"
|
||
|
|
>
|
||
|
|
<div class="detalles-completos">
|
||
|
|
<div class="detalle-item">
|
||
|
|
<strong>Proceso:</strong> {{ examenStore.examenActual?.proceso?.nombre || 'No especificado' }}
|
||
|
|
</div>
|
||
|
|
<div class="detalle-item">
|
||
|
|
<strong>Área:</strong> {{ examenStore.examenActual?.area?.nombre || 'No especificado' }}
|
||
|
|
</div>
|
||
|
|
<div class="detalle-item">
|
||
|
|
<strong>Fecha de inicio:</strong> {{ formatFecha(examenStore.examenActual?.hora_inicio) }}
|
||
|
|
</div>
|
||
|
|
<div class="detalle-item">
|
||
|
|
<strong>Duración:</strong> {{ calcularDuracion() }}
|
||
|
|
</div>
|
||
|
|
<div class="detalle-item">
|
||
|
|
<strong>Intentos:</strong> {{ examenStore.intentos }}
|
||
|
|
</div>
|
||
|
|
<div class="detalle-item">
|
||
|
|
<strong>Estado:</strong>
|
||
|
|
<a-tag :color="getEstadoColor()">
|
||
|
|
{{ getEstadoTexto() }}
|
||
|
|
</a-tag>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<a-divider />
|
||
|
|
|
||
|
|
<h3>Resumen estadístico</h3>
|
||
|
|
<div class="estadisticas-detalle">
|
||
|
|
<a-row :gutter="16">
|
||
|
|
<a-col :span="6" v-for="stat in estadisticasDetalle" :key="stat.label">
|
||
|
|
<div class="estadistica-item">
|
||
|
|
<div class="estadistica-valor">{{ stat.value }}</div>
|
||
|
|
<div class="estadistica-label">{{ stat.label }}</div>
|
||
|
|
</div>
|
||
|
|
</a-col>
|
||
|
|
</a-row>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</a-modal>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
import { ref, computed, onMounted } from 'vue'
|
||
|
|
import { useRouter } from 'vue-router'
|
||
|
|
import { useExamenStore } from '../store/examen.store'
|
||
|
|
import { message } from 'ant-design-vue'
|
||
|
|
import {
|
||
|
|
CheckCircleOutlined,
|
||
|
|
CloseCircleOutlined,
|
||
|
|
StarOutlined,
|
||
|
|
PercentageOutlined,
|
||
|
|
EyeOutlined,
|
||
|
|
HomeOutlined,
|
||
|
|
PlusOutlined,
|
||
|
|
PrinterOutlined,
|
||
|
|
ShareAltOutlined,
|
||
|
|
TrophyOutlined,
|
||
|
|
BulbOutlined
|
||
|
|
} from '@ant-design/icons-vue'
|
||
|
|
|
||
|
|
const router = useRouter()
|
||
|
|
const examenStore = useExamenStore()
|
||
|
|
|
||
|
|
// Estados reactivos
|
||
|
|
const activeKeys = ref([])
|
||
|
|
const modalDetallesVisible = ref(false)
|
||
|
|
|
||
|
|
// Computed properties
|
||
|
|
const respuestasIncorrectas = computed(() => {
|
||
|
|
return examenStore.progreso.respondidas - examenStore.progreso.correctas
|
||
|
|
})
|
||
|
|
|
||
|
|
const porcentajeCorrectas = computed(() => {
|
||
|
|
if (examenStore.progreso.total === 0) return 0
|
||
|
|
return Math.round((examenStore.progreso.correctas / examenStore.progreso.total) * 100)
|
||
|
|
})
|
||
|
|
|
||
|
|
const resultadoStatus = computed(() => {
|
||
|
|
if (porcentajeCorrectas.value >= 80) return 'success'
|
||
|
|
if (porcentajeCorrectas.value >= 60) return 'warning'
|
||
|
|
return 'error'
|
||
|
|
})
|
||
|
|
|
||
|
|
const resultadoTitulo = computed(() => {
|
||
|
|
if (porcentajeCorrectas.value >= 80) return '¡Excelente resultado!'
|
||
|
|
if (porcentajeCorrectas.value >= 60) return 'Buen trabajo'
|
||
|
|
return 'Necesitas practicar más'
|
||
|
|
})
|
||
|
|
|
||
|
|
const resultadoSubtitulo = computed(() => {
|
||
|
|
return `Obtuviste ${examenStore.progreso.correctas} de ${examenStore.progreso.total} respuestas correctas (${porcentajeCorrectas.value}%)`
|
||
|
|
})
|
||
|
|
|
||
|
|
const recomendacionTexto = computed(() => {
|
||
|
|
if (porcentajeCorrectas.value >= 80) {
|
||
|
|
return 'Tu nivel es excelente. Sigue manteniendo el buen trabajo.'
|
||
|
|
} else if (porcentajeCorrectas.value >= 60) {
|
||
|
|
return 'Tienes un buen nivel, pero hay áreas que puedes mejorar.'
|
||
|
|
} else {
|
||
|
|
return 'Recomendamos repasar los temas antes de intentar nuevamente.'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
const recomendaciones = computed(() => {
|
||
|
|
const recomendacionesList = []
|
||
|
|
|
||
|
|
if (porcentajeCorrectas.value >= 80) {
|
||
|
|
recomendacionesList.push({
|
||
|
|
tipo: 'fortaleza',
|
||
|
|
titulo: 'Excelente comprensión',
|
||
|
|
descripcion: 'Demuestras un dominio sólido de los temas evaluados.',
|
||
|
|
accion: 'Ver temas avanzados'
|
||
|
|
})
|
||
|
|
} else if (porcentajeCorrectas.value >= 60) {
|
||
|
|
recomendacionesList.push({
|
||
|
|
tipo: 'fortaleza',
|
||
|
|
titulo: 'Bases sólidas',
|
||
|
|
descripcion: 'Tienes buenos fundamentos en la mayoría de temas.',
|
||
|
|
accion: 'Profundizar conocimientos'
|
||
|
|
})
|
||
|
|
recomendacionesList.push({
|
||
|
|
tipo: 'mejora',
|
||
|
|
titulo: 'Áreas específicas',
|
||
|
|
descripcion: 'Algunos temas requieren más atención.',
|
||
|
|
accion: 'Repasar temas'
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
recomendacionesList.push({
|
||
|
|
tipo: 'mejora',
|
||
|
|
titulo: 'Revisión general',
|
||
|
|
descripcion: 'Recomendamos repasar todos los temas del área.',
|
||
|
|
accion: 'Iniciar repaso'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Recomendaciones basadas en respuestas incorrectas
|
||
|
|
if (respuestasIncorrectas.value > examenStore.progreso.total / 2) {
|
||
|
|
recomendacionesList.push({
|
||
|
|
tipo: 'mejora',
|
||
|
|
titulo: 'Tiempo de estudio',
|
||
|
|
descripcion: 'Dedica más tiempo al estudio antes de evaluarte nuevamente.',
|
||
|
|
accion: 'Programar estudio'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
return recomendacionesList
|
||
|
|
})
|
||
|
|
|
||
|
|
const estadisticasDetalle = computed(() => [
|
||
|
|
{ label: 'Total preguntas', value: examenStore.progreso.total },
|
||
|
|
{ label: 'Respondidas', value: examenStore.progreso.respondidas },
|
||
|
|
{ label: 'Correctas', value: examenStore.progreso.correctas },
|
||
|
|
{ label: 'Incorrectas', value: respuestasIncorrectas.value },
|
||
|
|
{ label: 'Puntaje máximo', value: examenStore.progreso.total * 10 }, // Asumiendo 10 puntos por pregunta
|
||
|
|
{ label: 'Puntaje obtenido', value: examenStore.progreso.puntaje_total },
|
||
|
|
{ label: 'Porcentaje', value: `${porcentajeCorrectas.value}%` },
|
||
|
|
{ label: 'Tasa de acierto', value: `${Math.round((examenStore.progreso.correctas / examenStore.progreso.respondidas) * 100) || 0}%` }
|
||
|
|
])
|
||
|
|
|
||
|
|
// Funciones de utilidad
|
||
|
|
const formatearEnunciado = (enunciado) => {
|
||
|
|
if (!enunciado) return ''
|
||
|
|
return enunciado.replace(/\n/g, '<br>')
|
||
|
|
}
|
||
|
|
|
||
|
|
const obtenerLetraOpcion = (key) => {
|
||
|
|
const letras = ['A', 'B', 'C', 'D', 'E', 'F']
|
||
|
|
return letras[key] || `Opción ${key + 1}`
|
||
|
|
}
|
||
|
|
|
||
|
|
const getResumenPregunta = (pregunta) => {
|
||
|
|
const estado = pregunta.es_correcta ? '✓ Correcta' : '✗ Incorrecta'
|
||
|
|
return `${estado} - Puntaje: ${pregunta.puntaje_obtenido || 0}`
|
||
|
|
}
|
||
|
|
|
||
|
|
const esOpcionCorrecta = (pregunta, opcionKey) => {
|
||
|
|
// En un sistema real, esto vendría del backend
|
||
|
|
// Por ahora, asumimos que la primera opción es correcta (para demo)
|
||
|
|
return opcionKey == 0
|
||
|
|
}
|
||
|
|
|
||
|
|
const getDescripcionResultado = (pregunta) => {
|
||
|
|
if (pregunta.es_correcta) {
|
||
|
|
return `Obtuviste ${pregunta.puntaje_obtenido || 0} puntos por esta respuesta correcta.`
|
||
|
|
} else {
|
||
|
|
return `Tu respuesta fue: ${obtenerLetraOpcion(parseInt(pregunta.respuesta_usuario))}. La respuesta correcta era: ${obtenerLetraOpcion(0)}.`
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const formatFecha = (fecha) => {
|
||
|
|
if (!fecha) return 'No disponible'
|
||
|
|
const date = new Date(fecha)
|
||
|
|
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||
|
|
}
|
||
|
|
|
||
|
|
const calcularDuracion = () => {
|
||
|
|
if (!examenStore.examenActual?.hora_inicio) return 'No disponible'
|
||
|
|
|
||
|
|
const inicio = new Date(examenStore.examenActual.hora_inicio)
|
||
|
|
const fin = new Date() // Asumimos que terminó ahora
|
||
|
|
const duracionMs = fin - inicio
|
||
|
|
|
||
|
|
const minutos = Math.floor(duracionMs / (1000 * 60))
|
||
|
|
const segundos = Math.floor((duracionMs % (1000 * 60)) / 1000)
|
||
|
|
|
||
|
|
return `${minutos} min ${segundos} seg`
|
||
|
|
}
|
||
|
|
|
||
|
|
const getEstadoColor = () => {
|
||
|
|
if (porcentajeCorrectas.value >= 80) return 'green'
|
||
|
|
if (porcentajeCorrectas.value >= 60) return 'blue'
|
||
|
|
return 'red'
|
||
|
|
}
|
||
|
|
|
||
|
|
const getEstadoTexto = () => {
|
||
|
|
if (porcentajeCorrectas.value >= 80) return 'Excelente'
|
||
|
|
if (porcentajeCorrectas.value >= 60) return 'Aprobado'
|
||
|
|
return 'Reprobado'
|
||
|
|
}
|
||
|
|
|
||
|
|
// Funciones de acciones
|
||
|
|
const verDetalles = () => {
|
||
|
|
modalDetallesVisible.value = true
|
||
|
|
}
|
||
|
|
|
||
|
|
const volverADashboard = () => {
|
||
|
|
router.push('/dashboard')
|
||
|
|
}
|
||
|
|
|
||
|
|
const crearNuevoExamen = () => {
|
||
|
|
// Resetear el examen actual y volver al dashboard
|
||
|
|
examenStore.resetExamen()
|
||
|
|
examenStore.resetConfiguracion()
|
||
|
|
router.push('/dashboard')
|
||
|
|
}
|
||
|
|
|
||
|
|
const imprimirResultados = () => {
|
||
|
|
window.print()
|
||
|
|
}
|
||
|
|
|
||
|
|
const compartirResultados = () => {
|
||
|
|
const resultados = `Mis resultados del examen: ${porcentajeCorrectas.value}% de acierto (${examenStore.progreso.correctas}/${examenStore.progreso.total})`
|
||
|
|
|
||
|
|
if (navigator.share) {
|
||
|
|
navigator.share({
|
||
|
|
title: 'Resultados de mi examen',
|
||
|
|
text: resultados,
|
||
|
|
url: window.location.href
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
// Fallback: copiar al portapapeles
|
||
|
|
navigator.clipboard.writeText(resultados)
|
||
|
|
.then(() => {
|
||
|
|
message.success('Resultados copiados al portapapeles')
|
||
|
|
})
|
||
|
|
.catch(() => {
|
||
|
|
message.info(resultados)
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Inicialización
|
||
|
|
onMounted(() => {
|
||
|
|
// Verificar que tenemos resultados
|
||
|
|
if (!examenStore.tieneExamenActual || !examenStore.tienePreguntasGeneradas) {
|
||
|
|
message.warning('No hay resultados de examen disponibles')
|
||
|
|
router.push('/dashboard')
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Expandir la primera pregunta por defecto
|
||
|
|
if (examenStore.preguntas.length > 0) {
|
||
|
|
activeKeys.value = [examenStore.preguntas[0].id]
|
||
|
|
}
|
||
|
|
})
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.resultados-container {
|
||
|
|
max-width: 1200px;
|
||
|
|
margin: 0 auto;
|
||
|
|
padding: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.resultados-header {
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stats-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
|
|
gap: 16px;
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-card {
|
||
|
|
transition: all 0.3s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-card:hover {
|
||
|
|
transform: translateY(-4px);
|
||
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-content {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-icon {
|
||
|
|
font-size: 32px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-info {
|
||
|
|
flex: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-value {
|
||
|
|
font-size: 28px;
|
||
|
|
font-weight: bold;
|
||
|
|
color: #333;
|
||
|
|
line-height: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-label {
|
||
|
|
font-size: 14px;
|
||
|
|
color: #666;
|
||
|
|
margin-top: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progreso-card {
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.progreso-labels {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
margin-top: 8px;
|
||
|
|
font-size: 12px;
|
||
|
|
color: #666;
|
||
|
|
}
|
||
|
|
|
||
|
|
.estado-final {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
margin-top: 16px;
|
||
|
|
padding-top: 16px;
|
||
|
|
border-top: 1px solid #f0f0f0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.recomendacion {
|
||
|
|
font-style: italic;
|
||
|
|
color: #666;
|
||
|
|
max-width: 400px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.preguntas-detalle-card {
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-enunciado {
|
||
|
|
margin-bottom: 20px;
|
||
|
|
padding: 16px;
|
||
|
|
background: #fafafa;
|
||
|
|
border-radius: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-enunciado h4 {
|
||
|
|
margin-bottom: 8px;
|
||
|
|
color: #333;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-images {
|
||
|
|
margin-top: 16px;
|
||
|
|
display: flex;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detalle-image {
|
||
|
|
max-width: 200px;
|
||
|
|
border-radius: 4px;
|
||
|
|
border: 1px solid #f0f0f0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-opciones {
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-opciones h4 {
|
||
|
|
margin-bottom: 12px;
|
||
|
|
color: #333;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle {
|
||
|
|
padding: 12px;
|
||
|
|
margin-bottom: 8px;
|
||
|
|
border: 1px solid #d9d9d9;
|
||
|
|
border-radius: 4px;
|
||
|
|
transition: all 0.3s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle.correcta {
|
||
|
|
border-color: #52c41a;
|
||
|
|
background-color: #f6ffed;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle.seleccionada {
|
||
|
|
border-color: #1890ff;
|
||
|
|
background-color: #e6f7ff;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle.incorrecta-seleccionada {
|
||
|
|
border-color: #f5222d;
|
||
|
|
background-color: #fff1f0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle-content {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle-letra {
|
||
|
|
width: 24px;
|
||
|
|
height: 24px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
background-color: #f0f0f0;
|
||
|
|
border-radius: 50%;
|
||
|
|
font-weight: bold;
|
||
|
|
font-size: 12px;
|
||
|
|
flex-shrink: 0;
|
||
|
|
position: relative;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle.correcta .opcion-detalle-letra {
|
||
|
|
background-color: #52c41a;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle.seleccionada .opcion-detalle-letra {
|
||
|
|
background-color: #1890ff;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle.incorrecta-seleccionada .opcion-detalle-letra {
|
||
|
|
background-color: #f5222d;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.correct-badge, .incorrect-badge {
|
||
|
|
position: absolute;
|
||
|
|
top: -6px;
|
||
|
|
right: -6px;
|
||
|
|
width: 12px;
|
||
|
|
height: 12px;
|
||
|
|
border-radius: 50%;
|
||
|
|
font-size: 8px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.correct-badge {
|
||
|
|
background-color: #52c41a;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.incorrect-badge {
|
||
|
|
background-color: #f5222d;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.opcion-detalle-texto {
|
||
|
|
flex: 1;
|
||
|
|
line-height: 1.4;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-resultado {
|
||
|
|
margin-bottom: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-explicacion {
|
||
|
|
padding: 16px;
|
||
|
|
background: #f6ffed;
|
||
|
|
border-radius: 4px;
|
||
|
|
border-left: 3px solid #52c41a;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pregunta-explicacion h4 {
|
||
|
|
margin-bottom: 8px;
|
||
|
|
color: #333;
|
||
|
|
}
|
||
|
|
|
||
|
|
.recomendaciones-card {
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.acciones-card {
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.acciones-content {
|
||
|
|
display: flex;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detalles-completos {
|
||
|
|
padding: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detalle-item {
|
||
|
|
margin-bottom: 12px;
|
||
|
|
padding: 8px 0;
|
||
|
|
border-bottom: 1px solid #f0f0f0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.detalle-item:last-child {
|
||
|
|
border-bottom: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.estadisticas-detalle {
|
||
|
|
margin-top: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.estadistica-item {
|
||
|
|
text-align: center;
|
||
|
|
padding: 16px;
|
||
|
|
background: #fafafa;
|
||
|
|
border-radius: 4px;
|
||
|
|
margin-bottom: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.estadistica-valor {
|
||
|
|
font-size: 24px;
|
||
|
|
font-weight: bold;
|
||
|
|
color: #1890ff;
|
||
|
|
margin-bottom: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.estadistica-label {
|
||
|
|
font-size: 12px;
|
||
|
|
color: #666;
|
||
|
|
text-transform: uppercase;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Estilos para impresión */
|
||
|
|
@media print {
|
||
|
|
.resultados-container {
|
||
|
|
padding: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.acciones-card,
|
||
|
|
.ant-space,
|
||
|
|
.ant-btn,
|
||
|
|
.ant-collapse-arrow {
|
||
|
|
display: none !important;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ant-collapse-content {
|
||
|
|
display: block !important;
|
||
|
|
height: auto !important;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Responsive */
|
||
|
|
@media (max-width: 768px) {
|
||
|
|
.stats-grid {
|
||
|
|
grid-template-columns: repeat(2, 1fr);
|
||
|
|
}
|
||
|
|
|
||
|
|
.estado-final {
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 12px;
|
||
|
|
align-items: flex-start;
|
||
|
|
}
|
||
|
|
|
||
|
|
.acciones-content .ant-space {
|
||
|
|
flex-direction: column;
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.acciones-content .ant-btn {
|
||
|
|
width: 100%;
|
||
|
|
margin-bottom: 8px;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 480px) {
|
||
|
|
.stats-grid {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-content {
|
||
|
|
justify-content: center;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|