ly před 1 rokem
rodič
revize
753d6489d4
53 změnil soubory, kde provedl 5532 přidání a 108 odebrání
  1. 114 1
      master/src/main/java/com/ruoyi/project/training/elearn/controller/TElPaperController.java
  2. 288 16
      master/src/main/java/com/ruoyi/project/training/elearn/controller/TElQuController.java
  3. 2 0
      master/src/main/java/com/ruoyi/project/training/elearn/controller/TElRepoController.java
  4. 103 0
      master/src/main/java/com/ruoyi/project/training/elearn/controller/TElUserBookController.java
  5. 104 0
      master/src/main/java/com/ruoyi/project/training/elearn/controller/TElUserExamController.java
  6. 138 0
      master/src/main/java/com/ruoyi/project/training/elearn/controller/TElUserQuController.java
  7. 53 0
      master/src/main/java/com/ruoyi/project/training/elearn/domain/TElPaper.java
  8. 43 0
      master/src/main/java/com/ruoyi/project/training/elearn/domain/TElPaperQu.java
  9. 20 0
      master/src/main/java/com/ruoyi/project/training/elearn/domain/TElPaperQuAnswer.java
  10. 194 0
      master/src/main/java/com/ruoyi/project/training/elearn/domain/TElUserBook.java
  11. 204 0
      master/src/main/java/com/ruoyi/project/training/elearn/domain/TElUserExam.java
  12. 194 0
      master/src/main/java/com/ruoyi/project/training/elearn/domain/TElUserQu.java
  13. 12 9
      master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElPaperQuAnswerMapper.java
  14. 16 9
      master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElPaperQuMapper.java
  15. 11 8
      master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElQuRepoMapper.java
  16. 62 0
      master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElUserBookMapper.java
  17. 65 0
      master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElUserExamMapper.java
  18. 7 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/ITElPaperService.java
  19. 61 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/ITElUserBookService.java
  20. 61 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/ITElUserExamService.java
  21. 61 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/ITElUserQuService.java
  22. 156 5
      master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElPaperServiceImpl.java
  23. 93 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElUserBookServiceImpl.java
  24. 93 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElUserExamServiceImpl.java
  25. 93 0
      master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElUserQuServiceImpl.java
  26. 65 0
      master/src/main/java/com/ruoyi/project/training/mapper/TElUserQuMapper.java
  27. 36 9
      master/src/main/resources/mybatis/training/elearn/TElPaperQuAnswerMapper.xml
  28. 55 8
      master/src/main/resources/mybatis/training/elearn/TElPaperQuMapper.xml
  29. 17 12
      master/src/main/resources/mybatis/training/elearn/TElQuMapper.xml
  30. 30 15
      master/src/main/resources/mybatis/training/elearn/TElQuRepoMapper.xml
  31. 114 0
      master/src/main/resources/mybatis/training/elearn/TElUserBookMapper.xml
  32. 127 0
      master/src/main/resources/mybatis/training/elearn/TElUserExamMapper.xml
  33. 128 0
      master/src/main/resources/mybatis/training/elearn/TElUserQuMapper.xml
  34. 47 0
      ui/src/api/training/elearn/paper.js
  35. 3 3
      ui/src/api/training/elearn/qu.js
  36. 4 0
      ui/src/api/training/elearn/repo.js
  37. 53 0
      ui/src/api/training/elearn/userBook.js
  38. 53 0
      ui/src/api/training/elearn/userExam.js
  39. 62 0
      ui/src/api/training/elearn/userQu.js
  40. 20 0
      ui/src/router/index.js
  41. 1 1
      ui/src/views/training/elearn/exam/index.vue
  42. 63 0
      ui/src/views/training/elearn/paper/ExamTimer/index.vue
  43. 442 0
      ui/src/views/training/elearn/paper/exam.vue
  44. 0 7
      ui/src/views/training/elearn/paper/index.vue
  45. 0 1
      ui/src/views/training/elearn/paper/list.vue
  46. 3 2
      ui/src/views/training/elearn/paper/preview.vue
  47. 228 0
      ui/src/views/training/elearn/paper/result.vue
  48. 40 2
      ui/src/views/training/elearn/qu/index.vue
  49. 393 0
      ui/src/views/training/elearn/userBook/index.vue
  50. 386 0
      ui/src/views/training/elearn/userExam/index.vue
  51. 428 0
      ui/src/views/training/elearn/userQu/index.vue
  52. 291 0
      ui/src/views/training/elearn/userQu/repo.vue
  53. 195 0
      ui/src/views/training/elearn/userQu/train.vue

+ 114 - 1
master/src/main/java/com/ruoyi/project/training/elearn/controller/TElPaperController.java

@@ -1,13 +1,24 @@
 package com.ruoyi.project.training.elearn.controller;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 
+import com.alibaba.fastjson.JSON;
 import com.ruoyi.framework.job.service.JobService;
+import com.ruoyi.project.training.elearn.domain.TElPaperQu;
+import com.ruoyi.project.training.elearn.domain.TElPaperQuAnswer;
+import com.ruoyi.project.training.elearn.domain.TElQu;
 import com.ruoyi.project.training.elearn.job.BreakExamJob;
+import com.ruoyi.project.training.elearn.mapper.TElPaperQuAnswerMapper;
+import com.ruoyi.project.training.elearn.mapper.TElPaperQuMapper;
+import com.ruoyi.project.training.elearn.mapper.TElQuMapper;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
@@ -25,6 +36,8 @@ import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.page.TableDataInfo;
 
+import javax.annotation.Resource;
+
 /**
  * 在线考试Controller
  *
@@ -37,7 +50,12 @@ public class TElPaperController extends BaseController
 {
     @Autowired
     private ITElPaperService tElPaperService;
-
+    @Resource
+    private TElPaperQuMapper tElPaperQuMapper;
+    @Resource
+    private TElPaperQuAnswerMapper tElPaperQuAnswerMapper;
+    @Resource
+    private TElQuMapper tElQuMapper;
 
     /**
      * 查询在线考试列表
@@ -96,6 +114,101 @@ public class TElPaperController extends BaseController
         return AjaxResult.success(tElPaperService.createPaper(tElPaper,getUserId()));
     }
 
+    /**
+     * 试卷详情
+     */
+    @GetMapping(value = "/paperDetail")
+    public AjaxResult paperDetail(TElPaper tElPaper)
+    {
+        TElPaper paper = tElPaperService.selectTElPaperById(tElPaper.getPaperId());
+        //查询题目列表
+        TElPaperQu quey = new TElPaperQu();
+        quey.setPaperId(tElPaper.getPaperId());
+        List<TElPaperQu> list = tElPaperQuMapper.selectTElPaperQuList(quey);
+        logger.info(JSON.toJSONString(list));
+        List<TElPaperQu> radioList = new ArrayList<>();
+        List<TElPaperQu> multiList = new ArrayList<>();
+        List<TElPaperQu> judgeList = new ArrayList<>();
+        for(TElPaperQu item: list){
+            if("1".equals(item.getQuType().toString())){
+                radioList.add(item);
+            }
+            if("2".equals(item.getQuType().toString())){
+                multiList.add(item);
+            }
+            if("3".equals(item.getQuType().toString())){
+                judgeList.add(item);
+            }
+        }
+        paper.setRadioList(radioList);
+        paper.setMultiList(multiList);
+        paper.setJudgeList(judgeList);
+
+        // 结束时间
+        Calendar cl = Calendar.getInstance();
+        cl.setTime(paper.getCreatedate());
+        cl.add(Calendar.MINUTE, paper.getTotalTime().intValue());
+
+        long seconds = (cl.getTimeInMillis() - System.currentTimeMillis()) / 1000;
+        paper.setLeftSeconds(seconds);
+
+        return AjaxResult.success(paper);
+    }
+
+    /**
+     * 试卷题目详情
+     */
+    @GetMapping(value = "/quDetail")
+    public AjaxResult quDetail(TElPaperQu paperQu)
+    {
+        TElPaperQu tElPaperQu = tElPaperQuMapper.selectTElPaperQuByPaperIdAndQuId(paperQu);
+
+        // 问题
+        TElQu qu = tElQuMapper.selectTElQuById(paperQu.getQuId());
+        tElPaperQu.setContent(qu.getContent());
+        tElPaperQu.setImage(qu.getImage());
+        //答案
+        TElPaperQuAnswer param = new TElPaperQuAnswer();
+        param.setPaperId(paperQu.getPaperId());
+        param.setQuId(qu.getQuId());
+        List<TElPaperQuAnswer> answerList = tElPaperQuAnswerMapper.selectTElPaperQuAnswerListByExam(param);
+        tElPaperQu.setAnswerList(answerList);
+
+        return AjaxResult.success(tElPaperQu);
+    }
+
+    /**
+     * 交卷
+     */
+    @Log(title = "交卷", businessType = BusinessType.INSERT)
+    @GetMapping(value = "/handExam")
+    public AjaxResult handExam(TElPaperQu paperQu)
+    {
+        tElPaperService.handExam(paperQu);
+        return AjaxResult.success();
+    }
+
+    /**
+     * 试卷详情
+     */
+    @GetMapping(value = "/paperResult")
+    public AjaxResult paperResult(TElPaperQu paperQu)
+    {
+        return AjaxResult.success(tElPaperService.paperResult(paperQu));
+    }
+
+
+
+    /**
+     * 填充答案
+     */
+    @PostMapping(value = "/fillAnswer")
+    public AjaxResult fillAnswer(@RequestBody TElPaperQu paperQu)
+    {
+        tElPaperService.fillAnswer(paperQu);
+        return AjaxResult.success();
+    }
+
     /**
      * 修改在线考试
      */

+ 288 - 16
master/src/main/java/com/ruoyi/project/training/elearn/controller/TElQuController.java

@@ -1,6 +1,23 @@
 package com.ruoyi.project.training.elearn.controller;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.fastjson.JSON;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.training.elearn.domain.TElQuAnswer;
+import com.ruoyi.project.training.spec.domain.vo.TStPlanImportVO;
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.hwpf.extractor.WordExtractor;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -19,6 +36,7 @@ import com.ruoyi.framework.web.controller.BaseController;
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.page.TableDataInfo;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 试题Controller
@@ -28,8 +46,7 @@ import com.ruoyi.framework.web.page.TableDataInfo;
  */
 @RestController
 @RequestMapping("/elearn/qu")
-public class TElQuController extends BaseController
-{
+public class TElQuController extends BaseController {
     @Autowired
     private ITElQuService tElQuService;
 
@@ -37,9 +54,8 @@ public class TElQuController extends BaseController
      * 查询试题列表
      */
     @PreAuthorize("@ss.hasPermi('elearn:qu:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(TElQu tElQu)
-    {
+    @PostMapping ("/list")
+    public TableDataInfo list(@RequestBody TElQu tElQu) {
         startPage();
         List<TElQu> list = tElQuService.selectTElQuList(tElQu);
         return getDataTable(list);
@@ -51,8 +67,7 @@ public class TElQuController extends BaseController
     @PreAuthorize("@ss.hasPermi('elearn:qu:export')")
     @Log(title = "试题", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult export(TElQu tElQu)
-    {
+    public AjaxResult export(TElQu tElQu) {
         List<TElQu> list = tElQuService.selectTElQuList(tElQu);
         ExcelUtil<TElQu> util = new ExcelUtil<TElQu>(TElQu.class);
         return util.exportExcel(list, "qu");
@@ -63,8 +78,7 @@ public class TElQuController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('elearn:qu:query')")
     @GetMapping(value = "/{quId}")
-    public AjaxResult getInfo(@PathVariable("quId") Long quId)
-    {
+    public AjaxResult getInfo(@PathVariable("quId") Long quId) {
         return AjaxResult.success(tElQuService.selectTElQuById(quId));
     }
 
@@ -74,8 +88,7 @@ public class TElQuController extends BaseController
     @PreAuthorize("@ss.hasPermi('elearn:qu:add')")
     @Log(title = "试题", businessType = BusinessType.INSERT)
     @PostMapping
-    public AjaxResult add(@RequestBody TElQu tElQu)
-    {
+    public AjaxResult add(@RequestBody TElQu tElQu) {
         return toAjax(tElQuService.insertTElQu(tElQu));
     }
 
@@ -85,8 +98,7 @@ public class TElQuController extends BaseController
     @PreAuthorize("@ss.hasPermi('elearn:qu:edit')")
     @Log(title = "试题", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody TElQu tElQu)
-    {
+    public AjaxResult edit(@RequestBody TElQu tElQu) {
         return toAjax(tElQuService.updateTElQu(tElQu));
     }
 
@@ -95,9 +107,269 @@ public class TElQuController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('elearn:qu:remove')")
     @Log(title = "试题", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{quIds}")
-    public AjaxResult remove(@PathVariable Long[] quIds)
-    {
+    @DeleteMapping("/{quIds}")
+    public AjaxResult remove(@PathVariable Long[] quIds) {
         return toAjax(tElQuService.deleteTElQuByIds(quIds));
     }
+
+    /**
+     * 导入题目
+     *
+     * @param file          用户上传的文件
+     * @param updateSupport 是否更新支持,如果已存在,则进行更新数据
+     * @param repoIds        题库id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/importData")
+    public AjaxResult importData(MultipartFile file, boolean updateSupport, String repoIds) throws Exception {
+        logger.info("xzst-------------------");
+        if (StringUtils.isEmpty(repoIds)) {
+            return AjaxResult.error("未选择题库");
+        }
+        List<String> repos = Arrays.asList(repoIds.split(",").clone());
+
+
+        FileInputStream fis = (FileInputStream) file.getInputStream();
+        HWPFDocument document = new HWPFDocument(fis);
+
+        WordExtractor extractor = new WordExtractor(document);
+        String[] paragraphs = extractor.getParagraphText();
+        // 正则表达式匹配选项
+        Pattern pattern = Pattern.compile("\\[T\\](.*?)A、");
+        TElQu qu = new TElQu();
+        String quStr = "";
+        for (String para : paragraphs) {
+            logger.info(para);
+            String text = para.trim();
+
+            if (text.startsWith("[T]") || text.startsWith("@[T]")) {
+                char thirdFromEnd = text.charAt(text.length() - 3);
+                logger.info(String.valueOf(thirdFromEnd));
+                if ("2".equals(String.valueOf(thirdFromEnd))) { //单选题
+                    qu = new TElQu();
+                    qu.setQuType(1l);
+                } else if ("7".equals(String.valueOf(thirdFromEnd))) {//多选题
+                    qu = new TElQu();
+                    qu.setQuType(2l);
+                } else if ("1".equals(String.valueOf(thirdFromEnd))) {//判断选题
+                    qu = new TElQu();
+                    qu.setQuType(3l);
+                } else {
+                    qu.setQuType(-1l);
+                }
+                quStr = text + "     ";
+
+            } else if (text.endsWith("[D/]")) {
+                quStr = quStr + text;
+                if (qu.getQuType() == 1l) {
+                    Matcher matcher = pattern.matcher(quStr);
+
+                    if (matcher.find()) {
+                        String content = matcher.group(1);
+                        System.out.println("从[T]到A、之间的内容是: " + content);
+                        qu.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+                    //答案
+                    Pattern patternA = Pattern.compile("A、(.*?)B、");
+                    Matcher matcherA = patternA.matcher(quStr);
+                    TElQuAnswer answerA = new TElQuAnswer();
+                    answerA.setIsRight(0l);
+                    TElQuAnswer answerB = new TElQuAnswer();
+                    answerB.setIsRight(0l);
+                    TElQuAnswer answerC = new TElQuAnswer();
+                    answerC.setIsRight(0l);
+                    TElQuAnswer answerD = new TElQuAnswer();
+                    answerD.setIsRight(0l);
+                    if (matcherA.find()) {
+                        String content = matcherA.group(1);
+                        System.out.println("从A、到B、之间的内容是: " + content);
+                        answerA.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+
+                    Pattern patternB = Pattern.compile("B、(.*?)C、");
+                    Matcher matcherB = patternB.matcher(quStr);
+                    if (matcherB.find()) {
+                        String content = matcherB.group(1);
+                        answerB.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+
+                    Pattern patternC = Pattern.compile("C、(.*?)D、");
+                    Matcher matcherC = patternC.matcher(quStr);
+                    if (matcherC.find()) {
+                        String content = matcherC.group(1);
+                        answerC.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+
+                    Pattern patternD = Pattern.compile("D、(.*?)(?=\\[T/\\])");
+                    Matcher matcherD = patternD.matcher(quStr);
+                    if (matcherD.find()) {
+                        String content = matcherD.group(1);
+                        answerD.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+                    Pattern patternR = Pattern.compile("\\[D\\](.*?)(?=\\[D/\\])");
+                    Matcher matcherR = patternR.matcher(quStr);
+                    if (matcherR.find()) {
+                        String res = matcherR.group(1);
+                        System.out.println("从[D]到[D/]之间的内容是: " + res);
+                        if ("A".equals(res)) {
+                            answerA.setIsRight(1l);
+                        } else if ("B".equals(res)) {
+                            answerB.setIsRight(1l);
+                        } else if ("C".equals(res)) {
+                            answerC.setIsRight(1l);
+                        } else if ("D".equals(res)) {
+                            answerD.setIsRight(1l);
+                        }
+                    }
+                    List<TElQuAnswer> answerList = new ArrayList<>();
+                    answerList.add(answerA);
+                    answerList.add(answerB);
+                    answerList.add(answerC);
+                    answerList.add(answerD);
+                    qu.setAnswerList(answerList);
+                    qu.setRepoIds(repos);
+                    logger.info(JSON.toJSONString(qu));
+                    tElQuService.insertTElQu(qu);
+                    quStr = "";
+                } else if (qu.getQuType() == 2l) {
+                    Matcher matcher = pattern.matcher(quStr);
+
+                    if (matcher.find()) {
+                        String content = matcher.group(1);
+                        System.out.println("从[T]到A、之间的内容是: " + content);
+                        qu.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+                    //答案
+                    Pattern patternA = Pattern.compile("A、(.*?)B、");
+                    Matcher matcherA = patternA.matcher(quStr);
+                    TElQuAnswer answerA = new TElQuAnswer();
+                    answerA.setIsRight(0l);
+                    TElQuAnswer answerB = new TElQuAnswer();
+                    answerB.setIsRight(0l);
+                    TElQuAnswer answerC = new TElQuAnswer();
+                    answerC.setIsRight(0l);
+                    TElQuAnswer answerD = new TElQuAnswer();
+                    answerD.setIsRight(0l);
+                    if (matcherA.find()) {
+                        String content = matcherA.group(1);
+                        System.out.println("从A、到B、之间的内容是: " + content);
+                        answerA.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+
+                    Pattern patternB = Pattern.compile("B、(.*?)C、");
+                    Matcher matcherB = patternB.matcher(quStr);
+                    if (matcherB.find()) {
+                        String content = matcherB.group(1);
+                        answerB.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+
+                    Pattern patternC = Pattern.compile("C、(.*?)D、");
+                    Matcher matcherC = patternC.matcher(quStr);
+                    if (matcherC.find()) {
+                        String content = matcherC.group(1);
+                        answerC.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+
+                    Pattern patternD = Pattern.compile("D、(.*?)(?=\\[T/\\])");
+                    Matcher matcherD = patternD.matcher(quStr);
+                    if (matcherD.find()) {
+                        String content = matcherD.group(1);
+                        answerD.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+                    Pattern patternR = Pattern.compile("\\[D\\](.*?)(?=\\[D/\\])");
+                    Matcher matcherR = patternR.matcher(quStr);
+                    if (matcherR.find()) {
+                        String res = matcherR.group(1);
+                        System.out.println("从[D]到[D/]之间的内容是: " + res);
+                        List<String> resList = Arrays.asList(res.split(","));
+                        if (resList.contains("A")) {
+                            answerA.setIsRight(1l);
+                        }
+                        if (resList.contains("B")) {
+                            answerB.setIsRight(1l);
+                        }
+                        if (resList.contains("C")) {
+                            answerC.setIsRight(1l);
+                        }
+                        if (resList.contains("D")) {
+                            answerD.setIsRight(1l);
+                        }
+                    }
+                    List<TElQuAnswer> answerList = new ArrayList<>();
+                    answerList.add(answerA);
+                    answerList.add(answerB);
+                    answerList.add(answerC);
+                    answerList.add(answerD);
+                    qu.setAnswerList(answerList);
+                    qu.setRepoIds(repos);
+                    logger.info(JSON.toJSONString(qu));
+                    tElQuService.insertTElQu(qu);
+                    quStr = "";
+                } else if (qu.getQuType() == 3l) {
+                    // 正则表达式匹配选项
+                    Pattern patternQ = Pattern.compile("\\[T\\](.*?)(?=\\[T/\\])");
+                    Matcher matcher = patternQ.matcher(quStr);
+
+                    if (matcher.find()) {
+                        String content = matcher.group(1);
+                        System.out.println("从[T]到A、之间的内容是: " + content);
+                        qu.setContent(content);
+                    } else {
+                        System.out.println("未找到匹配的内容");
+                    }
+                    TElQuAnswer answerA = new TElQuAnswer();
+                    answerA.setIsRight(0l);
+                    answerA.setContent("正确");
+                    TElQuAnswer answerB = new TElQuAnswer();
+                    answerB.setIsRight(0l);
+                    answerB.setContent("错误");
+                    Pattern patternR = Pattern.compile("\\[D\\](.*?)(?=\\[D/\\])");
+                    Matcher matcherR = patternR.matcher(quStr);
+                    if (matcherR.find()) {
+                        String res = matcherR.group(1);
+                        System.out.println("从[D]到[D/]之间的内容是: " + res);
+                        if ("√".equals(res)) {
+                            answerA.setIsRight(1l);
+                        } else {
+                            answerB.setIsRight(1l);
+                        }
+                    }
+                    List<TElQuAnswer> answerList = new ArrayList<>();
+                    answerList.add(answerA);
+                    answerList.add(answerB);
+                    qu.setAnswerList(answerList);
+                    qu.setRepoIds(repos);
+                    logger.info(JSON.toJSONString(qu));
+                    tElQuService.insertTElQu(qu);
+                    quStr = "";
+                }
+
+            } else {
+                quStr = quStr + text;
+            }
+        }
+
+        return AjaxResult.success();
+    }
 }

+ 2 - 0
master/src/main/java/com/ruoyi/project/training/elearn/controller/TElRepoController.java

@@ -1,6 +1,8 @@
 package com.ruoyi.project.training.elearn.controller;
 
 import java.util.List;
+
+import com.ruoyi.project.training.elearn.domain.TElUserQu;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;

+ 103 - 0
master/src/main/java/com/ruoyi/project/training/elearn/controller/TElUserBookController.java

@@ -0,0 +1,103 @@
+package com.ruoyi.project.training.elearn.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.project.training.elearn.domain.TElUserBook;
+import com.ruoyi.project.training.elearn.service.ITElUserBookService;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.web.page.TableDataInfo;
+
+/**
+ * 错题本Controller
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+@RestController
+@RequestMapping("/elearn/userBook")
+public class TElUserBookController extends BaseController
+{
+    @Autowired
+    private ITElUserBookService tElUserBookService;
+
+    /**
+     * 查询错题本列表
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userBook:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(TElUserBook tElUserBook)
+    {
+        startPage();
+        List<TElUserBook> list = tElUserBookService.selectTElUserBookList(tElUserBook);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出错题本列表
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userBook:export')")
+    @Log(title = "错题本", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(TElUserBook tElUserBook)
+    {
+        List<TElUserBook> list = tElUserBookService.selectTElUserBookList(tElUserBook);
+        ExcelUtil<TElUserBook> util = new ExcelUtil<TElUserBook>(TElUserBook.class);
+        return util.exportExcel(list, "userBook");
+    }
+
+    /**
+     * 获取错题本详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userBook:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(tElUserBookService.selectTElUserBookById(id));
+    }
+
+    /**
+     * 新增错题本
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userBook:add')")
+    @Log(title = "错题本", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody TElUserBook tElUserBook)
+    {
+        return toAjax(tElUserBookService.insertTElUserBook(tElUserBook));
+    }
+
+    /**
+     * 修改错题本
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userBook:edit')")
+    @Log(title = "错题本", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody TElUserBook tElUserBook)
+    {
+        return toAjax(tElUserBookService.updateTElUserBook(tElUserBook));
+    }
+
+    /**
+     * 删除错题本
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userBook:remove')")
+    @Log(title = "错题本", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(tElUserBookService.deleteTElUserBookByIds(ids));
+    }
+}

+ 104 - 0
master/src/main/java/com/ruoyi/project/training/elearn/controller/TElUserExamController.java

@@ -0,0 +1,104 @@
+package com.ruoyi.project.training.elearn.controller;
+
+import java.util.List;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.project.training.elearn.domain.TElUserExam;
+import com.ruoyi.project.training.elearn.service.ITElUserExamService;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.web.page.TableDataInfo;
+
+/**
+ * 我的成绩Controller
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+@RestController
+@RequestMapping("/elearn/userExam")
+public class TElUserExamController extends BaseController
+{
+    @Autowired
+    private ITElUserExamService tElUserExamService;
+
+    /**
+     * 查询我的成绩列表
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userExam:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(TElUserExam tElUserExam)
+    {
+        tElUserExam.setUserId(getUserId());
+        startPage();
+        List<TElUserExam> list = tElUserExamService.selectTElUserExamList(tElUserExam);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出我的成绩列表
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userExam:export')")
+    @Log(title = "我的成绩", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(TElUserExam tElUserExam)
+    {
+        List<TElUserExam> list = tElUserExamService.selectTElUserExamList(tElUserExam);
+        ExcelUtil<TElUserExam> util = new ExcelUtil<TElUserExam>(TElUserExam.class);
+        return util.exportExcel(list, "userExam");
+    }
+
+    /**
+     * 获取我的成绩详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userExam:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(tElUserExamService.selectTElUserExamById(id));
+    }
+
+    /**
+     * 新增我的成绩
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userExam:add')")
+    @Log(title = "我的成绩", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody TElUserExam tElUserExam)
+    {
+        return toAjax(tElUserExamService.insertTElUserExam(tElUserExam));
+    }
+
+    /**
+     * 修改我的成绩
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userExam:edit')")
+    @Log(title = "我的成绩", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody TElUserExam tElUserExam)
+    {
+        return toAjax(tElUserExamService.updateTElUserExam(tElUserExam));
+    }
+
+    /**
+     * 删除我的成绩
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userExam:remove')")
+    @Log(title = "我的成绩", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(tElUserExamService.deleteTElUserExamByIds(ids));
+    }
+}

+ 138 - 0
master/src/main/java/com/ruoyi/project/training/elearn/controller/TElUserQuController.java

@@ -0,0 +1,138 @@
+package com.ruoyi.project.training.elearn.controller;
+
+import java.util.List;
+
+import com.ruoyi.project.training.elearn.domain.TElQuRepo;
+import com.ruoyi.project.training.elearn.mapper.TElQuRepoMapper;
+import com.ruoyi.project.training.elearn.mapper.TElUserQuMapper;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.project.training.elearn.domain.TElUserQu;
+import com.ruoyi.project.training.elearn.service.ITElUserQuService;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.web.page.TableDataInfo;
+
+import javax.annotation.Resource;
+
+/**
+ * 答题记录Controller
+ *
+ * @author ssy
+ * @date 2024-06-04
+ */
+@RestController
+@RequestMapping("/elearn/userQu")
+public class TElUserQuController extends BaseController
+{
+    @Autowired
+    private ITElUserQuService tElUserQuService;
+    @Resource
+    private TElUserQuMapper tElUserQuMapper;
+    @Resource
+    private TElQuRepoMapper tElQuRepoMapper;
+    /**
+     * 查询答题记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userQu:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(TElUserQu tElUserQu)
+    {
+        startPage();
+        List<TElUserQu> list = tElUserQuService.selectTElUserQuList(tElUserQu);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出答题记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userQu:export')")
+    @Log(title = "答题记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(TElUserQu tElUserQu)
+    {
+        List<TElUserQu> list = tElUserQuService.selectTElUserQuList(tElUserQu);
+        ExcelUtil<TElUserQu> util = new ExcelUtil<TElUserQu>(TElUserQu.class);
+        return util.exportExcel(list, "userQu");
+    }
+
+    /**
+     * 获取答题记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userQu:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(tElUserQuService.selectTElUserQuById(id));
+    }
+
+    /**
+     * 修改题库
+     */
+    @PostMapping(value = "/nextQu")
+    public AjaxResult nextQu(@RequestBody TElUserQu tElUserQu)
+    {
+        tElUserQu.setUserId(getUserId());
+
+        if (tElUserQu.getQuId() == null) { //答题第一题
+            TElUserQu last = tElUserQuMapper.selectTElUserQuLast(tElUserQu);
+            if (last == null) {
+                //首次答题
+                TElQuRepo quRepo =  tElQuRepoMapper.selectTElQuRepoLast(tElUserQu);
+                return   AjaxResult.success(quRepo.getQuId());
+            }else {
+                //继续上次答题
+              return   AjaxResult.success(last.getQuId());
+            }
+        }
+        //继续答题
+        TElQuRepo quRepo =  tElQuRepoMapper.selectTElQuRepoLast(tElUserQu);
+
+        return AjaxResult.success(quRepo.getQuId());
+    }
+
+
+    /**
+     * 新增答题记录
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userQu:add')")
+    @Log(title = "答题记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody TElUserQu tElUserQu)
+    {
+        return toAjax(tElUserQuService.insertTElUserQu(tElUserQu));
+    }
+
+    /**
+     * 修改答题记录
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userQu:edit')")
+    @Log(title = "答题记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody TElUserQu tElUserQu)
+    {
+        return toAjax(tElUserQuService.updateTElUserQu(tElUserQu));
+    }
+
+    /**
+     * 删除答题记录
+     */
+    @PreAuthorize("@ss.hasPermi('elearn:userQu:remove')")
+    @Log(title = "答题记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(tElUserQuService.deleteTElUserQuByIds(ids));
+    }
+}

+ 53 - 0
master/src/main/java/com/ruoyi/project/training/elearn/domain/TElPaper.java

@@ -1,9 +1,12 @@
 package com.ruoyi.project.training.elearn.domain;
 
 import java.util.Date;
+import java.util.List;
+
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import com.ruoyi.framework.web.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
@@ -106,6 +109,16 @@ public class TElPaper extends BaseEntity
     @Excel(name = "备注")
     private String remarks;
 
+    private List<TElPaperQu> radioList;
+
+    private List<TElPaperQu> multiList;
+
+    private List<TElPaperQu> judgeList;
+
+    private List<TElPaperQu> quList;
+
+    private Long leftSeconds;
+
     public void setPaperId(Long paperId)
     {
         this.paperId = paperId;
@@ -305,6 +318,46 @@ public class TElPaper extends BaseEntity
         return remarks;
     }
 
+    public List<TElPaperQu> getRadioList() {
+        return radioList;
+    }
+
+    public void setRadioList(List<TElPaperQu> radioList) {
+        this.radioList = radioList;
+    }
+
+    public List<TElPaperQu> getMultiList() {
+        return multiList;
+    }
+
+    public void setMultiList(List<TElPaperQu> multiList) {
+        this.multiList = multiList;
+    }
+
+    public List<TElPaperQu> getJudgeList() {
+        return judgeList;
+    }
+
+    public void setJudgeList(List<TElPaperQu> judgeList) {
+        this.judgeList = judgeList;
+    }
+
+    public Long getLeftSeconds() {
+        return leftSeconds;
+    }
+
+    public void setLeftSeconds(Long leftSeconds) {
+        this.leftSeconds = leftSeconds;
+    }
+
+    public List<TElPaperQu> getQuList() {
+        return quList;
+    }
+
+    public void setQuList(List<TElPaperQu> quList) {
+        this.quList = quList;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 43 - 0
master/src/main/java/com/ruoyi/project/training/elearn/domain/TElPaperQu.java

@@ -2,9 +2,12 @@ package com.ruoyi.project.training.elearn.domain;
 
 import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import com.ruoyi.framework.web.domain.BaseEntity;
+import io.swagger.annotations.ApiModelProperty;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
+import java.util.List;
+
 /**
  * 试卷题目对象 t_el_paper_qu
  *
@@ -54,6 +57,14 @@ public class TElPaperQu extends BaseEntity
     @Excel(name = "是否答对")
     private Long isRight;
 
+    private String image;
+
+    private String content;
+
+    List<TElPaperQuAnswer> answerList;
+
+    private List<String> answers;
+
     public void setId(Long id)
     {
         this.id = id;
@@ -145,6 +156,38 @@ public class TElPaperQu extends BaseEntity
         return isRight;
     }
 
+    public List<String> getAnswers() {
+        return answers;
+    }
+
+    public void setAnswers(List<String> answers) {
+        this.answers = answers;
+    }
+
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public List<TElPaperQuAnswer> getAnswerList() {
+        return answerList;
+    }
+
+    public void setAnswerList(List<TElPaperQuAnswer> answerList) {
+        this.answerList = answerList;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 20 - 0
master/src/main/java/com/ruoyi/project/training/elearn/domain/TElPaperQuAnswer.java

@@ -46,6 +46,10 @@ public class TElPaperQuAnswer extends BaseEntity
     @Excel(name = "选项标签")
     private String abc;
 
+    private String image;
+
+    private String content;
+
     public void setId(Long id)
     {
         this.id = id;
@@ -119,6 +123,22 @@ public class TElPaperQuAnswer extends BaseEntity
         return abc;
     }
 
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 194 - 0
master/src/main/java/com/ruoyi/project/training/elearn/domain/TElUserBook.java

@@ -0,0 +1,194 @@
+package com.ruoyi.project.training.elearn.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 错题本对象 t_el_user_book
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+public class TElUserBook extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** id */
+    private Long id;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 考试ID */
+    @Excel(name = "考试ID")
+    private Long examId;
+
+    /** 题目ID */
+    @Excel(name = "题目ID")
+    private Long quId;
+
+    /** 错误次数 */
+    @Excel(name = "错误次数")
+    private Long wrongCount;
+
+    /** 题目标题 */
+    @Excel(name = "题目标题")
+    private String title;
+
+    /** 删除 */
+    private Long delFlag;
+
+    /** 创建人 */
+    @Excel(name = "创建人")
+    private String createrCode;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date createdate;
+
+    /** 更新人 */
+    @Excel(name = "更新人")
+    private String updaterCode;
+
+    /** 更新日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "更新日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date updatedate;
+
+    /** 排序 */
+    @Excel(name = "排序")
+    private Long sort;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+    public void setExamId(Long examId)
+    {
+        this.examId = examId;
+    }
+
+    public Long getExamId()
+    {
+        return examId;
+    }
+    public void setQuId(Long quId)
+    {
+        this.quId = quId;
+    }
+
+    public Long getQuId()
+    {
+        return quId;
+    }
+    public void setWrongCount(Long wrongCount)
+    {
+        this.wrongCount = wrongCount;
+    }
+
+    public Long getWrongCount()
+    {
+        return wrongCount;
+    }
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public String getTitle()
+    {
+        return title;
+    }
+    public void setDelFlag(Long delFlag)
+    {
+        this.delFlag = delFlag;
+    }
+
+    public Long getDelFlag()
+    {
+        return delFlag;
+    }
+    public void setCreaterCode(String createrCode)
+    {
+        this.createrCode = createrCode;
+    }
+
+    public String getCreaterCode()
+    {
+        return createrCode;
+    }
+    public void setCreatedate(Date createdate)
+    {
+        this.createdate = createdate;
+    }
+
+    public Date getCreatedate()
+    {
+        return createdate;
+    }
+    public void setUpdaterCode(String updaterCode)
+    {
+        this.updaterCode = updaterCode;
+    }
+
+    public String getUpdaterCode()
+    {
+        return updaterCode;
+    }
+    public void setUpdatedate(Date updatedate)
+    {
+        this.updatedate = updatedate;
+    }
+
+    public Date getUpdatedate()
+    {
+        return updatedate;
+    }
+    public void setSort(Long sort)
+    {
+        this.sort = sort;
+    }
+
+    public Long getSort()
+    {
+        return sort;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("userId", getUserId())
+            .append("examId", getExamId())
+            .append("quId", getQuId())
+            .append("wrongCount", getWrongCount())
+            .append("title", getTitle())
+            .append("delFlag", getDelFlag())
+            .append("createrCode", getCreaterCode())
+            .append("createdate", getCreatedate())
+            .append("updaterCode", getUpdaterCode())
+            .append("updatedate", getUpdatedate())
+            .append("sort", getSort())
+            .toString();
+    }
+}

+ 204 - 0
master/src/main/java/com/ruoyi/project/training/elearn/domain/TElUserExam.java

@@ -0,0 +1,204 @@
+package com.ruoyi.project.training.elearn.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 我的成绩对象 t_el_user_exam
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+public class TElUserExam extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** id */
+    private Long id;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 考试ID */
+    @Excel(name = "考试ID")
+    private Long examId;
+
+    private String title;
+
+    /** 考试次数 */
+    @Excel(name = "考试次数")
+    private Long tryCount;
+
+    /** 最高分数 */
+    @Excel(name = "最高分数")
+    private Long maxScore;
+
+    /** 是否通过 */
+    @Excel(name = "是否通过")
+    private Long passed;
+
+    /** 删除 */
+    private Long delFlag;
+
+    /** 创建人 */
+    @Excel(name = "创建人")
+    private String createrCode;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date createdate;
+
+    /** 更新人 */
+    @Excel(name = "更新人")
+    private String updaterCode;
+
+    /** 更新日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "更新日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date updatedate;
+
+    /** 所属部门 */
+    @Excel(name = "所属部门")
+    private Long deptId;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+    public void setExamId(Long examId)
+    {
+        this.examId = examId;
+    }
+
+    public Long getExamId()
+    {
+        return examId;
+    }
+    public void setTryCount(Long tryCount)
+    {
+        this.tryCount = tryCount;
+    }
+
+    public Long getTryCount()
+    {
+        return tryCount;
+    }
+    public void setMaxScore(Long maxScore)
+    {
+        this.maxScore = maxScore;
+    }
+
+    public Long getMaxScore()
+    {
+        return maxScore;
+    }
+    public void setPassed(Long passed)
+    {
+        this.passed = passed;
+    }
+
+    public Long getPassed()
+    {
+        return passed;
+    }
+    public void setDelFlag(Long delFlag)
+    {
+        this.delFlag = delFlag;
+    }
+
+    public Long getDelFlag()
+    {
+        return delFlag;
+    }
+    public void setCreaterCode(String createrCode)
+    {
+        this.createrCode = createrCode;
+    }
+
+    public String getCreaterCode()
+    {
+        return createrCode;
+    }
+    public void setCreatedate(Date createdate)
+    {
+        this.createdate = createdate;
+    }
+
+    public Date getCreatedate()
+    {
+        return createdate;
+    }
+    public void setUpdaterCode(String updaterCode)
+    {
+        this.updaterCode = updaterCode;
+    }
+
+    public String getUpdaterCode()
+    {
+        return updaterCode;
+    }
+    public void setUpdatedate(Date updatedate)
+    {
+        this.updatedate = updatedate;
+    }
+
+    public Date getUpdatedate()
+    {
+        return updatedate;
+    }
+    public void setDeptId(Long deptId)
+    {
+        this.deptId = deptId;
+    }
+
+    public Long getDeptId()
+    {
+        return deptId;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("userId", getUserId())
+            .append("examId", getExamId())
+            .append("tryCount", getTryCount())
+            .append("maxScore", getMaxScore())
+            .append("passed", getPassed())
+            .append("delFlag", getDelFlag())
+            .append("createrCode", getCreaterCode())
+            .append("createdate", getCreatedate())
+            .append("updaterCode", getUpdaterCode())
+            .append("updatedate", getUpdatedate())
+            .append("deptId", getDeptId())
+            .toString();
+    }
+}

+ 194 - 0
master/src/main/java/com/ruoyi/project/training/elearn/domain/TElUserQu.java

@@ -0,0 +1,194 @@
+package com.ruoyi.project.training.elearn.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 答题记录对象 t_el_user_qu
+ *
+ * @author ssy
+ * @date 2024-06-04
+ */
+public class TElUserQu extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** id */
+    private Long id;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 考试ID */
+    @Excel(name = "考试ID")
+    private Long examId;
+
+    /** 题目ID */
+    @Excel(name = "题目ID")
+    private Long quId;
+
+    /** 题库ID */
+    @Excel(name = "题库ID")
+    private Long repoId;
+
+    /** 题目标题 */
+    @Excel(name = "题目标题")
+    private String title;
+
+    /** 删除 */
+    private Long delFlag;
+
+    /** 创建人 */
+    @Excel(name = "创建人")
+    private String createrCode;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date createdate;
+
+    /** 更新人 */
+    @Excel(name = "更新人")
+    private String updaterCode;
+
+    /** 更新日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd" , timezone = "GMT+8")
+    @Excel(name = "更新日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date updatedate;
+
+    /** 排序 */
+    @Excel(name = "排序")
+    private Long sort;
+
+    public void setId(Long id)
+    {
+        this.id = id;
+    }
+
+    public Long getId()
+    {
+        return id;
+    }
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+    public void setExamId(Long examId)
+    {
+        this.examId = examId;
+    }
+
+    public Long getExamId()
+    {
+        return examId;
+    }
+    public void setQuId(Long quId)
+    {
+        this.quId = quId;
+    }
+
+    public Long getQuId()
+    {
+        return quId;
+    }
+    public void setRepoId(Long repoId)
+    {
+        this.repoId = repoId;
+    }
+
+    public Long getRepoId()
+    {
+        return repoId;
+    }
+    public void setTitle(String title)
+    {
+        this.title = title;
+    }
+
+    public String getTitle()
+    {
+        return title;
+    }
+    public void setDelFlag(Long delFlag)
+    {
+        this.delFlag = delFlag;
+    }
+
+    public Long getDelFlag()
+    {
+        return delFlag;
+    }
+    public void setCreaterCode(String createrCode)
+    {
+        this.createrCode = createrCode;
+    }
+
+    public String getCreaterCode()
+    {
+        return createrCode;
+    }
+    public void setCreatedate(Date createdate)
+    {
+        this.createdate = createdate;
+    }
+
+    public Date getCreatedate()
+    {
+        return createdate;
+    }
+    public void setUpdaterCode(String updaterCode)
+    {
+        this.updaterCode = updaterCode;
+    }
+
+    public String getUpdaterCode()
+    {
+        return updaterCode;
+    }
+    public void setUpdatedate(Date updatedate)
+    {
+        this.updatedate = updatedate;
+    }
+
+    public Date getUpdatedate()
+    {
+        return updatedate;
+    }
+    public void setSort(Long sort)
+    {
+        this.sort = sort;
+    }
+
+    public Long getSort()
+    {
+        return sort;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("userId", getUserId())
+            .append("examId", getExamId())
+            .append("quId", getQuId())
+            .append("repoId", getRepoId())
+            .append("title", getTitle())
+            .append("delFlag", getDelFlag())
+            .append("createrCode", getCreaterCode())
+            .append("createdate", getCreatedate())
+            .append("updaterCode", getUpdaterCode())
+            .append("updatedate", getUpdatedate())
+            .append("sort", getSort())
+            .toString();
+    }
+}

+ 12 - 9
master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElPaperQuAnswerMapper.java

@@ -6,15 +6,15 @@ import com.ruoyi.project.training.elearn.domain.TElPaperQuAnswer;
 
 /**
  * 试卷题目答案Mapper接口
- * 
+ *
  * @author ssy
  * @date 2024-05-29
  */
-public interface TElPaperQuAnswerMapper 
+public interface TElPaperQuAnswerMapper
 {
     /**
      * 查询试卷题目答案
-     * 
+     *
      * @param id 试卷题目答案ID
      * @return 试卷题目答案
      */
@@ -22,16 +22,15 @@ public interface TElPaperQuAnswerMapper
 
     /**
      * 查询试卷题目答案列表
-     * 
+     *
      * @param tElPaperQuAnswer 试卷题目答案
      * @return 试卷题目答案集合
      */
-    @DataScope(deptAlias = "d")
     public List<TElPaperQuAnswer> selectTElPaperQuAnswerList(TElPaperQuAnswer tElPaperQuAnswer);
 
     /**
      * 新增试卷题目答案
-     * 
+     *
      * @param tElPaperQuAnswer 试卷题目答案
      * @return 结果
      */
@@ -39,7 +38,7 @@ public interface TElPaperQuAnswerMapper
 
     /**
      * 修改试卷题目答案
-     * 
+     *
      * @param tElPaperQuAnswer 试卷题目答案
      * @return 结果
      */
@@ -47,7 +46,7 @@ public interface TElPaperQuAnswerMapper
 
     /**
      * 删除试卷题目答案
-     * 
+     *
      * @param id 试卷题目答案ID
      * @return 结果
      */
@@ -55,9 +54,13 @@ public interface TElPaperQuAnswerMapper
 
     /**
      * 批量删除试卷题目答案
-     * 
+     *
      * @param ids 需要删除的数据ID
      * @return 结果
      */
     public int deleteTElPaperQuAnswerByIds(Long[] ids);
+
+    List<TElPaperQuAnswer> selectTElPaperQuAnswerListByExam(TElPaperQuAnswer param);
+
+    List<TElPaperQuAnswer> selectTElPaperQuAnswerListByPaper(TElPaperQuAnswer a);
 }

+ 16 - 9
master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElPaperQuMapper.java

@@ -6,15 +6,15 @@ import com.ruoyi.project.training.elearn.domain.TElPaperQu;
 
 /**
  * 试卷题目Mapper接口
- * 
+ *
  * @author ssy
  * @date 2024-05-29
  */
-public interface TElPaperQuMapper 
+public interface TElPaperQuMapper
 {
     /**
      * 查询试卷题目
-     * 
+     *
      * @param id 试卷题目ID
      * @return 试卷题目
      */
@@ -22,16 +22,15 @@ public interface TElPaperQuMapper
 
     /**
      * 查询试卷题目列表
-     * 
+     *
      * @param tElPaperQu 试卷题目
      * @return 试卷题目集合
      */
-    @DataScope(deptAlias = "d")
     public List<TElPaperQu> selectTElPaperQuList(TElPaperQu tElPaperQu);
 
     /**
      * 新增试卷题目
-     * 
+     *
      * @param tElPaperQu 试卷题目
      * @return 结果
      */
@@ -39,7 +38,7 @@ public interface TElPaperQuMapper
 
     /**
      * 修改试卷题目
-     * 
+     *
      * @param tElPaperQu 试卷题目
      * @return 结果
      */
@@ -47,7 +46,7 @@ public interface TElPaperQuMapper
 
     /**
      * 删除试卷题目
-     * 
+     *
      * @param id 试卷题目ID
      * @return 结果
      */
@@ -55,9 +54,17 @@ public interface TElPaperQuMapper
 
     /**
      * 批量删除试卷题目
-     * 
+     *
      * @param ids 需要删除的数据ID
      * @return 结果
      */
     public int deleteTElPaperQuByIds(Long[] ids);
+
+    TElPaperQu selectTElPaperQuByPaperIdAndQuId(TElPaperQu paperQu);
+
+    void updateTElPaperQuByKey(TElPaperQu qu);
+
+    int sumObjective(Long paperId);
+
+    List<TElPaperQu> selectTElPaperQuListByPaper(TElPaperQu query);
 }

+ 11 - 8
master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElQuRepoMapper.java

@@ -3,18 +3,19 @@ package com.ruoyi.project.training.elearn.mapper;
 import java.util.List;
 import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
 import com.ruoyi.project.training.elearn.domain.TElQuRepo;
+import com.ruoyi.project.training.elearn.domain.TElUserQu;
 
 /**
  * 题目题库关联Mapper接口
- * 
+ *
  * @author ssy
  * @date 2024-05-28
  */
-public interface TElQuRepoMapper 
+public interface TElQuRepoMapper
 {
     /**
      * 查询题目题库关联
-     * 
+     *
      * @param id 题目题库关联ID
      * @return 题目题库关联
      */
@@ -22,7 +23,7 @@ public interface TElQuRepoMapper
 
     /**
      * 查询题目题库关联列表
-     * 
+     *
      * @param tElQuRepo 题目题库关联
      * @return 题目题库关联集合
      */
@@ -31,7 +32,7 @@ public interface TElQuRepoMapper
 
     /**
      * 新增题目题库关联
-     * 
+     *
      * @param tElQuRepo 题目题库关联
      * @return 结果
      */
@@ -39,7 +40,7 @@ public interface TElQuRepoMapper
 
     /**
      * 修改题目题库关联
-     * 
+     *
      * @param tElQuRepo 题目题库关联
      * @return 结果
      */
@@ -47,7 +48,7 @@ public interface TElQuRepoMapper
 
     /**
      * 删除题目题库关联
-     * 
+     *
      * @param id 题目题库关联ID
      * @return 结果
      */
@@ -55,9 +56,11 @@ public interface TElQuRepoMapper
 
     /**
      * 批量删除题目题库关联
-     * 
+     *
      * @param ids 需要删除的数据ID
      * @return 结果
      */
     public int deleteTElQuRepoByIds(Long[] ids);
+
+    TElQuRepo selectTElQuRepoLast(TElUserQu tElUserQu);
 }

+ 62 - 0
master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElUserBookMapper.java

@@ -0,0 +1,62 @@
+package com.ruoyi.project.training.elearn.mapper;
+
+import java.util.List;
+import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
+import com.ruoyi.project.training.elearn.domain.TElUserBook;
+
+/**
+ * 错题本Mapper接口
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+public interface TElUserBookMapper
+{
+    /**
+     * 查询错题本
+     *
+     * @param id 错题本ID
+     * @return 错题本
+     */
+    public TElUserBook selectTElUserBookById(Long id);
+
+    /**
+     * 查询错题本列表
+     *
+     * @param tElUserBook 错题本
+     * @return 错题本集合
+     */
+    public List<TElUserBook> selectTElUserBookList(TElUserBook tElUserBook);
+
+    /**
+     * 新增错题本
+     *
+     * @param tElUserBook 错题本
+     * @return 结果
+     */
+    public int insertTElUserBook(TElUserBook tElUserBook);
+
+    /**
+     * 修改错题本
+     *
+     * @param tElUserBook 错题本
+     * @return 结果
+     */
+    public int updateTElUserBook(TElUserBook tElUserBook);
+
+    /**
+     * 删除错题本
+     *
+     * @param id 错题本ID
+     * @return 结果
+     */
+    public int deleteTElUserBookById(Long id);
+
+    /**
+     * 批量删除错题本
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteTElUserBookByIds(Long[] ids);
+}

+ 65 - 0
master/src/main/java/com/ruoyi/project/training/elearn/mapper/TElUserExamMapper.java

@@ -0,0 +1,65 @@
+package com.ruoyi.project.training.elearn.mapper;
+
+import java.util.List;
+import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
+import com.ruoyi.project.training.elearn.domain.TElPaper;
+import com.ruoyi.project.training.elearn.domain.TElUserExam;
+
+/**
+ * 我的成绩Mapper接口
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+public interface TElUserExamMapper
+{
+    /**
+     * 查询我的成绩
+     *
+     * @param id 我的成绩ID
+     * @return 我的成绩
+     */
+    public TElUserExam selectTElUserExamById(Long id);
+
+    /**
+     * 查询我的成绩列表
+     *
+     * @param tElUserExam 我的成绩
+     * @return 我的成绩集合
+     */
+    public List<TElUserExam> selectTElUserExamList(TElUserExam tElUserExam);
+
+    /**
+     * 新增我的成绩
+     *
+     * @param tElUserExam 我的成绩
+     * @return 结果
+     */
+    public int insertTElUserExam(TElUserExam tElUserExam);
+
+    /**
+     * 修改我的成绩
+     *
+     * @param tElUserExam 我的成绩
+     * @return 结果
+     */
+    public int updateTElUserExam(TElUserExam tElUserExam);
+
+    /**
+     * 删除我的成绩
+     *
+     * @param id 我的成绩ID
+     * @return 结果
+     */
+    public int deleteTElUserExamById(Long id);
+
+    /**
+     * 批量删除我的成绩
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteTElUserExamByIds(Long[] ids);
+
+    TElUserExam selectTElUserExamByPaPer(TElPaper paper);
+}

+ 7 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/ITElPaperService.java

@@ -2,6 +2,7 @@ package com.ruoyi.project.training.elearn.service;
 
 import java.util.List;
 import com.ruoyi.project.training.elearn.domain.TElPaper;
+import com.ruoyi.project.training.elearn.domain.TElPaperQu;
 
 /**
  * 在线考试Service接口
@@ -60,4 +61,10 @@ public interface ITElPaperService
     public int deleteTElPaperById(Long paperId);
 
     TElPaper createPaper(TElPaper tElPaper, Long userId);
+
+    void fillAnswer(TElPaperQu paperQu);
+
+    void handExam(TElPaperQu paperQu);
+
+    TElPaper paperResult(TElPaperQu paperQu);
 }

+ 61 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/ITElUserBookService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.training.elearn.service;
+
+import java.util.List;
+import com.ruoyi.project.training.elearn.domain.TElUserBook;
+
+/**
+ * 错题本Service接口
+ * 
+ * @author ssy
+ * @date 2024-05-31
+ */
+public interface ITElUserBookService 
+{
+    /**
+     * 查询错题本
+     * 
+     * @param id 错题本ID
+     * @return 错题本
+     */
+    public TElUserBook selectTElUserBookById(Long id);
+
+    /**
+     * 查询错题本列表
+     * 
+     * @param tElUserBook 错题本
+     * @return 错题本集合
+     */
+    public List<TElUserBook> selectTElUserBookList(TElUserBook tElUserBook);
+
+    /**
+     * 新增错题本
+     * 
+     * @param tElUserBook 错题本
+     * @return 结果
+     */
+    public int insertTElUserBook(TElUserBook tElUserBook);
+
+    /**
+     * 修改错题本
+     * 
+     * @param tElUserBook 错题本
+     * @return 结果
+     */
+    public int updateTElUserBook(TElUserBook tElUserBook);
+
+    /**
+     * 批量删除错题本
+     * 
+     * @param ids 需要删除的错题本ID
+     * @return 结果
+     */
+    public int deleteTElUserBookByIds(Long[] ids);
+
+    /**
+     * 删除错题本信息
+     * 
+     * @param id 错题本ID
+     * @return 结果
+     */
+    public int deleteTElUserBookById(Long id);
+}

+ 61 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/ITElUserExamService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.training.elearn.service;
+
+import java.util.List;
+import com.ruoyi.project.training.elearn.domain.TElUserExam;
+
+/**
+ * 我的成绩Service接口
+ * 
+ * @author ssy
+ * @date 2024-05-31
+ */
+public interface ITElUserExamService 
+{
+    /**
+     * 查询我的成绩
+     * 
+     * @param id 我的成绩ID
+     * @return 我的成绩
+     */
+    public TElUserExam selectTElUserExamById(Long id);
+
+    /**
+     * 查询我的成绩列表
+     * 
+     * @param tElUserExam 我的成绩
+     * @return 我的成绩集合
+     */
+    public List<TElUserExam> selectTElUserExamList(TElUserExam tElUserExam);
+
+    /**
+     * 新增我的成绩
+     * 
+     * @param tElUserExam 我的成绩
+     * @return 结果
+     */
+    public int insertTElUserExam(TElUserExam tElUserExam);
+
+    /**
+     * 修改我的成绩
+     * 
+     * @param tElUserExam 我的成绩
+     * @return 结果
+     */
+    public int updateTElUserExam(TElUserExam tElUserExam);
+
+    /**
+     * 批量删除我的成绩
+     * 
+     * @param ids 需要删除的我的成绩ID
+     * @return 结果
+     */
+    public int deleteTElUserExamByIds(Long[] ids);
+
+    /**
+     * 删除我的成绩信息
+     * 
+     * @param id 我的成绩ID
+     * @return 结果
+     */
+    public int deleteTElUserExamById(Long id);
+}

+ 61 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/ITElUserQuService.java

@@ -0,0 +1,61 @@
+package com.ruoyi.project.training.elearn.service;
+
+import java.util.List;
+import com.ruoyi.project.training.elearn.domain.TElUserQu;
+
+/**
+ * 答题记录Service接口
+ * 
+ * @author ssy
+ * @date 2024-06-04
+ */
+public interface ITElUserQuService 
+{
+    /**
+     * 查询答题记录
+     * 
+     * @param id 答题记录ID
+     * @return 答题记录
+     */
+    public TElUserQu selectTElUserQuById(Long id);
+
+    /**
+     * 查询答题记录列表
+     * 
+     * @param tElUserQu 答题记录
+     * @return 答题记录集合
+     */
+    public List<TElUserQu> selectTElUserQuList(TElUserQu tElUserQu);
+
+    /**
+     * 新增答题记录
+     * 
+     * @param tElUserQu 答题记录
+     * @return 结果
+     */
+    public int insertTElUserQu(TElUserQu tElUserQu);
+
+    /**
+     * 修改答题记录
+     * 
+     * @param tElUserQu 答题记录
+     * @return 结果
+     */
+    public int updateTElUserQu(TElUserQu tElUserQu);
+
+    /**
+     * 批量删除答题记录
+     * 
+     * @param ids 需要删除的答题记录ID
+     * @return 结果
+     */
+    public int deleteTElUserQuByIds(Long[] ids);
+
+    /**
+     * 删除答题记录信息
+     * 
+     * @param id 答题记录ID
+     * @return 结果
+     */
+    public int deleteTElUserQuById(Long id);
+}

+ 156 - 5
master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElPaperServiceImpl.java

@@ -6,6 +6,7 @@ import java.util.*;
 import com.ruoyi.framework.job.service.JobService;
 import com.ruoyi.project.training.elearn.domain.*;
 import com.ruoyi.project.training.elearn.mapper.*;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.project.training.elearn.service.ITElPaperService;
@@ -47,6 +48,10 @@ public class TElPaperServiceImpl implements ITElPaperService
     private TElQuAnswerMapper tElQuAnswerMapper;
     @Resource
     private TElExamRepoMapper tElExamRepoMapper;
+    @Resource
+    private TElUserExamMapper tElUserExamMapper;
+    @Resource
+    private TElUserBookMapper tElUserBookMapper;
     /**
      * 查询在线考试
      *
@@ -149,7 +154,7 @@ public class TElPaperServiceImpl implements ITElPaperService
                 item.setQuType(2);
                 List<TElQu> multiListAll = tElQuMapper.selectTElQuListByRepo(item);
                 Collections.shuffle(multiListAll);
-                List<TElQu> multiList = multiListAll.subList(0,Integer.parseInt(String.valueOf(item.getRadioCount())) );
+                List<TElQu> multiList = multiListAll.subList(0,Integer.parseInt(String.valueOf(item.getMultiCount())) );
                 for (TElQu qu: multiList
                 ) {
                     TElPaperQu paperQu = this.processPaperQu(item,qu);
@@ -161,7 +166,7 @@ public class TElPaperServiceImpl implements ITElPaperService
                 item.setQuType(3);
                 List<TElQu> judgeListAll = tElQuMapper.selectTElQuListByRepo(item);
                 Collections.shuffle(judgeListAll);
-                List<TElQu> judgeLis = judgeListAll.subList(0,Integer.parseInt(String.valueOf(item.getRadioCount())) );
+                List<TElQu> judgeLis = judgeListAll.subList(0,Integer.parseInt(String.valueOf(item.getJudgeCount())) );
                 for (TElQu qu: judgeLis
                 ) {
                     TElPaperQu paperQu = this.processPaperQu(item,qu);
@@ -200,6 +205,152 @@ public class TElPaperServiceImpl implements ITElPaperService
         return paper;
     }
 
+    @Override
+    @Transactional
+    public void fillAnswer(TElPaperQu paperQu) {
+        // 未作答
+        if(CollectionUtils.isEmpty(paperQu.getAnswers())
+                && StringUtils.isBlank(paperQu.getAnswer())){
+            return;
+        }
+        TElPaperQuAnswer param = new TElPaperQuAnswer();
+        param.setQuId(paperQu.getQuId());
+        param.setPaperId(paperQu.getPaperId());
+        List<TElPaperQuAnswer> paperQuAnswers = tElPaperQuAnswerMapper.selectTElPaperQuAnswerList(param);
+
+        //是否正确
+        long right = 1;
+
+        //更新正确答案
+        for (TElPaperQuAnswer item : paperQuAnswers) {
+
+            if (paperQu.getAnswers().contains(String.valueOf(item.getId()))) {
+                item.setChecked(1l);
+            } else {
+                item.setChecked(0l);
+            }
+            //有一个对不上就是错的
+            if (item.getIsRight()!=null && !item.getIsRight().toString().equals(item.getChecked().toString())) {
+                right = 0;
+            }
+            tElPaperQuAnswerMapper.updateTElPaperQuAnswer(item);
+        }
+            //修改为已回答
+            TElPaperQu qu = new TElPaperQu();
+            qu.setQuId(paperQu.getQuId());
+            qu.setPaperId(paperQu.getPaperId());
+            qu.setIsRight(right);
+            qu.setAnswer(paperQu.getAnswer());
+            qu.setAnswered(1l);
+            tElPaperQuMapper.updateTElPaperQuByKey(qu);
+    }
+
+    @Override
+    @Transactional
+    public void handExam(TElPaperQu paperQu) {
+        TElPaper paper = tElPaperMapper.selectTElPaperById(paperQu.getPaperId());
+
+        // 客观分
+        int objScore = tElPaperQuMapper.sumObjective(paperQu.getPaperId());
+        paper.setObjScore((long) objScore);
+        paper.setUserScore((long) objScore);
+
+        // 主观分,因为要阅卷,所以给0
+        paper.setSubjScore(0l);
+        // 待阅卷
+        if(paper.getHasSaq() == 1) {
+
+        }else {
+            // 同步保存考试成绩
+            TElUserExam record = tElUserExamMapper.selectTElUserExamByPaPer(paper);
+            long pass = objScore>=paper.getQualifyScore()?1:0;
+            if(record == null){
+                record = new TElUserExam();
+                record.setUserId(paper.getUserId());
+                record.setExamId(paper.getExamId());
+                record.setTitle(paper.getTitle());
+                record.setMaxScore((long) objScore);
+                record.setTryCount(1l);
+                record.setPassed(pass);
+                tElUserExamMapper.insertTElUserExam(record);
+            }else {
+                // 修复低分数不加入统计问题
+                record.setTryCount(record.getTryCount()+1);
+                record.setUpdatedate(new Date());
+
+                if(record.getMaxScore() < objScore){
+                    record.setMaxScore((long) objScore);
+                    record.setPassed(pass);
+                }
+                tElUserExamMapper.updateTElUserExam(record);
+            }
+            paper.setState(2l);
+        }
+        paper.setUpdateTime(new Date());
+
+        //计算考试时长
+        Calendar cl = Calendar.getInstance();
+        cl.setTimeInMillis(System.currentTimeMillis());
+        int userTime = (int)((System.currentTimeMillis() - paper.getCreatedate().getTime()) / 1000 / 60);
+        if(userTime == 0){
+            userTime = 1;
+        }
+        paper.setUserTime((long) userTime);
+        //更新试卷
+        tElPaperMapper.updateTElPaper(paper);
+
+        new Thread(() -> {
+            //错题本
+            TElPaperQu  param = new TElPaperQu();
+            param.setPaperId(paperQu.getPaperId());
+            param.setIsRight(0l);
+            List<TElPaperQu> quList = tElPaperQuMapper.selectTElPaperQuList(param);
+            for (TElPaperQu i : quList) {
+                TElUserBook quey = new TElUserBook();
+                quey.setUserId(paper.getUserId());
+                quey.setExamId(paper.getExamId());
+                quey.setQuId(i.getQuId());
+                List<TElUserBook> books = tElUserBookMapper.selectTElUserBookList(quey);
+                TElQu qu = tElQuMapper.selectTElQuById(i.getQuId());
+
+                if (books.size() == 0) {
+                    TElUserBook  book = new TElUserBook();
+                    book.setExamId(paper.getExamId());
+                    book.setUserId(paper.getUserId());
+                    book.setTitle(qu.getContent());
+                    book.setQuId(i.getQuId());
+                    book.setWrongCount(1l);
+                    book.setUpdatedate(new Date());
+                    tElUserBookMapper.insertTElUserBook(book);
+                }else {
+                    TElUserBook  book =books.get(0);
+                    book.setUpdatedate(new Date());
+                    book.setWrongCount(book.getWrongCount()+1);
+                    tElUserBookMapper.updateTElUserBook(book);
+                }
+            }
+        },"错题本").start();
+
+    }
+
+    @Override
+    public TElPaper paperResult(TElPaperQu paperQu) {
+        TElPaper paper = tElPaperMapper.selectTElPaperById(paperQu.getPaperId());
+        TElPaperQu query = new TElPaperQu();
+        query.setPaperId(paperQu.getPaperId());
+        List<TElPaperQu> quList = tElPaperQuMapper.selectTElPaperQuListByPaper(query);
+        for (TElPaperQu item : quList
+             ) {
+            TElPaperQuAnswer a = new TElPaperQuAnswer();
+            a.setPaperId(paperQu.getPaperId());
+            a.setQuId(item.getQuId());
+            List<TElPaperQuAnswer> list =  tElPaperQuAnswerMapper.selectTElPaperQuAnswerListByPaper(a);
+            item.setAnswerList(list);
+        }
+        paper.setQuList(quList);
+        return paper;
+    }
+
     /**
      * 保存试卷试题列表
      * @param paperId
@@ -263,17 +414,17 @@ public class TElPaperServiceImpl implements ITElPaperService
         paperQu.setIsRight(0l);
         paperQu.setQuType(qu.getQuType());
 
-        if ("1".equals(qu.getQuType())) {
+        if ("1".equals(qu.getQuType().toString())) {
             paperQu.setScore(repo.getRadioScore());
             paperQu.setActualScore(repo.getRadioScore());
         }
 
-        if ("2".equals(qu.getQuType())) {
+        if ("2".equals(qu.getQuType().toString())) {
             paperQu.setScore(repo.getMultiScore());
             paperQu.setActualScore(repo.getMultiScore());
         }
 
-        if ("3".equals(qu.getQuType())) {
+        if ("3".equals(qu.getQuType().toString())) {
             paperQu.setScore(repo.getJudgeScore());
             paperQu.setActualScore(repo.getJudgeScore());
         }

+ 93 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElUserBookServiceImpl.java

@@ -0,0 +1,93 @@
+package com.ruoyi.project.training.elearn.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.training.elearn.mapper.TElUserBookMapper;
+import com.ruoyi.project.training.elearn.domain.TElUserBook;
+import com.ruoyi.project.training.elearn.service.ITElUserBookService;
+
+/**
+ * 错题本Service业务层处理
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+@Service
+public class TElUserBookServiceImpl implements ITElUserBookService
+{
+    @Autowired
+    private TElUserBookMapper tElUserBookMapper;
+
+    /**
+     * 查询错题本
+     *
+     * @param id 错题本ID
+     * @return 错题本
+     */
+    @Override
+    public TElUserBook selectTElUserBookById(Long id)
+    {
+        return tElUserBookMapper.selectTElUserBookById(id);
+    }
+
+    /**
+     * 查询错题本列表
+     *
+     * @param tElUserBook 错题本
+     * @return 错题本
+     */
+    @Override
+    public List<TElUserBook> selectTElUserBookList(TElUserBook tElUserBook)
+    {
+        return tElUserBookMapper.selectTElUserBookList(tElUserBook);
+    }
+
+    /**
+     * 新增错题本
+     *
+     * @param tElUserBook 错题本
+     * @return 结果
+     */
+    @Override
+    public int insertTElUserBook(TElUserBook tElUserBook)
+    {
+        return tElUserBookMapper.insertTElUserBook(tElUserBook);
+    }
+
+    /**
+     * 修改错题本
+     *
+     * @param tElUserBook 错题本
+     * @return 结果
+     */
+    @Override
+    public int updateTElUserBook(TElUserBook tElUserBook)
+    {
+        return tElUserBookMapper.updateTElUserBook(tElUserBook);
+    }
+
+    /**
+     * 批量删除错题本
+     *
+     * @param ids 需要删除的错题本ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTElUserBookByIds(Long[] ids)
+    {
+        return tElUserBookMapper.deleteTElUserBookByIds(ids);
+    }
+
+    /**
+     * 删除错题本信息
+     *
+     * @param id 错题本ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTElUserBookById(Long id)
+    {
+        return tElUserBookMapper.deleteTElUserBookById(id);
+    }
+}

+ 93 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElUserExamServiceImpl.java

@@ -0,0 +1,93 @@
+package com.ruoyi.project.training.elearn.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.training.elearn.mapper.TElUserExamMapper;
+import com.ruoyi.project.training.elearn.domain.TElUserExam;
+import com.ruoyi.project.training.elearn.service.ITElUserExamService;
+
+/**
+ * 我的成绩Service业务层处理
+ *
+ * @author ssy
+ * @date 2024-05-31
+ */
+@Service
+public class TElUserExamServiceImpl implements ITElUserExamService
+{
+    @Autowired
+    private TElUserExamMapper tElUserExamMapper;
+
+    /**
+     * 查询我的成绩
+     *
+     * @param id 我的成绩ID
+     * @return 我的成绩
+     */
+    @Override
+    public TElUserExam selectTElUserExamById(Long id)
+    {
+        return tElUserExamMapper.selectTElUserExamById(id);
+    }
+
+    /**
+     * 查询我的成绩列表
+     *
+     * @param tElUserExam 我的成绩
+     * @return 我的成绩
+     */
+    @Override
+    public List<TElUserExam> selectTElUserExamList(TElUserExam tElUserExam)
+    {
+        return tElUserExamMapper.selectTElUserExamList(tElUserExam);
+    }
+
+    /**
+     * 新增我的成绩
+     *
+     * @param tElUserExam 我的成绩
+     * @return 结果
+     */
+    @Override
+    public int insertTElUserExam(TElUserExam tElUserExam)
+    {
+        return tElUserExamMapper.insertTElUserExam(tElUserExam);
+    }
+
+    /**
+     * 修改我的成绩
+     *
+     * @param tElUserExam 我的成绩
+     * @return 结果
+     */
+    @Override
+    public int updateTElUserExam(TElUserExam tElUserExam)
+    {
+        return tElUserExamMapper.updateTElUserExam(tElUserExam);
+    }
+
+    /**
+     * 批量删除我的成绩
+     *
+     * @param ids 需要删除的我的成绩ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTElUserExamByIds(Long[] ids)
+    {
+        return tElUserExamMapper.deleteTElUserExamByIds(ids);
+    }
+
+    /**
+     * 删除我的成绩信息
+     *
+     * @param id 我的成绩ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTElUserExamById(Long id)
+    {
+        return tElUserExamMapper.deleteTElUserExamById(id);
+    }
+}

+ 93 - 0
master/src/main/java/com/ruoyi/project/training/elearn/service/impl/TElUserQuServiceImpl.java

@@ -0,0 +1,93 @@
+package com.ruoyi.project.training.elearn.service.impl;
+
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.training.elearn.mapper.TElUserQuMapper;
+import com.ruoyi.project.training.elearn.domain.TElUserQu;
+import com.ruoyi.project.training.elearn.service.ITElUserQuService;
+
+/**
+ * 答题记录Service业务层处理
+ *
+ * @author ssy
+ * @date 2024-06-04
+ */
+@Service
+public class TElUserQuServiceImpl implements ITElUserQuService
+{
+    @Autowired
+    private TElUserQuMapper tElUserQuMapper;
+
+    /**
+     * 查询答题记录
+     *
+     * @param id 答题记录ID
+     * @return 答题记录
+     */
+    @Override
+    public TElUserQu selectTElUserQuById(Long id)
+    {
+        return tElUserQuMapper.selectTElUserQuById(id);
+    }
+
+    /**
+     * 查询答题记录列表
+     *
+     * @param tElUserQu 答题记录
+     * @return 答题记录
+     */
+    @Override
+    public List<TElUserQu> selectTElUserQuList(TElUserQu tElUserQu)
+    {
+        return tElUserQuMapper.selectTElUserQuList(tElUserQu);
+    }
+
+    /**
+     * 新增答题记录
+     *
+     * @param tElUserQu 答题记录
+     * @return 结果
+     */
+    @Override
+    public int insertTElUserQu(TElUserQu tElUserQu)
+    {
+        return tElUserQuMapper.insertTElUserQu(tElUserQu);
+    }
+
+    /**
+     * 修改答题记录
+     *
+     * @param tElUserQu 答题记录
+     * @return 结果
+     */
+    @Override
+    public int updateTElUserQu(TElUserQu tElUserQu)
+    {
+        return tElUserQuMapper.updateTElUserQu(tElUserQu);
+    }
+
+    /**
+     * 批量删除答题记录
+     *
+     * @param ids 需要删除的答题记录ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTElUserQuByIds(Long[] ids)
+    {
+        return tElUserQuMapper.deleteTElUserQuByIds(ids);
+    }
+
+    /**
+     * 删除答题记录信息
+     *
+     * @param id 答题记录ID
+     * @return 结果
+     */
+    @Override
+    public int deleteTElUserQuById(Long id)
+    {
+        return tElUserQuMapper.deleteTElUserQuById(id);
+    }
+}

+ 65 - 0
master/src/main/java/com/ruoyi/project/training/mapper/TElUserQuMapper.java

@@ -0,0 +1,65 @@
+package com.ruoyi.project.training.elearn.mapper;
+
+import java.util.List;
+import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
+import com.ruoyi.project.training.elearn.domain.TElUserQu;
+
+/**
+ * 答题记录Mapper接口
+ *
+ * @author ssy
+ * @date 2024-06-04
+ */
+public interface TElUserQuMapper
+{
+    /**
+     * 查询答题记录
+     *
+     * @param id 答题记录ID
+     * @return 答题记录
+     */
+    public TElUserQu selectTElUserQuById(Long id);
+
+    /**
+     * 查询答题记录列表
+     *
+     * @param tElUserQu 答题记录
+     * @return 答题记录集合
+     */
+    @DataScope(deptAlias = "d")
+    public List<TElUserQu> selectTElUserQuList(TElUserQu tElUserQu);
+
+    /**
+     * 新增答题记录
+     *
+     * @param tElUserQu 答题记录
+     * @return 结果
+     */
+    public int insertTElUserQu(TElUserQu tElUserQu);
+
+    /**
+     * 修改答题记录
+     *
+     * @param tElUserQu 答题记录
+     * @return 结果
+     */
+    public int updateTElUserQu(TElUserQu tElUserQu);
+
+    /**
+     * 删除答题记录
+     *
+     * @param id 答题记录ID
+     * @return 结果
+     */
+    public int deleteTElUserQuById(Long id);
+
+    /**
+     * 批量删除答题记录
+     *
+     * @param ids 需要删除的数据ID
+     * @return 结果
+     */
+    public int deleteTElUserQuByIds(Long[] ids);
+
+    TElUserQu selectTElUserQuLast(TElUserQu tElUserQu);
+}

+ 36 - 9
master/src/main/resources/mybatis/training/elearn/TElPaperQuAnswerMapper.xml

@@ -29,10 +29,37 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="checked != null "> and checked = #{checked}</if>
             <if test="sort != null "> and sort = #{sort}</if>
             <if test="abc != null  and abc != ''"> and abc = #{abc}</if>
-            and d.del_flag = 0
         </where>
-        <!-- 数据范围过滤 -->
-        ${params.dataScope}
+    </select>
+
+    <select id="selectTElPaperQuAnswerListByExam" parameterType="TElPaperQuAnswer" resultMap="TElPaperQuAnswerResult">
+        select d.id, d.paper_id, d.qu_id, d.answer_id, d.is_right, d.checked, d.sort, d.abc,qa.content,qa.image  from t_el_paper_qu_answer d
+        LEFT JOIN t_el_qu_answer qa ON d.answer_id = qa.answer_id
+        <where>
+            <if test="paperId != null "> and d.paper_id = #{paperId}</if>
+            <if test="quId != null "> and d.qu_id = #{quId}</if>
+            <if test="answerId != null "> and d.answer_id = #{answerId}</if>
+            <if test="isRight != null "> and d.is_right = #{isRight}</if>
+            <if test="checked != null "> and d.checked = #{checked}</if>
+            <if test="sort != null "> and d.sort = #{sort}</if>
+            <if test="abc != null  and abc != ''"> and d.abc = #{abc}</if>
+        </where>
+        ORDER BY d.sort ASC
+    </select>
+
+    <select id="selectTElPaperQuAnswerListByPaper" parameterType="TElPaperQuAnswer" resultMap="TElPaperQuAnswerResult">
+        select d.id, d.paper_id, d.qu_id, d.answer_id, d.is_right, d.checked, d.sort, d.abc,qa.content,qa.image  from t_el_paper_qu_answer d
+        LEFT JOIN t_el_qu_answer qa ON d.answer_id = qa.answer_id
+        <where>
+            <if test="paperId != null "> and d.paper_id = #{paperId}</if>
+            <if test="quId != null "> and d.qu_id = #{quId}</if>
+            <if test="answerId != null "> and d.answer_id = #{answerId}</if>
+            <if test="isRight != null "> and d.is_right = #{isRight}</if>
+            <if test="checked != null "> and d.checked = #{checked}</if>
+            <if test="sort != null "> and d.sort = #{sort}</if>
+            <if test="abc != null  and abc != ''"> and d.abc = #{abc}</if>
+        </where>
+        ORDER BY d.sort ASC
     </select>
 
     <select id="selectTElPaperQuAnswerById" parameterType="Long" resultMap="TElPaperQuAnswerResult">
@@ -81,15 +108,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </update>
 
-    <update id="deleteTElPaperQuAnswerById" parameterType="Long">
-        update t_el_paper_qu_answer set del_flag = 2 where id = #{id}
-    </update>
+    <delete id="deleteTElPaperQuAnswerById" parameterType="Long">
+        delete from t_el_paper_qu_answer  where id = #{id}
+    </delete>
 
-    <update id="deleteTElPaperQuAnswerByIds" parameterType="String">
-        update t_el_paper_qu_answer set del_flag = 2 where id in
+    <delete id="deleteTElPaperQuAnswerByIds" parameterType="String">
+        delete from t_el_paper_qu_answer  where id in
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
-    </update>
+    </delete>
 
 </mapper>

+ 55 - 8
master/src/main/resources/mybatis/training/elearn/TElPaperQuMapper.xml

@@ -33,17 +33,38 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="score != null "> and score = #{score}</if>
             <if test="actualScore != null "> and actual_score = #{actualScore}</if>
             <if test="isRight != null "> and is_right = #{isRight}</if>
-            and d.del_flag = 0
         </where>
-        <!-- 数据范围过滤 -->
-        ${params.dataScope}
     </select>
 
+    <select id="selectTElPaperQuListByPaper" parameterType="TElPaperQu" resultMap="TElPaperQuResult">
+        select d.id, d.paper_id, d.qu_id, d.qu_type, d.answered, d.answer, d.sort, d.score, d.actual_score, d.is_right,eq.content,eq.image from t_el_paper_qu d
+        LEFT JOIN t_el_qu eq ON d.qu_id = eq.qu_id
+        <where>
+            <if test="paperId != null "> and paper_id = #{paperId}</if>
+            <if test="quId != null "> and qu_id = #{quId}</if>
+            <if test="quType != null "> and qu_type = #{quType}</if>
+            <if test="answered != null "> and answered = #{answered}</if>
+            <if test="answer != null  and answer != ''"> and answer = #{answer}</if>
+            <if test="sort != null "> and sort = #{sort}</if>
+            <if test="score != null "> and score = #{score}</if>
+            <if test="actualScore != null "> and actual_score = #{actualScore}</if>
+            <if test="isRight != null "> and is_right = #{isRight}</if>
+        </where>
+    </select>
+
+
+
     <select id="selectTElPaperQuById" parameterType="Long" resultMap="TElPaperQuResult">
         <include refid="selectTElPaperQuVo"/>
         where id = #{id}
     </select>
 
+    <select id="selectTElPaperQuByPaperIdAndQuId" parameterType="TElPaperQu" resultMap="TElPaperQuResult">
+        <include refid="selectTElPaperQuVo"/>
+        where paper_id = #{paperId} and qu_id = #{quId} and ROWNUM = 1
+    </select>
+
+
     <insert id="insertTElPaperQu" parameterType="TElPaperQu">
         <selectKey keyProperty="id" resultType="long" order="BEFORE">
             SELECT seq_t_el_paper_qu.NEXTVAL as id FROM DUAL
@@ -91,15 +112,41 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </update>
 
-    <update id="deleteTElPaperQuById" parameterType="Long">
-        update t_el_paper_qu set del_flag = 2 where id = #{id}
+    <update id="updateTElPaperQuByKey" parameterType="TElPaperQu">
+        update t_el_paper_qu
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="paperId != null">paper_id = #{paperId},</if>
+            <if test="quId != null">qu_id = #{quId},</if>
+            <if test="quType != null">qu_type = #{quType},</if>
+            <if test="answered != null">answered = #{answered},</if>
+            <if test="answer != null">answer = #{answer},</if>
+            <if test="sort != null">sort = #{sort},</if>
+            <if test="score != null">score = #{score},</if>
+            <if test="actualScore != null">actual_score = #{actualScore},</if>
+            <if test="isRight != null">is_right = #{isRight},</if>
+        </trim>
+        where paper_id = #{paperId} and qu_id = #{quId}
     </update>
 
-    <update id="deleteTElPaperQuByIds" parameterType="String">
-        update t_el_paper_qu set del_flag = 2 where id in
+
+
+    <delete id="deleteTElPaperQuById" parameterType="Long">
+        delete from t_el_paper_qu where id = #{id}
+    </delete>
+
+    <delete id="deleteTElPaperQuByIds" parameterType="String">
+        delete from t_el_paper_qu  where id in
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
-    </update>
+    </delete>
 
+
+    <select id="sumObjective" resultType="int">
+        SELECT NVL(SUM(actual_score), 0) as total
+        FROM t_el_paper_qu
+        WHERE paper_id = #{paperId}
+        AND is_right = 1
+        AND  4 > qu_type
+    </select>
 </mapper>

+ 17 - 12
master/src/main/resources/mybatis/training/elearn/TElQuMapper.xml

@@ -23,22 +23,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <sql id="selectTElQuVo">
         select d.qu_id, d.qu_type, d.qlevel, d.content, d.image, d.analysis, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.dept_id, d.remarks from t_el_qu d
+        LEFT JOIN t_el_qu_repo po ON d.qu_id=po.qu_id
     </sql>
 
     <select id="selectTElQuList" parameterType="TElQu" resultMap="TElQuResult">
         <include refid="selectTElQuVo"/>
         <where>
-            <if test="quType != null "> and qu_type = #{quType}</if>
-            <if test="qlevel != null "> and qlevel = #{qlevel}</if>
-            <if test="content != null  and content != ''"> and content = #{content}</if>
-            <if test="image != null  and image != ''"> and image = #{image}</if>
-            <if test="analysis != null  and analysis != ''"> and analysis = #{analysis}</if>
-            <if test="createrCode != null  and createrCode != ''"> and creater_code = #{createrCode}</if>
-            <if test="createdate != null "> and createdate = #{createdate}</if>
-            <if test="updaterCode != null  and updaterCode != ''"> and updater_code = #{updaterCode}</if>
-            <if test="updatedate != null "> and updatedate = #{updatedate}</if>
-            <if test="deptId != null "> and dept_id = #{deptId}</if>
-            <if test="remarks != null  and remarks != ''"> and remarks = #{remarks}</if>
+            <if test="repoIds!=null and repoIds.size()>0">
+                AND po.repo_id IN
+                <foreach collection="repoIds" open="(" close=")" separator="," item="repoId">#{repoId}</foreach>
+            </if>
+            <if test="quType != null "> and d.qu_type = #{quType}</if>
+            <if test="qlevel != null "> and d.qlevel = #{qlevel}</if>
+            <if test="content != null  and content != ''"> and cd.ontent = #{content}</if>
+            <if test="image != null  and image != ''"> and d.image = #{image}</if>
+            <if test="analysis != null  and analysis != ''"> and d.analysis = #{analysis}</if>
+            <if test="createrCode != null  and createrCode != ''"> and d.creater_code = #{createrCode}</if>
+            <if test="createdate != null "> and d.createdate = #{createdate}</if>
+            <if test="updaterCode != null  and updaterCode != ''"> and d.updater_code = #{updaterCode}</if>
+            <if test="updatedate != null "> and d.updatedate = #{updatedate}</if>
+            <if test="deptId != null "> and d.dept_id = #{deptId}</if>
+            <if test="remarks != null  and remarks != ''"> and d.remarks = #{remarks}</if>
             and d.del_flag = 0
         </where>
         <!-- 数据范围过滤 -->
@@ -53,7 +58,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectTElQuById" parameterType="Long" resultMap="TElQuResult">
         <include refid="selectTElQuVo"/>
-        where qu_id = #{quId}
+        where d.qu_id = #{quId}
     </select>
 
     <insert id="insertTElQu" parameterType="TElQu">

+ 30 - 15
master/src/main/resources/mybatis/training/elearn/TElQuRepoMapper.xml

@@ -1,28 +1,29 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper
-PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.project.training.elearn.mapper.TElQuRepoMapper">
 
     <resultMap type="TElQuRepo" id="TElQuRepoResult">
-        <result property="id"    column="id"    />
-        <result property="quId"    column="qu_id"    />
-        <result property="repoId"    column="repo_id"    />
-        <result property="quType"    column="qu_type"    />
-        <result property="sort"    column="sort"    />
+        <result property="id" column="id"/>
+        <result property="quId" column="qu_id"/>
+        <result property="repoId" column="repo_id"/>
+        <result property="quType" column="qu_type"/>
+        <result property="sort" column="sort"/>
     </resultMap>
 
     <sql id="selectTElQuRepoVo">
-        select d.id, d.qu_id, d.repo_id, d.qu_type, d.sort from t_el_qu_repo d
+        select d.id, d.qu_id, d.repo_id, d.qu_type, d.sort
+        from t_el_qu_repo d
     </sql>
 
     <select id="selectTElQuRepoList" parameterType="TElQuRepo" resultMap="TElQuRepoResult">
         <include refid="selectTElQuRepoVo"/>
         <where>
-            <if test="quId != null "> and qu_id = #{quId}</if>
-            <if test="repoId != null "> and repo_id = #{repoId}</if>
-            <if test="quType != null "> and qu_type = #{quType}</if>
-            <if test="sort != null "> and sort = #{sort}</if>
+            <if test="quId != null ">and qu_id = #{quId}</if>
+            <if test="repoId != null ">and repo_id = #{repoId}</if>
+            <if test="quType != null ">and qu_type = #{quType}</if>
+            <if test="sort != null ">and sort = #{sort}</if>
         </where>
         <!-- 数据范围过滤 -->
     </select>
@@ -32,6 +33,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </select>
 
+
+    <select id="selectTElQuRepoLast" parameterType="TElUserQu" resultMap="TElQuRepoResult">
+        select * from (select d.id, d.qu_id, d.repo_id, d.qu_type, d.sort from t_el_qu_repo d
+        <where>
+            <if test="repoId != null ">and repo_id = #{repoId}</if>
+            <if test="quId != null ">and #{quId} > qu_id</if>
+
+        </where>
+        order by d.qu_id desc)
+        where rownum = 1
+    </select>
+
     <insert id="insertTElQuRepo" parameterType="TElQuRepo">
         <selectKey keyProperty="id" resultType="long" order="BEFORE">
             SELECT seq_t_el_qu_repo.NEXTVAL as id FROM DUAL
@@ -43,14 +56,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="repoId != null">repo_id,</if>
             <if test="quType != null">qu_type,</if>
             <if test="sort != null">sort,</if>
-         </trim>
+        </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
             <if test="quId != null">#{quId},</if>
             <if test="repoId != null">#{repoId},</if>
             <if test="quType != null">#{quType},</if>
             <if test="sort != null">#{sort},</if>
-         </trim>
+        </trim>
     </insert>
 
     <update id="updateTElQuRepo" parameterType="TElQuRepo">
@@ -65,7 +78,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </update>
 
     <delete id="deleteTElQuRepoById" parameterType="Long">
-        delete from t_el_qu_repo where id = #{id}
+        delete
+        from t_el_qu_repo
+        where id = #{id}
     </delete>
 
     <delete id="deleteTElQuRepoByIds" parameterType="String">

+ 114 - 0
master/src/main/resources/mybatis/training/elearn/TElUserBookMapper.xml

@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.project.training.elearn.mapper.TElUserBookMapper">
+
+    <resultMap type="TElUserBook" id="TElUserBookResult">
+        <result property="id"    column="id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="examId"    column="exam_id"    />
+        <result property="quId"    column="qu_id"    />
+        <result property="wrongCount"    column="wrong_count"    />
+        <result property="title"    column="title"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createrCode"    column="creater_code"    />
+        <result property="createdate"    column="createdate"    />
+        <result property="updaterCode"    column="updater_code"    />
+        <result property="updatedate"    column="updatedate"    />
+        <result property="sort"    column="sort"    />
+        <result property="deptName" column="dept_name" />
+    </resultMap>
+
+    <sql id="selectTElUserBookVo">
+        select d.id, d.user_id, d.exam_id, d.qu_id, d.wrong_count, d.title, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.sort  from t_el_user_book d
+    </sql>
+
+    <select id="selectTElUserBookList" parameterType="TElUserBook" resultMap="TElUserBookResult">
+        <include refid="selectTElUserBookVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="examId != null "> and exam_id = #{examId}</if>
+            <if test="quId != null "> and qu_id = #{quId}</if>
+            <if test="wrongCount != null "> and wrong_count = #{wrongCount}</if>
+            <if test="title != null  and title != ''"> and title = #{title}</if>
+            <if test="createrCode != null  and createrCode != ''"> and creater_code = #{createrCode}</if>
+            <if test="createdate != null "> and createdate = #{createdate}</if>
+            <if test="updaterCode != null  and updaterCode != ''"> and updater_code = #{updaterCode}</if>
+            <if test="updatedate != null "> and updatedate = #{updatedate}</if>
+            <if test="sort != null "> and sort = #{sort}</if>
+            and d.del_flag = 0
+        </where>
+        <!-- 数据范围过滤 -->
+    </select>
+
+    <select id="selectTElUserBookById" parameterType="Long" resultMap="TElUserBookResult">
+        <include refid="selectTElUserBookVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertTElUserBook" parameterType="TElUserBook">
+        <selectKey keyProperty="id" resultType="long" order="BEFORE">
+            SELECT seq_t_el_user_book.NEXTVAL as id FROM DUAL
+        </selectKey>
+        insert into t_el_user_book
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="examId != null">exam_id,</if>
+            <if test="quId != null">qu_id,</if>
+            <if test="wrongCount != null">wrong_count,</if>
+            <if test="title != null">title,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createrCode != null">creater_code,</if>
+            <if test="createdate != null">createdate,</if>
+            <if test="updaterCode != null">updater_code,</if>
+            <if test="updatedate != null">updatedate,</if>
+            <if test="sort != null">sort,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="examId != null">#{examId},</if>
+            <if test="quId != null">#{quId},</if>
+            <if test="wrongCount != null">#{wrongCount},</if>
+            <if test="title != null">#{title},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createrCode != null">#{createrCode},</if>
+            <if test="createdate != null">#{createdate},</if>
+            <if test="updaterCode != null">#{updaterCode},</if>
+            <if test="updatedate != null">#{updatedate},</if>
+            <if test="sort != null">#{sort},</if>
+         </trim>
+    </insert>
+
+    <update id="updateTElUserBook" parameterType="TElUserBook">
+        update t_el_user_book
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="examId != null">exam_id = #{examId},</if>
+            <if test="quId != null">qu_id = #{quId},</if>
+            <if test="wrongCount != null">wrong_count = #{wrongCount},</if>
+            <if test="title != null">title = #{title},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createrCode != null">creater_code = #{createrCode},</if>
+            <if test="createdate != null">createdate = #{createdate},</if>
+            <if test="updaterCode != null">updater_code = #{updaterCode},</if>
+            <if test="updatedate != null">updatedate = #{updatedate},</if>
+            <if test="sort != null">sort = #{sort},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteTElUserBookById" parameterType="Long">
+        update t_el_user_book set del_flag = 2 where id = #{id}
+    </update>
+
+    <update id="deleteTElUserBookByIds" parameterType="String">
+        update t_el_user_book set del_flag = 2 where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+</mapper>

+ 127 - 0
master/src/main/resources/mybatis/training/elearn/TElUserExamMapper.xml

@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.project.training.elearn.mapper.TElUserExamMapper">
+
+    <resultMap type="TElUserExam" id="TElUserExamResult">
+        <result property="id"    column="id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="examId"    column="exam_id"    />
+        <result property="tryCount"    column="try_count"    />
+        <result property="maxScore"    column="max_score"    />
+        <result property="passed"    column="passed"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createrCode"    column="creater_code"    />
+        <result property="createdate"    column="createdate"    />
+        <result property="updaterCode"    column="updater_code"    />
+        <result property="updatedate"    column="updatedate"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="title"    column="title"    />
+        <result property="deptName" column="dept_name" />
+    </resultMap>
+
+    <sql id="selectTElUserExamVo">
+        select d.id,d.title, d.user_id, d.exam_id, d.try_count, d.max_score, d.passed, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.dept_id from t_el_user_exam d
+    </sql>
+
+    <select id="selectTElUserExamList" parameterType="TElUserExam" resultMap="TElUserExamResult">
+        <include refid="selectTElUserExamVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="examId != null "> and exam_id = #{examId}</if>
+            <if test="tryCount != null "> and try_count = #{tryCount}</if>
+            <if test="maxScore != null "> and max_score = #{maxScore}</if>
+            <if test="title != null  and title != ''"> and title like concat(concat('%', #{title}), '%')</if>
+
+            <if test="passed != null "> and passed = #{passed}</if>
+            <if test="createrCode != null  and createrCode != ''"> and creater_code = #{createrCode}</if>
+            <if test="createdate != null "> and createdate = #{createdate}</if>
+            <if test="updaterCode != null  and updaterCode != ''"> and updater_code = #{updaterCode}</if>
+            <if test="updatedate != null "> and updatedate = #{updatedate}</if>
+            <if test="deptId != null "> and dept_id = #{deptId}</if>
+            and d.del_flag = 0
+        </where>
+        <!-- 数据范围过滤 -->
+        order by d.updatedate desc
+    </select>
+
+    <select id="selectTElUserExamById" parameterType="Long" resultMap="TElUserExamResult">
+        <include refid="selectTElUserExamVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="selectTElUserExamByPaPer" parameterType="TElPaper" resultMap="TElUserExamResult">
+        <include refid="selectTElUserExamVo"/>
+        where exam_id = #{examId} and user_id = #{userId}
+    </select>
+
+
+    <insert id="insertTElUserExam" parameterType="TElUserExam">
+        <selectKey keyProperty="id" resultType="long" order="BEFORE">
+            SELECT seq_t_el_user_exam.NEXTVAL as id FROM DUAL
+        </selectKey>
+        insert into t_el_user_exam
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="examId != null">exam_id,</if>
+            <if test="tryCount != null">try_count,</if>
+            <if test="maxScore != null">max_score,</if>
+            <if test="passed != null">passed,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createrCode != null">creater_code,</if>
+            <if test="createdate != null">createdate,</if>
+            <if test="updaterCode != null">updater_code,</if>
+            <if test="updatedate != null">updatedate,</if>
+            <if test="deptId != null">dept_id,</if>
+            <if test="title != null">title,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="examId != null">#{examId},</if>
+            <if test="tryCount != null">#{tryCount},</if>
+            <if test="maxScore != null">#{maxScore},</if>
+            <if test="passed != null">#{passed},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createrCode != null">#{createrCode},</if>
+            <if test="createdate != null">#{createdate},</if>
+            <if test="updaterCode != null">#{updaterCode},</if>
+            <if test="updatedate != null">#{updatedate},</if>
+            <if test="deptId != null">#{deptId},</if>
+            <if test="title != null">#{title},</if>
+         </trim>
+    </insert>
+
+    <update id="updateTElUserExam" parameterType="TElUserExam">
+        update t_el_user_exam
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="examId != null">exam_id = #{examId},</if>
+            <if test="tryCount != null">try_count = #{tryCount},</if>
+            <if test="maxScore != null">max_score = #{maxScore},</if>
+            <if test="passed != null">passed = #{passed},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createrCode != null">creater_code = #{createrCode},</if>
+            <if test="createdate != null">createdate = #{createdate},</if>
+            <if test="updaterCode != null">updater_code = #{updaterCode},</if>
+            <if test="updatedate != null">updatedate = #{updatedate},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
+            <if test="title != null">title = #{title},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteTElUserExamById" parameterType="Long">
+        update t_el_user_exam set del_flag = 2 where id = #{id}
+    </update>
+
+    <update id="deleteTElUserExamByIds" parameterType="String">
+        update t_el_user_exam set del_flag = 2 where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+</mapper>

+ 128 - 0
master/src/main/resources/mybatis/training/elearn/TElUserQuMapper.xml

@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.project.training.elearn.mapper.TElUserQuMapper">
+
+    <resultMap type="TElUserQu" id="TElUserQuResult">
+        <result property="id"    column="id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="examId"    column="exam_id"    />
+        <result property="quId"    column="qu_id"    />
+        <result property="repoId"    column="repo_id"    />
+        <result property="title"    column="title"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createrCode"    column="creater_code"    />
+        <result property="createdate"    column="createdate"    />
+        <result property="updaterCode"    column="updater_code"    />
+        <result property="updatedate"    column="updatedate"    />
+        <result property="sort"    column="sort"    />
+    </resultMap>
+
+    <sql id="selectTElUserQuVo">
+        select d.id, d.user_id, d.exam_id, d.qu_id, d.repo_id, d.title, d.del_flag, d.creater_code, d.createdate, d.updater_code, d.updatedate, d.sort  from t_el_user_qu d
+    </sql>
+
+    <select id="selectTElUserQuList" parameterType="TElUserQu" resultMap="TElUserQuResult">
+        <include refid="selectTElUserQuVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="examId != null "> and exam_id = #{examId}</if>
+            <if test="quId != null "> and qu_id = #{quId}</if>
+            <if test="repoId != null "> and repo_id = #{repoId}</if>
+            <if test="title != null  and title != ''"> and title = #{title}</if>
+            <if test="createrCode != null  and createrCode != ''"> and creater_code = #{createrCode}</if>
+            <if test="createdate != null "> and createdate = #{createdate}</if>
+            <if test="updaterCode != null  and updaterCode != ''"> and updater_code = #{updaterCode}</if>
+            <if test="updatedate != null "> and updatedate = #{updatedate}</if>
+            <if test="sort != null "> and sort = #{sort}</if>
+            and d.del_flag = 0
+        </where>
+        <!-- 数据范围过滤 -->
+    </select>
+
+    <select id="selectTElUserQuById" parameterType="Long" resultMap="TElUserQuResult">
+        <include refid="selectTElUserQuVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="selectTElUserQuLast" parameterType="TElUserQu" resultMap="TElUserQuResult">
+        <include refid="selectTElUserQuVo"/>
+        <where>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="examId != null "> and exam_id = #{examId}</if>
+            <if test="quId != null "> and qu_id = #{quId}</if>
+            <if test="repoId != null "> and repo_id = #{repoId}</if>
+            <if test="title != null  and title != ''"> and title = #{title}</if>
+            <if test="sort != null "> and sort = #{sort}</if>
+            and d.del_flag = 0 and  rownum = 1
+        </where>
+        order by d.id desc
+    </select>
+
+
+    <insert id="insertTElUserQu" parameterType="TElUserQu">
+        <selectKey keyProperty="id" resultType="long" order="BEFORE">
+            SELECT seq_t_el_user_qu.NEXTVAL as id FROM DUAL
+        </selectKey>
+        insert into t_el_user_qu
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="examId != null">exam_id,</if>
+            <if test="quId != null">qu_id,</if>
+            <if test="repoId != null">repo_id,</if>
+            <if test="title != null">title,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createrCode != null">creater_code,</if>
+            <if test="createdate != null">createdate,</if>
+            <if test="updaterCode != null">updater_code,</if>
+            <if test="updatedate != null">updatedate,</if>
+            <if test="sort != null">sort,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="examId != null">#{examId},</if>
+            <if test="quId != null">#{quId},</if>
+            <if test="repoId != null">#{repoId},</if>
+            <if test="title != null">#{title},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createrCode != null">#{createrCode},</if>
+            <if test="createdate != null">#{createdate},</if>
+            <if test="updaterCode != null">#{updaterCode},</if>
+            <if test="updatedate != null">#{updatedate},</if>
+            <if test="sort != null">#{sort},</if>
+         </trim>
+    </insert>
+
+    <update id="updateTElUserQu" parameterType="TElUserQu">
+        update t_el_user_qu
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="examId != null">exam_id = #{examId},</if>
+            <if test="quId != null">qu_id = #{quId},</if>
+            <if test="repoId != null">repo_id = #{repoId},</if>
+            <if test="title != null">title = #{title},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createrCode != null">creater_code = #{createrCode},</if>
+            <if test="createdate != null">createdate = #{createdate},</if>
+            <if test="updaterCode != null">updater_code = #{updaterCode},</if>
+            <if test="updatedate != null">updatedate = #{updatedate},</if>
+            <if test="sort != null">sort = #{sort},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deleteTElUserQuById" parameterType="Long">
+        update t_el_user_qu set del_flag = 2 where id = #{id}
+    </update>
+
+    <update id="deleteTElUserQuByIds" parameterType="String">
+        update t_el_user_qu set del_flag = 2 where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+</mapper>

+ 47 - 0
ui/src/api/training/elearn/paper.js

@@ -9,6 +9,53 @@ export function listPaper(query) {
   })
 }
 
+// 查询试卷
+export function paperDetail(query) {
+  return request({
+    url: '/elearn/paper/paperDetail',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询题目
+export function quDetail(query) {
+  return request({
+    url: '/elearn/paper/quDetail',
+    method: 'get',
+    params: query
+  })
+}
+
+// 交卷
+export function handExam(query) {
+  return request({
+    url: '/elearn/paper/handExam',
+    method: 'get',
+    params: query
+  })
+}
+// 试卷详情
+export function paperResult(query) {
+  return request({
+    url: '/elearn/paper/paperResult',
+    method: 'get',
+    params: query
+  })
+}
+
+
+
+// 填充答案
+export function fillAnswer(data) {
+  return request({
+    url: '/elearn/paper/fillAnswer',
+    method: 'post',
+    data: data
+  })
+}
+
+
 // 查询在线考试详细
 export function getPaper(paperId) {
   return request({

+ 3 - 3
ui/src/api/training/elearn/qu.js

@@ -3,9 +3,9 @@ import request from '@/utils/request'
 // 查询试题列表
 export function listQu(query) {
   return request({
-    url: '/elearn/qu/list',
-    method: 'get',
-    params: query
+    url: '/elearn/qu/list?pageNum=' + query.pageNum + '&pageSize= '+ query.pageSize,
+    method: 'post',
+    data: query
   })
 }
 

+ 4 - 0
ui/src/api/training/elearn/repo.js

@@ -26,6 +26,10 @@ export function addRepo(data) {
   })
 }
 
+
+
+
+
 // 修改题库
 export function updateRepo(data) {
   return request({

+ 53 - 0
ui/src/api/training/elearn/userBook.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询错题本列表
+export function listUserBook(query) {
+  return request({
+    url: '/elearn/userBook/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询错题本详细
+export function getUserBook(id) {
+  return request({
+    url: '/elearn/userBook/' + id,
+    method: 'get'
+  })
+}
+
+// 新增错题本
+export function addUserBook(data) {
+  return request({
+    url: '/elearn/userBook',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改错题本
+export function updateUserBook(data) {
+  return request({
+    url: '/elearn/userBook',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除错题本
+export function delUserBook(id) {
+  return request({
+    url: '/elearn/userBook/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出错题本
+export function exportUserBook(query) {
+  return request({
+    url: '/elearn/userBook/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
ui/src/api/training/elearn/userExam.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询我的成绩列表
+export function listUserExam(query) {
+  return request({
+    url: '/elearn/userExam/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询我的成绩详细
+export function getUserExam(id) {
+  return request({
+    url: '/elearn/userExam/' + id,
+    method: 'get'
+  })
+}
+
+// 新增我的成绩
+export function addUserExam(data) {
+  return request({
+    url: '/elearn/userExam',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改我的成绩
+export function updateUserExam(data) {
+  return request({
+    url: '/elearn/userExam',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除我的成绩
+export function delUserExam(id) {
+  return request({
+    url: '/elearn/userExam/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出我的成绩
+export function exportUserExam(query) {
+  return request({
+    url: '/elearn/userExam/export',
+    method: 'get',
+    params: query
+  })
+}

+ 62 - 0
ui/src/api/training/elearn/userQu.js

@@ -0,0 +1,62 @@
+import request from '@/utils/request'
+
+// 查询答题记录列表
+export function listUserQu(query) {
+  return request({
+    url: '/elearn/userQu/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 题库练习
+export function nextQu(data) {
+  return request({
+    url: '/elearn/userQu/nextQu',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询答题记录详细
+export function getUserQu(id) {
+  return request({
+    url: '/elearn/userQu/' + id,
+    method: 'get'
+  })
+}
+
+// 新增答题记录
+export function addUserQu(data) {
+  return request({
+    url: '/elearn/userQu',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改答题记录
+export function updateUserQu(data) {
+  return request({
+    url: '/elearn/userQu',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除答题记录
+export function delUserQu(id) {
+  return request({
+    url: '/elearn/userQu/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出答题记录
+export function exportUserQu(query) {
+  return request({
+    url: '/elearn/userQu/export',
+    method: 'get',
+    params: query
+  })
+}

+ 20 - 0
ui/src/router/index.js

@@ -597,6 +597,26 @@ export const constantRoutes = [
         name: 'paper',
         meta: { title: '在线考试', noCache: true },
         hidden: true
+      },{
+        path: 'exam/start/:paperId',
+        component: () => import('@/views/training/elearn/paper/exam'),
+        name: 'StartExam',
+        meta: { title: '开始考试', noCache: true },
+        hidden: true
+      },
+      {
+        path: 'exam/result/:paperId',
+        component: () => import('@/views/training/elearn/paper/result'),
+        name: 'ShowExam',
+        meta: { title: '考试结果', noCache: true },
+        hidden: true
+      },
+      {
+        path: 'repo/training/:repoId',
+        component: () => import('@/views/training/elearn/userQu/train'),
+        name: 'RepoTraining',
+        meta: { title: '题库训练', noCache: true },
+        hidden: true
       },
     ]
   },

+ 1 - 1
ui/src/views/training/elearn/exam/index.vue

@@ -510,7 +510,7 @@ export default {
         examStartTime: null,
         examEndTime: null,
         totalScore: null,
-        totalTime: null,
+        totalTime: 30,
         qualifyScore: null,
         delFlag: null,
         createrCode: null,

+ 63 - 0
ui/src/views/training/elearn/paper/ExamTimer/index.vue

@@ -0,0 +1,63 @@
+<template>
+
+  <span style="color: #ff0000; font-weight: 700">{{ min }}分钟{{ sec }}秒</span>
+
+</template>
+
+<script>
+
+export default {
+  name: 'ExamTimer',
+  props: {
+    value: Number
+  },
+
+  data() {
+    return {
+      leftSeconds: 0,
+      min: '00',
+      sec: '00'
+    }
+  },
+
+  watch: {
+    value: {
+      handler() {
+        this.leftSeconds = this.value
+        this.countdown()
+      }
+    }
+  },
+
+  created() {
+    this.leftSeconds = this.value
+  },
+
+  methods: {
+
+    // 进行倒计时
+    countdown() {
+      // 倒计时结束了
+      if (this.leftSeconds < 0) {
+        this.$emit('timeout')
+        return
+      }
+
+      // 时
+      const min = parseInt(this.leftSeconds / 60)
+      const sec = parseInt(this.leftSeconds % 60)
+
+      this.min = min > 9 ? min : '0' + min
+      this.sec = sec > 9 ? sec : '0' + sec
+      this.leftSeconds -= 1
+
+      const that = this
+      setTimeout(function() {
+        that.countdown()
+      }, 1000)
+    }
+
+  }
+}
+</script>
+

+ 442 - 0
ui/src/views/training/elearn/paper/exam.vue

@@ -0,0 +1,442 @@
+<template>
+
+  <div class="app-container">
+
+    <el-row :gutter="24">
+
+      <el-col :span="24">
+        <el-card style="margin-bottom: 10px">
+
+          距离考试结束还有:
+          <exam-timer v-model="paperData.leftSeconds" @timeout="doHandler()" />
+
+          <el-button :loading="loading" style="float: right; margin-top: -10px" type="primary" icon="el-icon-plus" @click="handHandExam()">
+            {{ handleText }}
+          </el-button>
+
+        </el-card>
+      </el-col>
+
+      <el-col :span="5" :xs="24" style="margin-bottom: 10px">
+
+        <el-card class="content-h">
+
+          <p class="card-title">答题卡</p>
+          <el-row :gutter="24" class="card-line" style="padding-left: 10px">
+            <el-tag type="info">未作答</el-tag>
+            <el-tag type="success">已作答</el-tag>
+          </el-row>
+
+          <div v-if="paperData.radioList!==undefined && paperData.radioList.length > 0">
+            <p class="card-title">单选题</p>
+            <el-row :gutter="24" class="card-line">
+              <el-tag v-for="item in paperData.radioList" :type="cardItemClass(item.answered, item.quId)" @click="handSave(item)"> {{ item.sort+1 }}</el-tag>
+            </el-row>
+          </div>
+
+          <div v-if="paperData.multiList!==undefined && paperData.multiList.length > 0">
+            <p class="card-title">多选题</p>
+            <el-row :gutter="24" class="card-line">
+              <el-tag v-for="item in paperData.multiList" :type="cardItemClass(item.answered, item.quId)" @click="handSave(item)">{{ item.sort+1 }}</el-tag>
+            </el-row>
+          </div>
+
+          <div v-if="paperData.judgeList!==undefined && paperData.judgeList.length > 0">
+            <p class="card-title">判断题</p>
+            <el-row :gutter="24" class="card-line">
+              <el-tag v-for="item in paperData.judgeList" :type="cardItemClass(item.answered, item.quId)" @click="handSave(item)">{{ item.sort+1 }}</el-tag>
+            </el-row>
+          </div>
+
+        </el-card>
+
+      </el-col>
+
+      <el-col :span="19" :xs="24">
+
+        <el-card class="qu-content content-h">
+          <p style="margin-bottom: 10px" v-if="quData.content">{{ quData.sort + 1 }}.{{ quData.content }}</p>
+          <p v-if="quData.image!=null && quData.image!=''">
+            <el-image :src="quData.image" style="max-width:100%;" />
+          </p>
+          <div v-if="quData.quType === 1 || quData.quType===3">
+            <el-radio-group v-model="radioValue">
+              <el-radio v-for="item in quData.answerList" :label="item.id">{{ item.abc }}.{{ item.content }}
+                <div v-if="item.image!=null && item.image!=''" style="clear: both">
+                  <el-image :src="item.image" style="max-width:100%;" />
+                </div>
+              </el-radio>
+            </el-radio-group>
+          </div>
+
+          <div v-if="quData.quType === 2">
+
+            <el-checkbox-group v-model="multiValue">
+              <el-checkbox v-for="item in quData.answerList" :key="item.id" :label="item.id">{{ item.abc }}.{{ item.content }}
+                <div v-if="item.image!=null && item.image!=''" style="clear: both">
+                  <el-image :src="item.image" style="max-width:100%;" />
+                </div>
+              </el-checkbox>
+            </el-checkbox-group>
+
+          </div>
+
+          <div style="margin-top: 20px">
+            <el-button v-if="showPrevious" type="primary" icon="el-icon-back" @click="handPrevious()">
+              上一题
+            </el-button>
+
+            <el-button v-if="showNext" type="warning" icon="el-icon-right" @click="handNext()">
+              下一题
+            </el-button>
+
+          </div>
+
+        </el-card>
+
+      </el-col>
+
+    </el-row>
+  </div>
+
+</template>
+
+<script>
+import { paperDetail, quDetail, handExam, fillAnswer } from '@/api/training/elearn/paper'
+import { Loading } from 'element-ui'
+import ExamTimer from '@/views/training/elearn/paper/ExamTimer'
+
+export default {
+  name: 'ExamProcess',
+  components: { ExamTimer },
+  data() {
+    return {
+      // 全屏/不全屏
+      isFullscreen: false,
+      showPrevious: false,
+      showNext: true,
+      loading: false,
+      handleText: '交卷',
+      pageLoading: false,
+      // 试卷ID
+      paperId: '',
+      // 当前答题卡
+      cardItem: {},
+      allItem: [],
+      // 当前题目内容
+      quData: {
+        answerList: []
+      },
+      // 试卷信息
+      paperData: {
+        leftSeconds: 99999,
+        radioList: [],
+        multiList: [],
+        judgeList: []
+      },
+      // 单选选定值
+      radioValue: '',
+      // 多选选定值
+      multiValue: [],
+      // 已答ID
+      answeredIds: []
+    }
+  },
+  created() {
+    const id = this.$route.params.paperId
+    if (typeof id !== 'undefined') {
+      this.paperId = id
+      this.fetchData(id)
+    }
+  },
+
+  methods: {
+
+    // 答题卡样式
+    cardItemClass(answered, quId) {
+      if (quId === this.cardItem.quId) {
+        return 'warning'
+      }
+
+      if (answered) {
+        return 'success'
+      }
+
+      if (!answered) {
+        return 'info'
+      }
+    },
+
+    /**
+     * 统计有多少题没答的
+     * @returns {number}
+     */
+    countNotAnswered() {
+      let notAnswered = 0
+
+      this.paperData.radioList.forEach(function(item) {
+        if (!item.answered) {
+          notAnswered += 1
+        }
+      })
+
+      this.paperData.multiList.forEach(function(item) {
+        if (!item.answered) {
+          notAnswered += 1
+        }
+      })
+
+      this.paperData.judgeList.forEach(function(item) {
+        if (!item.answered) {
+          notAnswered += 1
+        }
+      })
+
+      return notAnswered
+    },
+
+    /**
+     * 下一题
+     */
+    handNext() {
+      const index = this.cardItem.sort + 1
+      this.handSave(this.allItem[index])
+    },
+
+    /**
+     * 上一题
+     */
+    handPrevious() {
+      const index = this.cardItem.sort - 1
+      this.handSave(this.allItem[index])
+    },
+
+    doHandler() {
+      this.handleText = '正在交卷,请等待...'
+      this.loading = true
+
+      const params = { paperId: this.paperId }
+      handExam(params).then(() => {
+        this.$message({
+          message: '试卷提交成功,即将进入试卷详情!',
+          type: 'success'
+        })
+        this.$store.dispatch("tagsView/delView", this.$route);
+        this.$router.push({ name: 'ShowExam', params: { id: this.paperId }})
+      })
+    },
+
+    // 交卷操作
+    handHandExam() {
+      const that = this
+
+      // 交卷保存答案
+      this.handSave(this.cardItem, function() {
+        const notAnswered = that.countNotAnswered()
+
+        let msg = '确认要交卷吗?'
+
+        if (notAnswered > 0) {
+          msg = '您还有' + notAnswered + '题未作答,确认要交卷吗?'
+        }
+
+        that.$confirm(msg, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          that.doHandler()
+        }).catch(() => {
+          that.$message({
+            type: 'info',
+            message: '交卷已取消,您可以继续作答!'
+          })
+        })
+      })
+    },
+
+    // 保存答案
+    handSave(item, callback) {
+      if (item.id === this.allItem[0].id) {
+        this.showPrevious = false
+      } else {
+        this.showPrevious = true
+      }
+
+      // 最后一个索引
+      const last = this.allItem.length - 1
+
+      if (item.id === this.allItem[last].id) {
+        this.showNext = false
+      } else {
+        this.showNext = true
+      }
+
+      const answers = this.multiValue
+      if (this.radioValue !== '') {
+        answers.push(this.radioValue)
+      }
+
+      const params = { paperId: this.paperId, quId: this.cardItem.quId, answers: answers, answer: '' }
+      fillAnswer(params).then(() => {
+        // 必须选择一个值
+        if (answers.length > 0) {
+          // 加入已答列表
+          this.cardItem.answered = true
+        }
+
+        // 最后一个动作,交卷
+        if (callback) {
+          callback()
+        }
+
+        // 查找详情
+        this.fetchQuData(item)
+      })
+    },
+
+    // 试卷详情
+    fetchQuData(item) {
+      // 打开
+      const loading = Loading.service({
+        text: '拼命加载中',
+        background: 'rgba(0, 0, 0, 0.7)'
+      })
+
+      // 获得详情
+      this.cardItem = item
+
+      // 查找下个详情
+      const params = { paperId: this.paperId, quId: item.quId }
+      quDetail(params).then(response => {
+        console.log(response)
+        this.quData = response.data
+        this.radioValue = ''
+        this.multiValue = []
+
+        // 填充该题目的答案
+        this.quData.answerList.forEach((item) => {
+          if ((this.quData.quType === 1 || this.quData.quType === 3) && item.checked) {
+            this.radioValue = item.id
+          }
+
+          if (this.quData.quType === 2 && item.checked) {
+            this.multiValue.push(item.id)
+          }
+        })
+
+        // 关闭详情
+        loading.close()
+      })
+    },
+
+    // 试卷详情
+    fetchData(id) {
+      const params = { paperId: id }
+      paperDetail(params).then(response => {
+        // 试卷内容
+        this.paperData = response.data
+
+        // 获得第一题内容
+        if (this.paperData.radioList && this.paperData.radioList.length>0) {
+          this.cardItem = this.paperData.radioList[0]
+        } else if (this.paperData.multiList && this.paperData.multiList.length>0) {
+          this.cardItem = this.paperData.multiList[0]
+        } else if (this.paperData.judgeList && this.paperData.judgeList.length>0) {
+          this.cardItem = this.paperData.judgeList[0]
+        }
+
+        const that = this
+
+        this.paperData.radioList.forEach(function(item) {
+          that.allItem.push(item)
+        })
+
+        this.paperData.multiList.forEach(function(item) {
+          that.allItem.push(item)
+        })
+
+        this.paperData.judgeList.forEach(function(item) {
+          that.allItem.push(item)
+        })
+
+        // 当前选定
+        this.fetchQuData(this.cardItem)
+      })
+    }
+
+  }
+}
+</script>
+
+<style scoped>
+
+  .qu-content div{
+    line-height: 30px;
+    width: 100%;
+  }
+
+  .el-checkbox-group label,.el-radio-group label{
+    width: 100%;
+  }
+
+  .content-h{
+    height: calc(100vh - 110px);
+    overflow-y: auto;
+  }
+
+  .card-title{
+    background: #eee;
+    line-height: 35px;
+    text-align: center;
+    font-size: 14px;
+  }
+  .card-line{
+    padding-left: 10px
+  }
+  .card-line span {
+    cursor: pointer;
+    margin: 2px;
+  }
+
+  ::v-deep
+  .el-radio, .el-checkbox{
+    padding: 9px 20px 9px 10px;
+    border-radius: 4px;
+    border: 1px solid #dcdfe6;
+    margin-bottom: 10px;
+    width: 100%;
+  }
+
+  .is-checked{
+    border: #409eff 1px solid;
+  }
+
+  .el-radio img, .el-checkbox img{
+    max-width: 200px;
+    max-height: 200px;
+    border: #dcdfe6 1px dotted;
+  }
+
+  ::v-deep
+  .el-checkbox__inner {
+    display: none;
+  }
+
+  ::v-deep
+  .el-radio__inner{
+    display: none;
+  }
+
+  ::v-deep
+  .el-checkbox__label{
+    line-height: 30px;
+  }
+
+  ::v-deep
+  .el-radio__label{
+    line-height: 30px;
+  }
+
+</style>
+

+ 0 - 7
ui/src/views/training/elearn/paper/index.vue

@@ -513,7 +513,6 @@ export default {
           this.clientHeight = document.body.clientHeight -250
       })
     this.getList();
-    this.getTreeselect();
   },
   methods: {
     /** 查询在线考试列表 */
@@ -525,12 +524,6 @@ export default {
         this.loading = false;
       });
     },
-     /** 查询部门下拉树结构 */
-     getTreeselect() {
-          treeselect().then(response => {
-              this.deptOptions = response.data;
-          });
-     },
     // 取消按钮
     cancel() {
       this.open = false;

+ 0 - 1
ui/src/views/training/elearn/paper/list.vue

@@ -240,7 +240,6 @@ export default {
           this.clientHeight = document.body.clientHeight -250
       })
     this.getList();
-    this.getTreeselect();
   },
   methods: {
     /** 查询考试列表 */

+ 3 - 2
ui/src/views/training/elearn/paper/preview.vue

@@ -51,6 +51,7 @@ export default {
   data() {
     return {
       detailData: {},
+      loading: false,
       postForm: {
         examId: '',
       },
@@ -85,11 +86,11 @@ export default {
       createPaper(this.postForm).then(response => {
         console.log(response)
 
-        if (response.code === 0) {
+        if (response.code === 200) {
           setTimeout(function() {
             this.loading = false
             that.dialogVisible = false
-            that.$router.push({ name: 'StartExam', params: { id: response.data.id }})
+            that.$router.push({ name: 'StartExam', params: { paperId: response.data.paperId }})
           }, 1000)
         }
       }).catch(() => {

+ 228 - 0
ui/src/views/training/elearn/paper/result.vue

@@ -0,0 +1,228 @@
+<template>
+  <div class="app-container">
+
+    <h2 class="text-center">{{ paperData.title }}</h2>
+    <p class="text-center" style="color: #666">{{ paperData.createTime }}</p>
+
+    <el-row :gutter="24" style="margin-top: 50px">
+
+      <el-col :span="8" class="text-center">
+        考生姓名:{{ paperData.userId_dictText }}
+      </el-col>
+
+      <el-col :span="8" class="text-center">
+        考试用时:{{ paperData.userTime }}分钟
+      </el-col>
+
+      <el-col :span="8" class="text-center">
+        考试得分:{{ paperData.userScore }}
+      </el-col>
+
+    </el-row>
+
+    <el-card style="margin-top: 20px">
+
+      <div v-for="item in paperData.quList" :key="item.id" class="qu-content">
+
+        <p>{{ item.sort + 1 }}.{{ item.content }}(得分:{{ item.actualScore }})</p>
+        <p v-if="item.image!=null && item.image!=''">
+          <el-image :src="item.image" style="max-width:100%;" />
+        </p>
+        <div v-if="item.quType === 1 || item.quType===3">
+          <el-radio-group v-model="radioValues[item.id]">
+            <el-radio v-for="an in item.answerList" :label="an.id">
+              {{ an.abc }}.{{ an.content }}
+              <div v-if="an.image!=null && an.image!=''" style="clear: both">
+                <el-image :src="an.image" style="max-width:100%;" />
+              </div>
+            </el-radio>
+          </el-radio-group>
+
+          <el-row :gutter="24">
+
+            <el-col :span="12" style="color: #24da70">
+              正确答案:{{ radioRights[item.id] }}
+            </el-col>
+
+            <el-col v-if="!item.answered" :span="12" style="text-align: right; color: #ff0000;">
+              答题结果:未答
+            </el-col>
+
+            <el-col v-if="item.answered && !item.isRight" :span="12" style="text-align: right; color: #ff0000;">
+              答题结果:{{ myRadio[item.id] }}
+            </el-col>
+
+            <el-col v-if="item.answered && item.isRight" :span="12" style="text-align: right; color: #24da70;">
+              答题结果:{{ myRadio[item.id] }}
+            </el-col>
+
+          </el-row>
+
+        </div>
+
+        <div v-if="item.quType === 4">
+
+          <el-row :gutter="24">
+
+            <el-col :span="12">
+              我的回答:{{ item.answer }}
+            </el-col>
+
+          </el-row>
+
+        </div>
+
+        <div v-if="item.quType === 2">
+          <el-checkbox-group v-model="multiValues[item.id]">
+            <el-checkbox v-for="an in item.answerList" :key="an.id" :label="an.id">{{ an.abc }}.{{ an.content }}
+              <div v-if="an.image!=null && an.image!=''" style="clear: both">
+                <el-image :src="an.image" style="max-width:100%;" />
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+
+          <el-row :gutter="24">
+
+            <el-col :span="12" style="color: #24da70">
+              正确答案:{{ multiRights[item.id].join(',') }}
+            </el-col>
+
+            <el-col v-if="!item.answered" :span="12" style="text-align: right; color: #ff0000;">
+              答题结果:未答
+            </el-col>
+
+            <el-col v-if="item.answered && !item.isRight" :span="12" style="text-align: right; color: #ff0000;">
+              答题结果:{{ myMulti[item.id].join(',') }}
+            </el-col>
+
+            <el-col v-if="item.answered && item.isRight" :span="12" style="text-align: right; color: #24da70;">
+              答题结果:{{ myMulti[item.id].join(',') }}
+            </el-col>
+
+          </el-row>
+        </div>
+
+      </div>
+
+    </el-card>
+
+  </div>
+</template>
+
+<script>
+
+import { paperResult } from '@/api/training/elearn/paper'
+
+export default {
+  data() {
+    return {
+      // 试卷ID
+      paperId: '',
+      paperData: {
+        quList: []
+      },
+      radioValues: {},
+      multiValues: {},
+      radioRights: {},
+      multiRights: {},
+      myRadio: {},
+      myMulti: {}
+    }
+  },
+  created() {
+    const id = this.$route.params.id
+    if (typeof id !== 'undefined') {
+      this.paperId = id
+      this.fetchData(id)
+    }
+  },
+  methods: {
+
+    fetchData(id) {
+      const params = { paperId: id }
+      paperResult(params).then(response => {
+        // 试卷内容
+        this.paperData = response.data
+
+        // 填充该题目的答案
+        this.paperData.quList.forEach((item) => {
+          let radioValue = ''
+          let radioRight = ''
+          let myRadio = ''
+          const multiValue = []
+          const multiRight = []
+          const myMulti = []
+
+          item.answerList.forEach((an) => {
+            // 用户选定的
+            if (an.checked) {
+              if (item.quType === 1 || item.quType === 3) {
+                radioValue = an.id
+                myRadio = an.abc
+              } else {
+                multiValue.push(an.id)
+                myMulti.push(an.abc)
+              }
+            }
+
+            // 正确答案
+            if (an.isRight) {
+              if (item.quType === 1 || item.quType === 3) {
+                radioRight = an.abc
+              } else {
+                multiRight.push(an.abc)
+              }
+            }
+          })
+
+          this.multiValues[item.id] = multiValue
+          this.radioValues[item.id] = radioValue
+
+          this.radioRights[item.id] = radioRight
+          this.multiRights[item.id] = multiRight
+
+          this.myRadio[item.id] = myRadio
+          this.myMulti[item.id] = myMulti
+        })
+
+        console.log(this.multiValues)
+        console.log(this.radioValues)
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+  .qu-content{
+
+    border-bottom: #eee 1px solid;
+    padding-bottom: 10px;
+
+  }
+
+  .qu-content div{
+    line-height: 30px;
+  }
+
+  .el-checkbox-group label,.el-radio-group label{
+    width: 100%;
+  }
+
+  .card-title{
+    background: #eee;
+    line-height: 35px;
+    text-align: center;
+    font-size: 14px;
+  }
+  .card-line{
+    padding-left: 10px
+  }
+  .card-line span {
+    cursor: pointer;
+    margin: 2px;
+  }
+
+</style>
+

+ 40 - 2
ui/src/views/training/elearn/qu/index.vue

@@ -11,7 +11,9 @@
           />
         </el-select>
       </el-form-item>
-
+      <el-form-item label="题库" prop="repoIds">
+        <repo-select v-model="queryParams.repoIds" :multi="true" />
+      </el-form-item>
       <el-form-item>
         <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
         <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -47,6 +49,14 @@
           @click="handleDelete"
           v-hasPermi="['elearn:qu:remove']"
         >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          icon="el-icon-upload2"
+          size="mini"
+          @click="handleImport"
+        >导入</el-button>
       </el-col>
 	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
@@ -221,6 +231,33 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+    <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+      <el-upload
+        ref="upload"
+        :limit="1"
+        :headers="upload.headers"
+        :action="upload.url + '?repoIds=' + upload.repoIds"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          将文件拖到此处,或
+          <em>点击上传</em>
+        </div>
+        <div class="el-upload__tip" slot="tip">
+          <repo-select v-model="upload.repoIds" :multi="true" />
+        </div>
+        <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“doc”格式文件!</div>
+      </el-upload>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitFileForm">确 定</el-button>
+        <el-button @click="upload.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
 
   </div>
 </template>
@@ -309,7 +346,8 @@ export default {
         updaterCode: null,
         updatedate: null,
         deptId: null,
-        remarks: null
+        remarks: null,
+        repoIds: null,
       },
       // 表单参数
       form: {

+ 393 - 0
ui/src/views/training/elearn/userBook/index.vue

@@ -0,0 +1,393 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+
+      <el-form-item label="题目标题" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入题目标题"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+        >练习</el-button>
+      </el-col>
+
+
+
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="userBookList" @selection-change="handleSelectionChange" :height="clientHeight" border>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="题目标题" align="center" prop="title" :show-overflow-tooltip="true"/>
+
+      <el-table-column label="错误次数" align="center" prop="wrongCount" :show-overflow-tooltip="true"/>
+
+      <el-table-column label="更新日期" align="center" prop="updatedate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updatedate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+<!--      <el-table-column label="操作" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['elearn:userBook:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['elearn:userBook:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>-->
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改错题本对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="用户ID" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户ID" />
+        </el-form-item>
+        <el-form-item label="考试ID" prop="examId">
+          <el-input v-model="form.examId" placeholder="请输入考试ID" />
+        </el-form-item>
+        <el-form-item label="题目ID" prop="quId">
+          <el-input v-model="form.quId" placeholder="请输入题目ID" />
+        </el-form-item>
+        <el-form-item label="错误次数" prop="wrongCount">
+          <el-input v-model="form.wrongCount" placeholder="请输入错误次数" />
+        </el-form-item>
+        <el-form-item label="题目标题" prop="title">
+          <el-input v-model="form.title" placeholder="请输入题目标题" />
+        </el-form-item>
+        <el-form-item label="删除" prop="delFlag">
+          <el-input v-model="form.delFlag" placeholder="请输入删除" />
+        </el-form-item>
+        <el-form-item label="创建人" prop="createrCode">
+          <el-input v-model="form.createrCode" placeholder="请输入创建人" />
+        </el-form-item>
+        <el-form-item label="创建时间" prop="createdate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.createdate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择创建时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="更新人" prop="updaterCode">
+          <el-input v-model="form.updaterCode" placeholder="请输入更新人" />
+        </el-form-item>
+        <el-form-item label="更新日期" prop="updatedate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.updatedate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择更新日期">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input v-model="form.sort" placeholder="请输入排序" />
+        </el-form-item>
+          <el-form-item label="归属部门" prop="deptId">
+              <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+          </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+      <!-- 用户导入对话框 -->
+      <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+          <el-upload
+                  ref="upload"
+                  :limit="1"
+                  accept=".xlsx, .xls"
+                  :headers="upload.headers"
+                  :action="upload.url + '?updateSupport=' + upload.updateSupport"
+                  :disabled="upload.isUploading"
+                  :on-progress="handleFileUploadProgress"
+                  :on-success="handleFileSuccess"
+                  :auto-upload="false"
+                  drag
+          >
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">
+                  将文件拖到此处,或
+                  <em>点击上传</em>
+              </div>
+              <div class="el-upload__tip" slot="tip">
+                  <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
+                  <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
+              </div>
+              <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
+          </el-upload>
+          <div slot="footer" class="dialog-footer">
+              <el-button type="primary" @click="submitFileForm">确 定</el-button>
+              <el-button @click="upload.open = false">取 消</el-button>
+          </div>
+      </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listUserBook, getUserBook, delUserBook, addUserBook, updateUserBook, exportUserBook, importTemplate} from "@/api/training/elearn/userBook";
+import { treeselect } from "@/api/system/dept";
+import { getToken } from "@/utils/auth";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "UserBook",
+  components: { Treeselect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: false,
+      // 总条数
+      total: 0,
+      // 错题本表格数据
+      userBookList: [],
+      // 弹出层标题
+      title: "",
+      // 部门树选项
+      deptOptions: undefined,
+      clientHeight:300,
+      // 是否显示弹出层
+      open: false,
+        // 用户导入参数
+        upload: {
+            // 是否显示弹出层(用户导入)
+            open: false,
+            // 弹出层标题(用户导入)
+            title: "",
+            // 是否禁用上传
+            isUploading: false,
+            // 是否更新已经存在的用户数据
+            updateSupport: 0,
+            // 设置上传的请求头部
+            headers: { Authorization: "Bearer " + getToken() },
+            // 上传的地址
+            url: process.env.VUE_APP_BASE_API + "/elearn/userBook/importData"
+        },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 20,
+        userId: null,
+        examId: null,
+        quId: null,
+        wrongCount: null,
+        title: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        sort: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  watch: {
+        // 根据名称筛选部门树
+        deptName(val) {
+            this.$refs.tree.filter(val);
+        }
+   },
+  created() {
+      //设置表格高度对应屏幕高度
+      this.$nextTick(() => {
+          this.clientHeight = document.body.clientHeight -250
+      })
+    this.getList();
+    this.getTreeselect();
+  },
+  methods: {
+    /** 查询错题本列表 */
+    getList() {
+      this.loading = true;
+      listUserBook(this.queryParams).then(response => {
+        this.userBookList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+     /** 查询部门下拉树结构 */
+     getTreeselect() {
+          treeselect().then(response => {
+              this.deptOptions = response.data;
+          });
+     },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        userId: null,
+        examId: null,
+        quId: null,
+        wrongCount: null,
+        title: null,
+        delFlag: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        sort: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加错题本";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getUserBook(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改错题本";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateUserBook(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addUserBook(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delUserBook(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有错题本数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportUserBook(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        })
+    },
+      /** 导入按钮操作 */
+      handleImport() {
+          this.upload.title = "用户导入";
+          this.upload.open = true;
+      },
+      /** 下载模板操作 */
+      importTemplate() {
+          importTemplate().then(response => {
+              this.download(response.msg);
+          });
+      },
+      // 文件上传中处理
+      handleFileUploadProgress(event, file, fileList) {
+          this.upload.isUploading = true;
+      },
+      // 文件上传成功处理
+      handleFileSuccess(response, file, fileList) {
+          this.upload.open = false;
+          this.upload.isUploading = false;
+          this.$refs.upload.clearFiles();
+          this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+          this.getList();
+      },
+      // 提交上传文件
+      submitFileForm() {
+          this.$refs.upload.submit();
+      }
+  }
+};
+</script>

+ 386 - 0
ui/src/views/training/elearn/userExam/index.vue

@@ -0,0 +1,386 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+
+      <el-form-item label="考试名称" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入考试名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="userExamList" @selection-change="handleSelectionChange" :height="clientHeight" border>
+      <el-table-column label="考试" align="center" prop="title" :show-overflow-tooltip="true"/>
+      <el-table-column label="考试次数" align="center" prop="tryCount" :show-overflow-tooltip="true"/>
+      <el-table-column label="最高分数" align="center" prop="maxScore" :show-overflow-tooltip="true"/>
+      <el-table-column label="是否通过" align="center" prop="passed" :show-overflow-tooltip="true">
+          <template v-slot="scope">
+            <el-tag v-if="scope.row.passed == 1" size="small" type="success">通过</el-tag>
+            <el-tag v-else-if="scope.row.passed == 0" size="small" type="info">未通过</el-tag>
+          </template>
+      </el-table-column>
+
+      <el-table-column label="更新日期" align="center" prop="updatedate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updatedate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['elearn:userExam:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['elearn:userExam:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改我的成绩对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="用户ID" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户ID" />
+        </el-form-item>
+        <el-form-item label="考试ID" prop="examId">
+          <el-input v-model="form.examId" placeholder="请输入考试ID" />
+        </el-form-item>
+        <el-form-item label="考试次数" prop="tryCount">
+          <el-input v-model="form.tryCount" placeholder="请输入考试次数" />
+        </el-form-item>
+        <el-form-item label="最高分数" prop="maxScore">
+          <el-input v-model="form.maxScore" placeholder="请输入最高分数" />
+        </el-form-item>
+        <el-form-item label="是否通过" prop="passed">
+          <el-input v-model="form.passed" placeholder="请输入是否通过" />
+        </el-form-item>
+        <el-form-item label="删除" prop="delFlag">
+          <el-input v-model="form.delFlag" placeholder="请输入删除" />
+        </el-form-item>
+        <el-form-item label="创建人" prop="createrCode">
+          <el-input v-model="form.createrCode" placeholder="请输入创建人" />
+        </el-form-item>
+        <el-form-item label="创建时间" prop="createdate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.createdate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择创建时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="更新人" prop="updaterCode">
+          <el-input v-model="form.updaterCode" placeholder="请输入更新人" />
+        </el-form-item>
+        <el-form-item label="更新日期" prop="updatedate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.updatedate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择更新日期">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="所属部门" prop="deptId">
+          <el-input v-model="form.deptId" placeholder="请输入所属部门" />
+        </el-form-item>
+          <el-form-item label="归属部门" prop="deptId">
+              <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+          </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+      <!-- 用户导入对话框 -->
+      <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+          <el-upload
+                  ref="upload"
+                  :limit="1"
+                  accept=".xlsx, .xls"
+                  :headers="upload.headers"
+                  :action="upload.url + '?updateSupport=' + upload.updateSupport"
+                  :disabled="upload.isUploading"
+                  :on-progress="handleFileUploadProgress"
+                  :on-success="handleFileSuccess"
+                  :auto-upload="false"
+                  drag
+          >
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">
+                  将文件拖到此处,或
+                  <em>点击上传</em>
+              </div>
+              <div class="el-upload__tip" slot="tip">
+                  <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
+                  <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
+              </div>
+              <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
+          </el-upload>
+          <div slot="footer" class="dialog-footer">
+              <el-button type="primary" @click="submitFileForm">确 定</el-button>
+              <el-button @click="upload.open = false">取 消</el-button>
+          </div>
+      </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listUserExam, getUserExam, delUserExam, addUserExam, updateUserExam, exportUserExam, importTemplate} from "@/api/training/elearn/userExam";
+import { treeselect } from "@/api/system/dept";
+import { getToken } from "@/utils/auth";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "UserExam",
+  components: { Treeselect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 我的成绩表格数据
+      userExamList: [],
+      // 弹出层标题
+      title: "",
+      // 部门树选项
+      deptOptions: undefined,
+      clientHeight:300,
+      // 是否显示弹出层
+      open: false,
+        // 用户导入参数
+        upload: {
+            // 是否显示弹出层(用户导入)
+            open: false,
+            // 弹出层标题(用户导入)
+            title: "",
+            // 是否禁用上传
+            isUploading: false,
+            // 是否更新已经存在的用户数据
+            updateSupport: 0,
+            // 设置上传的请求头部
+            headers: { Authorization: "Bearer " + getToken() },
+            // 上传的地址
+            url: process.env.VUE_APP_BASE_API + "/elearn/userExam/importData"
+        },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 20,
+        userId: null,
+        examId: null,
+        tryCount: null,
+        maxScore: null,
+        passed: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        deptId: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  watch: {
+        // 根据名称筛选部门树
+        deptName(val) {
+            this.$refs.tree.filter(val);
+        }
+   },
+  created() {
+      //设置表格高度对应屏幕高度
+      this.$nextTick(() => {
+          this.clientHeight = document.body.clientHeight -250
+      })
+    this.getList();
+    this.getTreeselect();
+  },
+  methods: {
+    /** 查询我的成绩列表 */
+    getList() {
+      this.loading = true;
+      listUserExam(this.queryParams).then(response => {
+        this.userExamList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+     /** 查询部门下拉树结构 */
+     getTreeselect() {
+          treeselect().then(response => {
+              this.deptOptions = response.data;
+          });
+     },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        userId: null,
+        examId: null,
+        tryCount: null,
+        maxScore: null,
+        passed: null,
+        delFlag: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        deptId: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加我的成绩";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getUserExam(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改我的成绩";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateUserExam(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addUserExam(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delUserExam(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有我的成绩数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportUserExam(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        })
+    },
+      /** 导入按钮操作 */
+      handleImport() {
+          this.upload.title = "用户导入";
+          this.upload.open = true;
+      },
+      /** 下载模板操作 */
+      importTemplate() {
+          importTemplate().then(response => {
+              this.download(response.msg);
+          });
+      },
+      // 文件上传中处理
+      handleFileUploadProgress(event, file, fileList) {
+          this.upload.isUploading = true;
+      },
+      // 文件上传成功处理
+      handleFileSuccess(response, file, fileList) {
+          this.upload.open = false;
+          this.upload.isUploading = false;
+          this.$refs.upload.clearFiles();
+          this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+          this.getList();
+      },
+      // 提交上传文件
+      submitFileForm() {
+          this.$refs.upload.submit();
+      }
+  }
+};
+</script>

+ 428 - 0
ui/src/views/training/elearn/userQu/index.vue

@@ -0,0 +1,428 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+
+
+
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['elearn:userQu:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['elearn:userQu:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['elearn:userQu:remove']"
+        >删除</el-button>
+      </el-col>
+        <el-col :span="1.5">
+            <el-button
+                    type="info"
+                    icon="el-icon-upload2"
+                    size="mini"
+                    @click="handleImport"
+                    v-hasPermi="['elearn:userQu:edit']"
+            >导入</el-button>
+        </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['elearn:userQu:export']"
+        >导出</el-button>
+      </el-col>
+	  <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="userQuList" @selection-change="handleSelectionChange" :height="clientHeight" border>
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="id" align="center" prop="id" :show-overflow-tooltip="true"/>
+      <el-table-column label="用户ID" align="center" prop="userId" :show-overflow-tooltip="true"/>
+      <el-table-column label="考试ID" align="center" prop="examId" :show-overflow-tooltip="true"/>
+      <el-table-column label="题目ID" align="center" prop="quId" :show-overflow-tooltip="true"/>
+      <el-table-column label="题库ID" align="center" prop="repoId" :show-overflow-tooltip="true"/>
+      <el-table-column label="题目标题" align="center" prop="title" :show-overflow-tooltip="true"/>
+      <el-table-column label="创建人" align="center" prop="createrCode" :show-overflow-tooltip="true"/>
+      <el-table-column label="创建时间" align="center" prop="createdate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createdate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="更新人" align="center" prop="updaterCode" :show-overflow-tooltip="true"/>
+      <el-table-column label="更新日期" align="center" prop="updatedate" width="100">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.updatedate, '{y}-{m}-{d}') }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="排序" align="center" prop="sort" :show-overflow-tooltip="true"/>
+      <el-table-column label="操作" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['elearn:userQu:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['elearn:userQu:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改答题记录对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="用户ID" prop="userId">
+          <el-input v-model="form.userId" placeholder="请输入用户ID" />
+        </el-form-item>
+        <el-form-item label="考试ID" prop="examId">
+          <el-input v-model="form.examId" placeholder="请输入考试ID" />
+        </el-form-item>
+        <el-form-item label="题目ID" prop="quId">
+          <el-input v-model="form.quId" placeholder="请输入题目ID" />
+        </el-form-item>
+        <el-form-item label="题库ID" prop="repoId">
+          <el-input v-model="form.repoId" placeholder="请输入题库ID" />
+        </el-form-item>
+        <el-form-item label="题目标题" prop="title">
+          <el-input v-model="form.title" placeholder="请输入题目标题" />
+        </el-form-item>
+        <el-form-item label="删除" prop="delFlag">
+          <el-input v-model="form.delFlag" placeholder="请输入删除" />
+        </el-form-item>
+        <el-form-item label="创建人" prop="createrCode">
+          <el-input v-model="form.createrCode" placeholder="请输入创建人" />
+        </el-form-item>
+        <el-form-item label="创建时间" prop="createdate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.createdate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择创建时间">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="更新人" prop="updaterCode">
+          <el-input v-model="form.updaterCode" placeholder="请输入更新人" />
+        </el-form-item>
+        <el-form-item label="更新日期" prop="updatedate">
+          <el-date-picker clearable size="small" style="width: 200px"
+            v-model="form.updatedate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择更新日期">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="排序" prop="sort">
+          <el-input v-model="form.sort" placeholder="请输入排序" />
+        </el-form-item>
+          <el-form-item label="归属部门" prop="deptId">
+              <treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" placeholder="请选择归属部门" />
+          </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+      <!-- 用户导入对话框 -->
+      <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
+          <el-upload
+                  ref="upload"
+                  :limit="1"
+                  accept=".xlsx, .xls"
+                  :headers="upload.headers"
+                  :action="upload.url + '?updateSupport=' + upload.updateSupport"
+                  :disabled="upload.isUploading"
+                  :on-progress="handleFileUploadProgress"
+                  :on-success="handleFileSuccess"
+                  :auto-upload="false"
+                  drag
+          >
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">
+                  将文件拖到此处,或
+                  <em>点击上传</em>
+              </div>
+              <div class="el-upload__tip" slot="tip">
+                  <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
+                  <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
+              </div>
+              <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
+          </el-upload>
+          <div slot="footer" class="dialog-footer">
+              <el-button type="primary" @click="submitFileForm">确 定</el-button>
+              <el-button @click="upload.open = false">取 消</el-button>
+          </div>
+      </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listUserQu, getUserQu, delUserQu, addUserQu, updateUserQu, exportUserQu, importTemplate} from "@/api/training/elearn/userQu";
+import { treeselect } from "@/api/system/dept";
+import { getToken } from "@/utils/auth";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "UserQu",
+  components: { Treeselect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: false,
+      // 总条数
+      total: 0,
+      // 答题记录表格数据
+      userQuList: [],
+      // 弹出层标题
+      title: "",
+      // 部门树选项
+      deptOptions: undefined,
+      clientHeight:300,
+      // 是否显示弹出层
+      open: false,
+        // 用户导入参数
+        upload: {
+            // 是否显示弹出层(用户导入)
+            open: false,
+            // 弹出层标题(用户导入)
+            title: "",
+            // 是否禁用上传
+            isUploading: false,
+            // 是否更新已经存在的用户数据
+            updateSupport: 0,
+            // 设置上传的请求头部
+            headers: { Authorization: "Bearer " + getToken() },
+            // 上传的地址
+            url: process.env.VUE_APP_BASE_API + "/elearn/userQu/importData"
+        },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 20,
+        userId: null,
+        examId: null,
+        quId: null,
+        repoId: null,
+        title: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        sort: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  watch: {
+        // 根据名称筛选部门树
+        deptName(val) {
+            this.$refs.tree.filter(val);
+        }
+   },
+  created() {
+      //设置表格高度对应屏幕高度
+      this.$nextTick(() => {
+          this.clientHeight = document.body.clientHeight -250
+      })
+    this.getList();
+    this.getTreeselect();
+  },
+  methods: {
+    /** 查询答题记录列表 */
+    getList() {
+      this.loading = true;
+      listUserQu(this.queryParams).then(response => {
+        this.userQuList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+     /** 查询部门下拉树结构 */
+     getTreeselect() {
+          treeselect().then(response => {
+              this.deptOptions = response.data;
+          });
+     },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        userId: null,
+        examId: null,
+        quId: null,
+        repoId: null,
+        title: null,
+        delFlag: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        sort: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加答题记录";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getUserQu(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改答题记录";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateUserQu(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addUserQu(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delUserQu(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有答题记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportUserQu(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+        })
+    },
+      /** 导入按钮操作 */
+      handleImport() {
+          this.upload.title = "用户导入";
+          this.upload.open = true;
+      },
+      /** 下载模板操作 */
+      importTemplate() {
+          importTemplate().then(response => {
+              this.download(response.msg);
+          });
+      },
+      // 文件上传中处理
+      handleFileUploadProgress(event, file, fileList) {
+          this.upload.isUploading = true;
+      },
+      // 文件上传成功处理
+      handleFileSuccess(response, file, fileList) {
+          this.upload.open = false;
+          this.upload.isUploading = false;
+          this.$refs.upload.clearFiles();
+          this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+          this.getList();
+      },
+      // 提交上传文件
+      submitFileForm() {
+          this.$refs.upload.submit();
+      }
+  }
+};
+</script>

+ 291 - 0
ui/src/views/training/elearn/userQu/repo.vue

@@ -0,0 +1,291 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true"  label-width="68px">
+      <el-form-item label="题库名称" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入题库名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-form>
+
+
+
+    <el-table v-loading="loading" :data="repoList" @selection-change="handleSelectionChange" :height="clientHeight"
+              border>
+      <el-table-column type="selection" width="55" align="center"/>
+      <!--      <el-table-column label="题库编号" align="center" prop="code" :show-overflow-tooltip="true"/>-->
+      <el-table-column label="题库名称" align="center" prop="title" :show-overflow-tooltip="true"/>
+      <el-table-column
+        label="单选题数量"
+        prop="radioCount"
+        align="center"
+      />
+      <el-table-column
+        label="多选题数量"
+        prop="multiCount"
+        align="center"
+      />
+      <el-table-column
+        label="判断题数量"
+        prop="judgeCount"
+        align="center"
+      />
+      <el-table-column label="备注" align="center" prop="remarks" :show-overflow-tooltip="true"/>
+      <el-table-column label="操作" align="center" fixed="right" width="120" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleTrain(scope.row)"
+          >开始练习
+          </el-button>
+
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import {listRepo, getRepo, delRepo, addRepo, updateRepo, exportRepo, importTemplate} from "@/api/training/elearn/repo";
+import {treeselect} from "@/api/system/dept";
+import {getToken} from "@/utils/auth";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+
+export default {
+  name: "Repo",
+  components: {Treeselect},
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 题库表格数据
+      repoList: [],
+      // 弹出层标题
+      title: "",
+      // 部门树选项
+      deptOptions: undefined,
+      clientHeight: 300,
+      // 是否显示弹出层
+      open: false,
+      // 用户导入参数
+      upload: {
+        // 是否显示弹出层(用户导入)
+        open: false,
+        // 弹出层标题(用户导入)
+        title: "",
+        // 是否禁用上传
+        isUploading: false,
+        // 是否更新已经存在的用户数据
+        updateSupport: 0,
+        // 设置上传的请求头部
+        headers: {Authorization: "Bearer " + getToken()},
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + "/elearn/repo/importData"
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 20,
+        code: null,
+        title: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        deptId: null,
+        remarks: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {}
+    };
+  },
+  watch: {
+    // 根据名称筛选部门树
+    deptName(val) {
+      this.$refs.tree.filter(val);
+    }
+  },
+  created() {
+    //设置表格高度对应屏幕高度
+    this.$nextTick(() => {
+      this.clientHeight = document.body.clientHeight - 250
+    })
+    this.getList();
+    this.getTreeselect();
+  },
+  methods: {
+    /** 查询题库列表 */
+    getList() {
+      this.loading = true;
+      listRepo(this.queryParams).then(response => {
+        this.repoList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 查询部门下拉树结构 */
+    getTreeselect() {
+      treeselect().then(response => {
+        this.deptOptions = response.data;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        repoId: null,
+        code: null,
+        title: null,
+        delFlag: null,
+        createrCode: null,
+        createdate: null,
+        updaterCode: null,
+        updatedate: null,
+        deptId: null,
+        remarks: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.repoId)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加题库";
+    },
+    /** 修改按钮操作 */
+    handleTrain(row) {
+      const repoId = row.repoId || this.ids
+      this.$router.push({ name: 'RepoTraining', params: { repoId: repoId }})
+
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.repoId != null) {
+            updateRepo(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addRepo(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const repoIds = row.repoId || this.ids;
+      this.$confirm('是否确认删除?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return delRepo(repoIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有题库数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function () {
+        return exportRepo(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+      })
+    },
+    /** 导入按钮操作 */
+    handleImport() {
+      this.upload.title = "用户导入";
+      this.upload.open = true;
+    },
+    /** 下载模板操作 */
+    importTemplate() {
+      importTemplate().then(response => {
+        this.download(response.msg);
+      });
+    },
+    // 文件上传中处理
+    handleFileUploadProgress(event, file, fileList) {
+      this.upload.isUploading = true;
+    },
+    // 文件上传成功处理
+    handleFileSuccess(response, file, fileList) {
+      this.upload.open = false;
+      this.upload.isUploading = false;
+      this.$refs.upload.clearFiles();
+      this.$alert(response.msg, "导入结果", {dangerouslyUseHTMLString: true});
+      this.getList();
+    },
+    // 提交上传文件
+    submitFileForm() {
+      this.$refs.upload.submit();
+    }
+  }
+};
+</script>

+ 195 - 0
ui/src/views/training/elearn/userQu/train.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="app-container">
+
+    <el-card style="margin-top: 20px">
+
+      <div class="qu-content">
+
+        <p>【{{ getQuType(quData.quType)  }}】{{ quData.content }}</p>
+        <p v-if="quData.image!=null && quData.image!=''">
+          <el-image :src="quData.image" style="max-width:100%;" />
+        </p>
+
+        <div v-if="quData.quType === 1 || quData.quType===3 ">
+          <el-radio-group v-model="answerValues[0]" readonly>
+            <el-radio v-for="an in quData.answerList" :key="an.answerId" :label="an.answerId" readonly>
+              {{ an.abc }}.{{ an.content }}
+              <div v-if="an.image!=null && an.image!=''" style="clear: both">
+                <el-image :src="an.image" style="max-width:100%;" />
+              </div>
+            </el-radio>
+          </el-radio-group>
+        </div>
+
+        <!-- 多选题 -->
+        <div v-if="quData.quType === 2">
+          <el-checkbox-group v-model="answerValues" readonly>
+            <el-checkbox v-for="an in quData.answerList" :key="an.answerId" :label="an.answerId">
+              {{ an.abc }}.{{ an.content }}
+              <div v-if="an.image!=null && an.image!=''" style="clear: both">
+                <el-image :src="an.image" style="max-width:100%;" />
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+        </div>
+
+        <div v-if="analysisShow" style="margin-top: 20px; color: #1890ff; font-weight: bold">
+          正确答案:{{ rightTags.join(' ') }}
+        </div>
+
+      </div>
+
+    </el-card>
+
+    <el-card v-if="analysisShow" class="qu-analysis" style="margin-top: 20px">
+      整题解析:
+      <p>{{ quData.analysis }}</p>
+      <p v-if="!quData.analysis">暂无解析内容!</p>
+    </el-card>
+
+    <el-card v-if="analysisShow" class="qu-analysis" style="margin-top: 20px;">
+      选项解析:
+      <div v-for="an in quData.answerList" v-if="an.analysis" class="qu-analysis-line">
+        <p style="color: #555;">{{ an.content }}:</p>
+        <p style="color: #1890ff;">{{ an.analysis }}</p>
+      </div>
+      <p v-if="analysisCount === 0">暂无选项解析</p>
+
+    </el-card>
+
+    <div style="padding-top: 30px">
+      <el-button type="primary" @click="handNext">继续下一题</el-button>
+      <el-button type="info" @click="onCancel">返回</el-button>
+    </div>
+
+  </div>
+</template>
+
+<script>
+import { getQu } from '@/api/training/elearn/qu'
+import { nextQu } from '@/api/training/elearn/userQu'
+
+export default {
+  name: 'BookTrain',
+  data() {
+    return {
+      repoId: null,
+      examId: null,
+      quId: null,
+      tags: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N'],
+      analysisShow: false,
+      quData: {
+
+      },
+      answerValues: [],
+      rightValues: [],
+      rightTags: []
+
+    }
+  },
+  created() {
+    this.repoId = this.$route.params.repoId
+
+    this.fetchNextQu()
+  },
+  methods: {
+    getQuType(data){
+      if (data == 1) {
+        return "单选题"
+      }else if (data == 2){
+        return "多选题"
+      }else if (data == 3){
+        return "判断题"
+      }
+    },
+    // 清理值
+    clearValues() {
+      this.answerValues = []
+      this.rightValues = []
+      this.analysisShow = false
+      this.rightTags = []
+    },
+
+    // 查找试卷详情
+    fetchQuDetail(id) {
+      // 当前赋值
+      this.quId = id
+      this.clearValues()
+
+      getQu(id).then(response => {
+        // 题目信息
+        this.quData = response.data
+
+        // 保存正确答案
+        this.quData.answerList.forEach((an, index) => {
+          an.abc = this.tags[index]
+
+          // 用户选定的
+          if (an.isRight) {
+            this.rightValues.push(an.answerId)
+            this.rightTags.push(an.abc)
+          }
+        })
+      })
+    },
+
+    fetchNextQu() {
+      // 查找下一个
+      nextQu({repoId: this.repoId,quId: this.quId}).then(response => {
+        this.fetchQuDetail(response.data)
+      })
+    },
+    onCancel() {
+      // this.$router.push({ name: 'ListTran' })
+      this.$router.push({ name: 'BookList' })
+    },
+
+    handNext() {
+      // 直接显示下一个
+      if (this.analysisShow) {
+        // 正确显示下一个
+        this.fetchNextQu()
+      } else {
+        // 直接判断正确性
+        if (this.rightValues.join(',') === this.answerValues.join(',')) {
+          this.$message({
+            message: '回答正确,你好棒哦!',
+            type: 'success'
+          })
+
+          // 正确显示下一个
+          this.fetchNextQu()
+        } else {
+          // 错误显示解析
+          this.analysisShow = true
+
+          this.$message({
+            message: '很遗憾,做错了呢,请参考答案解析!',
+            type: 'error'
+          })
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+  .qu-content div{
+    line-height: 30px;
+  }
+
+  .qu-analysis p{
+    color: #555; font-size: 14px
+  }
+  .qu-analysis-line{
+    margin-top: 20px; border-bottom: #eee 1px solid
+  }
+
+  .el-checkbox-group label,.el-radio-group label{
+    width: 100%;
+  }
+
+</style>
+