基于Vue+element-ui实现后台管理系统 菜单权限控制+按钮权限控制

路由配置

import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

/* Layout */
import Layout from "@/layout";


 
 // 不需要后台返回的公共路由
export const constantRoutes = [
  {
    path: "/login",
    component: () => import("@/views/login/index"),
    hidden: true
  },

  {
    path: "/404",
    component: () => import("@/views/404"),
    hidden: true
  },

  {
    path: "/",
    component: () => import("@/layout"),
    redirect: "/dashboard",
    children: [
      {
        path: "dashboard",
        name: "Dashboard",
        component: () => import("@/views/dashboard/index"),
        meta: { title: "首页", icon: "dashboard" }
      }
    ]
  },
  {
    path: "/clue",
    component: () => import("@/layout"),
    redirect: "/clue/private",
    name: "Clue",
    hidden: true,
    meta: {
      title: "线索管理",
      icon: "example"
    },
    children: [
      {
        path: "private/add",
        name: "PrivateAdd",
        hidden: true,
        component: () => import("@/views/clue/private-sea/add"),
        meta: { title: "单条新增", icon: "table" }
      },
      {
        path: "turnCustomer",
        name: "turnCustomer",
        hidden: true,
        component: () => import("@/views/clue/private-sea/turn-customer"),
        meta: { title: "角色新增", icon: "table" }
      },
      {
        path: "clueDetails",
        name: "clueDetails",
        hidden: true,
        component: () => import("@/views/clue/private-sea/clue-details"),
        meta: {
          title: "查看线索",
          icon: "table"
        }
      },
      {
        path: "followClue",
        name: "followClue",
        hidden: true,
        component: () => import("@/views/clue/private-sea/follow-clue"),
        meta: { title: "跟进线索", icon: "table" }
      }
    ]
  },
  {
    path: "/productManagement",
    component: () => import("@/layout"),
    redirect: "/productManagement/list",
    name: "ProductManagement",
    hidden: true,
    meta: { title: "商品管理", icon: "table" },
    children: [
      {
        path: "add",
        name: "ProductManagementAdd",
        hidden: true,
        component: () => import("@/views/product/add"),
        meta: { title: "商品新增", icon: "table" }
      },
      {
        path: "edit",
        name: "ProductManagementEdit",
        hidden: true,
        component: () => import("@/views/product/edit"),
        meta: { title: "商品编辑", icon: "table" }
      }
    ]
  },
  {
    path: "/authority",
    component: () => import("@/layout"),
    redirect: "/authority/roleManage",
    name: "Authority",
    meta: { title: "权限管理", icon: "eye-open" },
    hidden: true,
    children: [
      {
        path: "roleManage/add",
        name: "RoleManageAdd",
        hidden: true,
        component: () => import("@/views/authority/role-manage/add-or-edit"),
        meta: { title: "角色新增", icon: "table" }
      },
      {
        path: "roleManage/edit",
        name: "RoleManageEdit",
        hidden: true,
        component: () => import("@/views/authority/role-manage/add-or-edit"),
        meta: { title: "角色编辑", icon: "table" }
      },
      {
        path: "user/add",
        name: "UserAdd",
        hidden: true,
        component: () => import("@/views/authority/user-manage/add-or-edit"),
        meta: { title: "用户新增", icon: "table" }
      },
      {
        path: "user/edit",
        name: "UserEdit",
        hidden: true,
        component: () => import("@/views/authority/user-manage/add-or-edit"),
        meta: { title: "用户编辑", icon: "table" }
      }
    ]
  }
  // {
  //   path: "/performance",
  //   component: () => import("@/layout"),
  //   redirect: "/performance/assess",
  //   name: "Performance",
  //   meta: { title: "绩效管理", icon: "link" },
  //   hidden: true,
  //   children: [
  //   ]
  // }
  // 404 page must be placed at the end !!!
  // { path: "*", redirect: "/404", hidden: true }
];


 
 // 需要后台返回的异步路由
export const asyncRoutes = [
  {
    path: "/productManagement",
    component: () => import("@/layout"),
    redirect: "/productManagement/list",
    name: "ProductManagement",
    meta: { title: "商品管理", icon: "link" },
    children: [
      {
        path: "list",
        name: "ProductList",
        component: () => import("@/views/product/index"),
        meta: { title: "商品列表", icon: "table", btnPermissions: [1, 2, 3] }
      },
      {
        path: "parts",
        name: "ProductParts",
        component: () => import("@/views/product/parts"),
        meta: { title: "配件管理", icon: "table" }
      }
    ]
  },
  {
    path: "/clue",
    component: () => import("@/layout"),
    redirect: "/clue/private",
    name: "Clue",
    meta: { title: "线索管理", icon: "example" },
    children: [
      {
        path: "private",
        name: "Private",
        component: () => import("@/views/clue/private-sea/index"),
        meta: {
          title: "私海管理",
          icon: "table",
          keepAlive: true, //该字段表示该页面需要缓存
          isBack: false //判断是否是返回
        }
      },

      {
        path: "public",
        name: "Public",
        component: () => import("@/views/clue/public-sea/index"),
        meta: { title: "公海管理", icon: "tree" }
      }
    ]
  },
  {
    path: "/authority",
    component: () => import("@/layout"),
    redirect: "/authority/roleManage",
    name: "Authority",
    meta: { title: "权限管理", icon: "eye-open" },
    children: [
      {
        path: "roleManage",
        name: "RoleManage",
        component: () => import("@/views/authority/role-manage/index"),
        meta: { title: "角色管理", icon: "table" }
      },

      {
        path: "user",
        name: "UserManage",
        component: () => import("@/views/authority/user-manage/index"),
        meta: { title: "用户管理", icon: "tree" }
      }
    ]
  },
  {
    path: "/performance",
    component: () => import("@/layout"),
    redirect: "/performance/assess",
    name: "Performance",
    meta: { title: "绩效管理", icon: "link" },
    children: [
      {
        path: "assess",
        name: "Assess",
        component: () => import("@/views/performance/assess"),
        meta: { title: "绩效考核", icon: "form" }
      },
      {
        path: "mine",
        name: "Mine",
        component: () => import("@/views/performance/mine"),
        meta: { title: "绩效查询", icon: "nested" }
      }
    ]
  },
  // 404 page must be placed at the end !!!
  { path: "*", redirect: "/404", hidden: true }
];


const createRouter = () =>
  new Router({
    // mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes
  });

const router = createRouter();

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}

export default router;

这里可能很多朋友会出现这样的问题 我在配置的时候同样踩了坑

vue 动态路由 addRoutes 刷新页面404(空白)问题

 1:静态路由中注释掉{ path: '*', redirect: '/404', hidden: true }(如果有)
 2:放在动态路由中添加

登录页面

 登录
 
 data() {
    return {
      loading:false,
      loginForm: {
        username: '',
        password: ''
      },
    }
  },
 methods: {
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true

          this.$store
            .dispatch('user/login', this.loginForm) //通过vuex 登录获取动态路由配置
            .then(() => {
              this.$router.push({ path: this.redirect || '/' })
              this.loading = false
              this.$store
                .dispatch('user/getSettings')
                .then(result => {})
                .catch(err => {})
            })
            .catch(() => {
              this.loading = false
            })
        } else {
          return false
        }
      })
    }
  }
}
login({ commit }, userInfo) {
    const { username, password } = userInfo;
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password })
        .then(response => {
          const { data } = response;
          commit("SET_TOKEN", data);  // 存储token
          setToken(data);
          resolve();
        })
        .catch(error => {
          reject(error);
        });
    });
  },
  // 获取动态路由菜单
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getUserInfo(state.token)
        .then(response => {
          const { data } = response;

          if (!data) {
            reject("Verification failed, please Login again.");
          }
          localStorage.setItem(
            "adminUserListBoList",
            JSON.stringify(data.adminUserListBoList)
          );
          const { sonMenu: roles, username, avatar } = data;
          // const { roles, username, avatar } = data

          // roles must be a non-empty array
          if (!roles || roles.length <= 0) {
            reject("getInfo: roles must be a non-null array!");
          }
          commit("SET_ROLES", roles);
          commit("SET_NAME", username);
          commit("SET_AVATAR", avatar);
          resolve(data);
        })
        .catch(error => {
          reject(error);
        });
    });
  },

permission.js 文件 store中的

import { asyncRoutes, constantRoutes } from "@/router";

console.log("async routes: --", asyncRoutes);
console.log("constantRoutes : --", constantRoutes);
// /**
//  *
//  * Use meta.role to determine if the current user has permission
//  * @param roles
//  * @param route
//  */
// function hasPermission(roles, route) {
//   if (route.meta && route.meta.roles) {
//     return roles.some(role => route.meta.roles.includes(role))
//   } else {
//     return true
//   }
// }
// export function mapAsyncRoutes(routes) {
//   return routes.map(route => {
//     const tmp = { ...route }
//     if (typeof tmp.component === 'string') {
//       const path = tmp.component
//       tmp.component = () => import(`@/${path}`)
//     }
//     if (tmp.children && tmp.children.length) {
//       tmp.children = mapAsyncRoutes(tmp.children)
//     }
//     return tmp
//   })
// }

// /**
//  * Filter asynchronous routing tables by recursion
//  * @param routes asyncRoutes
//  * @param roles
//  */
// export function filterAsyncRoutes(routes, roles) {
//
//
//   const res = []

//   routes.forEach(route => {
//     const tmp = { ...route }
//     if (hasPermission(roles, tmp)) {
//       if (tmp.children) {
//         tmp.children = filterAsyncRoutes(tmp.children, roles)
//       }
//       res.push(tmp)
//     }
//   })

//   return res
// }

/**
 *
 * @param  {Array} userRouter 后台返回的用户权限json
 * @param  {Array} allRouter  前端配置好的所有动态路由的集合
 * @return {Array} realRoutes 过滤后的路由
 */

export function recursionRouter(userRouter = [], allRouter = []) {
  var realRoutes = [];
  allRouter.forEach((v, i) => {
    userRouter.forEach((item, index) => {
      if (item.ename == v.name) {
        if (item.children && item.children.length > 0) {
          v.children = recursionRouter(item.children, v.children);
          
        }
        // v.meta={...v.meta,...{btnPermissions:[1,2,3]}}  // 这里可以通过后台返回数据做按钮控制权限 btnPermissions:[1,2,3] 为后台返回的数组
        realRoutes.push(v);
        
      }
    });
  });
  // console.log(realRoutes);
  return realRoutes;
}

const state = {
  routes: [],
  addRoutes: []
};

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes;
    state.routes = constantRoutes.concat(routes);

    console.log("all routes====>", state.routes);
  }
};

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes;
      // if (roles.includes('admin')) {
      //   accessedRoutes = asyncRoutes || []
      // } else {
      //   accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      // }
      // 拿到后台的路由表和前端本地存的路由表   映射出来有权限的路由

      accessedRoutes = recursionRouter(roles, asyncRoutes);
      console.log('哈哈'+JSON.stringify( roles)); 

      commit("SET_ROUTES", accessedRoutes);
      resolve(accessedRoutes);
    });
  }
};

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

同main.js级创建 permission.js 配置 路由并引入main.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, filterRouter } 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 {
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = store.getters.roles && store.getters.roles.length > 0

      if (hasRoles) {
        next()
      } else {
        try {
          // get user info
          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']

          const { sonMenu: roles } = await store.dispatch('user/getInfo')  // 通过vuex获取动态路由菜单
          // const { roles } = await store.dispatch('user/getInfo')
          // const { roles } = store.getters.roles
          //
          // generate accessible routes map based on roles
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles) // 通过vuex创建动态路由

          // dynamically add accessible routes
          router.addRoutes(accessRoutes)
          // hack method to ensure that addRoutes is complete
          // set the replace: true, so the navigation will not leave a history record
          // next({ path: '/' })
          next({ ...to, replace: true })
        } 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}`)
          // next(`/`)

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




main.js

import Vue from 'vue'

import 'normalize.css/normalize.css' // A modern alternative to CSS resets

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n

import '@/styles/index.scss' // global css

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

import '@/icons' // icon
import '@/permission' // permission control

/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online! ! !
 */
// import { mockXHR } from '../mock'
// if (process.env.NODE_ENV === 'production') {
//   mockXHR()
// }

// set ElementUI lang to EN
Vue.use(ElementUI, { locale })

import * as filters from './filters' // 引入全局filters
// 注册全局  utility filters
Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
})

Vue.config.productionTip = false

//后台返回菜单的同时一起返回按钮权限的数组 如:[1,2,3,4] //对应增、删、查、改
//前端处理数据把按钮权限放入路由的 meta 中。
//创建自定指令v-permission,进行判断对比。
// 使用 删除 
Vue.directive('permission', {
  inserted(el, binding, vnode) {
    const {value} = binding
    const roles = vnode.context.$route.meta.btnPermissions || [] //获取mate中的权限
    console.log(value,roles);
    if (value && value instanceof Array && value.length > 0) {
      const permissionRoles = value
      const hasPermission = roles.some(role => {
        return permissionRoles.includes(role)
      })
      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`需要的权限, v-permission="[1,2,3,4]"`)
    }
  },

})

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

基于Vue+element-ui实现后台管理系统 菜单权限控制+按钮权限控制_第1张图片

…个别地方省略了 有不懂得 可以评论区留言

你可能感兴趣的:(vue,element,vue.js)