java后端如何创建vue所需要的动态路由格式

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=[]}]}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值