JavaScript

超轻量级php框架startmvc

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

更新时间:2020-09-05 14:06:01 作者:startmvc
树形列表菜单的数据组织形式一般有两种:一种是一次性向服务器请求所有节点的数据,一

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

树形菜单是我们经常会用到的一种菜单展现方式,这里我推荐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']
 }
 ]; 

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

创建一个实例,组织所需要的数据结构的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多级树形菜单 bootstrap treeview多级菜单 bootstrap treeview树形菜单