vue 权限管理


核心想法:

1.登陆后获得用户角色,通过角色获得用户的权限,注入权限对应的路由。
2.刷新页面,从localStorage用角色(更好的方式是通过token)再次获得所属权限,再次注入路由。在管理界面左端循环权限对应的路由菜单

3.localStorage存用户的信息(token),权限路由不会存。

所有的路由分为2种:
  • 公共路由:所有用户可以查看。
  • 权限路由:当前用户权限所属的路由。
实现控制的方式分两种:
  • 通过 vue-router addRoutes 方法注入路由实现控制
  • 通过 vue-router beforeEach 钩子限制路由跳转
一般来讲,需要采用第一种方式,addRoutes注入的方式,第二种每次判断,开销比较大,而且一次性拿不到角色对应的所有路由列表。这个列表需要再登陆后立即渲染在左边。

最终的效果:

 

 



权限的控制分为好几种:
  • 一级菜单的权限控制,
  • 二级菜单的权限控制,
  • 按钮级别的权限控制。
前后端约定控制方式有2种:
  • 后端返回路由控制列表
  • 前后端约定路由控制列表

以下代码展示一级菜单的权限控制,并且路由不是后端返回,而是通过前后端约定的方式,确定角色能访问的路由。

routers/index.js

 

import Vue from 'vue'
import Router from 'vue-router'
import loginRoute from './modules/login.js';
import homeRoute from './modules/home.js'
import productionRoute from './modules/production.js';
import store from '../stores/index.js';


Vue.use(Router)

//不需要权限的路由
export const constantRoutes = [loginRoute];
//需要权限的路由
//这里只是导出。实际上添加到router里在在store的permission里
export const asyncRoutes = [
    homeRoute,
    productionRoute,
];
console.log(asyncRoutes);
const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
        ...constantRoutes,
    ]
})
router.beforeEach((to,from,next)=>{
    let user = store.state.user.user;
    let permissionRoutes= store.state.permission.permissionRoutes;
    console.log(store.state);
    console.log(user);
    if(!user && to.path!=='/'){
        next({
            path:'/'
        });
    }else{
        //刷新页面的情况
        if(!permissionRoutes && user){
            store.dispatch('generateRoutes',{
                role:user.role
            })

            console.log(router);
            console.log(to);
            //这里写next没用
            next({...to})
        }else{
            next();
        }


        // next({...to, replace: true })
    }
});
export default  router;

  其中,productionRoute:

import Layout from '../../layouts/Index.vue'
const productionAdd = () => import('../../pages/production/Add.vue')
const productionList = () => import('../../pages/production/List.vue')
const productionTemplateComplete  = () => import('../../pages/production/TemplateComplete.vue')
let routes = {
    path: '/production',
    component: Layout,
    meta: {
        title: '产品管理',
        roles: [0, 1]
    },
    children: [
        {
            path: 'add',
            component: productionAdd,
            beforeEnter (to, from, next) {
                console.log('router beforeEnter');
                next();
            },
            meta: {
                title: '产品新增'
            }
        },
        {
            path: 'list',
            component: productionList,
            meta: {
                title: '产品列表'
            }
        },
        {
            path: 'templateComplete',
            component: productionTemplateComplete,
            meta: {
                title: '产品新增完善'
            }
        }
    ]
}

export default routes

  

stores/index.js
import Vue from 'vue'
import Vuex from 'vuex'

import user from './modules/user.js'
import permission from './modules/permission.js'


Vue.use(Vuex)

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

  



stores/modules/user.js
const SET_USER = 'SET_USER' //登录成功
const CLEAR_USER = 'CLEAR_USER' //退出登录
import $ajax from '../../axios/index.js';
import store from '../index.js';
export default {
    state: {
        user:JSON.parse(localStorage.getItem('user')) || null
    },
    mutations: {
        [SET_USER](state, user) {
            state.user = user;
            localStorage.setItem('user', JSON.stringify(user))
        },

        [CLEAR_USER](state) {
            state.user = null;
            localStorage.removeItem('user');
        }
    },
    actions: {
        login({commit}, param) {
            console.log('action');
            console.log(param);
            return new Promise((resolve, reject) => {
                //一期不做后台登陆,前端判断
                // $ajax({
                //     url:api.login,
                //     data:{
                //         ...param
                //     }
                // }).then(res=>{
                //     let user = res.data.data;
                //     commit(SET_USER, user);
                //     resolve();
                // }).catch(res=>{
                //     reject();
                // })
                //前端验证用户名,密码
                window.setTimeout(()=>{
                    if(param.username === 'huainanju' && param.password === 'huainanju123'){
                        let user = {
                            username:'管理员',
                            role:0
                        }
                        commit(SET_USER, user);
                        store.dispatch('generateRoutes',{
                            role:user.role
                        })
                        resolve();
                    }else{
                        reject();
                    }
                },1000)
            });
        },
        logout({commit}) {
            commit(CLEAR_USER)
        }
    }
}

  



stores/modules/permisson.js
import {asyncRoutes} from '../../routers/index';
import router from '../../routers/index';
/**
 * 递归过滤异步路由表,返回符合用户角色权限的路由表
 * @param role
 */
function filterAsyncRoutes(role) {
    const res = asyncRoutes.filter(route => {
        return route.meta.roles.includes(role)
    });
    return res
}

const permisssion = {
    state:{
        permissionRoutes:null
    },
    getter:{

    },
    mutations:{
        SET_ROUTERS: (state, routes) => {
            //添加路由
            console.log('添加路由');
            router.addRoutes(routes);
            state.permissionRoutes = routes // 权限路由
        },
    },
    actions:{
        generateRoutes({ commit }, data) {
            let {role} = data;
            let routes = filterAsyncRoutes(role);
            console.log(routes);
            commit('SET_ROUTERS', routes)
        }
    }
}

export default permisssion;

  

layouts/Left.vue

<template>
    <el-aside style="min-height:100%;min-width:250px;" width="250px">
        <el-menu :default-active="defaultActive"
                 background-color="#324157"
                 text-color="#bfcbd9"
                 active-text-color="#409EFF"
                 style="position:fixed;top:0;left:0;min-height: 100%;width:250px;z-index:100"
                 router>
            <template v-for="(item,index) in permissionRoutes">
                <!--一级菜单-->
                <el-menu-item :index="item.path" v-if="item.children.length === 1 ">
                    <i class="el-icon-menu"></i>
<!--                    <div>我是</div>-->
                    <span>{{item.meta.title}}</span>
                </el-menu-item>
                <!--多级菜单-->
                <el-submenu :index="index+''" v-else>
                    <template slot="title">
                        <i class="el-icon-menu"></i>
                        <span>{{item.meta.title}}</span>
                    </template>
                    <template v-for="child in item.children">
                        <el-menu-item :index="item.path+'/'+child.path">{{child.meta.title}}</el-menu-item>
                    </template>
                </el-submenu>
            </template>
        </el-menu>
    </el-aside>
</template>
<script>
    import {mapState} from 'vuex'

    export default {
        data() {
            return {
            }
        },
        computed: {
            ...mapState({
                'permissionRoutes':state=>state.permission.permissionRoutes
            }),
            defaultActive: function () {
                if (this.$route.path.indexOf('/classInfo') >= 0) {
                    return 'classList'
                }
                return this.$route.path.replace('/org/', '');
            }
        },
        created() {
            window.setTimeout(()=>{
                console.log(this.permissionRoutes);
            },1000)
        }
    }
</script>

  










posted @ 2019-08-06 14:38  小虫1  阅读(6545)  评论(0编辑  收藏  举报