diff --git a/backend/src/main/java/com/designhub/entity/TaskItem.java b/backend/src/main/java/com/designhub/entity/TaskItem.java index a77a2ea..1689ca6 100644 --- a/backend/src/main/java/com/designhub/entity/TaskItem.java +++ b/backend/src/main/java/com/designhub/entity/TaskItem.java @@ -41,6 +41,11 @@ public class TaskItem implements Serializable { */ private Long taskTypeId; + /** + * 任务类型明细 (JSON数组: [{typeId, typeName, quantity, unitScore, totalScore}]) + */ + private String typeDetails; + /** * 数量 */ diff --git a/backend/src/main/java/com/designhub/service/TaskService.java b/backend/src/main/java/com/designhub/service/TaskService.java index cf0a496..205c9d8 100644 --- a/backend/src/main/java/com/designhub/service/TaskService.java +++ b/backend/src/main/java/com/designhub/service/TaskService.java @@ -420,6 +420,8 @@ public class TaskService { BigDecimal groupTotalHours = BigDecimal.ZERO; int groupTotalQuantity = 0; StringBuilder taskTypeIds = new StringBuilder(); + // 构建任务类型明细 JSON 数组 + java.util.List> typeDetailsList = new java.util.ArrayList<>(); for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : groupItems) { var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId()); @@ -436,6 +438,15 @@ public class TaskService { taskTypeIds.append(","); } taskTypeIds.append(itemDto.getTaskTypeId()); + + // 添加任务类型明细 + java.util.Map detail = new java.util.LinkedHashMap<>(); + detail.put("typeId", itemDto.getTaskTypeId()); + detail.put("typeName", demandType.getName()); + detail.put("quantity", itemDto.getQuantity()); + detail.put("unitScore", demandType.getUnitScore()); + detail.put("totalScore", itemScore); + typeDetailsList.add(detail); } // 创建子任务 @@ -444,6 +455,13 @@ public class TaskService { item.setItemNo(generateTaskItemNo(task.getTaskNo(), itemIndex)); item.setGroupNo(groupNo); item.setTaskTypeId(groupFirstTaskTypeId.get(groupNo)); // 使用第一个任务类型ID + // 保存任务类型明细 JSON + try { + com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper(); + item.setTypeDetails(objectMapper.writeValueAsString(typeDetailsList)); + } catch (Exception e) { + log.warn("序列化任务类型明细失败: {}", e.getMessage()); + } item.setQuantity(groupTotalQuantity); item.setUnitScore(groupTotalScore / groupTotalQuantity); // 平均单价 item.setTotalScore(groupTotalScore); diff --git a/backend/src/main/resources/db/migration/V10__add_task_type_details.sql b/backend/src/main/resources/db/migration/V10__add_task_type_details.sql new file mode 100644 index 0000000..d764d31 --- /dev/null +++ b/backend/src/main/resources/db/migration/V10__add_task_type_details.sql @@ -0,0 +1,6 @@ +-- V10__add_task_type_details.sql + +-- 添加任务类型明细字段到子任务表 +ALTER TABLE `task_item` + ADD COLUMN `type_details` JSON NULL COMMENT '任务类型明细(JSON数组: [{typeId, typeName, quantity, unitScore, totalScore}])' AFTER `task_type_id`; + diff --git a/frontend/src/views/TaskHall.vue b/frontend/src/views/TaskHall.vue index 0cf3665..96fa125 100644 --- a/frontend/src/views/TaskHall.vue +++ b/frontend/src/views/TaskHall.vue @@ -240,7 +240,7 @@
预估积分 - {{ task.score || '--' }} 分 + {{ task.totalScore || task.score || '--' }} 分
数量 @@ -256,6 +256,17 @@
+ +
+
任务类型明细
+
+
+ └ {{ detail.typeName }} + x{{ detail.quantity }} = {{ detail.totalScore }}分 +
+
+
+
@@ -541,7 +552,7 @@
预估积分 - {{ task.score }} 分 + {{ task.totalScore || task.score }} 分
数量 @@ -557,6 +568,17 @@
+ +
+
任务类型明细
+
+
+ └ {{ detail.typeName }} + x{{ detail.quantity }} = {{ detail.totalScore }}分 +
+
+
+
@@ -850,7 +872,7 @@
预估积分 - {{ task.score }} 分 + {{ task.totalScore || task.score }} 分
数量 @@ -866,6 +888,17 @@
+ +
+
任务类型明细
+
+
+ └ {{ detail.typeName }} + x{{ detail.quantity }} = {{ detail.totalScore }}分 +
+
+
+
@@ -2341,6 +2374,14 @@ import { useConfigStore } from '@/stores/config' import dayjs from 'dayjs' // 扩展Task类型以支持子任务字段 +interface TaskTypeDetail { + typeId: number + typeName: string + quantity: number + unitScore: number + totalScore: number +} + interface TaskOrSubTask extends Task { isSubTask?: boolean // 是否为子任务 itemId?: number // 子任务ID @@ -2351,6 +2392,7 @@ interface TaskOrSubTask extends Task { submitTime?: string // 提交时间 taskTypeName?: string // 任务类型名称(子任务直接返回) prevDownloadUrl?: string // 前置子任务的素材下载链接 + typeDetails?: string | TaskTypeDetail[] // 任务类型明细(JSON字符串或解析后的数组) } const router = useRouter() @@ -2540,6 +2582,18 @@ const getTaskTypeName = (typeId: number, taskTypeName?: string, taskSubType?: st return type?.name || '未知类型' } +// 解析任务类型明细 +const parseTypeDetails = (typeDetails: string | TaskTypeDetail[] | undefined): TaskTypeDetail[] => { + if (!typeDetails) return [] + if (Array.isArray(typeDetails)) return typeDetails + try { + return JSON.parse(typeDetails) as TaskTypeDetail[] + } catch (e) { + console.warn('解析任务类型明细失败:', e) + return [] + } +} + // 获取任务类型单位积分 const getTaskTypeUnitScore = (typeId: number) => { const type = demandTypes.value.find(t => t.id === typeId) @@ -5031,6 +5085,50 @@ onUnmounted(() => { } } +// 任务类型明细 +.type-details-section { + background: rgba($brand-primary, 0.05); + border: 1px solid rgba($brand-primary, 0.2); + border-radius: 8px; + padding: $space-sm $space-md; + margin-bottom: $space-md; + + .type-details-title { + font-size: $font-size-xs; + color: $text-tertiary; + margin-bottom: $space-xs; + } + + .type-details-list { + display: flex; + flex-direction: column; + gap: 4px; + } + + .type-detail-item { + display: flex; + justify-content: space-between; + align-items: center; + font-size: $font-size-sm; + padding: 2px 0; + + .type-name { + color: $text-secondary; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .type-score { + color: $brand-primary; + font-weight: $font-weight-medium; + margin-left: $space-md; + white-space: nowrap; + } + } +} + // 任务详情 .task-details { display: grid;