写点什么

BootStrap-Table-Treegrid 树形表格使用指南,mysql 连接查询原理

作者:MySQL神话
  • 2021 年 11 月 26 日
  • 本文字数:6578 字

    阅读完需:约 22 分钟

import org.springframework.data.domain.Sort.Direction;


import org.springframework.data.domain.Sort.Order;


/**


  • 分页的一个工具类,接收分页信息


*/


public class TablePageable{


private Integer limit; //分页


private Integer offset;//首记录号(从 0 开始)


private String sort; //排序字段


private String order; //顺序,逆序


public Integer getLimit() {


return limit;


}


public void setLimit(Integer limit) {


this.limit = limit;


}


public Integer getOffset() {


return offset;


}


public void setOffset(Integer offset) {


this.offset = offset;


}


public String getSort() {


return sort;


}


public void setSort(String sort) {


this.sort = sort;


}


public String getOrder() {


return order;


}


public void setOrder(String order) {


this.order = order;


}


public PageRequest bulidPageRequest() {


int page=(offset!=null&&limit!=null)?offset/limit:0;


int size=limit!=null?limit:10;


if(sort==null) {


return PageRequest.of(page, size);


}else {


Order order2=new Order(Direction.fromString(order), sort);


Sort sort2= Sort.by(order2);


return PageRequest.of(page,size,sort2 );


}


}


public PageRequest bulidPageable(Sort sort) {


int page=(offset!=null&&limit!=null)?offset/limit:0;


int size=limit!=null?limit:10;


return PageRequest.of(page, size, sort);


}


public Sort bulidSort() {


Order order2=new Order(Direction.fromString(order), sort);


Sort sort2= Sort.by(order2);


return sort2;


}


}


这里持久层查询是用的 Spring-Data-Jpa,如果是其他持久层框架,只要查询后返回的 JSON 数据格式一致就可以了。


JSON 数据格式如下,parentId 为父节点 Id,和前面 js 中设置的一定要对应一样,不然树形结构显示不出来。


**注意:**如果父节点为空,parentId 的值需要为 null,不能为""空串,一个 parentId 类型为 Integer 类型,为空串的话,树形结构显示不出来。



加载完成后数据显示的效果如下。



实体类代码。


import com.fasterxml.jackson.annotation.JsonIgnore;


import com.mcy.springbootsecurity.custom.BaseEntity;


import org.springframework.data.annotation.CreatedBy;


import javax.persistence.*;


import java.util.ArrayList;


import java.util.List;


/**


  • 菜单表

  • @author


*/


@Entity


public class TbMenu {


private Integer id;


private String name;


private String url;


private Integer idx;


@JsonIgnore


private TbMenu parent;


@JsonIgnore


private List<TbMenu> children=new ArrayList<>();


@Id


@GeneratedValue(strategy = GenerationType.IDENTITY)


public Integer getId() {


return id;


}


public void setId(Integer id) {


this.id = id;


}


@Column(unique=true)


public String getName() {


return name;


}


public void setName(String name) {


this.name = name;


}


public String getUrl() {


return url;


}


public void setUrl(String url) {


this.url = url;


}


public Integer getIdx() {


return idx;


}


public void setIdx(Integer idx) {


this.idx = idx;


}


@ManyToOne


@CreatedBy


public TbMenu getParent() {


return parent;


}


public void setParent(TbMenu parent) {


this.parent = parent;


}


@OneToMany(cascade=CascadeType.ALL,mappedBy="parent")


@OrderBy(value="idx")


public List<TbMenu> getChildren() {


return children;


}


public void setChildren(List<TbMenu> children) {


this.children = children;


}


public TbMenu(Integer id) {


super(id);


}


public TbMenu(){


super();


}


public TbMenu(String name, String url, Integer idx, TbMenu parent, List<TbMenu> children) {


this.name = name;


this.url = url;


this.idx = idx;


this.parent = parent;


this.children = children;


}


public TbMenu(Integer integer, String name, String url, Integer idx, TbMenu parent, List<TbMenu> children) {


super(integer);


this.name = name;


this.url = url;


this.idx = idx;


this.parent = parent;


this.children = children;


}


@Transient


public Integer getParentId() {


return parent==null?null:parent.getId();


}


}


新增和修改方法,前台 JS 代码(因为新增和修改是共用的一个页面和一个方法,所以这里放一起写了)。


//新增


function btn_add(){


var selectRows = $table.bootstrapTable('getSelections');


var dialog = $('<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="myModalLabel"></div>');


if(selectRows&&selectRows.length==1){


var parentId=selectRows[0].id;


dialog.load('menu/edit',{parentId:parentId});


}else{


dialog.load('menu/edit');


}


$("body").append(dialog);


dialog.modal().on('hidden.bs.modal', function () {


dialog.remove();


$table.bootstrapTable('refresh');


});


}


//修改


function btn_edit(){


var str = $("#menu_table").bootstrapTable('getSelections');


if(str.length != 1){


bootbox.alert("请选中一行进行编辑");


return ;


}


var dialog = $('<div class="modal fade" id="myModal" tabindex="-1" aria-labelledby="myModalLabel"></div>');


var id = str[0].id;


dialog.load("menu/edit?id="+id);


/添加到 body 中/


$("body").append(dialog);


/弹出模态框,绑定关闭后的事件/


dialog.modal().on('hidden.bs.modal', function () {


//删除模态框


dialog.remove();


$table.bootstrapTable('refresh');


});


}


新增和修改的后台请求方法,跳转到新增修改页面,通过 id 来判断是修改还是新增,parentId 判断新增时是否勾选行,勾选后为默认父级。


@Override


public void edit(TbMenuForm form, ModelMap map) throws InstantiationException, IllegalAccessException {


TbMenu model = new TbMenu();


if(form.getId() != null) {


model = menuService.findById(form.getId());


}


if(form.getParentId() != null) {


model.setParent(new TbMenu(form.getParentId()));


}


map.put("model", model);


}


新增修改页面代码


<body>


<form id="myForm" class="form-horizontal" role="form" >


<div class="modal-dialog" role="document">


<div class="modal-content">


<div class="modal-header">


<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>


<h4 class="modal-title" id="myModalLabel">操作</h4>


</div>


<div class="modal-body">


<input type="hidden" name="id" th:value="${model.id}" id="modelId">


<div class="form-group">


<label for="txt_parent" class="col-sm-2 control-label">上级菜单</label>


<div class="col-sm-9">


<input name="parentId" data-th-value="${model.parent==null?null:model.parent.id}" class="form-control" id="txt_parent">


</div>


</div>


<div class="form-group">


<label for="txt_name" class="col-sm-2 control-label">菜单名称</label>


<div class="col-sm-9">


<input type="text" name="name" data-th-value="${model.name}" class="form-control" id="txt_name">


</div>


</div>


<div class="form-group">


<label for="txt_url" class="col-sm-2 control-label">访问地址</label>


<div class="col-sm-9">


<input type="text" name="url" data-th-value=${model.url} class="form-control" id="txt_url" placeholder="">


</div>


</div>


<div class="form-group">


<label for="txt_sort" class="col-sm-2 control-label">排序</label>


<div class="col-sm-9">


<input type="text" name="idx" data-th-value="${model.idx}" class="form-control" id="txt_sort">


</div>


</div>


</div>


<div class="modal-footer">


<button type="button" onclick="btnSubmit()" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button>


<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button>


</div>


</div>


</div>


</form>


</body>


JS 部分代码。


<script th:inline="javascript">


$(function(){


$('#txt_parent').bootstrapCombotree({


url:'menu/treedata?id='+$('#modelId').val()


});


$('.selectpicker').selectpicker({});


});


function btnSubmit() {


var form= new FormData($("#myForm")[0]);


$.ajax({


url: 'menu/save',


type: 'post',


data: form,


processData: false, //不处理数据


contentType: false, //不设置内容类型


success: function(result){


if(result.success){


$("#myModal").modal("hide");


bootbox.alert("数据保存成功");


}else{


bootbox.alert(result.msg);


}


},


error: function(result){


bootbox.alert("数据保存失败!");


}


})


}


</script>


页面初始化后,加载 Combotree 下拉树的数据,并初始化,后台 Combotree 数据请求方法,如果是修改,就把当前修改项的 id 传递到后台,代码如下。


/***


  • combotree 树形加载

  • @return


*/


@RequestMapping(value="/treedata")


@ResponseBody


public Object treedata(Integer id) {


Sort sort = Sort.by("idx");


Specification<TbMenu> spec = buildSpec1();


List<TbMenu> list = menuService.findAll(spec,sort);


return buildTree(list,id);


}


《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享


private Object buildTree(List<TbMenu> list, Integer id) {


List<HashMap<String, Object>> result=new ArrayList<>();


for (TbMenu dept : list) {


//判断如果是修改,修改节点在下拉树中不显示


if(dept.getId()!=id) {


//因为实体类中的字段名和需要返回的 JSON 字段名不同,这里需要转换一下


HashMap<String, Object> node=new HashMap<>();


node.put("id", dept.getId());


node.put("text",dept.getName());


node.put("pid", dept.getParentId());


node.put("nodes",buildTree(dept.getChildren(), id));


result.add(node);


}


}


return result;


}


//条件查询,先只显示最上级的菜单,即 parent 父级节点为空的数据


public Specification<TbMenu> buildSpec1() {


Specification<TbMenu> specification = new Specification<TbMenu>() {


private static final long serialVersionUID = 1L;


@Override


public Predicate toPredicate(Root<TbMenu> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


HashSet<Predicate> rules=new HashSet<>();


Predicate parent = cb.isNull(root.get("parent"));


rules.add(parent);


return cb.and(rules.toArray(new Predicate[rules.size()]));


}


};


return specification;


}


这里持久层查询是用的 Spring-Data-Jpa,如果是其他持久层框架,只要查询后返回的 JSON 数据格式一致就可以了。


响应请求数据返回的 JSON 格式数据,pid 为父节点 id。



Combotree 下拉树,在前面还引入了一个自己写的 combotree.js 文件,代码如下。


(function ($) {


function getTree(url){


var result=$.ajax({


type : 'post',


url : url ,


dataType : 'json' ,


async : false


}).responseText;


return eval('('+result+')');


}


$.fn.extend({


bootstrapCombotree: function(options, param){


var self=this;


this.onBodyDown=function(event){


var $options=undefined;


try{


.data($(event.target).prev()[0],'bootstrapCombotree');


}catch (e) {


$options=undefined;


}


if (!((event.target).parents("#comboDiv").length>0)) {


//if (!(event.target.id == 'comboDiv' || $(event.target).parents("#comboDiv").length>0)) {


self.hideMenu();


}


}


this.hideMenu=function(){


$('#comboDiv').fadeOut('fast');


$("#comboDiv").remove();


$("body").unbind("mousedown", self.onBodyDown);


}


this.getNode=function(root,id){


for(var i=0;i<root.length;i++){


var node=root[i];


if(node.id==id){


return node;


}


var x=self.getNode(node.nodes?node.nodes:[],id);


if(x){


return x;


}


}


return null;


},


this.create=function(target){


var (target);


var .data(target,'bootstrapCombotree').options;


$self.attr("type","hidden");


var ('<input class="form-control" />');


self.attr('placeholder'));


$this.attr('readonly',true);


this[0]);


//取出 options


var options=option,{


onNodeSelected:function(event,node){


$self.val(node.id);


$this.val(node.text);


$('#comboDiv').fadeOut('fast');


$("#comboDiv").remove();


$("body").unbind("mousedown", self.onBodyDown);


}


});


//在显示的框中写入默认值对应的 text 的内容


var self.val();


if($value){


var value);


if($node){


node.text);


}


}


$this.focus(function(){


if($('#comboDiv').length>0){


return;


}


var ('<div class="panel panel-default combotree" id="comboDiv"><div class="panel-body"><div id="bootstrapCombotree"></div></div></div>');


this.parent());


$('#bootstrapCombotree').treeview(options);


var thisObj=$(this);


var thisOffset=$(this).position();


var $dialogOffset=thisObj.closest('.modal-dialog').offset();


var $left=thisOffset.left;


var $top=thisOffset.top+thisObj.outerHeight();


$div.css({


left : $left+"px",


top: $top+"px",


zIndex: 1100


}).slideDown('fast');


$('body').bind("mousedown",self.onBodyDown);


});


};


if (typeof options == 'string'){


var method = $.fn.bootstrapCombotree.methods[options];


if (method){


return method(this, param);


}


return;


}


options = options || {};


return this.each(function(){


var state = $.data(this, 'bootstrapCombotree');


if (state){


$.extend(state.options, options);


} else {


.extend({}, $.fn.bootstrapCombotree.defaults, options);


$options.thisObj=this;


if($options.url){


options.url);


}


state = $.data(this, 'bootstrapCombotree', {


options: $options,


});


}


self.create(this);


});


}


});


$.fn.bootstrapCombotree.methods={


options:function(jq){


var .data(jq[0],'bootstrapCombotree').options;


return $option;


}


}


$.fn.bootstrapCombotree.defaults = {


expandIcon: "glyphicon glyphicon-plus-sign",


collapseIcon: "glyphicon glyphicon-minus-sign",


emptyIcon:"glyphicon glyphicon-file",


showBorder:false,


showTags: true,


url:'combotree',


data:[]


};


})(jQuery)


保存 JS 中的 btnSubmit()方法用于保存数据方法,后台保存响应方法。


@Override


public Object save(TbMenuForm form) {


try {


TbMenu model = new TbMenu();


Integer id = form.getId();


if(id != null) {


model = menuService.findById(id);


}


//父级菜单 id


Integer parentId = form.getParentId();


if(parentId == null) {


model.setParent(null);


}else {


model.setParent(new TbMenu(parentId));


}


BeanUtils.copyProperties(form, model,"id", "parent");


menuService.save(model);


return new AjaxResult("数据保存成功!");


} catch (Exception e) {


return new AjaxResult(false,"数据保存失败");


}


}


方法中 TbMenuForm 接收类,中字段和实体类差不多。代码如下。


import com.mcy.springbootsecurity.custom.BaseForm;


import com.mcy.springbootsecurity.entity.TbMenu;


public class TbMenuForm {


private Integer id;


private String name;


private String url;


private Integer idx;


private TbMenu parent;


private Integer parentId;


public Integer getId() {


return id;


}


public void setId(Integer id) {


this.id = id;


}


public String getName() {


return name;


}


public void setName(String name) {

架构学习资料






由于篇幅限制小编,pdf 文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!


本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

用户头像

MySQL神话

关注

还未添加个人签名 2021.11.12 加入

还未添加个人简介

评论

发布
暂无评论
BootStrap-Table-Treegrid树形表格使用指南,mysql连接查询原理