动态路由前后端vue+springboot实现

动态路由前后端vue+springboot实现,第1张

动态路由

用户登录成功后根据用户名获取菜单数据,后台封装前端需要的路由格式

1.后端封装路由组件 1.1 RouterVo 实体类
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

/**
 * 路由需要的数据格式
 */
@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class RouterVo {
    
    private String path; 
    
    private Boolean hidden;
    
    private String name;
    
    private String component;
    
    private boolean alwaysShow;

    private Meta meta;

    @Data
    @AllArgsConstructor
    public class Meta {
        private String title;
        private String icon;
        private Object[] roles;
    }

    private List<RouterVo> children = new ArrayList<>();

}
1.2 封装成树结构
   /**
     * 根据用户名查询菜单树 --》这里是下面前端调用的getMenus service层实现
     *
     * @param username
     */
    @Override
    public List<RouterVo> selectMenusByUsername(String username) {
        Users one = userDao.selectOne(new QueryWrapper<Users>().eq("username", username));
        //根据userId查询菜单
        List<Permissions> menuList = permissionsDao.selectPermitsByUser(one.getId());
        List<Permissions> collect = menuList.stream().filter(item -> item != null && !item.getMenuType().equals(2)).collect(Collectors.toList());
        //组装成路由数据
        return MenuUtils.makeRouter(collect, 0);
    }
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

//构造树形工具
public class MenuUtils {
    /**
     * 生成路由数据格式
     */
    public static List<RouterVo> makeRouter(List<Permissions> menuList, Integer pid) {
        //接受生产的路由数据
        List<RouterVo> list = new ArrayList<>();
        //组装数据
        Optional.ofNullable(menuList).orElse(new ArrayList<>())
                .stream()
                .filter(item -> item != null && item.getParentPermissId() == pid)
                .forEach(item -> {
                    RouterVo router = new RouterVo();
                    router.setName(item.getName());  //name前端标识
                    router.setPath(item.getUrl()); //url
                    router.setHidden("1".equals(item.getHidden())); // hidden:0否;1是
                    //判断是否是一级菜单
                    if (item.getParentPermissId() == 0L) {
                        router.setComponent("Layout");
                        router.setAlwaysShow(true);
                    } else {
                        router.setComponent(item.getUrl());
                        router.setAlwaysShow(false);
                    }
                    //设置meta
                    router.setMeta(router.new Meta(
                            item.getPermissionName(),
                            item.getMenuIcon(),
                            item.getPerms().split(",")
                    ));
                    //设置children
                    List<RouterVo> children = makeRouter(menuList, item.getId());
                    router.setChildren(children);
                    list.add(router);
                });
        return list;
    }
}
2.前端获取到后台返回的路由数据 2.1 router.beforeEach拦截路由进行封装
router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      // 从store里面获取用户信息, VUEX每次刷新页面都会丢失数据,所以permits放在了sessionStorage中
      // const hasGetUserInfo = store.getters.name
      // 从store里面获取权限信息
      const hasPermits = store.getters.permits && store.getters.permits.length > 0
      if (hasPermits) {
        // 如果权限存在,直接放行
        next()
      } else {
        // 如果不存在,从服务器获取数据
        try {
          // 从服务器获取用户信息
          const { permits } = await store.dispatch('user/getInfo')

          //-----------这里是动态路由:从服务器获取菜单、路由信息------------------
          const accessRoutes = await store.dispatch('menu/generateRoutes', permits)
          let obj = { path: '*', redirect: '/404', hidden: true }
          //把返回的数据添加到路由
          accessRoutes.push(obj)
          router.addRoutes(accessRoutes);
          next({ ...to, replace: true })
      	  //-----------------------------------------------------------------
          // next()
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/
    // 判断路由是否在白名单中
    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})
2.2新建menu.js中递归封装component
import { constantRoutes } from '@/router'
import { getMenus } from '@/api/user'
import Layout from '@/layout'

/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param permits
 */
export function filterAsyncRoutes(routes, permits) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(permits, tmp)) {
      //动态找到页面路径
      const component = tmp.component
      if (route.component) {
        //判断是否是一级菜单
        if (component == 'Layout') {
          tmp.component = Layout
        } else {
          tmp.component = (resolve) => require([`@/views${component}`], resolve)
        }
      }
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, permits)
      }
      res.push(tmp)
    }
  })
  return res
}

/**
 * Use meta.role to determine if the current user has permission
 * @param permits
 * @param route
 */
function hasPermission(permits, route) {
  if (route.meta && route.meta.permits) {
    return permits.some(permit => route.meta.permits.includes(permit))
  } else {
    return true
  }
}

const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  //获取路由数据
  generateRoutes({ commit }, permits) {
    return new Promise((resolve, reject) => {
      getMenus().then(res => {
        let accessedRoutes
        if (res.code == 200) {
          accessedRoutes = filterAsyncRoutes(res.data, permits)
        }
        //把返回的数据存到vuex里面
        commit('SET_ROUTES', accessedRoutes)
        resolve(accessedRoutes)

      }).catch(error => {
        reject(error)
      })
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
sideBar中修改引用的路由,从vuex中获取

<template>
  <div :class="{'has-logo':showLogo}">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="true"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        mode="vertical"
      >
        <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
      el-menu>
    el-scrollbar>
  div>
template>

<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'

export default {
  components: { SidebarItem, Logo },
  computed: {
    ...mapGetters([
      'sidebar',
      'permission_routes'
    ]),
    routes() {
      return this.$router.options.routes
    },
    activeMenu() {
      const route = this.$route
      const { meta, path } = route
      // if set path, the sidebar will highlight the path you set
      if (meta.activeMenu) {
        return meta.activeMenu
      }
      return path
    },
    showLogo() {
      return this.$store.state.settings.sidebarLogo
    },
    variables() {
      return variables
    },
    isCollapse() {
      return !this.sidebar.opened
    }
  }
}
script>

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/726339.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存