From b3de960892bc0da4186782a19e2eb1612c93faac Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 9 Sep 2025 15:42:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XskhDisciplineRecordController.java | 15 +- .../XskhRewardRecordController.java | 12 + .../mapper/XskhDisciplineRecordMapper.java | 12 + .../xskh/mapper/XskhRewardRecordMapper.java | 12 + .../service/IXskhDisciplineRecordService.java | 10 + .../service/IXskhRewardRecordService.java | 10 + .../impl/XskhDisciplineRecordServiceImpl.java | 19 + .../impl/XskhRewardRecordServiceImpl.java | 21 + .../mapper/XskhDisciplineRecordMapper.xml | 17 + .../mapper/xskh/XskhRewardRecordMapper.xml | 17 + docs/开发文档/API/API文档总览.md | 1 + .../AssessmentApplicationForm.vue | 478 +++++++++++++----- 12 files changed, 507 insertions(+), 117 deletions(-) diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhDisciplineRecordController.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhDisciplineRecordController.java index 58daab72..be7e95fc 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhDisciplineRecordController.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhDisciplineRecordController.java @@ -151,6 +151,19 @@ public class XskhDisciplineRecordController extends BaseController { @RequestParam String disciplineTypeIds, @RequestParam String startTime, @RequestParam String endTime) { - return R.ok(xskhDisciplineRecordService.getStudentDisciplineRecords(studentId, disciplineTypeIds, startTime, endTime)); + return R.ok(xskhDisciplineRecordService.getStudentDisciplineRecords(studentId, disciplineTypeIds, startTime, + endTime)); + } + + /** + * 获取指定违纪类型在指定时间范围内所有学生的最高总扣分 + */ + @SaCheckPermission("disciplineRecord:disciplineRecord:query") + @GetMapping("/getMaxScoreByTypeAndPeriod/{typeId}") + public R getMaxScoreByTypeAndPeriod( + @NotNull(message = "违纪类型ID不能为空") @PathVariable Long typeId, + @RequestParam String startTime, + @RequestParam String endTime) { + return R.ok(xskhDisciplineRecordService.getMaxScoreByTypeAndPeriod(typeId, startTime, endTime)); } } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhRewardRecordController.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhRewardRecordController.java index c6375b5c..a8e2a19b 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhRewardRecordController.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhRewardRecordController.java @@ -143,4 +143,16 @@ public class XskhRewardRecordController extends BaseController { @RequestParam String endTime) { return R.ok(xskhRewardRecordService.getStudentRewardRecords(studentId, rewardTypeIds, startTime, endTime)); } + + /** + * 获取指定奖励类型在指定时间范围内所有学生的最高总加分 + */ + @SaCheckPermission("rewardRecord:rewardRecord:query") + @GetMapping("/getMaxScoreByTypeAndPeriod/{typeId}") + public R getMaxScoreByTypeAndPeriod( + @NotNull(message = "奖励类型ID不能为空") @PathVariable Long typeId, + @RequestParam String startTime, + @RequestParam String endTime) { + return R.ok(xskhRewardRecordService.getMaxScoreByTypeAndPeriod(typeId, startTime, endTime)); + } } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/mapper/XskhDisciplineRecordMapper.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/mapper/XskhDisciplineRecordMapper.java index 3557020d..75db4990 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/mapper/XskhDisciplineRecordMapper.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/mapper/XskhDisciplineRecordMapper.java @@ -69,4 +69,16 @@ public interface XskhDisciplineRecordMapper extends BaseMapperPlus rewardTypeIds, @Param("startTime") String startTime, @Param("endTime") String endTime); + + /** + * 获取指定奖励类型在指定时间范围内所有学生的最高总加分 + * + * @param typeId 奖励类型ID + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 最高总加分 + */ + java.math.BigDecimal selectMaxScoreByTypeAndPeriod(@Param("typeId") Long typeId, + @Param("startTime") String startTime, + @Param("endTime") String endTime); } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhDisciplineRecordService.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhDisciplineRecordService.java index f6a5ad67..4f061dce 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhDisciplineRecordService.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhDisciplineRecordService.java @@ -91,4 +91,14 @@ public interface IXskhDisciplineRecordService { */ List getStudentDisciplineRecords(Long studentId, String disciplineTypeIds, String startTime, String endTime); + + /** + * 获取指定违纪类型在指定时间范围内所有学生的最高总扣分 + * + * @param typeId 违纪类型ID + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 最高总扣分 + */ + java.math.BigDecimal getMaxScoreByTypeAndPeriod(Long typeId, String startTime, String endTime); } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhRewardRecordService.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhRewardRecordService.java index 4112809f..dd68d0c3 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhRewardRecordService.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhRewardRecordService.java @@ -104,4 +104,14 @@ public interface IXskhRewardRecordService { */ List getStudentRewardRecords(Long studentId, String rewardTypeIds, String startTime, String endTime); + + /** + * 获取指定奖励类型在指定时间范围内所有学生的最高总加分 + * + * @param typeId 奖励类型ID + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 最高总加分 + */ + java.math.BigDecimal getMaxScoreByTypeAndPeriod(Long typeId, String startTime, String endTime); } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhDisciplineRecordServiceImpl.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhDisciplineRecordServiceImpl.java index 9f12aaf5..97a73cbf 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhDisciplineRecordServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhDisciplineRecordServiceImpl.java @@ -267,4 +267,23 @@ public class XskhDisciplineRecordServiceImpl implements IXskhDisciplineRecordSer return baseMapper.selectStudentDisciplineRecords(studentId, typeIds, startTime, endTime); } + + /** + * 获取指定违纪类型在指定时间范围内所有学生的最高总扣分 + */ + @Override + public java.math.BigDecimal getMaxScoreByTypeAndPeriod(Long typeId, String startTime, String endTime) { + log.info("获取违纪类型 {} 在时间范围 {} - {} 内的最高总扣分", typeId, startTime, endTime); + + java.math.BigDecimal maxScore = baseMapper.selectMaxScoreByTypeAndPeriod(typeId, startTime, endTime); + + // 如果查询结果为null,返回0 + if (maxScore == null) { + log.info("违纪类型 {} 在指定时间范围内无数据,返回0", typeId); + return java.math.BigDecimal.ZERO; + } + + log.info("违纪类型 {} 的最高总扣分为: {}", typeId, maxScore); + return maxScore; + } } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhRewardRecordServiceImpl.java b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhRewardRecordServiceImpl.java index ecbd0688..e637eb82 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhRewardRecordServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhRewardRecordServiceImpl.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Arrays; import java.util.stream.Collectors; import java.util.Map; +import lombok.extern.slf4j.Slf4j; /** * 学生奖励记录Service业务层处理 @@ -31,6 +32,7 @@ import java.util.Map; * @author ruoyi * @date 2025-01-03 */ +@Slf4j @RequiredArgsConstructor @Service public class XskhRewardRecordServiceImpl implements IXskhRewardRecordService { @@ -249,4 +251,23 @@ public class XskhRewardRecordServiceImpl implements IXskhRewardRecordService { return baseMapper.selectStudentRewardRecords(studentId, typeIds, startTime, endTime); } + + /** + * 获取指定奖励类型在指定时间范围内所有学生的最高总加分 + */ + @Override + public java.math.BigDecimal getMaxScoreByTypeAndPeriod(Long typeId, String startTime, String endTime) { + log.info("获取奖励类型 {} 在时间范围 {} - {} 内的最高总加分", typeId, startTime, endTime); + + java.math.BigDecimal maxScore = baseMapper.selectMaxScoreByTypeAndPeriod(typeId, startTime, endTime); + + // 如果查询结果为null,返回0 + if (maxScore == null) { + log.info("奖励类型 {} 在指定时间范围内无数据,返回0", typeId); + return java.math.BigDecimal.ZERO; + } + + log.info("奖励类型 {} 的最高总加分为: {}", typeId, maxScore); + return maxScore; + } } diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/XskhDisciplineRecordMapper.xml b/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/XskhDisciplineRecordMapper.xml index 9713a923..ac51253f 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/XskhDisciplineRecordMapper.xml +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/XskhDisciplineRecordMapper.xml @@ -134,4 +134,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and deleted = 0 + + + diff --git a/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhRewardRecordMapper.xml b/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhRewardRecordMapper.xml index 2b4783b5..1d78d650 100644 --- a/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhRewardRecordMapper.xml +++ b/backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhRewardRecordMapper.xml @@ -151,4 +151,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" order by create_time desc + + + diff --git a/docs/开发文档/API/API文档总览.md b/docs/开发文档/API/API文档总览.md index 5d93d987..06e458c5 100644 --- a/docs/开发文档/API/API文档总览.md +++ b/docs/开发文档/API/API文档总览.md @@ -20,6 +20,7 @@ - [学生管理员授权关系API](./学生管理员授权关系API.md) - [学生销假管理API](./学生销假管理API.md) - [假期留宿管理API](./假期留宿管理API.md) - **重要修复:单人留宿计算逻辑**(2025-01-03) +- [均化处理最高分值API](./均化处理最高分值API.md) - **新增**:支持考核申请均化处理功能(2025-01-03) ### 1. 用户认证登录 diff --git a/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue b/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue index b4768de0..dac5e5c1 100644 --- a/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue +++ b/web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue @@ -65,7 +65,7 @@ @@ -92,27 +92,18 @@ @@ -401,6 +392,7 @@ import { getApplicationByStudentAndSchedule, getIndicatorsByScheduleId } from '@/api/xskh/assessmentApplication/assessmentApplication'; +// 注意:暂时使用request直接调用API,避免导入路径问题 // 类型定义 interface XskhAssessmentApplicationApprovalForm { @@ -446,6 +438,9 @@ interface XskhAssessmentSelfScoreForm { selfRemark?: string; teacherScore?: number; teacherRemark?: string; + // 新增字段:用于新算分规则 + deductionScore?: number; // 扣分 + additionScore?: number; // 加分 } interface XskhAssessmentBonusApprovalForm { @@ -600,6 +595,210 @@ const getCurrentUserStudentInfo = async () => { } }; +/** 计算单个指标的自评得分(新算分规则) */ +const calculateIndicatorSelfScore = async (indicatorId: string | number): Promise<{ deduction: number; addition: number }> => { + try { + // 获取指标关联的违纪处分类型和奖励类型 + const indicatorTypesResponse = await request({ + url: `/disciplineRecord/disciplineRecord/getIndicatorTypes/${indicatorId}`, + method: 'get' + }); + + if (indicatorTypesResponse.code !== 200 || !indicatorTypesResponse.data) { + console.warn(`获取指标 ${indicatorId} 的类型信息失败`); + return { deduction: 0, addition: 0 }; + } + + // 修复字段名映射:后端返回下划线格式,前端使用驼峰格式 + const disciplineTypeIds = indicatorTypesResponse.data.discipline_type_ids || indicatorTypesResponse.data.disciplineTypeIds; + const rewardTypeIds = indicatorTypesResponse.data.reward_type_ids || indicatorTypesResponse.data.rewardTypeIds; + + // 并行获取违纪处分记录和奖励记录 + const [disciplineRecords, rewardRecords] = await Promise.all([getDisciplineRecords(disciplineTypeIds), getRewardRecords(rewardTypeIds)]); + + // 计算违纪扣分(正数,表示扣分金额) + let disciplineDeduction = 0; + if (disciplineRecords && disciplineRecords.length > 0) { + disciplineDeduction = disciplineRecords.reduce((sum: number, record: any) => { + const deduction = parseFloat(String(record.scoreDeduction || 0)) || 0; + return sum + deduction; + }, 0); + + // 检查违纪类型是否需要均化处理 + if (disciplineTypeIds) { + disciplineDeduction = await applyEqualizationIfNeeded(disciplineTypeIds, disciplineDeduction, 'discipline'); + } + } + + // 计算奖励加分(正数,表示加分金额) + let rewardAddition = 0; + if (rewardRecords && rewardRecords.length > 0) { + rewardAddition = rewardRecords.reduce((sum: number, record: any) => { + const addition = parseFloat(String(record.scoreAddition || 0)) || 0; + return sum + addition; + }, 0); + + // 检查奖励类型是否需要均化处理 + if (rewardTypeIds) { + rewardAddition = await applyEqualizationIfNeeded(rewardTypeIds, rewardAddition, 'reward'); + } + } + + console.log( + `指标 ${indicatorId} 自评得分计算完成: 扣分=${disciplineDeduction}, 加分=${rewardAddition} (违纪扣分: ${disciplineRecords?.length || 0}条, 奖励加分: ${rewardRecords?.length || 0}条)` + ); + + return { + deduction: Math.round(disciplineDeduction * 10) / 10, + addition: Math.round(rewardAddition * 10) / 10 + }; + } catch (error) { + console.error(`计算指标 ${indicatorId} 自评得分失败:`, error); + return { deduction: 0, addition: 0 }; + } +}; + +/** 获取学生违纪处分记录 */ +const getDisciplineRecords = async (disciplineTypeIds: string): Promise => { + if (!disciplineTypeIds || !form.value.studentId) { + return []; + } + + try { + const response = await request({ + url: '/disciplineRecord/disciplineRecord/getStudentDisciplineRecords', + method: 'get', + params: { + studentId: form.value.studentId, + disciplineTypeIds: disciplineTypeIds, + startTime: form.value.dataStartTime, + endTime: form.value.dataEndTime, + status: 1 // 只查询状态为1的记录 + } + }); + + if (response.code === 200) { + return response.data || []; + } else { + console.warn('获取违纪处分记录失败:', response.msg); + return []; + } + } catch (error) { + console.error('获取违纪处分记录失败:', error); + return []; + } +}; + +/** 获取学生奖励记录 */ +const getRewardRecords = async (rewardTypeIds: string): Promise => { + if (!rewardTypeIds || !form.value.studentId) { + return []; + } + + try { + const response = await request({ + url: '/rewardRecord/rewardRecord/getStudentRewardRecords', + method: 'get', + params: { + studentId: form.value.studentId, + rewardTypeIds: rewardTypeIds, + startTime: form.value.dataStartTime, + endTime: form.value.dataEndTime, + status: 1 // 只查询状态为1的记录 + } + }); + + if (response.code === 200) { + return response.data || []; + } else { + console.warn('获取奖励记录失败:', response.msg); + return []; + } + } catch (error) { + console.error('获取奖励记录失败:', error); + return []; + } +}; + +/** 应用均化处理(如果需要) */ +const applyEqualizationIfNeeded = async (typeIds: string, studentScore: number, scoreType: 'discipline' | 'reward'): Promise => { + if (!typeIds || studentScore === 0) { + return studentScore; + } + + try { + // 将逗号分隔的类型ID字符串转换为数组 + const typeIdArray = typeIds + .split(',') + .map((id) => id.trim()) + .filter((id) => id); + + console.log('typeIdArray:', typeIdArray); + + // 遍历每个类型,检查是否需要均化 + for (const typeId of typeIdArray) { + console.log('typeId:', typeId); + + // 获取类型的均化设置 + const typeDetailsResponse = await request({ + url: scoreType === 'discipline' ? `/disciplineType/disciplineType/${typeId}` : `/rewardType/rewardType/${typeId}`, + method: 'get' + }); + + console.log('typeDetailsResponse:', typeDetailsResponse); + + if (typeDetailsResponse.code === 200 && typeDetailsResponse.data) { + const typeInfo = typeDetailsResponse.data; + + // 检查是否启用均化 (isEqualized: '1' 表示启用) + const isEqualized = typeInfo.isEqualized === '1'; + const equalizedThreshold = parseFloat(String(typeInfo.equalizedThreshold || 0)) || 0; + + if (isEqualized && equalizedThreshold > 0) { + console.log(`类型 ${typeId} 启用了均化处理,均化阈值: ${equalizedThreshold}`); + + // 调用专门的接口获取统计周期内该类型下所有学生的最高总分值 + const maxScoreResponse = await request({ + url: + scoreType === 'discipline' + ? `/disciplineRecord/disciplineRecord/getMaxScoreByTypeAndPeriod/${typeId}` + : `/rewardRecord/rewardRecord/getMaxScoreByTypeAndPeriod/${typeId}`, + method: 'get', + params: { + startTime: form.value.dataStartTime, + endTime: form.value.dataEndTime + } + }); + + if (maxScoreResponse.code === 200 && maxScoreResponse.data !== null && maxScoreResponse.data !== undefined) { + const maxScore = parseFloat(String(maxScoreResponse.data)) || 0; + + console.log(`类型 ${typeId} 统计周期内学生最高总分值: ${maxScore}, 当前学生得分: ${studentScore}, 均化阈值: ${equalizedThreshold}`); + + // 如果最高分值超过了均化阈值,应用均化公式 + if (maxScore > equalizedThreshold) { + const equalizedScore = (studentScore * equalizedThreshold) / maxScore; + console.log(`应用均化公式: ${studentScore} * ${equalizedThreshold} / ${maxScore} = ${equalizedScore}`); + return Math.round(equalizedScore * 10) / 10; // 保留一位小数 + } else { + console.log(`最高分值 ${maxScore} 未超过均化阈值 ${equalizedThreshold},不需要均化处理`); + } + } else { + console.warn(`获取类型 ${typeId} 的最高分值失败:`, maxScoreResponse.msg); + } + } + } + } + + // 如果没有需要均化的类型,返回原始分值 + return studentScore; + } catch (error) { + console.error(`均化处理失败 (类型: ${scoreType}, typeIds: ${typeIds}):`, error); + // 均化失败时返回原始分值,不影响正常计算 + return studentScore; + } +}; + /** 根据考核计划ID获取关联的指标数据 */ const loadIndicatorsByScheduleId = async (scheduleId: string | number) => { try { @@ -607,7 +806,7 @@ const loadIndicatorsByScheduleId = async (scheduleId: string | number) => { if (response.code === 200 && response.data) { // 将指标数据转换为自评得分列表格式 - form.value.selfScoreList = response.data.map((item: any) => ({ + const indicatorList = response.data.map((item: any) => ({ id: undefined, applicationId: undefined, indicatorId: item.indicatorId, @@ -618,12 +817,19 @@ const loadIndicatorsByScheduleId = async (scheduleId: string | number) => { maxScore: item.maxScore, // 修改:使用maxScore字段名,匹配后端期望 planMaxScore: item.maxScore, // 保留planMaxScore用于前端显示 sortOrder: item.sortOrder, - selfScore: 0, + selfScore: 0, // 初始化为0,稍后计算 selfRemark: '', teacherScore: 0, teacherRemark: '' })); + form.value.selfScoreList = indicatorList; + + // 自动计算每个指标的自评得分 + if (form.value.studentId && form.value.dataStartTime && form.value.dataEndTime) { + await calculateAllIndicatorScores(); + } + console.log('获取到考核计划关联的指标数据:', response.data); } else { ElMessage.warning(response.msg || '获取指标数据失败'); @@ -638,115 +844,133 @@ const loadIndicatorsByScheduleId = async (scheduleId: string | number) => { } }; -/** 计算总得分 */ -const calculateTotalScore = () => { - // 计算自评得分 - let selfScore = 0; - if (form.value.selfScoreList && form.value.selfScoreList.length > 0) { - selfScore += form.value.selfScoreList.reduce((sum, item) => { - const score = parseFloat(String(item.selfScore || 0)) || 0; - return sum + score; - }, 0); +/** 计算所有指标的自评得分(新算分规则) */ +const calculateAllIndicatorScores = async () => { + if (!form.value.selfScoreList || form.value.selfScoreList.length === 0) { + return; } + console.log('开始计算所有指标的自评得分(新算分规则)...'); + + // 并行计算所有指标的得分 + const scorePromises = form.value.selfScoreList.map(async (indicator) => { + const { deduction, addition } = await calculateIndicatorSelfScore(indicator.indicatorId); + + // 设置扣分和加分 + indicator.deductionScore = deduction; + indicator.additionScore = addition; + + // 为了兼容现有显示,selfScore设置为净得分(加分-扣分) + indicator.selfScore = addition - deduction; + + return { + indicatorId: indicator.indicatorId, + deduction, + addition, + netScore: indicator.selfScore + }; + }); + + try { + const results = await Promise.all(scorePromises); + console.log('所有指标自评得分计算完成(新算分规则):', results); + + // 重新计算总得分 + calculateTotalScore(); + } catch (error) { + console.error('计算指标自评得分失败:', error); + ElMessage.error('计算指标自评得分失败,请稍后重试'); + } +}; + +/** 计算总得分(新算分规则) */ +const calculateTotalScore = () => { + // 第1步:基础分100分 - 指标模块的扣分,最小为0 + const baseScore = 100; + let totalIndicatorDeduction = 0; + let totalIndicatorAddition = 0; + + // 计算指标模块的总扣分和总加分 + if (form.value.selfScoreList && form.value.selfScoreList.length > 0) { + form.value.selfScoreList.forEach((item) => { + const deduction = parseFloat(String(item.deductionScore || 0)) || 0; + const addition = parseFloat(String(item.additionScore || 0)) || 0; + totalIndicatorDeduction += deduction; + totalIndicatorAddition += addition; + }); + } + + // 第1步:基础分 - 扣分,结果不能小于0 + let step1Score = baseScore - totalIndicatorDeduction; + if (step1Score < 0) { + step1Score = 0; + } + + // 第2步:加上指标模块的加分 + const step2Score = step1Score + totalIndicatorAddition; + + // 第3步:加上附加分模块的分值(可正可负) + let totalBonusScore = 0; if (form.value.bonusApprovalList && form.value.bonusApprovalList.length > 0) { - selfScore += form.value.bonusApprovalList.reduce((sum, item) => { + totalBonusScore = form.value.bonusApprovalList.reduce((sum, item) => { const score = parseFloat(String(item.bonusScore || 0)) || 0; return sum + score; }, 0); } - form.value.selfScore = parseFloat(selfScore.toFixed(1)); + // 最终自评得分 + const finalSelfScore = step2Score + totalBonusScore; + form.value.selfScore = parseFloat(finalSelfScore.toFixed(1)); - // 计算班主任评分 - let teacherScore = 0; + console.log( + `自评得分计算详情: 基础分=${baseScore}, 指标扣分=${totalIndicatorDeduction}, 第1步结果=${step1Score}, 指标加分=${totalIndicatorAddition}, 第2步结果=${step2Score}, 附加分=${totalBonusScore}, 最终得分=${form.value.selfScore}` + ); + + // 班主任评分采用相同逻辑,但使用班主任的评分数据 + let teacherTotalIndicatorDeduction = 0; + let teacherTotalIndicatorAddition = 0; + + // 计算班主任对指标模块的评分 if (form.value.selfScoreList && form.value.selfScoreList.length > 0) { - teacherScore += form.value.selfScoreList.reduce((sum, item) => { - const score = parseFloat(String(item.teacherScore || 0)) || 0; - return sum + score; - }, 0); + form.value.selfScoreList.forEach((item) => { + const teacherScore = parseFloat(String(item.teacherScore || 0)) || 0; + // 班主任评分可能是综合评分,这里需要根据实际情况调整 + if (teacherScore < 0) { + teacherTotalIndicatorDeduction += Math.abs(teacherScore); + } else { + teacherTotalIndicatorAddition += teacherScore; + } + }); } + // 第1步:基础分 - 班主任评定的扣分 + let teacherStep1Score = baseScore - teacherTotalIndicatorDeduction; + if (teacherStep1Score < 0) { + teacherStep1Score = 0; + } + + // 第2步:加上班主任评定的加分 + const teacherStep2Score = teacherStep1Score + teacherTotalIndicatorAddition; + + // 第3步:加上附加分模块的审核得分 + let totalApprovalScore = 0; if (form.value.bonusApprovalList && form.value.bonusApprovalList.length > 0) { - teacherScore += form.value.bonusApprovalList.reduce((sum, item) => { + totalApprovalScore = form.value.bonusApprovalList.reduce((sum, item) => { const score = parseFloat(String(item.approvalScore || 0)) || 0; return sum + score; }, 0); } - form.value.teacherScore = parseFloat(teacherScore.toFixed(1)); + // 最终班主任评分 + const finalTeacherScore = teacherStep2Score + totalApprovalScore; + form.value.teacherScore = parseFloat(finalTeacherScore.toFixed(1)); // 总得分等于班主任评分 - form.value.totalScore = parseFloat(teacherScore.toFixed(1)); -}; + form.value.totalScore = parseFloat(finalTeacherScore.toFixed(1)); -/** 处理自评得分输入验证 */ -const handleSelfScoreInput = (value: string | number, row: XskhAssessmentSelfScoreForm) => { - const numValue = parseFloat(String(value)) || 0; - const maxScore = parseFloat(String(row.planMaxScore)) || 0; - - // 如果输入值超过最高分值,自动限制为最高分值 - if (numValue > maxScore) { - row.selfScore = maxScore; - ElMessage.warning(`自评得分不能超过最高分值 ${maxScore}`); - } else { - row.selfScore = numValue; - } - - // 重新计算总得分 - calculateTotalScore(); -}; - -/** 处理自评得分失焦验证 */ -const handleSelfScoreBlur = (event: Event, row: XskhAssessmentSelfScoreForm) => { - const target = event.target as HTMLInputElement; - const value = parseFloat(target.value) || 0; - const maxScore = parseFloat(String(row.planMaxScore)) || 0; - - // 失焦时再次验证,确保不超过最高分值 - if (value > maxScore) { - row.selfScore = maxScore; - target.value = maxScore.toString(); - ElMessage.warning(`自评得分已自动调整为最高分值 ${maxScore}`); - } - - // 确保值为非负数 - if (value < 0) { - row.selfScore = 0; - target.value = '0'; - } -}; - -/** 验证所有自评得分是否超过最高分值 */ -const validateSelfScores = (): boolean => { - if (!form.value.selfScoreList || form.value.selfScoreList.length === 0) { - return true; - } - - for (let i = 0; i < form.value.selfScoreList.length; i++) { - const item = form.value.selfScoreList[i]; - const selfScore = parseFloat(String(item.selfScore || 0)) || 0; - const maxScore = parseFloat(String(item.planMaxScore || 0)) || 0; - - if (selfScore > maxScore) { - ElMessage.error(`第${i + 1}项指标"${item.indicatorName}"的自评得分(${selfScore})不能超过最高分值(${maxScore})`); - return false; - } - - if (selfScore < 0) { - ElMessage.error(`第${i + 1}项指标"${item.indicatorName}"的自评得分不能为负数`); - return false; - } - } - - return true; -}; - -/** 检查单个指标的自评得分是否超过最高分值 */ -const isScoreExceeded = (row: XskhAssessmentSelfScoreForm): boolean => { - const selfScore = parseFloat(String(row.selfScore || 0)) || 0; - const maxScore = parseFloat(String(row.planMaxScore || 0)) || 0; - return selfScore > maxScore; + console.log( + `班主任评分计算详情: 基础分=${baseScore}, 指标扣分=${teacherTotalIndicatorDeduction}, 第1步结果=${teacherStep1Score}, 指标加分=${teacherTotalIndicatorAddition}, 第2步结果=${teacherStep2Score}, 附加审核分=${totalApprovalScore}, 最终得分=${form.value.teacherScore}` + ); }; /** 添加附加分项目 */ @@ -1096,6 +1320,11 @@ const openDialog = async (id?: string | number, title?: string, schedule?: Asses teacherScore: item.teacherScore || 0, teacherRemark: item.teacherRemark || '' })); + + // 重新计算自评得分(确保数据是最新的) + if (form.value.studentId && form.value.dataStartTime && form.value.dataEndTime) { + await calculateAllIndicatorScores(); + } } // 确保附加分列表正确回显 @@ -1164,6 +1393,11 @@ const openDialog = async (id?: string | number, title?: string, schedule?: Asses teacherScore: item.teacherScore || 0, teacherRemark: item.teacherRemark || '' })); + + // 重新计算自评得分(确保数据是最新的) + if (form.value.studentId && form.value.dataStartTime && form.value.dataEndTime) { + await calculateAllIndicatorScores(); + } } // 确保附加分列表正确回显 @@ -1258,11 +1492,6 @@ const safeConvertId = (id: string | number | undefined): string | undefined => { const handleSave = async () => { assessmentApplicationFormRef.value?.validate(async (valid) => { if (valid) { - // 验证自评得分是否超过最高分值 - if (!validateSelfScores()) { - return; - } - try { // 处理附件数据,将fileList中的ossId合并为evidenceFile字段 if (form.value.bonusApprovalList && form.value.bonusApprovalList.length > 0) { @@ -1310,11 +1539,6 @@ const handleSave = async () => { const handleSubmit = async () => { assessmentApplicationFormRef.value?.validate(async (valid) => { if (valid) { - // 验证自评得分是否超过最高分值 - if (!validateSelfScores()) { - return; - } - try { // 处理附件数据,将fileList中的ossId合并为evidenceFile字段 if (form.value.bonusApprovalList && form.value.bonusApprovalList.length > 0) { @@ -1764,6 +1988,28 @@ defineExpose({ justify-content: center; } +/* 自动计算得分样式 */ +.score-display.auto-calculated { + /* background: #f0f9ff; */ + /* order: 1px solid #38bdf8; */ + /* color: #0369a1; */ + font-weight: 600; + position: relative; +} + +.score-display.auto-calculated::after { + /* content: '自动计算'; */ + position: absolute; + top: -8px; + right: -8px; + background: #38bdf8; + color: white; + font-size: 10px; + padding: 1px 4px; + border-radius: 2px; + font-weight: normal; +} + /* 备注展示样式 */ .remark-display { padding: 8px 12px;