|
|
<template>
|
|
|
<el-container style="min-height: 100vh">
|
|
|
<el-aside :width="sideWidth+'px'" style="background-color: rgb(238, 241, 246); box-shadow: 2px 0 6px rgb(0 21 41 / 35%)">
|
|
|
<Aside :isCollapse="isCollapse" :logoTextShow="logoTextShow"/>
|
|
|
</el-aside>
|
|
|
|
|
|
<el-container>
|
|
|
<el-header style="border-bottom:1px solid #ccc;">
|
|
|
<Header :collapseBtnClass="collapseBtnClass" :collapse="collapse"/>
|
|
|
</el-header>
|
|
|
<el-main>
|
|
|
<div id="app">
|
|
|
<div class="filter-container">
|
|
|
<div class="filter-group">
|
|
|
<label>试卷类型:</label>
|
|
|
<button
|
|
|
v-for="(type, index) in paperTypes"
|
|
|
:key="index"
|
|
|
:class="{ active: selectedType === type }"
|
|
|
@click="handleTypeChange(type)"
|
|
|
>
|
|
|
{{ type }}
|
|
|
</button>
|
|
|
</div>
|
|
|
<div class="filter-group">
|
|
|
<label>试卷学科:</label>
|
|
|
<button
|
|
|
v-for="(subject, index) in subjects"
|
|
|
:key="index"
|
|
|
:class="{ active: selectedSubject === subject }"
|
|
|
@click="handleSubjectChange(subject)"
|
|
|
>
|
|
|
{{ subject }}
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-if="loading" class="loading">加载中...</div>
|
|
|
<div v-else-if="filteredPapers.length === 0" class="empty">暂无试卷</div>
|
|
|
<div v-else class="card-container">
|
|
|
<div
|
|
|
v-for="(paper, index) in filteredPapers"
|
|
|
:key="index"
|
|
|
class="card"
|
|
|
>
|
|
|
<h3>{{ paper.name }}</h3>
|
|
|
<p>学科:{{ paper.subject }}</p>
|
|
|
<p>题目数:{{ paper.questionCount }}</p>
|
|
|
<p>试卷总分:{{ paper.totalScore }}</p>
|
|
|
<p>考试时长:{{ paper.time + " 分钟"}}</p>
|
|
|
<p>开始时间:{{ paper.startTime || "未设置" }}</p>
|
|
|
<p>结束时间:{{ paper.endTime || "未设置" }}</p>
|
|
|
<button
|
|
|
v-if="paper.hasTaken"
|
|
|
class="start-button taken"
|
|
|
disabled
|
|
|
>
|
|
|
已考过
|
|
|
</button>
|
|
|
<button
|
|
|
v-else
|
|
|
class="start-button"
|
|
|
@click="startExam(paper.id, paper.name, paper.subject, paper.time)"
|
|
|
>
|
|
|
开始答题
|
|
|
</button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-main>
|
|
|
</el-container>
|
|
|
</el-container>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import axios from "axios";
|
|
|
import { mapActions } from "vuex";
|
|
|
import Aside from "@/components/Aside.vue";
|
|
|
import Header from "@/components/Header.vue";
|
|
|
|
|
|
export default {
|
|
|
name: 'Exam',
|
|
|
components: {Aside, Header},
|
|
|
data() {
|
|
|
return {
|
|
|
collapse: false,
|
|
|
paperTypes: ["固定试卷", "时段试卷", "班级试卷"], // 试卷类型
|
|
|
subjects: ["语文", "数学"], // 学科
|
|
|
selectedType: "班级试卷", // 默认选中的试卷类型
|
|
|
selectedSubject: "语文", // 默认选中的学科
|
|
|
papers: [], // 试卷数据
|
|
|
loading: true, // 加载状态
|
|
|
tableData: [],
|
|
|
collapseBtnClass: 'el-icon-s-fold',
|
|
|
isCollapse: false,
|
|
|
sideWidth: 200,
|
|
|
logoTextShow: true,
|
|
|
hasTaken:false
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
filteredPapers() {
|
|
|
// 根据筛选条件动态过滤试卷
|
|
|
return this.papers.filter(
|
|
|
(paper) =>
|
|
|
paper.subject === this.selectedSubject
|
|
|
);
|
|
|
},
|
|
|
},
|
|
|
created() {
|
|
|
this.fetchPapers(); // 页面加载时获取试卷数据
|
|
|
},
|
|
|
watch: {
|
|
|
// 监听试卷类型或学科的变化,调用 fetchPapers 重新加载数据
|
|
|
selectedType() {
|
|
|
this.fetchPapers(); // 当选中的试卷类型改变时,重新加载试卷数据
|
|
|
},
|
|
|
selectedSubject() {
|
|
|
this.fetchPapers(); // 当选中的学科改变时,重新加载试卷数据
|
|
|
},
|
|
|
},
|
|
|
methods: {
|
|
|
startExam(id,name,subject,time) {
|
|
|
this.$router.push({ name: "ExamPaper", query: { id:id ,name: name, subject: subject ,time:time} });
|
|
|
},
|
|
|
handleTypeChange(type) {
|
|
|
this.selectedType = type; // 修改选中的试卷类型
|
|
|
},
|
|
|
handleSubjectChange(subject) {
|
|
|
this.selectedSubject = subject; // 修改选中的学科
|
|
|
},
|
|
|
async checkExamStatus(testId) {
|
|
|
const token = this.$store.state.token;
|
|
|
|
|
|
console.log('token',token);
|
|
|
if (!token) {
|
|
|
alert("用户未登录,请重新登录!");
|
|
|
this.$router.push('/login');
|
|
|
return false;
|
|
|
}
|
|
|
try {
|
|
|
const response = await axios.get(`http://localhost:8080/student/examPaper/whetherTest`, {
|
|
|
params: { testId },
|
|
|
headers: {
|
|
|
Authorization: `Bearer ${token}`,
|
|
|
'Content-Type': 'application/json',
|
|
|
},
|
|
|
});
|
|
|
if (response.status === 200) {
|
|
|
return response.data; // 假设返回的数据中包含 hasTaken 字段
|
|
|
} else {
|
|
|
console.warn("检查考试状态失败:" + response.data.msg);
|
|
|
return false; // 默认未考
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error("检查考试状态接口失败:", error);
|
|
|
return false;
|
|
|
}
|
|
|
},
|
|
|
async fetchPapers(page = 1, size = 10) {
|
|
|
const token = this.$store.state.token; // 获取 token
|
|
|
console.log("token:",token);
|
|
|
if (!token) {
|
|
|
alert("用户未登录,请重新登录!");
|
|
|
this.$router.push('/login'); // 引导用户重新登录
|
|
|
return;
|
|
|
}
|
|
|
try {
|
|
|
this.loading = true;
|
|
|
|
|
|
// 确保 page 和 size 参数是有效的
|
|
|
const pageNumber = page || 1; // 使用默认值 1
|
|
|
const pageSize = size || 10; // 使用默认值 10
|
|
|
|
|
|
const response = await axios.get("http://localhost:8080/student/examPaper/examPaperByTypeAndSubject", {
|
|
|
params: {
|
|
|
pagenum: pageNumber,
|
|
|
pagesize: pageSize, // 确保传递 pagesize
|
|
|
type: this.selectedType, // 类型
|
|
|
subject: this.selectedSubject, // 学科
|
|
|
},
|
|
|
headers: {
|
|
|
Authorization: `Bearer ${token}`, // 使用 Vuex 中的 token
|
|
|
'Content-Type': 'application/json',
|
|
|
},
|
|
|
});
|
|
|
|
|
|
if (response.data.code === 200) {
|
|
|
const { records } = response.data.data;
|
|
|
|
|
|
const updatedRecords = await Promise.all(
|
|
|
records.map(async (record) => {
|
|
|
const hasTaken = await this.checkExamStatus(record.id);
|
|
|
return {
|
|
|
id: record.id,
|
|
|
name: record.name,
|
|
|
subject: record.subject,
|
|
|
questionCount: record.totalquestion,
|
|
|
totalScore: record.totalscore,
|
|
|
time: record.time,
|
|
|
startTime: record.start_time || "未设置",
|
|
|
endTime: record.end_time || "未设置",
|
|
|
hasTaken: hasTaken,
|
|
|
};
|
|
|
})
|
|
|
);
|
|
|
this.papers = updatedRecords;
|
|
|
console.log("更新后的试卷数据:", updatedRecords);
|
|
|
} else {
|
|
|
alert("数据加载失败:" + response.data.msg);
|
|
|
this.$router.push('/login'); // 引导用户重新登录
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error("获取试卷数据失败:", error);
|
|
|
alert("试卷数据加载失败,请稍后重试!");
|
|
|
} finally {
|
|
|
this.loading = false;
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.start-button.taken {
|
|
|
background-color: #ccc;
|
|
|
cursor: not-allowed;
|
|
|
}
|
|
|
|
|
|
.start-button.taken:hover {
|
|
|
background-color: #ccc; /* 已考过状态无变化 */
|
|
|
}
|
|
|
|
|
|
/* 顶部筛选部分 */
|
|
|
.filter-container {
|
|
|
margin: 20px;
|
|
|
display: flex;
|
|
|
flex-direction: column; /* 设置为垂直排列 */
|
|
|
gap: 15px; /* 使两个筛选组之间有一定的间距 */
|
|
|
}
|
|
|
|
|
|
.filter-group {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.filter-group label {
|
|
|
margin-right: 10px;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
|
|
|
button {
|
|
|
margin: 5px 10px 5px 0; /* 设置按钮的间距 */
|
|
|
padding: 5px 10px;
|
|
|
border: none;
|
|
|
background-color: #f0f0f0;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
button.active {
|
|
|
background-color: #007bff;
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
/* 卡片部分 */
|
|
|
.card-container {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 20px;
|
|
|
padding: 20px;
|
|
|
justify-content: space-between; /* Ensures spacing between cards */
|
|
|
}
|
|
|
|
|
|
.card {
|
|
|
width: calc(33.333% - 20px); /* Each card takes up 1/3 of the container width, minus gap */
|
|
|
border: 1px solid #ccc;
|
|
|
border-radius: 5px;
|
|
|
padding: 15px;
|
|
|
background-color: #fff;
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
box-sizing: border-box; /* Ensures padding and border are included in the width */
|
|
|
}
|
|
|
|
|
|
.card h3 {
|
|
|
margin: 0 0 10px;
|
|
|
font-size: 16px;
|
|
|
}
|
|
|
|
|
|
.card p {
|
|
|
margin: 5px 0;
|
|
|
font-size: 14px;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.start-button {
|
|
|
margin-top: 10px;
|
|
|
width: 100%;
|
|
|
padding: 5px;
|
|
|
background-color: #007bff;
|
|
|
color: white;
|
|
|
border: none;
|
|
|
border-radius: 3px;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
.start-button:hover {
|
|
|
background-color: #0056b3;
|
|
|
}
|
|
|
|
|
|
/* 加载和空状态 */
|
|
|
.loading,
|
|
|
.empty {
|
|
|
text-align: center;
|
|
|
padding: 20px;
|
|
|
font-size: 16px;
|
|
|
color: #666;
|
|
|
}
|
|
|
</style>
|