• vue+elementui搭建后台管理界面(7 vuex和mockjs的使用)


    将权限管理应用到系统,首先做好登录, 点击登录按钮后,触发以下动作

    • vuex 中的 login 动作,设置 cookie
    • vuex 中的 getuserinfo , 获取权限、用户名、头像等
      由于目前未使用连接后端服务器,所以使用 mockjs 拦截请求并返回。
      github中查看

    1 全局请求拦截

    使用axios 封装好请求和响应

    src/utils/request.js

    import axios from 'axios'
    
    const clearRequest = {
      source: {
        token: null,
        cancel: null
      }
    }
    const cancelToken = axios.CancelToken
    const source = cancelToken.source()
    
    // 创建 axios 实例
    const service = axios.create({
      cancelToken: source.token,
      timeout: 6000,  // 请求超时时间
    })
    
    // request 拦截器
    service.interceptors.request.use(
      config => {
        config.cancelToken = clearRequest.source.token
        return config
      },
      error => {
        return Promise.reject(error)
      }
    )
    
    // response 拦截器
    service.interceptors.response.use(
      resp => resp,
      error => {
        return Promise.reject(error)
      }
    )
    
    export { clearRequest }
    export default service
    

    2 封装一些常用的函数

    src/utils/index.js

    export function param2Obj(url){
      const search = url.split('?')[1]
      if(!search){
          return {}
      }
      return JSON.parse(
          '{"' +
          decodeURIComponent(search)
              .replace(/"/g, '\"')
              .replace(/&/g, '","')
              .replace(/=/g, '":"') + 
          '"}'
      )
    }
    

    src/utils/auth.js

    import Cookies from 'js-cookie'
    
    const tokenKey = 'X-Token'
    
    export function getToken(){
      return Cookies.get(tokenKey)
    }
    
    export function setToken(token){
      return Cookies.set(tokenKey, token)
    }
    
    export function removeToken(){
      return Cookies.remove(tokenKey)
    }
    

    3 编写登录api

    src/api/login.js 文件

    import request from '@/utils/request'
    
    export function loginByUsernameApi(username, password){
      return request({
        url: '/api/auth/api-token-auth',
        method: 'post',
        data: {
          username,
          password
        }
      })
    }
    
    export function getUserInfoApi(token){
      return request({
        url: '/api/userinfo',
        method: 'get',
        params: {token}
      })
    }
    
    export function logoutApi(){
      return request({
        url: '/api/auth/logout',
        method: 'post'
      })
    }
    

    4 mock 拦截

    src/mock/index.js

    import Mock from 'mockjs'
    import login from './login'
    
    // 登录相关
    Mock.mock(//api/auth/api-token-auth/, 'post', login.loginByUsername)
    Mock.mock(//api/auth/logout/, 'post', login.logout)
    Mock.mock(//api/userinfo/, 'get', login.getUserInfo)
    
    export default Mock
    

    src/mock/login.js

    import { param2Obj } from '@/utils'
    
    const usermap = {
      admin: {
        token: 'admin',
        introduction: '我是超级管理员',
        name: 'Super Admin',
        pass: 'e10adc3949ba59abbe56e057f20f883e',
        roles: ['admin']
      },
      developer: {
        token: 'developer',
        introduction: '我是开发',
        name: '工程师小王',
        pass: 'e10adc3949ba59abbe56e057f20f883e',
        roles: ['/system', '/system/permit', '/system/permit/account']
      }
    }
    
    export default {
      loginByUsername: config => {
        const { username, password } = JSON.parse(config.body)
        console.log('loginByUsername username, password: ', username, password)
        if(username === 'admin'){
          if(usermap[username].pass === password){
          return usermap['admin']
          }else{
            return []
          }
        }
        return usermap[username]
      },
      getUserInfo: config => {
        console.log('getUserInfo config: ', config)
        const { token } = param2Obj(config.url)
        let tok = false
        for(let key in usermap){
          if(token.indexOf(usermap[key].token) !== -1){
            tok = usermap[key]
            break;
          }
        }
        return tok
    
      },
      logout: () => 'success'
    }
    

    5 vuex 的使用

    src/store/index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    import user from './modules/user'
    import permission from './modules/permissions'
    import getters from './getters'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      modules: {
        permission,
        user,
      },
      getters
    })
    export default store
    

    src/store/modules/user.js

    import { loginByUsernameApi, logoutApi, getUserInfoApi } from '@/api/login'
    import { getToken, setToken, removeToken } from '@/utils/auth'
    
    const user = {
      state: {
        token: getToken(),
        name: '',
        avatar: '',
        roles: []
      },
      mutations: {
        SET_TOKEN: (state, token) => {
          state.token = token
        },
        SET_NAME: ( state, name) => {
          state.name = name
        },
        SET_AVATAR: (state, avatar) => {
            state.avatar = avatar
        },
        SET_ROLES: (state, roles) => {
          state.roles = roles
        }
      },
      actions: {
        // 登入
        LoginByUsername({commit}, userinfo){
          const username = userinfo.username.trim()
          return new Promise((resolve, reject) => {
            loginByUsernameApi(username, userinfo.password)
            .then(resp => {
              const data = resp.data
              setToken(data.token)
              console.log('in LoginByUsername, setToken: ', data.token)
              commit('SET_TOKEN', data.token)
              resolve()
            })
            .catch(err => {
              reject(err)
            })
          })
        },
        // 获取用户权限等
        GetUserInfo({commit, state}) {
          console.log('in GetUserInfo')
          return new Promise((resolve, reject) => {
            getUserInfoApi(state.token)
            .then(resp => {
              if(!resp.data){
                reject('error')
              }
              const data = resp.data
              if(data.roles && data.roles.length){
                commit('SET_ROLES', data.roles)
              }else {
                reject('getUserInfoApi: roles must be a non-null array!')
              }
              if(data.name){
                commit('SET_NAME', data.name)
              }
              if(data.avatar){
                commit('SET_AVATAR', data.avatar)
              }
              resolve(resp)
            })
            .catch(err => {
              reject(err)
            })
          })
        },
        // 登出
        LogOut({commit, state}){
          return new Promise((resolve, reject) => {
            logoutApi(state.token)
            .then(() => {
              commit('SET_TOKEN', '')
              commit('SET_ROLES', [])
              removeToken()
              resolve()
            })
            .catch(err => {
              reject(err)
            })
          })
        },
        // 前端登出
        FedLogOut({commit}){
          return new Promise(resolve => {
            commit('SET_TOKEN', '')
            removeToken()
            resolve()
          })
        }
      }
    }
    
    export default user
    

    src/store/modules/permissions.js

    import { asyncRouterMap, constantRouterMap } from '@/router'
    
    /**
     * 通过 meta.role 判断是否与当前用户权限匹配
     */
    function hasRoles (roles, route){
      if(route.meta && route.meta.roles){
        return roles.some(role => route.meta.roles.includes(role))
      }else{
        return true
      }
    }
    
    /**
     * 递归过滤异步路由表,返回符合用户角色权限的路由表
     */
    function filterAsyncRouter(asyncRouterMap, roles){
      const accessedRouters = asyncRouterMap.filter(route => {
        // 404
        if(route.path === '*'){
          return true 
        }else if(hasRoles(roles, route)){
          if(route.children && route.children.length){
            route.children = filterAsyncRouter(route.children, roles)
          }
          return true
        }
        return false
      })
      return accessedRouters
    }
    
    // 在有权限的路由表里,查找是否有到目标path的路由
    // 为了保持路由唯一性,拼接父子路由
    function hasDestRoute (froute, permitRouterMap, to) {
      let r = froute === '/' ? '' : froute
      return permitRouterMap.some(route => {
        let path = r + '/' + route.path
        if (to.path.indexOf(path) !== -1) {
          return true;
        }
        if (route.children && route.children.length) { //如果有孩子就遍历孩子
          return hasDestRoute(path, route.children, to)
        }
      })
    }
    
    const permission = {
      state: {
        routers: constantRouterMap,
        addRouters: [],
        sidebar_routers: {},
      },
      mutations: {
        SET_ROUTERS: (state, routers) => {
          state.addRouters = routers,
          state.routers = constantRouterMap.concat(routers)
        },
        SET_NOW_ROUTERS: (state, to) => {
          console.log('in SET_NOW_ROUTERS')
          // 由于首页重定向到 /dashboard,并且不参与权限控制,特殊处理
          if(to.path === '/dashboard'){
            let dashboard = state.routers.filter(v => v.path === '/' )
            state.sidebar_routers = dashboard[0]
          }else{
              // 递归访问 accessedRouters,找到包含to 的那个路由对象,设置给 sidebar_routers
              state.addRouters.forEach(e => {
              if (e.children && e.children.length) {
                  if ( hasDestRoute(e.path, e.children, to)){
                      if(state.sidebar_routers.path){
                          // 存在 sidebar_routers 且与目标路由不同
                          if(state.sidebar_routers.path !== e.path){
                              state.sidebar_routers = e;
                          }
                      }else{
                          state.sidebar_routers = e;
                      }
                  }
              }
              })
          }
        }
      },
      actions: {
        GenerateRoutes({commit}, data) {
          console.log('in GenerateRoutes')
          return new Promise(resolve => {
            const {roles} = data
            let accessedRouters
            if(roles.includes('admin')){
              accessedRouters = asyncRouterMap
            }else{
              accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
            }
            commit('SET_ROUTERS', accessedRouters)
            resolve()
          })
        },
        GenSidebarRoutes({commit}, data) {
          console.log('in GenSidebarRoutes')
          return new Promise(resolve => {
            commit('SET_NOW_ROUTERS', data)
            resolve()
          })
        }
      }
    }
    
    export default permission
    

    src/store/getters.js

    const getters = {
      token: state => state.user.token,
      roles: state => state.user.roles,
      avatar: state => state.user.avatar,
      name: state => state.user.name,
      addRouters: state => state.permission.addRouters,
      permission_routers: state => state.permission.routers,
      sidebar_routers: state => state.permission.sidebar_routers,
    }
    
    export default getters
    

    6 修改 login 页面

    src/views/TheLogin.vue

    handleSubmit(event) {
      this.$refs.ruleForm2.validate((valid) => {
        if (valid) {
          this.logining = true;
          // 触发 vuex 中 LoginByUsername 事件
          const username = this.ruleForm2.username
          let password = md5(this.ruleForm2.password)
          if(this.hidePassword !== '' && this.ruleForm2.password === '********'){
            password = this.hidePassword
          }
          this.$store.dispatch('LoginByUsername', {'username': username, 'password': password})
            .then(() => {
              this.logining = false
              if(this.rememberme){
                this.setCookie(this.ruleForm2.username, password, 7)
              }else{
                this.clearCookie()
              }
              // 重定向到首页
              this.$router.push({ path: this.redirect || '/' })
            })
            .catch(err => {
              this.logining = false
              this.$alert(err, {
                type: 'warning',
                confirmButtonText: 'ok'
              })
            })
        } else {
          console.log('error submit!');
          return false;
        }
      })
    }
    

    7 修改main 主入口

    src/main.js

    /** ...*/
    import store from './store'
    import '@/login'
    import '@/mock'
    
    Vue.use(ElementUI)
    Vue.config.productionTip = false
    
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      store,
      render: h => h(App)
    })
    

    8 关键的权限控制

    src/login.js

    import router from './router'
    import NProgress from 'nprogress'
    import {clearRequest} from '@/utils/request'
    import axios from 'axios'
    import store from './store'
    
    function hasPermission(roles, permissionRoles){
      if(roles.indexOf('admin') >= 0){
        return true  // admin 权限  直接通过
      }
      // 没有配置权限的菜单直接进入
      if(! permissionRoles){
        return true
      }
      return roles.some(role => permissionRoles.indexOf(role) >= 0)
    }
    
    const whiteList = ['/login', ]   // 不重定向白名单
    router.beforeEach((to, from, next) => {
      console.log('to.path: ' + to.path)
      console.log('store.getters.token: ' + store.getters.token)
      // 切换路由时清空上个路由未完成的所有请求
      const cancelToken = axios.CancelToken
      clearRequest.source.cancel && clearRequest.source.cancel()
      clearRequest.source = cancelToken.source()
    
      if(whiteList.indexOf(to.path) !== -1){
        next()
      }else{
        if(store.getters.token){
          if(to.path === '/login'){
            next({path: '/'})
            NProgress.done()
          }else{
            // 拉取用户信息
            if(store.getters.roles.length === 0){
              store.dispatch('GetUserInfo')
              .then(resp => {
                const roles = resp.data.roles
                console.log('roles: ', roles)
                // 根据权限生成可访问的路由表
                store.dispatch('GenerateRoutes', {roles})
                .then(() => {
                  // 动态添加路由表
                  router.addRoutes(store.getters.addRouters)
                  next({...to, replace: true})  // 确保 addRouters 已完成
                })
              })
              .catch(err => {
                store.dispatch('FedLogOut')
                .then(() => {
                  console.log('认证失败,请重新登陆')
                  next({path: '/login'})
                })
              })
            }else{
              console.log('call GenSidebarRoutes')
              store.dispatch('GenSidebarRoutes', to)
              .then(() => {
                if(hasPermission(store.getters.roles, to.meta.role)){
                  next()
                }else{
                  next({path: '/', query: {noGoBack: true}})
                }
              })
            }
          }
        }else{
          // 重定向到 /login
          next({path: '/login', query: {redirect: to.fullpath}})
          NProgress.done()
        }
      }
    })
    
    router.afterEach(() => {
      NProgress.done()
    })
    

    目录结构

    │  App.vue
    │  login.js
    │  main.js
    ├─api
    │      login.js
    │      system.js
    ├─assets
    │      logo.png
    ├─components
    │      HelloWorld.vue
    │      Navbar.vue
    │      NavbarItem.vue
    │      Sidebar.vue
    │      SidebarItem.vue
    ├─containers
    │      Container.vue
    ├─mock
    │      article.js
    │      index.js
    │      login.js
    │      system.js
    ├─router
    │  │  index.js
    │  │
    │  └─modules
    │          system.js
    ├─store
    │  │  getters.js
    │  │  index.js
    │  │
    │  └─modules
    │          permissions.js
    │          user.js
    ├─styles
    │      animate.scss
    │      browser-prefix.scss
    │      index.scss
    ├─utils
    │      auth.js
    │      index.js
    │      request.js
    └─views
        │  404.vue
        │  Home.vue
        │  TheLogin.vue
        ├─article
        │      index.vue
        ├─dashboard
        │      index.vue
        ├─SidebarMenu
        │      index.vue
        └─system
            └─permit
                │  account.vue
                │  accountgroup.vue
                │  authorize.vue
                │  index.vue
                │  role.vue
                └─components
                        DialogRoleMenu.vue
    

    admin 用户权限:

    developer 权限:

  • 相关阅读:
    BorderContainer 背景透明一不小心就解决了!
    C#编程应用线程与委托
    第二次SQL RAP要点
    最近的学习
    BW中传输的问题
    7月总结Dotnetnuke的研究总结
    EP学习要点记忆
    盲人摸象SAP PS模块的介绍与讨论
    如何跨Client删除数据
    如何修改Portal与BW系统的链接域名
  • 原文地址:https://www.cnblogs.com/wbjxxzx/p/10091258.html
Copyright © 2020-2023  润新知