前端增加试卷中心与考试界面

main
yuan 1 week ago
parent 9ba848b72c
commit 11e604778c

@ -38,6 +38,15 @@ public class StudentExamPaperController {
@Autowired
private IExamCreateService iExamCreateService;
@ApiOperation("班级查询")
@GetMapping("selectByUserId")
public R<StudentClass> selectByUserId(){
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
Long id = sysUser.getUserId();
Long userid = iUserService.selectByzhid(id);
return R.ok(iStudentClassService.selectById(userid));
}
@ApiOperation("试卷查询")
@GetMapping("examPaperByTypeAndSubject")
public R<IPage<ExamPaperDO1>> examPaperByTypeAndSubject(int pagenum,int pagesize,String type,String subject){

@ -8,6 +8,12 @@ import store from '../store'; // 引入 Vuex store
Vue.use(VueRouter);
const routes = [
{
path: '/student/examPaper',
name: 'ExamPaper',
component: () => import(/* webpackChunkName: "about" */ '../views/Student/ExamPaper.vue'),
meta: { requiresAuth: true }
},
{
path:'/student/exam',
component: () => import(/* webpackChunkName: "about" */ '../views/Student/Exam.vue'),

@ -49,7 +49,7 @@
<p>考试时长{{ paper.time }}</p>
<p>开始时间{{ paper.startTime || "未设置" }}</p>
<p>结束时间{{ paper.endTime || "未设置" }}</p>
<button class="start-button">开始答题</button>
<button class="start-button" @click="startExam(paper.name, paper.subject)">开始答题</button>
</div>
</div>
</div>
@ -104,6 +104,9 @@ export default {
},
},
methods: {
startExam(name,subject) {
this.$router.push({ name: "ExamPaper", query: { name: name, subject: subject } });
},
handleTypeChange(type) {
this.selectedType = type; //
},

@ -0,0 +1,291 @@
<template>
<div class="exam-container">
<!-- 左侧栏 -->
<div class="sidebar">
<h2>{{ examTitle }}</h2>
<p>总分{{ totalScore }} &nbsp; 时长{{ duration }}</p>
<p class="time-remaining">剩余时间<br /><span>{{ formattedTime }}</span></p>
<button class="submit-button" @click="submitExam"></button>
</div>
<!-- 试题内容 -->
<div class="main-content" v-if="questions.length > 0">
<div v-for="(question, index) in questions" :key="question.id" class="question">
<p>{{ index + 1 }}. {{ question.text }}</p>
<div class="options">
<!-- 有选项时 -->
<template v-if="question.options.length > 0">
<label v-for="(option, optIndex) in question.options" :key="optIndex">
<input
type="radio"
:name="'question-' + question.id"
:value="option.value"
v-model="answers[question.id]"
/>
{{ option.label }}
</label>
</template>
<!-- 无选项时 -->
<template v-else>
<textarea
v-model="answers[question.id]"
placeholder="请填写答案"
rows="3"
></textarea>
</template>
</div>
</div>
</div>
<!-- 加载中提示 -->
<div class="loading" v-else>
正在加载试题请稍候...
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "ExamPaper",
data() {
return {
examTitle: "考试中...", //
totalScore: 0, //
duration: "未知", //
remainingTime: 0, //
questions: [], //
answers: {}, //
examId: null, // ID
};
},
computed: {
//
formattedTime() {
const minutes = Math.floor(this.remainingTime / 60);
const seconds = this.remainingTime % 60;
return `${minutes}${seconds}`;
},
},
methods: {
//
async fetchQuestions() {
try {
const token = this.$store.state.token;
if (!token) {
alert("用户未登录,请重新登录!");
this.$router.push("/login");
return;
}
const res = await axios.get('http://localhost:8080/student/examPaper/selectByUserId', {
headers: { Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',}
});
// res.data res.data.data
console.log('请求返回的数据:', res.data);
// res.data res.data.data grade
if (res.data && res.data.data && res.data.data.grade) {
const grade = res.data.data.grade;
console.log('学生年级:', grade);
} else {
console.error("返回数据缺少 grade 字段!", res.data);
alert("返回数据缺少年级信息!");
return;
}
const examRes = await axios.get(`http://localhost:8080/student/homepage/task_paper`, {
params: { name: this.name, subject: this.subject,grade: res.data.data.grade},
headers: { Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
}
});
console.log('examRes', examRes);
if (examRes.data.code === 200) {
const { name, grade, subject, list } = examRes.data.data;
this.examTitle = name;
this.totalScore = list.length * 10; // 10
this.duration = "60分钟"; //
this.remainingTime = 3600; // 1
this.questions = list.map(item => ({
id: item.id,
text: item.content,
options: item.chance ? item.chance.map(opt => ({ value: opt.label, label: opt.text })) : [],
}));
console.log("试题数据:", this.questions);
} else {
alert("加载试题失败:" + examRes.data.msg);
}
} catch (error) {
console.error("加载试题时发生错误:"+ error);
alert("加载试题失败,请稍后再试!");
}
},
//
async submitExam() {
try {
//
const unanswered = this.questions.filter(
(q) => !this.answers[q.id] || this.answers[q.id].trim() === ""
);
if (unanswered.length > 0) {
alert(`还有 ${unanswered.length} 道题未作答,请完成后再提交!`);
return;
}
//
const response = await axios.post("http://localhost:8080/api/exam/submit", {
examId: this.examId,
answers: this.answers,
});
if (response.data.code === 200) {
alert("考试提交成功!");
this.$router.push("/examList");
} else {
alert("提交失败:" + response.data.msg);
}
} catch (error) {
console.error("提交考试时发生错误:", error);
alert("提交试卷失败,请稍后再试!");
}
},
//
startCountdown() {
if (this.timer) clearInterval(this.timer);
this.timer = setInterval(() => {
if (this.remainingTime > 0) {
this.remainingTime -= 1;
} else {
clearInterval(this.timer);
alert("时间到,试卷将自动提交!");
this.submitExam();
}
}, 1000);
},
},
created() {
this.subject = this.$route.query.subject;
this.name = this.$route.query.name;
if (!this.subject && !this.name) {
alert("未获取到试卷,请重新选择试卷!");
this.$router.push("/exam");
return;
}
//
this.fetchQuestions();
},
mounted() {
this.startCountdown();
},
};
</script>
<style scoped>
/* 页面布局 */
.exam-container {
display: flex;
height: 100vh;
background-color: #f4f6f9;
padding: 0; /* 确保没有额外的内边距 */
position: relative;
}
/* 左侧栏样式 */
.sidebar {
width: 20%;
padding: 20px;
background-color: #ffffff;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
text-align: center;
position: sticky;
top: 0; /* 固定位置 */
height: 100vh; /* 确保左侧栏一直显示 */
}
/* 使左侧栏文字不会被遮挡 */
.sidebar h2 {
font-size: 20px;
margin-bottom: 20px;
}
.time-remaining span {
color: red;
font-size: 24px;
}
.submit-button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.submit-button:hover {
background-color: #0056b3;
}
/* 主内容区域 */
.main-content {
flex: 1;
padding: 30px;
background-color: #ffffff; /* 设置和左侧栏一致的背景 */
overflow-y: auto; /* 允许滚动 */
height: 100vh; /* 确保主内容区域可以填满视口高度 */
box-sizing: border-box; /* 包括内边距在内的大小计算 */
}
/* 题目区域 */
.question {
margin-bottom: 20px; /* 保证每道题目之间有间距 */
padding-bottom: 10px; /* 让横线不与题目文字紧贴 */
border-bottom: 1px solid #e0e0e0; /* 设置浅色横线 */
}
.question p {
font-size: 2rem; /* 默认字体大小 */
line-height: 1.5;
margin-bottom: 20px;
}
/* 选项样式 */
.options label {
font-size: 1.2rem; /* 默认字体大小 */
display: flex;
align-items: center;
margin-bottom: 10px;
}
.options input[type="radio"] {
margin-right: 10px;
}
textarea {
width: 100%;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
}
/* 加载中样式 */
.loading {
text-align: center;
font-size: 18px;
color: #666;
margin-top: 50px;
}
/* 页面根元素字体大小 */
html {
font-size: 16px;
}
</style>
Loading…
Cancel
Save