标签(空格分隔): jpa repository 树形菜单 adminlte springboot thymeleaf
侧边栏树形结构菜单应该是后台管理系统的标配吧,这里讲一下在选用的方案中有thymeleaf,jpa/repository,adminlte的情况下怎么实现这个功能。
实现
功能虽然简单,但是要综合三种工具来做,不太好说,所以下面一边列代码一遍做点说明
layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 2 | Dashboard</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="bower_components/Ionicons/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="dist/css/AdminLTE.min.css">
<!-- AdminLTE Skins. Choose a skin from the css/skins
folder instead of downloading all of them to reduce the load. -->
<link rel="stylesheet" href="dist/css/skins/_all-skins.min.css">
<!-- Morris chart -->
<link rel="stylesheet" href="bower_components/morris.js/morris.css">
<!-- jvectormap -->
<link rel="stylesheet" href="bower_components/jvectormap/jquery-jvectormap.css">
<!-- Date Picker -->
<link rel="stylesheet" href="bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css">
<!-- Daterange picker -->
<link rel="stylesheet" href="bower_components/bootstrap-daterangepicker/daterangepicker.css">
<!-- bootstrap wysihtml5 - text editor -->
<link rel="stylesheet" href="plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- Google Font -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="index2.html" class="logo">
<span class="logo-lg"><b>Ticket</b>Business</span>
</a>
<nav class="navbar navbar-static-top">
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">HEADER</li>
<li class="treeview" th:each="FirstLevelItem : ${FirstLeveles}">
<a href="#">
<i class="fa fa-pie-chart"></i>
<span th:text="${FirstLevelItem.name}">Multilevel</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li><a th:each="SecondLevelItem : ${FirstLevelItem.GetSecondLevelItems()}" th:href="@{${SecondLevelItem.site}}" th:text="${SecondLevelItem.name}"><i class="fa fa-circle-o"></i>Link in level 2</a></li>
</ul>
</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper" layout:fragment="contentWrapper">
</div>
</div>
<!-- ./wrapper -->
<!-- jQuery 3 -->
<script src="bower_components/jquery/dist/jquery.min.js"></script>
<!-- jQuery UI 1.11.4 -->
<script src="bower_components/jquery-ui/jquery-ui.min.js"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button);
</script>
<!-- Bootstrap 3.3.7 -->
<script src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- Morris.js charts -->
<script src="bower_components/raphael/raphael.min.js"></script>
<script src="bower_components/morris.js/morris.min.js"></script>
<!-- Sparkline -->
<script src="bower_components/jquery-sparkline/dist/jquery.sparkline.min.js"></script>
<!-- jvectormap -->
<script src="plugins/jvectormap/jquery-jvectormap-1.2.2.min.js"></script>
<script src="plugins/jvectormap/jquery-jvectormap-world-mill-en.js"></script>
<!-- jQuery Knob Chart -->
<script src="bower_components/jquery-knob/dist/jquery.knob.min.js"></script>
<!-- daterangepicker -->
<script src="bower_components/moment/min/moment.min.js"></script>
<script src="bower_components/bootstrap-daterangepicker/daterangepicker.js"></script>
<!-- datepicker -->
<script src="bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js"></script>
<!-- Bootstrap WYSIHTML5 -->
<script src="plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js"></script>
<!-- Slimscroll -->
<script src="bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script>
<!-- FastClick -->
<script src="bower_components/fastclick/lib/fastclick.js"></script>
<!-- AdminLTE App -->
<script src="dist/js/adminlte.min.js"></script>
<!-- AdminLTE dashboard demo (This is only for demo purposes) -->
<script src="dist/js/pages/dashboard.js"></script>
<!-- AdminLTE for demo purposes -->
<script src="dist/js/demo.js"></script>
</body>
</html>
针对侧边栏树形树形菜单的这个功能,三个工具在layout.html中分别有以下的贡献:
- adminlte
class="treeview"
class="treeview-menu"
treeview是li的样式,treeview-menu是ul的样式
- thymeleaf
th:each
th:text
th:each 告诉thymeleaf,需要循环渲染多次当前这个标签(渲染一个树形结构必定是要依赖循环操作的);
th:text 告诉thymeleaf当前标签的text是什么
- jpa/repository
${FirstLeveles}
${FirstLevelItem.GetSecondLevelItems()}
FirstLeveles对应的是用jpa/repository从数据库中读取出来的第一层的数据;FirstLevelItem.GetSecondLevelItems()对应的是从用jpa/repository从数据库中读取出来的第二层的数据。
FirstLevelRepository.java
package springbootexample.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import springboot.entities.FirstLevel;
@Repository
public interface FirstLevelRepository extends JpaRepository<FirstLevels, Integer> {
}
FirstLevel.java
package springbootexample.entities;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
/**
* The persistent class for the firstlevel database table.
*
*/
@Entity
@Table(name="firstlevel")
@NamedQuery(name="FirstLevel.findAll", query="SELECT t FROM FirstLevel t")
public class FirstLevel implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="ID")
private int id;
@Column(name="NAME")
private String name;
public FirstLevel() {
}
public FirstLevel(String name) {
this.name = name;
}
@Transient //告诉hibernage不要固化这个变量
@JsonSerialize //虽然不固化,但是允许序列化
@JsonDeserialize //虽然不固化,但是允许反序列化
private List<SecondLevel> secondLevels = new ArrayList<SecondLevel>();
public void addSecondLevel(SecondLevel secondLevel) {
this.secondLevels.add(secondLevel);
}
public List<SecondLevel> GetSecondLevels(){
return this.secondLevels;
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
SecondLevelRepository.java
package springbootexample.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import springboot.entities.SecondLevel;
@Repository
public interface SecondLevelRepository extends JpaRepository<SecondLevel, Integer> {
}
SecondLevel.java
package springbootexample.entities;
import java.io.Serializable;
import javax.persistence.*;
/**
* The persistent class for the secondlevel database table.
*
*/
@Entity
@Table(name="secondlevel")
@NamedQuery(name="SecondLevel.findAll", query="SELECT p FROM SecondLevel p")
public class SecondLevel implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="ID")
private int id;
@Column(name="NAME")
private String name;
@Column(name="SITE")
private String site;
@Column(name="FIRSTLEVEL_ID")
private int firstlevelId;
public SecondLevel() {
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getSite() {
return this.site;
}
public void setSite(String site) {
this.site = site;
}
public int getFirstLevelId() {
return this.firstlevelId;
}
public void setFirstLevelId(int firstelvelId) {
this.firstelvelId = firstelvelId;
}
}
上面列出来的是数据库操作相关的主要的代码,代码的重要部分做了一点点的解释。