Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/dev_v2' into dev_v2

jinyw 2 mesiacov pred
rodič
commit
08b5a5d536
33 zmenil súbory, kde vykonal 1146 pridanie a 91 odobranie
  1. 17 11
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/common/utils/jep/JEPUtils.java
  2. 106 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/controller/train/TtAssessResultController.java
  3. 10 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/controller/train/TtTrainController.java
  4. 30 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/dto/TtAssessResultDto.java
  5. 112 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/train/TtAssessResult.java
  6. 1 1
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TrainParticipantRes.java
  7. 5 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TtTestPaperDetailVo.java
  8. 32 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TtTestPaperVo.java
  9. 0 1
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TtTrainVo.java
  10. 6 1
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/enums/InputTypeEnum.java
  11. 22 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/enums/train/TrainAssessCompleteFlagEnum.java
  12. 1 1
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/enums/train/TrainStatusEnum.java
  13. 64 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/mapper/TtAssessResultMapper.java
  14. 63 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/ITtAssessResultService.java
  15. 3 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/ITtTrainService.java
  16. 134 0
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/impl/TtAssessResultServiceImpl.java
  17. 21 12
      RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/impl/TtTrainServiceImpl.java
  18. 6 6
      RuoYi-Vue-fast-master/src/main/resources/application-druid.yml
  19. 107 0
      RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtAssessResultMapper.xml
  20. 9 7
      RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtQuestionDatabaseMapper.xml
  21. 1 1
      RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtTestPaperDetailMapper.xml
  22. 1 1
      RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtTrainMapper.xml
  23. 3 8
      RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtTrainTargetMapper.xml
  24. 6 0
      ruoyi-ui-vue2/README.md
  25. 44 0
      ruoyi-ui-vue2/src/api/train/assessResult.js
  26. 9 0
      ruoyi-ui-vue2/src/api/train/train.js
  27. 0 2
      ruoyi-ui-vue2/src/components/FloatingComponent/index.vue
  28. 42 35
      ruoyi-ui-vue2/src/layout/components/AppMain.vue
  29. 20 1
      ruoyi-ui-vue2/src/router/index.js
  30. 9 0
      ruoyi-ui-vue2/src/utils/lang/zh-cn.js
  31. 249 0
      ruoyi-ui-vue2/src/views/train/trainTarget/detail/testPaper.vue
  32. 6 2
      ruoyi-ui-vue2/src/views/train/trainTarget/index.vue
  33. 7 1
      ruoyi-ui-vue2/src/views/train/trainlist/detail/addTrain.vue

+ 17 - 11
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/common/utils/jep/JEPUtils.java

@@ -5,12 +5,13 @@ import com.ruoyi.project.business.dto.JEPCustomFunction.FindFunction;
 import com.ruoyi.project.business.dto.JEPCustomFunction.IFSFunction;
 import com.ruoyi.project.business.dto.JEPCustomFunction.MaxFunction;
 import com.ruoyi.project.business.dto.JEPCustomFunction.MinFunction;
-import com.ruoyi.project.business.dto.JEPNumberFactory.BigDecimalFactory;
 import com.ruoyi.project.business.enums.CalculateResultEnum;
 import com.ruoyi.project.business.enums.InputTypeEnum;
 import com.ruoyi.project.business.enums.JEPFuncEnum;
 import com.ruoyi.project.business.enums.JEPSplitEnum;
+
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -19,6 +20,7 @@ import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.math.NumberUtils;
@@ -61,7 +63,7 @@ public class JEPUtils {
      * @date: 2024/6/13 14:20
      **/
     public static Map<String, Object> calculate(String formula, Map<String, Object> map,
-        Map<String, String> paramInputType, Boolean isDebug) {
+                                                Map<String, String> paramInputType, Boolean isDebug) {
         Map<String, Object> result = new HashMap<>();
         List<String> encipherdList = new ArrayList<>();
 
@@ -94,12 +96,12 @@ public class JEPUtils {
         for (String param : map.keySet()) {
             if (!isDebug) {
                 log.info("加密后的入参:{}",
-                    JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey());
+                        JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey());
                 encipherdList.add(
-                    JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey());
+                        JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey());
                 jep.addVariable(
-                    JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey(),
-                    map.get(param));
+                        JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey() + param + JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey(),
+                        map.get(param));
             } else {
                 log.info("加密后的入参:{}", param);
                 encipherdList.add(param);
@@ -110,7 +112,7 @@ public class JEPUtils {
                         case INPUT:
                         case SELECT:
                         case INPUT_SELECT:
-                            String paramStr = (String)map.get(param);
+                            String paramStr = (String) map.get(param);
                             if (StringUtils.isNotBlank(paramStr)) {
                                 boolean isNumberFlag = NumberUtils.isCreatable(paramStr);
                                 jep.addVariable(param, isNumberFlag ? new BigDecimal(paramStr) : paramStr);
@@ -121,7 +123,8 @@ public class JEPUtils {
                             }
                             break;
                         case INPUT_NUMBER:
-                            String paramStr1 = (String)map.get(param);
+                        case INPUT_DECIMAL:
+                            String paramStr1 = (String) map.get(param);
                             if (Objects.nonNull(paramStr1)) {
                                 jep.addVariable(param, new BigDecimal(paramStr1));
                                 log.info("入参值:{}", new BigDecimal(paramStr1));
@@ -152,9 +155,12 @@ public class JEPUtils {
         result.put(CALCULATE_VALUE, jep.getValue() + "");
         try {
             BigDecimal objValue = new BigDecimal(jep.getValueAsObject() + "");
+            objValue = objValue.stripTrailingZeros();
+            objValue = objValue.setScale(2, RoundingMode.HALF_UP);
             result.put(OBJECT_CALCULATE_VALUE, objValue.stripTrailingZeros().toPlainString());
         } catch (Exception e) {
             result.put(OBJECT_CALCULATE_VALUE, jep.getValueAsObject() + "");
+            log.info("JEPUTils 计算结果 数值格式化失败,使用原始值:{}", jep.getValueAsObject());
             log.error("JEPUtils 计算结果格式化失败", e);
         }
         return result;
@@ -206,11 +212,11 @@ public class JEPUtils {
         //替换掉其中的 $_$、$@$ 按照长度 倒序排序
         if (paramList.size() > 0) {
             paramList = paramList.stream().map(item -> item.replace(JEPSplitEnum.PARAM_SPLIT_FLAG.getFuncKey(), ""))
-                .collect(Collectors.toList());
+                    .collect(Collectors.toList());
         }
         if (funcList.size() > 0) {
             funcList = funcList.stream().map(item -> item.replace(JEPSplitEnum.FUNC_SPLIT_FLAG.getFuncKey(), ""))
-                .collect(Collectors.toList());
+                    .collect(Collectors.toList());
         }
 
         paramList.sort((o1, o2) -> o2.length() - o1.length());
@@ -220,7 +226,7 @@ public class JEPUtils {
         for (String s : funcList) {
             JEPFuncEnum funcEnum = JEPFuncEnum.fromByKey(s);
             formula = formula.replace(JEPSplitEnum.FUNC_SPLIT_FLAG.getFuncKey(), "")
-                .replace(funcEnum.getFuncKey(), funcEnum.getFuncValue());
+                    .replace(funcEnum.getFuncKey(), funcEnum.getFuncValue());
 
         }
 

+ 106 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/controller/train/TtAssessResultController.java

@@ -0,0 +1,106 @@
+package com.ruoyi.project.business.controller.train;
+
+import java.util.List;
+import javax.servlet.http.HttpServletResponse;
+
+import com.ruoyi.project.business.domain.dto.TtAssessResultDto;
+import com.ruoyi.project.business.domain.train.TtAssessResult;
+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.business.service.ITtAssessResultService;
+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 ruoyi
+ * @date 2024-07-04
+ */
+@RestController
+@RequestMapping("/train/assessResult")
+public class TtAssessResultController extends BaseController
+{
+    @Autowired
+    private ITtAssessResultService ttAssessResultService;
+
+    /**
+     * 查询答卷列表
+     */
+    // @PreAuthorize("@ss.hasPermi('train:assessResult:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(TtAssessResult ttAssessResult)
+    {
+        startPage();
+        List<TtAssessResult> list = ttAssessResultService.selectTtAssessResultList(ttAssessResult);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出答卷列表
+     */
+    // @PreAuthorize("@ss.hasPermi('train:assessResult:export')")
+    @Log(title = "答卷", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, TtAssessResult ttAssessResult)
+    {
+        List<TtAssessResult> list = ttAssessResultService.selectTtAssessResultList(ttAssessResult);
+        ExcelUtil<TtAssessResult> util = new ExcelUtil<TtAssessResult>(TtAssessResult.class);
+        util.exportExcel(response, list, "答卷数据");
+    }
+
+    /**
+     * 获取答卷详细信息
+     */
+    // @PreAuthorize("@ss.hasPermi('train:assessResult:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return success(ttAssessResultService.selectTtAssessResultById(id));
+    }
+
+    /**
+     * 新增答卷
+     */
+    // @PreAuthorize("@ss.hasPermi('train:assessResult:add')")
+    @Log(title = "答卷", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody TtAssessResultDto ttAssessResultDto)
+    {
+
+        return toAjax(ttAssessResultService.insertTtAssessResult(ttAssessResultDto));
+    }
+
+    /**
+     * 修改答卷
+     */
+    // @PreAuthorize("@ss.hasPermi('train:assessResult:edit')")
+    @Log(title = "答卷", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody TtAssessResult ttAssessResult)
+    {
+        return toAjax(ttAssessResultService.updateTtAssessResult(ttAssessResult));
+    }
+
+    /**
+     * 删除答卷
+     */
+    // @PreAuthorize("@ss.hasPermi('train:assessResult:remove')")
+    @Log(title = "答卷", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(ttAssessResultService.deleteTtAssessResultByIds(ids));
+    }
+}

+ 10 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/controller/train/TtTrainController.java

@@ -11,6 +11,7 @@ import com.ruoyi.project.business.domain.train.TtTrain;
 import com.ruoyi.project.business.domain.train.TtTrainTarget;
 import com.ruoyi.project.business.domain.vo.train.TrainParticipantReq;
 import com.ruoyi.project.business.domain.vo.train.TrainParticipantRes;
+import com.ruoyi.project.business.domain.vo.train.TtTestPaperVo;
 import com.ruoyi.project.business.domain.vo.train.TtTrainVo;
 import com.ruoyi.project.business.service.ITtTrainService;
 import lombok.RequiredArgsConstructor;
@@ -99,4 +100,13 @@ public class TtTrainController extends BaseController {
         List<TrainParticipantRes> list = ttTrainService.selectParticipantTrainListCur(req);
         return getDataTable(list);
     }
+
+    /**
+     * 获取该培训 试卷详细
+     */
+    @GetMapping("/getTestPaperDetail")
+    public AjaxResult getTestPaperDetail(TtTrainDto ttTrainDto) {
+        TtTestPaperVo result = ttTrainService.selectTestPaperDetail(ttTrainDto);
+        return AjaxResult.success(result);
+    }
 }

+ 30 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/dto/TtAssessResultDto.java

@@ -0,0 +1,30 @@
+package com.ruoyi.project.business.domain.dto;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+import com.ruoyi.project.business.domain.train.TtAssessResult;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.List;
+
+/**
+ * 答卷对象 tt_assess_result
+ * 
+ * @author ruoyi
+ * @date 2024-07-04
+ */
+@Data
+public class TtAssessResultDto extends TtAssessResult
+{
+    /** 培训id */
+    private Long trainId;
+
+    /** 考试结果 */
+    private List<TtAssessResult> ttAssessResults;
+
+    /** 通过分数 */
+    private Long passScore;
+
+}

+ 112 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/train/TtAssessResult.java

@@ -0,0 +1,112 @@
+package com.ruoyi.project.business.domain.train;
+
+import lombok.EqualsAndHashCode;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import com.ruoyi.framework.web.domain.BaseEntity;
+
+/**
+ * 答卷对象 tt_assess_result
+ * 
+ * @author ruoyi
+ * @date 2024-07-04
+ */
+public class TtAssessResult extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** ID */
+    private Long id;
+
+    /** 试卷试题ID */
+    @Excel(name = "试卷试题ID")
+    private Long testPaperDetailId;
+
+    /** 培训对象ID */
+    @Excel(name = "培训对象ID")
+    private Long trainTargetId;
+
+    /** 答题内容 */
+    @Excel(name = "答题内容")
+    private String answerContent;
+
+    /** 得分 */
+    @Excel(name = "得分")
+    private Long score;
+
+    /** 删除标记(0:未删除  1:已删除) */
+    private String delFlag;
+
+    public void setId(Long id) 
+    {
+        this.id = id;
+    }
+
+    public Long getId() 
+    {
+        return id;
+    }
+    public void setTestPaperDetailId(Long testPaperDetailId) 
+    {
+        this.testPaperDetailId = testPaperDetailId;
+    }
+
+    public Long getTestPaperDetailId() 
+    {
+        return testPaperDetailId;
+    }
+    public void setTrainTargetId(Long trainTargetId) 
+    {
+        this.trainTargetId = trainTargetId;
+    }
+
+    public Long getTrainTargetId() 
+    {
+        return trainTargetId;
+    }
+    public void setAnswerContent(String answerContent) 
+    {
+        this.answerContent = answerContent;
+    }
+
+    public String getAnswerContent() 
+    {
+        return answerContent;
+    }
+    public void setScore(Long score) 
+    {
+        this.score = score;
+    }
+
+    public Long getScore() 
+    {
+        return score;
+    }
+    public void setDelFlag(String delFlag) 
+    {
+        this.delFlag = delFlag;
+    }
+
+    public String getDelFlag() 
+    {
+        return delFlag;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
+            .append("id", getId())
+            .append("testPaperDetailId", getTestPaperDetailId())
+            .append("trainTargetId", getTrainTargetId())
+            .append("answerContent", getAnswerContent())
+            .append("score", getScore())
+            .append("delFlag", getDelFlag())
+            .append("createTime", getCreateTime())
+            .append("createBy", getCreateBy())
+            .append("updateTime", getUpdateTime())
+            .append("updateBy", getUpdateBy())
+            .append("remark", getRemark())
+            .toString();
+    }
+}

+ 1 - 1
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TrainParticipantRes.java

@@ -88,7 +88,7 @@ public class TrainParticipantRes {
     /**
      * 考核状态,多选查询
      */
-    private String passFlag;
+    private String assessCompleteFlag;
 
 
     private String remark;

+ 5 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TtTestPaperDetailVo.java

@@ -28,6 +28,11 @@ public class TtTestPaperDetailVo extends TtTestPaperDetail {
     private String questionType;
 
     /**
+     * 标准答案
+     */
+    private String correctAnswer;
+
+    /**
      * 选项
      */
     private String option1;

+ 32 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TtTestPaperVo.java

@@ -0,0 +1,32 @@
+package com.ruoyi.project.business.domain.vo.train;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.ruoyi.framework.web.domain.BaseEntity;
+import com.ruoyi.project.business.domain.train.TtTestPaper;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+import lombok.experimental.Accessors;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * @TableName tt_test_paper
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class TtTestPaperVo extends TtTestPaper {
+
+    /**
+     * 试题详细信息
+     */
+    private List<TtTestPaperDetailVo> ttTestPaperDetailVoList;
+
+}

+ 0 - 1
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/domain/vo/train/TtTrainVo.java

@@ -1,7 +1,6 @@
 package com.ruoyi.project.business.domain.vo.train;
 
 import com.ruoyi.project.business.domain.train.*;
-import com.ruoyi.project.system.domain.SysUser;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 

+ 6 - 1
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/enums/InputTypeEnum.java

@@ -23,14 +23,19 @@ public enum InputTypeEnum {
      * 下拉选择
      */
     SELECT("02", "Select"),
+    /**
+     * 浮点数输入框
+     */
+    INPUT_DECIMAL("03", "InputFloat"),
 
     /**
      * 可输入下拉选择
-     * */
+     */
     INPUT_SELECT("04", "InputSelect"),
     ;
 
     private final String code;
+
     private final String desc;
 
 

+ 22 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/enums/train/TrainAssessCompleteFlagEnum.java

@@ -0,0 +1,22 @@
+package com.ruoyi.project.business.enums.train;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author zhouy
+ * 培训状态枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum TrainAssessCompleteFlagEnum {
+
+    ASSESS_NOT_COMPLETE("0","未参加"),
+    NOT_PASS("1","未通过"),
+    PASS("2","已通过");
+
+
+
+    private final String code;
+    private final String msg;
+}

+ 1 - 1
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/common/enums/train/TrainStatusEnum.java → RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/enums/train/TrainStatusEnum.java

@@ -1,4 +1,4 @@
-package com.ruoyi.project.common.enums.train;
+package com.ruoyi.project.business.enums.train;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;

+ 64 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/mapper/TtAssessResultMapper.java

@@ -0,0 +1,64 @@
+package com.ruoyi.project.business.mapper;
+
+import com.ruoyi.project.business.domain.train.TtAssessResult;
+
+import java.util.List;
+
+/**
+ * 答卷Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2024-07-04
+ */
+public interface TtAssessResultMapper 
+{
+    /**
+     * 查询答卷
+     * 
+     * @param id 答卷主键
+     * @return 答卷
+     */
+    public TtAssessResult selectTtAssessResultById(Long id);
+
+    /**
+     * 查询答卷列表
+     * 
+     * @param ttAssessResult 答卷
+     * @return 答卷集合
+     */
+    public List<TtAssessResult> selectTtAssessResultList(TtAssessResult ttAssessResult);
+
+    /**
+     * 新增答卷
+     * 
+     * @param ttAssessResult 答卷
+     * @return 结果
+     */
+    public int insertTtAssessResult(TtAssessResult ttAssessResult);
+
+    /**
+     * 修改答卷
+     * 
+     * @param ttAssessResult 答卷
+     * @return 结果
+     */
+    public int updateTtAssessResult(TtAssessResult ttAssessResult);
+
+    /**
+     * 删除答卷
+     * 
+     * @param id 答卷主键
+     * @return 结果
+     */
+    public int deleteTtAssessResultById(Long id);
+
+    /**
+     * 批量删除答卷
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteTtAssessResultByIds(Long[] ids);
+
+    public int insertTtAssessResultBatch(List<TtAssessResult> ttAssessResultList);
+}

+ 63 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/ITtAssessResultService.java

@@ -0,0 +1,63 @@
+package com.ruoyi.project.business.service;
+
+import com.ruoyi.project.business.domain.dto.TtAssessResultDto;
+import com.ruoyi.project.business.domain.train.TtAssessResult;
+
+import java.util.List;
+
+/**
+ * 答卷Service接口
+ * 
+ * @author ruoyi
+ * @date 2024-07-04
+ */
+public interface ITtAssessResultService 
+{
+    /**
+     * 查询答卷
+     * 
+     * @param id 答卷主键
+     * @return 答卷
+     */
+    public TtAssessResult selectTtAssessResultById(Long id);
+
+    /**
+     * 查询答卷列表
+     * 
+     * @param ttAssessResult 答卷
+     * @return 答卷集合
+     */
+    public List<TtAssessResult> selectTtAssessResultList(TtAssessResult ttAssessResult);
+
+    /**
+     * 新增答卷
+     * 
+     * @param ttAssessResultDto 答卷
+     * @return 结果
+     */
+    public int insertTtAssessResult(TtAssessResultDto ttAssessResultDto);
+
+    /**
+     * 修改答卷
+     * 
+     * @param ttAssessResult 答卷
+     * @return 结果
+     */
+    public int updateTtAssessResult(TtAssessResult ttAssessResult);
+
+    /**
+     * 批量删除答卷
+     * 
+     * @param ids 需要删除的答卷主键集合
+     * @return 结果
+     */
+    public int deleteTtAssessResultByIds(Long[] ids);
+
+    /**
+     * 删除答卷信息
+     * 
+     * @param id 答卷主键
+     * @return 结果
+     */
+    public int deleteTtAssessResultById(Long id);
+}

+ 3 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/ITtTrainService.java

@@ -5,6 +5,7 @@ import com.ruoyi.project.business.domain.dto.TtTrainDto;
 import com.ruoyi.project.business.domain.train.TtTrain;
 import com.ruoyi.project.business.domain.vo.train.TrainParticipantReq;
 import com.ruoyi.project.business.domain.vo.train.TrainParticipantRes;
+import com.ruoyi.project.business.domain.vo.train.TtTestPaperVo;
 import com.ruoyi.project.business.domain.vo.train.TtTrainVo;
 
 import java.util.List;
@@ -68,4 +69,6 @@ public interface ITtTrainService extends IService<TtTrain> {
      * 查询当前用户参与的培训列表
      */
     List<TrainParticipantRes> selectParticipantTrainListCur(TrainParticipantReq req);
+
+    TtTestPaperVo selectTestPaperDetail(TtTrainDto ttTrainDto);
 }

+ 134 - 0
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/impl/TtAssessResultServiceImpl.java

@@ -0,0 +1,134 @@
+package com.ruoyi.project.business.service.impl;
+
+import java.lang.annotation.Target;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.project.business.domain.dto.TtAssessResultDto;
+import com.ruoyi.project.business.domain.train.TtAssessResult;
+import com.ruoyi.project.business.domain.train.TtTrainTarget;
+import com.ruoyi.project.business.domain.vo.train.TtTrainTargetVo;
+import com.ruoyi.project.business.enums.train.TrainAssessCompleteFlagEnum;
+import com.ruoyi.project.business.mapper.TtTrainTargetMapper;
+import com.ruoyi.project.business.service.ITtTrainTargetService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.project.business.mapper.TtAssessResultMapper;
+import com.ruoyi.project.business.service.ITtAssessResultService;
+
+/**
+ * 答卷Service业务层处理
+ *
+ * @author ruoyi
+ * @date 2024-07-04
+ */
+@Service
+public class TtAssessResultServiceImpl implements ITtAssessResultService {
+    @Autowired
+    private TtAssessResultMapper ttAssessResultMapper;
+    @Autowired
+    private TtTrainTargetMapper ttTrainTargetMapper;
+
+    /**
+     * 查询答卷
+     *
+     * @param id 答卷主键
+     * @return 答卷
+     */
+    @Override
+    public TtAssessResult selectTtAssessResultById(Long id) {
+        return ttAssessResultMapper.selectTtAssessResultById(id);
+    }
+
+    /**
+     * 查询答卷列表
+     *
+     * @param ttAssessResult 答卷
+     * @return 答卷
+     */
+    @Override
+    public List<TtAssessResult> selectTtAssessResultList(TtAssessResult ttAssessResult) {
+        return ttAssessResultMapper.selectTtAssessResultList(ttAssessResult);
+    }
+
+    /**
+     * 新增答卷
+     *
+     * @param ttAssessResultDto 答卷
+     * @return 结果
+     */
+    @Override
+    public int insertTtAssessResult(TtAssessResultDto ttAssessResultDto) {
+        // 获取登录人
+        Long loginUserId = SecurityUtils.getLoginUser().getUserId();
+
+        List<TtAssessResult> ttAssessResultList = ttAssessResultDto.getTtAssessResults();
+        ttAssessResultList.forEach(item -> {
+            item.setTrainTargetId(loginUserId);
+            item.setDelFlag("0");
+            // 设置创建者创建时间,更新这更新时间
+            item.setCreateBy(loginUserId.toString());
+            item.setCreateTime(DateUtils.getNowDate());
+            item.setUpdateBy(loginUserId.toString());
+            item.setUpdateTime(DateUtils.getNowDate());
+        });
+        ttAssessResultMapper.insertTtAssessResultBatch(ttAssessResultList);
+
+        // 获取考核id
+        QueryWrapper<TtTrainTarget> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("train_id", ttAssessResultDto.getTrainId());
+        queryWrapper.eq("train_target_id", loginUserId);
+        TtTrainTarget targetUpdateInfo = new TtTrainTarget();
+
+        // 判断考核是否通过
+        // 获取总分
+        Long totalScore = 0L;
+        for (TtAssessResult ttAssessResult : ttAssessResultList) {
+            totalScore += ttAssessResult.getScore();
+        }
+        if(totalScore >= ttAssessResultDto.getPassScore()){
+            targetUpdateInfo.setAssessCompleteFlag(TrainAssessCompleteFlagEnum.PASS.getCode());
+        }else {
+            targetUpdateInfo.setAssessCompleteFlag(TrainAssessCompleteFlagEnum.NOT_PASS.getCode());
+        }
+//        targetUpdateInfo.setAssessCompleteFlag(TrainAssessCompleteFlagEnum.ASSESS_COMPLETE.getCode());
+
+        return  ttTrainTargetMapper.update(targetUpdateInfo,queryWrapper);
+    }
+
+    /**
+     * 修改答卷
+     *
+     * @param ttAssessResult 答卷
+     * @return 结果
+     */
+    @Override
+    public int updateTtAssessResult(TtAssessResult ttAssessResult) {
+        ttAssessResult.setUpdateTime(DateUtils.getNowDate());
+        return ttAssessResultMapper.updateTtAssessResult(ttAssessResult);
+    }
+
+    /**
+     * 批量删除答卷
+     *
+     * @param ids 需要删除的答卷主键
+     * @return 结果
+     */
+    @Override
+    public int deleteTtAssessResultByIds(Long[] ids) {
+        return ttAssessResultMapper.deleteTtAssessResultByIds(ids);
+    }
+
+    /**
+     * 删除答卷信息
+     *
+     * @param id 答卷主键
+     * @return 结果
+     */
+    @Override
+    public int deleteTtAssessResultById(Long id) {
+        return ttAssessResultMapper.deleteTtAssessResultById(id);
+    }
+}

+ 21 - 12
RuoYi-Vue-fast-master/src/main/java/com/ruoyi/project/business/service/impl/TtTrainServiceImpl.java

@@ -13,7 +13,7 @@ import com.ruoyi.project.business.domain.train.*;
 import com.ruoyi.project.business.domain.vo.train.*;
 import com.ruoyi.project.business.mapper.*;
 import com.ruoyi.project.business.service.ITtTrainService;
-import com.ruoyi.project.common.enums.train.TrainStatusEnum;
+import com.ruoyi.project.business.enums.train.TrainStatusEnum;
 import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.service.ISysUserService;
 import lombok.RequiredArgsConstructor;
@@ -159,9 +159,6 @@ public class TtTrainServiceImpl extends ServiceImpl<TtTrainMapper, TtTrain> impl
                 ttTrainTarget.setTargetPersonId(userId);
                 // 培训id
                 ttTrainTarget.setTrainId(ttTrain.getId());
-                ttTrainTarget.setTrainCompleteFlag("0");
-                ttTrainTarget.setAssessCompleteFlag("0");
-                ttTrainTarget.setPassFlag("0");
                 ttTrainTarget.setDelFlag("0");
                 // 设置创建者创建时间,更新者更新时间
                 ttTrainTarget.setCreateBy(loginUserId.toString());
@@ -312,9 +309,8 @@ public class TtTrainServiceImpl extends ServiceImpl<TtTrainMapper, TtTrain> impl
      * 根据主数据id 删除该主数据的子表数据,用于编辑
      *
      * @param ids 需要删除的培训考核主键
-     * @return 结果
      */
-    private int deleteTtTrainByIdToEdit(Long[] ids) {
+    private void deleteTtTrainByIdToEdit(Long[] ids) {
 
         // 一、根据培训id 删除培训对象列表
         int result = ttTrainTargetMapper.deleteTtTrainTargetByTrainIds(ids);
@@ -349,9 +345,8 @@ public class TtTrainServiceImpl extends ServiceImpl<TtTrainMapper, TtTrain> impl
             // 设置更新字段
             TtTestPaperDetail ttTestPaperDetail = new TtTestPaperDetail();
             ttTestPaperDetail.setDelFlag("1");
-            return ttTestPaperMapper.update(ttTestPaper, testQueryWrapper);
+            ttTestPaperMapper.update(ttTestPaper, testQueryWrapper);
         }
-       return result;
     }
 
     /**
@@ -373,10 +368,6 @@ public class TtTrainServiceImpl extends ServiceImpl<TtTrainMapper, TtTrain> impl
                 ttTrainTarget.setTargetPersonId(userId);
                 // 培训id
                 ttTrainTarget.setTrainId(ttTrainDto.getId());
-                // 0:未完成 1:完成
-                ttTrainTarget.setTrainCompleteFlag("0");
-                ttTrainTarget.setAssessCompleteFlag("0");
-                ttTrainTarget.setPassFlag("0");
                 ttTrainTarget.setDelFlag("0");
                 // 设置创建者创建时间,更新者更新时间
                 ttTrainTarget.setCreateBy(loginUserId.toString());
@@ -466,4 +457,22 @@ public class TtTrainServiceImpl extends ServiceImpl<TtTrainMapper, TtTrain> impl
         req.setUserId(SecurityUtils.getLoginUser().getUserId());
         return baseMapper.selectParticipantTrainListUserId(req);
     }
+
+    @Override
+    public TtTestPaperVo selectTestPaperDetail(TtTrainDto ttTrainDto) {
+
+        // 1. 根据试卷id 获取试卷详细 及试卷试题详细
+        QueryWrapper<TtTestPaper> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("train_id", ttTrainDto.getId());
+        TtTestPaper ttTestPaper = ttTestPaperMapper.selectOne(queryWrapper);
+        TtTestPaperVo result = new TtTestPaperVo();
+        BeanUtils.copyProperties(ttTestPaper, result);
+
+        // 2. 根据试卷id 获取试题详细
+        TtTestPaperDetail ttTestPaperDetail = new TtTestPaperDetail();
+        ttTestPaperDetail.setTestPaperId(ttTestPaper.getId());
+        result.setTtTestPaperDetailVoList(ttTestPaperDetailMapper.selectTestPaperDetailList(ttTestPaperDetail));
+
+        return result;
+    }
 }

+ 6 - 6
RuoYi-Vue-fast-master/src/main/resources/application-druid.yml

@@ -24,13 +24,13 @@ spring:
       # 主库数据源
       master:
         # 本地开发环境
-#        url: jdbc:mysql://172.28.186.146:3306/koyo-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-#        username: root
-#        password: qaz1997417
+        url: jdbc:mysql://172.28.186.146:3306/koyo-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        username: root
+        password: qaz1997417
         #         # 阿里云环境
-        url: jdbc:mysql://rm-uf62e36t771a0f9yoao.mysql.rds.aliyuncs.com:3306/koyo_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
-        username: koyo_admin_user
-        password: Gold@1234
+#        url: jdbc:mysql://rm-uf62e36t771a0f9yoao.mysql.rds.aliyuncs.com:3306/koyo_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+#        username: koyo_admin_user
+#        password: Gold@1234
       # 从库数据源
       slave:
         # 从数据源开关/默认关闭

+ 107 - 0
RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtAssessResultMapper.xml

@@ -0,0 +1,107 @@
+<?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.business.mapper.TtAssessResultMapper">
+    
+    <resultMap type="TtAssessResult" id="TtAssessResultResult">
+        <result property="id"    column="id"    />
+        <result property="testPaperDetailId"    column="test_paper_detail_id"    />
+        <result property="trainTargetId"    column="train_target_id"    />
+        <result property="answerContent"    column="answer_content"    />
+        <result property="score"    column="score"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectTtAssessResultVo">
+        select id, test_paper_detail_id, train_target_id, answer_content, score, del_flag, create_time, create_by, update_time, update_by, remark from tt_assess_result
+    </sql>
+
+    <select id="selectTtAssessResultList" parameterType="TtAssessResult" resultMap="TtAssessResultResult">
+        <include refid="selectTtAssessResultVo"/>
+        <where>  
+            <if test="testPaperDetailId != null "> and test_paper_detail_id = #{testPaperDetailId}</if>
+            <if test="trainTargetId != null "> and train_target_id = #{trainTargetId}</if>
+            <if test="answerContent != null  and answerContent != ''"> and answer_content = #{answerContent}</if>
+            <if test="score != null "> and score = #{score}</if>
+        </where>
+    </select>
+    
+    <select id="selectTtAssessResultById" parameterType="Long" resultMap="TtAssessResultResult">
+        <include refid="selectTtAssessResultVo"/>
+        where id = #{id}
+    </select>
+        
+    <insert id="insertTtAssessResult" parameterType="TtAssessResult" useGeneratedKeys="true" keyProperty="id">
+        insert into tt_assess_result
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="testPaperDetailId != null">test_paper_detail_id,</if>
+            <if test="trainTargetId != null">train_target_id,</if>
+            <if test="answerContent != null">answer_content,</if>
+            <if test="score != null">score,</if>
+            <if test="delFlag != null">del_flag,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="remark != null">remark,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="testPaperDetailId != null">#{testPaperDetailId},</if>
+            <if test="trainTargetId != null">#{trainTargetId},</if>
+            <if test="answerContent != null">#{answerContent},</if>
+            <if test="score != null">#{score},</if>
+            <if test="delFlag != null">#{delFlag},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="remark != null">#{remark},</if>
+         </trim>
+    </insert>
+
+    <insert id="insertTtAssessResultBatch">
+        insert into tt_assess_result
+            (test_paper_detail_id, train_target_id, answer_content, score, del_flag, create_time, create_by, update_time, update_by, remark)
+        values
+            <foreach item="item" collection="list" separator=",">
+                (#{item.testPaperDetailId}, #{item.trainTargetId},
+                 #{item.answerContent}, #{item.score}, #{item.delFlag},
+                 #{item.createTime}, #{item.createBy}, #{item.updateTime},
+                 #{item.updateBy}, #{item.remark})
+            </foreach>
+    </insert>
+
+    <update id="updateTtAssessResult" parameterType="TtAssessResult">
+        update tt_assess_result
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="testPaperDetailId != null">test_paper_detail_id = #{testPaperDetailId},</if>
+            <if test="trainTargetId != null">train_target_id = #{trainTargetId},</if>
+            <if test="answerContent != null">answer_content = #{answerContent},</if>
+            <if test="score != null">score = #{score},</if>
+            <if test="delFlag != null">del_flag = #{delFlag},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="remark != null">remark = #{remark},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteTtAssessResultById" parameterType="Long">
+        delete from tt_assess_result where id = #{id}
+    </delete>
+
+    <delete id="deleteTtAssessResultByIds" parameterType="String">
+        delete from tt_assess_result where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 9 - 7
RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtQuestionDatabaseMapper.xml

@@ -101,12 +101,14 @@
     <select id="selectRandomQuestionListByNum"
             resultType="com.ruoyi.project.business.domain.train.TtQuestionDatabase">
         SELECT * FROM tt_question_database
-                 WHERE status != '1'
-                    and question_type = #{questionType}
-                    and id not in
-                 <foreach collection="existIds" item="item" open="(" separator="," close=")">
-                     #{item}
-                 </foreach>
-                 ORDER BY RAND() LIMIT #{questionTypeNum};
+        WHERE status != '1'
+        and question_type = #{questionType}
+        <if test="existIds != null">
+            and id not in
+            <foreach collection="existIds" item="item" open="(" separator="," close=")">
+                #{item}
+            </foreach>
+        </if>
+        ORDER BY RAND() LIMIT #{questionTypeNum};
     </select>
 </mapper>

+ 1 - 1
RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtTestPaperDetailMapper.xml

@@ -35,7 +35,7 @@
 
     <select id="selectTestPaperDetailList"
             resultType="com.ruoyi.project.business.domain.vo.train.TtTestPaperDetailVo">
-        select pd.*,qd.question_context,qd.question_type,qd.option1,qd.option2,qd.option3,
+        select pd.*,qd.correct_answer,qd.question_context,qd.question_type,qd.option1,qd.option2,qd.option3,
                qd.option4,qd.option5,qd.option6,qd.option7,qd.option8,qd.option9,qd.option10
         from tt_test_paper_detail pd
         left join tt_question_database qd on pd.question_id = qd.id and qd.del_flag != '1'

+ 1 - 1
RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtTrainMapper.xml

@@ -120,7 +120,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             t.create_time,
             t.create_by,
             u.nick_name as create_by_name,
-            tt.pass_flag,
+            tt.assess_complete_flag,
             t.remark
         from tt_train t
             join tt_train_target tt on t.id = tt.train_id

+ 3 - 8
RuoYi-Vue-fast-master/src/main/resources/mybatis/business/train/TtTrainTargetMapper.xml

@@ -10,7 +10,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="targetPersonId"    column="target_person_id"    />
         <result property="trainCompleteFlag"    column="train_complete_flag"    />
         <result property="assessCompleteFlag"    column="assess_complete_flag"    />
-        <result property="passFlag"    column="pass_flag"    />
         <result property="delFlag"    column="del_flag"    />
         <result property="createTime"    column="create_time"    />
         <result property="createBy"    column="create_by"    />
@@ -20,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectTtTrainTargetVo">
-        select id, train_id, target_person_id, train_complete_flag, assess_complete_flag, pass_flag, del_flag, create_time, create_by, update_time, update_by, remark from tt_train_target
+        select id, train_id, target_person_id, train_complete_flag, assess_complete_flag, del_flag, create_time, create_by, update_time, update_by, remark from tt_train_target
     </sql>
 
     <sql id="selectTtTrainTargetVoWithNickname">
@@ -38,7 +37,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="targetPersonId != null "> and t.target_person_id = #{targetPersonId}</if>
             <if test="trainCompleteFlag != null  and trainCompleteFlag != ''"> and t.train_complete_flag = #{trainCompleteFlag}</if>
             <if test="assessCompleteFlag != null  and assessCompleteFlag != ''"> and t.assess_complete_flag = #{assessCompleteFlag}</if>
-            <if test="passFlag != null  and passFlag != ''"> and t.pass_flag = #{passFlag}</if>
         </where>
     </select>
     
@@ -54,7 +52,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="targetPersonId != null">target_person_id,</if>
             <if test="trainCompleteFlag != null">train_complete_flag,</if>
             <if test="assessCompleteFlag != null">assess_complete_flag,</if>
-            <if test="passFlag != null">pass_flag,</if>
             <if test="delFlag != null">del_flag,</if>
             <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
@@ -67,7 +64,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="targetPersonId != null">#{targetPersonId},</if>
             <if test="trainCompleteFlag != null">#{trainCompleteFlag},</if>
             <if test="assessCompleteFlag != null">#{assessCompleteFlag},</if>
-            <if test="passFlag != null">#{passFlag},</if>
             <if test="delFlag != null">#{delFlag},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
@@ -80,10 +76,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <insert id="insertTtTrainTargetBatch" parameterType="TtTrainTarget" useGeneratedKeys="true" keyProperty="id">
         insert into tt_train_target
         (train_id,target_person_id,train_complete_flag,assess_complete_flag,
-         pass_flag,del_flag,create_time,create_by,update_time,update_by,remark) values
+        del_flag,create_time,create_by,update_time,update_by,remark) values
         <foreach collection="list" item="item" index="index" separator=",">
             (#{item.trainId},#{item.targetPersonId},#{item.trainCompleteFlag},#{item.assessCompleteFlag},
-                #{item.passFlag},#{item.delFlag},#{item.createTime},#{item.createBy},#{item.updateTime},#{item.updateBy},#{item.remark})
+                #{item.delFlag},#{item.createTime},#{item.createBy},#{item.updateTime},#{item.updateBy},#{item.remark})
         </foreach>
     </insert>
 
@@ -94,7 +90,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="targetPersonId != null">target_person_id = #{targetPersonId},</if>
             <if test="trainCompleteFlag != null">train_complete_flag = #{trainCompleteFlag},</if>
             <if test="assessCompleteFlag != null">assess_complete_flag = #{assessCompleteFlag},</if>
-            <if test="passFlag != null">pass_flag = #{passFlag},</if>
             <if test="delFlag != null">del_flag = #{delFlag},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>

+ 6 - 0
ruoyi-ui-vue2/README.md

@@ -194,4 +194,10 @@ computed: {
       }
 ```
 
+### AppMain 注意事项
+```bash
+   全局 ”chatRoomImage“ class类不可额外定义,已经服务于聊天室图片预览,对于这个类的dom元素添加click监听事件,阻止了click事件传递;
+  messagePreview 方法不能删除
+```
+
 

+ 44 - 0
ruoyi-ui-vue2/src/api/train/assessResult.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询答卷列表
+export function listAssessResult(query) {
+  return request({
+    url: '/train/assessResult/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询答卷详细
+export function getAssessResult(id) {
+  return request({
+    url: '/train/assessResult/' + id,
+    method: 'get'
+  })
+}
+
+// 新增答卷
+export function addAssessResult(data) {
+  return request({
+    url: '/train/assessResult',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改答卷
+export function updateAssessResult(data) {
+  return request({
+    url: '/train/assessResult',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除答卷
+export function delAssessResult(id) {
+  return request({
+    url: '/train/assessResult/' + id,
+    method: 'delete'
+  })
+}

+ 9 - 0
ruoyi-ui-vue2/src/api/train/train.js

@@ -60,3 +60,12 @@ export function participantTrainPage(query) {
     params: query
   })
 }
+
+// 查询【获取试卷详细】
+export function getTestPaperDetail(query) {
+  return request({
+    url: '/train/train/getTestPaperDetail',
+    method: 'get',
+    params: query
+  })
+}

+ 0 - 2
ruoyi-ui-vue2/src/components/FloatingComponent/index.vue

@@ -402,7 +402,6 @@ export default {
       }
     },
     handleDelete(item){
-      console.log('handleDelete', item);
       messageChatUserDelete({
         messageChatId:item.id
       }).then(res=>{
@@ -415,7 +414,6 @@ export default {
 
     async toActive(item) {
       this.activeChatRoom = item;
-      console.log('toActive', item);
       this.activeChatRoom.hasUnReadMessage = false;
       let res = await readChatMessage(item);
       this.totalQty = res.data;

+ 42 - 35
ruoyi-ui-vue2/src/layout/components/AppMain.vue

@@ -2,75 +2,76 @@
   <section class="app-main">
     <transition name="fade-transform" mode="out-in">
       <keep-alive :include="cachedViews">
-        <router-view v-if="!$route.meta.link" :key="key" />
+        <router-view v-if="!$route.meta.link" :key="key"/>
       </keep-alive>
     </transition>
-    <iframe-toggle />
-    <floating-component />
+    <iframe-toggle/>
+    <floating-component/>
     <DialogPage ref="auditStatusDialog" title="审批履历" maxWidth="500px" @close="auditHistory = []">
       <template>
         <div v-loading="loading" class="container">
           <div v-if="auditHistory && auditHistory.length > 0" v-for="item, index in auditHistory" style="font-size: 0;">
             <div class="left">
-              <div class="step"><img :src="item.status == AuditStatus.AUDITING ? audit : finished" width="25px" /></div>
+              <div class="step"><img :src="item.status == AuditStatus.AUDITING ? audit : finished" width="25px"/></div>
               <div class="line" v-if="index !== (auditHistory.length - 1)"
-                :style="{ height: item.auditUser ? '180px' : '30px' }"></div>
+                   :style="{ height: item.auditUser ? '180px' : '30px' }"></div>
             </div>
             <div class="right">
               <div class="head">
                 <span class="date" v-if="item.auditTime">{{
-                  item.auditTime.split(' ')[0].replaceAll('-', '/')
-                }}<br /><span class="time">{{
-                    item.auditTime.split(' ')[1]
-                  }}</span></span>
+                    item.auditTime.split(' ')[0].replaceAll('-', '/')
+                  }}<br/><span class="time">{{
+                      item.auditTime.split(' ')[1]
+                    }}</span></span>
                 <span
                   :class="{ status: true, success: item.status == AuditStatus.PASS, fail: item.status == AuditStatus.REJECT, auditing: item.status == AuditStatus.AUDITING, }">
                   {{ item.desc }}
                   <img v-if="item.status == AuditStatus.AUDITING" :src="alert" width="25px"
-                    style="display: inline-block;vertical-align: bottom;cursor: pointer;"
-                    @click="handleAlert(item.taskId)" />
+                       style="display: inline-block;vertical-align: bottom;cursor: pointer;"
+                       @click="handleAlert(item.taskId)"/>
                 </span>
 
               </div>
               <div class="audit-info" v-if="item.auditUser">
                 <span class="name">{{ item.nickName }}</span>
                 <img :src="pin" width="25px" class="attachment"
-                  v-if="item.attachmentList && item.attachmentList.length > 0"
-                  @click="$refs.auditAttachmentDialog.open(null, { attachmentList: item.attachmentList })" />
+                     v-if="item.attachmentList && item.attachmentList.length > 0"
+                     @click="$refs.auditAttachmentDialog.open(null, { attachmentList: item.attachmentList })"/>
                 <div class="remark">{{ item.remark }}</div>
               </div>
             </div>
           </div>
           <Col v-if="auditHistory && auditHistory.length === 0" class="NoDataEmpty">
-          <Row>
-            <Col style="text-align: center">
-            <el-image :src="require('@/assets/images/index/NoUnReadMsg.svg')" />
-            </Col>
-            <Col style="text-align: center">
-            <span class="noDataEmptyText">{{ $t('common.noDataEmptyText') }}</span>
-            </Col>
-          </Row>
+            <Row>
+              <Col style="text-align: center">
+                <el-image :src="require('@/assets/images/index/NoUnReadMsg.svg')"/>
+              </Col>
+              <Col style="text-align: center">
+                <span class="noDataEmptyText">{{ $t('common.noDataEmptyText') }}</span>
+              </Col>
+            </Row>
           </Col>
         </div>
       </template>
     </DialogPage>
     <DialogPage ref="auditAttachmentDialog" title="附件" maxWidth="550px" disabled label-position="top">
-      <FileUploadItem label="附件" prop="attachmentList" :module="module" :func="func" :category="category" />
+      <FileUploadItem label="附件" prop="attachmentList" :module="module" :func="func" :category="category"/>
     </DialogPage>
 
     <DialogPage ref="dialog" :title="title()" maxWidth="800px" :disabled="disabled" label-position="top" :header="true">
       <FileUploadItem :button-style="{ marginLeft: '5px' }" :label="label" class="normalClass" :prop="prop"
-        :module="module" :func="func" :category="category" :disabled="disabled" />
+                      :module="module" :func="func" :category="category" :disabled="disabled"/>
     </DialogPage>
     <DialogPage ref="dialogWatch" :title="title()" maxWidth="550px" :disabled="disabled" label-position="top"
-      :header="true">
+                :header="true">
       <FileUploadItem :button-style="{ marginLeft: '5px' }" :label="label" class="watchClass" :prop="prop"
-        :module="module" :func="func" :category="category" :disabled="disabled" />
+                      :module="module" :func="func" :category="category" :disabled="disabled"/>
     </DialogPage>
     <!--聊天室 图片预览-->
-    <DialogPage @close.stop="e=> handleCloseStop(e)"  ref="previewDialog" :modalAppendToBody="true" :appendToBody="true" :close-on-click-modal="true"
-      :title="'图片预览'" maxWidth="550px" :disabled="disabled" label-position="top">
-      <el-image :src="imageSrc" />
+    <DialogPage class="chatRoomImage" ref="previewDialog" :modalAppendToBody="true" :appendToBody="true"
+                :close-on-click-modal="true"
+                :title="'图片预览'" maxWidth="550px" :disabled="disabled" label-position="top">
+      <el-image :src="imageSrc"/>
     </DialogPage>
 
 
@@ -80,7 +81,7 @@
 <script>
 import iframeToggle from "./IframeToggle/index";
 import FloatingComponent from "../../components/FloatingComponent/index";
-import { getAuditHistory, alertAudit } from "@/api/audit/flow.js";
+import {getAuditHistory, alertAudit} from "@/api/audit/flow.js";
 import finished from "@/assets/audit/finished-green.png";
 import audit from "@/assets/audit/audit.png";
 import alert from "@/assets/audit/alert.png";
@@ -89,7 +90,7 @@ import DialogPage from "../../components/element/Dialog.vue";
 
 export default {
   name: "AppMain",
-  components: { DialogPage, FloatingComponent, iframeToggle },
+  components: {DialogPage, FloatingComponent, iframeToggle},
   provide: function () {
     return {
       main: this,
@@ -120,6 +121,12 @@ export default {
       return this.$route.path;
     },
   },
+  mounted() {
+    const dialog = document.getElementsByClassName("chatRoomImage")[0]
+    dialog.addEventListener('click', (e) => {
+      e.stopPropagation();
+    })
+  },
   methods: {
     showAuditHistory(businessId, auditFlows, module, func, category) {
       this.module = module;
@@ -127,7 +134,7 @@ export default {
       this.category = category;
       this.loading = true;
       this.$refs.auditStatusDialog.open();
-      getAuditHistory({ businessId, auditFlows, module, func, category })
+      getAuditHistory({businessId, auditFlows, module, func, category})
         .then((res) => {
           if (res.code === 200) {
             this.auditHistory = res.rows;
@@ -160,7 +167,7 @@ export default {
             this.$set(row, this.prop, form[this.prop]);
             this.showIcon = true;
           },
-          { [this.prop]: row[this.prop] }
+          {[this.prop]: row[this.prop]}
         );
       } else {
         this.$refs.dialog.open(
@@ -168,7 +175,7 @@ export default {
             this.$set(row, this.prop, form[this.prop]);
             this.showIcon = true;
           },
-          { [this.prop]: row[this.prop] }
+          {[this.prop]: row[this.prop]}
         );
       }
     },
@@ -191,7 +198,7 @@ export default {
   background: #f1f5f9;
 }
 
-.fixed-header+.app-main {
+.fixed-header + .app-main {
   padding-top: 50px;
 }
 
@@ -202,7 +209,7 @@ export default {
     min-height: calc(100vh - 94px);
   }
 
-  .fixed-header+.app-main {
+  .fixed-header + .app-main {
     padding-top: 84px;
   }
 }

+ 20 - 1
ruoyi-ui-vue2/src/router/index.js

@@ -365,6 +365,7 @@ export const dynamicRoutes = [
       }
     ]
   },
+  // 培训一览(发起)
   {
     path: '/train/trainlist/detail',
     component: Layout,
@@ -403,7 +404,25 @@ export const dynamicRoutes = [
       }
     ]
   },
-
+  // 培训参与
+  {
+    path: '/train/trainTarget/detail',
+    component: Layout,
+    hidden: true,
+    permissions: ['train:trainTarget:exam'],
+    children: [
+      {
+        path: 'testPaper',
+        component: () => import('@/views/train/trainTarget/detail/testPaper.vue'),
+        name: 'TestPaper',
+        meta: {
+          title: '参与考核',
+          activeMenu: '/testPaper',
+          keepAlive: true,
+        }
+      },
+    ]
+  },
 ]
 
 // 防止连续点击多次路由报错

+ 9 - 0
ruoyi-ui-vue2/src/utils/lang/zh-cn.js

@@ -1094,6 +1094,15 @@
     questionUnit:'道',
     scoreUnit:'分',
     randomSelectError:'获取题目失败,请检查题库是否为空',
+    // 6.26
+    previewError:'预览失败,请添加题目',
+    questionTypeNum:'题数',
+    passScoreMsgError:'通过分数要在选择题目总分的范围内',
+    // 7.4
+    assessResultConfirm : '试卷提交成功',
+    examInProgress : '考核',
+    // 7.5
+    isConfirmTest:'是否确认提交试卷'
   },
   question: {
     questionContext:'题目名称',

+ 249 - 0
ruoyi-ui-vue2/src/views/train/trainTarget/detail/testPaper.vue

@@ -0,0 +1,249 @@
+<template>
+  <div class="account-container" v-loading="loading">
+    <div class="top" style="margin-bottom: 40px">
+      <div class="account-title">{{ this.$t('trainList.examInProgress') }}</div>
+      <div class="button-group">
+        <el-button type="primary" icon="el-icon-circle-check" @click="submitForm"
+                   :loading="submitLoading">{{ $t('common.confirm') }}
+        </el-button>
+        <el-button @click="cancel" icon="el-icon-remove-outline">
+          {{ $t('common.cancel') }}
+        </el-button>
+      </div>
+    </div>
+    <div class="content">
+      <div class="card card-full">
+        <div class="test-title">{{ assessmentPlanForm.testPaperName }}</div>
+
+        <el-form ref="form" :model="form" :rules="rules" label-position="top" style="margin: 0 0 0 200px">
+          <el-form-item
+              :label="index + 1 + '. ' + item.questionContext + '('+item.questionTypeName + ',' + $t('question.score') + item.score + $t('trainList.scoreUnit')+')'"
+              v-for="(item,index) in questionList" :key="index" prop="answer">
+            <!--            单选/判断--><!--v-model="demo['radio' + index]"-->
+            <el-radio-group v-if="item.questionType === '01' || item.questionType === '03'"
+                            v-model="item.answer">
+              <el-radio v-for="(option,optionIndex) in optionListFiltered(item)" :key="optionIndex" :label="option">
+                {{ letterFromIndex(optionIndex) }}. {{ option }}
+              </el-radio>
+            </el-radio-group>
+
+            <!--            多选-->
+            <el-checkbox-group v-else-if="item.questionType === '02'" v-model="item.answer">
+              <el-checkbox v-for="(option,optionIndex) in optionListFiltered(item)" :key="optionIndex" :label="option">
+                {{ letterFromIndex(optionIndex) }}. {{ option }}
+              </el-checkbox>
+            </el-checkbox-group>
+
+            <!--            填空题-->
+            <el-input v-else type="textarea" v-model="item.answer" style="width: 50%;"/>
+
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {getTestPaperDetail} from "@/api/train/train";
+import {addAssessResult} from "@/api/train/assessResult";
+
+export default {
+  dicts: ['train_question_type', 'training_category', 'train_status', 'train_whether_to_assess'],
+  data() {
+    return {
+      submitLoading: false,
+      demo: {},
+
+      loading: false,
+      questionList: [],
+      form: {},
+      assessmentPlanForm: {
+        testPaperName: '试卷模板'
+      },
+      // 校验规则
+      rules: {},
+      // 结果列表(1.testPaperDetailId,2.trainTargetId,3.answerContent,4.score)
+      assessResult: [],
+    };
+  },
+  created() {
+    this.getList()
+  },
+// Vue实例中的computed属性
+  computed: {
+
+    letterFromIndex() {
+      const letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
+      return (index) => {
+        if (index >= 0 && index <= letters.length - 1) {
+          return letters[index];
+        } else {
+          return '';
+        }
+      };
+    }
+  },
+  methods: {
+    // 初始化表单校验(废弃)
+    // initFormRules() {
+    //   this.questionList.forEach(item => {
+    //     this.$set(this.rules, 'answer', [{required: true, message: '此项为必填项', trigger: 'blur'}]);
+    //   });
+    // },
+    // 提交试卷
+    async submitForm() {
+      this.$modal.confirm(this.$t('trainList.isConfirmTest')).then(()=>{
+        this.$refs['form'].validate(async valid => {
+          console.log(valid);
+        })
+        console.log("this.form==>", this.questionList)
+        this.questionList.forEach(item => {
+          let result
+          // 如果为多选,则使用';' 分割数组作为答案
+          if (item.questionType === '02') {
+            result = {
+              testPaperDetailId: item.id,
+              answerContent: item.answer.join(";"),
+              score: item.correctAnswer === item.answer.join(";") ? item.score : 0
+            }
+          } else {
+            result = {
+              testPaperDetailId: item.id,
+              answerContent: item.answer,
+              score: item.correctAnswer === item.answer ? item.score : 0
+            }
+          }
+          this.assessResult.push(result)
+        })
+        // 插入答卷
+        let query = {
+          trainId: this.assessmentPlanForm.trainId,
+          ttAssessResults: this.assessResult
+        }
+        addAssessResult(query).then(()=>{
+          this.$modal.msgSuccess(this.$t('trainList.assessResultConfirm'))
+
+        })
+      })
+
+    },
+    // 获取试卷
+    getList() {
+      console.log("this.$route.query", this.$route.query.data.id)
+      let trainId = this.$route.query.data.id
+      if (!trainId) {
+        return
+      }
+      let query = {
+        id: trainId
+      }
+      // 根据培训id获取 试卷详细
+      getTestPaperDetail(query).then(res => {
+        console.log("response", res)
+        this.assessmentPlanForm.testPaperName = res.data.testPaperName
+        this.assessmentPlanForm.remark = res.data.remark
+        this.assessmentPlanForm.passScore = res.data.passScore
+        this.questionList = res.data.ttTestPaperDetailVoList
+        res.data.ttTestPaperDetailVoList.forEach(item => {
+          item.questionTypeName = this.dict.label.train_question_type[item.questionType];
+          item.score = parseInt(item.questionScore)
+        })
+        // 初始化,多选题答案属性为空数组
+        this.questionList = this.questionList.map(question => {
+          if (question.questionType === '02') {
+            return {
+              ...question,
+              answer: []
+            }
+          }
+          return {...question}
+        })
+      }).then(() => {
+        // todo(等会做 表单校验)
+        // this.initFormRules()
+      })
+      // this.questionList = []
+
+    },
+    // 预览返回
+    cancel() {
+      this.$tab.closeOpenPage("/train/trainTarget/")
+    },
+    // 获取选项
+    optionListFiltered: function (item) {
+      const optionList = [];
+      for (let i = 1; i <= 10; i++) {
+        const option = item[`option${i}`];
+        if (option !== null) {
+          optionList.push(option);
+        }
+      }
+      return optionList;
+    }
+  }
+};
+</script>
+
+<style scoped>
+
+.account-container {
+  padding: 16px 1vw;
+
+  .account-title {
+    font-family: Arial, Arial;
+    font-weight: bold;
+    font-size: 18px;
+    line-height: 21px;
+  }
+
+  .question-content {
+    text-align: center; /* 水平居中 */
+    display: flex;
+    justify-content: center;
+  }
+
+  .test-title {
+    display: flex;
+    justify-content: center; /* 水平居中,也可以使用 text-align */
+    align-items: center; /* 垂直居中 */
+    font-size: 20px;
+    height: 50px; /* 设置高度以确保垂直居中生效 */
+  }
+
+  .top {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .button-group {
+    display: flex;
+  }
+
+  .content {
+    width: 100%;
+    display: flex;
+
+    & > div {
+      align-self: stretch;
+    }
+
+    .card {
+      background: #FFFFFF;
+      box-shadow: 0px 2px 10px 0px #CED6DD;
+      border-radius: 6px;
+      padding: 20px;
+      margin: 0 auto; /* 实现水平居中 */
+      width: 60%; /* 设置元素宽度 */
+    }
+
+    .card-full {
+      width: 80vw;
+    }
+
+  }
+
+}
+
+</style>

+ 6 - 2
ruoyi-ui-vue2/src/views/train/trainTarget/index.vue

@@ -58,7 +58,7 @@
         <!--考核情况-->
         <Column :label="$t('trainTarget.examStatus')" :searchDict="dict.type.train_exam_status"
                 :searchParams="queryParams"
-                prop="passFlag" searchType="select" select-more
+                prop="assessCompleteFlag" searchType="select" select-more
                 select-prop="passFlags" show-search
         >
         </Column>
@@ -153,6 +153,7 @@ export default {
       try {
         const { trainType, trainStatus, passFlag, createTime, ...params } = this.queryParams
         const response = await participantTrainPage(params)
+        console.log("response", response)
         this.data = response.rows
         // this.total = response.total;
         this.queryParams.total = response.total
@@ -213,7 +214,10 @@ export default {
 
     // 参与考核
     handleExam(row) {
-      alert("参与考核")
+      let query = {
+        data:row
+      }
+      this.$tab.openPage(this.$t('trainTarget.participateExam'),"/train/trainTarget/detail/testPaper",query)
     }
 
   }

+ 7 - 1
ruoyi-ui-vue2/src/views/train/trainlist/detail/addTrain.vue

@@ -778,10 +778,16 @@ export default {
       });
       randomSelectByNum(this.questionNumList).then(response => {
         if (response && response.rows) {
-          this.questionList = this.questionList.concat(response.rows)
+          this.questionList = response.rows.concat(this.questionList)
           this.questionList.forEach(item => {
             item.questionTypeName = this.dict.label.train_question_type[item.questionType];
           })
+
+          response.rows.forEach(row => {
+            this.selectQuestionIdSet.add(row.id)
+            this.questionSelectList.add(row)
+          })
+
           this.$message.success(this.$t('common.addSuccess'))
         } else {
           this.$message.error(this.$t("trainList.randomSelectError"));