Thymeleaf+MySQL实现一个两级树状目录首页导航栏
在完成个人毕设的过程中,依旧使用的是对用户权限判断,分别定制独立页面的方式,这样大大增加了前端的页面冗余,还使工作量大大提升,后来通过朋友提示,可以通过数据库来读取每个用户的个人主页设置。再结合正在使用的Thymeleaf,因为不会js,因此打算全部通过Thymeleaf来实现此功能
思路步骤
我的项目环境包括前端的layui和Thymeleaf,后端则是用的SpringBoot的网页框架。
- 数据库的实体类entity
因为后面的实现过程中发现,若没有List实现父包子菜单集合的属性,会使得在循环时没办法实现父菜单点击收回子菜单的功能,这个后面仍会说明。public class Menu { private int access; //权限 private String menuid; //每项菜单的ID private String menuname; //每项菜单的名称 private String location; //菜单所处左侧导航还是顶部ToolBar private String menutype; //菜单类型,区分子菜单归属的上一级父菜单 private String menulevel; //菜单等级,区分父菜单还是子菜单 private String url; //菜单跳转URL private List<Menu> secondMenus; //二级目录集合List }
- 实现此服务层功能@Service类
@Override public List<Menu> getCorrectMenusByID(int userid) { //由于数据库使用的是平面设计 没有进行分层 因此在另外定义了一个没有注入使用SQL语句的Service方法 //然后通过此方法调用其他SQL然后对menus对象初始化 把二级菜单List填充 //MenuUtil定义了用于区分菜单等级的常量字段 List<Menu> menus = mapper.getMenusByIDMenuLv(userid, MenuUtil.MENU_LEVEL_ONE); for(Menu m: menus){ m.setSecondMenus(mapper.getMenusByID_Lv_Type(userid, MenuUtil.MENU_LEVEL_TWO, m.getMenutype())); } return menus; }
- 响应请求@Controller类
@RequestMapping(value = {"index", "index.html"}) public String indexPage(Model m, HttpServletRequest request){ //从session取User对象 HttpSession session = request.getSession(); User current = (User) session.getAttribute(UserUtil.CURRENT_USER); //给Thymeleaf填充项设置属性 m.addAttribute("name", userInfo.getNickname()); m.addAttribute(MenuUtil.USER_MENUS, setSessionMenus(current, session)); return "index"; } public List<Menu> setSessionMenus(User user, HttpSession session){ //获取已分级的新菜单 List<Menu> menusNew = menuService.getCorrectMenusByID(user.getId()); session.setAttribute(MenuUtil.USER_MENUS, menusNew); return menusNew; }
- 前端Html结构
<ul class="layui-nav layui-nav-tree" lay-filter="test"> <li class="layui-nav-item layui-nav-itemed" th:each="menu:${userMenus}"> <a href="javascript:;" th:text="${menu.menuname}">父菜单</a> <dl class="layui-nav-child"> <dd th:each="child:${menu.secondMenus}"> <a href="javascript:;" th:text="${child.menuname}">子菜单</a> </dd> </dl> </li> </ul>
此结构是使用我参考的别的项目的菜单结构修改的,可以有更简表达形式,此处不再做修改。
若此处不是使用父子菜单包含而是使用的平级循环,通过一个
和th:each
会导致子菜单也是独立项,没办法实现父菜单的收回子菜单的功能,因为这个js是已经写在layui模板中的模块。th:if
<!-- 错误示范 --> <ul class="layui-nav layui-nav-tree" lay-filter="test"> <li class="layui-nav-item layui-nav-itemed" th:each="menu:${userMenus}"> <a href="javascript:;" th:if="${menu.menulevel} eq 1" th:text="${menu.menuname}">父菜单</a> <dl class="layui-nav-child"> <dd><a href="javascript:;" th:if="${menu.menulevel} eq 2" th:text="${menu.menuname}">子菜单</a> </dd> </dl> </li> </ul> <!-- 错误示范 -->
错误总结
- Thymeleaf的
循环需要写在内层,否则会导致每个item都是独立的大框架,没办法做到单选响应的css样式th:each
- 子菜单循环时写的应该是
而不是${menu.secondMenus}
,userMenus是个List,没有secondMenus属性会导致${userMenus.secondMenus}
报错EL1008E
- 此处也能通过Thymeleaf直接读取Session而非通过传值的方法得到MenusList
- 注意写Mapper、Service和其实现类时的规范
参考文献
- thymeleaf之菜单树
- springboot+thymeleaf把菜单栏存放到数据库
- 关于springMVC结合mybatis,thymeleaf,实现二级联动