feat: 所有页面积分显示支持小数点后两位
- Analytics.vue: 设计师绩效、用户需求、扣分明细、CSV导出 - DesignerHall.vue: 设计师卡片累计积分 - TaskHall.vue: 任务卡片积分、typeDetails积分 - LeaderWorkbench.vue: 任务积分 - MyTasks.vue: 任务积分 - CreateTask.vue: 预估积分、任务类型积分 - RewardRecords.vue: 累计积分、奖励积分 - Goals.vue: 奖励积分 - IncentiveCenter.vue: 用户积分、奖励积分 - Contests.vue: 竞赛得分、奖励积分 - CheckIn.vue: 历史奖励积分 - ScorePoolManagement.vue: 积分变动 - Achievements.vue: 奖励积分
This commit is contained in:
parent
b47e322870
commit
3922d3653b
BIN
backend-src.tar.gz
Normal file
BIN
backend-src.tar.gz
Normal file
Binary file not shown.
@ -16,6 +16,10 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 任务管理控制器
|
||||
*/
|
||||
@ -59,14 +63,25 @@ public class TaskController {
|
||||
@Parameter(description = "任务状态") @RequestParam(required = false) String status,
|
||||
@Parameter(description = "任务类型ID") @RequestParam(required = false) Long taskTypeId,
|
||||
@Parameter(description = "发布者部门ID") @RequestParam(required = false) Long publisherDeptId,
|
||||
@Parameter(description = "发布者部门ID列表(逗号分隔)") @RequestParam(required = false) String publisherDeptIds,
|
||||
@Parameter(description = "接收部门ID") @RequestParam(required = false) Long receiverDeptId,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||
@Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||
|
||||
log.info("查询任务列表: status={}, taskTypeId={}, publisherDeptId={}, receiverDeptId={}, page={}, pageSize={}",
|
||||
status, taskTypeId, publisherDeptId, receiverDeptId, page, pageSize);
|
||||
log.info("查询任务列表: status={}, taskTypeId={}, publisherDeptId={}, publisherDeptIds={}, receiverDeptId={}, page={}, pageSize={}",
|
||||
status, taskTypeId, publisherDeptId, publisherDeptIds, receiverDeptId, page, pageSize);
|
||||
|
||||
IPage<Task> taskPage = taskService.getTaskList(page, pageSize, status, taskTypeId, publisherDeptId, receiverDeptId);
|
||||
// 处理多个发布者部门ID
|
||||
List<Long> publisherDeptIdList = null;
|
||||
if (publisherDeptIds != null && !publisherDeptIds.isEmpty()) {
|
||||
publisherDeptIdList = Arrays.stream(publisherDeptIds.split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.map(Long::parseLong)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
IPage<Task> taskPage = taskService.getTaskList(page, pageSize, status, taskTypeId, publisherDeptId, publisherDeptIdList, receiverDeptId);
|
||||
return Result.success(taskPage);
|
||||
}
|
||||
|
||||
|
||||
@ -56,9 +56,9 @@ public class Task implements Serializable {
|
||||
private Integer quantity;
|
||||
|
||||
/**
|
||||
* 预估积分
|
||||
* 预估积分(支持小数,保留2位)
|
||||
*/
|
||||
private Integer score;
|
||||
private BigDecimal score;
|
||||
|
||||
/**
|
||||
* 实际积分奖励
|
||||
@ -408,8 +408,8 @@ public class Task implements Serializable {
|
||||
public Integer getQuantity() { return quantity; }
|
||||
public void setQuantity(Integer quantity) { this.quantity = quantity; }
|
||||
|
||||
public Integer getScore() { return score; }
|
||||
public void setScore(Integer score) { this.score = score; }
|
||||
public BigDecimal getScore() { return score; }
|
||||
public void setScore(BigDecimal score) { this.score = score; }
|
||||
|
||||
public BigDecimal getScoreReward() { return scoreReward; }
|
||||
public void setScoreReward(BigDecimal scoreReward) { this.scoreReward = scoreReward; }
|
||||
|
||||
@ -52,14 +52,14 @@ public class TaskItem implements Serializable {
|
||||
private Integer quantity;
|
||||
|
||||
/**
|
||||
* 单价积分
|
||||
* 单价积分(支持小数,保留2位)
|
||||
*/
|
||||
private Integer unitScore;
|
||||
private BigDecimal unitScore;
|
||||
|
||||
/**
|
||||
* 总积分
|
||||
* 总积分(支持小数,保留2位)
|
||||
*/
|
||||
private Integer totalScore;
|
||||
private BigDecimal totalScore;
|
||||
|
||||
/**
|
||||
* 预估工时
|
||||
|
||||
@ -326,7 +326,7 @@ public class AnalyticsService {
|
||||
if (t.getScoreReward() != null && t.getScoreReward().compareTo(BigDecimal.ZERO) > 0) {
|
||||
return t.getScoreReward();
|
||||
}
|
||||
return t.getScore() != null ? BigDecimal.valueOf(t.getScore()) : BigDecimal.ZERO;
|
||||
return t.getScore() != null ? t.getScore() : BigDecimal.ZERO;
|
||||
})
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
@ -679,7 +679,7 @@ public class AnalyticsService {
|
||||
if (task.getScoreReward() != null && task.getScoreReward().compareTo(BigDecimal.ZERO) > 0) {
|
||||
taskScore = task.getScoreReward();
|
||||
} else if (task.getScore() != null) {
|
||||
taskScore = BigDecimal.valueOf(task.getScore());
|
||||
taskScore = task.getScore();
|
||||
}
|
||||
|
||||
// 计算已完成任务的积分和延误扣分
|
||||
@ -760,7 +760,7 @@ public class AnalyticsService {
|
||||
|
||||
// 如果完成时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && item.getCompleteTime().isAfter(expectedDeadline)) {
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? BigDecimal.valueOf(item.getTotalScore()) : BigDecimal.ZERO;
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : BigDecimal.ZERO;
|
||||
|
||||
if (itemScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算延误小时数(只计算工作时间段内的延误)
|
||||
@ -795,7 +795,7 @@ public class AnalyticsService {
|
||||
|
||||
// 如果当前时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && now.isAfter(expectedDeadline)) {
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? BigDecimal.valueOf(item.getTotalScore()) : BigDecimal.ZERO;
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : BigDecimal.ZERO;
|
||||
|
||||
if (itemScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算延误小时数(只计算工作时间段内的延误)
|
||||
@ -1492,7 +1492,7 @@ public class AnalyticsService {
|
||||
if (task.getScoreReward() != null && task.getScoreReward().compareTo(BigDecimal.ZERO) > 0) {
|
||||
taskScore = task.getScoreReward();
|
||||
} else if (task.getScore() != null) {
|
||||
taskScore = BigDecimal.valueOf(task.getScore());
|
||||
taskScore = task.getScore();
|
||||
}
|
||||
|
||||
if (taskScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
@ -1544,7 +1544,7 @@ public class AnalyticsService {
|
||||
if (task.getScoreReward() != null && task.getScoreReward().compareTo(BigDecimal.ZERO) > 0) {
|
||||
taskScore = task.getScoreReward();
|
||||
} else if (task.getScore() != null) {
|
||||
taskScore = BigDecimal.valueOf(task.getScore());
|
||||
taskScore = task.getScore();
|
||||
}
|
||||
|
||||
log.info("进行中任务积分计算: taskNo={}, taskScore={}", task.getTaskNo(), taskScore);
|
||||
@ -1622,7 +1622,7 @@ public class AnalyticsService {
|
||||
|
||||
// 如果完成时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && item.getCompleteTime().isAfter(expectedDeadline)) {
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? BigDecimal.valueOf(item.getTotalScore()) : BigDecimal.ZERO;
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : BigDecimal.ZERO;
|
||||
|
||||
if (itemScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算延误小时数(只计算工作时间段内的延误)
|
||||
@ -1678,7 +1678,7 @@ public class AnalyticsService {
|
||||
|
||||
// 如果当前时间超过预期deadline,计算延误扣分
|
||||
if (expectedDeadline != null && now.isAfter(expectedDeadline)) {
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? BigDecimal.valueOf(item.getTotalScore()) : BigDecimal.ZERO;
|
||||
BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : BigDecimal.ZERO;
|
||||
|
||||
if (itemScore.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 计算延误小时数(只计算工作时间段内的延误)
|
||||
|
||||
@ -285,16 +285,18 @@ public class DesignerService {
|
||||
// 累计获得积分(主任务 + 子任务)
|
||||
// 优先使用 scoreReward(实际获得积分),如果没有则使用 score(任务积分)
|
||||
Integer mainTaskScore = completedTasks.stream()
|
||||
.mapToInt(task -> {
|
||||
.map(task -> {
|
||||
if (task.getScoreReward() != null && task.getScoreReward().compareTo(java.math.BigDecimal.ZERO) > 0) {
|
||||
return task.getScoreReward().intValue();
|
||||
return task.getScoreReward();
|
||||
}
|
||||
return task.getScore() != null ? task.getScore() : 0;
|
||||
return task.getScore() != null ? task.getScore() : java.math.BigDecimal.ZERO;
|
||||
})
|
||||
.sum();
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
|
||||
.intValue();
|
||||
Integer subTaskScore = completedSubTasks.stream()
|
||||
.mapToInt(item -> item.getTotalScore() != null ? item.getTotalScore() : 0)
|
||||
.sum();
|
||||
.map(item -> item.getTotalScore() != null ? item.getTotalScore() : java.math.BigDecimal.ZERO)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
|
||||
.intValue();
|
||||
Integer totalScore = mainTaskScore + subTaskScore;
|
||||
stats.setTotalScore(totalScore);
|
||||
log.debug("设计师积分统计: userId={}, mainTaskScore={}, subTaskScore={}, totalScore={}",
|
||||
@ -432,8 +434,9 @@ public class DesignerService {
|
||||
);
|
||||
|
||||
Integer totalScore = completedTasks.stream()
|
||||
.mapToInt(task -> task.getScore() != null ? task.getScore() : 0)
|
||||
.sum();
|
||||
.map(task -> task.getScore() != null ? task.getScore() : java.math.BigDecimal.ZERO)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
|
||||
.intValue();
|
||||
stats.setTotalScore(totalScore);
|
||||
|
||||
// 当月延期任务数
|
||||
|
||||
@ -248,7 +248,7 @@ public class TaskApprovalService {
|
||||
|
||||
// 扣除部门积分
|
||||
try {
|
||||
teamScorePoolService.deductScore(task.getPublisherDeptId(), task.getScore());
|
||||
teamScorePoolService.deductScore(task.getPublisherDeptId(), task.getScore() != null ? task.getScore().intValue() : 0);
|
||||
} catch (Exception e) {
|
||||
log.error("扣除部门积分失败", e);
|
||||
throw new BusinessException(ResultCode.ERROR, "扣除部门积分失败:" + e.getMessage());
|
||||
@ -704,7 +704,7 @@ public class TaskApprovalService {
|
||||
}
|
||||
|
||||
// 重新计算积分
|
||||
int totalScore = taskService.calculateTotalScore(updates.getTaskTypes());
|
||||
java.math.BigDecimal totalScore = taskService.calculateTotalScoreDecimal(updates.getTaskTypes());
|
||||
task.setScore(totalScore);
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,7 +848,9 @@ public class TaskItemService {
|
||||
|
||||
// 发送钉钉群通知并@指定的设计师
|
||||
try {
|
||||
int totalScore = groupItems.stream().mapToInt(item -> item.getTotalScore() != null ? item.getTotalScore() : 0).sum();
|
||||
java.math.BigDecimal totalScore = groupItems.stream()
|
||||
.map(item -> item.getTotalScore() != null ? item.getTotalScore() : java.math.BigDecimal.ZERO)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
String title = "子任务组已指派";
|
||||
StringBuilder content = new StringBuilder();
|
||||
content.append("-------------------------------- \n");
|
||||
@ -1256,8 +1258,8 @@ public class TaskItemService {
|
||||
}
|
||||
}
|
||||
|
||||
// 计算子任务组总积分
|
||||
groupInfo.put("totalScore", item.getTotalScore() != null ? item.getTotalScore() : 0);
|
||||
// 计算子任务组总积分(BigDecimal类型)
|
||||
groupInfo.put("totalScore", item.getTotalScore() != null ? item.getTotalScore() : java.math.BigDecimal.ZERO);
|
||||
|
||||
// 获取前置子任务的素材下载链接(流水线继承)
|
||||
if (item.getGroupNo() != null && item.getGroupNo() > 1) {
|
||||
@ -1294,10 +1296,10 @@ public class TaskItemService {
|
||||
} else {
|
||||
Map<String, Object> groupInfo = groupMap.get(key);
|
||||
groupInfo.put("itemCount", (Integer) groupInfo.get("itemCount") + 1);
|
||||
// 累加积分
|
||||
Integer currentScore = (Integer) groupInfo.get("totalScore");
|
||||
Integer itemScore = item.getTotalScore() != null ? item.getTotalScore() : 0;
|
||||
groupInfo.put("totalScore", currentScore + itemScore);
|
||||
// 累加积分(BigDecimal类型)
|
||||
java.math.BigDecimal currentScore = (java.math.BigDecimal) groupInfo.get("totalScore");
|
||||
java.math.BigDecimal itemScore = item.getTotalScore() != null ? item.getTotalScore() : java.math.BigDecimal.ZERO;
|
||||
groupInfo.put("totalScore", currentScore.add(itemScore));
|
||||
|
||||
// 添加到子任务列表
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@ -115,19 +115,19 @@ public class TaskService {
|
||||
}
|
||||
}
|
||||
|
||||
// 计算总积分和工时:取第一个类型的单价
|
||||
// 计算总积分和工时:取第一个类型的单价(保留2位小数)
|
||||
Long firstTypeId = Long.parseLong(typeIds[0].trim());
|
||||
var firstType = configService.getDemandTypeById(firstTypeId);
|
||||
int totalScore = firstType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).intValue();
|
||||
BigDecimal totalScoreDecimal = firstType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
BigDecimal totalHours = firstType.getEstimatedHours() != null ? firstType.getEstimatedHours().multiply(BigDecimal.valueOf(task.getQuantity())) : BigDecimal.ZERO;
|
||||
|
||||
task.setScore(totalScore);
|
||||
task.setScore(totalScoreDecimal);
|
||||
task.setEstimatedHours(totalHours);
|
||||
|
||||
// 兼容性:同时保存第一个类型到task_type_id
|
||||
task.setTaskTypeId(firstTypeId);
|
||||
|
||||
log.info("创建多类型任务: typeIds={}, score={}", task.getTaskTypeIds(), totalScore);
|
||||
log.info("创建多类型任务: typeIds={}, score={}", task.getTaskTypeIds(), totalScoreDecimal);
|
||||
}
|
||||
// 兼容单类型模式
|
||||
else if (task.getTaskTypeId() != null) {
|
||||
@ -136,17 +136,17 @@ public class TaskService {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型不存在");
|
||||
}
|
||||
|
||||
// 计算总积分和工时
|
||||
int totalScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).intValue();
|
||||
// 计算总积分和工时(保留2位小数)
|
||||
BigDecimal totalScoreSingle = demandType.getUnitScore().multiply(BigDecimal.valueOf(task.getQuantity())).setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
BigDecimal totalHours = demandType.getEstimatedHours() != null ? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(task.getQuantity())) : BigDecimal.ZERO;
|
||||
|
||||
task.setScore(totalScore);
|
||||
task.setScore(totalScoreSingle);
|
||||
task.setEstimatedHours(totalHours);
|
||||
|
||||
// 自动填充taskTypeIds字段
|
||||
task.setTaskTypeIds(String.valueOf(task.getTaskTypeId()));
|
||||
|
||||
log.info("创建单类型任务: typeId={}, score={}", task.getTaskTypeId(), totalScore);
|
||||
log.info("创建单类型任务: typeId={}, score={}", task.getTaskTypeId(), totalScoreSingle);
|
||||
} else {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "请选择任务类型");
|
||||
}
|
||||
@ -155,12 +155,13 @@ public class TaskService {
|
||||
// 检查部门积分是否充足(管理员跳过积分检查)
|
||||
boolean isAdminPublisher = task.getPublisherId() != null && isAdminUser(task.getPublisherId());
|
||||
if (!isAdminPublisher && task.getPublisherDeptId() != null && task.getScore() != null) {
|
||||
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), task.getScore())) {
|
||||
int scoreInt = task.getScore().intValue();
|
||||
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), scoreInt)) {
|
||||
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
|
||||
}
|
||||
|
||||
// 检查周期消耗限制
|
||||
if (!teamScorePoolService.checkPeriodLimit(task.getPublisherDeptId(), task.getScore())) {
|
||||
if (!teamScorePoolService.checkPeriodLimit(task.getPublisherDeptId(), scoreInt)) {
|
||||
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
|
||||
}
|
||||
}
|
||||
@ -250,8 +251,8 @@ public class TaskService {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "流水线任务至少需要一个子任务");
|
||||
}
|
||||
|
||||
// 计算总积分和工时
|
||||
int totalScore = 0;
|
||||
// 计算总积分和工时(使用BigDecimal保留小数精度)
|
||||
BigDecimal totalScoreDecimal = BigDecimal.ZERO;
|
||||
double totalHours = 0;
|
||||
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : dto.getItems()) {
|
||||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||||
@ -259,20 +260,24 @@ public class TaskService {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型ID无效: " + itemDto.getTaskTypeId());
|
||||
}
|
||||
|
||||
int itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue();
|
||||
BigDecimal itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity()));
|
||||
double itemHours = demandType.getEstimatedHours() != null ? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity())).doubleValue() : 0;
|
||||
|
||||
totalScore += itemScore;
|
||||
totalScoreDecimal = totalScoreDecimal.add(itemScore);
|
||||
totalHours += itemHours;
|
||||
}
|
||||
|
||||
// 保留2位小数
|
||||
totalScoreDecimal = totalScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
int totalScoreInt = totalScoreDecimal.intValue(); // 用于积分检查
|
||||
|
||||
// 检查部门积分是否充足(管理员跳过积分检查)
|
||||
if (!isAdmin && user.getDeptId() != null) {
|
||||
if (!teamScorePoolService.checkBalance(user.getDeptId(), totalScore)) {
|
||||
if (!teamScorePoolService.checkBalance(user.getDeptId(), totalScoreInt)) {
|
||||
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
|
||||
}
|
||||
|
||||
if (!teamScorePoolService.checkPeriodLimit(user.getDeptId(), totalScore)) {
|
||||
if (!teamScorePoolService.checkPeriodLimit(user.getDeptId(), totalScoreInt)) {
|
||||
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
|
||||
}
|
||||
}
|
||||
@ -290,7 +295,7 @@ public class TaskService {
|
||||
task.setPublisherDeptId(user.getDeptId());
|
||||
task.setIsPipeline(1);
|
||||
task.setPipelineMode(dto.getPipelineMode());
|
||||
task.setScore(totalScore);
|
||||
task.setScore(totalScoreDecimal);
|
||||
task.setEstimatedHours(BigDecimal.valueOf(totalHours));
|
||||
task.setBrandId(dto.getBrandId());
|
||||
task.setCategoryId(dto.getCategoryId());
|
||||
@ -495,8 +500,8 @@ public class TaskService {
|
||||
int currentDeptIndex = deptIndexMap.getOrDefault(deptKey, 1);
|
||||
int subIndex = groupSubIndexMap.getOrDefault(groupNo, 1);
|
||||
|
||||
// 汇总该分组的积分、工时、数量
|
||||
int groupTotalScore = 0;
|
||||
// 汇总该分组的积分、工时、数量(使用 BigDecimal 保留小数精度)
|
||||
BigDecimal groupTotalScoreDecimal = BigDecimal.ZERO;
|
||||
BigDecimal groupTotalHours = BigDecimal.ZERO;
|
||||
int groupTotalQuantity = 0;
|
||||
Long firstTaskTypeId = null;
|
||||
@ -505,12 +510,13 @@ public class TaskService {
|
||||
|
||||
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : groupItems) {
|
||||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||||
int itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue();
|
||||
// 使用 BigDecimal 保留小数精度
|
||||
BigDecimal itemScoreDecimal = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity()));
|
||||
BigDecimal itemHours = demandType.getEstimatedHours() != null
|
||||
? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity()))
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
groupTotalScore += itemScore;
|
||||
groupTotalScoreDecimal = groupTotalScoreDecimal.add(itemScoreDecimal);
|
||||
groupTotalHours = groupTotalHours.add(itemHours);
|
||||
groupTotalQuantity += itemDto.getQuantity();
|
||||
|
||||
@ -524,7 +530,8 @@ public class TaskService {
|
||||
detail.put("typeName", demandType.getName());
|
||||
detail.put("quantity", itemDto.getQuantity());
|
||||
detail.put("unitScore", demandType.getUnitScore());
|
||||
detail.put("totalScore", itemScore);
|
||||
// 保存精确的小数积分值(保留2位小数)
|
||||
detail.put("totalScore", itemScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP).doubleValue());
|
||||
// 如果指定了设计师,也保存到明细中
|
||||
if (itemDto.getReceiverId() != null) {
|
||||
detail.put("receiverId", itemDto.getReceiverId());
|
||||
@ -536,6 +543,9 @@ public class TaskService {
|
||||
typeDetailsList.add(detail);
|
||||
}
|
||||
|
||||
// 保留2位小数
|
||||
BigDecimal groupTotalScore = groupTotalScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
|
||||
// 创建子任务
|
||||
com.designhub.entity.TaskItem item = new com.designhub.entity.TaskItem();
|
||||
item.setTaskId(task.getId());
|
||||
@ -551,7 +561,10 @@ public class TaskService {
|
||||
log.warn("序列化任务类型明细失败: {}", e.getMessage());
|
||||
}
|
||||
item.setQuantity(groupTotalQuantity);
|
||||
item.setUnitScore(groupTotalScore / Math.max(groupTotalQuantity, 1)); // 平均单价
|
||||
// 计算平均单价(保留2位小数)
|
||||
item.setUnitScore(groupTotalQuantity > 0
|
||||
? groupTotalScore.divide(BigDecimal.valueOf(groupTotalQuantity), 2, java.math.RoundingMode.HALF_UP)
|
||||
: BigDecimal.ZERO);
|
||||
item.setTotalScore(groupTotalScore);
|
||||
item.setEstimatedHours(groupTotalHours);
|
||||
item.setSortOrder(itemIndex);
|
||||
@ -668,7 +681,7 @@ public class TaskService {
|
||||
/**
|
||||
* 获取任务列表
|
||||
*/
|
||||
public IPage<Task> getTaskList(Integer page, Integer pageSize, String status, Long taskTypeId, Long publisherDeptId, Long receiverDeptId) {
|
||||
public IPage<Task> getTaskList(Integer page, Integer pageSize, String status, Long taskTypeId, Long publisherDeptId, List<Long> publisherDeptIds, Long receiverDeptId) {
|
||||
Page<Task> pageParam = new Page<>(page, pageSize);
|
||||
LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
@ -683,7 +696,11 @@ public class TaskService {
|
||||
}
|
||||
|
||||
// 根据发布者部门筛选(部门负责人视角切换时使用)
|
||||
if (publisherDeptId != null) {
|
||||
// 优先使用 publisherDeptIds(多个部门ID列表)
|
||||
if (publisherDeptIds != null && !publisherDeptIds.isEmpty()) {
|
||||
wrapper.in(Task::getPublisherDeptId, publisherDeptIds);
|
||||
log.info("按多个发布者部门筛选: publisherDeptIds={}", publisherDeptIds);
|
||||
} else if (publisherDeptId != null) {
|
||||
wrapper.eq(Task::getPublisherDeptId, publisherDeptId);
|
||||
}
|
||||
|
||||
@ -694,6 +711,9 @@ public class TaskService {
|
||||
wrapper.in(Task::getReceiverDeptId, deptAndChildIds);
|
||||
}
|
||||
|
||||
// 未删除的任务
|
||||
wrapper.eq(Task::getDeleted, 0);
|
||||
|
||||
// 按更新时间倒序
|
||||
wrapper.orderByDesc(Task::getUpdateTime);
|
||||
|
||||
@ -1119,6 +1139,31 @@ public class TaskService {
|
||||
// 设置子任务列表(包含处理人信息和deadline)
|
||||
task.setSubTasks(subTasks);
|
||||
|
||||
// 聚合所有子任务的 typeDetails
|
||||
java.util.List<java.util.Map<String, Object>> allDetails = new java.util.ArrayList<>();
|
||||
for (TaskItem item : subTasks) {
|
||||
if (item.getTypeDetails() != null && !item.getTypeDetails().isEmpty()) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
java.util.List<java.util.Map<String, Object>> itemDetails = objectMapper.readValue(
|
||||
item.getTypeDetails(),
|
||||
new com.fasterxml.jackson.core.type.TypeReference<java.util.List<java.util.Map<String, Object>>>() {}
|
||||
);
|
||||
allDetails.addAll(itemDetails);
|
||||
} catch (Exception e) {
|
||||
log.warn("解析子任务 {} 的 typeDetails 失败: {}", item.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allDetails.isEmpty()) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
task.setTypeDetails(objectMapper.writeValueAsString(allDetails));
|
||||
} catch (Exception e) {
|
||||
log.warn("序列化任务 {} 的 typeDetails 失败: {}", task.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 如果主任务没有receiverName,从子任务中获取接单人
|
||||
if (task.getReceiverName() == null || task.getReceiverName().isEmpty()) {
|
||||
// 收集所有不同的接单人名称
|
||||
@ -1292,8 +1337,8 @@ public class TaskService {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "流水线任务至少需要一个子任务");
|
||||
}
|
||||
|
||||
// 计算总积分和工时
|
||||
int totalScore = 0;
|
||||
// 计算总积分和工时(使用BigDecimal保留小数精度)
|
||||
BigDecimal totalScoreDecimal = BigDecimal.ZERO;
|
||||
double totalHours = 0;
|
||||
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : dto.getItems()) {
|
||||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||||
@ -1301,22 +1346,26 @@ public class TaskService {
|
||||
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型ID无效: " + itemDto.getTaskTypeId());
|
||||
}
|
||||
|
||||
int itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue();
|
||||
BigDecimal itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity()));
|
||||
double itemHours = demandType.getEstimatedHours() != null ?
|
||||
demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity())).doubleValue() : 0;
|
||||
|
||||
totalScore += itemScore;
|
||||
totalScoreDecimal = totalScoreDecimal.add(itemScore);
|
||||
totalHours += itemHours;
|
||||
}
|
||||
|
||||
// 保留2位小数
|
||||
totalScoreDecimal = totalScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
int totalScoreInt = totalScoreDecimal.intValue(); // 用于积分检查
|
||||
|
||||
// 检查部门积分是否充足(管理员跳过积分检查)
|
||||
User user = userService.getUserById(userId);
|
||||
if (!isAdmin && user.getDeptId() != null) {
|
||||
if (!teamScorePoolService.checkBalance(user.getDeptId(), totalScore)) {
|
||||
if (!teamScorePoolService.checkBalance(user.getDeptId(), totalScoreInt)) {
|
||||
throw new BusinessException(ResultCode.INSUFFICIENT_SCORE, "部门积分余额不足");
|
||||
}
|
||||
|
||||
if (!teamScorePoolService.checkPeriodLimit(user.getDeptId(), totalScore)) {
|
||||
if (!teamScorePoolService.checkPeriodLimit(user.getDeptId(), totalScoreInt)) {
|
||||
throw new BusinessException(ResultCode.SCORE_LIMIT_EXCEEDED, "超过本周期积分消耗限制");
|
||||
}
|
||||
}
|
||||
@ -1324,7 +1373,7 @@ public class TaskService {
|
||||
// 更新主任务信息
|
||||
existTask.setTitle(dto.getTitle());
|
||||
existTask.setPipelineMode(dto.getPipelineMode());
|
||||
existTask.setScore(totalScore);
|
||||
existTask.setScore(totalScoreDecimal);
|
||||
existTask.setEstimatedHours(BigDecimal.valueOf(totalHours));
|
||||
existTask.setBrandId(dto.getBrandId());
|
||||
existTask.setCategoryId(dto.getCategoryId());
|
||||
@ -1518,8 +1567,8 @@ public class TaskService {
|
||||
int currentDeptIndex = deptIndexMap.getOrDefault(deptKey, 1);
|
||||
int subIndex = groupSubIndexMap.getOrDefault(groupNo, 1);
|
||||
|
||||
// 汇总该分组的积分、工时、数量
|
||||
int groupTotalScore = 0;
|
||||
// 汇总该分组的积分、工时、数量(使用 BigDecimal 保留小数精度)
|
||||
BigDecimal groupTotalScoreDecimal = BigDecimal.ZERO;
|
||||
BigDecimal groupTotalHours = BigDecimal.ZERO;
|
||||
int groupTotalQuantity = 0;
|
||||
Long firstTaskTypeId = null;
|
||||
@ -1527,12 +1576,13 @@ public class TaskService {
|
||||
|
||||
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : groupItems) {
|
||||
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
|
||||
int itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity())).intValue();
|
||||
// 使用 BigDecimal 保留小数精度
|
||||
BigDecimal itemScoreDecimal = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity()));
|
||||
BigDecimal itemHours = demandType.getEstimatedHours() != null
|
||||
? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity()))
|
||||
: BigDecimal.ZERO;
|
||||
|
||||
groupTotalScore += itemScore;
|
||||
groupTotalScoreDecimal = groupTotalScoreDecimal.add(itemScoreDecimal);
|
||||
groupTotalHours = groupTotalHours.add(itemHours);
|
||||
groupTotalQuantity += itemDto.getQuantity();
|
||||
|
||||
@ -1546,7 +1596,8 @@ public class TaskService {
|
||||
detail.put("typeName", demandType.getName());
|
||||
detail.put("quantity", itemDto.getQuantity());
|
||||
detail.put("unitScore", demandType.getUnitScore());
|
||||
detail.put("totalScore", itemScore);
|
||||
// 保存精确的小数积分值(保留2位小数)
|
||||
detail.put("totalScore", itemScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP).doubleValue());
|
||||
// 如果指定了设计师,也保存到明细中
|
||||
if (itemDto.getReceiverId() != null) {
|
||||
detail.put("receiverId", itemDto.getReceiverId());
|
||||
@ -1558,6 +1609,9 @@ public class TaskService {
|
||||
typeDetailsList.add(detail);
|
||||
}
|
||||
|
||||
// 保留2位小数
|
||||
BigDecimal groupTotalScore = groupTotalScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
|
||||
// 创建子任务
|
||||
com.designhub.entity.TaskItem item = new com.designhub.entity.TaskItem();
|
||||
item.setTaskId(mainTask.getId());
|
||||
@ -1572,7 +1626,10 @@ public class TaskService {
|
||||
log.warn("序列化任务类型明细失败: {}", e.getMessage());
|
||||
}
|
||||
item.setQuantity(groupTotalQuantity);
|
||||
item.setUnitScore(groupTotalScore / Math.max(groupTotalQuantity, 1));
|
||||
// 计算平均单价(保留2位小数)
|
||||
item.setUnitScore(groupTotalQuantity > 0
|
||||
? groupTotalScore.divide(BigDecimal.valueOf(groupTotalQuantity), 2, java.math.RoundingMode.HALF_UP)
|
||||
: BigDecimal.ZERO);
|
||||
item.setTotalScore(groupTotalScore);
|
||||
item.setEstimatedHours(groupTotalHours);
|
||||
item.setSortOrder(itemIndex);
|
||||
@ -1825,7 +1882,7 @@ public class TaskService {
|
||||
|
||||
// 奖励设计师所在部门积分(任务奖励)
|
||||
if (task.getReceiverDeptId() != null && task.getScore() != null) {
|
||||
teamScorePoolService.addScore(task.getReceiverDeptId(), task.getScore());
|
||||
teamScorePoolService.addScore(task.getReceiverDeptId(), task.getScore().intValue());
|
||||
|
||||
// 如果有延误扣分,从积分池中扣除
|
||||
if (delayRecord != null && delayRecord.getPenaltyScore() > 0
|
||||
@ -1959,7 +2016,7 @@ public class TaskService {
|
||||
|
||||
// 返还积分(任务取消时返还给发布部门)
|
||||
if (task.getPublisherDeptId() != null && task.getScore() != null) {
|
||||
teamScorePoolService.addScore(task.getPublisherDeptId(), task.getScore());
|
||||
teamScorePoolService.addScore(task.getPublisherDeptId(), task.getScore().intValue());
|
||||
}
|
||||
|
||||
// 发送通知给接单设计师
|
||||
@ -2127,13 +2184,14 @@ public class TaskService {
|
||||
|
||||
// 审核通过后扣除部门积分
|
||||
if (task.getPublisherDeptId() != null && task.getScore() != null) {
|
||||
int scoreInt = task.getScore().intValue();
|
||||
// 检查积分是否充足
|
||||
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), task.getScore())) {
|
||||
if (!teamScorePoolService.checkBalance(task.getPublisherDeptId(), scoreInt)) {
|
||||
throw new BusinessException("部门积分余额不足,无法通过审核");
|
||||
}
|
||||
|
||||
// 扣除积分
|
||||
teamScorePoolService.consumeScore(task.getPublisherDeptId(), task.getScore());
|
||||
teamScorePoolService.consumeScore(task.getPublisherDeptId(), scoreInt);
|
||||
log.info("审核通过,扣除部门积分: deptId={}, score={}", task.getPublisherDeptId(), task.getScore());
|
||||
}
|
||||
} else if ("reject".equals(request.getResult())) {
|
||||
@ -2492,8 +2550,9 @@ public class TaskService {
|
||||
List<Task> publishedTasks = taskMapper.selectList(scoreQuery);
|
||||
totalScore = publishedTasks.stream()
|
||||
.filter(t -> t.getScore() != null)
|
||||
.mapToInt(Task::getScore)
|
||||
.sum();
|
||||
.map(Task::getScore)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add)
|
||||
.intValue();
|
||||
log.debug("需求部门积分统计: deptIds={}, totalScore={}", deptAndChildIds, totalScore);
|
||||
} else if (isDeptView && "receiving".equals(deptType)) {
|
||||
// 接单部门视角:统计该部门设计师获得的总积分
|
||||
@ -2813,7 +2872,7 @@ public class TaskService {
|
||||
// 计算实际积分奖励(原积分 - 延误扣分)
|
||||
BigDecimal earnedScore = BigDecimal.ZERO;
|
||||
if (task.getScore() != null) {
|
||||
earnedScore = BigDecimal.valueOf(task.getScore());
|
||||
earnedScore = task.getScore();
|
||||
BigDecimal actualReward = earnedScore.subtract(deductedScore);
|
||||
if (actualReward.compareTo(BigDecimal.ZERO) < 0) {
|
||||
actualReward = BigDecimal.ZERO; // 最少为0,不会变成负数
|
||||
@ -3068,17 +3127,24 @@ public class TaskService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务总积分(用于审核时修改)
|
||||
* 计算任务总积分(用于审核时修改)- 返回整数
|
||||
*/
|
||||
public int calculateTotalScore(List<TaskUpdateDTO.TaskTypeUpdateDTO> taskTypes) {
|
||||
int totalScore = 0;
|
||||
return calculateTotalScoreDecimal(taskTypes).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算任务总积分(用于审核时修改)- 返回BigDecimal保留2位小数
|
||||
*/
|
||||
public BigDecimal calculateTotalScoreDecimal(List<TaskUpdateDTO.TaskTypeUpdateDTO> taskTypes) {
|
||||
BigDecimal totalScore = BigDecimal.ZERO;
|
||||
for (TaskUpdateDTO.TaskTypeUpdateDTO taskType : taskTypes) {
|
||||
DemandType demandType = configService.getDemandTypeById(taskType.getTaskTypeId());
|
||||
if (demandType != null && demandType.getUnitScore() != null) {
|
||||
totalScore += demandType.getUnitScore().multiply(BigDecimal.valueOf(taskType.getQuantity())).intValue();
|
||||
totalScore = totalScore.add(demandType.getUnitScore().multiply(BigDecimal.valueOf(taskType.getQuantity())));
|
||||
}
|
||||
}
|
||||
return totalScore;
|
||||
return totalScore.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -89,8 +89,8 @@ public class WarningTaskVO {
|
||||
private Double hoursInfo;
|
||||
|
||||
/**
|
||||
* 积分
|
||||
* 积分(支持小数)
|
||||
*/
|
||||
private Integer score;
|
||||
private java.math.BigDecimal score;
|
||||
}
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
<!-- 奖励信息 -->
|
||||
<div class="achievement-reward">
|
||||
<Icon name="trophy" size="sm" />
|
||||
奖励 {{ achievement.rewardScore }} 积分
|
||||
奖励 {{ formatScore(achievement.rewardScore) }} 积分
|
||||
</div>
|
||||
|
||||
<!-- 已解锁信息 -->
|
||||
@ -162,6 +162,12 @@ const filteredAchievements = computed(() => {
|
||||
return result
|
||||
})
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 加载所有成就
|
||||
const loadAchievements = async () => {
|
||||
loading.value = true
|
||||
|
||||
@ -162,6 +162,9 @@
|
||||
<template v-if="column.key === 'rank'">
|
||||
<span class="rank-badge" :class="getRankClass(index + 1)">{{ index + 1 }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'totalScore'">
|
||||
<span class="score-value">{{ formatScore(record.totalScore) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'completionRate'">
|
||||
<a-progress
|
||||
:percent="Math.round(record.completionRate)"
|
||||
@ -250,7 +253,7 @@
|
||||
<a-rate :value="record.avgRating" disabled allow-half :count="5" class="white-rate" />
|
||||
</template>
|
||||
<template v-else-if="column.key === 'totalScore'">
|
||||
<span class="score-value">{{ record.totalScore || 0 }}</span>
|
||||
<span class="score-value">{{ formatScore(record.totalScore) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'deductionScore'">
|
||||
<a-button
|
||||
@ -260,17 +263,17 @@
|
||||
@click.stop="showDeductionDetails(record)"
|
||||
:style="{ padding: 0, height: 'auto', color: '#ef4444' }"
|
||||
>
|
||||
{{ record.deductionScore || 0 }}
|
||||
{{ formatScore(record.deductionScore) }}
|
||||
</a-button>
|
||||
<span v-else class="deduction-value">
|
||||
{{ record.deductionScore || 0 }}
|
||||
{{ formatScore(record.deductionScore) }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'netScore'">
|
||||
<span class="net-score-value">{{ record.netScore || 0 }}</span>
|
||||
<span class="net-score-value">{{ formatScore(record.netScore) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'targetScore'">
|
||||
<span>{{ record.targetScore || '-' }}</span>
|
||||
<span>{{ record.targetScore ? formatScore(record.targetScore) : '-' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'targetCompletionRate'">
|
||||
<span :class="getTargetRateClass(record.targetCompletionRate)">
|
||||
@ -445,7 +448,7 @@
|
||||
<div class="designer-info">
|
||||
<h4>{{ currentDesigner.realName || currentDesigner.userName }}</h4>
|
||||
<p class="designer-dept">部门:{{ currentDesigner.deptName }}</p>
|
||||
<p class="total-deduction">总扣分:<span class="deduction-number">{{ deductionDetail.totalDeduction || 0 }}</span> 分</p>
|
||||
<p class="total-deduction">总扣分:<span class="deduction-number">{{ formatScore(deductionDetail.totalDeduction) }}</span> 分</p>
|
||||
</div>
|
||||
|
||||
<!-- 延误扣分明细 -->
|
||||
@ -458,9 +461,12 @@
|
||||
:scroll="{ y: 300 }"
|
||||
row-key="taskId"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'deductionScore'">
|
||||
<span class="text-danger">{{ record.deductionScore }} 分</span>
|
||||
<span class="text-danger">{{ formatScore(record.deductionScore) }} 分</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'taskScore'">
|
||||
<span>{{ formatScore(record.taskScore) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'overdueHours'">
|
||||
<span>{{ record.overdueHours }} 小时</span>
|
||||
@ -484,7 +490,7 @@
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'deductionScore'">
|
||||
<span class="text-danger">{{ record.deductionScore }} 分</span>
|
||||
<span class="text-danger">{{ formatScore(record.deductionScore) }} 分</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'deductionDate'">
|
||||
{{ formatDateOnly(record.deductionDate) }}
|
||||
@ -723,7 +729,7 @@ const userColumns = [
|
||||
{ title: '部门', dataIndex: 'deptName', key: 'deptName' },
|
||||
{ title: '发起数', dataIndex: 'totalDemands', key: 'totalDemands', sorter: (a: any, b: any) => a.totalDemands - b.totalDemands, align: 'center' },
|
||||
{ title: '完成数', dataIndex: 'completedDemands', key: 'completedDemands', align: 'center' },
|
||||
{ title: '总积分', dataIndex: 'totalScore', key: 'totalScore', align: 'center' },
|
||||
{ title: '总积分', key: 'totalScore', align: 'center' },
|
||||
{ title: '完成率', key: 'completionRate', width: 150, align: 'center' }
|
||||
]
|
||||
|
||||
@ -1010,7 +1016,7 @@ const exportData = () => {
|
||||
csvContent += '排名,用户名,部门,发起数,完成数,总积分,完成率\n'
|
||||
statistics.userStats.forEach((user: any, index: number) => {
|
||||
const rate = user.totalDemands > 0 ? ((user.completedDemands / user.totalDemands) * 100).toFixed(1) : '0.0'
|
||||
csvContent += `${index + 1},${user.userName || '-'},${user.deptName || '-'},${user.totalDemands},${user.completedDemands},${user.totalScore || 0},${rate}%\n`
|
||||
csvContent += `${index + 1},${user.userName || '-'},${user.deptName || '-'},${user.totalDemands},${user.completedDemands},${formatScore(user.totalScore)},${rate}%\n`
|
||||
})
|
||||
csvContent += '\n'
|
||||
}
|
||||
@ -1029,9 +1035,9 @@ const exportData = () => {
|
||||
// 4. 设计师绩效
|
||||
if (designerPerformance.designerStats && designerPerformance.designerStats.length > 0) {
|
||||
csvContent += '【设计师绩效】\n'
|
||||
csvContent += '排名,设计师,部门,接单量,完成量,完成率,准时率,评分\n'
|
||||
csvContent += '排名,设计师,部门,接单量,完成量,获得积分,扣分,净积分,目标积分,目标完成率,准时率,评分\n'
|
||||
designerPerformance.designerStats.forEach((designer: any, index: number) => {
|
||||
csvContent += `${index + 1},${designer.realName || '-'},${designer.deptName || '-'},${designer.receivedCount || 0},${designer.completedCount || 0},${designer.completionRate || 0}%,${designer.onTimeRate || 0}%,${designer.avgRating?.toFixed(1) || '-'}\n`
|
||||
csvContent += `${index + 1},${designer.realName || '-'},${designer.deptName || '-'},${designer.receivedCount || 0},${designer.completedCount || 0},${formatScore(designer.totalScore)},${formatScore(designer.deductionScore)},${formatScore(designer.netScore)},${designer.targetScore ? formatScore(designer.targetScore) : '-'},${designer.targetScore > 0 ? (designer.targetCompletionRate || 0) + '%' : '-'},${designer.onTimeRate || 0}%,${designer.avgRating?.toFixed(1) || '-'}\n`
|
||||
})
|
||||
csvContent += '\n'
|
||||
}
|
||||
@ -1142,6 +1148,12 @@ const formatDateOnly = (date: string | undefined) => {
|
||||
return dayjs(date).format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 单独加载设计师绩效数据(用于部门筛选)
|
||||
const loadDesignerData = async () => {
|
||||
loading.value = true
|
||||
|
||||
@ -156,7 +156,7 @@
|
||||
连续 {{ record.continuousDays }} 天
|
||||
</div>
|
||||
<div class="history-reward">
|
||||
+{{ record.rewardScore }} 积分
|
||||
+{{ formatScore(record.rewardScore) }} 积分
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -264,6 +264,12 @@ const calculateReward = (continuousDays: number): number => {
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr: string) => {
|
||||
const date = new Date(dateStr)
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
<div class="data-label">当前排名</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-value">{{ myContestData.totalScore }}</div>
|
||||
<div class="data-value">{{ formatScore(myContestData.totalScore) }}</div>
|
||||
<div class="data-label">竞赛得分</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
@ -86,7 +86,7 @@
|
||||
</div>
|
||||
<div class="reward-info" v-if="myContestData.rewardScore > 0">
|
||||
<span class="reward-label">可领取奖励:</span>
|
||||
<span class="reward-value">{{ myContestData.rewardScore }} 积分</span>
|
||||
<span class="reward-value">{{ formatScore(myContestData.rewardScore) }} 积分</span>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@ -126,7 +126,7 @@
|
||||
<span class="stat">{{ participant.taskCompleted }} 任务</span>
|
||||
<span class="stat">{{ participant.fiveStarCount }} 五星</span>
|
||||
</div>
|
||||
<div class="participant-score">{{ participant.totalScore }} 分</div>
|
||||
<div class="participant-score">{{ formatScore(participant.totalScore) }} 分</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -290,6 +290,12 @@ const contestForm = ref({
|
||||
endTime: null as Dayjs | null
|
||||
})
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
@ -196,7 +196,7 @@
|
||||
<span class="group-count">({{ group.items.length }}个任务)</span>
|
||||
</div>
|
||||
<div class="group-summary">
|
||||
<span class="summary-item">{{ group.totalScore }}分</span>
|
||||
<span class="summary-item">{{ formatScore(group.totalScore) }}分</span>
|
||||
<span class="summary-divider">|</span>
|
||||
<span class="summary-item">{{ formatWorkHours(group.totalHours) }}</span>
|
||||
</div>
|
||||
@ -210,7 +210,7 @@
|
||||
<span class="item-icon">└─</span>
|
||||
<span class="item-name">{{ item.name }}</span>
|
||||
<span class="item-quantity">× {{ item.quantity }}</span>
|
||||
<span class="item-score">= {{ item.totalScore }}分</span>
|
||||
<span class="item-score">= {{ formatScore(item.totalScore) }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -971,6 +971,12 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 计算预估积分(累加所有任务类型)
|
||||
const estimatedScore = computed(() => {
|
||||
let totalScore = 0
|
||||
@ -984,7 +990,7 @@ const estimatedScore = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
return totalScore
|
||||
return formatScore(totalScore)
|
||||
})
|
||||
|
||||
// 计算累计时效(小时)
|
||||
|
||||
@ -171,7 +171,7 @@
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">累计积分</div>
|
||||
<div class="stat-value text-brand">{{ designer.totalScore }}</div>
|
||||
<div class="stat-value text-brand">{{ formatScore(designer.totalScore) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -356,6 +356,12 @@ const formatDate = (date: string | null | undefined) => {
|
||||
return dayjs(date).format('MM/DD HH:mm')
|
||||
}
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 获取进度条颜色
|
||||
const getProgressColor = (progress: number) => {
|
||||
if (progress >= 80) return '#31f08b' // 绿色
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
<p class="preset-desc">{{ preset.description }}</p>
|
||||
<div class="preset-reward">
|
||||
<Icon name="trophy" size="sm" color="#FFD700" />
|
||||
<span>奖励 {{ preset.reward }} 积分</span>
|
||||
<span>奖励 {{ formatScore(preset.reward) }} 积分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -104,7 +104,7 @@
|
||||
|
||||
<div class="goal-reward" v-if="goal.rewardScore > 0">
|
||||
<Icon name="trophy" size="sm" color="#FFD700" />
|
||||
<span>完成奖励 {{ goal.rewardScore }} 积分</span>
|
||||
<span>完成奖励 {{ formatScore(goal.rewardScore) }} 积分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -140,7 +140,7 @@
|
||||
:disabled="goal.rewardClaimed === 1"
|
||||
@click="handleClaimReward(goal)"
|
||||
>
|
||||
{{ goal.rewardClaimed === 1 ? '已领取' : `领取 ${goal.rewardScore} 积分` }}
|
||||
{{ goal.rewardClaimed === 1 ? '已领取' : `领取 ${formatScore(goal.rewardScore)} 积分` }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -262,6 +262,12 @@ const goalForm = ref({
|
||||
rewardScore: 50
|
||||
})
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
@ -252,8 +252,14 @@ import {
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 用户数据
|
||||
const userScore = computed(() => authStore.userInfo?.score || 0)
|
||||
const userScore = computed(() => formatScore(authStore.userInfo?.score))
|
||||
const continuousCheckIn = ref(0)
|
||||
const achievementCount = ref(0)
|
||||
const activeGoalCount = ref(0)
|
||||
@ -341,7 +347,7 @@ const formatTime = (time: string) => {
|
||||
const parseRewardValue = (value: string) => {
|
||||
try {
|
||||
const obj = JSON.parse(value)
|
||||
if (obj.score) return `+${obj.score} 积分`
|
||||
if (obj.score) return `+${formatScore(obj.score)} 积分`
|
||||
if (obj.badge) return `获得徽章`
|
||||
if (obj.title) return `获得称号`
|
||||
return '+奖励'
|
||||
|
||||
@ -109,7 +109,7 @@
|
||||
</div>
|
||||
<div class="info-col">
|
||||
<span class="label">积分</span>
|
||||
<span class="value text-brand">{{ task.score }}</span>
|
||||
<span class="value text-brand">{{ formatScore(task.score) }}</span>
|
||||
</div>
|
||||
<div v-if="task.completeTime" class="info-col">
|
||||
<span class="label">完成时间</span>
|
||||
@ -257,6 +257,12 @@ const formatTime = (time: string | undefined) => {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 判断是否逾期
|
||||
const isOverdue = (deadline: string) => {
|
||||
return dayjs(deadline).isBefore(dayjs())
|
||||
|
||||
@ -176,7 +176,7 @@
|
||||
</div>
|
||||
<div class="info-col">
|
||||
<span class="label">积分</span>
|
||||
<span class="value text-brand">{{ task.score }}</span>
|
||||
<span class="value text-brand">{{ formatScore(task.score) }}</span>
|
||||
</div>
|
||||
<div class="info-col">
|
||||
<span class="label">预计工时</span>
|
||||
@ -581,6 +581,12 @@ const formatTime = (time: string | undefined) => {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 判断是否逾期
|
||||
const isOverdue = (deadline: string) => {
|
||||
return dayjs(deadline).isBefore(dayjs())
|
||||
|
||||
@ -90,7 +90,7 @@ const columns = [
|
||||
]
|
||||
|
||||
const totalScore = computed(() => {
|
||||
return records.value.reduce((sum, r) => {
|
||||
const total = records.value.reduce((sum, r) => {
|
||||
try {
|
||||
const obj = JSON.parse(r.rewardValue)
|
||||
return sum + (obj.score || 0)
|
||||
@ -98,8 +98,15 @@ const totalScore = computed(() => {
|
||||
return sum
|
||||
}
|
||||
}, 0)
|
||||
return formatScore(total)
|
||||
})
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
const totalCount = computed(() => total.value)
|
||||
|
||||
const getTypeColor = (type: string) => {
|
||||
@ -125,7 +132,7 @@ const getTypeName = (type: string) => {
|
||||
const parseRewardValue = (value: string) => {
|
||||
try {
|
||||
const obj = JSON.parse(value)
|
||||
if (obj.score) return `+${obj.score} 积分`
|
||||
if (obj.score) return `+${formatScore(obj.score)} 积分`
|
||||
if (obj.badge) return `徽章: ${obj.badge}`
|
||||
if (obj.title) return `称号: ${obj.title}`
|
||||
return value
|
||||
|
||||
@ -256,7 +256,7 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">预估积分</span>
|
||||
<span class="value text-brand">{{ task.totalScore || task.score || '--' }} 分</span>
|
||||
<span class="value text-brand">{{ formatScore(task.totalScore || task.score) }} 分</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">数量</span>
|
||||
@ -278,7 +278,7 @@
|
||||
<div class="type-details-list">
|
||||
<div v-for="(detail, idx) in parseTypeDetails(task.typeDetails)" :key="idx" class="type-detail-item">
|
||||
<span class="type-name">└ {{ detail.typeName }}</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ detail.totalScore }}分</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ formatScore(detail.totalScore) }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -588,7 +588,7 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">预估积分</span>
|
||||
<span class="value text-brand">{{ task.totalScore || task.score || 0 }} 分</span>
|
||||
<span class="value text-brand">{{ formatScore(task.totalScore || task.score) }} 分</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">{{ task._itemType === 'taskGroup' ? '子任务数' : '数量' }}</span>
|
||||
@ -610,7 +610,7 @@
|
||||
<div class="type-details-list">
|
||||
<div v-for="(detail, idx) in parseTypeDetails(task.typeDetails)" :key="idx" class="type-detail-item">
|
||||
<span class="type-name">└ {{ detail.typeName }}</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ detail.totalScore }}分</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ formatScore(detail.totalScore) }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -948,7 +948,7 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">积分</span>
|
||||
<span class="value text-brand">{{ task.score }} 分</span>
|
||||
<span class="value text-brand">{{ formatScore(task.score) }} 分</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">数量</span>
|
||||
@ -996,7 +996,7 @@
|
||||
<div class="type-details-list">
|
||||
<div v-for="(detail, idx) in parseTypeDetails(task.typeDetails)" :key="idx" class="type-detail-item">
|
||||
<span class="type-name">└ {{ detail.typeName }}</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ detail.totalScore }}分</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ formatScore(detail.totalScore) }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1341,7 +1341,7 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">获得积分</span>
|
||||
<span class="value text-brand">{{ task.score }} 分</span>
|
||||
<span class="value text-brand">{{ formatScore(task.score) }} 分</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">数量</span>
|
||||
@ -1712,7 +1712,7 @@
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">积分</span>
|
||||
<span class="value text-brand">{{ task.score }} 分</span>
|
||||
<span class="value text-brand">{{ formatScore(task.score) }} 分</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">数量</span>
|
||||
@ -1760,7 +1760,7 @@
|
||||
<div class="type-details-list">
|
||||
<div v-for="(detail, idx) in parseTypeDetails(task.typeDetails)" :key="idx" class="type-detail-item">
|
||||
<span class="type-name">└ {{ detail.typeName }}</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ detail.totalScore }}分</span>
|
||||
<span class="type-score">x{{ detail.quantity }} = {{ formatScore(detail.totalScore) }}分</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -2891,6 +2891,12 @@ const formatDate = (date: string | undefined) => {
|
||||
return dayjs(date).format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
// 格式化截止时间
|
||||
const formatDeadline = (deadline: string | undefined) => {
|
||||
if (!deadline) return '--'
|
||||
@ -4251,6 +4257,14 @@ const loadInProgressTasks = async () => {
|
||||
const isAdmin = userInfo.isAdmin === 1 || userInfo.isAdmin === true
|
||||
const isDeptLeader = userInfo.isDepartmentLeader === 1 || managedDepartments.value.length > 0
|
||||
|
||||
console.log('=== loadInProgressTasks 调试信息 ===')
|
||||
console.log('userInfo.isDepartmentLeader:', userInfo.isDepartmentLeader)
|
||||
console.log('managedDepartments.value:', managedDepartments.value)
|
||||
console.log('isDeptLeader:', isDeptLeader)
|
||||
console.log('isAdmin:', isAdmin)
|
||||
console.log('currentDeptView.value:', currentDeptView.value)
|
||||
console.log('userDeptType.value:', userDeptType.value)
|
||||
|
||||
let allTasks: any[] = []
|
||||
let totalCount = 0
|
||||
|
||||
@ -4306,17 +4320,21 @@ const loadInProgressTasks = async () => {
|
||||
}
|
||||
} else {
|
||||
// 需求部门视角:看该部门发起的任务(通过 deptId 参数)
|
||||
console.log('需求部门视角切换,查询部门ID:', currentDeptView.value)
|
||||
// 需求部门负责人:不传递status参数,查询所有非草稿的任务
|
||||
const { status, ...paramsWithoutStatus } = params
|
||||
const taskResponse = await taskApi.getTasks({
|
||||
...params,
|
||||
...paramsWithoutStatus,
|
||||
publisherDeptId: currentDeptView.value // 按发布者部门过滤
|
||||
})
|
||||
if (taskResponse.data && taskResponse.data.records) {
|
||||
// 需求部门负责人:显示所有已发布且未完成的任务(pending、in_progress、review等)
|
||||
const tasksWithInProgress = taskResponse.data.records.filter((task: any) => {
|
||||
if (task.isPipeline === 1) {
|
||||
return (task.subTaskInProgress || 0) > 0 || task.status === 'in_progress'
|
||||
}
|
||||
return task.status === 'in_progress'
|
||||
// 排除草稿和已完成的任务
|
||||
const excludeStatuses = ['draft', 'completed', 'cancelled']
|
||||
return !excludeStatuses.includes(task.status)
|
||||
})
|
||||
console.log('需求部门视角切换 - 过滤后任务数:', tasksWithInProgress.length)
|
||||
allTasks = tasksWithInProgress.map((task: any) => ({
|
||||
...task,
|
||||
isSubTask: false
|
||||
@ -4337,17 +4355,43 @@ const loadInProgressTasks = async () => {
|
||||
totalCount += itemResponse.data.total || 0
|
||||
}
|
||||
} else {
|
||||
// 需求部门负责人:看自己部门发起的任务
|
||||
// 需求部门负责人:看自己管理的所有需求部门发起的任务
|
||||
// 获取用户管理的所有需求部门ID(包括自己所属的部门)
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
|
||||
const demandDeptIds: number[] = []
|
||||
|
||||
console.log('需求部门负责人 - userDeptId:', userDeptId.value, 'userDeptType:', userDeptType.value)
|
||||
console.log('需求部门负责人 - userInfo.managedDepartments:', userInfo.managedDepartments)
|
||||
|
||||
// 添加用户自己的部门(如果是需求部门)
|
||||
if (userDeptId.value && userDeptType.value === 'demand') {
|
||||
demandDeptIds.push(userDeptId.value)
|
||||
}
|
||||
|
||||
// 添加用户管理的所有需求部门
|
||||
if (userInfo.managedDepartments && Array.isArray(userInfo.managedDepartments)) {
|
||||
userInfo.managedDepartments.forEach((dept: any) => {
|
||||
console.log('检查管理的部门:', dept.deptId, dept.deptName, 'deptType:', dept.deptType)
|
||||
if (dept.deptType === 'demand' && !demandDeptIds.includes(dept.deptId)) {
|
||||
demandDeptIds.push(dept.deptId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
console.log('需求部门负责人查询的部门ID列表:', demandDeptIds)
|
||||
|
||||
// 需求部门负责人:不传递status参数,查询所有非草稿的任务
|
||||
const { status: _, ...paramsWithoutStatus } = params
|
||||
const taskResponse = await taskApi.getTasks({
|
||||
...params,
|
||||
publisherDeptId: userDeptId.value // 按发布者部门过滤
|
||||
...paramsWithoutStatus,
|
||||
publisherDeptIds: demandDeptIds.join(',') // 按多个发布者部门过滤
|
||||
})
|
||||
if (taskResponse.data && taskResponse.data.records) {
|
||||
// 需求部门负责人:显示所有已发布且未完成的任务(pending、in_progress、review等)
|
||||
const tasksWithInProgress = taskResponse.data.records.filter((task: any) => {
|
||||
if (task.isPipeline === 1) {
|
||||
return (task.subTaskInProgress || 0) > 0 || task.status === 'in_progress'
|
||||
}
|
||||
return task.status === 'in_progress'
|
||||
// 排除草稿和已完成的任务
|
||||
const excludeStatuses = ['draft', 'completed', 'cancelled']
|
||||
return !excludeStatuses.includes(task.status)
|
||||
})
|
||||
allTasks = tasksWithInProgress.map((task: any) => ({
|
||||
...task,
|
||||
|
||||
@ -326,7 +326,7 @@
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'scoreChange'">
|
||||
<span :class="record.scoreChange > 0 ? 'text-green' : 'text-red'">
|
||||
{{ record.scoreChange > 0 ? '+' : '' }}{{ record.scoreChange }}
|
||||
{{ record.scoreChange > 0 ? '+' : '' }}{{ formatScore(record.scoreChange) }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'rewardType'">
|
||||
@ -419,6 +419,12 @@ const recordPagination = ref({
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 格式化积分(保留两位小数)
|
||||
const formatScore = (score: number | null | undefined): string => {
|
||||
if (score === null || score === undefined) return '0.00'
|
||||
return Number(score).toFixed(2)
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user