修改了view文件夹

pull/4/head
hunqiu 3 months ago
parent 0fe04dc92a
commit 94e6719942

@ -10,4 +10,9 @@
</profile> </profile>
</annotationProcessing> </annotationProcessing>
</component> </component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="exam" options="-parameters" />
</option>
</component>
</project> </project>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
</component> </component>
</project> </project>

@ -1,685 +1,13 @@
/*********************************************************** public class ActionVo {
* @Description : // 使用@JsonProperty注解将actionName属性映射到JSON中的action字段
* @author : 广(Laing Shan Guang) @JsonProperty("action")
* @date : 2019-05-28 08:06 private String actionName;
* @email : liangshanguang2@gmail.com
***********************************************************/ // 使用@JsonProperty注解将actionDescription属性映射到JSON中的describe字段
package lsgwr.exam.service.impl; @JsonProperty("describe")
private String actionDescription;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; // 使用@JsonProperty注解将defaultCheck属性映射到JSON中的defaultCheck字段
import lsgwr.exam.entity.*; @JsonProperty("defaultCheck")
import lsgwr.exam.enums.QuestionEnum; private Boolean defaultCheck;
import lsgwr.exam.service.ExamService; }
import lsgwr.exam.repository.*;
import lsgwr.exam.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.*;
@Service
@Transactional
public class ExamServiceImpl implements ExamService {
// 定义一个ExamRepository类型的私有变量
private final ExamRepository examRepository;
// 定义一个ExamRecordRepository类型的私有变量
private final ExamRecordRepository examRecordRepository;
// 定义一个QuestionRepository类型的私有变量
private final QuestionRepository questionRepository;
// 定义一个UserRepository类型的私有变量
private final UserRepository userRepository;
// 定义一个QuestionLevelRepository类型的私有变量
private final QuestionLevelRepository questionLevelRepository;
// 定义一个QuestionTypeRepository类型的私有变量
private final QuestionTypeRepository questionTypeRepository;
// 定义一个QuestionCategoryRepository类型的私有变量
private final QuestionCategoryRepository questionCategoryRepository;
// 定义一个QuestionOptionRepository类型的私有变量
private final QuestionOptionRepository questionOptionRepository;
// 构造函数传入多个Repository类型的参数
public ExamServiceImpl(QuestionRepository questionRepository, UserRepository userRepository, QuestionLevelRepository questionLevelRepository, QuestionTypeRepository questionTypeRepository, QuestionCategoryRepository questionCategoryRepository, QuestionOptionRepository questionOptionRepository, ExamRepository examRepository, ExamRecordRepository examRecordRepository) {
this.questionRepository = questionRepository;
this.userRepository = userRepository;
this.questionLevelRepository = questionLevelRepository;
this.questionTypeRepository = questionTypeRepository;
this.questionCategoryRepository = questionCategoryRepository;
this.questionOptionRepository = questionOptionRepository;
this.examRepository = examRepository;
this.examRecordRepository = examRecordRepository;
}
@Override
public List<QuestionVo> getQuestionAll() {
List<Question> questionList = questionRepository.findAll();
return getQuestionVos(questionList);
}
private List<QuestionVo> getQuestionVos(List<Question> questionList) {
// 需要自定义的question列表
List<QuestionVo> questionVoList = new ArrayList<>();
// 循环完成每个属性的定制
for (Question question : questionList) {
QuestionVo questionVo = getQuestionVo(question);
questionVoList.add(questionVo);
}
return questionVoList;
}
// 根据传入的Question对象返回一个QuestionVo对象
private QuestionVo getQuestionVo(Question question) {
// 创建一个新的QuestionVo对象
QuestionVo questionVo = new QuestionVo();
// 先复制能复制的属性
BeanUtils.copyProperties(question, questionVo);
// 设置问题的创建者
questionVo.setQuestionCreator(
Objects.requireNonNull(
userRepository.findById(
question.getQuestionCreatorId()
).orElse(null)
).getUserUsername());
// 设置问题的难度
questionVo.setQuestionLevel(
Objects.requireNonNull(
questionLevelRepository.findById(
question.getQuestionLevelId()
).orElse(null)
).getQuestionLevelDescription());
// 设置题目的类别,比如单选、多选、判断等
questionVo.setQuestionType(
Objects.requireNonNull(
questionTypeRepository.findById(
question.getQuestionTypeId()
).orElse(null)
).getQuestionTypeDescription());
// 设置题目分类,比如数学、语文、英语、生活、人文等
questionVo.setQuestionCategory(
Objects.requireNonNull(
questionCategoryRepository.findById(
question.getQuestionCategoryId()
).orElse(null)
).getQuestionCategoryName()
);
// 选项的自定义Vo列表
List<QuestionOptionVo> optionVoList = new ArrayList<>();
// 获得所有的选项列表
List<QuestionOption> optionList = questionOptionRepository.findAllById(
Arrays.asList(question.getQuestionOptionIds().split("-"))
);
// 获取所有的答案列表optionList中每个option的isAnswer选项
List<QuestionOption> answerList = questionOptionRepository.findAllById(
Arrays.asList(question.getQuestionAnswerOptionIds().split("-"))
);
// 根据选项和答案的id相同设置optionVo的isAnswer属性
for (QuestionOption option : optionList) {
QuestionOptionVo optionVo = new QuestionOptionVo();
BeanUtils.copyProperties(option, optionVo);
for (QuestionOption answer : answerList) {
if (option.getQuestionOptionId().equals(answer.getQuestionOptionId())) {
optionVo.setAnswer(true);
}
}
optionVoList.add(optionVo);
}
// 设置题目的所有选项
questionVo.setQuestionOptionVoList(optionVoList);
return questionVo;
}
@Override
public QuestionVo updateQuestion(QuestionVo questionVo) {
// 1.把需要的属性都设置好
StringBuilder questionAnswerOptionIds = new StringBuilder();
List<QuestionOption> questionOptionList = new ArrayList<>();
List<QuestionOptionVo> questionOptionVoList = questionVo.getQuestionOptionVoList();
int size = questionOptionVoList.size();
for (int i = 0; i < questionOptionVoList.size(); i++) {
QuestionOptionVo questionOptionVo = questionOptionVoList.get(i);
QuestionOption questionOption = new QuestionOption();
BeanUtils.copyProperties(questionOptionVo, questionOption);
questionOptionList.add(questionOption);
if (questionOptionVo.getAnswer()) {
if (i != size - 1) {
// 把更新后的答案的id加上去,记得用-连到一起
questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId()).append("-");
} else {
// 最后一个不需要用-连接
questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId());
}
}
}
// 1.更新问题
Question question = questionRepository.findById(questionVo.getQuestionId()).orElse(null);
assert question != null;
BeanUtils.copyProperties(questionVo, question);
question.setQuestionAnswerOptionIds(questionAnswerOptionIds.toString());
questionRepository.save(question);
// 2.更新所有的option
questionOptionRepository.saveAll(questionOptionList);
// 返回更新后的问题,方便前端局部刷新
return getQuestionVo(question);
}
@Override
public void questionCreate(QuestionCreateVo questionCreateVo) {
// 问题创建
Question question = new Question();
// 把能复制的属性都复制过来
BeanUtils.copyProperties(questionCreateVo, question);
// 设置下questionOptionIds和questionAnswerOptionIds需要自己用Hutool生成下
List<QuestionOption> questionOptionList = new ArrayList<>();
List<QuestionOptionCreateVo> questionOptionCreateVoList = questionCreateVo.getQuestionOptionCreateVoList();
for (QuestionOptionCreateVo questionOptionCreateVo : questionOptionCreateVoList) {
QuestionOption questionOption = new QuestionOption();
// 设置选项的的内容
questionOption.setQuestionOptionContent(questionOptionCreateVo.getQuestionOptionContent());
// 设置选项的id
questionOption.setQuestionOptionId(IdUtil.simpleUUID());
questionOptionList.add(questionOption);
}
// 把选项都存起来然后才能用于下面设置Question的questionOptionIds和questionAnswerOptionIds
questionOptionRepository.saveAll(questionOptionList);
String questionOptionIds = "";
String questionAnswerOptionIds = "";
// 经过上面的saveAll方法所有的option的主键id都已经持久化了
for (int i = 0; i < questionOptionCreateVoList.size(); i++) {
// 获取指定选项
QuestionOptionCreateVo questionOptionCreateVo = questionOptionCreateVoList.get(i);
// 获取保存后的指定对象
QuestionOption questionOption = questionOptionList.get(i);
questionOptionIds += questionOption.getQuestionOptionId() + "-";
if (questionOptionCreateVo.getAnswer()) {
// 如果是答案的话
questionAnswerOptionIds += questionOption.getQuestionOptionId() + "-";
}
}
// 把字符串最后面的"-"给去掉
questionAnswerOptionIds = replaceLastSeparator(questionAnswerOptionIds);
questionOptionIds = replaceLastSeparator(questionOptionIds);
// 设置选项id组成的字符串
question.setQuestionOptionIds(questionOptionIds);
// 设置答案选项id组成的字符串
question.setQuestionAnswerOptionIds(questionAnswerOptionIds);
// 自己生成问题的id
question.setQuestionId(IdUtil.simpleUUID());
// 先把创建时间和更新时间每次都取当前时间吧
question.setCreateTime(new Date());
question.setUpdateTime(new Date());
// 保存问题到数据库
questionRepository.save(question);
}
@Override
public QuestionSelectionVo getSelections() {
QuestionSelectionVo questionSelectionVo = new QuestionSelectionVo();
questionSelectionVo.setQuestionCategoryList(questionCategoryRepository.findAll());
questionSelectionVo.setQuestionLevelList(questionLevelRepository.findAll());
questionSelectionVo.setQuestionTypeList(questionTypeRepository.findAll());
return questionSelectionVo;
}
/**
* split
*
* @param str
* @return
*/
public static String trimMiddleLine(String str) {
if (str.charAt(str.length() - 1) == '-') {
str = str.substring(0, str.length() - 1);
}
return str;
}
@Override
public QuestionDetailVo getQuestionDetail(String id) {
Question question = questionRepository.findById(id).orElse(null);
QuestionDetailVo questionDetailVo = new QuestionDetailVo();
questionDetailVo.setId(id);
questionDetailVo.setName(question.getQuestionName());
questionDetailVo.setDescription(question.getQuestionDescription());
// 问题类型,单选题/多选题/判断题
questionDetailVo.setType(
Objects.requireNonNull(
questionTypeRepository.findById(
question.getQuestionTypeId()
).orElse(null)
).getQuestionTypeDescription()
);
// 获取当前问题的选项
String optionIdsStr = trimMiddleLine(question.getQuestionOptionIds());
String[] optionIds = optionIdsStr.split("-");
// 获取选项列表
List<QuestionOption> optionList = questionOptionRepository.findAllById(Arrays.asList(optionIds));
questionDetailVo.setOptions(optionList);
return questionDetailVo;
}
@Override
public List<ExamVo> getExamAll() {
List<Exam> examList = examRepository.findAll();
return getExamVos(examList);
}
private List<ExamVo> getExamVos(List<Exam> examList) {
// 需要自定义的exam列表
List<ExamVo> examVoList = new ArrayList<>();
// 循环完成每个属性的定制
for (Exam exam : examList) {
ExamVo examVo = new ExamVo();
// 先尽量复制能复制的所有属性
BeanUtils.copyProperties(exam, examVo);
// 设置问题的创建者
examVo.setExamCreator(
Objects.requireNonNull(
userRepository.findById(
exam.getExamCreatorId()
).orElse(null)
).getUserUsername()
);
// 获取所有单选题列表并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
List<ExamQuestionSelectVo> radioQuestionVoList = new ArrayList<>();
List<Question> radioQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsRadio().split("-"))
);
for (Question question : radioQuestionList) {
ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, radioQuestionVo);
radioQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
radioQuestionVoList.add(radioQuestionVo);
}
examVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上
List<ExamQuestionSelectVo> checkQuestionVoList = new ArrayList<>();
List<Question> checkQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsCheck().split("-"))
);
for (Question question : checkQuestionList) {
ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, checkQuestionVo);
checkQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
checkQuestionVoList.add(checkQuestionVo);
}
examVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上
List<ExamQuestionSelectVo> judgeQuestionVoList = new ArrayList<>();
List<Question> judgeQuestionList = questionRepository.findAllById(
Arrays.asList(exam.getExamQuestionIdsJudge().split("-"))
);
for (Question question : judgeQuestionList) {
ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, judgeQuestionVo);
judgeQuestionVo.setChecked(true); // 考试中的问题肯定被选中的
judgeQuestionVoList.add(judgeQuestionVo);
}
examVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);
// 把examVo加到examVoList中
examVoList.add(examVo);
}
return examVoList;
}
@Override
public ExamQuestionTypeVo getExamQuestionType() {
ExamQuestionTypeVo examQuestionTypeVo = new ExamQuestionTypeVo();
// 获取所有单选题列表并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
List<ExamQuestionSelectVo> radioQuestionVoList = new ArrayList<>();
List<Question> radioQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.RADIO.getId());
for (Question question : radioQuestionList) {
ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, radioQuestionVo);
radioQuestionVoList.add(radioQuestionVo);
}
examQuestionTypeVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoCheckList上
List<ExamQuestionSelectVo> checkQuestionVoList = new ArrayList<>();
List<Question> checkQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.CHECK.getId());
for (Question question : checkQuestionList) {
ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, checkQuestionVo);
checkQuestionVoList.add(checkQuestionVo);
}
examQuestionTypeVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);
// 获取所有多选题列表并赋值到ExamVo的属性ExamQuestionSelectVoJudgeList上
List<ExamQuestionSelectVo> judgeQuestionVoList = new ArrayList<>();
List<Question> judgeQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.JUDGE.getId());
for (Question question : judgeQuestionList) {
ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
BeanUtils.copyProperties(question, judgeQuestionVo);
judgeQuestionVoList.add(judgeQuestionVo);
}
examQuestionTypeVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);
return examQuestionTypeVo;
}
@Override
public Exam create(ExamCreateVo examCreateVo, String userId) {
// 在线考试系统创建
Exam exam = new Exam();
BeanUtils.copyProperties(examCreateVo, exam);
exam.setExamId(IdUtil.simpleUUID());
exam.setExamCreatorId(userId);
exam.setCreateTime(new Date());
exam.setUpdateTime(new Date());
// Todo:这两个日志后面是要在前端传入的,这里暂时定为当前日期
exam.setExamStartDate(new Date());
exam.setExamEndDate(new Date());
String radioIdsStr = "";
String checkIdsStr = "";
String judgeIdsStr = "";
List<ExamQuestionSelectVo> radios = examCreateVo.getRadios();
List<ExamQuestionSelectVo> checks = examCreateVo.getChecks();
List<ExamQuestionSelectVo> judges = examCreateVo.getJudges();
int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
for (ExamQuestionSelectVo radio : radios) {
if (radio.getChecked()) {
radioIdsStr += radio.getQuestionId() + "-";
radioCnt++;
}
}
radioIdsStr = replaceLastSeparator(radioIdsStr);
for (ExamQuestionSelectVo check : checks) {
if (check.getChecked()) {
checkIdsStr += check.getQuestionId() + "-";
checkCnt++;
}
}
checkIdsStr = replaceLastSeparator(checkIdsStr);
for (ExamQuestionSelectVo judge : judges) {
if (judge.getChecked()) {
judgeIdsStr += judge.getQuestionId() + "-";
judgeCnt++;
}
}
judgeIdsStr = replaceLastSeparator(judgeIdsStr);
exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
// 设置各个题目的id
exam.setExamQuestionIdsRadio(radioIdsStr);
exam.setExamQuestionIdsCheck(checkIdsStr);
exam.setExamQuestionIdsJudge(judgeIdsStr);
// 计算总分数
int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
exam.setExamScore(examScore);
examRepository.save(exam);
return exam;
}
@Override
public Exam update(ExamVo examVo, String userId) {
Exam exam = new Exam();
BeanUtils.copyProperties(examVo, exam);
exam.setExamCreatorId(userId); // 考试的更新人为最新的创建人
exam.setUpdateTime(new Date()); // 考试的更新日期要记录下
String radioIdsStr = "";
String checkIdsStr = "";
String judgeIdsStr = "";
List<ExamQuestionSelectVo> radios = examVo.getExamQuestionSelectVoRadioList();
List<ExamQuestionSelectVo> checks = examVo.getExamQuestionSelectVoCheckList();
List<ExamQuestionSelectVo> judges = examVo.getExamQuestionSelectVoJudgeList();
int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
for (ExamQuestionSelectVo radio : radios) {
if (radio.getChecked()) {
radioIdsStr += radio.getQuestionId() + "-";
radioCnt++;
}
}
radioIdsStr = replaceLastSeparator(radioIdsStr);
for (ExamQuestionSelectVo check : checks) {
if (check.getChecked()) {
checkIdsStr += check.getQuestionId() + "-";
checkCnt++;
}
}
checkIdsStr = replaceLastSeparator(checkIdsStr);
for (ExamQuestionSelectVo judge : judges) {
if (judge.getChecked()) {
judgeIdsStr += judge.getQuestionId() + "-";
judgeCnt++;
}
}
judgeIdsStr = replaceLastSeparator(judgeIdsStr);
exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
// 设置各个题目的id
exam.setExamQuestionIdsRadio(radioIdsStr);
exam.setExamQuestionIdsCheck(checkIdsStr);
exam.setExamQuestionIdsJudge(judgeIdsStr);
// 计算总分数
int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
exam.setExamScore(examScore);
examRepository.save(exam);
return exam;
}
@Override
public List<ExamCardVo> getExamCardList() {
List<Exam> examList = examRepository.findAll();
List<ExamCardVo> examCardVoList = new ArrayList<>();
for (Exam exam : examList) {
ExamCardVo examCardVo = new ExamCardVo();
BeanUtils.copyProperties(exam, examCardVo);
examCardVoList.add(examCardVo);
}
return examCardVoList;
}
@Override
public ExamDetailVo getExamDetail(String id) {
Exam exam = examRepository.findById(id).orElse(null);
ExamDetailVo examDetailVo = new ExamDetailVo();
examDetailVo.setExam(exam);
assert exam != null;
examDetailVo.setRadioIds(exam.getExamQuestionIdsRadio().split("-"));
examDetailVo.setCheckIds(exam.getExamQuestionIdsCheck().split("-"));
examDetailVo.setJudgeIds(exam.getExamQuestionIdsJudge().split("-"));
return examDetailVo;
}
@Override
public ExamRecord judge(String userId, String examId, HashMap<String, List<String>> answersMap) {
// 开始考试判分啦~~~
// 1.首先获取考试对象和选项数组
ExamDetailVo examDetailVo = getExamDetail(examId);
Exam exam = examDetailVo.getExam();
// 2.然后获取该考试下所有的题目信息
List<String> questionIds = new ArrayList<>();
// 2.1 题目id的数组
List<String> radioIdList = Arrays.asList(examDetailVo.getRadioIds());
List<String> checkIdList = Arrays.asList(examDetailVo.getCheckIds());
List<String> judgeIdList = Arrays.asList(examDetailVo.getJudgeIds());
questionIds.addAll(radioIdList);
questionIds.addAll(checkIdList);
questionIds.addAll(judgeIdList);
// 2.2 每种题目的分数
int radioScore = exam.getExamScoreRadio();
int checkScore = exam.getExamScoreCheck();
int judgeScore = exam.getExamScoreJudge();
// 2.3 根据问题id的数组拿到所有的问题对象供下面步骤用
List<Question> questionList = questionRepository.findAllById(questionIds);
Map<String, Question> questionMap = new HashMap<>();
for (Question question : questionList) {
questionMap.put(question.getQuestionId(), question);
}
// 3.根据正确答案和用户作答信息进行判分
Set<String> questionIdsAnswer = answersMap.keySet();
// 存储当前考试每个题目的得分情况
Map<String, Integer> judgeMap = new HashMap<>();
// 考生作答地每个题目的选项(题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔),用于查看考试详情
// 例子题目1的id_作答选项1-作答选项2&题目2的id_作答选项1&题目3_作答选项1-作答选项2-作答选项3
StringBuilder answerOptionIdsSb = new StringBuilder();
// 用户此次考试的总分
int totalScore = 0;
for (String questionId : questionIdsAnswer) {
// 获取用户作答地这个题的答案信息
Question question = questionMap.get(questionId);
// 获取答案选项
String questionAnswerOptionIds = replaceLastSeparator(question.getQuestionAnswerOptionIds());
List<String> questionAnswerOptionIdList = Arrays.asList(questionAnswerOptionIds.split("-"));
Collections.sort(questionAnswerOptionIdList);
String answerStr = listConcat(questionAnswerOptionIdList);
// 获取用户作答
List<String> questionUserOptionIdList = answersMap.get(questionId);
Collections.sort(questionUserOptionIdList);
String userStr = listConcat(questionUserOptionIdList);
// 判断questionAnswerOptionIds和answersMap里面的答案是否相等
if (answerStr.equals(userStr)) {
// 说明题目作答正确,下面根据题型给分
int score = 0;
if (radioIdList.contains(questionId)) {
score = radioScore;
}
if (checkIdList.contains(questionId)) {
score = checkScore;
}
if (judgeIdList.contains(questionId)) {
score = judgeScore;
}
// 累计本次考试得分
totalScore += score;
// True代表题目答对
answerOptionIdsSb.append(questionId + "@True_" + userStr + "$");
judgeMap.put(questionId, score);
} else {
// 说明题目作答错误,直接判零分,False代表题目答错
answerOptionIdsSb.append(questionId + "@False_" + userStr + "$");
judgeMap.put(questionId, 0);
}
}
// 4.计算得分记录本次考试结果存到ExamRecord中
ExamRecord examRecord = new ExamRecord();
examRecord.setExamRecordId(IdUtil.simpleUUID());
examRecord.setExamId(examId);
// 注意去掉最后可能有的&_-
examRecord.setAnswerOptionIds(replaceLastSeparator(answerOptionIdsSb.toString()));
examRecord.setExamJoinerId(userId);
examRecord.setExamJoinDate(new Date());
examRecord.setExamJoinScore(totalScore);
examRecordRepository.save(examRecord);
return examRecord;
}
@Override
public List<ExamRecordVo> getExamRecordList(String userId) {
// 获取指定用户下的考试记录列表
List<ExamRecord> examRecordList = examRecordRepository.findByExamJoinerIdOrderByExamJoinDateDesc(userId);
List<ExamRecordVo> examRecordVoList = new ArrayList<>();
for (ExamRecord examRecord : examRecordList) {
ExamRecordVo examRecordVo = new ExamRecordVo();
Exam exam = examRepository.findById(examRecord.getExamId()).orElse(null);
examRecordVo.setExam(exam);
User user = userRepository.findById(userId).orElse(null);
examRecordVo.setUser(user);
examRecordVo.setExamRecord(examRecord);
examRecordVoList.add(examRecordVo);
}
return examRecordVoList;
}
@Override
public RecordDetailVo getRecordDetail(String recordId) {
// 获取考试详情的封装对象
ExamRecord record = examRecordRepository.findById(recordId).orElse(null);
RecordDetailVo recordDetailVo = new RecordDetailVo();
recordDetailVo.setExamRecord(record);
// 用户的答案,需要解析
HashMap<String, List<String>> answersMap = new HashMap<>();
HashMap<String, String> resultsMap = new HashMap<>();
assert record != null;
String answersStr = record.getAnswerOptionIds();
// $分隔题目,因为$在正则中有特殊用途(行尾),所以需要括起来
String[] questionArr = answersStr.split("[$]");
for (String questionStr : questionArr) {
System.out.println(questionStr);
// 区分开题目标题和选项
String[] questionTitleResultAndOption = questionStr.split("_");
String[] questionTitleAndResult = questionTitleResultAndOption[0].split("@");
String[] questionOptions = questionTitleResultAndOption[1].split("-");
// 题目:答案选项
answersMap.put(questionTitleAndResult[0], Arrays.asList(questionOptions));
// 题目True / False
resultsMap.put(questionTitleAndResult[0], questionTitleAndResult[1]);
}
recordDetailVo.setAnswersMap(answersMap);
recordDetailVo.setResultsMap(resultsMap);
// 下面再计算正确答案的map
ExamDetailVo examDetailVo = getExamDetail(record.getExamId());
List<String> questionIdList = new ArrayList<>();
questionIdList.addAll(Arrays.asList(examDetailVo.getRadioIds()));
questionIdList.addAll(Arrays.asList(examDetailVo.getCheckIds()));
questionIdList.addAll(Arrays.asList(examDetailVo.getJudgeIds()));
// 获取所有的问题对象
List<Question> questionList = questionRepository.findAllById(questionIdList);
HashMap<String, List<String>> answersRightMap = new HashMap<>();
for (Question question : questionList) {
// 记得去掉最后可能出现的特殊字符
String questionAnswerOptionIdsStr = replaceLastSeparator(question.getQuestionAnswerOptionIds());
String[] questionAnswerOptionIds = questionAnswerOptionIdsStr.split("-");
answersRightMap.put(question.getQuestionId(), Arrays.asList(questionAnswerOptionIds));
}
recordDetailVo.setAnswersRightMap(answersRightMap);
return recordDetailVo;
}
/**
* -
*
* @param str
* @return -
*/
private String replaceLastSeparator(String str) {
String lastChar = str.substring(str.length() - 1);
// 题目和题目之间用$分隔,题目有多个选项地话用-分隔,题目和选项之间用_分隔
if ("-".equals(lastChar) || "_".equals(lastChar) || "$".equals(lastChar)) {
str = StrUtil.sub(str, 0, str.length() - 1);
}
return str;
}
/**
* -
*
* @param strList
* @return -
*/
private String listConcat(List<String> strList) {
StringBuilder sb = new StringBuilder();
for (String str : strList) {
sb.append(str);
sb.append("-");
}
return replaceLastSeparator(sb.toString());
}
}

@ -6,20 +6,24 @@ const { Item, SubMenu } = Menu
export default { export default {
name: 'SMenu', name: 'SMenu',
props: { props: {
// 菜单数据
menu: { menu: {
type: Array, type: Array,
required: true required: true
}, },
// 主题
theme: { theme: {
type: String, type: String,
required: false, required: false,
default: 'dark' default: 'dark'
}, },
// 模式
mode: { mode: {
type: String, type: String,
required: false, required: false,
default: 'inline' default: 'inline'
}, },
// 是否折叠
collapsed: { collapsed: {
type: Boolean, type: Boolean,
required: false, required: false,
@ -28,12 +32,16 @@ export default {
}, },
data () { data () {
return { return {
// 打开的菜单项
openKeys: [], openKeys: [],
// 选择的菜单项
selectedKeys: [], selectedKeys: [],
// 缓存的打开的菜单项
cachedOpenKeys: [] cachedOpenKeys: []
} }
}, },
computed: { computed: {
// 根菜单项的key
rootSubmenuKeys: vm => { rootSubmenuKeys: vm => {
const keys = [] const keys = []
vm.menu.forEach(item => keys.push(item.path)) vm.menu.forEach(item => keys.push(item.path))
@ -41,23 +49,29 @@ export default {
} }
}, },
mounted () { mounted () {
// 组件挂载时更新菜单
this.updateMenu() this.updateMenu()
}, },
watch: { watch: {
// 监听折叠状态的变化
collapsed (val) { collapsed (val) {
if (val) { if (val) {
// 折叠时,缓存打开的菜单项
this.cachedOpenKeys = this.openKeys.concat() this.cachedOpenKeys = this.openKeys.concat()
this.openKeys = [] this.openKeys = []
} else { } else {
// 展开时,恢复打开的菜单项
this.openKeys = this.cachedOpenKeys this.openKeys = this.cachedOpenKeys
} }
}, },
// 监听路由的变化
$route: function () { $route: function () {
// 更新菜单
this.updateMenu() this.updateMenu()
} }
}, },
methods: { methods: {
// select menu item // 选择菜单项
onOpenChange (openKeys) { onOpenChange (openKeys) {
// 在水平模式下时执行,并且不再执行后续 // 在水平模式下时执行,并且不再执行后续
if (this.mode === 'horizontal') { if (this.mode === 'horizontal') {
@ -72,6 +86,7 @@ export default {
this.openKeys = latestOpenKey ? [latestOpenKey] : [] this.openKeys = latestOpenKey ? [latestOpenKey] : []
} }
}, },
// 更新菜单
updateMenu () { updateMenu () {
const routes = this.$route.matched.concat() const routes = this.$route.matched.concat()
const { hidden } = this.$route.meta const { hidden } = this.$route.meta
@ -91,13 +106,14 @@ export default {
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys) this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
}, },
// render // 渲染
renderItem (menu) { renderItem (menu) {
if (!menu.hidden) { if (!menu.hidden) {
return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu) return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
} }
return null return null
}, },
// 渲染菜单项
renderMenuItem (menu) { renderMenuItem (menu) {
const target = menu.meta.target || null const target = menu.meta.target || null
const tag = target && 'a' || 'router-link' const tag = target && 'a' || 'router-link'
@ -122,6 +138,7 @@ export default {
</Item> </Item>
) )
}, },
// 渲染子菜单
renderSubMenu (menu) { renderSubMenu (menu) {
const itemArr = [] const itemArr = []
if (!menu.hideChildrenInMenu) { if (!menu.hideChildrenInMenu) {
@ -137,6 +154,7 @@ export default {
</SubMenu> </SubMenu>
) )
}, },
// 渲染图标
renderIcon (icon) { renderIcon (icon) {
if (icon === 'none' || icon === undefined) { if (icon === 'none' || icon === undefined) {
return null return null
@ -177,4 +195,4 @@ export default {
</Menu> </Menu>
) )
} }
} }

@ -7,7 +7,9 @@
* @param children * @param children
* @returns {*[]} * @returns {*[]}
*/ */
// 导出一个函数,用于过滤掉空节点
export function filterEmpty (children = []) { export function filterEmpty (children = []) {
// 过滤掉没有tag属性和text属性为空字符串的节点
return children.filter(c => c.tag || (c.text && c.text.trim() !== '')) return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
} }
@ -15,12 +17,17 @@ export function filterEmpty (children = []) {
* 获取字符串长度英文字符 长度1中文字符长度2 * 获取字符串长度英文字符 长度1中文字符长度2
* @param {*} str * @param {*} str
*/ */
// 导出一个函数,用于获取字符串长度
export const getStrFullLength = (str = '') => export const getStrFullLength = (str = '') =>
// 将字符串分割成字符数组然后使用reduce方法遍历数组计算每个字符的长度
str.split('').reduce((pre, cur) => { str.split('').reduce((pre, cur) => {
// 获取字符的Unicode编码
const charCode = cur.charCodeAt(0) const charCode = cur.charCodeAt(0)
// 如果字符的Unicode编码在0-128之间说明是英文字符长度为1
if (charCode >= 0 && charCode <= 128) { if (charCode >= 0 && charCode <= 128) {
return pre + 1 return pre + 1
} }
// 否则说明是中文字符长度为2
return pre + 2 return pre + 2
}, 0) }, 0)
@ -29,18 +36,26 @@ export const getStrFullLength = (str = '') =>
* @param {*} str * @param {*} str
* @param {*} maxLength * @param {*} maxLength
*/ */
// 导出一个函数,用于截取字符串
export const cutStrByFullLength = (str = '', maxLength) => { export const cutStrByFullLength = (str = '', maxLength) => {
// 初始化显示长度为0
let showLength = 0 let showLength = 0
// 将字符串分割成字符数组然后使用reduce方法遍历数组截取字符串
return str.split('').reduce((pre, cur) => { return str.split('').reduce((pre, cur) => {
// 获取字符的Unicode编码
const charCode = cur.charCodeAt(0) const charCode = cur.charCodeAt(0)
// 如果字符的Unicode编码在0-128之间说明是英文字符长度为1
if (charCode >= 0 && charCode <= 128) { if (charCode >= 0 && charCode <= 128) {
showLength += 1 showLength += 1
} else { } else {
// 否则说明是中文字符长度为2
showLength += 2 showLength += 2
} }
// 如果显示长度小于等于maxLength则将字符添加到结果中
if (showLength <= maxLength) { if (showLength <= maxLength) {
return pre + cur return pre + cur
} }
// 否则,返回结果
return pre return pre
}, '') }, '')
} }

@ -6,8 +6,10 @@
:confirmLoading="confirmLoading" :confirmLoading="confirmLoading"
:width="800" :width="800"
@cancel="cancelHandel"> @cancel="cancelHandel">
<!-- 使用a-row和a-col布局左边是裁剪框右边是预览框 -->
<a-row> <a-row>
<a-col :xs="24" :md="12" :style="{height: '350px'}"> <a-col :xs="24" :md="12" :style="{height: '350px'}">
<!-- 使用vue-cropper组件设置图片是否显示信息自动裁剪自动裁剪宽度自动裁剪高度固定框 -->
<vue-cropper <vue-cropper
ref="cropper" ref="cropper"
:img="options.img" :img="options.img"
@ -21,12 +23,14 @@
</vue-cropper> </vue-cropper>
</a-col> </a-col>
<a-col :xs="24" :md="12" :style="{height: '350px'}"> <a-col :xs="24" :md="12" :style="{height: '350px'}">
<!-- 预览框显示裁剪后的图片 -->
<div class="avatar-upload-preview"> <div class="avatar-upload-preview">
<img :src="previews.url" :style="previews.img"/> <img :src="previews.url" :style="previews.img"/>
</div> </div>
</a-col> </a-col>
</a-row> </a-row>
<!-- 底部按钮 -->
<template slot="footer"> <template slot="footer">
<a-button key="back" @click="cancelHandel"></a-button> <a-button key="back" @click="cancelHandel"></a-button>
<a-button key="submit" type="primary" :loading="confirmLoading" @click="okHandel"></a-button> <a-button key="submit" type="primary" :loading="confirmLoading" @click="okHandel"></a-button>
@ -44,18 +48,18 @@ export default {
*/ */
data () { data () {
return { return {
visible: false, visible: false, //
id: null, id: null, // id
confirmLoading: false, confirmLoading: false, //
options: { options: {
img: '/avatar2.jpg', img: '/avatar2.jpg', //
autoCrop: true, autoCrop: true, //
autoCropWidth: 200, autoCropWidth: 200, //
autoCropHeight: 200, autoCropHeight: 200, //
fixedBox: true fixedBox: true //
}, },
previews: {} previews: {} //
} }
}, },
methods: { methods: {
@ -106,4 +110,4 @@ export default {
height: 100%; height: 100%;
} }
} }
</style> </style>

@ -1,26 +1,33 @@
<template> <template>
<div class="account-settings-info-view"> <div class="account-settings-info-view">
<!-- 使用a-row和a-col组件创建一个两列的布局 -->
<a-row :gutter="16"> <a-row :gutter="16">
<!-- 左侧列占据24/24的宽度在lg及以上屏幕上占据16/24的宽度 -->
<a-col :md="24" :lg="16"> <a-col :md="24" :lg="16">
<!-- 使用a-form组件创建一个垂直布局的表单 -->
<a-form layout="vertical"> <a-form layout="vertical">
<!-- 表单项用于输入昵称 -->
<a-form-item <a-form-item
label="昵称" label="昵称"
> >
<a-input placeholder="给自己起个名字" /> <a-input placeholder="给自己起个名字" />
</a-form-item> </a-form-item>
<!-- 表单项用于输入Bio -->
<a-form-item <a-form-item
label="Bio" label="Bio"
> >
<a-textarea rows="4" placeholder="You are not alone."/> <a-textarea rows="4" placeholder="You are not alone."/>
</a-form-item> </a-form-item>
<!-- 表单项用于输入电子邮件 -->
<a-form-item <a-form-item
label="电子邮件" label="电子邮件"
:required="false" :required="false"
> >
<a-input placeholder="exp@admin.com"/> <a-input placeholder="exp@admin.com"/>
</a-form-item> </a-form-item>
<!-- 表单项用于输入登录密码 -->
<a-form-item <a-form-item
label="登录密码" label="登录密码"
:required="false" :required="false"
@ -28,6 +35,7 @@
<a-input placeholder="密码"/> <a-input placeholder="密码"/>
</a-form-item> </a-form-item>
<!-- 表单项用于提交和保存按钮 -->
<a-form-item> <a-form-item>
<a-button type="primary">提交</a-button> <a-button type="primary">提交</a-button>
<a-button style="margin-left: 8px">保存</a-button> <a-button style="margin-left: 8px">保存</a-button>
@ -35,18 +43,25 @@
</a-form> </a-form>
</a-col> </a-col>
<!-- 右侧列占据24/24的宽度在lg及以上屏幕上占据8/24的宽度最小高度为180px -->
<a-col :md="24" :lg="8" :style="{ minHeight: '180px' }"> <a-col :md="24" :lg="8" :style="{ minHeight: '180px' }">
<!-- 使用ant-upload-preview组件创建一个头像预览框 -->
<div class="ant-upload-preview" @click="$refs.modal.edit(1)" > <div class="ant-upload-preview" @click="$refs.modal.edit(1)" >
<!-- 使用a-icon组件创建一个上传图标 -->
<a-icon type="cloud-upload-o" class="upload-icon"/> <a-icon type="cloud-upload-o" class="upload-icon"/>
<!-- 使用mask组件创建一个遮罩层 -->
<div class="mask"> <div class="mask">
<!-- 使用a-icon组件创建一个加号图标 -->
<a-icon type="plus" /> <a-icon type="plus" />
</div> </div>
<!-- 显示头像图片 -->
<img :src="option.img"/> <img :src="option.img"/>
</div> </div>
</a-col> </a-col>
</a-row> </a-row>
<!-- 使用avatar-modal组件创建一个头像模态框 -->
<avatar-modal ref="modal"> <avatar-modal ref="modal">
</avatar-modal> </avatar-modal>
@ -54,6 +69,7 @@
</template> </template>
<script> <script>
// AvatarModal
import AvatarModal from './AvatarModal' import AvatarModal from './AvatarModal'
export default { export default {
@ -65,18 +81,26 @@ export default {
// cropper // cropper
preview: {}, preview: {},
option: { option: {
//
img: '/avatar2.jpg', img: '/avatar2.jpg',
//
info: true, info: true,
//
size: 1, size: 1,
//
outputType: 'jpeg', outputType: 'jpeg',
//
canScale: false, canScale: false,
//
autoCrop: true, autoCrop: true,
// //
autoCropWidth: 180, autoCropWidth: 180,
autoCropHeight: 180, autoCropHeight: 180,
//
fixedBox: true, fixedBox: true,
// //
fixed: true, fixed: true,
//
fixedNumber: [1, 1] fixedNumber: [1, 1]
} }
} }
@ -142,4 +166,4 @@ export default {
overflow: hidden; overflow: hidden;
} }
} }
</style> </style>

@ -1,10 +1,14 @@
<script> <script>
//
import { colorList } from '../../../components/SettingDrawer/settingConfig' import { colorList } from '../../../components/SettingDrawer/settingConfig'
// ant-design-vueswitchlistitem
import ASwitch from 'ant-design-vue/es/switch' import ASwitch from 'ant-design-vue/es/switch'
import AList from 'ant-design-vue/es/list' import AList from 'ant-design-vue/es/list'
import AListItem from 'ant-design-vue/es/list/Item' import AListItem from 'ant-design-vue/es/list/Item'
// mixin
import { mixin } from '../../../utils/mixin' import { mixin } from '../../../utils/mixin'
// Meta
const Meta = AListItem.Meta const Meta = AListItem.Meta
export default { export default {
@ -20,6 +24,7 @@ export default {
} }
}, },
filters: { filters: {
//
themeFilter (theme) { themeFilter (theme) {
const themeMap = { const themeMap = {
'dark': '暗色', 'dark': '暗色',
@ -29,11 +34,13 @@ export default {
} }
}, },
methods: { methods: {
//
colorFilter (color) { colorFilter (color) {
const c = colorList.filter(o => o.color === color)[0] const c = colorList.filter(o => o.color === color)[0]
return c && c.key return c && c.key
}, },
//
onChange (checked) { onChange (checked) {
if (checked) { if (checked) {
this.$store.dispatch('ToggleTheme', 'dark') this.$store.dispatch('ToggleTheme', 'dark')
@ -72,4 +79,4 @@ export default {
<style scoped> <style scoped>
</style> </style>

@ -1,8 +1,12 @@
<template> <template>
<div class="page-header-index-wide"> <div class="page-header-index-wide">
<!-- 卡片组件用于展示内容 -->
<a-card :bordered="false" :bodyStyle="{ padding: '16px 0', height: '100%' }" :style="{ height: '100%' }"> <a-card :bordered="false" :bodyStyle="{ padding: '16px 0', height: '100%' }" :style="{ height: '100%' }">
<!-- 账户设置信息主容器 -->
<div class="account-settings-info-main" :class="device"> <div class="account-settings-info-main" :class="device">
<!-- 账户设置信息左侧 -->
<div class="account-settings-info-left"> <div class="account-settings-info-left">
<!-- 菜单组件用于展示导航 -->
<a-menu <a-menu
:mode="device == 'mobile' ? 'horizontal' : 'inline'" :mode="device == 'mobile' ? 'horizontal' : 'inline'"
:style="{ border: '0', width: device == 'mobile' ? '560px' : 'auto'}" :style="{ border: '0', width: device == 'mobile' ? '560px' : 'auto'}"
@ -10,22 +14,28 @@
type="inner" type="inner"
@openChange="onOpenChange" @openChange="onOpenChange"
> >
<!-- 菜单项用于展示导航链接 -->
<a-menu-item key="/account/settings/base"> <a-menu-item key="/account/settings/base">
<!-- 路由链接用于跳转到基本设置页面 -->
<router-link :to="{ name: 'BaseSettings' }"> <router-link :to="{ name: 'BaseSettings' }">
基本设置 基本设置
</router-link> </router-link>
</a-menu-item> </a-menu-item>
<a-menu-item key="/account/settings/custom"> <a-menu-item key="/account/settings/custom">
<!-- 路由链接用于跳转到个性化设置页面 -->
<router-link :to="{ name: 'CustomSettings' }"> <router-link :to="{ name: 'CustomSettings' }">
个性化 个性化
</router-link> </router-link>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</div> </div>
<!-- 账户设置信息右侧 -->
<div class="account-settings-info-right"> <div class="account-settings-info-right">
<!-- 账户设置信息标题 -->
<div class="account-settings-info-title"> <div class="account-settings-info-title">
<span>{{ $route.meta.title }}</span> <span>{{ $route.meta.title }}</span>
</div> </div>
<!-- 路由视图用于展示当前路由对应的组件 -->
<route-view></route-view> <route-view></route-view>
</div> </div>
</div> </div>
@ -132,4 +142,4 @@ export default {
} }
} }
</style> </style>
Loading…
Cancel
Save