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

动态路由

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

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>
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
宿舍管理系统是一个比较综合的系统,需要涉及到前端、后端、数据库等多个方面。下面是一个基于VueSpringboot的宿舍管理系统的搭建步骤: 1. 环境搭建 首先需要安装好Node.js和Java开发环境,以及MySQL数据库。 2. 前端搭建 使用Vue-cli创建一个Vue项目,安装Vue-router和Axios等必要的依赖。在src目录下创建components、views、router、store等文件夹,分别存放组件、页面、路由和状态管理相关文件。 3. 后端搭建 使用Spring Initializr创建一个Springboot项目,选择必要的依赖,如Spring Web、Spring Data JPA、MySQL Driver等。在src目录下创建entity、repository、service、controller等文件夹,分别存放实体类、数据访问层、服务层和控制层相关文件。 4. 数据库设计 根据宿舍管理系统的需求,设计数据库表结构,如宿舍楼信息表、宿舍信息表、学生信息表等。 5. 前后端交互 使用Axios在前端与后端进行数据交互,使用Vue-router实现页面跳转。 6. 前端界面设计 根据需求设计前端页面,如登录页面、宿舍楼信息管理页面、宿舍信息管理页面、学生信息管理页面等。 7. 后端接口设计 设计后端接口,如登录接口、宿舍楼信息管理接口、宿舍信息管理接口、学生信息管理接口等。使用@RestController注解实现控制层,使用@Service注解实现服务层,使用@Repository注解实现数据访问层。 8. 系统测试 完成前后端搭建后,进行系统测试,测试系统的功能是否正常。 以上是一个基于VueSpringboot的宿舍管理系统的搭建步骤,具体实现还需根据需求进行具体开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值