mysql查询结果拼接树结构(树节点的移动)

2022-11-22 00:00:00 节点 位置 目标 拼接 下移

思路:单表内查询全部数据,在业务层内递归拼接树结构。

前端用的是element的Tree 树形控件:


树结构实体:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TreeSelect implements Serializable {
    
    /**
    * 所需参数跟前端商讨,参数名跟前端确认一下,我的就是两个前端没统一后面还重加了个
    */

    private static final long serialVersionUID = -1370015781272165366L;

    private Long id;

    private Long pid;

    private String label;

    /**
    * 排序,树节点移动需要
    */
    private int sno;

    @ApiModelProperty(value = "是否启用,0禁用,1正常")
    private int status;

    /**
    * 子级,下一级节点
    */
    private List<TreeSelect> subordinate;

}

业务层拼接树:

    @Override
    public Object getSysClassifyItemTree(Long versionId) {
		//查询出全部数据
        List<TreeSelect> sysClassifyItemList = classifyMapper.getAll(versionId);

        Map<Long, List<TreeSelect>> collect = sysClassifyItemList.stream().collect(Collectors.groupingBy(TreeSelect::getPid));
        List<TreeSelect> list = collect.get(0L);
        List<TreeSelect> items = new ArrayList<>();
        if (null == list) {
            //在没有数据的时候,给前端返回一个虚拟节点方便展示,操作
            TreeSelect sss = new TreeSelect(0l, "虚拟节点", 0l);
            items.add(sss);
            return JSON.toJSON(items).toString();
        }
        for (TreeSelect sysClassifyItem : collect.get(0L)) {
            buildTree(sysClassifyItem, collect);
        }
        //如果这个树需要一个固定的父节点,new一个TreeSelect对象 f,把拼出来的树塞到f的subordinate也就是子级。
        return JSON.toJSON(list).toString();
    }

	/**
	* 拼接树
	*/
    private void buildTree(TreeSelect sysClassifyItem, Map<Long, List<TreeSelect>> collect) {
        if (null == sysClassifyItem || null == collect.get(sysClassifyItem.getId())) {
            return;
        }
        sysClassifyItem.setSubordinate(collect.get(sysClassifyItem.getId()));
        for (TreeSelect classifyItem : collect.get(sysClassifyItem.getId())) {
            buildTree(classifyItem, collect);
        }
    }

下来是树节点的移动,也就是上移下移(提出到上层节点跟插入到子级节点就不放了,单纯的修改而已)

在移动的时候我们需要知道自己的位置和要移动到的位置,也就是目标位置

移动节点实体:

@Data
public class MoveNode implements Serializable {
    private static final long serialVersionUID = 8370232720686896563L;

    private Long parentId;
    private Long sno;//排序,节点当前位置
    private Long targetSno;//目标位置
    private Long id;

}

业务层:

@Override
public boolean moveUp(MoveNode moveNode) {

    //判断节点是上移还是下移
    if (moveNode.getMoveSno() > moveNode.getSno()) {
        if (structureMapper.moveDownA(moveNode) == -1 || structureMapper.move(moveNode) == -1) {
            return false;
        }
    } else if (moveNode.getMoveSno() < moveNode.getSno()) {
        if (structureMapper.moveUpA(moveNode) == -1 || structureMapper.move(moveNode) == -1) {
            return false;
        }
    }
    return true;
}

mapper:

<!--上移我们需要以目标位置到自身位置的前一个位置为区间给每个节点的顺序都往后调一位-->
<update id="moveUpA">
    UPDATE SYS_TEAM
    <set>
        WEIGHT = WEIGHT + 1
    </set>
    <where>
        <if test="parentId != null">and UNITID = #{parentId}</if>
        <if test="moveSno != null and sno != null">and WEIGHT BETWEEN #{moveSno} and #{sno}-1</if>
    </where>
</update>

<!--下移就刚好相反,我们需要以自身位置的后一个位置到目标位置为区间给每个节点的顺序都往前调一位-->
<update id="moveDownA">
    UPDATE SYS_TEAM
    <set>
        WEIGHT = WEIGHT - 1
    </set>
    <where>
        <if test="parentId != null">and UNITID = #{parentId}</if>
        <if test="moveSno != null and sno != null">and WEIGHT BETWEEN #{sno}+1 and #{moveSno}</if>
    </where>
</update>

<!--每次移动后将自身位置替换为目标位置-->
<update id="move">
    UPDATE SYS_TEAM
    <set>
        <if test="moveSno != null">WEIGHT = #{moveSno}</if>
    </set>
    <where>
        <if test="parentId != null">and UNITID = #{parentId}</if>
        <if test="sno != null">and WEIGHT = #{sno}</if>
        <if test="id != null">and ID = #{id}</if>
    </where>
</update>

看下图,移动这里有个问题是前端无法拿到准确的位置,因为element的可拖拽树每个节点有after,before两条线,绑定的是这个节点自身的属性,也就是说两个节点之间有两条线,这就导致c节点在移动到A——B之间时可能会放到a2的这条线导致目标位置拿成了A的位置。即我本身是想将c移动到B的上方,结果是变成了A的上方,同理下移的时候A移动到B的下面,不小心就变成了C的下面。我在网上看有其他的tree控件是可以解决的,前端怕改的地方太多引起别的问题,一直没动。不知道大家有没有遇到过,怎么解决的呢?

相关文章