bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?


Posted in Javascript onJuly 26, 2019

树形列表菜单的数据组织形式一般有两种:一种是一次性向服务器请求所有节点的数据,一种是先请求根目录的节点,当用户点击该节点时,再去请求该节点的子叶节点的数据。这里我们的需求是第一种。

bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?

树形菜单是我们经常会用到的一种菜单展现方式,这里我推荐bootstrap-treeview,它是一款效果非常酷的基于bootstrap的jQuery多级列表树插件。该jQuery插件基于Twitter Bootstrap,以简单和优雅的方式来显示一些继承树结构,如视图树、列表树等。

前端页面的编写,比较简单,故简略描述,我们着重将后端如何按照要求组织返回所需的Json。

使用方法

首先要在页面中引入依赖的css样式和 bootstrap-treeview.js文件。

<!-- Required Stylesheets -->
<link href="./css/bootstrap.css" rel="external nofollow" rel="stylesheet">
 
<!-- Required Javascript -->
<script src="./js/jquery.js"></script>
<script src="./js/bootstrap-treeview.js"></script>

可以使用任何HTML DOM元素来作为该列表树的容器:

<div id="tree"></div>

调用

<script type="text/javascript">
 $(function() {$.ajax({
  type: "Get",
  url: "doctype/FarmDoctypeLoadTreeNodes.do",
  data: {id: ""},
  dataType: "json",
  success: function (result) {
  $("#tree").treeview({
  data: result.nodes,  // 数据源
  levels:1, //设置继承树默认展开的级别
  showTags: true, //是否在每个节点右边显示tags标签。tag值必须在每个列表树的data结构中给出
  onNodeSelected: function (event, data) {
   /* console.log(data); */
   /* alert(data.nodeId); */
   window.location = basePath + "webtype/view"
 + data.id + "/Pub1.html"; 
  }
   }); 
  },
  error: function () {
  alert("加载失败!")
  }
 }); 
 
 });
</script>

数据源要求的数据格式:

var tree = [
  {
  text: "Parent 1",
  tags: ['2'],
  nodes: [
   {
   text: "Child 1",
   tags: ['3'],
   nodes: [
   {
   text: "Grandchild 1",
   tags: ['1']
   },
   {
   text: "Grandchild 2",
   tags: ['1']
   }
   ]
   },
   {
   text: "Child 2",
   tags: ['1']
   }
  ]
  },
  {
  text: "Parent 2",
   tags: ['1']
  },
  {
  text: "Parent 3",
   tags: ['1']
  },
  {
  text: "Parent 4",
   tags: ['1']
  },
  {
  text: "Parent 5",
   tags: ['1']
  }
  ];

看到这个数据结构,我们首先想到的是数据结构中的二叉树。欧拉,我们不妨可以把它理解成,这样的数据结构:

bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?

创建一个实例,组织所需要的数据结构的json.

public class BootstrapUiTreeNode implements Serializable {
 private static final long serialVersionUID = 1L;
 static final Logger log = Logger.getLogger(BootstrapUiTreeNode.class);
 
 private String id;
 private String parentId;
 private String text;
 
 //子叶节点
 private List<BootstrapUiTreeNode> nodes = new ArrayList<>();
 
 //统计该节点分类下文档的数量
 private List<String> tags = new ArrayList<>(); // tags: ['NUM']
 
 public BootstrapUiTreeNode(String id, String parentId, String text) {
 super();
 this.id = id;
 this.parentId = parentId;
 this.text = text;
 }
 
 
 public BootstrapUiTreeNode(String id, String parentId, String text, List<BootstrapUiTreeNode> nodes) {
 super();
 this.id = id;
 this.parentId = parentId;
 this.text = text;
 this.nodes = nodes;
 }
 
 
 public String getParentId() {
 return parentId;
 }
 
 
 public void setParentId(String parentId) {
 this.parentId = parentId;
 }
 
 
 public String getId() {
 return id;
 }
 
 public void setId(String id) {
 this.id = id;
 }
 
 public String getText() {
 return text;
 }
 
 public void setText(String text) {
 this.text = text;
 }
 
 public List<BootstrapUiTreeNode> getNodes() {
 return nodes;
 }
 
 public void setNodes(List<BootstrapUiTreeNode> nodes) {
 this.nodes = nodes;
 }
 
 
 public List<String> getTags() {
 return tags;
 }
 
 
 public void setTags(List<String> tags) {
 this.tags = tags;
 }
 
 
 
 /**
 * 加载第一层树节点 父节点的根目录的父id必须为"NONE",包含字段名ID,PARENTID,NAME
 * 
 * @param parentId
 *  父亲节点id
 * @param SortTitle
 *  排序字段index
 * @param tableTitle
 *  表名index
 * @param id_title
 *  表id的index
 * @param parentId_title
 *  表parentid的index
 * @param name_title
 *  表name的index
 * @param icon_title
 */
 public static List<Map<String, Object>> queryTreeNodeOne(String parentId, String SortTitle, String tableTitle, String id_title,
 String parentId_title, String name_title) {
 if (StringUtils.isEmpty(parentId)) {
 parentId = "NONE";
 }
 DataResult nodeResult = null;
 DBSort sort = new DBSort("a." + SortTitle, "ASC");
 DataQuery query = DataQuery.getInstance("1", 
 id_title
 + ","
 + parentId_title
 + " as "
 + parentId_title
 + ","
 + name_title
 + " as "
 + name_title,
 tableTitle + " a");
 query.setPagesize(100);
 query.addSort(sort);
 DataQuerys.wipeVirus(parentId);
 query.setSqlRule("and " + parentId_title + "='" + parentId + "'");
 try {
 nodeResult = query.search();
 } catch (SQLException e) {
 log.error(e.getMessage());
 }
 return nodeResult.getResultList();
 }
 
 public static List<TypeBrief> getPopTypesForReadDoc() {
 DataQuery query = DataQuery.init(new DataQuery(),
 "(SELECT a.NAME as NAME,a.SORT as SORT, a.ID as ID, a.READPOP as READPOP,a.WRITEPOP AS WRITEPOP, a.AUDITPOP AS AUDITPOP, a.PARENTID AS PARENTID, (SELECT COUNT(B1.ID) FROM FARM_DOC B1 LEFT JOIN FARM_RF_DOCTYPE B2 ON B1.ID = B2.DOCID LEFT JOIN FARM_DOCTYPE B3 ON B3.ID = B2.TYPEID WHERE B1.STATE='1' and B3.TREECODE LIKE CONCAT(A.TREECODE,'%') AND B1.STATE='1') AS NUM,f.oid as OID,f.FUNTYPE as FUNTYPE FROM farm_doctype AS a left join FARM_DOCTYPE_POP as f on f.TYPEID=a.ID WHERE 1 = 1 AND (TYPE = '1' OR TYPE = '3') AND PSTATE = '1' ) AS e",
 "NAME,ID,PARENTID,NUM,OID,READPOP,AUDITPOP,WRITEPOP,FUNTYPE,SORT");
 query.setPagesize(1000);
 query.setNoCount();
 query.setCache(Integer.valueOf(0), CACHE_UNIT.second);
 query.addSort(new DBSort("SORT", "ASC"));
 try {
 DataResult result = query.search();
 //利用反射获取对象集合
 return result.getObjectList(TypeBrief.class);
 } catch (SQLException e) {
 log.error(e.toString());
 return new ArrayList<TypeBrief>();
 }
 }
 
 
 
 /**
 * 遍历集合获得对象
 * 
 * @param nodeList
 * @return
 */
 public static List<BootstrapUiTreeNode> findNode(List<TypeBrief> typeBriefs, List<Map<String, Object>> data, String id_title,
 String parentId_title, String name_title) {
 List<BootstrapUiTreeNode> nodeList = new ArrayList<>();
 try {
 for (Map<String, Object> node : data) {
 if (node.get(id_title) == null) {
 continue;
 }
 BootstrapUiTreeNode item = new BootstrapUiTreeNode(node.get(id_title).toString(), 
 node.get(parentId_title).toString(), node.get(name_title).toString());
 //统计该节点下分类的文档数
 for(TypeBrief typeBrief : typeBriefs) {
 if(typeBrief.getId().equals(node.get(id_title).toString())) {
 item.getTags().add(typeBrief.getNum() + "");
 break;
 }
 }
 nodeList.add(item);
 }
 } catch (Exception e) {
 throw new RuntimeException(e);
 }
 return nodeList;
 }
 
 /**
 * 先用递归构造除第一层几个根节点外的类多叉树结构
 * @param node
 * @param parentId
 * @param sortTitle
 * @param tableTitle
 * @param id_title
 * @param parentId_title
 * @param name_title
 * @return
 */
 public static BootstrapUiTreeNode createSubtreeTreeNodes(List<TypeBrief> typeBriefs, BootstrapUiTreeNode node, String parentId, String sortTitle, String tableTitle, String id_title,
 String parentId_title, String name_title) {
 
 //获取该根节点下的所有子节点
 List<Map<String, Object>> data = queryTreeNodeOne(parentId, sortTitle, tableTitle, id_title, parentId_title, name_title);
 List<BootstrapUiTreeNode> roots = findNode(typeBriefs, data, id_title, parentId_title, name_title);
 
 if(null != roots && roots.size() > 0) {
 //递归继续查询
 for(BootstrapUiTreeNode child : roots) {
 BootstrapUiTreeNode treeNode = createSubtreeTreeNodes(typeBriefs, child, child.getId(), sortTitle, tableTitle, id_title, parentId_title, name_title);
 node.getNodes().add(treeNode);
 }
 
 } else {
 node.setNodes(null); 
 }
 
 return node;
 }
 
 /**
 * 补上第一层的几个根节点
 * @param parentId
 * @param sortTitle
 * @param tableTitle
 * @param id_title
 * @param parentId_title
 * @param name_title
 * @return
 * 
 * 注意:所以会存在叶子节点中的nodes没有数据的情况,这个时候会出现叶子节点是可以伸缩的,
 * 因为nodes属性就是一个对象数组,它会被认为它还有子节点,其实是没有的。
 * 
 */
 public static List<BootstrapUiTreeNode> createTreeNodes(String parentId, String sortTitle, String tableTitle, String id_title,
 String parentId_title, String name_title) {
 //统计所有节点分类对应的文档数目
 List<TypeBrief> typeBriefs = getPopTypesForReadDoc();
 
 List<BootstrapUiTreeNode> nodes = new ArrayList<>();
 //查询第一层根节点
 List<Map<String, Object>> data = queryTreeNodeOne(parentId, sortTitle, tableTitle, id_title, parentId_title, name_title);
 List<BootstrapUiTreeNode> treeNodes = findNode(typeBriefs, data, id_title, parentId_title, name_title);
 
 for(BootstrapUiTreeNode node : treeNodes) {
 BootstrapUiTreeNode treeNode = createSubtreeTreeNodes(typeBriefs, node, node.getId(), sortTitle, tableTitle, id_title, parentId_title, name_title);
 nodes.add(treeNode);
 }
 
 return nodes;
 }
}

控制器:

@RequestMapping("/FarmDoctypeLoadTreeNodes")
 @ResponseBody
 public Map<String, Object> loadTreeNodes(DataQuery query, HttpServletRequest request, String id) {
 query = EasyUiUtils.formatGridQuery(request, query);
 try {
 List<BootstrapUiTreeNode> treeNodes = BootstrapUiTreeNode.createTreeNodes(id, "SORT", "FARM_DOCTYPE", "ID", "PARENTID", "NAME");
 return ViewMode.getInstance().putAttr("nodes", treeNodes).returnObjMode();
 } catch (Exception e) {
 log.error(e.getMessage());
 return ViewMode.getInstance().setError(e.getMessage()).returnObjMode();
 }
 }

访问结果:

bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?

最终树形菜单效果:

bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery AnythingSlider滑动效果插件
Feb 07 Javascript
图片在浏览器中底部对齐 解决方法之一
Nov 30 Javascript
创建公共调用 jQuery Ajax 带返回值
Aug 01 Javascript
js隐藏与显示回到顶部按钮及window.onscroll事件应用
Jan 25 Javascript
鼠标滑过出现预览的大图提示效果
Feb 26 Javascript
ECMAScript6的新特性箭头函数(Arrow Function)详细介绍
Jun 07 Javascript
avalon js实现仿微博拖动图片排序
Aug 14 Javascript
谈谈我对JavaScript原型和闭包系列理解(随手笔记8)
Dec 24 Javascript
vue项目base64字符串转图片的实现代码
Jul 13 Javascript
JS实现判断图片是否加载完成的方法分析
Jul 31 Javascript
在vue中使用express-mock搭建mock服务的方法
Nov 07 Javascript
vue中实现点击空白区域关闭弹窗的两种方法
Dec 30 Vue.js
bootstrap-table+treegrid实现树形表格
Jul 26 #Javascript
Vue实现商品分类菜单数量提示功能
Jul 26 #Javascript
vue实现滑动切换效果(仅在手机模式下可用)
Jun 29 #Javascript
微信小程序设置滚动条过程详解
Jul 25 #Javascript
vuejs移动端实现div拖拽移动
Jul 25 #Javascript
vue实现拖拽的简单案例 不超出可视区域
Jul 25 #Javascript
vue实现一拉到底的滑动验证
Jul 25 #Javascript
You might like
星际争霸任务指南——人族
2020/03/04 星际争霸
谈一谈收音机的高放电路
2021/03/02 无线电
PHP实现CSV文件的导入和导出类
2015/03/24 PHP
Laravel-添加后台模板AdminLte的实现方法
2019/10/08 PHP
php变量与字符串的增删改查操作示例
2020/05/07 PHP
一直复略了的一个问题,关于表单重复提交
2007/02/15 Javascript
Grid得到选择行数据的方法总结
2011/01/17 Javascript
让textarea自动调整大小的js代码
2011/04/12 Javascript
js给onclick事件赋值,动态传参数实例解说
2013/03/28 Javascript
jquery实现倒计时代码分享
2014/06/13 Javascript
Javascript 赋值机制详解
2014/11/23 Javascript
jQuery插件slider实现拖动滑块选取价格范围
2015/04/30 Javascript
BootStrap和jQuery相结合实现可编辑表格
2016/04/21 Javascript
浅谈Angularjs link和compile的使用区别
2016/10/21 Javascript
基于Javascript倒计时效果
2016/12/22 Javascript
vuex学习之Actions的用法详解
2017/08/29 Javascript
requireJS模块化实现返回顶部功能的方法详解
2017/10/16 Javascript
Angular6笔记之封装http的示例代码
2018/07/27 Javascript
解决vue js IOS H5focus无法自动弹出键盘的问题
2018/08/30 Javascript
JavaScript键盘事件常见用法实例分析
2019/01/03 Javascript
axios解决高并发的方法:axios.all()与axios.spread()的操作
2020/11/09 Javascript
利用Vue实现简易播放器的完整代码
2020/12/30 Vue.js
Python将多个excel表格合并为一个表格
2021/02/22 Python
用TensorFlow实现戴明回归算法的示例
2018/05/02 Python
python实现简单tftp(基于udp协议)
2018/07/30 Python
在Pycharm中对代码进行注释和缩进的方法详解
2019/01/20 Python
Python BeautifulSoup [解决方法] TypeError: list indices must be integers or slices, not str
2019/08/07 Python
Python自动化之UnitTest框架实战记录
2020/09/08 Python
世界排名第一的万圣节服装店:Spirit Halloween
2018/10/16 全球购物
家得宝官网:The Home Depot(全球最大的家居装饰专业零售商)
2018/12/17 全球购物
奥地利购买珠宝和手表网站:ELLA JUWELEN
2019/09/03 全球购物
汽车销售员如何做职业生涯规划
2014/02/16 职场文书
个人总结与自我评价
2015/02/14 职场文书
2015年世界无车日活动总结
2015/03/23 职场文书
nginx+lua单机上万并发的实现
2021/05/31 Servers
vue实现Toast组件轻提示
2022/04/10 Vue.js