项目源码:bootdo: 面向学习型的开源框架,简洁高效,减少过渡封装,展现技术本质

左边的菜单栏分析

数据库结构

样式实例 

用树结构封装菜单数据,方便获取前后节点,通过自身id和父id相匹配,构建菜单。通过type确定类型,perms控制权限。

将菜单转换成树结构

import com.bootdo.common.domain.Tree;
import com.bootdo.common.utils.BuildTree;
import com.bootdo.system.dao.MenuDao;
import com.bootdo.system.dao.RoleMenuDao;
import com.bootdo.system.domain.MenuDO;
import com.bootdo.system.service.MenuService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.*;@Service
@Transactional(readOnly = true,rollbackFor = Exception.class)
public class MenuServiceImpl implements MenuService {@AutowiredMenuDao menuMapper;@AutowiredRoleMenuDao roleMenuMapper;/*** @param* @return 树形菜单  树结构方便获取父节点,子节点,节点属性*/@Cacheable@Overridepublic Tree<MenuDO> getSysMenuTree(Long id) {List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();List<MenuDO> menuDOs = menuMapper.listMenuByUserId(id);for (MenuDO sysMenuDO : menuDOs) {Tree<MenuDO> tree = new Tree<MenuDO>();tree.setId(sysMenuDO.getMenuId().toString());tree.setParentId(sysMenuDO.getParentId().toString());tree.setText(sysMenuDO.getName());Map<String, Object> attributes = new HashMap<>(16);attributes.put("url", sysMenuDO.getUrl());attributes.put("icon", sysMenuDO.getIcon());tree.setAttributes(attributes);trees.add(tree);}// 默认顶级菜单为0,根据数据库实际情况调整Tree<MenuDO> t = BuildTree.build(trees);return t;}@Overridepublic List<MenuDO> list(Map<String, Object> params) {List<MenuDO> menus = menuMapper.list(params);return menus;}// 开启事务,rollbackFor设置事务回滚的异常类型@Transactional(readOnly = false,rollbackFor = Exception.class)@Overridepublic int remove(Long id) {int result = menuMapper.remove(id);return result;}@Transactional(readOnly = false,rollbackFor = Exception.class)@Overridepublic int save(MenuDO menu) {int r = menuMapper.save(menu);return r;}@Transactional(readOnly = false,rollbackFor = Exception.class)@Overridepublic int update(MenuDO menu) {int r = menuMapper.update(menu);return r;}@Overridepublic MenuDO get(Long id) {MenuDO menuDO = menuMapper.get(id);return menuDO;}@Overridepublic Tree<MenuDO> getTree() {List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();List<MenuDO> menuDOs = menuMapper.list(new HashMap<>(16));for (MenuDO sysMenuDO : menuDOs) {Tree<MenuDO> tree = new Tree<MenuDO>();tree.setId(sysMenuDO.getMenuId().toString());tree.setParentId(sysMenuDO.getParentId().toString());tree.setText(sysMenuDO.getName());trees.add(tree);}// 默认顶级菜单为0,根据数据库实际情况调整Tree<MenuDO> t = BuildTree.build(trees);return t;}@Overridepublic Tree<MenuDO> getTree(Long id) {// 根据roleId查询权限List<MenuDO> menus = menuMapper.list(new HashMap<String, Object>(16));List<Long> menuIds = roleMenuMapper.listMenuIdByRoleId(id);List<Long> temp = menuIds;for (MenuDO menu : menus) {if (temp.contains(menu.getParentId())) {menuIds.remove(menu.getParentId());}}List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();List<MenuDO> menuDOs = menuMapper.list(new HashMap<String, Object>(16));for (MenuDO sysMenuDO : menuDOs) {Tree<MenuDO> tree = new Tree<MenuDO>();tree.setId(sysMenuDO.getMenuId().toString());tree.setParentId(sysMenuDO.getParentId().toString());tree.setText(sysMenuDO.getName());Map<String, Object> state = new HashMap<>(16);Long menuId = sysMenuDO.getMenuId();if (menuIds.contains(menuId)) {state.put("selected", true);} else {state.put("selected", false);}tree.setState(state);trees.add(tree);}// 默认顶级菜单为0,根据数据库实际情况调整Tree<MenuDO> t = BuildTree.build(trees);return t;}@Overridepublic Set<String> listPerms(Long userId) {List<String> perms = menuMapper.listUserPerms(userId);Set<String> permsSet = new HashSet<>();for (String perm : perms) {if (StringUtils.isNotBlank(perm)) {permsSet.addAll(Arrays.asList(perm.trim().split(",")));}}return permsSet;}@Overridepublic List<Tree<MenuDO>> listMenuTree(Long id) {List<Tree<MenuDO>> trees = new ArrayList<Tree<MenuDO>>();List<MenuDO> menuDOs = menuMapper.listMenuByUserId(id);for (MenuDO sysMenuDO : menuDOs) {Tree<MenuDO> tree = new Tree<MenuDO>();tree.setId(sysMenuDO.getMenuId().toString());tree.setParentId(sysMenuDO.getParentId().toString());tree.setText(sysMenuDO.getName());Map<String, Object> attributes = new HashMap<>(16);attributes.put("url", sysMenuDO.getUrl());attributes.put("icon", sysMenuDO.getIcon());tree.setAttributes(attributes);trees.add(tree);}// 默认顶级菜单为0,根据数据库实际情况调整List<Tree<MenuDO>> list = BuildTree.buildList(trees, "0");return list;}}

关于@Transactional

后台管理页面源码

整个页面通过页面嵌套组成,由index_v1.html和main.html组成

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="renderer" content="webkit"><title>面向学习型的开源框架,简洁高效,减少过渡封装,展现技术本质</title><meta name="keywords" content="面向学习型的开源框架,简洁高效,杜绝过渡封装,展现技术本质本质"><meta name="description" content="面向学习型的开源框架,简洁高效,杜绝过渡封装,展现技术本质本质"><!--[if lt IE 9]><meta http-equiv="refresh" content="0;ie.html"/><![endif]--><link rel="shortcut icon" href="favicon.ico"><link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet"><link href="/css/font-awesome.min.css?v=4.4.0" rel="stylesheet"><link href="/css/plugins/toastr/toastr.min.css" rel="stylesheet"><link href="/css/animate.css" rel="stylesheet"><link href="/css/style.css?v=4.1.0" rel="stylesheet">
</head>
<body class="fixed-sidebar full-height-layout gray-bg"style="overflow: hidden">
<div id="wrapper"><!--左侧导航开始--><nav class="navbar-default navbar-static-side" role="navigation"><div class="nav-close"><i class="fa fa-times-circle"></i></div><div class="sidebar-collapse"><ul class="nav" id="side-menu"><li class="nav-header"><div><span><img alt="image" class="img-circle" height="60" width="60" th:src="${picUrl}"/></span><h3 class="" style="color: #ffffff"></i>BootDo后台管理系统</h3></div><div class="dropdown profile-element hidden"><a data-toggle="dropdown" class="dropdown-toggle" href="#"><span class="clear"><span class="block m-t-xs"><strong class="font-bold"th:text="${username}">admin</strong></span><span class="text-muted text-xs block">超级管理员<b class="caret"></b></span></span></a><ul class="dropdown-menu animated fadeInRight m-t-xs"><li><a @click="personal" href="#">修改头像</a></li><li><a @click="personal" href="#">个人资料</a></li><li><a @click="personal" href="#">密码修改</a></li><li><a @click="personal" href="#">信箱</a></li><li class="divider"></li><li><a href="/logout">安全退出</a></li></ul><div class="logo-element">BootDo</div></div></li><li><a href="#"> <i class="fa fa-home"></i> <spanclass="nav-label">主页</span> <span class="fa arrow"></span></a><ul class="nav nav-second-level"><li><a id="index001" class="J_menuItem" href="index_v1.html"data-index="0" th:href="@{/main}">了解BootDo</a></li></ul></li><li th:each="menu : ${menus}"><a href="#"> <iclass="fa fa fa-bar-chart-o" th:class="${menu.attributes.icon}"></i><span class="nav-label" th:text="${menu.text}">基础信息</span> <spanclass="fa arrow"></span></a><ul class="nav nav-second-level"><li th:each="cmenu : ${menu.children}"><a class="J_menuItem" href="graph_echarts.html"th:text="${cmenu.text}"th:href="${cmenu.attributes.url}">系统管理</a></li></ul></li></ul></div></nav><!--左侧导航结束--><!--右侧部分开始--><div id="page-wrapper" class="gray-bg dashbard-1"><div class="row border-bottom"><nav class="navbar navbar-static-top" role="navigation"style="margin-bottom: 0"><div class="navbar-header"><a class="navbar-minimalize minimalize-styl-2 btn btn-default "href="#" title="收起菜单"><i class="fa fa-bars"></i> </a><form role="search" class="navbar-form-custom"method="post" action=""><div class="form-group"><input type="text" placeholder="请输入您需要查找的内容 …"class="form-control" name="top-search" id="top-search"></div></form></div><ul class="nav navbar-top-links navbar-right"><li class="hidden-xs"><a href="/blog" target="_Blank"class=""><i class="fa fa-rss-square"></i>博客</a></li><li class="dropdown"><a class="dropdown-toggle count-info"data-toggle="dropdown" href="#"> <i class="fa fa-envelope"></i><span class="label label-warning">{{total}}</span>通知</a><ul class="dropdown-menu dropdown-messages"><li v-for="row in rows" class="m-t-xs"><div class="dropdown-messages-box"><a class="pull-left"> <iclass="fa fa-server"></i></a><div class="media-body"><small class="pull-right">{{row.before}}</small><strong>{{row.sender}}</strong>{{row.title}} <br><small class="text-muted">{{row.updateDate}}</small></div></div><div class="divider"></div></li><li><div class="text-center link-block"><a class="J_menuItem" href="/oa/notify/selfNotify"> <iclass="fa fa-envelope"></i> <strong> 查看所有消息</strong></a></div></li></ul></li><li class="hidden-xs"><a @click="personal" href="#" ><i class="fa fa-id-card"></i> 个人</a></li><li class="dropdown hidden-xs"><aclass="right-sidebar-toggle" aria-expanded="false"> <iclass="fa fa-tasks"></i> 主题</a></li></ul></nav></div><div class="row content-tabs"><button class="roll-nav roll-left J_tabLeft"><i class="fa fa-backward"></i></button><nav class="page-tabs J_menuTabs"><div class="page-tabs-content"><a href="javascript:;" class="active J_menuTab"data-id="index_v1.html">首页</a></div></nav><button class="roll-nav roll-right J_tabRight"><i class="fa fa-forward"></i></button><div class="btn-group roll-nav roll-right"><button class="dropdown J_tabClose" data-toggle="dropdown">关闭操作<span class="caret"></span></button><ul role="menu" class="dropdown-menu dropdown-menu-right"><li class="J_tabShowActive"><a>定位当前选项卡</a></li><li class="divider"></li><li class="J_tabCloseAll"><a>关闭全部选项卡</a></li><li class="J_tabCloseOther"><a>关闭其他选项卡</a></li></ul></div><a href="/logout" class="roll-nav roll-right J_tabExit"><iclass="fa fa fa-sign-out"></i> 退出</a></div><div class="row J_mainContent" id="content-main"><iframe class="J_iframe" name="iframe0" width="100%" height="100%"src="" th:src="@{/main}" frameborder="0" data-id="index_v1.html"seamless></iframe></div><div class="footer"><div class="pull-right">BootDo面向学习型的开源框架</div></div></div><!--右侧部分结束--><!--右侧边栏开始--><div id="right-sidebar"><div class="sidebar-container"><ul class="nav nav-tabs navs-3"><li class="active"><a data-toggle="tab" href="#tab-1"> <iclass="fa fa-gear"></i> 主题</a></li><li class=""><a data-toggle="tab" href="#tab-2"> 通知 </a></li><li><a data-toggle="tab" href="#tab-3"> 项目进度 </a></li></ul><div class="tab-content"><div id="tab-1" class="tab-pane active"><div class="sidebar-title"><h3><i class="fa fa-comments-o"></i> 主题设置</h3><small><i class="fa fa-tim"></i>你可以从这里选择和预览主题的布局和样式,这些设置会被保存在本地,下次打开的时候会直接应用这些设置。</small></div><div class="skin-setttings"><div class="title">主题设置</div><div class="setings-item"><span>收起左侧菜单</span><div class="switch"><div class="onoffswitch"><input type="checkbox" name="collapsemenu"class="onoffswitch-checkbox" id="collapsemenu"> <labelclass="onoffswitch-label" for="collapsemenu"> <spanclass="onoffswitch-inner"></span> <spanclass="onoffswitch-switch"></span></label></div></div></div><div class="setings-item"><span>固定顶部</span><div class="switch"><div class="onoffswitch"><input type="checkbox" name="fixednavbar"class="onoffswitch-checkbox" id="fixednavbar"> <labelclass="onoffswitch-label" for="fixednavbar"> <spanclass="onoffswitch-inner"></span> <spanclass="onoffswitch-switch"></span></label></div></div></div><div class="setings-item"><span> 固定宽度 </span><div class="switch"><div class="onoffswitch"><input type="checkbox" name="boxedlayout"class="onoffswitch-checkbox" id="boxedlayout"> <labelclass="onoffswitch-label" for="boxedlayout"> <spanclass="onoffswitch-inner"></span> <spanclass="onoffswitch-switch"></span></label></div></div></div><div class="title">皮肤选择</div><div class="setings-item default-skin nb"><span class="skin-name "> <a href="#" class="s-skin-0">默认皮肤 </a></span></div><div class="setings-item blue-skin nb"><span class="skin-name "> <a href="#" class="s-skin-1">蓝色主题 </a></span></div><div class="setings-item yellow-skin nb"><span class="skin-name "> <a href="#" class="s-skin-3">黄色/紫色主题 </a></span></div></div></div><div id="tab-2" class="tab-pane"><div class="sidebar-title"><h3><i class="fa fa-comments-o"></i> 最新通知</h3><small><i class="fa fa-tim"></i> 您当前有10条未读信息</small></div></div><div id="tab-3" class="tab-pane"><div class="sidebar-title"><h3><i class="fa fa-cube"></i> 最新任务</h3><small><i class="fa fa-tim"></i> 您当前有14个任务,10个已完成</small></div></div></div></div></div>
</div>
<!-- 全局js -->
<script src="/js/jquery.min.js?v=2.1.4"></script>
<script src="/js/bootstrap.min.js?v=3.3.6"></script>
<script src="/js/plugins/metisMenu/jquery.metisMenu.js"></script>
<script src="/js/plugins/slimscroll/jquery.slimscroll.min.js"></script>
<script src="/js/plugins/layer/layer.min.js"></script>
<!-- 自定义js -->
<script src="/js/app.js?v=4.1.0"></script>
<script type="text/javascript" src="/js/contabs.js"></script>
<!-- 第三方插件 -->
<script src="/js/plugins/pace/pace.min.js"></script>
<!-- vue -->
<script type="text/javascript"src="/js/vue.min.js"></script>
<script src="/js/appjs/oa/webSocket/sockjs.min.js"></script>
<script src="/js/appjs/oa/webSocket/stomp.min.js"></script>
<!-- Toastr script -->
<script src="/js/plugins/toastr/toastr.min.js"></script>
<script type="text/javascript">var stompClient = null;$(function () {connect();});function connect() {var sock = new SockJS("/endpointChat");var stomp = Stomp.over(sock);stomp.connect('guest', 'guest', function(frame) {/**
 订阅了/user/queue/notifications 发送的消息,这里雨在控制器的 convertAndSendToUser 定义的地址保持一致,
*  这里多用了一个/user,并且这个user 是必须的,使用user 才会发送消息到指定的用户。
*  */stomp.subscribe("/user/queue/notifications", handleNotification);stomp.subscribe('/topic/getResponse', function (response) { //订阅/topic/getResponse 目标发送的消息。这个是在控制器的@SendTo中定义的。toastr.options = {"closeButton": true,"debug": false,"progressBar": true,"positionClass": "toast-bottom-right","onclick": null,"showDuration": "400","hideDuration": "1000","timeOut": "7000","extendedTimeOut": "1000","showEasing": "swing","hideEasing": "linear","showMethod": "fadeIn","hideMethod": "fadeOut"}toastr.info(JSON.parse(response.body).responseMessage);});});function handleNotification(message) {wrapper.notify();toastr.info(message.body);}}var wrapper = new Vue({el: '#wrapper',data: {total: '',rows: '',},methods: {notify: function () {$.getJSON('/oa/notify/message', function (r) {wrapper.total = r.total;wrapper.rows = r.rows;});},personal: function () {layer.open({type: 2,title: '个人设置',maxmin: true,shadeClose: false,area: ['800px', '600px'],content: '/sys/user/personal'});}},created: function () {this.notify()}})
</script>
</body>
</html>

嵌套

        <div class="row J_mainContent" id="content-main"><iframe class="J_iframe" name="iframe0" width="100%" height="100%"src="" th:src="@{/main}" frameborder="0" data-id="index_v1.html"seamless></iframe></div>

了解链接

关于sockjs即时通信:SockJS实践:即时通信关键点 - 简书

关于iframe页面嵌套:Web前端之iframe详解 - 滥好人 - 博客园

后台管理页面(bootdo)相关推荐

  1. 后台管理页面布局(左侧导航长度根据右侧内容无限延伸)

    2019独角兽企业重金招聘Python工程师标准>>>     如图为标准后台管理页面布局,左侧导航布局一般是设置高度100%来适应所有尺寸的显示器打开的浏览器高度. 但是这种布局会 ...

  2. 后台管理页面基本布局方式

    经典页面布局 简易后台管理页面布局 1 左边菜单栏固定 2 header固定高度(宽度自适应) 3 主体统计列表(宽度自适应) 代码如下 html <div class="main&q ...

  3. bootstrap搭建后台管理页面

    bootstrap搭建后台管理页面 管理页面主体包括4部分,顶部导航栏,左侧导航栏,中间正文页和底部的页脚.其中左侧和顶部使用的是bootstrap的导航和下拉菜单组件,主体页面为了演示方便,使用了i ...

  4. Vue后台管理页面总体结构及主要功能设计

    后台管理页面总体结构为:顶部左侧为系统标题,顶部右侧为用户图标及改密.退出菜单.中间左侧为功能菜单,中间右侧为操作区域,可以用el-row配合el-col来实现布局.其中导航菜单可以用el-menu配 ...

  5. Django admin后台管理页面的常用设置

    Django admin后台管理页面的常用设置 选择列表选项choices # filename: models.pyfrom django.db import models# 例1 int类型:ST ...

  6. 登录、注册、后台管理页面(动态)

    一.作业需求: 1.后台管理主界面(左边菜单框.(全选.反选)框.返回顶部按钮) 2.老男孩登录.注册页面 二.博客地址:https://www.cnblogs.com/catepython/p/93 ...

  7. 老男孩Day16作业:登录、注册、后台管理页面(动态)

    一.作业需求:1.后台管理主界面(左边菜单框.(全选.反选)框.返回顶部按钮)2.老男孩登录.注册页面二.博客地址:https://www.cnblogs.com/catepython/p/93063 ...

  8. Linux下RabbitMq的安装以及后台管理页面的安装

    安装: 1.下载安装包,可以去github找对应的rpm文件,也可以从rabbitmq · GitCode获取,注意erlang和rabbitmq需要对应版本否则会出现版本异常报错,可以从Rabbit ...

  9. 用jquery-easyui的布局layout写后台管理页面

    先在官网下载easyui文档 引入头部文件 <link rel="stylesheet" type="text/css" href="${pag ...

  10. 微信后台管理页面html

    wx_manage(git仓库见文末) 原文:https://i847.cn/article/63834.html 介绍 微信后台管理页面,由其他项目二次开发而来. 注意 自己写接口,接口名字和参数详 ...

最新文章

  1. 897B. Chtholly's request#长度为偶数的回文数(模拟)
  2. 软件定义的网络体系结构是否使用网段路由?—Vecloud微云
  3. 【Python】Modin,只需一行代码加速你的Pandas
  4. 计算机二级考数组吗,计算机二级考试!
  5. 用Visual Studio2019自定义项目模板
  6. 【Antlr】Antler 解析 没有进入 visit 相关方法中 一个愚蠢的问题
  7. JMM内存模型详解(一)
  8. 如何搭建一个spring boot项目
  9. 【技术专题研究】OSPF的LSA类型
  10. OpenCV通过cvFindContours与cvDrawCountours函数查找轮廓
  11. 【网络是怎样连接的】—— TCP/IP 传输数据
  12. 【上班摸鱼系列】文字选择类游戏:《职员默示录 - 启》末日丧尸爆发,你,是什么下场!?
  13. 手机共享计算机网络连接,电脑和手机网络的相互共享
  14. php 499,nginx 499 状态码优化
  15. 美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇
  16. 坚定的更稳固的更有信心的脚步,前进。
  17. xxxxxxxxxxxxx
  18. uni-app实现联系人右侧索引字母表点击滚动到相应的位置(uni.pageScrollTo(OBJECT))
  19. 在js中调用微信的扫描二维码功能
  20. 当我们再谈AI芯片,有些东西已经变了

热门文章

  1. 《现代操作系统》读书笔记
  2. ChinaPub地毯式搜索 - 数据库
  3. Lattice、ALTERA、Xilinx FPGA元件封装信息官网下载地址
  4. 清华大学王行言教授DELPHI程序设计在线学习
  5. vc2008调试技巧之输出重定向
  6. 微信小程序:全新独立后台月老办事处一元交友盲盒
  7. ubuntu12.04完美安装QQ2012、QQMusic、Foxmail等
  8. opencv:image-imageData+image-widthStep*i)[j]表达式含义
  9. 005 IK-analyzer添加搜狗词库
  10. 联众打码写滑动_自己写了一个答题的软件现在分享一下(在分享一下联众打码源码调用) _ 综合讨论 - 按键精灵论坛...