LayuiMini+spring boot实现动态左侧边栏(不定时更新)


前言

自己开发中遇到的问题,仅供参考,有什么不足之处还望指出。

2022.10.28更新:layui更新2.7.x版本后动态侧边栏无法使用问题
2022.11.26更新:选择时间范围并计算剩余天数


一、前端请求接口更改

在index.html页面里面的javaScript里面,代码如下:

var options = {
            iniUrl: "/menu",    // 初始化接口,请求后台接口
            clearUrl: "/mini/api/clear.json", // 缓存清理接口
            urlHashLocation: true,      // 是否打开hash定位
            bgColorDefault: false,      // 主题默认配置
            multiModule: true,          // 是否开启多模块
            menuChildOpen: false,       // 是否默认展开菜单
            loadingTime: 0,             // 初始化加载时间
            pageAnim: true,             // iframe窗口动画
            maxTabNum: 20,              // 最大的tab打开数量
        };

二、后端接口

1.Controller层

代码如下:

// 获取菜单栏
    @GetMapping("/menu")
    @ResponseBody
    public Map<String, Object> menu() {
        return menuService.menu();
    }

2.Service层

代码如下(示例):

public interface MenuService {
    Map<String, Object> menu();
}

3.ServiceImpl层

该方法来自官网,但是我自己用的时候前端总是没有数据,于是自己做了一点更改。

@Service
public class MenuServiceImpl implements MenuService {

    @Resource
    private MenuMapper menuMapper;

	// 使用vo的写法
    @Override
    public Map<String, Object> menu() {
        Map<String, Object> map = new HashMap<>();
        Map<String,Object> home = new HashMap<>();
        Map<String,Object> logo = new HashMap<>();
        List<SysMenu> menuList = menuMapper.findAllByStatusOrderBySort();
        List<MenuVo> menuInfo = new ArrayList<>();
        for (SysMenu e : menuList) {
            MenuVo menuVO = new MenuVo();
            menuVO.setId(e.getId());
            menuVO.setPId(e.getPId());
            menuVO.setHref(e.getHref());
            menuVO.setTitle(e.getTitle());
            menuVO.setIcon(e.getIcon());
            menuVO.setTarget(e.getTarget());
            menuInfo.add(menuVO);       
        }
        map.put("menuInfo", TreeUtil.toTree(menuInfo, 0)); // 0:父id
        home.put("title","首页");
        home.put("href","/welcome-1");//控制器路由,自行定义
        map.put("homeInfo",home);
        logo.put("title","后台管理系统");
        logo.put("image","/users/images/logo.png");//静态资源文件路径,可使用默认的logo.png
        logo.put("href","");
        map.put("logoInfo",logo);
        return map;
    }
}

	// 只有一个实体类的写法
	 @Override
    public Map<String, Object> menu() {
        Map<String, Object> map = new HashMap<>();
        Map<String,Object> home = new HashMap<>();
        Map<String,Object> logo = new HashMap<>();
        // 查询数据库:Mapper 层 :List<SysMenu> findAllByStatusOrderBySort();
        List<SysMenu> menuList = menuMapper.findAllByStatusOrderBySort();
        map.put("menuInfo", TreeUtil.toTree(menuList , 0)); 
        home.put("title","首页");
        home.put("href","/welcome-1");//控制器路由,自行定义
        map.put("homeInfo",home);
        logo.put("title","后台管理系统");
        logo.put("image","/users/images/logo.png");//静态资源文件路径,可使用默认的logo.png
        logo.put("href","");
        map.put("logoInfo",logo);
        return map;
    }
}

4.递归工具类

public class TreeUtil {

	// 使用vo的写法
    public static List<MenuVo> toTree(List<MenuVo> treeList, Integer pid) {
        List<MenuVo> retList = new ArrayList<MenuVo>();
        for (MenuVo parent : treeList) {
            if (pid.equals(parent.getPId())) {
                retList.add(findChildren(parent, treeList));
            }
        }
        return retList;
    }
    private static MenuVo findChildren(MenuVo parent, List<MenuVo> treeList) {
        for (MenuVo child : treeList) {
            if (parent.getId().equals(child.getPId())) {
                if (parent.getChild() == null) {
                    parent.setChild(new ArrayList<>());
                }
                parent.getChild().add(findChildren(child, treeList));
            }
        }
        return parent;
    }

    // 不使用vo的写法
    /*public static List<SysMenu> toTree(List<SysMenu> treeList, Integer pid) {
        List<SysMenu> retList = new ArrayList<SysMenu>();
        for (SysMenu parent : treeList) {
            if (pid.equals(parent.getPId())) {
                retList.add(findChildren(parent, treeList));
            }
        }
        return retList;
    }
    private static SysMenu findChildren(SysMenu parent, List<SysMenu> treeList) {
        for (SysMenu child : treeList) {
            if (parent.getId().equals(child.getPId())) {
                if (parent.getChild() == null) {
                    parent.setChild(new ArrayList<>());
                }
                parent.getChild().add(findChildren(child, treeList));
            }
        }
        return parent;
    }
   */

4.layui版本升级至2.7.6,左侧菜单栏无法显示问题

1.找到miniMeun.js,替换成下列js。注意不是全部,看清楚更换。

/**
 * 渲染一级菜单
 */
compileMenu: function(menu,isSub){
    var menuHtml = '<li {{#if( d.menu){ }}  data-menu="{{d.menu}}" {{#}}} class="layui-nav-item menu-li {{d.childOpenClass}} {{d.className}}"  {{#if( d.id){ }}  id="{{d.id}}" {{#}}}> <a {{#if( d.href){ }} layuimini-href="{{d.href}}" {{#}}} {{#if( d.target){ }}  target="{{d.target}}" {{#}}} href="javascript:;">{{#if( d.icon){ }}  <i class="{{d.icon}}"></i> {{#}}} <span class="layui-left-nav">{{d.title}}</span></a>  {{# if(d.children){}} {{- d.children}} {{#}}} </li>' ;
    if(isSub){
        menuHtml = '<dd class="menu-dd {{d.childOpenClass}} {{ d.className }}"> <a href="javascript:;"  {{#if( d.menu){ }}  data-menu="{{d.menu}}" {{#}}} {{#if( d.id){ }}  id="{{d.id}}" {{#}}} {{#if(( !d.child || !d.child.length ) && d.href){ }} layuimini-href="{{d.href}}" {{#}}} {{#if( d.target){ }}  target="{{d.target}}" {{#}}}> {{#if( d.icon){ }}  <i class="{{d.icon}}"></i> {{#}}} <span class="layui-left-nav"> {{d.title}}</span></a> {{# if(d.children){}} {{- d.children}} {{#}}}</dd>'
    }
    return laytpl(menuHtml).render(menu);
},
compileMenuContainer :function(menu,isSub){
    var wrapperHtml = '<ul class="layui-nav layui-nav-tree layui-left-nav-tree {{d.className}}" id="{{d.id}}">{{- d.children}}</ul>' ;
    if(isSub){
        wrapperHtml = '<dl class="layui-nav-child ">{{- d.children}}</dl>' ;
    }
    if(!menu.children){
        return "";
    }
    return laytpl(wrapperHtml).render(menu);
},

2.箭头样式不正常:删除layuimini.css行683-736的代码.

5.选择时间范围并计算剩余天数(2022/11/26更新)

如图:选择开始时间与结束时间,到期的天数为到期日期当前的日期

在这里插入图片描述

1、HTML代码:

 <div class="layui-form-item">
        <label class="layui-form-label required">证书日期范围</label>
        <div class="layui-inline" id="date-range">
            <div class="layui-input-inline">
                <input type="text" id="ybStartTime" lay-verify="date" name="ybStartTime" class="layui-input" placeholder="发证日期">
            </div>
        <div class="layui-form-mid">-</div>
            <div class="layui-input-inline">
                <input type="text" id="ybDueTime" name="ybDueTime" lay-verify="ybDueTime" class="layui-input" placeholder="到期日期">
            </div>
       </div>
    </div>

2、JS

       		$ = layui.$,
            laydate = layui.laydate;

        // 日期范围和计算天数
        laydate.render({
            elem: '#date-range', // 开始结束时间的input框的父选择器
            range: ['#ybStartTime','#ybDueTime'], // 数组格式为2.6.6开始新增
            trigger: 'click',
            done: function (value, date, ybDueTime) {
                // 起始时间
                let first = value.substr(0,10);
                let f1 = new Date(first);
                // 终止时间
                let last = value.substr(13, 13);
                let l1 = new Date(last);
                let serverTime = new Date(); // 获取当前日期
				// 如果需要计算总天数就用结束时间减去当前时间,把serverTime替换成上面的起始时间 f1
                // 算出相差毫秒值
                var day = l1.getTime() - serverTime.getTime();
                var time = parseInt(day / (1000 * 60 * 60 * 24) + 1);
                if (time > 0) {
                    // 显示时间
                    lay('#ybSky').val(time);
                }else {
                    lay('#ybSky').val(0);
                }
                //因为我做的结束时间和剩余天数可为空,所以写了下面这个可以清除的js
                // 离焦清除结束时间和天数内容
                //$("#ybDueTime").blur(function () {
                  //  document.getElementById("ybSky").value = null;
                    //document.getElementById("ybDueTime").value = null;
                //});
            }
        });

        // 结束日期自定义校验 日期正则表达式
        var exp = /^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/;
        // 不为空的时候判断格式
        form.verify({
            ybDueTime: function (value) {
                if (value && !exp.test(value)) {
                    return '日期格式不正确';
                }
            }
        })

总结

有什么不足之处还望大家指出,有什么问题可以评论。以后遇到关于layuimini的问题也会在此更新。

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的示例代码,演示如何使用Spring Security实现动态分配菜单权限和数据权限。在本示例中,我们假设您已经创建了一个名为“menu”和一个名为“role”的数据库表。 首先,您需要定义菜单和角色的实体类: ```java @Entity @Table(name = "menu") public class Menu { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String url; private String icon; private Long parentId; // getter and setter } @Entity @Table(name = "role") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; private String menus; // 存储菜单ID的字符串,以逗号分隔 // getter and setter } ``` 然后,您需要为这些实体类创建相应的Repository接口: ```java public interface MenuRepository extends JpaRepository<Menu, Long> { } public interface RoleRepository extends JpaRepository<Role, Long> { } ``` 接下来,您需要在Spring Security中配置角色和权限: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") .anyRequest().authenticated() .and().formLogin().loginPage("/login").permitAll() .and().logout().logoutUrl("/logout").permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` 在上述配置中,我们定义了两个角色:“ADMIN”和“USER”,并将其与不同的资源进行关联。例如,只有拥有“ADMIN”角色的用户才能访问“/admin/**”路径。 接下来,您需要实现自定义的UserDetailsService类,以根据用户的角色动态生成菜单: ```java @Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Autowired private MenuRepository menuRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found with username: " + username); } List<GrantedAuthority> authorities = new ArrayList<>(); String[] roles = user.getRoles().split(","); for (String roleName : roles) { Role role = roleRepository.findByName(roleName); if (role != null) { authorities.add(new SimpleGrantedAuthority(role.getName())); } String[] menuIds = role.getMenus().split(","); for (String menuId : menuIds) { Menu menu = menuRepository.findById(Long.parseLong(menuId)).orElse(null); if (menu != null) { // 生成菜单 } } } return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities); } } ``` 在上述代码中,我们首先获取用户所拥有的角色,然后根据角色获取对应的菜单。最后,我们将这些菜单生成为动态菜单,以供用户访问。 最后,您可以使用Spring Data JPA和Thymeleaf等技术实现动态生成菜单的功能。例如: ```html <ul class="sidebar-menu"> <li th:each="menu : ${menus}" th:class="${menu.parentId == null ? 'treeview' : ''}"> <a th:href="@{${menu.url}}"> <i th:class="${menu.icon}"></i> <span th:text="${menu.name}"></span> <i th:if="${menu.children.size() > 0}" class="fa fa-angle-left pull-right"></i> </a> <ul th:if="${menu.children.size() > 0}" class="treeview-menu"> <li th:each="child : ${menu.children}"> <a th:href="@{${child.url}}"> <i th:class="${child.icon}"></i> <span th:text="${child.name}"></span> </a> </li> </ul> </li> </ul> ``` 上述代码将根据用户所拥有的菜单动态生成侧边栏菜单。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值