笔记 vue 路由权限组装

125 阅读4分钟

1.router.js 文件组装路由并组装菜单

2.main.js 做全局路由守卫 判断 是否有访问权限

3.点击组装菜单导航 router-link :to="item.path" 跳转 到 router-viewe 坑位

router.js 文件

import Vue from 'vue'

/**=====================================================================================
 * 公共模板组件
 *======================================================================================*/
const Layout = () => import('./views/layout.vue')

/**=====================================================================================
 * 登录
 *======================================================================================*/
const Login = () => import(/* webpackChunckName: "group-login" */ './views/login.vue')

/**=====================================================================================
 * 首页
 *======================================================================================*/
const Index = () => import(/* webpackChunckName: "group-index" */ './views/index.vue')

/**=====================================================================================
 * 任务中心
 *======================================================================================*/
const taskList = () => import(/* webpackChunckName: "group-taskList" */ './views/task/taskList.vue')
const taskDetail = () => import(/* webpackChunckName: "group-taskDetail" */ './views/task/taskDetail.vue')
const taskDetailDetail = () => import(/* webpackChunckName: "group-taskDetailDetail" */ './views/task/taskDetailDetail.vue')
/**=====================================================================================
 * 结算中心
 *======================================================================================*/
 const settlementList = () => import(/* webpackChunckName: "group-settlementList" */ './views/settlement/settlementList.vue')
 const settlementDetail = () => import(/* webpackChunckName: "group-settlementDetail" */ './views/settlement/settlementDetail.vue')

 /**=====================================================================================
 * 结算批次
 *======================================================================================*/
  const settlementBatchList = () => import(/* webpackChunckName: "group-settlementBatchList" */ './views/settlementBatch/settlementBatchList.vue')
  const settlementBatchDetail = () => import(/* webpackChunckName: "group-settlementBatchDetail" */ './views/settlementBatch/settlementBatchDetail.vue')

  /**=====================================================================================
 * 结算统计
 *======================================================================================*/
   const settlementStatisticsList = () => import(/* webpackChunckName: "group-settlementStatisticsList" */ './views/settlementStatistics/settlementStatisticsList.vue')
   const settlementStatisticsDetail = () => import(/* webpackChunckName: "group-settlementStatisticsDetail" */ './views/settlementStatistics/settlementStatisticsDetail.vue')

 /**=====================================================================================
 * 签约管理
 *======================================================================================*/
  const contractList = () => import(/* webpackChunckName: "group-list" */ './views/contract/contractList.vue')
  const contractAddContract = () => import(/* webpackChunckName: "group-addContract" */ './views/contract/addContract.vue')

/**=====================================================================================
 * 合同模板管理
 *======================================================================================*/
const contractTemplateList = () => import(/* webpackChunckName: "group-templateList" */ './views/contractTemplate/templateList.vue')
const contractTemplateDetail = () => import(/* webpackChunckName: "group-templateDetail" */ './views/contractTemplate/templateDetail.vue')

export default {
    routes: [
        /**=====================================================================================
         * 登录
         *======================================================================================*/
        {
            path: '/login',
            name: '登录',
            component: Login,
            meta: {
                noAuth: true
            }
        },
        /**=====================================================================================
         * 首页
         *======================================================================================*/
        {
            path: '/',
            name: '首页',
            component: Index,
            meta: {
                alias: 'index'
            }
        }
    ],
    /**
     * [getRoutesList description] 根据权限组装路由
     * @param  {[type]} ids [description] isLogin接口返回的权限id列表
     * @return {[type]}     [description]
     */
    getRoutesList: function (ids) {
        let navs = [],
            list = [],
            sublist = [];

        /**=====================================================================================
         * 任务中心 User
         * 2001 -> 查看
         * 2002 -> 编辑
         *======================================================================================*/
        list = [];
        if (ids.indexOf(4101) > -1) {
            list.push({
                path: '/task/taskList',
                name: '任务中心',
                component: taskList,
                meta: {
                    parent: 'user'
                }
            });
            list.push({
                path: '/task/taskDetail',
                name: '任务详情',
                component: taskDetail,
                meta: {
                    parent: 'user'
                }
            });
            list.push({
                path: '/task/taskDetailDetail',
                name: '结算批次详情',
                component: taskDetailDetail,
                meta: {
                    parent: 'user'
                }
            });
        }
        if (list.length > 0) {
            navs.push({
                path: list[0].path,
                name: '任务中心',
                component: Layout,
                meta: {
                    alias: 'user'
                },
                children: list
            });
        }

        /**=====================================================================================
         * 结算中心 User 
         * 2001 -> 查看
         * 2002 -> 编辑
         *======================================================================================*/
         list = [];
         if (ids.indexOf(9201) > -1) {
             list.push({
                 path: '/settlement/settlementList',
                 name: '结算中心',
                 component: settlementList,
                 meta: {
                    alias: 'user'
                 }
             });
             list.push({
                 path: '/settlement/settlementDetail',
                 name: '结算详情',
                 component: settlementDetail,
                 meta: {
                    alias: 'user'
                 }
             });
         }
         if (list.length > 0) {
             navs.push({
                 path: list[0].path,
                 name: '结算中心',
                 component: Layout,
                 meta: {
                     alias: 'user'
                 },
                 children: list
             });
         }

        /**=====================================================================================
         * 结算批次 User 
         * 2001 -> 查看
         * 2002 -> 编辑
         *======================================================================================*/
        list = [];
        if (ids.indexOf(9201) > -1) {
            list.push({
                path: '/settlementBatch/settlementBatchList',
                name: '结算批次',
                component: settlementBatchList,
                meta: {
                    alias: 'user'
                }
            });
            list.push({
                path: '/settlementBatch/settlementBatchDetail',
                name: '结算详情',
                component: settlementBatchDetail,
                meta: {
                    alias: 'user'
                }
            });
        }
        if (list.length > 0) {
            navs.push({
                path: list[0].path,
                name: '结算批次',
                component: Layout,
                meta: {
                    alias: 'user'
                },
                children: list
            });
        }

        
        /**=====================================================================================
         * 结算统计 User 
         * 2001 -> 查看
         * 2002 -> 编辑
         *======================================================================================*/
         list = [];
         if (ids.indexOf(2001) > -1) {
             list.push({
                 path: '/settlementStatistics/settlementStatisticsList',
                 name: '结算批次',
                 component: settlementStatisticsList,
                 meta: {
                     alias: 'user'
                 }
             });
             list.push({
                 path: '/settlementStatistics/settlementStatisticsDetail',
                 name: '统计详情',
                 component: settlementStatisticsDetail,
                 meta: {
                     alias: 'user'
                 }
             });
         }
         if (list.length > 0) {
             navs.push({
                 path: list[0].path,
                 name: '结算统计',
                 component: Layout,
                 meta: {
                     alias: 'user'
                 },
                 children: list
             });
         }

        /**=====================================================================================
         * 签约管理 User
         * 2001 -> 查看
         * 2002 -> 编辑
         *======================================================================================*/
         list = [];
         if (ids.indexOf(4201) > -1) {
             list.push({
                 path: '/Contract/contractList',
                 name: '签约管理',
                 component: contractList,
                 meta: {
                    alias: 'user'
                 }
             });
             list.push({
                 path: '/Contract/addContract',
                 name: '新增签约企业',
                 component: contractAddContract,
                 meta: {
                    alias: 'user'
                 }
             });
         }
         if (list.length > 0) {
             navs.push({
                 path: list[0].path,
                 name: '签约管理',
                 component: Layout,
                 meta: {
                     alias: 'user'
                 },
                 children: list
             });
         }

        /**=====================================================================================
         * 合同模板管理 User
         * 2001 -> 查看
         * 2002 -> 编辑
         *======================================================================================*/
         list = [];
         if (ids.indexOf(9101) > -1) {
             list.push({
                 path: '/contractTemplate/templateList',
                 name: '合同模板管理',
                 component: contractTemplateList,
                 meta: {
                    alias: 'user'
                 }
             });
             list.push({
                 path: '/contractTemplate/templateDetail',
                 name: '新增服务协议',
                 component: contractTemplateDetail,
                 meta: {
                    alias: 'user'
                 }
             });
         }
         if (list.length > 0) {
             navs.push({
                 path: list[0].path,
                 name: '合同模板管理',
                 component: Layout,
                 meta: {
                     alias: 'user'
                 },
                 children: list
             });
         }

        this.setRoutesStorage(navs);

        return navs;
    },

    /**
     * [setRoutesStorage description] 将完整的route结构存本地,供左侧导航渲染使用
     * @param {[type]} list [description]
     */
    setRoutesStorage: function (list) {
        let lists = this.routes.concat(list);
        localStorage.setItem('routes', JSON.stringify(lists));
    },

    /**
     * [hasPermission description] 判断是否具有对应权限
     * @param  {[type]}  toPath [description] 目标路由
     * @return {Boolean}        [description]
     */
    hasPermission: function (toPath) {
        let lists = JSON.parse(localStorage.getItem('routes')),
            paths = [];

        lists.forEach(navItem => {
            if (paths.indexOf(navItem.path) == -1) {
                paths.push(navItem.path);
            }

            if (navItem.children && navItem.children.length > 0) {
                navItem.children.forEach(listItem => {
                    if (paths.indexOf(listItem.path) == -1) {
                        paths.push(listItem.path);
                    }

                    if (listItem.children && listItem.children.length > 0) {
                        listItem.children.forEach(subListItem => {
                            if (paths.indexOf(subListItem.path) == -1) {
                                paths.push(subListItem.path);
                            }

                            if (subListItem.children && subListItem.children.length > 0) {
                                subListItem.children.forEach(dynamicItem => {
                                    if (paths.indexOf(dynamicItem.path) == -1) {
                                        dynamicItem.path = dynamicItem.path.replace(/\/:\w+/g, '');
                                        paths.push(subListItem.path + '/' + dynamicItem.path);
                                    }

                                    if (dynamicItem.children && dynamicItem.children.length > 0) {
                                        dynamicItem.children.forEach(dynamicGrandparents => {
                                            if (paths.indexOf(dynamicGrandparents.path) == -1) {
                                                dynamicItem.path = dynamicItem.path.replace(/\/:\w+/g, '');
                                                dynamicGrandparents.path = dynamicGrandparents.path.replace(/\/:\w+/g, '');
                                                paths.push(subListItem.path + '/' + dynamicItem.path + '/' + dynamicGrandparents.path);
                                            }
                                        })
                                    }
                                })
                            }
                        });
                    }
                });
            }
        });


        if (paths.join(',').indexOf(toPath) > -1) {
            return true;
        } else {
            return false;
        }
    }
}

main.js 文件

// router
import routers from './router'
import Router from 'vue-router'

Vue.use(Router)


const router = new Router({
    routes: routers.routes
})

// ====================================== axios
axios.interceptors.request.use((config) => {
    // let token = localStorage.getItem('token') || '';

    // token = token.replaceAll('"', '');

    // if(token) {
    //     config.headers['token'] = token;
    // }
    return config
}, function (error) {
    console.log('request -> error', error)
    return Promise.reject(error)
});
axios.interceptors.response.use(function (response) {
    let {code, msg} = response.data;

    if (code == 10001) { // 参数验证失败
        Message.error(msg);
    } else if (code == 30001) { // 业务错误报告
        Message.error(msg);
    } else if (code == 401) { // 登陆过期或未登陆
        Message.error(msg);
        // =重定向到登录页面
        // router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } })
        router.replace({path: '/login'});
    } else if (code == 403) { // 权限不足
        Message.error(msg);
    }

    return response
}, function (error) {
    // Do something with response error
    Message.error(error);
    return Promise.reject(error)
});

// ======================================== beforeEach
router.beforeEach((to, from, next) => {
    if (!to.matched.some(record => record.meta.noAuth)) {
        NProgress.start();

        // 验证是否已登录
        commonApi.COMMON.api('/perms', {}).then(res => {
            NProgress.done();

            let {code, data} = res;

            if (code == 0) {
                // 权限id数组
                let ids = data || [];

                // 判断接口返回的权限 和 本地存储的权限 是否一致
                if (localStorage.getItem('ids') == ids) { // 权限无变化
                    // 判断当前路径是否在routes里面
                    if (routers.hasPermission(to.path.replace(/\/\d+/g, ''))) {
                        next();
                    } else {
                        Message.error('您没有权限访问这个页面,请联系管理员');
                        next({path: '/'})
                    }
                } else { // 权限变化
                    localStorage.setItem('ids', ids);
                    // 更新route
                    router.addRoutes(routers.getRoutesList(ids));
                    // 判断当前路径是否在routes里面
                    // 不能写成next(), 不然addRoutes不起作用
                    // to.path 会造成刷新页面的时候 query 所带参数丢失
                    if (routers.hasPermission(to.path.replace(/\/\d+/g, ''))) {
                        next({path: to.fullPath});
                    } else {
                        Message.error('您没有权限访问这个页面,请联系管理员');
                        next({path: '/'})
                    }
                }
            }
        }).catch(error => {
            console.log(error);

            NProgress.done();
            Message.error('服务器异常');
            next({
                path: '/login'
            })
        });
    } else {
        next()
    }
});

new Vue({
    router,
    render: h => h(App),
    /**
     * [created description] vue实例被销毁的时候(如:页面刷新),清除routes、ids的数据,使isLogin的时候能更新route
     * @return {[type]} [description]
     */
    created: function () {
        localStorage.removeItem('ids');
        localStorage.removeItem('routes');
    }
}).$mount('#app')

Nav.vue 导航

<template>
    <aside class="app-aside">
        <div class="logo-container">
            <div class="logo">logo</div>
        </div>
        <nav role="main" class="main-nav">
            <ul class="nav-list">
                <li class="list-item" v-for="(item, index) in routes" v-if="item.meta.alias">
                    <router-link :to="item.path" class="main-link" :class="{ 'active' : mainIndex == index }">
                        <icon-font :type="'icon-' + item.meta.alias"/>
                        <span class="main-name">{{ item.name }}</span>
                    </router-link>
                </li>
            </ul>
        </nav>
    </aside>
</template>

<script>
import {Icon} from 'ant-design-vue';

const IconFont = Icon.createFromIconfontCN({
    scriptUrl: '//at.alicdn.com/t/font_2252332_t0hzfw2rgi8.js',
});

export default {
    components: {
        IconFont
    },
    data() {
        return {
            'route': {},  //当前路由
            'routes': [],  //路由列表
            'mainIndex': ''  //一级列表下标
        }
    },
    watch: {
        /**
         * [description] 监听路由变化
         * @param  {[type]} val [description] 当前路由
         * @return {[type]}     [description]
         */
        '$route': function (val) {
            this.route = this.$route;
            this.routes = localStorage.getItem('routes') ? JSON.parse(localStorage.getItem('routes')) : this.$router.options.routes;

            this.mainIndex = '';

            for (let i = 0; i < this.routes.length; i++) {
                let routeItem = this.routes[i];

                if (routeItem.meta.alias && (routeItem.meta.alias == this.route.meta.parent || routeItem.meta.alias == this.route.meta.alias)) {
                    this.mainIndex = i;
                    this.$emit('subNavShow', false);
                    break;
                }
            }
        }
    },
    methods: {}
}
</script>

app.vue 文件 router-view路由坑位

<template>
    <div id="app">
        <Header v-show="!hideNav" :pageTitle="pageTitle"></Header>
        <Nav v-show="!hideNav"></Nav>

        <div class="app-container">
            <keep-alive>
                <router-view v-if="$route.meta.keepAlive"/>
            </keep-alive>
            <router-view v-if="!$route.meta.keepAlive"/>
        </div>
    </div>
</template>

<script>
import Nav from '@/components/Nav.vue'
import Header from '@/components/Header.vue'

export default {
    components: {
        Nav,
        Header
    },
    data() {
        return {
            hideNav: true,
            pageTitle: ''
        }
    },
    watch: {
        '$route': function () {
            this.pageTitle = this.$route.name
            if (this.$route.fullPath == '/login' || this.$route.path == '/login') {
                this.hideNav = true;
            } else {
                this.hideNav = false;
            }
        }
    },
    methods: {},
}
</script>