树结构在关系型数据库中节点排序的实现(基于elementui)

前言

在GIS系统中图层目录是方便用户对图层进行分类,点击加载的一个很好的展示方式,很多大数据平台都会用到,想做一个图层目录,需要用到树结构,在关系型数据库中树结构通常使用标注parentId的方式来存储。

处理流程

数据库表

构建一个树结构表,带有排序字段(sort),父节点Id(parentId),自身id(id),其余按需添加
在这里插入图片描述

前端展示

通过后台接口查询数据,前端使用elementui的树结构来展示,需要先将查询的数据处理成其格式。其中onlyDirectory是为了只展示文件夹的参数,每一级的树节点都会根据sort字段排序,所以如何排序中如何准确处理sort字段很重要!

    <div class="layerTree">
      <div class="custom-tree-container">
        <div class="block" style="width:400px;height: 100%">
          <div style="display: flex ; padding: 10px 0px">
            <div class="titleContainer">图层目录</div>
          </div>
          <el-tree v-loading="layerTreeLoading"
                  :data="layer_tree"
                   ref="treeRef"
                   node-key="id"
                   default-expand-all
                   :expand-on-click-node="false"
                   @node-click="nodeclick"
                   @node-drag-end="handleDragEnd"
                   @node-drop="handleDrop"
                   draggable
                   :allow-drop="allowDrop"
                   :allow-drag="allowDrag">
          <span class="custom-tree-node"
                slot-scope="{ node, data }">
            <span >
              <span :class="data.type == 'layer' ? 'el-icon-picture-outline' :'el-icon-folder'" style="margin-right: 10px"></span>
              <template v-if="data.isEdit==1">
                <el-input ref="input"
                          @blur="() => submitEdit(node,data)"
                          v-model="newlabel"
                          style="height:20px;line-height:10px"></el-input>
              </template>
              <span v-else v-text="data.name"></span>
            </span>
            <span style="margin-left: 30px">
              <el-button
                type="text"
                 size="mini"
                 @click="() => append(node,data)">
                添加目录
              </el-button>
              <el-button
                  type="text"
                  size="mini"
                  @click="() => edit(node,data)">
                修改
              </el-button>
              <el-button
                v-if="!data.undeletable"
                type="text"
                size="mini"
                @click="() => remove(node, data)">
                删除
              </el-button>
            </span>
          </span>
          </el-tree>
        </div>
      </div>
    </div>
    handleTree(data,onlyDirectory ,id, parentId, children, rootId, type) {
      id = id || 'id'
      parentId = parentId || 'parentId'
      children = children || 'children'
      type = type || 'type'
      rootId = rootId || Math.min.apply(Math, data.map(item => {
        return item[parentId]
      })) || 0
      //对源数据深度克隆
      const cloneData = JSON.parse(JSON.stringify(data))
      //循环所有项
      const treeData = cloneData.filter(father => {
        let branchArr = cloneData.filter(child => {
          //返回每一项的子级数组
          return father[id] === child[parentId] && (onlyDirectory? child[type]!= 'layer' : true)
        });
        branchArr.length > 0 ? father.children = branchArr.sort(function (a,b) {return a.sort-b.sort-1}) : '';
        //返回第一层
        return father[parentId] === rootId;
      });
      //第一层排序
      let resTreeData = treeData !== '' ? treeData.sort(function (a,b) {return a.sort-b.sort-1}) : data
      return resTreeData;
    },

在这里插入图片描述

排序实现

利用elementui的拖拽实现排序,也可以说是节点的移动,关键在于elementui自带的@node-drag-end监听拖拽结束的时间,主要有三个参数,一个拖拽的节点,一个目标节点,一个目标节点状态。

同一个父节点

情况一

4移动到0后,此时拖拽的节点为4,目标节点为0,在目标节点后面所以状态为after,
拖拽节点的sort = 目标节点的sort + 1
1、2、3节点加1
在这里插入图片描述

情况二

1移动到3后,此时拖拽的节点为1,目标节点为3,在目标节点后面所以状态为after,
拖拽节点的sort = 目标节点的sort
2和3节点减1
在这里插入图片描述

情况三

4移动到1前,此时拖拽的节点为4,目标节点为1,在目标节点前面所以状态为before,
拖拽节点的sort = 目标节点的sort
1、2、3节点加1
在这里插入图片描述

情况四

1移动到4前,此时拖拽的节点为1,目标节点为4,在目标节点前面所以状态为before,
拖拽节点的sort = 目标节点的sort - 1
2和3节点减1
在这里插入图片描述

不同父节点

情况一

目录1的3节点移动到目录2的1节点前,此时拖拽的节点为目录1的节点3,目标节点为目录2的节点1,在目标节点前面所以状态为before,
拖拽节点的parentId = 目标节点的parentId
拖拽节点的sort = 目标节点的sort
目录1的4节点减1
目录2的1234节点加1
在这里插入图片描述

情况二

目录1的3节点移动到目录2的1节点前,此时拖拽的节点为目录1的节点3,目标节点为目录2的节点0,在目标节点后面所以状态为after,
拖拽节点的parentId = 目标节点的parentId
拖拽节点的sort = 目标节点的sort + 1
目录1的4节点减1
目录2的1234节点加1
在这里插入图片描述

情况三

elementui拖拽到目录节点上而不是之间时,其状态状态会为inner,将目录1的节点3拖拽到目录2,将默认后方插入,此时
目录1的4节点减1
拖拽节点的sort = 目录2长度
在这里插入图片描述

实现代码

    handleDragEnd(draggingNode, dropNode, dropType, ev) {
      let update_data = draggingNode.data
      update_data["oldParentId"] = update_data.parentId
      update_data["oldSort"] = update_data.sort
      update_data["dropType"] = dropType
      if(dropType == 'after'){
        let newParentId = dropNode.parent.level == 0 ? "0" :dropNode.parent.data.id
        if(update_data.parentId == newParentId)
          if(dropNode.data.sort > update_data.sort ) {
            update_data.sort = dropNode.data.sort
          }
          else{
            update_data.sort = dropNode.data.sort + 1
          }
        else{
          update_data.sort = dropNode.data.sort + 1
        }
        update_data.parentId = newParentId
      }
      else if(dropType == 'before'){
        let newParentId = dropNode.parent.level == 0 ? "0" :dropNode.parent.data.id
        if(update_data.parentId == newParentId) {
          if (dropNode.data.sort < update_data.sort) {
            update_data.sort = dropNode.data.sort
          } else {
            update_data.sort = dropNode.data.sort - 1
          }
        }
        else{
          update_data.sort = dropNode.data.sort
        }
        update_data.parentId = newParentId
      }
      else if(dropType == 'inner'){
        debugger
        update_data.parentId = dropNode.data.id
      }
      else{
        return
      }
      this.layerTreeLoading = true
     updateLayertreeSort(update_data).then(res=>{
       this.$modal.msgSuccess('修改成功')
       this.initLayerTree()
     })
    },
/*service*/
public void updateLayertreeSort(LayertreeUpdateSortReqVO updateReqVO){
        // 校验存在
        validateLayertreeExists(updateReqVO.getId());
        //检验更新方法
        if(updateReqVO.getOldParentId().longValue() == updateReqVO.getParentId().longValue()) {
            if(updateReqVO.getOldSort() > updateReqVO.getSort()){
                layertreeMapper.updateSortBefore(updateReqVO.getOldParentId(),updateReqVO.getOldSort(),updateReqVO.getSort());
            }
            else{
                layertreeMapper.updateSortAfter(updateReqVO.getOldParentId(),updateReqVO.getOldSort(),updateReqVO.getSort());
            }
        }
        else{
            if (updateReqVO.getDropType().equals("before") || updateReqVO.getDropType().equals("after")){
                layertreeMapper.updateSortInOldParent(updateReqVO.getOldParentId(),updateReqVO.getOldSort());
                layertreeMapper.updateSortInNewParent(updateReqVO.getParentId(),updateReqVO.getSort());
            }
            else if (updateReqVO.getDropType().equals("inner")) {
                layertreeMapper.updateSortInOldParent(updateReqVO.getOldParentId(),updateReqVO.getOldSort());
                Set<Long> set = new HashSet<>();
                set.add(updateReqVO.getParentId());
                int length = layertreeMapper.selectList("parent_id",set).size();
                updateReqVO.setSort(length);
            } else {
                return;
            }
        }
        // 更新
        LayertreeDO updateObj = LayertreeConvert.INSTANCE.convert(updateReqVO);
        layertreeMapper.updateById(updateObj);
    };
    /*mybatis mapper*/
    @Update("update sbsj_layertree set sort = sort + 1 where parent_id = #{parentId} and sort >= #{newSort} and sort < #{oldSort}")
    void updateSortBefore(@Param("parentId") Long parentId, @Param("oldSort") Integer oldSort,@Param("newSort") Integer newSort);

    @Update("update sbsj_layertree set sort = sort - 1 where parent_id = #{parentId} and sort > #{oldSort} and sort <= #{newSort}")
    void updateSortAfter(@Param("parentId") Long parentId, @Param("oldSort") Integer oldSort,@Param("newSort") Integer newSort);

     @Update("update sbsj_layertree set sort = sort - 1 where parent_id = #{parentId} and sort > #{oldSort}")
     void updateSortInOldParent(@Param("parentId") Long parentId,@Param("oldSort") Integer oldSort);

    @Update("update sbsj_layertree set sort = sort + 1 where parent_id = #{parentId} and sort >= #{newSort}")
    void updateSortInNewParent(@Param("parentId") Long parentId,@Param("newSort") Integer newSort);

结尾

要确保排序的正确还需要在其添加节点、删除节点、修改节点的时候都要做数据库校验与控制,这里就不一一阐述。

参考

芋道源码: https://doc.iocoder.cn/
elementui树形控件: https://element.eleme.io/#/zh-CN/component/tree

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值