diff --git a/backend/src/main/java/com/designhub/controller/DesignerTargetScoreController.java b/backend/src/main/java/com/designhub/controller/DesignerTargetScoreController.java new file mode 100644 index 0000000..f7add09 --- /dev/null +++ b/backend/src/main/java/com/designhub/controller/DesignerTargetScoreController.java @@ -0,0 +1,57 @@ +package com.designhub.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.designhub.common.Result; +import com.designhub.dto.DesignerTargetScoreDTO; +import com.designhub.entity.Department; +import com.designhub.service.DesignerTargetScoreService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/api/designer-target-score") +@Tag(name = "设计师目标积分管理") +@RequiredArgsConstructor +public class DesignerTargetScoreController { + + private final DesignerTargetScoreService designerTargetScoreService; + + @Operation(summary = "获取目标积分配置列表") + @GetMapping("/list") + public Result>> getList( + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "20") int pageSize, + @RequestParam(required = false) Long deptId, + @RequestParam(required = false) String month + ) { + return Result.success(designerTargetScoreService.getTargetScoreList(page, pageSize, deptId, month)); + } + + @Operation(summary = "保存目标积分配置") + @PostMapping("/save") + public Result save(@RequestBody DesignerTargetScoreDTO dto) { + designerTargetScoreService.saveTargetScore(dto); + return Result.success("保存成功"); + } + + @Operation(summary = "删除目标积分配置") + @DeleteMapping("/{id}") + public Result delete(@PathVariable Long id) { + designerTargetScoreService.deleteTargetScore(id); + return Result.success("删除成功"); + } + + @Operation(summary = "获取所有接单部门") + @GetMapping("/receiving-departments") + public Result> getReceivingDepartments() { + return Result.success(designerTargetScoreService.getReceivingDepartments()); + } +} + diff --git a/backend/src/main/java/com/designhub/dto/DesignerTargetScoreDTO.java b/backend/src/main/java/com/designhub/dto/DesignerTargetScoreDTO.java new file mode 100644 index 0000000..e60da3f --- /dev/null +++ b/backend/src/main/java/com/designhub/dto/DesignerTargetScoreDTO.java @@ -0,0 +1,16 @@ +package com.designhub.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class DesignerTargetScoreDTO { + private Long id; + private Long deptId; + private Long userId; + private BigDecimal targetScore; + private String effectiveMonth; + private BigDecimal deductionRate; +} + diff --git a/backend/src/main/java/com/designhub/entity/DesignerTargetScore.java b/backend/src/main/java/com/designhub/entity/DesignerTargetScore.java new file mode 100644 index 0000000..c01ed36 --- /dev/null +++ b/backend/src/main/java/com/designhub/entity/DesignerTargetScore.java @@ -0,0 +1,56 @@ +package com.designhub.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 设计师目标积分配置表 + */ +@Data +@TableName("designer_target_score") +public class DesignerTargetScore { + + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 部门ID(接单部门) + */ + private Long deptId; + + /** + * 用户ID(可选,如果为空则为部门通用配置) + */ + private Long userId; + + /** + * 目标积分 + */ + private BigDecimal targetScore; + + /** + * 生效年月(格式:yyyy-MM) + */ + private String effectiveMonth; + + /** + * 延误扣分比例(每延误1小时扣除积分百分比,默认1%) + */ + private BigDecimal deductionRate; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; +} + diff --git a/backend/src/main/java/com/designhub/mapper/DesignerTargetScoreMapper.java b/backend/src/main/java/com/designhub/mapper/DesignerTargetScoreMapper.java new file mode 100644 index 0000000..1ca5aec --- /dev/null +++ b/backend/src/main/java/com/designhub/mapper/DesignerTargetScoreMapper.java @@ -0,0 +1,10 @@ +package com.designhub.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.designhub.entity.DesignerTargetScore; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface DesignerTargetScoreMapper extends BaseMapper { +} + diff --git a/backend/src/main/java/com/designhub/service/AnalyticsService.java b/backend/src/main/java/com/designhub/service/AnalyticsService.java index bd4a2d6..e5020ce 100644 --- a/backend/src/main/java/com/designhub/service/AnalyticsService.java +++ b/backend/src/main/java/com/designhub/service/AnalyticsService.java @@ -36,6 +36,7 @@ public class AnalyticsService { private final TeamScorePoolMapper teamScorePoolMapper; private final TaskReviewMapper taskReviewMapper; private final RoleService roleService; + private final DesignerTargetScoreMapper designerTargetScoreMapper; /** * 获取需求统计数据(根据角色返回不同维度的数据) @@ -676,19 +677,63 @@ public class AnalyticsService { log.warn("获取评价数据失败: {}", e.getMessage()); } - // 计算获得积分(使用 score 字段,score_reward 可能未设置) + // 计算获得积分和延误扣分 BigDecimal totalScore = BigDecimal.ZERO; + BigDecimal deductionScore = BigDecimal.ZERO; + + // 获取扣分比例配置(默认每延误1小时扣任务积分的1%) + BigDecimal deductionRate = getDeductionRate(designer.getDeptId(), currentMonth); + for (Task task : designerTasks) { if ("completed".equals(task.getStatus())) { - // 优先使用 scoreReward,如果为空或0则使用 score + // 计算任务积分 + BigDecimal taskScore = BigDecimal.ZERO; if (task.getScoreReward() != null && task.getScoreReward().compareTo(BigDecimal.ZERO) > 0) { - totalScore = totalScore.add(task.getScoreReward()); + taskScore = task.getScoreReward(); } else if (task.getScore() != null) { - totalScore = totalScore.add(BigDecimal.valueOf(task.getScore())); + taskScore = BigDecimal.valueOf(task.getScore()); + } + totalScore = totalScore.add(taskScore); + + // 计算延误扣分(如果超时完成) + if (task.getDeadline() != null && task.getActualEndTime() != null + && task.getActualEndTime().isAfter(task.getDeadline())) { + // 计算延误小时数 + long overdueHours = Duration.between(task.getDeadline(), task.getActualEndTime()).toHours(); + if (overdueHours > 0 && taskScore.compareTo(BigDecimal.ZERO) > 0) { + // 扣分 = 任务积分 * 延误小时数 * 扣分比例 + BigDecimal taskDeduction = taskScore + .multiply(BigDecimal.valueOf(overdueHours)) + .multiply(deductionRate) + .divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); + // 扣分不能超过任务积分 + if (taskDeduction.compareTo(taskScore) > 0) { + taskDeduction = taskScore; + } + deductionScore = deductionScore.add(taskDeduction); + } } } } + // 净积分 = 总积分 - 扣分 + BigDecimal netScore = totalScore.subtract(deductionScore); + if (netScore.compareTo(BigDecimal.ZERO) < 0) { + netScore = BigDecimal.ZERO; + } + + // 获取目标积分 + BigDecimal targetScore = getTargetScore(designer.getId(), designer.getDeptId(), currentMonth); + + // 计算目标完成率 + Double targetCompletionRate = 0.0; + if (targetScore != null && targetScore.compareTo(BigDecimal.ZERO) > 0) { + targetCompletionRate = netScore.divide(targetScore, 4, RoundingMode.HALF_UP) + .multiply(BigDecimal.valueOf(100)) + .doubleValue(); + targetCompletionRate = Math.round(targetCompletionRate * 10) / 10.0; + } + // 本周完成数 int weekCompletedCount = (int) designerTasks.stream() .filter(t -> "completed".equals(t.getStatus()) && @@ -716,6 +761,10 @@ public class AnalyticsService { .fiveStarCount(fiveStarCount) .satisfactionRate(Math.round(satisfactionRate * 10) / 10.0) .totalScore(totalScore) + .deductionScore(deductionScore) + .netScore(netScore) + .targetScore(targetScore) + .targetCompletionRate(targetCompletionRate) .weekCompletedCount(weekCompletedCount) .monthCompletedCount(completedCount) .build()); @@ -1067,5 +1116,101 @@ public class AnalyticsService { default: return status; } } + + /** + * 获取延误扣分比例 + * @param deptId 部门ID + * @param month 月份 yyyy-MM + * @return 扣分比例(默认1%) + */ + private BigDecimal getDeductionRate(Long deptId, String month) { + if (deptId == null) { + return BigDecimal.ONE; // 默认1% + } + + try { + // 先查找指定月份的配置 + DesignerTargetScore config = designerTargetScoreMapper.selectOne( + new LambdaQueryWrapper() + .eq(DesignerTargetScore::getDeptId, deptId) + .isNull(DesignerTargetScore::getUserId) // 部门通用配置 + .eq(DesignerTargetScore::getEffectiveMonth, month) + ); + + if (config != null && config.getDeductionRate() != null) { + return config.getDeductionRate(); + } + + // 查找通用配置(没有指定月份的) + config = designerTargetScoreMapper.selectOne( + new LambdaQueryWrapper() + .eq(DesignerTargetScore::getDeptId, deptId) + .isNull(DesignerTargetScore::getUserId) + .isNull(DesignerTargetScore::getEffectiveMonth) + ); + + if (config != null && config.getDeductionRate() != null) { + return config.getDeductionRate(); + } + } catch (Exception e) { + log.warn("获取扣分比例配置失败: deptId={}, month={}", deptId, month, e); + } + + return BigDecimal.ONE; // 默认1% + } + + /** + * 获取设计师目标积分 + * @param userId 用户ID + * @param deptId 部门ID + * @param month 月份 yyyy-MM + * @return 目标积分 + */ + private BigDecimal getTargetScore(Long userId, Long deptId, String month) { + try { + // 先查找用户个人配置 + if (userId != null) { + DesignerTargetScore config = designerTargetScoreMapper.selectOne( + new LambdaQueryWrapper() + .eq(DesignerTargetScore::getUserId, userId) + .eq(DesignerTargetScore::getEffectiveMonth, month) + ); + + if (config != null && config.getTargetScore() != null) { + return config.getTargetScore(); + } + } + + // 查找部门通用配置 + if (deptId != null) { + DesignerTargetScore config = designerTargetScoreMapper.selectOne( + new LambdaQueryWrapper() + .eq(DesignerTargetScore::getDeptId, deptId) + .isNull(DesignerTargetScore::getUserId) + .eq(DesignerTargetScore::getEffectiveMonth, month) + ); + + if (config != null && config.getTargetScore() != null) { + return config.getTargetScore(); + } + + // 查找部门默认配置(没有指定月份) + config = designerTargetScoreMapper.selectOne( + new LambdaQueryWrapper() + .eq(DesignerTargetScore::getDeptId, deptId) + .isNull(DesignerTargetScore::getUserId) + .isNull(DesignerTargetScore::getEffectiveMonth) + ); + + if (config != null && config.getTargetScore() != null) { + return config.getTargetScore(); + } + } + } catch (Exception e) { + log.warn("获取目标积分配置失败: userId={}, deptId={}, month={}", userId, deptId, month, e); + } + + return BigDecimal.ZERO; // 默认0 + } } diff --git a/backend/src/main/java/com/designhub/service/DesignerTargetScoreService.java b/backend/src/main/java/com/designhub/service/DesignerTargetScoreService.java new file mode 100644 index 0000000..7ef9dc0 --- /dev/null +++ b/backend/src/main/java/com/designhub/service/DesignerTargetScoreService.java @@ -0,0 +1,158 @@ +package com.designhub.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.designhub.dto.DesignerTargetScoreDTO; +import com.designhub.entity.Department; +import com.designhub.entity.DesignerTargetScore; +import com.designhub.entity.User; +import com.designhub.mapper.DepartmentMapper; +import com.designhub.mapper.DesignerTargetScoreMapper; +import com.designhub.mapper.UserMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DesignerTargetScoreService { + + private final DesignerTargetScoreMapper designerTargetScoreMapper; + private final DepartmentMapper departmentMapper; + private final UserMapper userMapper; + + /** + * 获取目标积分配置列表 + */ + public Page> getTargetScoreList(int page, int pageSize, Long deptId, String month) { + Page pageParam = new Page<>(page, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (deptId != null) { + wrapper.eq(DesignerTargetScore::getDeptId, deptId); + } + if (month != null && !month.isEmpty()) { + wrapper.eq(DesignerTargetScore::getEffectiveMonth, month); + } + wrapper.orderByDesc(DesignerTargetScore::getCreateTime); + + Page result = designerTargetScoreMapper.selectPage(pageParam, wrapper); + + // 转换为包含部门名称和用户名的结果 + Page> resultPage = new Page<>(page, pageSize); + resultPage.setTotal(result.getTotal()); + + List> records = result.getRecords().stream().map(config -> { + Map map = new HashMap<>(); + map.put("id", config.getId()); + map.put("deptId", config.getDeptId()); + map.put("userId", config.getUserId()); + map.put("targetScore", config.getTargetScore()); + map.put("effectiveMonth", config.getEffectiveMonth()); + map.put("deductionRate", config.getDeductionRate()); + map.put("createTime", config.getCreateTime()); + + // 获取部门名称 + if (config.getDeptId() != null) { + Department dept = departmentMapper.selectById(config.getDeptId()); + map.put("deptName", dept != null ? dept.getDeptName() : ""); + } + + // 获取用户名 + if (config.getUserId() != null) { + User user = userMapper.selectById(config.getUserId()); + map.put("userName", user != null ? user.getRealName() : ""); + } else { + map.put("userName", "部门通用配置"); + } + + return map; + }).collect(Collectors.toList()); + + resultPage.setRecords(records); + return resultPage; + } + + /** + * 保存目标积分配置 + */ + @Transactional + public void saveTargetScore(DesignerTargetScoreDTO dto) { + DesignerTargetScore config; + + if (dto.getId() != null) { + // 更新 + config = designerTargetScoreMapper.selectById(dto.getId()); + if (config == null) { + config = new DesignerTargetScore(); + } + } else { + // 新增 - 检查是否已存在 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DesignerTargetScore::getDeptId, dto.getDeptId()); + + if (dto.getUserId() != null) { + wrapper.eq(DesignerTargetScore::getUserId, dto.getUserId()); + } else { + wrapper.isNull(DesignerTargetScore::getUserId); + } + + if (dto.getEffectiveMonth() != null && !dto.getEffectiveMonth().isEmpty()) { + wrapper.eq(DesignerTargetScore::getEffectiveMonth, dto.getEffectiveMonth()); + } else { + wrapper.isNull(DesignerTargetScore::getEffectiveMonth); + } + + config = designerTargetScoreMapper.selectOne(wrapper); + if (config == null) { + config = new DesignerTargetScore(); + config.setCreateTime(LocalDateTime.now()); + } + } + + config.setDeptId(dto.getDeptId()); + config.setUserId(dto.getUserId()); + config.setTargetScore(dto.getTargetScore()); + config.setEffectiveMonth(dto.getEffectiveMonth()); + config.setDeductionRate(dto.getDeductionRate()); + config.setUpdateTime(LocalDateTime.now()); + + if (config.getId() != null) { + designerTargetScoreMapper.updateById(config); + } else { + designerTargetScoreMapper.insert(config); + } + + log.info("保存设计师目标积分配置: deptId={}, userId={}, targetScore={}, month={}", + dto.getDeptId(), dto.getUserId(), dto.getTargetScore(), dto.getEffectiveMonth()); + } + + /** + * 删除目标积分配置 + */ + public void deleteTargetScore(Long id) { + designerTargetScoreMapper.deleteById(id); + log.info("删除设计师目标积分配置: id={}", id); + } + + /** + * 获取所有接单部门 + */ + public List getReceivingDepartments() { + return departmentMapper.selectList( + new LambdaQueryWrapper() + .eq(Department::getDeptType, "receiving") + .eq(Department::getStatus, 1) + .orderByAsc(Department::getSortOrder) + ); + } +} + diff --git a/backend/src/main/java/com/designhub/vo/DesignerPerformanceVO.java b/backend/src/main/java/com/designhub/vo/DesignerPerformanceVO.java index 1c7efcb..d9d65f2 100644 --- a/backend/src/main/java/com/designhub/vo/DesignerPerformanceVO.java +++ b/backend/src/main/java/com/designhub/vo/DesignerPerformanceVO.java @@ -57,6 +57,10 @@ public class DesignerPerformanceVO { private Integer fiveStarCount; // 5星评价数 private Double satisfactionRate; // 好评率 private BigDecimal totalScore; // 获得总积分 + private BigDecimal deductionScore; // 延误扣分 + private BigDecimal netScore; // 净积分(总积分-扣分) + private BigDecimal targetScore; // 当月目标积分 + private Double targetCompletionRate;// 目标完成率 private Integer weekCompletedCount; // 本周完成数 private Integer monthCompletedCount;// 本月完成数 } diff --git a/frontend/src/api/system.ts b/frontend/src/api/system.ts index 11d5694..335b413 100644 --- a/frontend/src/api/system.ts +++ b/frontend/src/api/system.ts @@ -97,3 +97,49 @@ export const initDefaultConfigs = () => { return request.post('/system-config/init') } +// ==================== 设计师目标积分配置 ==================== + +export interface DesignerTargetScore { + id?: number + deptId: number + userId?: number + targetScore: number + effectiveMonth?: string + deductionRate?: number + deptName?: string + userName?: string +} + +/** + * 获取目标积分配置列表 + */ +export const getDesignerTargetScoreList = (params: { + page?: number + pageSize?: number + deptId?: number + month?: string +}) => { + return request.get('/designer-target-score/list', { params }) +} + +/** + * 保存目标积分配置 + */ +export const saveDesignerTargetScore = (data: DesignerTargetScore) => { + return request.post('/designer-target-score/save', data) +} + +/** + * 删除目标积分配置 + */ +export const deleteDesignerTargetScore = (id: number) => { + return request.delete(`/designer-target-score/${id}`) +} + +/** + * 获取所有接单部门 + */ +export const getReceivingDepartments = () => { + return request.get('/designer-target-score/receiving-departments') +} + diff --git a/frontend/src/views/Analytics.vue b/frontend/src/views/Analytics.vue index 225fe12..f1c3c96 100644 --- a/frontend/src/views/Analytics.vue +++ b/frontend/src/views/Analytics.vue @@ -225,6 +225,20 @@ + + + + @@ -577,15 +591,18 @@ const userColumns = [ ] const designerColumns = [ - { title: '排名', key: 'rank', width: 80, align: 'center' }, - { title: '设计师', dataIndex: 'realName', key: 'realName' }, - { title: '部门', dataIndex: 'deptName', key: 'deptName' }, - { title: '接单量', dataIndex: 'receivedCount', key: 'receivedCount', sorter: (a: any, b: any) => a.receivedCount - b.receivedCount, align: 'center' }, - { title: '完成量', dataIndex: 'completedCount', key: 'completedCount', align: 'center' }, - { title: '完成率', dataIndex: 'completionRate', key: 'completionRate', customRender: ({ text }: any) => text + '%', align: 'center' }, - { title: '准时率', key: 'onTimeRate', align: 'center' }, - { title: '积分', dataIndex: 'totalScore', key: 'totalScore', sorter: (a: any, b: any) => (a.totalScore || 0) - (b.totalScore || 0), align: 'center' }, - { title: '评分', key: 'avgRating', width: 140, align: 'center' } + { title: '排名', key: 'rank', width: 60, align: 'center' }, + { title: '设计师', dataIndex: 'realName', key: 'realName', width: 80 }, + { title: '部门', dataIndex: 'deptName', key: 'deptName', width: 100 }, + { title: '接单', dataIndex: 'receivedCount', key: 'receivedCount', sorter: (a: any, b: any) => a.receivedCount - b.receivedCount, align: 'center', width: 60 }, + { title: '完成', dataIndex: 'completedCount', key: 'completedCount', align: 'center', width: 60 }, + { title: '准时率', key: 'onTimeRate', align: 'center', width: 70 }, + { title: '获得积分', key: 'totalScore', align: 'center', width: 80 }, + { title: '延误扣分', key: 'deductionScore', align: 'center', width: 80 }, + { title: '净积分', key: 'netScore', sorter: (a: any, b: any) => (a.netScore || 0) - (b.netScore || 0), align: 'center', width: 70 }, + { title: '目标积分', key: 'targetScore', align: 'center', width: 80 }, + { title: '目标完成率', key: 'targetCompletionRate', align: 'center', width: 90 }, + { title: '评分', key: 'avgRating', width: 120, align: 'center' } ] const efficiencyColumns = [ @@ -776,6 +793,15 @@ const getRankClass = (rank: number) => { return '' } +// 目标完成率样式 +const getTargetRateClass = (rate: number | null | undefined) => { + if (rate === null || rate === undefined) return '' + if (rate >= 100) return 'text-success' // 完成目标 - 绿色 + if (rate >= 80) return 'text-primary' // 接近目标 - 蓝色 + if (rate >= 50) return 'text-warning' // 进度一般 - 黄色 + return 'text-danger' // 进度落后 - 红色 +} + const getProgressColor = (rate: number) => { if (rate >= 80) return '#31f08b' if (rate >= 60) return '#3B82F6' @@ -1371,6 +1397,25 @@ onMounted(() => { font-size: 14px; } +// 延误扣分样式 +.deduction-value { + font-weight: 500; + color: #ef4444; + font-size: 14px; +} + +// 净积分样式 +.net-score-value { + font-weight: 600; + color: #60a5fa; + font-size: 14px; +} + +// 文字颜色 +.text-primary { + color: #60a5fa !important; +} + // 全宽卡片 .full-width { width: 100%; diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue index 0a7c2c4..cf3ac09 100644 --- a/frontend/src/views/Settings.vue +++ b/frontend/src/views/Settings.vue @@ -1414,6 +1414,86 @@ + + + +
+
+

设计师目标积分配置

+ + + 添加配置 + +
+ + + +
+ + + {{ dept.deptName }} + + + +
+ + + + +
+
@@ -2093,6 +2173,70 @@ + + + + + + + + {{ dept.deptName }} + + + + + +
+ 设计师每月需完成的目标积分数 +
+
+ + +
+ 每延误1小时,扣除任务积分的百分比。例如:设置为1%,延误2小时,100分任务扣除 2 分 +
+
+ + +
+ 留空表示通用配置,适用于所有月份 +
+
+
+
@@ -2134,7 +2278,12 @@ import { batchUpdateSystemConfigs, createSystemConfig, updateSystemConfig, - type SystemConfig + getDesignerTargetScoreList, + saveDesignerTargetScore, + deleteDesignerTargetScore, + getReceivingDepartments, + type SystemConfig, + type DesignerTargetScore } from '@/api/system' import { getAllUsers, @@ -2304,6 +2453,35 @@ const thirdPartyDbColumns = [ { title: '操作', key: 'actions', width: 260 } ] +// 设计师目标积分配置相关 +const designerTargetList = ref([]) +const loadingDesignerTarget = ref(false) +const receivingDepartments = ref([]) +const designerTargetFilter = reactive({ + deptId: undefined as number | undefined, + month: undefined as string | undefined, + monthValue: undefined as any +}) +const designerTargetForm = reactive({ + deptId: 0, + userId: undefined, + targetScore: 0, + effectiveMonth: undefined, + deductionRate: 1 +}) +const designerTargetModalVisible = ref(false) +const designerTargetModalTitle = ref('添加目标积分配置') + +const designerTargetColumns = [ + { title: 'ID', dataIndex: 'id', key: 'id', width: 60 }, + { title: '部门', dataIndex: 'deptName', key: 'deptName' }, + { title: '适用对象', dataIndex: 'userName', key: 'userName' }, + { title: '目标积分', key: 'targetScore', width: 100 }, + { title: '延误扣分比例', key: 'deductionRate', width: 120 }, + { title: '生效月份', key: 'effectiveMonth', width: 120 }, + { title: '操作', key: 'actions', width: 150 } +] + const defaultDepartmentForm = (): DepartmentForm => ({ deptName: '', deptCode: '', @@ -4431,6 +4609,9 @@ onMounted(() => { loadDelayPenaltyConfig() // 加载第三方素材API配置 loadExternalAssetApiConfig() + // 加载设计师目标积分配置 + loadReceivingDepartments() + loadDesignerTargetList() }) // S3配置相关方法 @@ -4623,6 +4804,99 @@ const handleDisableThirdPartyDb = async (id: number) => { } } +// ==================== 设计师目标积分配置相关方法 ==================== + +const loadReceivingDepartments = async () => { + try { + const response = await getReceivingDepartments() + receivingDepartments.value = response.data || [] + } catch (error: any) { + console.error('加载接单部门失败:', error) + } +} + +const loadDesignerTargetList = async () => { + loadingDesignerTarget.value = true + try { + const params: any = { page: 1, pageSize: 100 } + if (designerTargetFilter.deptId) { + params.deptId = designerTargetFilter.deptId + } + if (designerTargetFilter.month) { + params.month = designerTargetFilter.month + } + const response = await getDesignerTargetScoreList(params) + designerTargetList.value = response.data?.records || [] + } catch (error: any) { + console.error('加载目标积分配置失败:', error) + message.error(error.response?.data?.message || '加载目标积分配置失败') + } finally { + loadingDesignerTarget.value = false + } +} + +const handleDesignerTargetMonthChange = (value: any) => { + designerTargetFilter.month = value + loadDesignerTargetList() +} + +const showDesignerTargetModal = (record?: any) => { + if (record) { + designerTargetModalTitle.value = '编辑目标积分配置' + Object.assign(designerTargetForm, { + id: record.id, + deptId: record.deptId, + userId: record.userId, + targetScore: record.targetScore, + effectiveMonth: record.effectiveMonth, + deductionRate: record.deductionRate || 1 + }) + } else { + designerTargetModalTitle.value = '添加目标积分配置' + Object.assign(designerTargetForm, { + id: undefined, + deptId: receivingDepartments.value[0]?.id || 0, + userId: undefined, + targetScore: 500, + effectiveMonth: undefined, + deductionRate: 1 + }) + } + designerTargetModalVisible.value = true +} + +const handleSaveDesignerTarget = async () => { + if (!designerTargetForm.deptId) { + message.warning('请选择部门') + return + } + if (!designerTargetForm.targetScore || designerTargetForm.targetScore <= 0) { + message.warning('请输入有效的目标积分') + return + } + + try { + await saveDesignerTargetScore(designerTargetForm) + message.success('保存成功') + designerTargetModalVisible.value = false + loadDesignerTargetList() + } catch (error: any) { + console.error('保存目标积分配置失败:', error) + message.error(error.response?.data?.message || '保存目标积分配置失败') + } +} + +const handleDeleteDesignerTarget = async (id: number) => { + try { + await deleteDesignerTargetScore(id) + message.success('删除成功') + loadDesignerTargetList() + } catch (error: any) { + console.error('删除目标积分配置失败:', error) + message.error(error.response?.data?.message || '删除目标积分配置失败') + } +} + watch(activeTab, (value) => { // 切换到用户与部门 tab 时,始终刷新数据以获取最新状态 if (value === 'users') {