就在上周突然收到一个需求,左侧导航栏要无限级可配置。。。一般左侧导航栏都是三级最多五级很少有需要无限级的,有人可能会问无限级是什么?(再次声明我不卖保健品。。。)无限级导航就是一个无限下级的导航树结构。在这里给大家分享一下我做的demo。温故而知,也方便各位大虾遇到此坑能稳稳跳过。废话不说开始吧。
第一步搭建环境:springMVC
Web.xml
xml version="1.0" encoding="UTF-8"?>xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true"> Archetype Created Web Application contextConfigLocation classpath:config/contextConfigLocation.xml org.springframework.web.context.ContextLoaderListener SpringEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true SpringEncodingFilter /* logbackConfigLocation classpath:logback.xml ch.qos.logback.ext.spring.web.LogbackConfigListener springMVC org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:config/spring-mvc.xml 1 springMVC / /WEB-INF/jsp/login.jsp
pom.xml
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
com.cms
MyCms
war
1.0-SNAPSHOT
MyCms Maven Webapp
http://maven.apache.org
4.0.5.RELEASE
3.2.1
1.6.6
1.2.12
5.1.35
3.1.6
org.springframework
spring-core
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-aspects
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework.data
spring-data-redis
1.6.2.RELEASE
redis.clients
jedis
2.8.0
org.apache.cxf
cxf-rt-frontend-jaxws
${cxf.version}
org.apache.cxf
cxf-rt-transports-http
${cxf.version}
org.apache.cxf
cxf-rt-transports-http-jetty
${cxf.version}
org.mybatis
mybatis-ehcache
1.0.0
org.springframework
spring-test
${spring.version}
test
javax.mail
mail
1.4.7
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-web
${spring.version}
mysql
mysql-connector-java
${mysql.version}
com.alibaba
druid
0.2.23
com.alibaba
fastjson
1.1.41
org.slf4j
slf4j-api
${slf4j.version}
ch.qos.logback
logback-classic
1.1.2
ch.qos.logback
logback-core
1.1.2
org.logback-extensions
logback-ext-spring
0.1.1
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
1.2.0
javax.servlet
javax.servlet-api
3.0.1
javax.servlet.jsp
javax.servlet.jsp-api
2.3.2-b01
javax.servlet
jstl
1.2
junit
junit
3.8.1
test
org.wicketstuff
wicketstuff-logback
7.0.0-M5
log4j
log4j
1.2.14
compile
MyCms
org.mybatis.generator
mybatis-generator-maven-plugin
1.3.2
true
true
spring-mvc.xml
在这里有个自定义的拦截器,是为了验证用户是否登录的。。。
xml version="1.0" encoding="UTF-8"?>xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> class="org.springframework.http.converter.StringHttpMessageConverter"> mvc:message-converters> mvc:annotation-driven> <context:component-scan base-package="com.cms.controller" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> context:component-scan>name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <mvc:resources mapping="/css/**" location="/static/css/"/> <mvc:resources mapping="/img/**" location="/static/img/"/> <mvc:resources mapping="/js/**" location="/static/js/"/> <mvc:resources mapping="/layer/**" location="/static/layer/"/> <mvc:resources mapping="/font/**" location="/static/layer/font/"/>name="favorPathExtension" value="true"/> name="favorParameter" value="true"/> name="parameterName" value="format"/> name="ignoreAcceptHeader" value="false"/> name="mediaTypes"> json=application/json xml=application/xml html=text/html name="defaultContentType" value="text/html"/> id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/**/*.css"/> <mvc:exclude-mapping path="/**/*.js"/> <mvc:exclude-mapping path="/**/*.png"/> <mvc:exclude-mapping path="/**/*.gif"/> <mvc:exclude-mapping path="/**/*.jpg"/> <mvc:exclude-mapping path="/**/*.jpeg"/> class="com.cms.util.logInterception"> mvc:interceptor> mvc:interceptors>
mybatis-config.xml
这里咱们用的阿里的数据连接池Druid
xml version="1.0" encoding="UTF-8"?>xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"> id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> name="driverClassName" value="${jdbc_driverClassName}"/> name="url" value="${jdbc_url}"/> name="username" value="${jdbc_username}"/> name="password" value="${jdbc_password}"/> name="initialSize" value="10"/> name="minIdle" value="10"/> name="maxActive" value="50"/> name="maxWait" value="60000"/> name="timeBetweenEvictionRunsMillis" value="60000" /> name="minEvictableIdleTimeMillis" value="300000" /> name="validationQuery" value="SELECT 'x'" /> name="testWhileIdle" value="true" /> name="testOnBorrow" value="false" /> name="testOnReturn" value="false" /> name="poolPreparedStatements" value="false" /> name="maxPoolPreparedStatementPerConnectionSize" value="20" /> name="filters" value="wall,stat" /> id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> name="dataSource" ref="dataSource" /> name="mapperLocations" value="classpath:com/cms/mapper/*.xml"> class="org.mybatis.spring.mapper.MapperScannerConfigurer"> name="basePackage" value="com.cms.dao" /> name="sqlSessionFactoryBeanName" value="sqlSessionFactory"> id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <tx:annotation-driven transaction-manager="transactionManager" />name="dataSource" ref="dataSource" />
contextConfigLocation.xml
jdbc_driverClassName =com.mysql.jdbc.Driver jdbc_url=jdbc:mysql://192.168.8.96:3306/qg?useUnicode=true&characterEncoding=utf8 jdbc_username=root jdbc_password=qiaogang
这里使用redis作为mybatis的二级缓存使用
xml version="1.0" encoding="UTF-8"?>mysql数据源xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:util="http://www.springframework.org/schema/util" xmlns:cxf="http://cxf.apache.org/core" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd "> id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> name="locations">
classpath:jdbc.properties classpath:redis.properties id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> name="maxIdle" value="${redis.maxIdle}" /> name="maxTotal" value="${redis.maxActive}" /> name="maxWaitMillis" value="${redis.maxWait}" /> name="testOnBorrow" value="${redis.testOnBorrow}" /> id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/> id="redisCacheTransfer" class="com.cms.util.RedisCacheTransfer"> name="jedisConnectionFactory" ref="jedisConnectionFactory"/> id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <context:component-scan base-package="com.cms.*.*"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> context:component-scan>name="connectionFactory" ref="jedisConnectionFactory" /> name="keySerializer"> class="org.springframework.data.redis.serializer.StringRedisSerializer" /> name="valueSerializer"> class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> name="hashKeySerializer"> class="org.springframework.data.redis.serializer.StringRedisSerializer"/> name="hashValueSerializer"> class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/> resource="mybatis-config.xml" /> resource="classpath:META-INF/cxf/cxf.xml"> resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxws:endpoint id="TestWebService" implementor="com.cms.webService.Impl.TestWebServiceImpl" address="/webSerciceTest_endpoint" />
jdbc_driverClassName =com.mysql.jdbc.Driver jdbc_url=jdbc:mysql://192.168.8.96:3306/qg?useUnicode=true&characterEncoding=utf8 jdbc_username=root jdbc_password=qiaogang
redis数据源
# Redis settings redis.host=192.168.8.96 redis.port=6379 redis.pass= redis.maxIdle=300 redis.maxActive=600 redis.maxWait=10000 redis.testOnBorrow=true
generatorConfig.xml(mybatis自动生成器)
xml version="1.0" encoding="UTF-8" ?> generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >location="D:\\mysql.jar"/> id="mysqlgenerator" targetRuntime="MyBatis3"> name="suppressAllComments" value="true" /> driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://192.168.16.48:3306/qg" userId="root" password="qiaogang" /> targetPackage="com.wgs.domain" targetProject="src/main/java" > name="enableSubPackages" value="true" /> name="trimStrings" value="true" /> targetPackage="com.cms.mapper" targetProject="src/main/resources" > name="enableSubPackages" value="true" /> type="XMLMAPPER" targetPackage="com.cms.dao" targetProject="src/main/java" > name="enableSubPackages" value="true" /> tableName="menu" domainObjectName="menu">
环境搭建完成,下一步让我看看数据表
user用户表:
菜单表 menu:
我们使用sql获取树形结构,再从前端使用递归渲染菜单。我们来看一下sql的写法(menuMapper.xml)
xml version="1.0" encoding="UTF-8" ?> mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >namespace="com.cms.dao.menuMapper" > type="com.cms.util.RedisCache"/> id="BaseResultMap" type="com.cms.demo.menu" > column="mid" property="mid" jdbcType="INTEGER" /> column="menuname" property="menuname" jdbcType="VARCHAR" /> column="mcode" property="mcode" jdbcType="INTEGER" /> column="supercode" property="supercode" jdbcType="INTEGER" /> column="u_id" property="uId" jdbcType="INTEGER" /> column="is_last" property="isLast" jdbcType="INTEGER" /> id="BaseTreeResultMap" type="com.cms.demo.menu" > column="mid" property="mid" jdbcType="INTEGER" /> column="menuname" property="menuname" jdbcType="VARCHAR" /> column="mcode" property="mcode" jdbcType="VARCHAR" /> column="url" property="url" jdbcType="VARCHAR" /> column="mid" property="next" javaType="java.util.ArrayList" ofType="com.cms.demo.menu" select="getNextNodeTree"/> id="NextTreeResultMap" type="com.cms.demo.menu" > column="mid" property="mid" jdbcType="INTEGER" /> column="menuname" property="menuname" jdbcType="VARCHAR" /> column="mcode" property="mcode" jdbcType="VARCHAR" /> column="url" property="url" jdbcType="VARCHAR" /> column="mid" property="next" javaType="java.util.ArrayList" ofType="com.cms.demo.menu" select="getNextNodeTree"/> id="Base_Column_List"> mid,menuname,url,mcode
这里我们会用到mybatis的一个标签Collection。 Collection主要处理“一对多”类型映射关系,例如,查询部门中有多个员工,就需要使用的到集合:List
/** *用户登录 * @param userCode 用户名 * @param userPass 密码 * @return 登录成功 */ @RequestMapping(value = "/userLogin",method = RequestMethod.POST,produces = "text/html; charset=utf-8") @ResponseBody public String userLogin(HttpSession session,@RequestParam("usercode") String userCode, @RequestParam("userpassword") String userPass, HttpServletRequest request, HttpServletResponse res) { if(userCode!=null && userPass!=null) { Listlist= testservice.userlogin(userCode,userPass); if(list!=null && list.size()>0) { CmsUser cms = list.get(0); session.setAttribute("userid",cms);//写入用户信息 Listtestservice.queryTree(); Map,Object> map1 = new HashMap , Object>(); Map ,Object> map = new HashMap , Object>(); map.put("userid",cms.getUid()); map.put("usercode",cms.getUsercode()); map1.put("next",lists); map.put("extend",map1); String navjs= JSON.toJSON(map).toString();//得到菜单列表的json session.setAttribute("nav",navjs); System.out.println(navjs); ValueOperations , Object> value = redisTemplate.opsForValue(); value.set("lp", "hello word"); System.out.println("读取内存====================="+value.get("lp")); return "success"; } else { throw new RuntimeException("用户名密码错误!!"); } }else { throw new RuntimeException("用户名密码为空!"); } }
我呢打印下得到的json串:
{
"extend": {
"next": [{
"mcode": 1000,
"menuname": "基础设置",
"mid": 1,
"next": [{
"mcode": 10001,
"menuname": "用户管理",
"mid": 3,
"next": [],
"url": "http://localhost:8080/Cms/go/test"
}, {
"mcode": 10002,
"menuname": "角色管理",
"mid": 4,
"next": [],
"url": "http://localhost:8080/Cms/go/wb"
}, {
"mcode": 10003,
"menuname": "权限管理",
"mid": 5,
"next": [],
"url": "http://localhost:8080/Cms/"
}, {
"mcode": 10004,
"menuname": "数据统计",
"mid": 6,
"next": [],
"url": "http://localhost:8080/Cms/"
}, {
"mcode": 10004,
"menuname": "字典维护",
"mid": 7,
"next": [],
"url": "http://localhost:8080/Cms/"
}],
"url": "http://localhost:8080/Cms/"
}, {
"mcode": 1001,
"menuname": "物资管理",
"mid": 2,
"next": [{
"mcode": 100101,
"menuname": "物资维护",
"mid": 8,
"next": [{
"mcode": 10010101,
"menuname": "物资分配",
"mid": 9,
"next": [],
"url": "http://localhost:8080/Cms/"
}],
"url": "http://localhost:8080/Cms/"
}],
"url": "http://localhost:8080/Cms/"
}]
},
"userid": 1
}
大家可以看到我们得到json结构,其页面效果为:
我们看一下前端的渲染写法这里我们用的layui:
function gets() { debugger; var isFirstMenu; var data=${nav};//得到数据集 var showlist = $("\"layui-nav layui-nav-tree\" lay-filter=\"hbkNavbar\">
"); isFirstMenu=data.extend.next.length; showall(data.extend.next, showlist); $("#view").append(showlist); function showall(menu_list, parent) { for (var menu in menu_list) { if (menu_list[menu].next.length > 0) { console.log(menu_list[menu].next.mcode) if(menu_list[menu].next.mcode!='0') { var lis = $(""); } var lim = $("\"layui-nav-item layui-nav-itemed\"> ") var as = $("\"javascript\:\;\">"+menu_list[menu].menuname+""); var dls = $("\"layui-nav-child\">
"); lis.appendTo(parent); lim.appendTo(lis); as.appendTo(lim); dls.appendTo(lim); showall(menu_list[menu].next, dls); }else { // console.log(menu_list[menu].url); var dd = $("\"javascript:;\" data-id='"+menu_list[menu].mid+"' data-url='"+menu_list[menu].url+"' data-title='"+menu_list[menu].menuname+"'> "+menu_list[menu].menuname+" "); dd.appendTo(parent); } } } }
大家可以仔细阅读一下,前端也是使用递归的方式去渲染的,
我们还是先得到最高节点
然后再递归渲染子节点
大功告成,喜欢的小伙伴可以关注一下咱们的公众号,关注公众号回复“牛牛浪起来“会得到高清电子书
需要源码的同学可以加小编VX"qiaoshiershao"