layui.tree无线菜单无线分类及增删改查事件,自动展开

创建Menu类

创建menu类,键名必须和layui文档一致,否则不能读取数据
在这里插入图片描述

public class Menu {
    private int id;
    private String title;
    private int pId;
    private List<Menu> children = new ArrayList<>();

	//getter setter constructor省略
}

layui接收的数据是嵌套的json数组

data = [{
    title: '一级1'
    ,id: 1
    ,field: 'name1'
    ,checked: true
    ,spread: true
    ,children: [{
      title: '二级1-1 可允许跳转'
      ,id: 3
      ,field: 'name11'
      ,href: 'https://www.layui.com/'
      ,children: [{
        title: '三级1-1-3'
        ,id: 23
        ,field: ''
        ,children: [{
          title: '四级1-1-3-1'
          ,id: 24
          ,field: ''
          ,children: [{
            title: '五级1-1-3-1-1'
            ,id: 30
            ,field: ''
          },{
            title: '五级1-1-3-1-2'
            ,id: 31
            ,field: ''
          }]
        }]
      },{
        title: '三级1-1-1'
        ,id: 7
        ,field: ''
        ,children: [{
          title: '四级1-1-1-1 可允许跳转'
          ,id: 15
          ,field: ''
          ,href: 'https://www.layui.com/doc/'
        }]
      },{
        title: '三级1-1-2'
        ,id: 8
        ,field: ''
        ,children: [{
          title: '四级1-1-2-1'
          ,id: 32
          ,field: ''
        }]
      }]
    },{
      title: '二级1-2'
      ,id: 4
      ,spread: true
      ,children: [{
        title: '三级1-2-1'
        ,id: 9
        ,field: ''
        ,disabled: true
      },{
        title: '三级1-2-2'
        ,id: 10
        ,field: ''
      }]
    },{
      title: '二级1-3'
      ,id: 20
      ,field: ''
      ,children: [{
        title: '三级1-3-1'
        ,id: 21
        ,field: ''
      },{
        title: '三级1-3-2'
        ,id: 22
        ,field: ''
      }]
    }]
  },{
    title: '一级2'
    ,id: 2
    ,field: ''
    ,spread: true
    ,children: [{
      title: '二级2-1'
      ,id: 5
      ,field: ''
      ,spread: true
      ,children: [{
        title: '三级2-1-1'
        ,id: 11
        ,field: ''
      },{
        title: '三级2-1-2'
        ,id: 12
        ,field: ''
      }]
    },{
      title: '二级2-2'
      ,id: 6
      ,field: ''
      ,children: [{
        title: '三级2-2-1'
        ,id: 13
        ,field: ''
      },{
        title: '三级2-2-2'
        ,id: 14
        ,field: ''
        ,disabled: true
      }]
    }]
  },{
    title: '一级3'
    ,id: 16
    ,field: ''
    ,children: [{
      title: '二级3-1'
      ,id: 17
      ,field: ''
      ,fixed: true
      ,children: [{
        title: '三级3-1-1'
        ,id: 18
        ,field: ''
      },{
        title: '三级3-1-2'
        ,id: 19
        ,field: ''
      }]
    },{
      title: '二级3-2'
      ,id: 27
      ,field: ''
      ,children: [{
        title: '三级3-2-1'
        ,id: 28
        ,field: ''
      },{
        title: '三级3-2-2'
        ,id: 29
        ,field: ''
      }]
    }]
  }]

Mapper

public interface MenuMapper {

    @Select("select * from menu")
    List<Menu> selectAllMenu();

    @Select("select * from menu where id = #{id}")
    Menu selectMenuById(Integer id);

    //查询一个父类ID下的所有子类
    @Select("select * from menu where pId = #{pId}")
    List<Menu> selectMenuByPId(Integer pId);

    //添加分类,顶级pid为0
    @Insert("insert into menu (id,title,pId) value(#{id},#{title},#{pId})")
    int addMenu(Menu menu);

    @Update("update menu set title = #{title} , pId = #{pId} where id = #{id}")
    int updateMenu(Menu menu);


    @Delete("delete from menu where id = #{id}")
    int delMenuById(Menu menu);

    @Delete("delete from menu where title = #{title}")
    int delMenuByTitle(Menu menu);
}

Service及实现类省略,没有特殊的代码

Controller

@Controller
@RequestMapping("/admin/menu")
public class MenuController {

    @Resource
    private MenuService menuService;

    @GetMapping("/")
    public String allMenu() {
        return "/admin/menu/list";
    }

    @GetMapping("/list")
    @ResponseBody
    public JsonResult findAllMenu() {
        List<Menu> menus = menuService.selectAllMenu();
        List<Menu> menusTree = TreeUtil.toTree(menus);//下面类方法将所有菜单数据转化为json嵌套数据
        for (Menu menu : menusTree) {
            System.out.println(menu.toString());
        }
        return JsonResult.success(menusTree);
    }

    @PostMapping("/add")
    @ResponseBody
    public JsonResult addMenu(Menu parent) {
        System.out.println(parent);
        Menu menu = new Menu();
        menu.setpId(parent.getId());
        menu.setTitle("未命名");
        int i = menuService.addMenu(menu);
        if (i > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.failed("1", "添加失败");
        }
    }

   @PostMapping("/edit")
    @ResponseBody
    public JsonResult editMenu(Menu menu) {
        System.out.println(menu.getId());
        String editTitle = menu.getTitle();
        Menu selMenu = new Menu();
        selMenu.setTitle(editTitle);
        Menu resMenu = menuService.selectMenuByTitle(selMenu);
        if (resMenu != null) {
            return JsonResult.failed("1","已存在该类名");
        }
        
        int i = menuService.updateMenu(menu);
        if (i > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.failed("1", "修改失败");
        }
    }

    @PostMapping("/del")
    @ResponseBody
    public JsonResult delMenu(Menu menu) {
        int i = menuService.delMenuById(menu);
        if (i > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.failed("1", "删除失败");
        }
    }
}

TreeUtil工具类,将menu列表转化为嵌套json

public class TreeUtil {

    /**
     * 所有待用"菜单"
     */
    private static List<Menu> all = null;

    /**
     * 转换为树形
     * @param list 所有节点
     * @return 转换后的树结构菜单
     */
    public static List<Menu> toTree(List<Menu> list) {
        // 最初, 所有的 "菜单" 都是待用的
        all = new ArrayList<>(list);

        // 拿到所有的顶级 "菜单"
        List<Menu> roots = new ArrayList<>();

        for (Menu menu : list) {
            if (menu.getpId() == 0) {
                roots.add(menu);
            }
        }

        // 将所有顶级菜单从 "待用菜单列表" 中删除
        all.removeAll(roots);

        //调用下面静态方法,将顶级菜单中的子节点加入children列表,同时该方法会将子节点的子节点持续循环
        for (Menu menu : roots) {
            menu.setChildren(getCurrentNodeChildren(menu));
        }
        return roots;
    }

    /**
     * 递归函数
     *      递归目的: 拿到子节点
     *      递归终止条件: 没有子节点
     * @param parent 父节点
     * @return  子节点
     */
    private static List<Menu> getCurrentNodeChildren(Menu parent) {
        // 判断当前节点有没有子节点, 没有则创建一个空长度的 List, 有就使用之前已有的所有子节点.
        List<Menu> childList = parent.getChildren() == null ? new ArrayList<>() : parent.getChildren();

        // 从 "待用菜单列表" 中找到当前节点的所有子节点
        for (Menu child : all) {
            if (parent.getId() == child.getpId()) {
                childList.add(child);
            }
        }

        // 将当前节点的所有子节点从 "待用菜单列表" 中删除
        all.removeAll(childList);

        // 所有的子节点再寻找它们自己的子节点
        for (Menu menu : childList) {
            menu.setChildren(getCurrentNodeChildren(menu));
        }
        return childList;
    }
}

前端

<body>

    <div class="menu-center">
        <div class="layui-btn-container">
            <button type="button" class="layui-btn layui-btn-sm" lay-demo="openAll">展开所有菜单</button>
            <button type="button" class="layui-btn layui-btn-sm" lay-demo="closeAll">收起所有菜单</button>
        </div>
        <!-- 只需要配置id -->
        <div id="test9" class="demo-tree demo-tree-box" style="width: 500px; height: 600px; overflow: scroll;"></div>
    </div>

  <script th:inline="none">
  layui.config({
    base: '/layuiAdmin/src/layuiadmin/' //静态资源所在路径
  }).extend({
    index: 'lib/index' //主入口模块
  }).use(['tree'], function() {
      var $ = layui.$
          ,tree = layui.tree;

      tree.render({
          elem: '#test9'
          ,data: getData()//获取所有json的方法
          ,id: 'menuTree'//用于重载时指定
          ,edit: ['add', 'update', 'del'] //操作节点的图标
          ,operate: function (obj) {//操作事件
              var type = obj.type; //得到操作类型:add、edit、del
              var data = obj.data; //得到当前节点的数据
              var elem = obj.elem; //得到当前节点元素
              var objId = data.id; //当前节点id
              
              if (type === 'add') {
                  $.ajax({
                      url: '/admin/menu/add',
                      data: data,
                      type: 'post',
                      dataType: 'json',
                      success: function (res) {
                          layer.msg(res.msg);
                          //layui中添加的是未命名的dom节点,没有id所以不能编辑改名,所以需要重载,参考链接中有直接弹窗添加的方法
                          tree.reload('menuTree',{ //tree组件的ID,渲染时需要设置
                          //展开操作节点的所有父类节点
                              data: openTree(getData(),objId) 
                          });
                      }
                  });

              } else if (type === 'update') {
                  $.ajax({
                      url: "/admin/menu/edit",
                      data: data,
                      type: 'post',
                      dataType: 'json',
                      success: function (res) {
                          layer.msg(res.msg);
                          
                      }
                  })
              } else {
                  $.ajax({
                      url: "/admin/menu/del",
                      data: data,
                      type: 'post',
                      dataType: 'json',
                      success: function (res) {
                          layer.msg(res.msg);
                          
                      }
                  })
              }
          }
      });

      //点击按钮展开所有菜单,循环所有节点和子节点改spread属性
      $("button[lay-demo='openAll']").click(function () {
          var treeData = getData();
           function each(data){
              data.forEach(function (value) {
                  value.spread = true;
                  each(value.children)
              })
          }

          each(treeData);
          tree.reload('menuTree',{
              data: treeData
          })
      });

      //点击按钮收起所有菜单
      $("button[lay-demo='closeAll']").click(function () {
          var treeData = getData();
          treeData.forEach(function (value) {
              value.spread = false;
          });
          tree.reload('menuTree',{
              data: treeData
          })
      });

      //获取所有树状json数据
      function getData() {
          var data = [];
          $.ajax({
              url: "/admin/menu/list",
              method: "get",
              async:false, //同步执行,否则不会返回数据
              success: function (res) {
                  data = res.data;
                  console.log(data);
              }
          });
          return data;
      }

      //使操作节点的所有上级节点增加layui展开属性,treeData:所有树节点,objId:操作节点的ID
      var openTree = function (treeData, objId) {
          var nodeId = familyTree(treeData, objId);//获得操作节点的所有的父类ID数组

          //循环所有节点,在nodeId中的加上展开属性
          function each(data) {
              data.forEach(function (e) {
                  if (nodeId.indexOf(e.id) !== -1) {
                      e.spread = true;
                  }
                  if (e.children.length > 0) {
                      each(e.children);
                  }
              })
          }
          //执行循环
          each(treeData);
          return treeData;
      };

      //获取操作节点的所有父节点
      var familyTree = function (treeData, objId) {
          var temp = [];//父节点数组

          var forFn = function (arr,id) {
              //传入所有数组后循环,id=操作节点id的放进数组,并在他的父类子类中继续循环
              for (var i = 0; i < arr.length; i++) {
                  var item = arr[i];
                  if (item.id === id) {
                      temp.push(item.id);
                      forFn(treeData, item.pId);
                      break;
                  } else {
                      if (item.children) {
                          forFn(item.children, id);
                      }
                  }
              }
          };

          //执行循环
          forFn(treeData, objId);
          return temp;
      }


  });
  


  </script>
</body>

效果

在这里插入图片描述
在这里插入图片描述

参考链接

添加事件弹窗和父节点展开

增删改查事件

list转化为嵌套json

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值