创建ExamingView考试页模板

This commit is contained in:
chenqiang 2025-08-10 08:20:02 +08:00
parent fc110beee0
commit 627ca9bc75
3 changed files with 408 additions and 2 deletions

View File

@ -1,6 +1,8 @@
import { createRouter, createWebHistory } from 'vue-router'
import WelcomeView from '@/views/WelcomeView.vue'
import ExamingView from '@/views/user/ExamingView.vue'
import ExamineeHomeView from '@/views/user/ExamineeHomeView.vue'
import AdminHomeView from '@/views/admin/AdminHomeView.vue'
// 导入QuestionManagementView
import QuestionManagementView from '@/views/admin/QuestionManagementView.vue'
@ -26,6 +28,12 @@ const router = createRouter({
name: 'examinee-home',
component: ExamineeHomeView,
},
// Modify the examing route to use lazy loading
{
path: '/examinee/examing',
name: 'examinee-examing',
component: ExamingView,
},
// admin/AdminHomeView路由
{
path: '/admin/home',

View File

@ -256,7 +256,7 @@ const startExam = async () => {
}
console.log('开始考试 - 调用接口')
isLoading.value = true
// isLoading.value = true
try {
//
@ -268,7 +268,7 @@ const startExam = async () => {
console.log('开始考试 - 成功')
ElMessage.success('即将开始考试!')
//
// router.push('/exam')
router.push('/examinee/examing')
isLoading.value = false
}, 1000)
} catch (error) {

View File

@ -0,0 +1,398 @@
<template>
<div v-if="isReady" class="examing-container">
<el-container>
<!-- 头部 -->
<Header />
<!-- 主要内容区域 -->
<el-main class="exam-content">
<!-- 顶栏考生信息和考试信息 -->
<div class="exam-top-bar">
<div class="left-info">
<div class="info-item"><span class="label">考生姓名</span><span class="value">张三</span></div>
<div class="info-item"><span class="label">身份证号</span><span class="value">2104XXXXXXXXXXXX1234</span></div>
<div class="info-item"><span class="label">准考证号</span><span class="value">20230001</span></div>
</div>
<div class="right-info">
<div class="info-item"><span class="label">考试总分</span><span class="value">100</span></div>
<div class="info-item"><span class="label">总时长</span><span class="value">120分钟</span></div>
<div class="info-item"><span class="label">最小时长</span><span class="value">60分钟</span></div>
<div class="info-item timer"><span class="label">倒计时</span><span class="value">{{ countdown }}</span></div>
</div>
</div>
<div class="exam-main">
<!-- 左侧边栏试题序号列表 -->
<div class="exam-sidebar">
<div class="sidebar-title">试题列表</div>
<div class="question-list">
<div v-for="i in 20" :key="i" class="question-item" :class="{ 'current': i === currentQuestion }"
@click="goToQuestion(i)">{{ i }}</div>
</div>
</div>
<!-- 主体部分试题内容 -->
<div class="exam-question-content">
<div class="question-header">
<span class="question-number"> {{ currentQuestion }} </span>
<span class="question-score">({{ questionScore }})</span>
</div>
<div class="question-body">
<div class="question-type">选择题</div>
<div class="question-text">
以下关于统计法的描述正确的是
</div>
<div class="question-options" v-if="questionType === 'choice'">
<div class="option-item" v-for="(option, index) in ['A', 'B', 'C', 'D']" :key="index"
@click="selectOption(index)">
<span class="option-letter">{{ option }}</span>
<span class="option-text">{{ getOptionText(option) }}</span>
</div>
</div>
<div class="question-answer" v-else-if="questionType === 'fill_blank'">
<input type="text" placeholder="请在此填写答案">
</div>
</div>
</div>
</div>
<!-- 底栏操作按钮 -->
<div class="exam-bottom-bar">
<el-button type="default" @click="prevQuestion" :disabled="currentQuestion === 1">上一题</el-button>
<el-button type="primary" @click="nextQuestion" :disabled="currentQuestion === totalQuestions">下一题</el-button>
<el-button type="danger" @click="submitExam">交卷</el-button>
</div>
</el-main>
<!-- 底部 -->
<Footer />
</el-container>
</div>
</template>
<script setup>
//
import Header from '@/components/common/Header.vue'
import Footer from '@/components/common/Footer.vue'
import { ElButton } from 'element-plus'
import { ref, onUnmounted, onMounted } from 'vue'
//
const currentQuestion = ref(1)
const totalQuestions = ref(20)
const questionScore = ref(5)
const questionType = ref('choice') // 'choice' 'fill_blank'
const countdown = ref('01:59:59') // 120
//
let timer = null
//
const formatTime = (seconds) => {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}
//
const getOptionText = (option) => {
const options = {
'A': '统计法是调整统计关系的法律规范的总称',
'B': '统计法仅适用于政府统计活动',
'C': '统计法不包括地方性统计法规',
'D': '统计法制定的依据是宪法'
}
return options[option] || ''
}
//
const prevQuestion = () => {
if (currentQuestion.value > 1) {
currentQuestion.value--
}
}
//
const nextQuestion = () => {
if (currentQuestion.value < totalQuestions.value) {
currentQuestion.value++
}
}
//
const goToQuestion = (index) => {
currentQuestion.value = index
}
//
const selectOption = (index) => {
//
}
//
const submitExam = () => {
if (confirm('确定要交卷吗?交卷后将无法修改答案。')) {
//
alert('交卷成功!')
}
}
//
// Add this line
const isReady = ref(false)
// Modify onMounted
onMounted(() => {
// Set isReady to true after mount
isReady.value = true
// 1207200
let remainingSeconds = 120 * 60
//
countdown.value = formatTime(remainingSeconds)
//
timer = setInterval(() => {
remainingSeconds--
if (remainingSeconds <= 0) {
clearInterval(timer)
countdown.value = '00:00:00'
alert('考试时间已结束,系统将自动交卷。')
//
} else {
countdown.value = formatTime(remainingSeconds)
}
}, 1000)
})
//
onUnmounted(() => {
if (timer) {
clearInterval(timer)
timer = null // Add this line to prevent memory leaks
}
})
</script>
<style scoped>
/* 自定义样式 */
.examing-container, .el-container {
height: 100vh;
display: flex;
flex-direction: column;
}
.exam-content {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
box-sizing: border-box;
overflow: hidden;
}
/* 顶栏样式 */
.exam-top-bar {
display: flex;
justify-content: space-between;
padding: 0.5rem 1rem;
background-color: #f5f7fa;
border-radius: 4px;
margin-bottom: 1rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.left-info, .right-info {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.info-item {
display: flex;
align-items: center;
}
.label {
font-weight: bold;
margin-right: 0.5rem;
color: #606266;
}
.value {
color: #303133;
}
.timer .value {
color: #f56c6c;
font-weight: bold;
}
/* 主要内容区样式 */
.exam-main {
display: flex;
flex: 1;
gap: 1rem;
overflow: hidden;
}
/* 侧边栏样式 */
.exam-sidebar {
width: 240px;
background-color: #f5f7fa;
border-radius: 4px;
display: flex;
flex-direction: column;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.sidebar-title {
padding: 0.75rem 1rem;
font-weight: bold;
color: #1e40af;
border-bottom: 1px solid #e4e7ed;
}
.question-list {
padding: 1rem;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
overflow-y: auto;
flex: 1;
}
.question-item {
width: 36px;
height: 36px;
border-radius: 4px;
background-color: #fff;
border: 1px solid #dcdfe6;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
}
.question-item:hover {
border-color: #409eff;
}
.question-item.current {
background-color: #409eff;
color: #fff;
border-color: #409eff;
}
/* 试题内容区样式 */
.exam-question-content {
flex: 1;
background-color: #fff;
border-radius: 4px;
padding: 1rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
overflow-y: auto;
}
.question-header {
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e4e7ed;
}
.question-number {
font-weight: bold;
color: #303133;
}
.question-score {
color: #f56c6c;
}
.question-type {
display: inline-block;
padding: 0.25rem 0.5rem;
background-color: #e6f7ff;
color: #1890ff;
border-radius: 4px;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.question-text {
margin-bottom: 1.5rem;
line-height: 1.6;
color: #303133;
}
/* 选项样式 */
.question-options {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.option-item {
padding: 0.75rem 1rem;
border-radius: 4px;
border: 1px solid #dcdfe6;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: flex-start;
}
.option-item:hover {
border-color: #409eff;
background-color: #f0f8ff;
}
.option-letter {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 50%;
background-color: #f5f7fa;
text-align: center;
line-height: 24px;
margin-right: 0.75rem;
flex-shrink: 0;
}
/* 填空题样式 */
.question-answer input {
width: 100%;
padding: 0.75rem;
border: 1px solid #dcdfe6;
border-radius: 4px;
box-sizing: border-box;
}
/* 底栏样式 */
.exam-bottom-bar {
display: flex;
justify-content: space-between;
padding: 1rem;
background-color: #f5f7fa;
border-radius: 4px;
margin-top: 1rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
/* 响应式样式 */
@media (max-width: 992px) {
.exam-main {
flex-direction: column;
}
.exam-sidebar {
width: 100%;
height: 200px;
}
}
</style>