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 相关文章推荐
js限制文本框只能输入数字方法小结
Jun 16 Javascript
javascript中关于&amp;&amp; 和 || 表达式的小技巧分享
Apr 10 Javascript
jquery序列化方法实例分析
Jun 10 Javascript
jquery实现的3D旋转木马特效代码分享
Aug 25 Javascript
javascript动态获取登录时间和在线时长
Feb 25 Javascript
javascript高级选择器querySelector和querySelectorAll全面解析
Apr 07 Javascript
深入剖析JavaScript面向对象编程
Jul 12 Javascript
微信小程序使用Promise简化回调
Feb 06 Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
Jun 03 Javascript
nuxt踩坑之Vuex状态树的模块方式使用详解
Sep 06 Javascript
微信小程序转发事件实现解析
Oct 22 Javascript
webpack中的模式(mode)使用详解
Feb 20 Javascript
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
亚洲咖啡有什么?亚洲咖啡产地介绍 亚洲咖啡有什么特点?
2021/03/05 新手入门
PHP5中的时间相差8小时的解决办法
2008/03/28 PHP
php接口数据加密、解密、验证签名
2015/03/12 PHP
Yii2框架制作RESTful风格的API快速入门教程
2016/11/08 PHP
yii2利用自带UploadedFile实现上传图片的示例
2017/02/16 PHP
yii2 在控制器中验证请求参数的使用方法
2019/06/19 PHP
JavaScript中的对象化编程
2008/01/16 Javascript
jQuery AJAX 调用WebService实现代码
2010/03/24 Javascript
jQuery对表单的操作代码集合
2011/04/06 Javascript
jQuery实现新消息闪烁标题提示的方法
2015/03/11 Javascript
javascript实现手机震动API代码
2015/08/05 Javascript
JS代码随机生成姓名、手机号、身份证号、银行卡号
2016/04/27 Javascript
jQuery选择器基础入门教程
2016/05/10 Javascript
微信小程序 location API实例详解
2016/10/02 Javascript
node实现定时发送邮件的示例代码
2017/08/26 Javascript
vuex存值与取值的实例
2019/11/06 Javascript
openlayers4实现点动态扩散
2020/08/17 Javascript
vue 在单页面应用里使用二级套嵌路由
2020/12/19 Vue.js
Python中基本的日期时间处理的学习教程
2015/10/16 Python
Python多线程threading和multiprocessing模块实例解析
2018/01/29 Python
python爬虫爬取淘宝商品信息(selenum+phontomjs)
2018/02/24 Python
python 图像平移和旋转的实例
2019/01/10 Python
python 计算概率密度、累计分布、逆函数的例子
2020/02/25 Python
详解python3类型注释annotations实用案例
2021/01/20 Python
HTML5 progress和meter控件_动力节点Java学院整理
2017/07/06 HTML / CSS
请写出 float x 与"零值"比较的 if 语句
2016/01/04 面试题
事业单位个人应聘自荐信
2013/09/21 职场文书
缓刑人员的思想汇报
2014/01/11 职场文书
采购意向书范本
2014/03/31 职场文书
学校领导班子四风对照检查材料
2014/09/27 职场文书
美容院合作经营协议书
2014/10/10 职场文书
天下第一关导游词
2015/02/06 职场文书
2015年保卫科工作总结
2015/05/14 职场文书
教您怎么制定西餐厅运营方案 ?
2019/07/05 职场文书
python-opencv 中值滤波{cv2.medianBlur(src, ksize)}的用法
2021/06/05 Python
微信小程序APP的生命周期及页面的生命周期
2022/04/19 Javascript