最近在写Struts2+Spring+Mybatis+freemarker 的项目(一个简单的订单后台管理)。
一、起因
用的这个ace后台模板看着怪好看的,就是左侧导航栏竟然每个页面都要写一次???为啥不带动态选中?这样每个页面就变得非常臃肿,我还是喜欢干干净净的页面看着多舒服!所以,就把这个导航栏改造了一下,变成动态模板,每个页面只要引入就可以了动态选中!
就像下面这样:
1. 这是模板代码:
<ul class="nav nav-list" id="menu">
<#assign text>${menuList!}</#assign>
<#assign menuList=text?eval/>
<#list menuList as menu>
<#--子菜单当前菜单是否被选中-->
<#if menu.sonMenuActive == true>
<li class="active open">
<#elseif menu.active == true>
<li class="active">
<#else >
<li class="">
</#if>
<#--是否存在子菜单,显示点击地址-->
<#if menu.haveSonMenu == false>
<a href="${ctx}${menu.menuUrl!}">
<#else >
<a href="javascript:void(0);" class="dropdown-toggle">
</#if>
<i class="menu-icon fa ${menu.menuIcon!}"></i>
<span class="menu-text"> ${menu.menuName!} </span>
<#--是否存在子菜单-->
<#if menu.haveSonMenu == true>
<b class="arrow fa fa-angle-down"></b>
</#if>
</a>
<b class="arrow"></b>
<#--存在子菜单-->
<#if menu.haveSonMenu == true>
<#--子菜单有被选中-->
<#if menu.sonMenuActive == true>
<ul class="submenu nav-show" style="display: block">
<#else>
<ul class="submenu nav-hide" style="display: none">
</#if>
<#list menu.parentMenuList as item>
<#if item.active == true>
<li class="active">
<#else>
<li class="">
</#if>
<a href="${ctx}${item.menuUrl!}">
<i class="menu-icon fa fa-caret-right"></i>
${item.menuName!}
</a>
<b class="arrow"></b>
</li>
</#list>
</ul>
</#if>
</li>
</#list>
</ul>
2. 这是效果图:
3. 这是引入页面:
每个页面加一行代码就搞定了,还是挺不错的。不过。。。节省下来的代码,都被在后台写出了!
二、详细介绍
因为要动态选中,所以将导航栏中出现的URL都存放在数据库中,定义一个菜单表(Memu)。每次访问时候在拦截器中获取访问的URL和菜单表中所有设定的URL。如果访问的是一级菜单,则设定导航栏中一级菜单选中,如果是二级菜单,就保持选中菜单不变,完成动态选中的过程。
1. 菜单表结构
一级目录的父级ID就是0,二级目录的父级ID就是一级目录的主键ID。表的结构还是挺简单,便于理解
2. 后台Struts2 拦截器获取request请求URL
- 先自定义了一个拦截器,除了登录,其他所有的包都继承此拦截器,拦截所有请求
<!--登陆拦截器包-->
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
<!--注册拦截器-->
<interceptor name="login" class="top.cmyang.app.interceptor.LoginInterceptor"></interceptor>
<!--注册拦截器栈-->
<interceptor-stack name="myStack">
<interceptor-ref name="login"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!--注册拦截器到Struts 2-->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
<!--定义返回结果-->
<global-results>
<result name="login">/templates/login.ftl</result>
</global-results>
</package>
- 需要拦截的地方继承此包
<package name="productPackage" namespace="/product" extends="loginInterceptor">
-
拦截器中设置,最主要的是在拦截器中获取HttpServletRequest 对象,这样才能拿到请求URL进行处理
闲话不多说,上代码
ActionContext actionContext = invocation.getInvocationContext();
HttpServletRequest request = (HttpServletRequest) actionContext.get(ServletActionContext.HTTP_REQUEST);
最终的结果如下面图一样,主要分为两部分:
- 获取访问的action地址,通过 request.getServletPath() 即可;
- 处理得到的action地址类似于 : /home/index。