Vue Router 分为静态路由和动态路由,权限这块一般有几个静态页面路由用来对权限路由的增删改查,创建出来的路由会被存入到数据库内,当某个用户登录了,我们就会查询它的权限,从数据库中拿到属于他的路由数据渲染到页面上。这就是动态路由,动态路由的数据来源于后端,静态路由的数据是前端vue直接写死的。
接下来我们看看后端如何查询出数据,并且将数据库查询出的线性结构的数据改造成Tree型结构的返回给前端使用。
1.模拟数据库查询的数据
package java将线性结构的菜单转换为vuetree型结构菜单;
/**
* 模拟数据库查询的数据
* 父子全在一起的数据
* @author T011921
*
*/
public class DataBean {
//private String PRVG_TP_CODE;
private Object PRN_PRVG_ID; //父级id 顶级为0
private String PRVG_NAME;//名称
private String PRVG_ID; //父亲子集id --对应孩子的父级id
private String COMPONENT; //菜单对应的模块路由
private String URL; //访问地址
public Object getPRN_PRVG_ID() {
return PRN_PRVG_ID;
}
public void setPRN_PRVG_ID(Object pRN_PRVG_ID) {
PRN_PRVG_ID = pRN_PRVG_ID;
}
public String getPRVG_NAME() {
return PRVG_NAME;
}
public void setPRVG_NAME(String pRVG_NAME) {
PRVG_NAME = pRVG_NAME;
}
public String getPRVG_ID() {
return PRVG_ID;
}
public void setPRVG_ID(String pRVG_ID) {
PRVG_ID = pRVG_ID;
}
public String getCOMPONENT() {
return COMPONENT;
}
public void setCOMPONENT(String cOMPONENT) {
COMPONENT = cOMPONENT;
}
public String getURL() {
return URL;
}
public void setURL(String uRL) {
URL = uRL;
}
@Override
public String toString() {
return "DataBean [PRN_PRVG_ID=" + PRN_PRVG_ID + ", PRVG_NAME=" + PRVG_NAME + ", PRVG_ID=" + PRVG_ID
+ ", COMPONENT=" + COMPONENT + ", URL=" + URL + "]";
}
public DataBean(Object pRN_PRVG_ID, String pRVG_NAME, String pRVG_ID, String cOMPONENT, String uRL) {
super();
PRN_PRVG_ID = pRN_PRVG_ID;
PRVG_NAME = pRVG_NAME;
PRVG_ID = pRVG_ID;
COMPONENT = cOMPONENT;
URL = uRL;
}
public DataBean() {
super();
// TODO Auto-generated constructor stub
}
}
package java将线性结构的菜单转换为vuetree型结构菜单;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Data {
public static List<DataBean> getData() {
List<DataBean> list = new ArrayList<>();
list.add(new DataBean("0","顶级父级1","10","@/ul.vue","/aa.jsp"));
list.add(new DataBean("0","顶级父级2","11","@/ul.vue","/aa.jsp"));
list.add(new DataBean("0","顶级父级3","12","@/ul.vue","/aa.jsp"));
list.add(new DataBean("0","顶级父级4","13","@/ul.vue","/aa.jsp"));
list.add(new DataBean("0","顶级父级5","14","@/ul.vue","/aa.jsp"));
list.add(new DataBean("10","子集","21","@/ul.vue","/aa.jsp"));
list.add(new DataBean("10","子集","22","@/ul.vue","/aa.jsp"));
list.add(new DataBean("10","子集","23","@/ul.vue","/aa.jsp"));
list.add(new DataBean("12","子集","31","@/ul.vue","/aa.jsp"));
list.add(new DataBean("12","子集","32","@/ul.vue","/aa.jsp"));
list.add(new DataBean("14","子集","33","@/ul.vue","/aa.jsp"));
list.add(new DataBean("21","孙子集","55","@/ul.vue","/aa.jsp"));
list.add(new DataBean("21","孙子集","56","@/ul.vue","/aa.jsp"));
list.add(new DataBean("21","孙子集","57","@/ul.vue","/aa.jsp"));
list.add(new DataBean("22","孙子集","58","@/ul.vue","/aa.jsp"));
list.add(new DataBean("22","孙子集","59","@/ul.vue","/aa.jsp"));
list.add(new DataBean("23","孙子集","59","@/ul.vue","/aa.jsp"));
return list;
}
}
数据库存储的格式是子集父级都存在一张表里,没条数据都有一个父级id和子集id。子集id对应它孩子的父级id,当然顶级的父级id为0;下面我们将它通过递归的方式改成Tree型结构的数据
注意:代码里说的两种解决方案,其实都不是最方便的,最方便的是,我们在sql上处理,将我们的用到的需要分组的那个字段用to_char(),转换成字符串,这样不管我们定义String或者Object,都不会产生bug,也不用计较使用方式一或者方式二了。
所说的bug:我们分组使用的字段是数字,虽然我们认为它是字符串但是java在查出来时转换为Bigdecimal 对象了,我们分组之后,如果牵扯到比较方法就会报错。int类型比较或者字符串类型比较都会报错 ;这时候我们只需要在sql上把这个字段to_char()就行了
package java将线性结构的菜单转换为vuetree型结构菜单;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 本类作用:前端想做动态路由,数据从后端获取,
* 后端的menu 数据全在一张表里,根节点的父节点为0,它的子节点对应孩子的父节点,孩子的子节点对应孙子的父节点,以此类推的一种数据格式。
* 后端获取这样线性结构的数据,需要将这些数据处理成Tree型结构的数据返回给前端,前端直接用就可以了。
* 本类牵扯到java8的分组,和递归的方式,实现了树形结构。有了这种思想,可以仿照做各种tree型结构的数据出来
* @author T011921
*
*/
public class SysRouterUtil {
/**
* 在第二步分组之后获取父节点集合时有个bug
* glist.get("0")根本取不出来数据,原因是请求参数中的map的值定义的是Object,我们定义父子级节点一般都会用数字。从数据库查出来之后这个数字类型被转换为Object对象,debug是BigDecemal,所以map.get("0")是取不出来的。
* 解决方案1.mybatis 取数据时,定义为List<Map<String,String>>类型的,这个方法的请求参数和分组的返回值也改成Map<Object, List<Map<String, String>>> 类型,这样就可以直接取了glist.get("0");
*
* 解决方案2.依旧使用 List<Map<String,Object>>的方式查询。在分过组之后 使用forEach语法迭代。
*
* @param list //数据库查出来的menu数据
* @return
* @throws Exception
*/
public static List<Map<String,Object>> getRouterMenu(List<Map<String,Object>> list) throws Exception{
List<Map<String,Object>> result = new ArrayList<>();
try {
if(list.size()>0) {
//1.分组:使用java8的方式,父id(PRN_PRVG_ID)的字段进行分组
Map<Object, List<Map<String, Object>>> glist = list.stream().collect(Collectors.groupingBy(e ->e.get("PRN_PRVG_ID")));
//2.取出顶层的根节点的所有数据集合,父id的根节点都为0的。这里有个bug。
//方案1: 我这里不会报错,因为我的数据是从java构造出来的,节点是字符串数字,如果从数据库查询的话,会给你转成BigDecmal对象,你要么将数据value定义为String。要么用方案2 将k转换为字符串在去判断为0。使用当前的集合
List<Map<String, Object>> parentlist = glist.get("0");//所有父级的数据
for(Map<String,Object> plist : parentlist) {
//3. 遍历父节点的所有数据,使用LinkedHashMap,使最终的结果结构层次更加分明
Map<String,Object> parent = new LinkedHashMap<>();
parent.put("path", plist.get("URL"));
parent.put("name", "");
parent.put("meta",setMeta(plist.get("PRVG_NAME").toString(),true));
parent.put("hideChildrenInMenu", true);
parent.put("component", plist.get("COMPONENT"));
parent.put("children", getChildren(glist,plist.get("PRVG_ID")));//这里去迭代当前所有子节点,将最终结果存入父节点的children 属性里
result.add(parent);
}
//方案2:
// glist.forEach((k,mylist) ->{
// //在这里将k转换为字符串,然后和0做匹配就行了
// if("0".equals(k.toString())) {
// for(Map<String,Object> plist : mylist) {
// //3. 遍历父节点的所有数据,使用LinkedHashMap,使最终的结果结构层次更加分明
// Map<String,Object> parent = new LinkedHashMap<>();
// parent.put("path", plist.get("URL"));
// parent.put("name", "");
// parent.put("meta",setMeta(plist.get("PRVG_NAME").toString(),true));
// parent.put("hideChildrenInMenu", true);
// parent.put("component", plist.get("COMPONENT"));
// parent.put("children", getChildren(glist,plist.get("PRVG_ID")));//这里去迭代当前所有子节点,将最终结果存入父节点的children 属性里
// result.add(parent);
// }
// }
//
// });
//
}
} catch (Exception e) {
throw new Exception("数据解析异常");
}
return result;
}
/**
* 递归迭代是否有子节点
* 递归说明,第一次父节点调用,去根据父传来的它所对应的子节点id,去判断分组里有没有包含这个key,包含了说明它存在孩子,不包含,返回一个空数组;
* 如果包含,就根据当前传来的父级id,取出对应的所有子集合
* 遍历这个子集合;设置当前子的router信息,当设置为chlidren的时候迭代该函数,传入当前子数剧的子节点,接着迭代它的下面有没有孩子,依次类推,当最后一个孩子被迭代出来的时候,
* 查询出它已经没有孩子了返回一个空数组给最后一个孩子的children,然后在接着循环最后一个孩子的兄弟级,当最后一个孩子的兄弟级也迭代完之后,依次类推,开始一级一级的往上级闭合。
* 最终所有的子节点数据返回给最大的父节点的children,存入到最终的list中,开始迭代第二个父级...最终形成一个tree型结构的Router
* @param glist //分组之后的源菜单数据
* @param plist // 当前循环的父节点
*/
private static List<Map<String,Object>> getChildren(Map<Object, List<Map<String, Object>>> glist, Object prvg_id) {
List<Map<String,Object>> children = new ArrayList<>();
try {
// 1.通过传入的prvg_id 判断组里是否存在子节点,
if(glist.containsKey(prvg_id)) {
//存在子节点,循环取出子节点的prvgid,
List<Map<String, Object>> childList = glist.get(prvg_id);//所有子级的数据
for(Map child : childList) {
//3.通过顶层父级的prvg_id,递归迭代当前父级的所有子集,子孙级
Map<String,Object> childMap = new LinkedHashMap<>();
childMap.put("path", child.get("URL"));
childMap.put("name", "");
childMap.put("meta",setMeta(child.get("PRVG_NAME").toString(),true));
childMap.put("hideChildrenInMenu", true);
childMap.put("component", child.get("COMPONENT"));
childMap.put("children", getChildren(glist,child.get("PRVG_ID")));
children.add(childMap);
}
}
}catch (Exception e) {
e.printStackTrace();
}
return children;
}
private static Object setMeta(String string, boolean b) {
Map<String,Object> meta = new HashMap<String,Object>();
meta.put("title", string);
meta.put("keepAlive", b);
return null;
}
//测试
public static void main(String[] args) {
//简单构造下数据,可以从数据库中取出
List<DataBean> list =Data.getData();
List<Map<String,Object>> listmap= new ArrayList<>();
// 将bean转换为map
for(DataBean bean : list) {
Map<String,Object> map = new HashMap<>();
map.put("PRN_PRVG_ID", bean.getPRN_PRVG_ID());
map.put("PRVG_ID", bean.getPRVG_ID());
map.put("COMPONENT", bean.getCOMPONENT());
map.put("PRVG_NAME", bean.getPRVG_NAME());
map.put("URL", bean.getURL());
listmap.add(map);
System.out.println("源数据--" + map);
}
//存放Vue Router的集合
List<Map<String,Object>> result = new ArrayList<>();
try {
result = SysRouterUtil.getRouterMenu(listmap);
for(Map r : result) {
System.out.println("----" + r);
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
结果传给前端就行了:
{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}, {path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}, {path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}]}, {path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}, {path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}]}, {path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}]}]}
----{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}
----{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}, {path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}]}
{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}
{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[{path=/aa.jsp, name=, meta=null, hideChildrenInMenu=true, component=@/ul.vue, children=[]}]}