前端增加分数查询,试卷查询界面,修改后端部分接口

main
yuan 1 week ago
parent ef4db46a90
commit b1b82d6e58

@ -75,9 +75,12 @@ public class AnswerSheetController {
//主观题自动更改
@ApiOperation("主观题批改")
@GetMapping("answerDisplay")
public R<String> answerDisplay(String sender, Long id) {
public R<String> answerDisplay( Long id) {
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
Long userid = sysUser.getUserId();
String sender = iUserService.user(iUserService.selectByzhid(id)).getTname();
List<Questionbank> list = iExamCreateService.selectListByPid(id);
Answer answer = iAnswerService.selectByTestId(sender, id);
Answer answer = iAnswerService.selectByTestId(userid,sender, id);
QuestionAnswerDO questionAnswerDO = iQuestionanswerService.createTest(list, answer);
if (iQuestionanswerService.addTest(questionAnswerDO) != 0) {
return R.ok("该学生客观题已改完!");
@ -88,8 +91,8 @@ public class AnswerSheetController {
@ApiOperation("查看学生的考试情况(最新)")
@GetMapping("selectQuestionanswer")
public R<QuestionAnswerDO> selectQuestionanswer(String sender, Long testid) {
return R.ok(iQuestionanswerService.selectQuestionanswer(sender,testid));
public R<QuestionAnswerDO> selectQuestionanswer(Long senderId,String sender, Long testid) {
return R.ok(iQuestionanswerService.selectQuestionanswer(senderId,sender,testid));
}
@ApiOperation("批改客观题")
@ -104,8 +107,8 @@ public class AnswerSheetController {
@ApiOperation("试卷批改完毕提交")
@PostMapping("addmarkedtest")
public R<String> addmarkedtest(String sender,Long testid){
if(iMarkedtestService.addmarkedtest(sender,testid)!=0){
public R<String> addmarkedtest(Long senderId,String sender,Long testid){
if(iMarkedtestService.addmarkedtest(senderId,sender,testid)!=0){
return R.ok("提交完成!");
}
return R.fail("提交失败!");

@ -38,6 +38,9 @@ public class StudentExamPaperController {
@Autowired
private IExamCreateService iExamCreateService;
@Autowired
private IAnswerService iAnswerService;
@ApiOperation("班级查询")
@GetMapping("selectByUserId")
public R<StudentClass> selectByUserId(){
@ -47,6 +50,21 @@ public class StudentExamPaperController {
return R.ok(iStudentClassService.selectById(userid));
}
@ApiOperation("获取试卷")
@GetMapping("examPaperById")
public R<ExamPaper> examPaperById(Long id){
return R.ok(iExamPaperService.selectById(id));
}
@ApiOperation("判断是否考过")
@GetMapping("whetherTest")
public Boolean whetherTest(Long testId){
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
Long userid = iUserService.selectByzhid(sysUser.getUserId());
return iAnswerService.selectBySenderIdAndTestId(userid,testId);
}
@ApiOperation("试卷查询")
@GetMapping("examPaperByTypeAndSubject")
public R<IPage<ExamPaperDO1>> examPaperByTypeAndSubject(int pagenum,int pagesize,String type,String subject){
@ -70,6 +88,7 @@ public class StudentExamPaperController {
continue;
}
ExamPaperDO1 examPaperDO1 = new ExamPaperDO1();
examPaperDO1.setId(examPaper.getId());
examPaperDO1.setName(examPaper.getName());
examPaperDO1.setTime(examPaper.getTime());
examPaperDO1.setSubject(examPaper.getSubject());

@ -60,6 +60,7 @@ public class StudentHomePageController {
String answerVo = gson.toJson(answerVos);
Answer answer = new Answer();
answer.setSenderId(id);
answer.setSender(sender);
answer.setTestid(answerDO.getTestid());
answer.setIdanswer(answerVo);
@ -72,12 +73,20 @@ public class StudentHomePageController {
}
@ApiOperation("任务中心分数")
@PostMapping("task_score")
public R<Double> taskScore(Long testid){
@GetMapping("task_score")
public R<List<Markedtest>> taskScore(){
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
Long id = sysUser.getUserId();
String sender = iUserService.user(iUserService.selectByzhid(id)).getTname();
return R.ok(iMarkedtestService.selectmarkedtestBySenderAndTestid(sender,testid).getScore());
return R.ok(iMarkedtestService.selectmarkedtestListBySenderAndSenderId(sender,id));
}
@ApiOperation("获取某一张的分数")
@GetMapping("getScoreByTestId")
public R<Markedtest> getScoreByTestId(Long testId) {
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
Long id = sysUser.getUserId();
return R.ok(iMarkedtestService.selectmarkedtestByTestid(id,testId));
}
@ApiOperation("查看学生的考试情况(最新)")
@ -86,7 +95,7 @@ public class StudentHomePageController {
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
Long id = sysUser.getUserId();
String sender = iUserService.user(iUserService.selectByzhid(id)).getTname();
return R.ok(iQuestionanswerService.selectQuestionanswer(sender,testid));
return R.ok(iQuestionanswerService.selectQuestionanswer(id,sender,testid));
}
//主观题自动更改
@ -97,7 +106,7 @@ public class StudentHomePageController {
Long id1 = sysUser.getUserId();
String sender = iUserService.user(iUserService.selectByzhid(id1)).getTname();
List<Questionbank> list = iExamCreateService.selectListByPid(id);
Answer answer = iAnswerService.selectByTestId(sender, id);
Answer answer = iAnswerService.selectByTestId(id1,sender, id);
QuestionAnswerDO questionAnswerDO = iQuestionanswerService.createTest(list, answer);
if (iQuestionanswerService.addTest(questionAnswerDO) != 0) {
return R.ok("该学生客观题已改完!");

@ -106,8 +106,8 @@ public class TeacherAnalysisController {
String tname = iUserService.user(studentid).getTname();
System.out.println(tname);
Double score = 0.0;
if(iMarkedtestService.selectmarkedtestBySenderAndTestid(tname,testid)!=null){
score =iMarkedtestService.selectmarkedtestBySenderAndTestid(tname,testid).getScore();
if(iMarkedtestService.selectmarkedtestBySenderAndTestid(studentClass.getStudentid(),testid)!=null){
score =iMarkedtestService.selectmarkedtestBySenderAndTestid(studentClass.getStudentid(),testid).getScore();
}
sum+=score;
}

@ -47,8 +47,8 @@ public class TeacherAnswerSheetController {
@ApiOperation("查看学生的考试情况(最新)")
@GetMapping("selectQuestionanswer")
public R<QuestionAnswerDO> selectQuestionanswer(String sender, Long testid) {
return R.ok(iQuestionanswerService.selectQuestionanswer(sender,testid));
public R<QuestionAnswerDO> selectQuestionanswer(Long senderId,String sender, Long testid) {
return R.ok(iQuestionanswerService.selectQuestionanswer(senderId,sender,testid));
}
@ApiOperation("批改客观题")
@ -63,8 +63,8 @@ public class TeacherAnswerSheetController {
@ApiOperation("试卷批改完毕提交")
@PostMapping("addmarkedtest")
public R<String> addmarkedtest(String sender,Long testid){
if(iMarkedtestService.addmarkedtest(sender,testid)!=0){
public R<String> addmarkedtest(Long senderId,String sender,Long testid){
if(iMarkedtestService.addmarkedtest(senderId,sender,testid)!=0){
return R.ok("提交完成!");
}
return R.fail("提交失败!");

@ -8,8 +8,9 @@ import lombok.Data;
public class Answer {
private Long senderId;
private String sender;
private long testid;
private Long testid;
private String idanswer;
private String finishtime;
private Integer time;

@ -4,7 +4,7 @@ import lombok.Data;
@Data
public class ExamPaperDO1 {
private Long id;
private String name;
private String subject;
private Integer totalquestion;

@ -12,5 +12,6 @@ public class QuestionbankDO {
private String content;
private List<Map<String,String>> chance;
private double score;
private String type;
}

@ -7,13 +7,13 @@ import java.util.List;
@Data
public class SingleChoiceDO {
private int type;
private Long type;
private String subject;
private String grade;
private String content;
private List<OptionDO> list;
private String answer;
private double score;
private long difficulty;
private Double score;
private Long difficulty;
private String analysis;
}

@ -11,10 +11,10 @@ public class Errorbook {
private String content;
private String chance;
private String answer;
private long difficulty;
private Long difficulty;
private String analysis;
private String subject;
private Integer type;
private Long type;
private Double score;

@ -7,6 +7,7 @@ import lombok.Data;
@TableName("markedtest")
public class Markedtest {
private Long senderId;
private String sender;
private long testid;
private double score;

@ -7,6 +7,7 @@ import lombok.Data;
@TableName("questionanswer")
public class Questionanswer {
private Long senderId;
private String sender;
private long testid;
private String qa;

@ -6,14 +6,14 @@ import lombok.Data;
@Data
@TableName("questionbank")
public class Questionbank {
private long id;
private int questiontype;
private Long id;
private Long questiontype;
private String subject;
private String content;
private String chance;
private String answer;
private double score;
private Double score;
private String analysis;
private long difficulty;
private Long difficulty;
private String createtime;
}

@ -8,7 +8,7 @@ import lombok.Data;
@TableName("student_class")
public class StudentClass {
private long studentid;
private Long studentid;
private String grade;
private String Class1;

@ -9,8 +9,10 @@ public interface IAnswerService {
int answer(Answer answer);
Answer selectByTestId(String sender,Long testid);
Answer selectByTestId(Long senderId,String sender,Long testid);
List<Answer> selectBySender(String sender);
Boolean selectBySenderIdAndTestId(Long senderId,Long testid);
}

@ -8,7 +8,7 @@ import java.util.List;
public interface IMarkedtestService {
int addmarkedtest(String sender,Long testid);
int addmarkedtest(Long senderId,String sender,Long testid);
IPage<Markedtest> selectmarkedtest(int pagenum);
@ -18,6 +18,11 @@ public interface IMarkedtestService {
IPage<Markedtest> selectmarkedtestBySubject(int pagenum,List<StudentClass> list,String subject);
Markedtest selectmarkedtestBySenderAndTestid(String sender,Long testid);
List<Markedtest> selectmarkedtestListBySenderAndSenderId(String sender,Long senderId);
Markedtest selectmarkedtestBySenderAndTestid(String sender,Long senderId);
Markedtest selectmarkedtestBySenderAndTestid(Long senderId,Long testId);
Markedtest selectmarkedtestByTestid(Long id,Long testId);
}

@ -2,7 +2,7 @@ package com.ruoyi.test.seriver;
public interface IQuestionTypeService {
String getQuestiontype(int id);
String getQuestiontype(Long id);
Long getId(String type);

@ -17,7 +17,7 @@ public interface IQuestionanswerService {
int addTest(QuestionAnswerDO questionAnswerDO);
QuestionAnswerDO selectQuestionanswer(String sender,Long id);
QuestionAnswerDO selectQuestionanswer(Long senderId,String sender,Long id);
int markObjectiveQuestions(QuestionAnswerDO questionAnswerDO);

@ -24,10 +24,11 @@ public class AnswerServiceImpl extends ServiceImpl<AnswerMapper, Answer> impleme
}
@Override
public Answer selectByTestId(String sender,Long testid) {
public Answer selectByTestId(Long senderId,String sender,Long testid) {
LambdaQueryWrapper<Answer> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Answer::getTestid,testid);
queryWrapper.eq(Answer::getSender,sender);
queryWrapper.eq(Answer::getSenderId,senderId);
queryWrapper.orderByDesc(Answer::getFinishtime).last("LIMIT 1");
return answerMapper.selectOne(queryWrapper);
}
@ -36,4 +37,16 @@ public class AnswerServiceImpl extends ServiceImpl<AnswerMapper, Answer> impleme
public List<Answer> selectBySender(String sender) {
return answerMapper.selectBySender(sender);
}
@Override
public Boolean selectBySenderIdAndTestId(Long senderId, Long testid) {
LambdaQueryWrapper<Answer> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Answer::getSenderId,senderId)
.eq(Answer::getTestid,testid);
if(answerMapper.selectCount(queryWrapper)>0){
return true;
}else{
return false;
}
}
}

@ -11,10 +11,7 @@ import com.ruoyi.test.domain.Questionbank;
import com.ruoyi.test.domain.Vo.ExamCreateVo;
import com.ruoyi.test.domain.Vo.QuestionbankVo;
import com.ruoyi.test.mapper.*;
import com.ruoyi.test.seriver.IAnswerSheetDOService;
import com.ruoyi.test.seriver.IExamCreateService;
import com.ruoyi.test.seriver.IExamDOService;
import com.ruoyi.test.seriver.IExamPaperService;
import com.ruoyi.test.seriver.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -43,6 +40,9 @@ public class AnswerSheetDOServiceImpl extends ServiceImpl<AnswerSheetDOMapper, A
@Autowired
private IExamDOService iExamDOService;
@Autowired
private IQuestionTypeService iQuestionTypeService;
@Override
public AnswerSheetDO testPaper(String subject, String grade, String name) {
Long pid = iExamPaperService.selectIdBySubjectAndGradeAndName(subject,grade,name);
@ -60,6 +60,7 @@ public class AnswerSheetDOServiceImpl extends ServiceImpl<AnswerSheetDOMapper, A
questionbankDO.setId(questionbank.getId());
questionbankDO.setContent(questionbank.getContent());
questionbankDO.setScore(questionbank.getScore());
questionbankDO.setType(iQuestionTypeService.getQuestiontype(questionbank.getQuestiontype()));
sum=sum+questionbank.getScore();
String chance = questionbank.getChance();
List<Map<String,String>> listMap = gson.fromJson(chance,List.class);

@ -50,7 +50,7 @@ public class ErrorbookServiceImpl extends ServiceImpl<ErrorbookMapper, Errorbook
errorbookVo.setChance(listMap);
errorbookVo.setAnalysis(errorbook.getAnalysis());
errorbookVo.setSubject(errorbook.getSubject());
Integer type = errorbook.getType();
Long type = errorbook.getType();
String type1 = iQuestionTypeService.getQuestiontype(type);
errorbookVo.setType(type1);
errorbookVo.setScore(errorbook.getScore());
@ -81,7 +81,7 @@ public class ErrorbookServiceImpl extends ServiceImpl<ErrorbookMapper, Errorbook
errorbookVo.setChance(listMap);
errorbookVo.setAnalysis(errorbook.getAnalysis());
errorbookVo.setSubject(errorbook.getSubject());
Integer type = errorbook.getType();
Long type = errorbook.getType();
String type1 = iQuestionTypeService.getQuestiontype(type);
errorbookVo.setType(type1);
errorbookVo.setScore(errorbook.getScore());

@ -20,6 +20,7 @@ import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service
@ -47,8 +48,8 @@ public class MarkedtestServiceImpl extends ServiceImpl<MarkedtestMapper, Markedt
private IUserService iUserService;
@Override
public int addmarkedtest(String sender,Long testid) {
QuestionAnswerDO questionAnswerDO = iQuestionanswerService.selectQuestionanswer(sender,testid);
public int addmarkedtest(Long senderId,String sender,Long testid) {
QuestionAnswerDO questionAnswerDO = iQuestionanswerService.selectQuestionanswer(senderId,sender,testid);
int totalscore = 0;
int score = 0;
@ -67,9 +68,10 @@ public class MarkedtestServiceImpl extends ServiceImpl<MarkedtestMapper, Markedt
totalquestions++;
}
Answer answer = iAnswerService.selectByTestId(sender, testid);
Answer answer = iAnswerService.selectByTestId(senderId,sender, testid);
Markedtest markedtest = new Markedtest();
markedtest.setSenderId(senderId);
markedtest.setSender(sender);
markedtest.setTestid(testid);
markedtest.setScore(score);
@ -152,10 +154,34 @@ public class MarkedtestServiceImpl extends ServiceImpl<MarkedtestMapper, Markedt
}
@Override
public Markedtest selectmarkedtestBySenderAndTestid(String sender, Long testid) {
public List<Markedtest> selectmarkedtestListBySenderAndSenderId(String sender, Long senderId) {
LambdaQueryWrapper<Markedtest> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Markedtest::getSender,sender)
.eq(Markedtest::getTestid,testid);
.eq(Markedtest::getSenderId,senderId);
return markedtestMapper.selectList(queryWrapper);
}
@Override
public Markedtest selectmarkedtestBySenderAndTestid(String sender,Long senderId) {
LambdaQueryWrapper<Markedtest> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Markedtest::getSender,sender)
.eq(Markedtest::getSenderId,senderId);
return markedtestMapper.selectOne(queryWrapper);
}
@Override
public Markedtest selectmarkedtestBySenderAndTestid(Long senderId, Long testId) {
LambdaQueryWrapper<Markedtest> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Markedtest::getTestid,testId)
.eq(Markedtest::getSenderId,senderId);
return markedtestMapper.selectOne(queryWrapper);
}
@Override
public Markedtest selectmarkedtestByTestid(Long id, Long testId) {
LambdaQueryWrapper<Markedtest> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Markedtest::getSenderId,id)
.eq(Markedtest::getTestid,testId);
return markedtestMapper.selectOne(queryWrapper);
}

@ -15,7 +15,10 @@ public class QuestionTypeServiceImpl extends ServiceImpl<QuestionTypeMapper, Que
private QuestionTypeMapper questionTypeMapper;
@Override
public String getQuestiontype(int id) {
public String getQuestiontype(Long id) {
System.out.println("--------------"+id);
LambdaQueryWrapper<QuestionType> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(QuestionType::getId,id);
return questionTypeMapper.selectOne(queryWrapper).getType();

@ -91,12 +91,15 @@ public class QuestionanswerServiceImpl extends ServiceImpl<QuestionanswerMapper,
}
@Override
public QuestionAnswerDO selectQuestionanswer(String sender, Long id) {
public QuestionAnswerDO selectQuestionanswer(Long senderId,String sender, Long id) {
System.out.println("-----------"+senderId+" "+sender+" "+id);
LambdaQueryWrapper<Questionanswer> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Questionanswer::getSenderId,senderId);
queryWrapper.eq(Questionanswer::getSender,sender);
queryWrapper.eq(Questionanswer::getTestid,id);
queryWrapper.orderByDesc(Questionanswer::getCreatetime).last("LIMIT 1");
Questionanswer questionanswer = questionanswerMapper.selectOne(queryWrapper);
System.out.println("========"+questionanswer);
String qa = questionanswer.getQa();
// System.out.println(qa);
Gson gson = new Gson();

@ -32,7 +32,7 @@ public class QuestionbankServiceImpl extends ServiceImpl<QuestionbankMapper, Que
QuestionbankDO1 questionbankDO1 = new QuestionbankDO1();
questionbankDO1.setId(questionbank.getId());
questionbankDO1.setContent(questionbank.getContent());
int questiontype = questionbank.getQuestiontype();
Long questiontype = questionbank.getQuestiontype();
questionbankDO1.setQuestiontype(iQuestionTypeService.getQuestiontype(questiontype));
return questionbankDO1;
}

@ -8,12 +8,14 @@
<!-- 批量插入Answer对象 -->
<insert id="answer" parameterType="Answer">
INSERT INTO answer (
sender_id,
sender,
testid,
idanswer,
finishtime,
time
)VALUES(
#{answer.senderId},
#{answer.sender},
#{answer.testid},
#{answer.idanswer},

@ -7,6 +7,7 @@
<insert id="addmarkedtest" parameterType="Markedtest">
insert into markedtest(
sender_id,
sender,
testid,
score,
@ -16,7 +17,8 @@
time,
finishtime
)values (
#{markedtest.sender},
#{markedtest.senderId},
#{markedtest.sender},
#{markedtest.testid},
#{markedtest.score},
#{markedtest.totalscore},

@ -19,6 +19,8 @@
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"sass": "^1.81.0",
"sass-loader": "^16.0.3",
"vue-template-compiler": "^2.6.14"
}
},
@ -1713,6 +1715,302 @@
"node": ">= 8"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.0.tgz",
"integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.0",
"@parcel/watcher-darwin-arm64": "2.5.0",
"@parcel/watcher-darwin-x64": "2.5.0",
"@parcel/watcher-freebsd-x64": "2.5.0",
"@parcel/watcher-linux-arm-glibc": "2.5.0",
"@parcel/watcher-linux-arm-musl": "2.5.0",
"@parcel/watcher-linux-arm64-glibc": "2.5.0",
"@parcel/watcher-linux-arm64-musl": "2.5.0",
"@parcel/watcher-linux-x64-glibc": "2.5.0",
"@parcel/watcher-linux-x64-musl": "2.5.0",
"@parcel/watcher-win32-arm64": "2.5.0",
"@parcel/watcher-win32-ia32": "2.5.0",
"@parcel/watcher-win32-x64": "2.5.0"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz",
"integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz",
"integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz",
"integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz",
"integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz",
"integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz",
"integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz",
"integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz",
"integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz",
"integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz",
"integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz",
"integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz",
"integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz",
"integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.28",
"resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.28.tgz",
@ -4494,6 +4792,19 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz",
@ -5762,6 +6073,12 @@
"node": ">= 4"
}
},
"node_modules/immutable": {
"version": "5.0.3",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.0.3.tgz",
"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
"dev": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
@ -6780,6 +7097,13 @@
"tslib": "^2.0.3"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"optional": true
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
@ -8380,6 +8704,94 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"node_modules/sass": {
"version": "1.81.0",
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.81.0.tgz",
"integrity": "sha512-Q4fOxRfhmv3sqCLoGfvrC9pRV8btc0UtqL9mN6Yrv6Qi9ScL55CVH1vlPP863ISLEEMNLLuu9P+enCeGHlnzhA==",
"dev": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sass-loader": {
"version": "16.0.3",
"resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-16.0.3.tgz",
"integrity": "sha512-gosNorT1RCkuCMyihv6FBRR7BMV06oKRAs+l4UMp1mlcVg9rWN6KMmUj3igjQwmYys4mDP3etEYJgiHRbgHCHA==",
"dev": true,
"dependencies": {
"neo-async": "^2.6.2"
},
"engines": {
"node": ">= 18.12.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"@rspack/core": "0.x || 1.x",
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0",
"sass": "^1.3.0",
"sass-embedded": "*",
"webpack": "^5.0.0"
},
"peerDependenciesMeta": {
"@rspack/core": {
"optional": true
},
"node-sass": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"webpack": {
"optional": true
}
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
"dev": true,
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
"dev": true,
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",

@ -18,6 +18,8 @@
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"sass": "^1.81.0",
"sass-loader": "^16.0.3",
"vue-template-compiler": "^2.6.14"
},
"browserslist": [

@ -22,9 +22,9 @@
<span slot="title">试卷中心</span>
</template>
</el-menu-item>
<el-menu-item index="3">
<el-menu-item index="/student/scoreList">
<template slot="title"><i class="el-icon-message"></i>
<span slot="title">考试记录</span>
<span slot="title">分数查询</span>
</template>
</el-menu-item>
</el-menu>

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

@ -49,7 +49,20 @@
<p>考试时长{{ paper.time + " 分钟"}}</p>
<p>开始时间{{ paper.startTime || "未设置" }}</p>
<p>结束时间{{ paper.endTime || "未设置" }}</p>
<button class="start-button" @click="startExam(paper.id,paper.name, paper.subject,paper.time)">开始答题</button>
<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>
@ -80,7 +93,8 @@ export default {
collapseBtnClass: 'el-icon-s-fold',
isCollapse: false,
sideWidth: 200,
logoTextShow: true
logoTextShow: true,
hasTaken:false
};
},
computed: {
@ -114,18 +128,45 @@ export default {
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;
const token = this.$store.state.token; // token
if (!token) {
alert("用户未登录,请重新登录!");
this.$router.push('/login'); //
return;
}
// page size
const pageNumber = page || 1; // 使 1
const pageSize = size || 10; // 使 10
@ -144,19 +185,29 @@ export default {
});
if (response.data.code === 200) {
const {records} = response.data.data;
this.papers = records.map((record) => ({
name: record.name,
subject: record.subject,
questionCount: record.totalquestion,
totalScore: record.totalscore,
time: record.time,
startTime: record.start_time || "未设置",
endTime: record.end_time || "未设置",
}));
console.log("试卷数据:", this.papers);
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);
@ -165,11 +216,22 @@ export default {
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;

@ -7,12 +7,11 @@
<p class="time-remaining">剩余时间<br /><span>{{ formattedTime }}</span></p>
<button class="submit-button" @click="submitExam" :disabled="loading">提交试卷</button>
<div class="question-navigation">
<!-- Question navigation with clickable boxes -->
<div
v-for="(question, index) in questions"
:key="question.id"
class="question-box"
:class="{ answered: answers[question.id] }"
:class="{ answered: answers[question.id]?.length > 0 }"
@click="scrollToQuestion(index)"
>
{{ index + 1 }}
@ -22,18 +21,14 @@
<!-- 主内容 -->
<div class="main-content" v-if="!loading && questions.length > 0">
<div
class="question"
v-for="(question, index) in questions"
:key="question.id"
:ref="'question-' + question.id"
>
<div class="question" v-for="(question, index) in questions" :key="question.id" :ref="'question-' + question.id">
<p>{{ index + 1 }}. {{ question.text }}
<span class="score" v-if="question.score">{{ question.score }} </span>
<span class="type" v-if="question.type">{{ question.type }}</span>
</p>
<div class="options">
<!-- 有选项 -->
<template v-if="question.options.length > 0">
<!-- 单选题 -->
<template v-if="question.type === '单选题'">
<label v-for="(option, optIndex) in question.options" :key="optIndex">
<input
type="radio"
@ -44,7 +39,42 @@
{{ option.label }}
</label>
</template>
<!-- 无选项 -->
<!-- 多选题 -->
<template v-else-if="question.type === ''">
<label v-for="(option, optIndex) in question.options" :key="optIndex">
<input
type="checkbox"
:value="option.value"
v-model="answers[question.id]"
/>
{{ option.label }}
</label>
</template>
<!-- 判断题 -->
<template v-else-if="question.type === ''">
<label>
<input
type="radio"
:name="'question-' + question.id"
value="T"
v-model="answers[question.id]"
/>
正确
</label>
<label>
<input
type="radio"
:name="'question-' + question.id"
value="F"
v-model="answers[question.id]"
/>
错误
</label>
</template>
<!-- 解答题和填空题 -->
<template v-else>
<textarea
v-model="answers[question.id]"
@ -70,37 +100,34 @@ export default {
name: "ExamPaper",
data() {
return {
examTitle: "考试中...", //
totalScore: 0, //
duration: "未知", //
remainingTime: 5*60, //
questions: [], //
answers: {}, //
loading: true, //
examId: null, // ID
timer: null, //
refreshTimer: null, //
examTitle: "考试中...",
totalScore: 0,
duration: "未知",
remainingTime: 5 * 60,
questions: [],
answers: {},
loading: true,
examId: null,
timer: null,
refreshTimer: null,
};
},
computed: {
//
formattedTime() {
const minutes = Math.floor(this.remainingTime / 60); //
const seconds = this.remainingTime % 60; //
const minutes = Math.floor(this.remainingTime / 60);
const seconds = this.remainingTime % 60;
return `${minutes}${seconds}`;
}
},
},
watch: {
questions(newVal) {
if (newVal.length > 0) {
//
this.startCountdown();
if (this.refreshTimer) clearTimeout(this.refreshTimer); //
if (this.refreshTimer) clearTimeout(this.refreshTimer);
}
},
},
methods: {
//
async fetchQuestions() {
try {
const token = this.$store.state.token;
@ -135,34 +162,39 @@ export default {
? item.chance.map((opt) => ({ value: opt.label, label: opt.text }))
: [],
score: item.score,
type: item.type,
}));
// answers
this.questions.forEach((question) => {
if (question.type === "多选题") {
this.answers[question.id] = [];
} else {
this.answers[question.id] = "";
}
});
} else {
alert("加载试题失败:" + examRes.data.msg);
this.$router.push('/login'); //
}
} else {
alert("返回数据缺少年级信息!");
this.$router.push('/student/exam'); //
}
} catch (error) {
console.error("加载试题时发生错误:", error);
alert("加载试题失败,请稍后再试!");
} finally {
this.loading = false;
this.startRefreshTimer(); //
}
},
scrollToQuestion(index) {
const questionId = this.questions[index].id;
let questionElement = this.$refs[`question-${questionId}`];
// questionElement
if (Array.isArray(questionElement)) {
//
console.log(`Element found as array for question ID: ${questionId}, Element:`, questionElement[0]);
questionElement = questionElement[0];
}
//
console.log(`Trying to scroll to question ID: ${questionId}, Element found:`, questionElement);
const questionElement = Array.isArray(this.$refs[`question-${questionId}`])
? this.$refs[`question-${questionId}`][0]
: this.$refs[`question-${questionId}`];
if (questionElement && questionElement.scrollIntoView) {
questionElement.scrollIntoView({ behavior: "smooth", block: "start" });
@ -171,35 +203,20 @@ export default {
}
},
//
startRefreshTimer() {
this.refreshTimer = setTimeout(() => {
if (!this.questions.length) {
alert("加载超时,页面将刷新以重试...");
location.reload(); //
startCountdown() {
this.timer = setInterval(() => {
if (this.remainingTime <= 0) {
clearInterval(this.timer);
alert("时间到!");
this.submitExam();
} else {
this.remainingTime--;
}
}, 10000); // 10
}, 1000);
},
//
async submitExam() {
try {
const unanswered = this.questions.filter(
(q) => !this.answers[q.id] || this.answers[q.id].trim() === ""
);
if (unanswered.length > 0) {
const confirmSubmit = confirm(`还有 ${unanswered.length} 道题未作答,是否仍然提交试卷?`);
if (!confirmSubmit) {
return; //
}
}
console.log('答案', this.answers);
//
const remainingTime = Math.round(Math.max(this.duration*60 - this.remainingTime, 0) / 60);
const token = this.$store.state.token;
if (!token) {
alert("用户未登录,请重新登录!");
@ -207,23 +224,34 @@ export default {
return;
}
// this.answers AnswerVo
const answerList = Object.keys(this.answers).map((questionId) => {
return {
id: Number(questionId), // id
answer: this.answers[questionId].trim() //
};
});
//
const unanswered = this.questions.filter(
(q) => !this.answers[q.id] || (typeof this.answers[q.id] === 'string' && this.answers[q.id].trim() === "")
);
const response = await axios.post("http://localhost:8080/student/homepage/task_answer", {
testid: this.examId, // testid
list: answerList, // list AnswerVo
time: remainingTime // time
}, {
headers: {
Authorization: `Bearer ${token}`
if (unanswered.length > 0) {
const confirmSubmit = confirm(`还有 ${unanswered.length} 道题未作答,是否仍然提交试卷?`);
if (!confirmSubmit) {
return; // 退
}
});
}
const answerList = Object.keys(this.answers).map((questionId) => ({
id: Number(questionId),
answer: Array.isArray(this.answers[questionId])
? this.answers[questionId].join("")
: this.answers[questionId].trim(),
}));
const response = await axios.post(
"http://localhost:8080/student/homepage/task_answer",
{
testid: this.examId,
list: answerList,
time: Math.round(Math.max(this.duration * 60 - this.remainingTime, 0) / 60),
},
{ headers: { Authorization: `Bearer ${token}` } }
);
if (response.data.code === 200) {
alert("考试提交成功!");
@ -236,18 +264,13 @@ export default {
alert("提交试卷失败,请稍后再试!");
}
},
//
startCountdown() {
this.timer = setInterval(() => {
if (this.remainingTime <= 0) {
clearInterval(this.timer);
alert("时间到!");
return;
startRefreshTimer() {
this.refreshTimer = setTimeout(() => {
if (!this.questions.length) {
alert("加载超时,页面将刷新以重试...");
location.reload(); //
}
this.remainingTime--; //
}, 1000); //
}, 10000); // 10
},
},
created() {

@ -0,0 +1,188 @@
<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>
<!-- 搜索框 -->
<el-row :gutter="20" class="search-row" style="margin-bottom: 20px;">
<el-col :span="6">
<el-input
v-model="searchKeyword"
placeholder="按试卷名称查询"
clearable
@input="search"
>
<template #prepend>
<i class="el-icon-search"></i>
</template>
</el-input>
</el-col>
</el-row>
<!-- 分数表格 -->
<el-table v-if="!loading" :data="filteredScores" border style="width: 100%">
<el-table-column label="试卷名称" width="200">
<template #default="{ row }">
{{ getTestInfo(row.testid).name }}
</template>
</el-table-column>
<el-table-column label="学科" width="100">
<template #default="{ row }">
{{ getTestInfo(row.testid).subject }}
</template>
</el-table-column>
<el-table-column prop="score" label="分数" width="100" />
<el-table-column prop="time" label="答题时间(分钟)" />
<!-- 传递点击的 testid 进行后续操作 -->
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button @click="viewTestPaper(row.testid, getTestInfo(row.testid).name)" style="background-color: beige;color:black">查看试卷</el-button>
</template>
</el-table-column>
</el-table>
<!-- 加载中 -->
<el-empty v-else description="加载中..." />
</el-main>
</el-container>
</el-container>
</template>
<script>
import Header from "@/components/Header.vue";
import Aside from "@/components/Aside.vue";
import axios from "axios";
export default {
name: "ScoreList",
components: { Aside, Header },
data() {
return {
collapse: false,
isCollapse: false,
sideWidth: 200,
logoTextShow: true,
collapseBtnClass: "el-icon-s-fold",
searchKeyword: "", //
scores: [], //
filteredScores: [], //
loading: false, //
testInfoMap: {}, //
};
},
methods: {
async fetchScores() {
this.loading = true;
try {
const token = this.$store.state.token; // Vuex token
if (!token) {
alert("用户未登录,请重新登录!");
this.$router.push("/login"); //
return;
}
const response = await axios.get(
"http://localhost:8080/student/homepage/task_score",
{
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
}
);
this.scores = response.data.data || [];
this.filteredScores = [...this.scores]; //
//
await this.fetchTestInfoForScores();
} catch (error) {
console.error("加载失败:", error);
this.$router.push('/login'); //
} finally {
this.loading = false;
}
},
//
async fetchTestInfoForScores() {
const testIds = this.scores.map((score) => score.testid);
// testid
const uniqueTestIds = [...new Set(testIds)];
// uniqueTestIds
for (let testid of uniqueTestIds) {
if (!this.testInfoMap[testid]) {
await this.fetchTestInfo(testid);
}
}
},
//
async fetchTestInfo(testid) {
try {
//
const response = await axios.get(
`http://localhost:8080/student/examPaper/examPaperById`,
{
params: {id: testid},
headers: {
Authorization: `Bearer ${this.$store.state.token}`,
'Content-Type': 'application/json',
},
}
);
const testInfo = response.data.data;
if (testInfo) {
// testInfoMap
this.testInfoMap[testid] = testInfo;
} else {
console.error("未找到试卷信息", testid);
}
} catch (error) {
console.error("加载失败:", error);
this.$router.push('/login'); //
}
},
// testid
getTestInfo(testid) {
return this.testInfoMap[testid] || {name: '未知', subject: '未知'};
},
search() {
this.filteredScores = this.scores.filter((item) =>
item.name.toLowerCase().includes(this.searchKeyword.toLowerCase())
);
},
viewTestPaper(testid,name) {
// 使 Vue Router
this.$router.push({
name: 'TestPaperDetail',
query: { id: testid ,name: name} // ID
});
},
},
created() {
this.fetchScores(); //
},
};
</script>
<style scoped lang="scss">
.search-row {
display: flex;
justify-content: flex-start;
align-items: center;
}
</style>

@ -0,0 +1,281 @@
<template>
<div class="exam-container">
<!-- 左侧栏 -->
<div class="sidebar">
<!-- 返回按钮 -->
<div class="back-button">
<button @click="goBack">
<span>返回</span>
</button>
</div>
<h2>{{ examTitle }}</h2>
<p>答题人{{ sender }}</p>
<p>得分{{ currentScore }} &nbsp; 总分{{ totalScore }}</p>
<p>已答对{{ correctQuestions }} &nbsp; 总题数{{ totalQuestions }}</p>
<div class="question-navigation">
<div
v-for="(question, index) in questions"
:key="question.id"
class="question-box"
:class="{ answered: answers[question.id]?.length > 0, active: currentQuestionIndex === index, correct: question.tf === 'true', incorrect: question.tf === 'false' }"
@click="scrollToQuestion(index)"
>
{{ index + 1 }}
</div>
</div>
</div>
<!-- 主内容 -->
<div class="main-content" v-if="!loading && questions.length">
<div
v-for="(question, index) in questions"
:key="question.id"
class="question"
:ref="'question-' + question.id"
:class="{ correct: question.tf === 'true', incorrect: question.tf === 'false', active: currentQuestionIndex === index }"
>
<p>{{ index + 1 }}. {{ question.content }}</p>
<p>你的答案<span>{{ question.answer }}</span></p>
<p>正确答案<span>{{ question.idanswer }}</span></p>
<p>分数{{ question.score }} </p>
<p v-if="question.analysis">{{ question.analysis }}</p>
</div>
</div>
<!-- 加载中 -->
<div v-if="loading" class="loading">
正在加载试题请稍候...
</div>
<!-- 空状态 -->
<div v-if="!loading && !questions.length" class="empty">
未找到试题请联系管理员
</div>
</div>
</template>
<script>
import axios from "axios";
import exam from "@/views/Student/Exam.vue";
export default {
name: "TestPaperDetail",
data() {
return {
examTitle: this.examname, //
sender: "", //
totalScore: 0, //
duration: "", //
questions: [], //
answers: {}, //
correctQuestions: 0, //
currentScore: 0, //
totalQuestions: 0, //
loading: true, //
submitted: false, //
currentQuestionIndex: 0, //
};
},
computed: {
totalQuestions() {
return this.questions.length;
},
},
methods: {
async fetchExamData() {
try {
const token = this.$store.state.token;
if (!token) {
alert("用户未登录,请重新登录!");
this.$router.push("/login");
return;
}
console.log('id', this.id);
const response = await axios.get(
"http://localhost:8080/student/homepage/selectQuestionanswer",
{
params: { testid: this.id },
headers: { Authorization: `Bearer ${token}` },
}
);
console.log('response.data', response.data);
const { sender, list, testid } = response.data.data;
this.examTitle = this.examname;
this.sender = sender;
this.questions = list;
console.log('list', list);
this.totalScore = list.reduce((sum, q) => sum + q.score, 0);
this.correctQuestions = list.filter((q) => q.tf === "true").length;
this.loading = false;
} catch (error) {
console.error("获取试题失败", error);
this.loading = false;
}
},
async getTest() {
try {
const token = this.$store.state.token;
if (!token) {
alert("用户未登录,请重新登录!");
this.$router.push("/login");
return;
}
const response = await axios.get(
"http://localhost:8080/student/homepage/getScoreByTestId",
{
params: { testId: this.id },
headers: { Authorization: `Bearer ${token}` },
}
);
const data = response.data.data;
this.sender = data.sender;
this.currentScore = data.score;
this.totalScore = data.totalscore;
this.correctQuestions = data.tquestions;
this.totalQuestions = data.totalquestions;
} catch (error) {
console.error("获取试卷信息失败", error);
this.loading = false;
}
},
scrollToQuestion(index) {
this.$nextTick(() => {
const questionRef = this.$refs[`question-${this.questions[index].id}`];
if (questionRef && questionRef[0]) {
questionRef[0].scrollIntoView({ behavior: "smooth", block: "start" });
this.currentQuestionIndex = index;
}
});
},
//
goBack() {
this.$router.go(-1); //
},
},
mounted() {
this.fetchExamData();
},
created() {
this.examname = this.$route.query.name;
this.id = this.$route.query.id;
this.getTest();
},
};
</script>
<style scoped>
.back-button {
padding-bottom: 10px;
text-align: center;
}
.back-button button {
background-color: #1976d2;
color: white;
padding: 8px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
}
.back-button button:hover {
background-color: #1565c0;
}
.exam-container {
display: flex;
}
.sidebar {
width: 25%;
padding: 20px;
border-right: 1px solid #ddd;
display: flex;
flex-direction: column;
}
.sidebar h2,
.sidebar p {
margin-bottom: 10px;
}
.question-navigation {
display: grid;
grid-template-columns: repeat(5, 1fr); /* Adjust number of columns */
gap: 10px;
margin-top: 20px;
}
.question-box {
padding: 15px;
background-color: #f1f1f1;
text-align: center;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 18px;
cursor: pointer;
transition: background-color 0.3s;
}
.question-box:hover {
background-color: #e0e0e0;
}
.question-box.active {
background-color: #bbdefb; /* Highlight active question */
font-weight: bold;
}
.question-box.answered {
background-color: #c8e6c9; /* Highlight answered question */
}
.question-box.correct {
background-color: #e0f7fa; /* Correct answer color */
color: #00796b; /* Text color for correct answers */
}
.question-box.incorrect {
background-color: #ffebee; /* Incorrect answer color */
color: #d32f2f; /* Text color for incorrect answers */
}
.main-content {
flex: 1;
padding: 20px;
}
.question {
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.correct {
background-color: #e0f7fa;
color: #00796b;
}
.incorrect {
background-color: #ffebee;
color: #d32f2f;
}
.loading,
.empty {
text-align: center;
margin-top: 50px;
font-size: 18px;
}
.empty {
color: #888;
}
</style>
Loading…
Cancel
Save