diff --git a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java index 19a88b5..d025d7b 100644 --- a/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java +++ b/nl-plugin/nl-plugin-pmm/src/main/java/org/nl/pmm/modular/stagedetail/service/impl/StageDetailServiceImpl.java @@ -14,6 +14,7 @@ package org.nl.pmm.modular.stagedetail.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollStreamUtil; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -32,6 +33,8 @@ import org.nl.pmm.modular.stagedetail.mapper.StageDetailMapper; import org.nl.pmm.modular.stagedetail.service.StageDetailService; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * 项目阶段明细Service接口实现类 @@ -113,6 +116,78 @@ public class StageDetailServiceImpl extends ServiceImpl getSuperior(StageDetailTreeParam detailTreeParam) { - return List.of(); + // 查询当前阶段下的所有节点 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StageDetail::getStageId, detailTreeParam.getStageId()) + .orderByAsc(StageDetail::getSeq, StageDetail::getDetailId); + List allDetails = this.list(wrapper); + if(CollUtil.isEmpty(allDetails)) { + return List.of(); + } + + // 构建 id -> 节点、 parentId -> children 映射 + Map idNodeMap = allDetails.stream() + .map(item -> BeanUtil.copyProperties(item, TreeStageDetailVo.class)) + .collect(Collectors.toMap(TreeStageDetailVo::getDetailId, item -> item)); + + Map> parentChildrenMap = idNodeMap.values().stream() + .collect(Collectors.groupingBy(node -> StrUtil.emptyToNull(node.getParentDetailId()))); + + // 组装 children + idNodeMap.values().forEach(node -> { + List children = parentChildrenMap.getOrDefault(node.getDetailId(), List.of()); + node.setChildren(children); + }); + + // 根节点 + List rootNodes = parentChildrenMap.getOrDefault(null, List.of()); + + // 无 parentDetailId 表示取顶层 + if(StrUtil.isBlank(detailTreeParam.getParentDetailId())) { + rootNodes.forEach(node -> node.setIsLeaf(CollUtil.isEmpty(node.getChildren()))); + return rootNodes; + } + + // 计算祖先链(包含目标父节点) + String targetParentId = detailTreeParam.getParentDetailId(); + List ancestorIds = CollUtil.newArrayList(); + String cursor = targetParentId; + while(StrUtil.isNotBlank(cursor)) { + ancestorIds.add(cursor); + TreeStageDetailVo current = idNodeMap.get(cursor); + if(current == null || StrUtil.isBlank(current.getParentDetailId())) { + cursor = null; + } else { + cursor = current.getParentDetailId(); + } + } + + // 构建仅保留祖先链上下级关系的树(顶层节点默认展开一层子节点) + return rootNodes.stream() + .map(node -> buildPrunedTree(node, ancestorIds, targetParentId, parentChildrenMap)) + .peek(node -> node.setIsLeaf(CollUtil.isEmpty(node.getChildren()))) + .collect(Collectors.toList()); + } + + /** + * 仅保留祖先链上节点的子集,顶层节点默认展开一层,其余节点不展开 children。 + */ + private TreeStageDetailVo buildPrunedTree(TreeStageDetailVo current, + List ancestorIds, + String targetParentId, + Map> parentChildrenMap) { + TreeStageDetailVo copy = BeanUtil.copyProperties(current, TreeStageDetailVo.class); + boolean expandChildren = ancestorIds.contains(current.getDetailId()) || StrUtil.isBlank(current.getParentDetailId()); + if(expandChildren) { + List children = parentChildrenMap.getOrDefault(current.getDetailId(), List.of()) + .stream() + .map(child -> buildPrunedTree(child, ancestorIds, targetParentId, parentChildrenMap)) + .collect(Collectors.toList()); + copy.setChildren(children); + } else { + copy.setChildren(List.of()); + } + copy.setIsLeaf(CollUtil.isEmpty(copy.getChildren())); + return copy; } } diff --git a/nl-vue/src/api/pmm/stageDetailApi.js b/nl-vue/src/api/pmm/stageDetailApi.js index 21c83f2..cc3a25a 100644 --- a/nl-vue/src/api/pmm/stageDetailApi.js +++ b/nl-vue/src/api/pmm/stageDetailApi.js @@ -24,5 +24,13 @@ export default { // 获取项目阶段明细详情 stageDetailDetail(data) { return request('detail', data, 'get') + }, + // 加载当前父节点的所有子节点 + loadClass(data) { + return request('loadClass', data, 'get') + }, + // 加载当前父节点的所有子节点 + superior(data) { + return request('superior', data, 'post') } }