vue2 项目实例 动态路由菜单(四)

动态路由涉及到 router、 store、 beforeEach、 permission权限

 

1、触发登录事件

    Login.vue

监听路由变化,下次登录重定向上次页面

  watch: {
    $route: {
      handler: function(route) {
        this.redirect = route.query && route.query.redirect
      },
      immediate: true
    }
  },

 

登录按钮事件

handleLogin() {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          this.loading = true
          this.$store.dispatch('user/login', this.loginForm).then(() => {
              this.$router.push({ path: this.redirect || '/' })
              this.loading = false
            })
            .catch(() => {
              this.loading = false
            })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }

 

2、vuex

 store/module/user.js

import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'

const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: ''
  }
}

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
  }
}

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password })
        .then((response) => {
          console.log('login', response)
          const { data } = response
          commit('SET_TOKEN', data.token)
          setToken(data.token)
          resolve()
        })
        .catch((error) => {
          reject(error)
        })
    })
  },

  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token)
        .then((response) => {
          const { data } = response

          if (!data) {
            return reject('Verification failed, please Login again.')
          }

          const { name, avatar } = data

          commit('SET_NAME', name)
          commit('SET_AVATAR', avatar)
          resolve(data)
        })
        .catch((error) => {
          reject(error)
        })
    })
  },

  // user logout
  logout({ commit, state }) {
    return new Promise((resolve, reject) => {
      logout(state.token)
        .then(() => {
          removeToken() // must remove  token  first
          resetRouter()
          commit('RESET_STATE')
          resolve()
        })
        .catch((error) => {
          reject(error)
        })
    })
  },

  // remove token
  resetToken({ commit }) {
    return new Promise((resolve) => {
      removeToken() // must remove  token  first
      commit('RESET_STATE')
      resolve()
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

 

login 成功 resolve 执行路由跳转

 

3、路由守卫 permission

   

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'

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 {
        try {
          // get user info
          await store.dispatch('user/getInfo')
//获取路由列表,会触发 src/store/modules/permission.js store.dispatch(
'GenerateRoutes').then(accessRoutes => { // 根据roles权限生成可访问的路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表 next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 }) 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() })

 


src/store/modules/permission.js

4、请求路由接口

path,name,component,children,meta 路由格式

import {constantRoutes} from '@/router'
import {getRouters} from '@/api/menu' 
import Layout from '@/layout/index'

const permission = {
  state: { 
    routes: [], // 路由数据 
  },
  mutations: { 
    SET_ROUTES: (state, routes) => {
      state.routes = constantRoutes.concat(routes)
    },
    SET_CURRENT_ROUTES: (state, routes) => {
      state.currentRoutes = routes
    }
  },
  actions: {
    // 生成路由
    GenerateRoutes({ commit }) {
      return new Promise(resolve => { // 向后端请求路由数据
        getRouters().then(res => {
          const accessedRoutes = filterAsyncRouter(res.data)
          accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
          
          commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes) // 找到代码 3标题处 }) }) } } }
// 遍历后台传来的路由字符串,转换为组件对象 function filterAsyncRouter(asyncRouterMap) {
const res = [] for(let i= 0; i < asyncRouterMap.length; i++ ){
// 组装路由模式 ⚠️
const temp = {...asyncRouteMap[i]}
if(asyncRouteMap[i].children){
temp.children = filterAsyncRouter(asyncRouteMap[i].children)
}else{
temp.component=loadView(temp.component)
}
res.push(temp)
}
return res } export const loadView = (view) => { // 路由懒加载 return (resolve) => require([`@/views/${view}`], resolve) } export default permission

 

5、渲染左侧菜单

<template>
  <div>
    <el-menu
      class="el-menu-vertical-demo"
      :collapse="isCollapse"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
    >
      <h4>通用后台管理系统</h4>
      // 在子组件配置路由
      <AsideItems :menu="menu" />
    </el-menu>
  </div>
</template>

<script>
import AsideItems from "./components/AsideItems";
export default {
  name: "CommonAside",
  components: {
    AsideItems,
  },
  data() {
    return {
      menu: [],
    };
  },
  mounted() {
    const mRoutes = this.$store.state.menu.menu;
    this.menu = mRoutes;
  },
};
</script>

 

 

<template>
  <div>
    <!-- 一定要加template遍历  递归调用 不然无效 -->
    <template v-for="(item, i) in menu">
      <!-- 判断没有子路由的 -->
      <el-menu-item
        v-if="!item.children"
        :key="i"
        :index="item.name"
        @click="$router.push({ name: item.name })"
      >
        <i :class="'el-icon-' + item.meta.icon"></i>
        <span slot="title">{{ item.meta.title }}</span>
      </el-menu-item>
      <!-- 有子路由的导航 -->
      <el-submenu v-else :key="i" :index="item.name">
        <template slot="title">
          <i :class="'el-icon-' + item.meta.icon"></i>
          <span slot="title">{{ item.meta.title }}</span>
        </template>
        <!-- 子路由 -->
        <aside-item :menu="item.children"></aside-item>
      </el-submenu>
    </template>
  </div>
</template>

<script>
export default {
  name: "AsideItem",
  props: {
    menu: { type: Array },
  },
};
</script>

 

 

如果按照 vue-element-admin 是通过路由过滤实现的,具体可以看下代码

 

posted on 2025-09-23 11:51  Mc525  阅读(32)  评论(0)    收藏  举报

导航