9

Vue admin template 动态路由配置

 3 years ago
source link: https://www.fdevops.com/2021/05/29/vue-30806
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Vue admin template 动态路由配置

兰玉磊 • 2021年5月29日 21:53 • Web • 阅读 441

本文将详细介绍,如何使用 Vue admin template 配置动态路由。

登陆配置部分不过多介绍,可参考 https://www.fdevops.com/2020/09/09/vue-django-5171

配置路由参数

路由参数存入 Vuex 内。

src/store/modules/user.js

import { login, getInfo, getMenuTree } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'
const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: '',
    menus: '' // 新增
const state = getDefaultState()
const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  SET_TOKEN: (state, token) => {
    state.token = token
  SET_NAME: (state, name) => {
    state.name = name
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  SET_MENUS: (state, menus) => { // 新增
    state.menus = menus
const actions = {
  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo().then(response => {
        const { data } = response
        if (!data) {
          return reject('Verification failed, please Login again.')
        const { username } = data
        // 新增 模拟路由表
        const menus = [
            path: '/system',
            component: 'Layout',
            meta: {
              title: '系统管理',
              icon: 'form'
            children: [
                path: '/user',
                component: 'system/user/index',
                meta: {
                  title: '用户管理',
                  icon: 'form'
        menus.push({ path: '*', redirect: '/404', hidden: true }) // 新增
        commit('SET_NAME', username)
        commit('SET_MENUS', menus) // 新增,将路由表保存到 Vuex 中
        resolve(data)
      }).catch(error => {
        reject(error)
export default {
  namespaced: true,
  state,
  mutations,
  actions

getter 内添加数据。

src/store/getters.js

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  name: state => state.user.name,
  menus: state => state.user.menus // 新增
export default getters

需注意,既然使用了动态路由,则需要将 router.js 中的非必要的路由删除。

组件导入配置

通过配置全局路由变量,在数据渲染的时候,使用全局变量进行渲染路由参数。

配置生产和开发两种模式的组件引入方式。进行组件的引入。

创建两个引入组件文件,分别是 _import_development.js 和 _import_production.js。

src/router/_import_development.js

module.exports = file => require('@/views/' + file + '.vue').default

src/router/_import_production.js

module.exports = file => () => import('@/views/' + file + '.vue')

配置权限文件。

src/permission.js

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import Layout from '@/layout'
const _import = require('./router/_import_' + process.env.NODE_ENV) // 获取组件的方法
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()
  // set page title
  document.title = getPageTitle(to.meta.title)
  // 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 {
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        next()
      } else {
          // get user info
          await store.dispatch('user/getInfo') // 请求获取用户信息
          await store.dispatch('user/getRouter') // 获取路由表
          if (store.getters.menus.length < 1) {
            global.RouterList = []
            next()
          const menus = filterAsyncRouter(store.getters.menus) // 1.过滤路由
          router.addRoutes(menus) // 2.动态添加路由
          global.RouterList = menus // 3.将路由数据传递给全局变量,做侧边栏菜单渲染工作
          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()
router.afterEach(() => {
  // finish progress bar
  NProgress.done()
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
  const accessedRouters = asyncRouterMap.filter(route => {
    if (route.component) {
        if (route.component === 'Layout') {
          route.component = Layout
        } else {
          route.component = _import(route.component) // 导入组件
      } catch (e) {
        Message.error(e.message)
    route.meta = {
      title: route.title,
      icon: route.icon
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children)
    return true
  return accessedRouters

最后修改菜单渲染的文件。

src/layout/components/Sidebar/index.vue

routes() {
  // return this.$router.options.routes
  return this.$router.options.routes.concat(global.RouterList) // 把路由concat进去
Vue admin template 动态路由配置

到这里我们通过模拟路由数据的方式,来实现了我们的动态路由,但是呢,我们经常会遗忘掉一个问题,就是创建我们路由对应的组件文件,因为如果你不去创建路由对应的组件文件的话,则路由是错误的。甚至无法登陆成功。因此,一定!一定!一定要创建路由对应的组件文件。

后端数据渲染路由表

上面的配置都完善了后,然后我们配置的模拟路由测试也没问题了。我们就基于上面的配置,改造成获取后端路由数据,来动态渲染路由表的效果了。

需注意,如果要配置后端数据渲染路由表的话,那么则需要将上面模拟路由数据删除。

在 src/store/modules/user.js 内添加 actions 如下:

// get router
getRouter({ commit, state }) {
  return new Promise((resolve, reject) => {
    getMenuTree().then(response => {
      const list = response.data
      if (!list) {
        reject('未查询到菜单数据,请确认。')
      list.push({ path: '*', redirect: '/404', hidden: true })
      commit('SET_MENUS', list)
      resolve(list)

后端接口代码如下:

class MenuTreeViews(APIView):
    def get(self, request, *args, **kwargs):
        res = {
            "code": 200,
            "msg": "获取菜单树成功",
            "data": []
            # 递归获取菜单树
            def recursion_menu(menu, roles):
                m = format_menu(menu)
                r_string = []
                for r in roles:
                    r_string.append(str(r))
                menuRaw = Menus.objects.raw(f"""select user_menus.* from user_menus left join user_role_related_menus on user_menus.id = user_role_related_menus.menu where user_role_related_menus.role in ({','.join(r_string)}) and user_menus.parent = {menu["id"]};""")
                m_list = []
                for mr in menuRaw:
                    for mc in menuRaw.columns:
                        d[mc] = getattr(mr, mc)
                    m_list.append(d)
                tmp_m_list = []
                for i in m_list:
                    tmp_m_list.append(format_menu(i))
                    recursion_menu(i, roles)
                m["children"] = tmp_m_list
                return m
            # 查询用户角色
            userinfo = UserInfo.objects.get(username=request.user.username)
            roles = [r[0] for r in list(UserRoles.objects.filter(user_id=userinfo.id).values_list("role_id"))]
            # 查询当前角色的权限
            permissions = [p[0] for p in list(RoleMenus.objects.filter(role__in=roles).values_list("menu"))]
            menu_list = [p[0] for p in list(Menus.objects.filter(id__in=permissions, menu_type=1).values_list("parent").distinct())]
            top_menu_list = list(Menus.objects.filter(id__in=menu_list).values())
            for i in top_menu_list:
                res["data"].append(recursion_menu(i, roles))
        except Exception as e:
            res["code"] = 400
            res["msg"] = f"获取菜单树失败, {e}"
        return JsonResponse(res)

本文为原创文章,未经授权禁止转载本站文章。
原文出处:兰玉磊的个人博客
原文链接:https://www.fdevops.com/2021/05/29/vue-30806
版权:本文采用「署名-非商业性使用-相同方式共享 4.0 国际」知识共享许可协议进行许可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK