diff --git a/backend/src/main/java/com/designhub/service/ConfigService.java b/backend/src/main/java/com/designhub/service/ConfigService.java index 21d24c9..e148299 100644 --- a/backend/src/main/java/com/designhub/service/ConfigService.java +++ b/backend/src/main/java/com/designhub/service/ConfigService.java @@ -112,7 +112,7 @@ public class ConfigService { } /** - * 构建需求类型树形结构 + * 构建需求类型树形结构(支持多级) */ private List buildDemandTypeTree(List allTypes) { // 获取所有根节点(parentId为null) @@ -120,16 +120,30 @@ public class ConfigService { .filter(dt -> dt.getParentId() == null) .collect(java.util.stream.Collectors.toList()); - // 为每个根节点填充子节点 + // 递归为每个节点填充子节点 for (DemandType root : roots) { - List children = allTypes.stream() - .filter(dt -> root.getId().equals(dt.getParentId())) - .collect(java.util.stream.Collectors.toList()); - root.setChildren(children); + fillChildren(root, allTypes); } return roots; } + + /** + * 递归填充子节点 + */ + private void fillChildren(DemandType parent, List allTypes) { + List children = allTypes.stream() + .filter(dt -> parent.getId().equals(dt.getParentId())) + .collect(java.util.stream.Collectors.toList()); + + if (!children.isEmpty()) { + parent.setChildren(children); + // 递归填充每个子节点的子节点 + for (DemandType child : children) { + fillChildren(child, allTypes); + } + } + } /** * 根据ID获取需求类型 diff --git a/frontend/src/views/CreateTask.vue b/frontend/src/views/CreateTask.vue index 55e5fbb..b920ee8 100644 --- a/frontend/src/views/CreateTask.vue +++ b/frontend/src/views/CreateTask.vue @@ -56,23 +56,26 @@ class="task-type-item" >
-
- + - - {{ type.name }} ({{ type.unitScore }}分/个, {{ type.estimatedHours }}小时) - - + + +
+ {{ getTaskTypeInfo(item.taskTypeId)?.unitScore }}分/个 + {{ getTaskTypeInfo(item.taskTypeId)?.estimatedHours }}小时 +
>({}) @@ -557,7 +570,7 @@ const formData = reactive({ title: '', isPipeline: true, // 固定为流水线任务 pipelineMode: 'sequential' as 'sequential' | 'parallel', // 固定为顺序流水线 - taskTypes: [{ taskTypeId: undefined, quantity: 1, receiverId: undefined }] as TaskTypeItem[], // 任务类型数组 + taskTypes: [{ taskTypeId: undefined, taskTypePath: undefined, quantity: 1, receiverId: undefined }] as TaskTypeItem[], // 任务类型数组 taskSubType: '', // 动态加载,初始为空 brandId: undefined, platformId: undefined, @@ -651,6 +664,83 @@ const brands = computed(() => configStore.brands) const platforms = computed(() => configStore.platforms) const categories = computed(() => configStore.categories) +// 层级任务类型数据 +const hierarchicalTaskTypes = ref([]) + +// 加载层级任务类型数据 +const loadHierarchicalTaskTypes = async () => { + try { + const response = await configApi.getDemandTypesTree() + if (response && response.data) { + hierarchicalTaskTypes.value = response.data + } + } catch (error) { + console.error('加载层级任务类型失败:', error) + } +} + +// 构建级联选择器选项(三级结构) +const taskTypeCascaderOptions = computed(() => { + const buildOptions = (items: any[]): CascaderOption[] => { + return items.map(item => { + const option: CascaderOption = { + value: item.id, + label: item.name, + unitScore: item.unitScore, + estimatedHours: item.estimatedHours + } + + // 如果有子级,递归构建 + if (item.children && item.children.length > 0) { + option.children = buildOptions(item.children) + } + + return option + }) + } + + return buildOptions(hierarchicalTaskTypes.value) +}) + +// 级联选择器搜索过滤 +const cascaderFilter = (inputValue: string, path: CascaderOption[]) => { + return path.some(option => + option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1 + ) +} + +// 任务类型级联选择变更 +const onTaskTypeChange = (value: (string | number)[] | undefined, item: TaskTypeItem, index: number) => { + if (value && value.length === 3) { + // 选择了三级分类,设置taskTypeId为三级分类的ID + item.taskTypeId = value[2] as number + item.taskTypePath = value + } else { + // 未选择完整路径 + item.taskTypeId = undefined + item.taskTypePath = value + } + calculateScore() +} + +// 获取任务类型信息(根据ID从平铺的taskTypes中查找) +const getTaskTypeInfo = (taskTypeId: number) => { + // 从平铺列表中查找 + return taskTypes.value.find((t: any) => t.id === taskTypeId) +} + +// 从平铺列表中根据ID查找任务类型(递归查找层级数据) +const findTaskTypeById = (id: number, items: any[] = hierarchicalTaskTypes.value): any => { + for (const item of items) { + if (item.id === id) return item + if (item.children) { + const found = findTaskTypeById(id, item.children) + if (found) return found + } + } + return null +} + // 加载设计师列表 const loadDesignersForItem = async (item: TaskTypeItem) => { if (!item.taskTypeId) return @@ -694,6 +784,7 @@ const getDesignersForItem = (item: TaskTypeItem) => { const addTaskType = () => { formData.taskTypes.push({ taskTypeId: undefined, + taskTypePath: undefined, quantity: 1, receiverId: undefined }) @@ -1428,7 +1519,8 @@ onMounted(async () => { loadConfigData(), loadTeamScore(), loadTaskSubTypes(), - loadReleaseTypeConfig() + loadReleaseTypeConfig(), + loadHierarchicalTaskTypes() ]) // 检查是否是编辑模式 @@ -1678,6 +1770,36 @@ onUnmounted(() => { flex: 2; } + .task-type-cascader { + display: flex; + flex-direction: column; + gap: 6px; + + :deep(.ant-cascader) { + width: 100%; + } + + .task-type-score-tip { + display: flex; + gap: 8px; + font-size: 12px; + + .score-badge { + color: $primary; + background: rgba($primary, 0.1); + padding: 2px 8px; + border-radius: 4px; + } + + .hour-badge { + color: $text-secondary; + background: rgba(255, 255, 255, 0.05); + padding: 2px 8px; + border-radius: 4px; + } + } + } + .task-type-quantity { flex: 1; min-width: 150px;