后台项目总绕不开权限验证,也总绕不开动态路由

动态路由:

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import tab from './tab'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    tab,
  },
})

store/tab.js

import Cookie from 'js-cookie'
export default {
  state: {
    // 全部菜单(左侧导航)
    menu: [],
  },
  mutations: {
    // 保存菜单项--保存在vuex和cookie中
    setMenu(state, val) {
      state.menu = val
      // cookie中只能存字符串, 所以需要转换
      Cookie.set('menu', JSON.stringify(val))
      console.log(val, 'vuex')
    },
    // 清除菜单项
    clearMenu(state) {
      state.menu = []
      Cookie.remove('menu')
    },
    // 动态添加菜单---既然需要添加路由,当然需要把路由对象(没错,就是全局的$router)传进来
    addMenu(state, router) {
      // 如果menu为空
      if (!Cookie.get('menu')) {
        // 路由为空,直接返回
        return
      }
      // 从cookie中获取menu---记得反序列化
      let menu = JSON.parse(Cookie.get('menu'))
      // 将从cookie里取到的menu存到vuex(避免刷新时vuex里数据丢失)
      state.menu = menu
      // 存放路由的数组
      let currentMenu = [
        {
          path: '/',
          component: () => import(`@/views/Main`),
          children: [],
        },
      ]
      // 循环menu,对其中的数据进行过滤后存入currentMenu的children中
      menu.forEach((item) => {
        // 如果当前项有子路由
        if (item.children) {
          item.children = item.children.map((item) => {
            // 懒加载import的方式务必按照如下格式进行, item.url的值是这样的  'Home/Home' ,这是一个字符串, 我们在本地进行路径的拼接.
            item.component = () => import(`@/views/${item.url}`)
            return item
          })
          // 将当前项压入数组  (运用了展开运算符)
          currentMenu[0].children.push(...item.children)
        } else {
          item.component = () => import(`@/views/${item.url}`)
          currentMenu[0].children.push(item)
        }
      })
      console.log(currentMenu, 'cur')
      // 添加路由
      router.addRoutes(currentMenu)
      // 如果你的菜单列表是基于router.options.routes来渲染的(比如vue-element-admin就是这么做的) , 那你就需要加上这样一句---来重设routes
      // router.options.routes = [...router.options.routes,...currentMenu]
    },
  },
  actions: {},
}

views/Login/index.js

login() {
      this.$http.post('api/permission/getMenu', this.form).then((res) => {
        res = res.data
        console.log(res)
        if (res.code === 20000) {
          // 先执行一次清空菜单, 防止用户二次登陆
          this.$store.commit('clearMenu')
          // 将后端返回的菜单项存进vuex
          this.$store.commit('setMenu', res.data.menu)
          // 登录成功后存储token
          this.$store.commit('setToken', res.data.token)
          // 在登录时触发路由添加事件
          this.$store.commit('addMenu', this.$router)
          // 跳转到首页
          this.$router.push({ name: 'home' })
        } else {
          // 使用elementUI的全局提示对象
          this.$message.warning(res.data.message)
        }
      })
    },

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.prototype.$http = http
new Vue({
  router,
  store,
  render: (h) => h(App),
  created() {
    // 在刷新时(即vue初始化时), 触发添加路由事件
    store.commit('addMenu', router)
  },
}).$mount('#app')

登录Mock接口定义:

import Mock from 'mockjs'
export default {
  getMenu: (config) => {
    const { username, password } = JSON.parse(config.body)
    console.log(JSON.parse(config.body))
    // 先判断用户是否存在
    if (username === 'admin' || username === 'wp') {
      // 判断账号和密码是否对应
      if (username === 'admin' && password === '123456') {
        return {
          code: 20000,
          data: {
            menu: [
              {
                path: '/',
                name: 'home',
                label: '首页',
                icon: 's-home',
                url: 'Home/Home',
              },
              {
                path: '/video',
                name: 'video',
                label: '视频管理页',
                icon: 'video-play',
                url: 'VideoManage/VideoManage',
              },
              {
                path: '/user',
                name: 'user',
                label: '用户管理页',
                icon: 'user',
                url: 'UserManage/UserManage',
              },
              {
                label: '其他',
                icon: 'location',
                children: [
                  {
                    path: '/page1',
                    name: 'page1',
                    label: '页面1',
                    icon: 'setting',
                    url: 'Other/PageOne',
                  },
                  {
                    path: '/page2',
                    name: 'page2',
                    label: '页面2',
                    icon: 'setting',
                    url: 'Other/PageTwo',
                  },
                ],
              },
            ],
            // 在返回menu的同时返回一个token(利用mock生成随机id来模拟--guid:生成随机字符串)
            token: Mock.Random.guid(),
            message: '获取成功',
          },
        }
      } else if (username === 'wp' && password === '123456') {
        return {
          code: 20000,
          data: {
            menu: [
              {
                path: '/',
                name: 'home',
                label: '首页',
                icon: 's-home',
                url: 'Home/Home',
              },
              {
                path: '/video',
                name: 'video',
                label: '视频管理页',
                icon: 'video-play',
                url: 'VideoManage/VideoManage',
              },
            ],
            // 在返回menu的同时返回一个token(利用mock生成随机id来模拟--guid:生成随机字符串)
            token: Mock.Random.guid(),
            message: '获取成功',
          },
        }
      } else {
        return {
          code: -999,
          data: {
            message: '密码错误',
          },
        }
      }
    } else {
      return {
        code: -999,
        data: {
          message: '用户不存在',
        },
      }
    }
  },
}