最近在写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);

最终的结果如下面图一样,主要分为两部分:

  1. 获取访问的action地址,通过 request.getServletPath() 即可;
  2. 处理得到的action地址类似于 : /home/index。

后端动态处理代码