“数据更新”

This commit is contained in:
weizhong committed 2025-08-19 19:59:44 +08:00
1 parent 1277f1c6aa
commit 26044cc790
23 files changed
+2835 -15

No files matched your search

@@ -1,5 +1,6 @@
package org.dromara.bzrkh.controller;
import java.math.BigDecimal;
import java.util.List;
import lombok.RequiredArgsConstructor;
@@ -14,7 +15,9 @@ import org.dromara.common.core.domain.R;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.bzrkh.domain.vo.TeacherClassVo;
import org.dromara.bzrkh.domain.vo.BmzrAssessmentPlanVo;
import org.dromara.bzrkh.domain.vo.AssessmentDetailVo;
import org.dromara.bzrkh.domain.bo.TeacherAssessmentQueryBo;
import org.dromara.bzrkh.domain.bo.AssessmentSubmitBo;
import org.dromara.bzrkh.service.ITeacherAssessmentService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
@@ -81,6 +84,39 @@ public class TeacherAssessmentController extends BaseController {
return R.ok(detail);
}
/**
* 获取考核提交详情数据
*/
@SaCheckPermission("bzrkh:teacherAssessment:detail")
@GetMapping("/submitDetail")
public R<AssessmentDetailVo> getAssessmentSubmitDetail(@RequestParam Long planId, @RequestParam Long classId) {
Long teacherId = LoginHelper.getUserId();
AssessmentDetailVo detail = teacherAssessmentService.getAssessmentSubmitDetail(planId, classId, teacherId);
return R.ok(detail);
}
/**
* 提交考核评分数据
*/
@SaCheckPermission("bzrkh:teacherAssessment:submit")
@Log(title = "班主任考核管理", businessType = BusinessType.INSERT)
@PostMapping("/submitScore")
public R<Void> submitAssessmentScore(@RequestBody AssessmentSubmitBo bo) {
Long teacherId = LoginHelper.getUserId();
boolean result = teacherAssessmentService.submitAssessmentScore(bo, teacherId);
return toAjax(result);
}
/**
* 测试人数系数计算(临时调试接口)
* 用于验证特定人数的系数计算是否正确
*/
@GetMapping("/testCoefficient")
public R<Object> testStudentCountCoefficient(@RequestParam int studentCount) {
BigDecimal coefficient = teacherAssessmentService.testStudentCountCoefficient(studentCount);
return R.ok("学生人数: " + studentCount + ", 计算系数: " + coefficient);
}
/**
* 提交考核请求对象
*/
@@ -0,0 +1,136 @@
package org.dromara.bzrkh.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.math.BigDecimal;
import java.io.Serial;
/**
* 班主任考核得分对象 bmzr_assessment_score
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bmzr_assessment_score")
public class BmzrAssessmentScore extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 考核计划ID
*/
private Long planId;
/**
* 考核方案ID
*/
private Long schemeId;
/**
* 考核指标ID
*/
private Long indicatorId;
/**
* 被评价用户ID
*/
private Long evaluatedUserId;
/**
* 被评价用户姓名
*/
private String evaluatedUserName;
/**
* 被评价用户部门ID
*/
private Long evaluatedDeptId;
/**
* 自评分数
*/
private BigDecimal selfScore;
/**
* 自评意见
*/
private String selfComment;
/**
* 自评提交时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date selfSubmitTime;
/**
* 上级评分数
*/
private BigDecimal superiorScore;
/**
* 上级评意见
*/
private String superiorComment;
/**
* 上级评价人ID
*/
private Long superiorEvaluatorId;
/**
* 上级评提交时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date superiorSubmitTime;
/**
* 评价等级 dict_type = bmzr_evaluation_level
*/
private String evaluationLevel;
/**
* 审核状态(0=待审核 1=审核通过 2=审核不通过)
*/
private String auditStatus;
/**
* 最终得分
*/
private BigDecimal finalScore;
/**
* 提交时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date submitTime;
/**
* 班级ID
*/
private Long classId;
/**
* 自我总体评价
*/
private String selfOverallComment;
/**
* 上级总体评价
*/
private String superiorOverallComment;
}
@@ -0,0 +1,61 @@
package org.dromara.bzrkh.domain.bo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.bzrkh.domain.vo.AssessmentIndicatorScoreVo;
import org.dromara.bzrkh.domain.vo.SpecialWorkIndicatorVo;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
/**
* 考核提交业务对象
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class AssessmentSubmitBo extends BaseEntity {
/**
* 考核计划ID
*/
@NotNull(message = "考核计划ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long planId;
/**
* 班级ID
*/
@NotNull(message = "班级ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long classId;
/**
* 班主任常规工作指标评分
*/
private List<AssessmentIndicatorScoreVo> routineWorkScores;
/**
* 班级扣分
*/
private BigDecimal classDeduction;
/**
* 班级扣分意见
*/
private String classDeductionComment;
/**
* 特色工作指标评分
*/
private List<SpecialWorkIndicatorVo> specialWorkScores;
/**
* 总体评价
*/
private String overallComment;
}
@@ -0,0 +1,80 @@
package org.dromara.bzrkh.domain.vo;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 考核详情视图对象
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
public class AssessmentDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 班级详细信息
*/
private ClassDetailVo classDetail;
/**
* 系数信息
*/
private CoefficientVo coefficient;
/**
* 班主任常规工作指标列表
*/
private List<AssessmentIndicatorScoreVo> routineWorkIndicators;
/**
* 班级扣分指标
*/
private AssessmentIndicatorScoreVo classDeductionIndicator;
/**
* 特色工作指标列表
*/
private List<SpecialWorkIndicatorVo> specialWorkIndicators;
/**
* 月考核得分
*/
private BigDecimal monthlyScore;
/**
* 总体评价
*/
private String overallComment;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,89 @@
package org.dromara.bzrkh.domain.vo;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 考核指标评分视图对象
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
public class AssessmentIndicatorScoreVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 指标ID
*/
private Long id;
/**
* 指标名称
*/
private String indicatorName;
/**
* 指标编码
*/
private String indicatorCode;
/**
* 指标描述
*/
private String description;
/**
* 指标分值
*/
private BigDecimal score;
/**
* 自评分数
*/
private BigDecimal selfScore;
/**
* 自评意见
*/
private String selfComment;
/**
* 自我总体评价
*/
private String selfOverallComment;
/**
* 排序
*/
private Integer sortOrder;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,78 @@
package org.dromara.bzrkh.domain.vo;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 班级详细信息视图对象
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
public class ClassDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 班级ID
*/
private Long id;
/**
* 学院名称
*/
private String collegeName;
/**
* 专业名称
*/
private String majorName;
/**
* 班级名称
*/
private String className;
/**
* 班级层次:1-中级工 2-高级工 3-预备技师 4-技师 5-中高职一体
*/
private String classType;
/**
* 班级总人数
*/
private Integer totalStudentCount;
/**
* 住校生人数
*/
private Integer boardingStudentCount;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,64 @@
package org.dromara.bzrkh.domain.vo;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 系数信息视图对象
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
public class CoefficientVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 人数系数(K2)
*/
private BigDecimal studentCountCoefficient;
/**
* 住校生系数(K3)
*/
private BigDecimal boardingCoefficient;
/**
* 层次系数(K1)
*/
private BigDecimal levelCoefficient;
/**
* 总系数(K = K1 * (K2 + K3))
*/
private BigDecimal totalCoefficient;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,64 @@
package org.dromara.bzrkh.domain.vo;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 特色工作指标视图对象
*
* @author ruoyi
* @date 2025-01-27
*/
@Data
public class SpecialWorkIndicatorVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 指标编码
*/
private String indicatorCode;
/**
* 指标名称
*/
private String indicatorName;
/**
* 分值
*/
private BigDecimal score;
/**
* 自评意见
*/
private String selfComment;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,14 @@
package org.dromara.bzrkh.mapper;
import org.dromara.bzrkh.domain.BmzrAssessmentScore;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 班主任考核得分Mapper接口
*
* @author ruoyi
* @date 2025-01-27
*/
public interface BmzrAssessmentScoreMapper extends BaseMapperPlus<BmzrAssessmentScore, BmzrAssessmentScore> {
}
@@ -2,10 +2,14 @@ package org.dromara.bzrkh.mapper;
import org.dromara.bzrkh.domain.vo.TeacherClassVo;
import org.dromara.bzrkh.domain.vo.BmzrAssessmentPlanVo;
import org.dromara.bzrkh.domain.vo.ClassDetailVo;
import org.dromara.bzrkh.domain.vo.AssessmentIndicatorScoreVo;
import org.dromara.bzrkh.domain.vo.SpecialWorkIndicatorVo;
import org.dromara.bzrkh.domain.bo.TeacherAssessmentQueryBo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -31,4 +35,82 @@ public interface TeacherAssessmentMapper extends BaseMapperPlus<Object, Object>
* @return 考核计划列表
*/
List<BmzrAssessmentPlanVo> selectTeacherAssessmentPlanList(TeacherAssessmentQueryBo bo);
/**
* 查询班级详细信息
*
* @param classId 班级ID
* @return 班级详细信息
*/
ClassDetailVo selectClassDetail(@Param("classId") Long classId);
/**
* 查询班主任常规工作指标列表
*
* @param planId 计划ID
* @return 指标列表
*/
List<AssessmentIndicatorScoreVo> selectRoutineWorkIndicators(@Param("planId") Long planId);
/**
* 查询班级扣分指标
*
* @return 班级扣分指标
*/
AssessmentIndicatorScoreVo selectClassDeductionIndicator();
/**
* 查询特色工作指标列表
*
* @return 特色工作指标列表
*/
List<SpecialWorkIndicatorVo> selectSpecialWorkIndicators();
/**
* 查询已保存的评分数据
*
* @param planId 计划ID
* @param teacherId 班主任ID
* @return 评分数据列表
*/
List<AssessmentIndicatorScoreVo> selectSavedAssessmentScores(@Param("planId") Long planId, @Param("teacherId") Long teacherId);
/**
* 根据计划ID查询方案ID
*
* @param planId 计划ID
* @return 方案ID
*/
Long selectSchemeIdByPlanId(@Param("planId") Long planId);
/**
* 根据指标编码查询指标ID
*
* @param indicatorCode 指标编码
* @return 指标ID
*/
Long selectIndicatorIdByCode(@Param("indicatorCode") String indicatorCode);
/**
* 逻辑删除指定计划和教师的评分数据
*
* @param planId 计划ID
* @param teacherId 教师ID
*/
void deleteAssessmentScoresByPlanAndTeacher(@Param("planId") Long planId, @Param("teacherId") Long teacherId);
/**
* 更新评分数据的备注
*
* @param planId 计划ID
* @param indicatorId 指标ID
* @param teacherId 教师ID
* @param remark 备注
*/
void updateAssessmentScoreRemark(@Param("planId") Long planId,
@Param("indicatorId") Long indicatorId,
@Param("teacherId") Long teacherId,
@Param("remark") String remark);
}
@@ -2,10 +2,13 @@ package org.dromara.bzrkh.service;
import org.dromara.bzrkh.domain.vo.TeacherClassVo;
import org.dromara.bzrkh.domain.vo.BmzrAssessmentPlanVo;
import org.dromara.bzrkh.domain.vo.AssessmentDetailVo;
import org.dromara.bzrkh.domain.bo.TeacherAssessmentQueryBo;
import org.dromara.bzrkh.domain.bo.AssessmentSubmitBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.math.BigDecimal;
import java.util.List;
/**
@@ -52,4 +55,32 @@ public interface ITeacherAssessmentService {
* @return 考核详情
*/
Object getAssessmentDetail(Long planId, Long classId, Long teacherId);
/**
* 获取考核提交详情数据
*
* @param planId 计划ID
* @param classId 班级ID
* @param teacherId 班主任ID
* @return 考核提交详情
*/
AssessmentDetailVo getAssessmentSubmitDetail(Long planId, Long classId, Long teacherId);
/**
* 提交考核评分数据
*
* @param bo 考核提交数据
* @param teacherId 班主任ID
* @return 是否成功
*/
boolean submitAssessmentScore(AssessmentSubmitBo bo, Long teacherId);
/**
* 测试人数系数计算(临时调试方法)
* 用于验证特定人数的系数计算是否正确
*
* @param studentCount 学生人数
* @return 计算出的系数
*/
BigDecimal testStudentCountCoefficient(int studentCount);
}
@@ -4,14 +4,28 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.bzrkh.domain.vo.TeacherClassVo;
import org.dromara.bzrkh.domain.vo.BmzrAssessmentPlanVo;
import org.dromara.bzrkh.domain.vo.AssessmentDetailVo;
import org.dromara.bzrkh.domain.vo.ClassDetailVo;
import org.dromara.bzrkh.domain.vo.CoefficientVo;
import org.dromara.bzrkh.domain.vo.AssessmentIndicatorScoreVo;
import org.dromara.bzrkh.domain.vo.SpecialWorkIndicatorVo;
import org.dromara.bzrkh.domain.bo.TeacherAssessmentQueryBo;
import org.dromara.bzrkh.domain.bo.AssessmentSubmitBo;
import org.dromara.bzrkh.mapper.TeacherAssessmentMapper;
import org.dromara.bzrkh.mapper.BmzrAssessmentScoreMapper;
import org.dromara.bzrkh.service.ITeacherAssessmentService;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.bzrkh.domain.BmzrAssessmentScore;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
/**
* 班主任考核管理Service业务层处理
@@ -19,11 +33,13 @@ import java.util.List;
* @author ruoyi
* @date 2025-01-27
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class TeacherAssessmentServiceImpl implements ITeacherAssessmentService {
private final TeacherAssessmentMapper teacherAssessmentMapper;
private final BmzrAssessmentScoreMapper assessmentScoreMapper;
/**
* 查询班主任管理的班级列表
@@ -96,4 +112,358 @@ public class TeacherAssessmentServiceImpl implements ITeacherAssessmentService {
// 4. 获取考核记录和评分
return null;
}
/**
* 获取考核提交详情数据
*
* @param planId 计划ID
* @param classId 班级ID
* @param teacherId 班主任ID
* @return 考核提交详情
*/
@Override
public AssessmentDetailVo getAssessmentSubmitDetail(Long planId, Long classId, Long teacherId) {
AssessmentDetailVo detailVo = new AssessmentDetailVo();
// 1. 获取班级详细信息
ClassDetailVo classDetail = teacherAssessmentMapper.selectClassDetail(classId);
detailVo.setClassDetail(classDetail);
// 2. 计算系数信息
CoefficientVo coefficient = calculateCoefficient(classDetail);
detailVo.setCoefficient(coefficient);
// 3. 获取班主任常规工作指标
List<AssessmentIndicatorScoreVo> routineWorkIndicators = teacherAssessmentMapper.selectRoutineWorkIndicators(planId);
detailVo.setRoutineWorkIndicators(routineWorkIndicators);
// 4. 获取班级扣分指标
AssessmentIndicatorScoreVo classDeductionIndicator = teacherAssessmentMapper.selectClassDeductionIndicator();
detailVo.setClassDeductionIndicator(classDeductionIndicator);
// 5. 获取特色工作指标
List<SpecialWorkIndicatorVo> specialWorkIndicators = teacherAssessmentMapper.selectSpecialWorkIndicators();
detailVo.setSpecialWorkIndicators(specialWorkIndicators);
// 6. 获取已保存的评分数据(如果有)
List<AssessmentIndicatorScoreVo> savedScores = teacherAssessmentMapper.selectSavedAssessmentScores(planId, teacherId);
if (savedScores != null && !savedScores.isEmpty()) {
// 将已保存的评分数据合并到指标中
mergeAssessmentScores(detailVo, savedScores);
// 获取总体评价(从任意一条记录中获取,因为都是同样的总体评价)
savedScores.stream()
.findFirst()
.ifPresent(saved -> detailVo.setOverallComment(saved.getSelfOverallComment()));
}
return detailVo;
}
/**
* 提交考核评分数据
*
* @param bo 考核提交数据
* @param teacherId 班主任ID
* @return 是否成功
*/
@Override
public boolean submitAssessmentScore(AssessmentSubmitBo bo, Long teacherId) {
try {
// 1. 先逻辑删除该计划下该教师的历史评分数据
teacherAssessmentMapper.deleteAssessmentScoresByPlanAndTeacher(bo.getPlanId(), teacherId);
Long schemeId = teacherAssessmentMapper.selectSchemeIdByPlanId(bo.getPlanId());
String currentUserName = LoginHelper.getUsername();
Long currentDeptId = LoginHelper.getDeptId();
Date now = new Date();
// 2. 插入班主任常规工作指标评分
if (bo.getRoutineWorkScores() != null && !bo.getRoutineWorkScores().isEmpty()) {
for (AssessmentIndicatorScoreVo score : bo.getRoutineWorkScores()) {
BmzrAssessmentScore assessmentScore = new BmzrAssessmentScore();
assessmentScore.setPlanId(bo.getPlanId());
assessmentScore.setSchemeId(schemeId);
assessmentScore.setIndicatorId(score.getId());
assessmentScore.setEvaluatedUserId(teacherId);
assessmentScore.setEvaluatedUserName(currentUserName);
assessmentScore.setEvaluatedDeptId(currentDeptId);
assessmentScore.setSelfScore(score.getSelfScore());
assessmentScore.setSelfComment(score.getSelfComment());
assessmentScore.setSelfSubmitTime(now);
assessmentScore.setSubmitTime(now);
assessmentScore.setAuditStatus("0");
// 设置班级ID和自我总体评价
assessmentScore.setClassId(bo.getClassId());
assessmentScore.setSelfOverallComment(bo.getOverallComment());
assessmentScoreMapper.insert(assessmentScore);
}
}
// 3. 插入班级扣分指标评分
Long classDeductionIndicatorId = teacherAssessmentMapper.selectIndicatorIdByCode("bzrwjkf001");
if (classDeductionIndicatorId != null) {
BmzrAssessmentScore assessmentScore = new BmzrAssessmentScore();
assessmentScore.setPlanId(bo.getPlanId());
assessmentScore.setSchemeId(schemeId);
assessmentScore.setIndicatorId(classDeductionIndicatorId);
assessmentScore.setEvaluatedUserId(teacherId);
assessmentScore.setEvaluatedUserName(currentUserName);
assessmentScore.setEvaluatedDeptId(currentDeptId);
assessmentScore.setSelfScore(bo.getClassDeduction());
assessmentScore.setSelfComment(bo.getClassDeductionComment());
assessmentScore.setSelfSubmitTime(now);
assessmentScore.setSubmitTime(now);
assessmentScore.setAuditStatus("0");
// 设置班级ID和自我总体评价
assessmentScore.setClassId(bo.getClassId());
assessmentScore.setSelfOverallComment(bo.getOverallComment());
assessmentScoreMapper.insert(assessmentScore);
}
// 4. 插入特色工作指标评分
if (bo.getSpecialWorkScores() != null && !bo.getSpecialWorkScores().isEmpty()) {
for (SpecialWorkIndicatorVo score : bo.getSpecialWorkScores()) {
Long indicatorId = teacherAssessmentMapper.selectIndicatorIdByCode(score.getIndicatorCode());
if (indicatorId != null) {
BmzrAssessmentScore assessmentScore = new BmzrAssessmentScore();
assessmentScore.setPlanId(bo.getPlanId());
assessmentScore.setSchemeId(schemeId);
assessmentScore.setIndicatorId(indicatorId);
assessmentScore.setEvaluatedUserId(teacherId);
assessmentScore.setEvaluatedUserName(currentUserName);
assessmentScore.setEvaluatedDeptId(currentDeptId);
assessmentScore.setSelfScore(score.getScore());
assessmentScore.setSelfComment(score.getSelfComment());
assessmentScore.setSelfSubmitTime(now);
assessmentScore.setSubmitTime(now);
assessmentScore.setAuditStatus("0");
// 设置班级ID和自我总体评价
assessmentScore.setClassId(bo.getClassId());
assessmentScore.setSelfOverallComment(bo.getOverallComment());
assessmentScoreMapper.insert(assessmentScore);
}
}
}
// 5. 总体评价已经通过上述步骤存储到每条记录的 self_overall_comment 字段中
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 计算系数信息
*
* @param classDetail 班级详细信息
* @return 系数信息
*/
private CoefficientVo calculateCoefficient(ClassDetailVo classDetail) {
CoefficientVo coefficient = new CoefficientVo();
// 计算人数系数(K2)
int totalStudents = classDetail.getTotalStudentCount() != null ? classDetail.getTotalStudentCount() : 0;
log.info("计算人数系数 - 班级ID: {}, 班级名称: {}, 学生总人数: {}",
classDetail.getId(), classDetail.getClassName(), totalStudents);
// 数据验证:如果学生人数为0但前端显示有人数,可能是数据同步问题
if (totalStudents == 0) {
log.warn("警告:班级{}学生人数为0,请检查xg_student_msg表数据", classDetail.getClassName());
}
BigDecimal studentCountCoefficient = calculateStudentCountCoefficient(totalStudents);
log.info("计算人数系数结果: {}", studentCountCoefficient);
// 关键修复:确保人数系数不为0
if (studentCountCoefficient == null || studentCountCoefficient.compareTo(BigDecimal.ZERO) == 0) {
log.error("人数系数计算异常,学生人数: {},计算结果: {},进行修复", totalStudents, studentCountCoefficient);
// 直接重新计算并设置最低值
if (totalStudents > 0) {
// 手动计算25人的系数:1 - (36-25)*0.02 = 1 - 0.22 = 0.78
if (totalStudents < 36) {
int missingStudents = 36 - totalStudents;
BigDecimal deduction = BigDecimal.valueOf(missingStudents).multiply(BigDecimal.valueOf(0.02));
studentCountCoefficient = BigDecimal.ONE.subtract(deduction);
if (studentCountCoefficient.compareTo(BigDecimal.valueOf(0.7)) < 0) {
studentCountCoefficient = BigDecimal.valueOf(0.7);
}
studentCountCoefficient = studentCountCoefficient.setScale(4, RoundingMode.HALF_UP);
log.info("修复后的人数系数: {}", studentCountCoefficient);
} else {
studentCountCoefficient = BigDecimal.valueOf(0.7).setScale(4, RoundingMode.HALF_UP);
}
} else {
studentCountCoefficient = BigDecimal.valueOf(0.7).setScale(4, RoundingMode.HALF_UP);
}
}
coefficient.setStudentCountCoefficient(studentCountCoefficient);
// 验证系数设置是否成功
if (coefficient.getStudentCountCoefficient() == null ||
coefficient.getStudentCountCoefficient().compareTo(BigDecimal.ZERO) == 0) {
log.error("错误:人数系数设置仍然失败,当前值: {}", coefficient.getStudentCountCoefficient());
}
// 计算住校生系数(K3)
int boardingStudents = classDetail.getBoardingStudentCount() != null ? classDetail.getBoardingStudentCount() : 0;
BigDecimal boardingCoefficient = calculateBoardingCoefficient(boardingStudents);
coefficient.setBoardingCoefficient(boardingCoefficient);
// 计算层次系数(K1)
BigDecimal levelCoefficient = calculateLevelCoefficient(classDetail.getClassType());
coefficient.setLevelCoefficient(levelCoefficient);
// 计算总系数(K = K1 * (K2 + K3))
BigDecimal totalCoefficient = levelCoefficient.multiply(studentCountCoefficient.add(boardingCoefficient));
coefficient.setTotalCoefficient(totalCoefficient);
return coefficient;
}
/**
* 计算人数系数(K2)
* 班级人数36~40人(含36人),系数为1;
* 超过40人,每增加1名学生,系数增加0.02;
* 少于36人,每减少1名学生,系数减少0.02;
* 本项系数最低0.7
*/
private BigDecimal calculateStudentCountCoefficient(int studentCount) {
log.info("开始计算人数系数 - 输入学生人数: {}", studentCount);
// 数据验证:如果学生人数异常,使用保护性默认值
if (studentCount < 0) {
log.warn("学生人数为负数({}),使用默认值0", studentCount);
studentCount = 0;
}
BigDecimal coefficient;
if (studentCount >= 36 && studentCount <= 40) {
coefficient = BigDecimal.ONE;
log.info("人数在36-40人范围内,系数为1.0");
} else if (studentCount > 40) {
int extraStudents = studentCount - 40;
coefficient = BigDecimal.ONE.add(BigDecimal.valueOf(extraStudents * 0.02));
log.info("人数超过40人,额外{}人,系数为{}", extraStudents, coefficient);
} else {
// 少于36人的情况
int missingStudents = 36 - studentCount;
// 使用更精确的BigDecimal计算,避免精度问题
BigDecimal deduction = BigDecimal.valueOf(missingStudents).multiply(BigDecimal.valueOf(0.02));
coefficient = BigDecimal.ONE.subtract(deduction);
log.info("人数少于36人,缺少{}人,扣减{},计算系数为{}", missingStudents, deduction, coefficient);
// 最低0.7
BigDecimal minCoefficient = BigDecimal.valueOf(0.7);
if (coefficient.compareTo(minCoefficient) < 0) {
log.info("系数{}低于最低值0.7,调整为0.7", coefficient);
coefficient = minCoefficient;
}
}
BigDecimal result = coefficient.setScale(4, RoundingMode.HALF_UP);
log.info("最终人数系数: {}", result);
// 额外验证:确保结果不为0
if (result.compareTo(BigDecimal.ZERO) == 0) {
log.error("严重错误:人数系数计算结果为0,学生人数: {},强制设置为最低值0.7", studentCount);
result = BigDecimal.valueOf(0.7).setScale(4, RoundingMode.HALF_UP);
}
return result;
}
/**
* 测试人数系数计算(临时调试方法)
* 可以通过调用这个方法来验证具体人数的系数计算
*/
public BigDecimal testStudentCountCoefficient(int studentCount) {
log.info("=== 测试人数系数计算 ===");
log.info("输入学生人数: {}", studentCount);
BigDecimal result = calculateStudentCountCoefficient(studentCount);
log.info("计算结果: {}", result);
log.info("=== 测试结束 ===");
return result;
}
/**
* 计算住校生系数(K3)
* 根据住校生人数,按人均0.005计算
*/
private BigDecimal calculateBoardingCoefficient(int boardingCount) {
return BigDecimal.valueOf(boardingCount * 0.005).setScale(4, RoundingMode.HALF_UP);
}
/**
* 计算层次系数(K1)
* 技师班(K1=0.95),高级工班(K1=1),中级工班(K1=1.05)
*/
private BigDecimal calculateLevelCoefficient(String classType) {
if (classType == null) {
return BigDecimal.ONE;
}
switch (classType) {
case "1": // 中级工
return BigDecimal.valueOf(1.05);
case "2": // 高级工
return BigDecimal.ONE;
case "3": // 预备技师
return BigDecimal.valueOf(0.95);
case "4": // 技师
return BigDecimal.valueOf(0.95);
case "5": // 中高职一体
return BigDecimal.ONE;
default:
return BigDecimal.ONE;
}
}
/**
* 合并已保存的评分数据
*/
private void mergeAssessmentScores(AssessmentDetailVo detailVo, List<AssessmentIndicatorScoreVo> savedScores) {
// 合并班主任常规工作指标评分
if (detailVo.getRoutineWorkIndicators() != null) {
for (AssessmentIndicatorScoreVo indicator : detailVo.getRoutineWorkIndicators()) {
savedScores.stream()
.filter(saved -> saved.getId().equals(indicator.getId()))
.findFirst()
.ifPresent(saved -> {
indicator.setSelfScore(saved.getSelfScore());
indicator.setSelfComment(saved.getSelfComment());
});
}
}
// 合并班级扣分指标评分
if (detailVo.getClassDeductionIndicator() != null) {
savedScores.stream()
.filter(saved -> "bzrwjkf001".equals(saved.getIndicatorCode()))
.findFirst()
.ifPresent(saved -> {
detailVo.getClassDeductionIndicator().setSelfScore(saved.getSelfScore());
detailVo.getClassDeductionIndicator().setSelfComment(saved.getSelfComment());
});
}
// 合并特色工作指标评分
if (detailVo.getSpecialWorkIndicators() != null) {
for (SpecialWorkIndicatorVo indicator : detailVo.getSpecialWorkIndicators()) {
savedScores.stream()
.filter(saved -> indicator.getIndicatorCode().equals(saved.getIndicatorCode()))
.findFirst()
.ifPresent(saved -> {
indicator.setScore(saved.getSelfScore());
indicator.setSelfComment(saved.getSelfComment());
});
}
}
}
}
@@ -45,6 +45,50 @@
<result property="deleted" column="deleted"/>
</resultMap>
<!-- 班级详细信息结果映射 -->
<resultMap type="org.dromara.bzrkh.domain.vo.ClassDetailVo" id="ClassDetailVoResult">
<result property="id" column="id"/>
<result property="collegeName" column="college_name"/>
<result property="majorName" column="major_name"/>
<result property="className" column="class_name"/>
<result property="classType" column="class_type"/>
<result property="totalStudentCount" column="total_student_count"/>
<result property="boardingStudentCount" column="boarding_student_count"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 考核指标评分结果映射 -->
<resultMap type="org.dromara.bzrkh.domain.vo.AssessmentIndicatorScoreVo" id="AssessmentIndicatorScoreVoResult">
<result property="id" column="id"/>
<result property="indicatorName" column="indicator_name"/>
<result property="indicatorCode" column="indicator_code"/>
<result property="description" column="description"/>
<result property="score" column="score"/>
<result property="selfScore" column="self_score"/>
<result property="selfComment" column="self_comment"/>
<result property="selfOverallComment" column="self_overall_comment"/>
<result property="sortOrder" column="sort_order"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 特色工作指标结果映射 -->
<resultMap type="org.dromara.bzrkh.domain.vo.SpecialWorkIndicatorVo" id="SpecialWorkIndicatorVoResult">
<result property="indicatorCode" column="indicator_code"/>
<result property="indicatorName" column="indicator_name"/>
<result property="score" column="score"/>
<result property="selfComment" column="self_comment"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 查询班主任管理的班级列表 -->
<select id="selectTeacherClassList" parameterType="Long" resultMap="TeacherClassVoResult">
SELECT
@@ -109,4 +153,159 @@
ORDER BY p.create_time DESC
</select>
<!-- 查询班级详细信息 -->
<select id="selectClassDetail" parameterType="Long" resultMap="ClassDetailVoResult">
SELECT
c.id,
c.college_name,
c.major_name,
c.class_name,
c.class_type,
(SELECT COUNT(1)
FROM xg_student_msg s
WHERE s.class_id = c.id
AND s.student_status = '1'
AND s.deleted = 0) as total_student_count,
(SELECT COUNT(1)
FROM xg_student_msg s
WHERE s.class_id = c.id
AND s.student_status = '1'
AND s.dormitory_room IS NOT NULL
AND s.dormitory_room != ''
AND s.deleted = 0) as boarding_student_count,
c.create_by,
c.create_time,
c.update_by,
c.update_time
FROM xg_class_msg c
WHERE c.id = #{classId}
AND c.deleted = 0
</select>
<!-- 查询班主任常规工作指标列表 -->
<select id="selectRoutineWorkIndicators" parameterType="Long" resultMap="AssessmentIndicatorScoreVoResult">
SELECT
i.id,
i.indicator_name,
i.indicator_code,
i.remark as description,
i.score,
i.sort_order,
NULL as self_score,
NULL as self_comment,
i.create_by,
i.create_time,
i.update_by,
i.update_time
FROM bmzr_assessment_plan p
JOIN bmzr_assessment_scheme_indicator si ON p.scheme_id = si.scheme_id AND si.deleted = 0
JOIN bmzr_assessment_indicator i ON si.indicator_id = i.id AND i.deleted = 0
WHERE p.id = #{planId}
AND p.deleted = 0
AND i.indicator_code NOT IN ('bzrwjkf001', 'bzrtsgz001', 'bzrtsgz002', 'bzrtsgz003', 'bzrtsgz004')
ORDER BY i.sort_order ASC
</select>
<!-- 查询班级扣分指标 -->
<select id="selectClassDeductionIndicator" resultMap="AssessmentIndicatorScoreVoResult">
SELECT
i.id,
i.indicator_name,
i.indicator_code,
i.remark as description,
i.score,
i.sort_order,
NULL as self_score,
NULL as self_comment,
i.create_by,
i.create_time,
i.update_by,
i.update_time
FROM bmzr_assessment_indicator i
WHERE i.indicator_code = 'bzrwjkf001'
AND i.deleted = 0
LIMIT 1
</select>
<!-- 查询特色工作指标列表 -->
<select id="selectSpecialWorkIndicators" resultMap="SpecialWorkIndicatorVoResult">
SELECT
i.indicator_code,
i.indicator_name,
0.00 as score,
NULL as self_comment,
i.create_by,
i.create_time,
i.update_by,
i.update_time
FROM bmzr_assessment_indicator i
WHERE i.indicator_code IN ('bzrtsgz001', 'bzrtsgz002', 'bzrtsgz003', 'bzrtsgz004')
AND i.deleted = 0
ORDER BY i.indicator_code ASC
</select>
<!-- 查询已保存的评分数据 -->
<select id="selectSavedAssessmentScores" resultMap="AssessmentIndicatorScoreVoResult">
SELECT
i.id,
i.indicator_name,
i.indicator_code,
i.remark as description,
i.score,
s.self_score,
s.self_comment,
s.self_overall_comment,
i.sort_order,
s.create_by,
s.create_time,
s.update_by,
s.update_time
FROM bmzr_assessment_score s
JOIN bmzr_assessment_indicator i ON s.indicator_id = i.id AND i.deleted = 0
WHERE s.plan_id = #{planId}
AND s.evaluated_user_id = #{teacherId}
AND s.deleted = 0
</select>
<!-- 根据计划ID查询方案ID -->
<select id="selectSchemeIdByPlanId" parameterType="Long" resultType="Long">
SELECT scheme_id
FROM bmzr_assessment_plan
WHERE id = #{planId}
AND deleted = 0
LIMIT 1
</select>
<!-- 根据指标编码查询指标ID -->
<select id="selectIndicatorIdByCode" parameterType="String" resultType="Long">
SELECT id
FROM bmzr_assessment_indicator
WHERE indicator_code = #{indicatorCode}
AND deleted = 0
LIMIT 1
</select>
<!-- 逻辑删除指定计划和教师的评分数据 -->
<update id="deleteAssessmentScoresByPlanAndTeacher">
UPDATE bmzr_assessment_score
SET deleted = 1,
update_time = NOW()
WHERE plan_id = #{planId}
AND evaluated_user_id = #{teacherId}
AND deleted = 0
</update>
<!-- 更新评分数据的备注 -->
<update id="updateAssessmentScoreRemark">
UPDATE bmzr_assessment_score
SET remark = #{remark},
update_time = NOW()
WHERE plan_id = #{planId}
AND indicator_id = #{indicatorId}
AND evaluated_user_id = #{teacherId}
AND deleted = 0
</update>
</mapper>
@@ -0,0 +1,49 @@
-- ==========================================
-- 班主任考核指标类型管理权限配置
-- 版本:V1.0.0
-- 功能:班主任考核指标类型管理相关权限
-- 作者:系统设计
-- 时间:2025-01-08
-- 模块:学工管理-班主任考核指标类型管理权限
-- ==========================================
-- 删除已存在的权限数据
DELETE FROM sys_menu WHERE menu_name = '指标类型管理' OR parent_id IN (SELECT id FROM (SELECT id FROM sys_menu WHERE menu_name = '指标类型管理') temp);
-- 1. 班主任考核指标类型管理主菜单
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id)
VALUES ('指标类型管理', 2000, 4, 'assessmentIndicatorType', 'bzrkh/assessmentIndicatorType/index', '', 1, 0, 'C', '0', '0', 'bzrkh:assessmentIndicatorType:list', 'list', 103, 1, NOW(), 1, NOW(), '班主任考核指标类型管理菜单', '000000');
-- 获取刚插入的主菜单ID
SET @menu_id = LAST_INSERT_ID();
-- 2. 班主任考核指标类型查询按钮
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id)
VALUES ('班主任考核指标类型查询', @menu_id, 1, '#', '', '', 1, 0, 'F', '0', '0', 'bzrkh:assessmentIndicatorType:query', '#', 103, 1, NOW(), 1, NOW(), '', '000000');
-- 3. 班主任考核指标类型新增按钮
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id)
VALUES ('班主任考核指标类型新增', @menu_id, 2, '#', '', '', 1, 0, 'F', '0', '0', 'bzrkh:assessmentIndicatorType:add', '#', 103, 1, NOW(), 1, NOW(), '', '000000');
-- 4. 班主任考核指标类型修改按钮
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id)
VALUES ('班主任考核指标类型修改', @menu_id, 3, '#', '', '', 1, 0, 'F', '0', '0', 'bzrkh:assessmentIndicatorType:edit', '#', 103, 1, NOW(), 1, NOW(), '', '000000');
-- 5. 班主任考核指标类型删除按钮
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id)
VALUES ('班主任考核指标类型删除', @menu_id, 4, '#', '', '', 1, 0, 'F', '0', '0', 'bzrkh:assessmentIndicatorType:remove', '#', 103, 1, NOW(), 1, NOW(), '', '000000');
-- 6. 班主任考核指标类型导出按钮
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id)
VALUES ('班主任考核指标类型导出', @menu_id, 5, '#', '', '', 1, 0, 'F', '0', '0', 'bzrkh:assessmentIndicatorType:export', '#', 103, 1, NOW(), 1, NOW(), '', '000000');
-- ==========================================
-- 权限字符说明
-- ==========================================
-- bzrkh:assessmentIndicatorType:list - 班主任考核指标类型查询权限
-- bzrkh:assessmentIndicatorType:query - 班主任考核指标类型详情查询权限
-- bzrkh:assessmentIndicatorType:add - 班主任考核指标类型新增权限
-- bzrkh:assessmentIndicatorType:edit - 班主任考核指标类型修改权限
-- bzrkh:assessmentIndicatorType:remove - 班主任考核指标类型删除权限
-- bzrkh:assessmentIndicatorType:export - 班主任考核指标类型导出权限
-- ==========================================
@@ -0,0 +1,247 @@
# 班主任考核提交功能开发说明
## 功能概述
完善了班主任考核管理界面中的"提交考核"按钮功能,实现了完整的考核评分提交流程。
## 开发内容
### 前端部分(Vue 3 + TypeScript + Element Plus)
#### 1. API类型定义扩展
**文件位置**: `web/src/api/bzrkh/teacherAssessment/types.ts`
**新增类型定义**:
- `ClassDetailVO` - 班级详细信息
- `CoefficientVO` - 系数信息
- `AssessmentIndicatorVO` - 考核指标信息
- `SpecialWorkIndicatorVO` - 特色工作指标
- `AssessmentSubmitVO` - 考核提交数据
- `AssessmentDetailVO` - 考核详情数据
#### 2. API接口扩展
**文件位置**: `web/src/api/bzrkh/teacherAssessment/index.ts`
**新增接口**:
- `getAssessmentSubmitDetail()` - 获取考核提交详情数据
- `submitAssessmentScore()` - 提交考核评分数据
#### 3. 提交弹窗组件
**文件位置**: `web/src/views/bzrkh/teacherAssessment/submitDialog.vue`
**功能特性**:
- 9张卡片式布局:班级信息、人数系数、住校生系数、层次系数、总系数、班主任常规工作、班级管理实效、特色工作、月考核得分
- 自动系数计算:根据班级人数、住校生人数、班级层次自动计算各项系数
- 动态评分:支持自评分数输入和自评意见填写
- 实时计算:月考核得分根据公式自动计算
- 数据验证:表单验证确保数据完整性
#### 4. 主界面集成
**文件位置**: `web/src/views/bzrkh/teacherAssessment/index.vue`
**集成内容**:
- 引入提交弹窗组件
- 修改提交考核按钮逻辑
- 添加成功回调处理
### 后端部分(Spring Boot 3.4.6 + MyBatis-Plus)
#### 1. VO类创建
**文件位置**: `backend/ruoyi-modules/ruoyi-bzrkh/src/main/java/org/dromara/bzrkh/domain/vo/`
**新增VO类**:
- `ClassDetailVo.java` - 班级详细信息视图对象
- `CoefficientVo.java` - 系数信息视图对象
- `AssessmentIndicatorScoreVo.java` - 考核指标评分视图对象
- `SpecialWorkIndicatorVo.java` - 特色工作指标视图对象
- `AssessmentDetailVo.java` - 考核详情视图对象
#### 2. BO类创建
**文件位置**: `backend/ruoyi-modules/ruoyi-bzrkh/src/main/java/org/dromara/bzrkh/domain/bo/`
**新增BO类**:
- `AssessmentSubmitBo.java` - 考核提交业务对象
#### 3. Controller扩展
**文件位置**: `backend/ruoyi-modules/ruoyi-bzrkh/src/main/java/org/dromara/bzrkh/controller/TeacherAssessmentController.java`
**新增接口**:
- `getAssessmentSubmitDetail()` - 获取考核提交详情数据
- `submitAssessmentScore()` - 提交考核评分数据
#### 4. Service接口和实现扩展
**文件位置**:
- `backend/ruoyi-modules/ruoyi-bzrkh/src/main/java/org/dromara/bzrkh/service/ITeacherAssessmentService.java`
- `backend/ruoyi-modules/ruoyi-bzrkh/src/main/java/org/dromara/bzrkh/service/impl/TeacherAssessmentServiceImpl.java`
**新增方法**:
- `getAssessmentSubmitDetail()` - 获取考核提交详情数据
- `submitAssessmentScore()` - 提交考核评分数据
- `calculateCoefficient()` - 计算系数信息(私有方法)
- `calculateStudentCountCoefficient()` - 计算人数系数(私有方法)
- `calculateBoardingCoefficient()` - 计算住校生系数(私有方法)
- `calculateLevelCoefficient()` - 计算层次系数(私有方法)
- `mergeAssessmentScores()` - 合并已保存的评分数据(私有方法)
#### 5. Mapper接口和XML扩展
**文件位置**:
- `backend/ruoyi-modules/ruoyi-bzrkh/src/main/java/org/dromara/bzrkh/mapper/TeacherAssessmentMapper.java`
- `backend/ruoyi-modules/ruoyi-bzrkh/src/main/resources/mapper/bzrkh/TeacherAssessmentMapper.xml`
**新增方法**:
- `selectClassDetail()` - 查询班级详细信息
- `selectRoutineWorkIndicators()` - 查询班主任常规工作指标列表
- `selectClassDeductionIndicator()` - 查询班级扣分指标
- `selectSpecialWorkIndicators()` - 查询特色工作指标列表
- `selectSavedAssessmentScores()` - 查询已保存的评分数据
- `selectSchemeIdByPlanId()` - 根据计划ID查询方案ID
- `selectIndicatorIdByCode()` - 根据指标编码查询指标ID
- `deleteAssessmentScoresByPlanAndTeacher()` - 逻辑删除评分数据
- `insertAssessmentScore()` - 插入评分数据
- `updateAssessmentScoreRemark()` - 更新评分数据备注
## 核心业务逻辑
### 1. 系数计算规则
#### 人数系数(K2)
- 班级人数36~40人(含36人),系数为1
- 超过40人,每增加1名学生,系数增加0.02
- 少于36人,每减少1名学生,系数减少0.02
- 本项系数最低0.7
#### 住校生系数(K3)
- 根据住校生人数,按人均0.005计算
#### 层次系数(K1)
- 技师班(K1=0.95)
- 高级工班(K1=1)
- 中级工班(K1=1.05)
- 预备技师班(K1=0.95)
- 中高职一体(K1=1)
#### 总系数(K)
- 计算公式:K = K1 * (K2 + K3)
### 2. 月考核得分计算
- 计算公式:总系数(K)* (班主任常规工作自评分之和 - 按班级扣分50%计的分值 + 特色工作总分值)
### 3. 数据存储逻辑
1. 先逻辑删除该计划下该教师的历史评分数据
2. 插入班主任常规工作指标评分
3. 插入班级扣分指标评分
4. 插入特色工作指标评分(4个固定指标)
5. 将总体评价存储到备注字段
## 权限配置
### API接口权限
- `bzrkh:teacherAssessment:list` - 查看班级和考核计划列表
- `bzrkh:teacherAssessment:detail` - 查看考核详情
- `bzrkh:teacherAssessment:submit` - 提交考核
详细权限配置请参考:`班主任考核提交功能权限配置.md`
## 数据准备
### 必要的指标数据
系统需要以下5个考核指标才能正常运行:
1. `bzrwjkf001` - 班级扣分
2. `bzrtsgz001` - 及时并妥善处理好学生违纪事件
3. `bzrtsgz002` - 班级活动获奖情况
4. `bzrtsgz003` - 班级宣传工作有特色
5. `bzrtsgz004` - 其他
数据插入脚本:`班主任考核提交功能必要数据.sql`
## 部署说明
### 1. 数据库脚本执行
```sql
-- 执行必要数据插入脚本
source 班主任考核提交功能必要数据.sql;
```
### 2. 后端编译重启
```bash
# 编译ruoyi-bzrkh模块
mvn clean compile -pl ruoyi-modules/ruoyi-bzrkh
# 重启应用服务
# (请根据实际部署方式重启服务)
```
### 3. 前端构建
```bash
# 前端无需重新构建,代码已支持热更新
# 如需构建:
npm run build:prod
```
### 4. 权限配置
1. 登录系统管理后台
2. 进入系统管理-菜单管理
3. 为班主任考核相关菜单配置权限
4. 为相关角色分配权限
## 测试验证
### 1. 基础功能测试
- [ ] 班主任登录系统
- [ ] 查看考核计划列表
- [ ] 点击"提交考核"按钮
- [ ] 弹窗正常显示9张卡片
- [ ] 系数自动计算正确
### 2. 评分功能测试
- [ ] 班主任常规工作指标可正常输入评分
- [ ] 班级扣分可正常输入,50%计算正确
- [ ] 特色工作4个指标可正常输入评分
- [ ] 月考核得分自动计算正确
### 3. 数据保存测试
- [ ] 提交评分数据成功
- [ ] 数据正确保存到数据库
- [ ] 再次打开弹窗数据正确回显
## 技术亮点
1. **响应式系数计算**:前端实时计算各项系数,提供直观的数据展示
2. **组件化设计**:弹窗组件独立封装,便于维护和复用
3. **数据一致性**:采用逻辑删除+重新插入的方式确保数据一致性
4. **权限控制**:完整的权限控制体系,确保数据安全
5. **用户体验**:直观的卡片式布局,清晰的数据展示
6. **实时人数统计**:班级总人数从xg_student_msg表实时统计,确保数据准确性
## 班级人数统计规则(2025-01-27更新)
### 班级总人数统计
- **数据来源**: `xg_student_msg`表
- **统计条件**:
- `class_id` = 班级ID
- `student_status` = '1'(正常状态)
- `deleted` = 0(未逻辑删除)
- **统计方式**: COUNT(1)
### 住校生人数统计
- **数据来源**: `xg_student_msg`表
- **统计条件**:
- `class_id` = 班级ID
- `student_status` = '1'(正常状态)
- `dormitory_room` IS NOT NULL AND `dormitory_room` != ''(有宿舍信息)
- `deleted` = 0(未逻辑删除)
- **统计方式**: COUNT(1)
## 待扩展功能
1. 学生违规处分信息查看功能
2. 考核结果审核功能
3. 考核数据统计分析功能
4. 考核历史记录查询功能
---
**开发完成时间**: 2025-01-27
**开发版本**: V1.0.0
**测试状态**: 待测试验证
@@ -0,0 +1,87 @@
-- ==========================================
-- 班主任考核提交功能必要数据
-- 功能:插入班主任考核提交功能所需的考核指标数据
-- 作者:系统开发
-- 时间:2025-01-27
-- ==========================================
-- 插入必要的考核指标数据
-- 注意:这些指标是班主任考核提交功能必需的,请确保在数据库中存在
-- 1. 班级扣分指标
INSERT INTO `bmzr_assessment_indicator` (
`id`, `tenant_id`, `indicator_name`, `indicator_code`, `indicator_type`,
`parent_id`, `sort_order`, `weight_ratio`, `max_score`, `score`,
`allow_self_evaluation`, `allow_superior_evaluation`, `status`, `remark`,
`create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`
) VALUES (
1882452817132150786, '000000', '班级扣分', 'bzrwjkf001', 'routine',
0, 1, 1.0000, 50.00, 50.00,
'1', '1', '0', '班级管理实效评分指标',
103, 1, NOW(), 1, NOW(), 0
) ON DUPLICATE KEY UPDATE
indicator_name = VALUES(indicator_name),
update_time = NOW();
-- 2. 及时并妥善处理好学生违纪事件
INSERT INTO `bmzr_assessment_indicator` (
`id`, `tenant_id`, `indicator_name`, `indicator_code`, `indicator_type`,
`parent_id`, `sort_order`, `weight_ratio`, `max_score`, `score`,
`allow_self_evaluation`, `allow_superior_evaluation`, `status`, `remark`,
`create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`
) VALUES (
1882452817132150787, '000000', '及时并妥善处理好学生违纪事件', 'bzrtsgz001', 'special',
0, 1, 1.0000, 20.00, 20.00,
'1', '1', '0', '特色工作评分指标',
103, 1, NOW(), 1, NOW(), 0
) ON DUPLICATE KEY UPDATE
indicator_name = VALUES(indicator_name),
update_time = NOW();
-- 3. 班级活动获奖情况
INSERT INTO `bmzr_assessment_indicator` (
`id`, `tenant_id`, `indicator_name`, `indicator_code`, `indicator_type`,
`parent_id`, `sort_order`, `weight_ratio`, `max_score`, `score`,
`allow_self_evaluation`, `allow_superior_evaluation`, `status`, `remark`,
`create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`
) VALUES (
1882452817132150788, '000000', '班级活动获奖情况', 'bzrtsgz002', 'special',
0, 2, 1.0000, 20.00, 20.00,
'1', '1', '0', '特色工作评分指标',
103, 1, NOW(), 1, NOW(), 0
) ON DUPLICATE KEY UPDATE
indicator_name = VALUES(indicator_name),
update_time = NOW();
-- 4. 班级宣传工作有特色
INSERT INTO `bmzr_assessment_indicator` (
`id`, `tenant_id`, `indicator_name`, `indicator_code`, `indicator_type`,
`parent_id`, `sort_order`, `weight_ratio`, `max_score`, `score`,
`allow_self_evaluation`, `allow_superior_evaluation`, `status`, `remark`,
`create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`
) VALUES (
1882452817132150789, '000000', '班级宣传工作有特色', 'bzrtsgz003', 'special',
0, 3, 1.0000, 20.00, 20.00,
'1', '1', '0', '特色工作评分指标',
103, 1, NOW(), 1, NOW(), 0
) ON DUPLICATE KEY UPDATE
indicator_name = VALUES(indicator_name),
update_time = NOW();
-- 5. 其他特色工作
INSERT INTO `bmzr_assessment_indicator` (
`id`, `tenant_id`, `indicator_name`, `indicator_code`, `indicator_type`,
`parent_id`, `sort_order`, `weight_ratio`, `max_score`, `score`,
`allow_self_evaluation`, `allow_superior_evaluation`, `status`, `remark`,
`create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `deleted`
) VALUES (
1882452817132150790, '000000', '其他', 'bzrtsgz004', 'special',
0, 4, 1.0000, 20.00, 20.00,
'1', '1', '0', '特色工作评分指标',
103, 1, NOW(), 1, NOW(), 0
) ON DUPLICATE KEY UPDATE
indicator_name = VALUES(indicator_name),
update_time = NOW();
-- 创建完成提示
SELECT '班主任考核提交功能必要数据插入完成!' AS result;
@@ -0,0 +1,86 @@
# 班主任考核提交功能权限配置
## API接口权限字符
### 新增的API接口权限
以下是新增的班主任考核提交功能相关的API接口权限字符,需要在系统菜单权限中进行配置:
1. **获取考核提交详情数据**
- 权限字符:`bzrkh:teacherAssessment:detail`
- 接口路径:`GET /bzrkh/teacherAssessment/submitDetail`
- 功能描述:获取考核提交弹窗的详情数据
2. **提交考核评分数据**
- 权限字符:`bzrkh:teacherAssessment:submit`
- 接口路径:`POST /bzrkh/teacherAssessment/submitScore`
- 功能描述:提交班主任考核评分数据
### 现有的API接口权限
以下是现有的相关API接口权限:
1. **查询班主任管理的班级列表**
- 权限字符:`bzrkh:teacherAssessment:list`
- 接口路径:`GET /bzrkh/teacherAssessment/classes`
2. **查询班主任考核计划列表**
- 权限字符:`bzrkh:teacherAssessment:list`
- 接口路径:`GET /bzrkh/teacherAssessment/plans`
3. **提交考核(原有)**
- 权限字符:`bzrkh:teacherAssessment:submit`
- 接口路径:`POST /bzrkh/teacherAssessment/submit`
4. **查看考核详情(原有)**
- 权限字符:`bzrkh:teacherAssessment:detail`
- 接口路径:`GET /bzrkh/teacherAssessment/detail`
## 菜单权限配置建议
### 菜单配置
建议在系统管理-菜单管理中配置以下菜单项:
```
父菜单:班主任考核管理
├── 考核计划管理(已有)
└── 班主任考核提交(新增)
├── 权限字符:bzrkh:teacherAssessment:list
├── 权限字符:bzrkh:teacherAssessment:detail
└── 权限字符:bzrkh:teacherAssessment:submit
```
### 角色权限分配
建议为以下角色分配相应权限:
1. **班主任角色**
- `bzrkh:teacherAssessment:list` - 查看班级和考核计划列表
- `bzrkh:teacherAssessment:detail` - 查看考核详情
- `bzrkh:teacherAssessment:submit` - 提交考核
2. **学工管理员角色**
- 全部权限(包括考核计划管理权限)
3. **系统管理员角色**
- 全部权限
## 数据库表说明
### 涉及的主要表
1. `bmzr_assessment_score` - 班主任考核得分表(核心业务表)
2. `bmzr_assessment_plan` - 考核计划表
3. `bmzr_assessment_scheme` - 考核方案表
4. `bmzr_assessment_indicator` - 考核指标表
5. `xg_class_msg` - 班级信息表
6. `xg_student_msg` - 学生信息表
### 关键业务逻辑
1. **系数计算**:根据班级人数、住校生人数、班级层次自动计算系数
2. **指标管理**:支持常规工作指标、班级扣分指标、特色工作指标
3. **数据存储**:评分数据统一存储在`bmzr_assessment_score`表中
4. **数据更新**:采用逻辑删除+重新插入的方式确保数据一致性
## 注意事项
1. 权限配置完成后需要重新登录系统生效
2. 确保相关角色用户已分配对应的菜单权限
3. 建议先在测试环境验证权限配置正确性
4. 需要确保数据库中存在相关的考核指标数据(特别是5个特定编码的指标)
@@ -155,11 +155,14 @@ CREATE TABLE `bmzr_assessment_score` (
`evaluated_user_id` bigint(20) NOT NULL COMMENT '被评价用户ID',
`evaluated_user_name` varchar(50) NOT NULL COMMENT '被评价用户姓名',
`evaluated_dept_id` bigint(20) COMMENT '被评价用户部门ID',
`class_id` bigint(20) COMMENT '班级ID',
`self_score` decimal(5,2) COMMENT '自评分数',
`self_comment` text COMMENT '自评意见',
`self_overall_comment` text COMMENT '自我总体评价',
`self_submit_time` datetime COMMENT '自评提交时间',
`superior_score` decimal(5,2) COMMENT '上级评分数',
`superior_comment` text COMMENT '上级评意见',
`superior_overall_comment` text COMMENT '上级总体评价',
`superior_evaluator_id` bigint(20) COMMENT '上级评价人ID',
`superior_submit_time` datetime COMMENT '上级评提交时间',
`evaluation_level` varchar(20) COMMENT '评价等级 dict_type = bmzr_evaluation_level',
@@ -174,7 +177,7 @@ CREATE TABLE `bmzr_assessment_score` (
`update_time` datetime COMMENT '更新时间',
`deleted` int(1) NOT NULL DEFAULT 0 COMMENT '删除标志(0代表存在 1代表删除)',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_plan_indicator_user` (`plan_id`, `indicator_id`, `evaluated_user_id`),
KEY `uk_plan_indicator_user` (`plan_id`, `indicator_id`, `evaluated_user_id`),
KEY `idx_plan_id` (`plan_id`),
KEY `idx_scheme_id` (`scheme_id`),
KEY `idx_indicator_id` (`indicator_id`),
@@ -70,7 +70,7 @@ export function delBmzrAssessmentIndicator(ids: string | number | Array<string |
*/
export function getIndicatorTypeOptions() {
return request({
url: '/bzrkh/assessmentIndicator/indicatorTypes/options',
url: '/bzrkh/assessmentIndicatorType/listAllIncludeDeleted',
method: 'get'
});
}
@@ -81,7 +81,7 @@ export function getIndicatorTypeOptions() {
*/
export function getParentIndicatorOptions() {
return request({
url: '/bzrkh/assessmentIndicator/parentIndicators/options',
url: '/bzrkh/assessmentIndicator/treeselect',
method: 'get'
});
}
+36 -1
View File
@@ -1,6 +1,13 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { TeacherClassVO, TeacherAssessmentPlanVO, TeacherAssessmentPlanQuery, TeacherClassQuery } from './types';
import {
TeacherClassVO,
TeacherAssessmentPlanVO,
TeacherAssessmentPlanQuery,
TeacherClassQuery,
AssessmentDetailVO,
AssessmentSubmitVO
} from './types';
/**
* 查询班主任管理的班级列表
@@ -58,4 +65,32 @@ export const getAssessmentDetail = (planId: string | number, classId: string | n
classId
}
});
};
/**
* 获取考核提交详情数据(用于提交弹窗)
* @param planId 计划ID
* @param classId 班级ID
*/
export const getAssessmentSubmitDetail = (planId: string | number, classId: string | number): AxiosPromise<AssessmentDetailVO> => {
return request({
url: '/bzrkh/teacherAssessment/submitDetail',
method: 'get',
params: {
planId,
classId
}
});
};
/**
* 提交考核评分数据
* @param data 考核提交数据
*/
export const submitAssessmentScore = (data: AssessmentSubmitVO): AxiosPromise<void> => {
return request({
url: '/bzrkh/teacherAssessment/submitScore',
method: 'post',
data
});
};
@@ -204,4 +204,221 @@ export interface TeacherClassQuery {
* 班级状态:1-正常,2-暂停,3-毕业,4-解散
*/
classStatus?: string;
}
// ========== 新增:考核提交相关类型定义 ==========
/**
* 班级详细信息
*/
export interface ClassDetailVO {
/**
* 班级ID
*/
id?: string | number;
/**
* 学院名称
*/
collegeName?: string;
/**
* 专业名称
*/
majorName?: string;
/**
* 班级名称
*/
className?: string;
/**
* 班级层次:1-中级工 2-高级工 3-预备技师 4-技师 5-中高职一体
*/
classType?: string;
/**
* 班级总人数
*/
totalStudentCount?: number;
/**
* 住校生人数
*/
boardingStudentCount?: number;
}
/**
* 系数信息
*/
export interface CoefficientVO {
/**
* 人数系数(K2)
*/
studentCountCoefficient?: number;
/**
* 住校生系数(K3)
*/
boardingCoefficient?: number;
/**
* 层次系数(K1)
*/
levelCoefficient?: number;
/**
* 总系数(K = K1 * (K2 + K3))
*/
totalCoefficient?: number;
}
/**
* 考核指标信息
*/
export interface AssessmentIndicatorVO {
/**
* 指标ID
*/
id?: string | number;
/**
* 指标名称
*/
indicatorName?: string;
/**
* 指标编码
*/
indicatorCode?: string;
/**
* 指标描述
*/
description?: string;
/**
* 指标分值
*/
score?: number;
/**
* 自评分数
*/
selfScore?: number;
/**
* 自评意见
*/
selfComment?: string;
/**
* 排序
*/
sortOrder?: number;
}
/**
* 特色工作指标
*/
export interface SpecialWorkIndicatorVO {
/**
* 指标编码
*/
indicatorCode?: string;
/**
* 指标名称
*/
indicatorName?: string;
/**
* 分值
*/
score?: number;
/**
* 自评意见
*/
selfComment?: string;
}
/**
* 考核提交数据
*/
export interface AssessmentSubmitVO {
/**
* 考核计划ID
*/
planId?: string | number;
/**
* 班级ID
*/
classId?: string | number;
/**
* 班主任常规工作指标评分
*/
routineWorkScores?: AssessmentIndicatorVO[];
/**
* 班级扣分
*/
classDeduction?: number;
/**
* 班级扣分意见
*/
classDeductionComment?: string;
/**
* 特色工作指标评分
*/
specialWorkScores?: SpecialWorkIndicatorVO[];
/**
* 总体评价
*/
overallComment?: string;
}
/**
* 考核详情数据
*/
export interface AssessmentDetailVO {
/**
* 班级详细信息
*/
classDetail?: ClassDetailVO;
/**
* 系数信息
*/
coefficient?: CoefficientVO;
/**
* 班主任常规工作指标列表
*/
routineWorkIndicators?: AssessmentIndicatorVO[];
/**
* 班级扣分指标
*/
classDeductionIndicator?: AssessmentIndicatorVO;
/**
* 特色工作指标列表
*/
specialWorkIndicators?: SpecialWorkIndicatorVO[];
/**
* 月考核得分
*/
monthlyScore?: number;
/**
* 总体评价
*/
overallComment?: string;
}
+104 -11
View File
@@ -35,15 +35,15 @@
<!-- 表格区域 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<!-- <el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" @click="() => handleSubmitAssessment()" :disabled="!hasSelectedPlan">提交考核</el-button>
<el-button type="primary" plain icon="Edit" @click="() => handleSubmitAssessment()" :disabled="!hasSelectedPlan"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="View" @click="() => handleViewAssessment()" :disabled="!hasSelectedPlan">查看考核</el-button>
<el-button type="success" plain icon="View" @click="() => handleViewAssessment()" :disabled="!hasSelectedPlan"></el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</el-row> -->
</template>
<div class="table-container">
@@ -78,10 +78,22 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="提交考核" placement="top">
<el-button link type="primary" icon="Edit" @click="handleSubmitAssessment(scope.row)">提交考核</el-button>
<el-button
link
type="primary"
icon="Edit"
:disabled="loading || !isDataReady"
@click="handleSubmitAssessment(scope.row)"
></el-button>
</el-tooltip>
<el-tooltip content="查看考核" placement="top">
<el-button link type="success" icon="View" @click="handleViewAssessment(scope.row)">查看考核</el-button>
<el-button
link
type="success"
icon="View"
:disabled="loading || !isDataReady"
@click="handleViewAssessment(scope.row)"
></el-button>
</el-tooltip>
</template>
</el-table-column>
@@ -100,10 +112,19 @@
<el-card shadow="never" v-else-if="!loading && teacherClasses.length === 0">
<el-empty description="您暂时没有担任班主任的班级" />
</el-card>
<!-- 考核提交弹窗 -->
<SubmitDialog
ref="submitDialogRef"
:plan-id="currentPlanId"
:class-id="currentClassId"
@success="handleSubmitSuccess"
/>
</div>
</template>
<script setup name="TeacherAssessment" lang="ts">
import { computed, nextTick } from 'vue';
import {
listTeacherClasses,
listTeacherAssessmentPlans,
@@ -116,6 +137,7 @@ import {
TeacherAssessmentPlanQuery
} from '@/api/bzrkh/teacherAssessment/types';
import { useUserStore } from '@/store/modules/user';
import SubmitDialog from './submitDialog.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStore();
@@ -129,6 +151,11 @@ const selectedPlans = ref<TeacherAssessmentPlanVO[]>([]);
const hasSelectedPlan = ref(false);
const total = ref(0);
// 考核提交相关状态
const currentPlanId = ref<string | number>();
const currentClassId = ref<string | number>();
const submitDialogRef = ref();
const queryFormRef = ref<ElFormInstance>();
const data = reactive<PageData<any, TeacherAssessmentPlanQuery>>({
@@ -144,6 +171,11 @@ const data = reactive<PageData<any, TeacherAssessmentPlanQuery>>({
const { queryParams } = toRefs(data);
// 计算数据是否已准备就绪
const isDataReady = computed(() => {
return teacherClasses.value.length > 0 && activeTab.value !== '';
});
/** 获取班主任管理的班级列表 */
const getTeacherClasses = async () => {
loading.value = true;
@@ -157,10 +189,13 @@ const getTeacherClasses = async () => {
if (teacherClasses.value.length > 0) {
// 默认选中第一个班级
activeTab.value = teacherClasses.value[0].id?.toString() || '';
// 等待activeTab设置完成后再获取列表
await nextTick();
await getList();
}
} catch (error) {
console.error('获取班级列表失败:', error);
proxy?.$modal.msgError('获取班级列表失败,请刷新页面重试');
} finally {
loading.value = false;
}
@@ -222,13 +257,41 @@ const handleSubmitAssessment = async (row?: TeacherAssessmentPlanVO) => {
return;
}
const planNames = plans.map(p => p.planName).join('、');
if (plans.length > 1) {
proxy?.$modal.msgWarning('每次只能提交一个考核计划');
return;
}
// 验证数据是否已准备就绪
if (!isDataReady.value) {
proxy?.$modal.msgWarning('数据加载中,请稍后再试');
return;
}
const plan = plans[0];
// 确保计划ID存在
if (!plan.id) {
proxy?.$modal.msgError('考核计划ID无效');
return;
}
const currentClass = teacherClasses.value.find(c => c.id?.toString() === activeTab.value);
await proxy?.$modal.confirm(`确认要提交班级"${currentClass?.className}"的考核计划"${planNames}"吗?`).then(async () => {
// TODO: 实现提交考核逻辑
proxy?.$modal.msg('提交考核功能待实现');
});
if (!currentClass || !currentClass.id) {
proxy?.$modal.msgError('获取班级信息失败,请刷新页面后重试');
return;
}
// 设置当前考核的计划ID和班级ID
currentPlanId.value = plan.id;
currentClassId.value = currentClass.id;
// 确保参数设置成功后再打开弹窗
await nextTick();
// 打开提交弹窗
submitDialogRef.value?.open();
};
/** 查看考核 */
@@ -239,10 +302,40 @@ const handleViewAssessment = async (row?: TeacherAssessmentPlanVO) => {
return;
}
// 验证数据是否已准备就绪
if (!isDataReady.value) {
proxy?.$modal.msgWarning('数据加载中,请稍后再试');
return;
}
const plan = plans[0];
// 确保计划ID存在
if (!plan.id) {
proxy?.$modal.msgError('考核计划ID无效');
return;
}
const currentClass = teacherClasses.value.find(c => c.id?.toString() === activeTab.value);
if (!currentClass || !currentClass.id) {
proxy?.$modal.msgError('获取班级信息失败,请刷新页面后重试');
return;
}
// TODO: 实现查看考核逻辑
proxy?.$modal.msg('查看考核功能待实现');
};
/** 考核提交成功回调 */
const handleSubmitSuccess = () => {
// 刷新考核计划列表
getList();
// 清空选择
selectedPlans.value = [];
hasSelectedPlan.value = false;
};
onMounted(() => {
getTeacherClasses();
});
@@ -0,0 +1,699 @@
<template>
<el-dialog
:title="dialogTitle"
v-model="visible"
width="1200px"
:close-on-click-modal="false"
append-to-body
@close="handleClose"
>
<div v-loading="loading" class="submit-dialog-content">
<!-- 1. 班级信息卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>班级信息</span>
</div>
</template>
<el-descriptions :column="3" border>
<el-descriptions-item label="学院名称">{{ assessmentDetail?.classDetail?.collegeName || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业名称">{{ assessmentDetail?.classDetail?.majorName || '-' }}</el-descriptions-item>
<el-descriptions-item label="班级名称">{{ assessmentDetail?.classDetail?.className || '-' }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 2. 人数系数卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>人数系数(K2)</span>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="班级总人数">{{ assessmentDetail?.classDetail?.totalStudentCount || 0 }}人</el-descriptions-item>
<el-descriptions-item label="人数系数">{{ formatNumber(assessmentDetail?.coefficient?.studentCountCoefficient, 4) }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 3. 住校生系数卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>住校生系数(K3)</span>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="住校生人数">{{ assessmentDetail?.classDetail?.boardingStudentCount || 0 }}人</el-descriptions-item>
<el-descriptions-item label="住校生系数">{{ formatNumber(assessmentDetail?.coefficient?.boardingCoefficient, 4) }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 4. 层次系数卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>层次系数(K1)</span>
</div>
</template>
<el-descriptions :column="2" border>
<el-descriptions-item label="班级学历层级">{{ getClassTypeText(assessmentDetail?.classDetail?.classType) }}</el-descriptions-item>
<el-descriptions-item label="层次系数">{{ formatNumber(assessmentDetail?.coefficient?.levelCoefficient, 4) }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 5. 总系数卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>总系数(K)</span>
</div>
</template>
<el-descriptions :column="1" border>
<el-descriptions-item label="总系数">{{ formatNumber(assessmentDetail?.coefficient?.totalCoefficient, 6) }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 6. 班主任常规工作(50分)卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>班主任常规工作(50分)</span>
</div>
</template>
<el-table border :data="routineWorkScores" style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="indicatorName" label="指标名称" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="description" label="指标描述" align="center" :show-overflow-tooltip="true" />
<el-table-column prop="score" label="指标分值" width="100" align="center">
<template #default="scope">
<span>{{ scope.row.score || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="自评分数" width="120" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.selfScore"
:min="0"
:max="scope.row.score || 100"
:precision="2"
controls-position="right"
style="width: 100%"
placeholder="请输入分数"
/>
</template>
</el-table-column>
<el-table-column label="自评意见" width="200" align="center">
<template #default="scope">
<el-input
v-model="scope.row.selfComment"
type="textarea"
:rows="2"
placeholder="请输入自评意见"
maxlength="200"
show-word-limit
/>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 7. 班级管理实效(50分)卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>班级管理实效(50分)</span>
</div>
</template>
<el-form :model="managementEffectivenessForm" label-width="150px">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="班级扣分">
<el-input-number
v-model="managementEffectivenessForm.classDeduction"
:min="0"
:precision="2"
controls-position="right"
style="width: 100%"
placeholder="请输入扣分"
@change="calculateDeductionHalf"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="按班级扣分50%计">
<el-input
:value="managementEffectivenessForm.classDeductionHalf?.toFixed(2) || '0.00'"
readonly
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button type="info" plain @click="handleViewStudentViolations">学生违规处分信息查看</el-button>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="自评意见">
<el-input
v-model="managementEffectivenessForm.selfComment"
type="textarea"
:rows="3"
placeholder="请输入自评意见"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 8. 特色工作(加20分)卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>特色工作(加20分)</span>
</div>
</template>
<el-form :model="specialWorkForm" label-width="200px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="及时并妥善处理好学生违纪事件">
<el-input-number
v-model="specialWorkForm.score1"
:min="0"
:max="20"
:precision="2"
controls-position="right"
style="width: 100%"
placeholder="请输入分值"
@change="calculateTotalSpecialScore"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="自评意见">
<el-input
v-model="specialWorkForm.comment1"
type="textarea"
:rows="2"
placeholder="请输入自评意见"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="班级活动获奖情况">
<el-input-number
v-model="specialWorkForm.score2"
:min="0"
:max="20"
:precision="2"
controls-position="right"
style="width: 100%"
placeholder="请输入分值"
@change="calculateTotalSpecialScore"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="自评意见">
<el-input
v-model="specialWorkForm.comment2"
type="textarea"
:rows="2"
placeholder="请输入自评意见"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="班级宣传工作有特色">
<el-input-number
v-model="specialWorkForm.score3"
:min="0"
:max="20"
:precision="2"
controls-position="right"
style="width: 100%"
placeholder="请输入分值"
@change="calculateTotalSpecialScore"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="自评意见">
<el-input
v-model="specialWorkForm.comment3"
type="textarea"
:rows="2"
placeholder="请输入自评意见"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="其他">
<el-input-number
v-model="specialWorkForm.score4"
:min="0"
:max="20"
:precision="2"
controls-position="right"
style="width: 100%"
placeholder="请输入分值"
@change="calculateTotalSpecialScore"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="自评意见">
<el-input
v-model="specialWorkForm.comment4"
type="textarea"
:rows="2"
placeholder="请输入自评意见"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="总分值">
<el-input
:value="specialWorkForm.totalScore?.toFixed(2) || '0.00'"
readonly
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 9. 月考核得分卡片 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span>月考核得分</span>
</div>
</template>
<el-form :model="monthlyScoreForm" label-width="100px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="月考核得分">
<el-input
:value="calculateMonthlyScore().toFixed(2)"
readonly
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="总体评价">
<el-input
v-model="monthlyScoreForm.overallComment"
type="textarea"
:rows="4"
placeholder="请输入总体评价"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
<template #footer>
<div class="dialog-footer">
<el-button :loading="submitLoading" type="primary" @click="handleSubmit">保存</el-button>
<el-button @click="handleClose">取消</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="SubmitDialog" lang="ts">
import {
getAssessmentSubmitDetail,
submitAssessmentScore
} from '@/api/bzrkh/teacherAssessment';
import {
AssessmentDetailVO,
AssessmentIndicatorVO,
SpecialWorkIndicatorVO,
AssessmentSubmitVO
} from '@/api/bzrkh/teacherAssessment/types';
interface Props {
planId?: string | number;
classId?: string | number;
}
const props = defineProps<Props>();
const emit = defineEmits(['success']);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const visible = ref(false);
const loading = ref(false);
const submitLoading = ref(false);
const assessmentDetail = ref<AssessmentDetailVO>();
const routineWorkScores = ref<AssessmentIndicatorVO[]>([]);
// 班级管理实效表单
const managementEffectivenessForm = reactive({
classDeduction: 0,
classDeductionHalf: 0,
selfComment: ''
});
// 特色工作表单
const specialWorkForm = reactive({
score1: 0, // 及时并妥善处理好学生违纪事件
comment1: '',
score2: 0, // 班级活动获奖情况
comment2: '',
score3: 0, // 班级宣传工作有特色
comment3: '',
score4: 0, // 其他
comment4: '',
totalScore: 0
});
// 月考核得分表单
const monthlyScoreForm = reactive({
overallComment: ''
});
const dialogTitle = computed(() => {
const className = assessmentDetail.value?.classDetail?.className || '';
return `${className} - 班主任考核提交`;
});
/** 获取班级层次文本 */
const getClassTypeText = (classType?: string) => {
const typeMap: Record<string, string> = {
'1': '中级工班',
'2': '高级工班',
'3': '预备技师班',
'4': '技师班',
'5': '中高职一体'
};
return typeMap[classType || ''] || '未知';
};
/** 格式化数字显示 */
const formatNumber = (value?: number | string, precision = 4) => {
if (value === undefined || value === null || value === '') {
return '0.' + '0'.repeat(precision);
}
const numValue = typeof value === 'string' ? parseFloat(value) : value;
if (isNaN(numValue)) {
return '0.' + '0'.repeat(precision);
}
return numValue.toFixed(precision);
};
/** 计算班级扣分50%计 */
const calculateDeductionHalf = () => {
managementEffectivenessForm.classDeductionHalf = (managementEffectivenessForm.classDeduction || 0) * 0.5;
};
/** 计算特色工作总分 */
const calculateTotalSpecialScore = () => {
specialWorkForm.totalScore = (specialWorkForm.score1 || 0) +
(specialWorkForm.score2 || 0) +
(specialWorkForm.score3 || 0) +
(specialWorkForm.score4 || 0);
};
/** 计算月考核得分 */
const calculateMonthlyScore = () => {
// 总系数(K)
const totalCoefficient = assessmentDetail.value?.coefficient?.totalCoefficient || 0;
// 班主任常规工作自评分之和
const routineWorkTotal = routineWorkScores.value.reduce((sum, item) => sum + (item.selfScore || 0), 0);
// 按班级扣分50%计的分值
const deductionHalf = managementEffectivenessForm.classDeductionHalf || 0;
// 特色工作总分值
const specialWorkTotal = specialWorkForm.totalScore || 0;
// 月考核得分 = 总系数(K)* (班主任常规工作自评分之和 - 按班级扣分50%计的分值 + 特色工作总分值)
return totalCoefficient * (routineWorkTotal - deductionHalf + specialWorkTotal);
};
/** 打开弹窗 */
const open = async () => {
if (!props.planId || !props.classId) {
proxy?.$modal.msgError('参数错误:缺少计划ID或班级ID');
return;
}
visible.value = true;
await loadAssessmentDetail();
};
/** 加载考核详情数据 */
const loadAssessmentDetail = async () => {
loading.value = true;
try {
const res = await getAssessmentSubmitDetail(props.planId!, props.classId!);
assessmentDetail.value = res.data;
// 初始化班主任常规工作数据
routineWorkScores.value = (assessmentDetail.value?.routineWorkIndicators || []).map(item => ({
...item,
selfScore: item.selfScore || 0,
selfComment: item.selfComment || ''
}));
// 初始化班级管理实效数据
if (assessmentDetail.value?.classDeductionIndicator) {
managementEffectivenessForm.classDeduction = assessmentDetail.value.classDeductionIndicator.selfScore || 0;
managementEffectivenessForm.selfComment = assessmentDetail.value.classDeductionIndicator.selfComment || '';
calculateDeductionHalf();
}
// 初始化特色工作数据
const specialWorkIndicators = assessmentDetail.value?.specialWorkIndicators || [];
specialWorkIndicators.forEach(item => {
switch (item.indicatorCode) {
case 'bzrtsgz001':
specialWorkForm.score1 = Number(item.score) || 0;
specialWorkForm.comment1 = item.selfComment || '';
break;
case 'bzrtsgz002':
specialWorkForm.score2 = Number(item.score) || 0;
specialWorkForm.comment2 = item.selfComment || '';
break;
case 'bzrtsgz003':
specialWorkForm.score3 = Number(item.score) || 0;
specialWorkForm.comment3 = item.selfComment || '';
break;
case 'bzrtsgz004':
specialWorkForm.score4 = Number(item.score) || 0;
specialWorkForm.comment4 = item.selfComment || '';
break;
}
});
// 确保特色工作总分重新计算
calculateTotalSpecialScore();
// 初始化月考核得分数据
monthlyScoreForm.overallComment = assessmentDetail.value?.overallComment || '';
// 调试信息:验证数据是否正确加载
console.log('数据回显完成:', {
totalSpecialScore: specialWorkForm.totalScore,
monthlyScore: calculateMonthlyScore(),
overallComment: monthlyScoreForm.overallComment
});
} catch (error) {
console.error('加载考核详情失败:', error);
proxy?.$modal.msgError('加载考核详情失败');
} finally {
loading.value = false;
}
};
/** 查看学生违规处分信息 */
const handleViewStudentViolations = () => {
proxy?.$modal.msg('学生违规处分信息查看功能暂未实现');
};
/** 提交考核 */
const handleSubmit = async () => {
try {
// 验证必填项
if (routineWorkScores.value.some(item => item.selfScore === undefined || item.selfScore === null)) {
proxy?.$modal.msgWarning('请完善班主任常规工作的自评分数');
return;
}
// 验证必要参数
if (!props.planId || !props.classId) {
proxy?.$modal.msgError('缺少必要参数:计划ID或班级ID');
return;
}
submitLoading.value = true;
// 构造提交数据
const submitData: AssessmentSubmitVO = {
planId: props.planId, // 考核计划ID
classId: props.classId, // 班级ID - 将存储到数据库 class_id 字段
routineWorkScores: routineWorkScores.value,
classDeduction: managementEffectivenessForm.classDeduction,
classDeductionComment: managementEffectivenessForm.selfComment,
specialWorkScores: [
{
indicatorCode: 'bzrtsgz001',
indicatorName: '及时并妥善处理好学生违纪事件',
score: specialWorkForm.score1,
selfComment: specialWorkForm.comment1
},
{
indicatorCode: 'bzrtsgz002',
indicatorName: '班级活动获奖情况',
score: specialWorkForm.score2,
selfComment: specialWorkForm.comment2
},
{
indicatorCode: 'bzrtsgz003',
indicatorName: '班级宣传工作有特色',
score: specialWorkForm.score3,
selfComment: specialWorkForm.comment3
},
{
indicatorCode: 'bzrtsgz004',
indicatorName: '其他',
score: specialWorkForm.score4,
selfComment: specialWorkForm.comment4
}
],
overallComment: monthlyScoreForm.overallComment // 自我总体评价 - 将存储到数据库 self_overall_comment 字段
};
await submitAssessmentScore(submitData);
proxy?.$modal.msgSuccess('考核提交成功');
emit('success');
handleClose();
} catch (error) {
console.error('提交考核失败:', error);
proxy?.$modal.msgError('提交考核失败');
} finally {
submitLoading.value = false;
}
};
/** 关闭弹窗 */
const handleClose = () => {
visible.value = false;
// 重置表单数据
routineWorkScores.value = [];
Object.assign(managementEffectivenessForm, {
classDeduction: 0,
classDeductionHalf: 0,
selfComment: ''
});
Object.assign(specialWorkForm, {
score1: 0,
comment1: '',
score2: 0,
comment2: '',
score3: 0,
comment3: '',
score4: 0,
comment4: '',
totalScore: 0
});
Object.assign(monthlyScoreForm, {
overallComment: ''
});
};
// 暴露给父组件的方法
defineExpose({
open
});
</script>
<style scoped>
.submit-dialog-content {
max-height: 70vh;
overflow-y: auto;
}
.info-card {
margin-bottom: 16px;
}
.info-card:last-child {
margin-bottom: 0;
}
.card-header {
font-weight: 600;
color: #303133;
}
/* 表格样式 */
:deep(.el-table) {
border-radius: 4px;
}
:deep(.el-table th) {
background-color: #f5f7fa;
color: #606266;
font-weight: 600;
}
/* 表单样式 */
:deep(.el-form-item__label) {
font-weight: 500;
}
/* 只读输入框样式 */
:deep(.el-input.is-disabled .el-input__wrapper) {
background-color: #f5f7fa;
}
/* 响应式布局 */
@media (max-width: 768px) {
.submit-dialog-content {
padding: 0 10px;
}
.info-card {
margin-bottom: 12px;
}
}
</style>