Basic ideas:
1. Create vueRouter and instantiate it with common routing
2. Create routing objects that need to be filtered according to permissions (add necessary permission judgment fields to routing objects)
3. The login is completed and the backend cooperates to return the current user's privilege set.
4. Screen out the privileged routing objects and use vueRouter's addRoutes method to generate complete routing
5. Processing refresh pages leads to re-instantiation of vueRouter and imperfection of routing objects (using router. beforeEast to navigate guards, using addRoutes() to improve routing objects)
6. Side navigation bar code
Related code
According to the order above
1,
Vue.use(Router) // Public routing export const publicRoutes = [ { path: '/', name: 'login', component: login, meta: { title: 'Sign in' } } ] // Routes that need to be filtered according to permissions export const asyncRoutes = [ ...Home, ...Seting, ...CRM, { path: '*', component: login, meta: { hidden: true }, redirect: '/' } ] const vr = new Router({ mode: 'history', routes: publicRoutes })
2
Take the setting module as an example. Role is the right field to judge yes. If a role has the corresponding value of this field, it will have the right of the current page.
import Layout from '@/views/layout/layout.vue' const account = r => require.ensure([], () => r(require('@/views/seting/account/account.vue')), 'seting'); const logs = r => require.ensure([], () => r(require('@/views/seting/logs/logs.vue')), 'seting'); const role = r => require.ensure([], () => r(require('@/views/seting/role/role.vue')), 'seting'); const Seting = [ { path: '/seting', component: Layout, redirect: '/seting/account', meta: { title: 'System setup', role: '123c6c6514d416472e640bc3f49297c550', icon: 'icon-xitong' }, children: [ { path: 'account', name: 'account', component: account, meta: { title: 'account management', role: '1325cdeb897cc7f8e951d647de9b1d8e11', } }, { path: 'logs', name: 'logs', component: logs, meta: { title: 'Log management', role: '14bfbb0337ad3e7e2c9fc101294c3fe645', } }, { path: 'role', name: 'role', component: role, meta: { title: 'Role management', role: '1559d1c05d15a0dce5549b8bf5a58c0cf9', } } ] } ] export default Seting
If there are some details pages that do not need to be displayed in the navigation list, you can also add fields and remove them when generating the navigation bar.
eg:
{ path: 'addJoiner', name: 'addJoiner', component: addJoiner, meta: { hidden: true, // Hidden field title: '***Details page', role: '14bfbb0337ad3e7e2c9fc101294c3fe645', } },
3
Log in to get permission sets and basic information
// Sign in login () { this.rememberPassword() this.$refs.form.validate((valid) => { if (valid) { this.loading = true let params = { login_name: this.form.user, password: this.form.password, code: this.form.yanzhenma, } this.$api.post('api/user/login', params).then(res => { if (res.err_code === 1) { // Store the user's basic information in vuex this.$store.dispatch('setBaseInfo', res.data).then(() => { // Get the privileged routing table and add it to the routing router.addRoutes(this.$store.getters.addRouters) this.$router.push({ name: 'home' }) }) } this.loading = false }) } })
Part of vuex code
If you don't understand it, point out:
vuex refresh has no solution
Seconds understand vuex
actions.js
// import api from '@/api' const actions = { setBaseInfo ({ commit }, data) { return new Promise(resolve => { commit('set_userInfo', data.userInfo) commit('set_token', data.token) commit('set_roles', data.menus) // Keep basic information locally to prevent loss after refresh sessionStorage.setItem('baseInfo', JSON.stringify(data)) resolve() }) } } export default actions
mutations.js
const setStorage = (key, value) => { if (typeof (value) === 'object') { value = JSON.stringify(value) } sessionStorage.setItem(key, value) } /* * Avoid vuex being reset after refresh and make a backup in session store */ const mutations = { set_userInfo (state, payload) { state.userInfo = payload setStorage('userInfo', payload) }, set_token (state, payload) { state.token = payload setStorage('token', payload) }, set_roles (state, payload) { state.roles = payload setStorage('roles', payload) }, set_breadcrumb (state, payload) { state.breadcrumb = payload setStorage('breadcrumb', payload)/* */ }, changeCollapsed (state, payload) { state.isCollapsed = payload } } export default mutations
getters.js
import createdRoutes from '@/utils/createdRoutes.js' import { asyncRoutes } from '@/router/index.js' let getStoryage = (item) => { let str = sessionStorage.getItem(item) return JSON.parse(str) } const getters = { get_userInfo: (state) => { return state.userInfo ? state.userInfo : getStoryage('userInfo') }, get_token: (state) => { return state.token ? state.token : sessionStorage.getItem('token') }, get_roles: (state) => { return state.roles.length ? state.roles : getStoryage('roles') }, addRouters: (state, getters) => { let routes = createdRoutes(asyncRoutes, getters.get_roles) return routes }, get_breadcrumb: (state, getters) => { return state.breadcrumb.length ? state.breadcrumb : getStoryage('getStoryage') } } export default getters;
4,
createdRoutes()
That's 3 getters, the method used, without reservation.
/** * Decide whether the current routing object is within the login's authority * @param {Array} roles Jurisdiction * @param {Object} route Route */ function hasPermission (roles, route) { if (route.meta && route.meta.role) { // Routing requires privileges to be judged in the privilege array return roles.includes(route.meta.role) } else { // Pass directly without permission return true } } /** * The side navigation bar of the current user is dynamically generated according to the list of privileges acquired by the interface, and the route array validated by the privilege is returned. * @param {Array} asyncRoutes Routes to be filtered * @param {Array} roles Jurisdiction */ function createdRoutes (asyncRoutes, roles) { const accessedRouters = asyncRoutes.filter(route => { if (hasPermission(roles, route)) { // Current routes pass through privilege validation directly if (route.children && route.children.length) { // Recursive validation for current routing with subrouting route.children = createdRoutes(route.children, roles) } return true } return false }) return accessedRouters } export default createdRoutes
5,
Dealing with problems caused by refresh
Actually, the code here is connected to 1. Note the comments.
// Global Navigation Guard vr.beforeEach((to, from, next) => { // When the page is refreshed, the vue-router and vuex are reset, and the routing is lost. What is used is that the state of vuex is reset after the page is refreshed. if (to.name !== 'login' && !store.state.token) { // Avoid not landing on the page directly if (!sessionStorage.getItem('token')) { location.href = '/' return } let data = JSON.parse(sessionStorage.getItem('baseInfo')) store.dispatch('setBaseInfo', data).then(() => { vr.addRoutes(store.getters.addRouters) }) } // Setting up breadcrumb navigation let breadcrumb = to.matched.filter(item => item.meta.title) if (breadcrumb.length) { breadcrumb = breadcrumb.map(item => item.meta.title) store.commit('set_breadcrumb', breadcrumb) } // Set title document.title = to.meta.title next() })
6. The complete code of the side navigation bar.
Or traverse the permission-based routing table to eliminate some details that need to be hidden pages and so on.
Here, the code and the core function of filtering l routing should be handled according to their own business.
menu of element-ui
<template> <el-menu class="el-menu-vertical-demo" :collapse="isCollapsed" background-color="#545c64" :default-active='activeIndex' text-color="#fff" active-text-color="#7EA8F5"> <section v-for="(item,index) in addRouters" :key="item.name" :class="isCollapsed ? 'collapsed':''"> <!-- There are submenus. --> <el-submenu :index=" `${index+1}`" v-if="!item.meta.hidden && item.children && item.children.length"> <template slot="title"> <i :class="`icon iconfont ${item.meta.icon}`"></i> <span slot="title">{{item.meta.title}}</span> </template> <section v-for="(item2,index2) in item.children" :key="item2.name"> <!-- Secondary menu has submenu --> <el-submenu :index="`${index+1}-${index2+1}`" v-if="item2.children && item2.children.length" class="sub2"> <template slot="title"> <span slot="title">{{item2.meta.title}}</span> </template> <!-- Three level menu --> <el-menu-item v-for="(item3,index3) in item2.children" v-if="!item3.meta.hidden" :index="item3.name" :key="index3" @click.native="$router.push({name:item3.name})"> <span slot="title">{{item3.meta.title}}</span> </el-menu-item> </el-submenu> <!-- Level 2 menu without submenu --> <!-- Not hidden, details page hidden --> <el-menu-item :index="item2.name" v-else-if="!item2.meta.hidden" @click.native="$router.push({name:item2.name})"> <span slot="title">{{item2.meta.title}}</span> </el-menu-item> </section> </el-submenu> <!-- No child menu --> <el-menu-item v-else-if="item.meta.hidden && item.children && item.children.length" :index="item.children[0].name" @click.native="$router.push({name:item.children[0].name})" class="item"> <i :class="`iconfont ${item.children[0].meta.icon}`"></i> <span slot="title">{{item.children[0].meta.title}}</span> </el-menu-item> </section> </el-menu> </template> <script> import { mapGetters } from 'vuex' export default { props: { isCollapsed: { type: Boolean, default: false } }, computed: { ...mapGetters(['addRouters']), activeIndex () { //Fire collection menu return this.$router.name } } } </script> <style lang="scss" scoped> section { /deep/ .el-submenu__title { .icon { margin-right: 10px; } i { color: white; font-size: 14px; } } /deep/ .el-menu-item { padding-left: 50px !important; } /deep/ .el-menu-item.item { padding-left: 19px !important; i { color: white; font-size: 14px; margin-right: 12px; } } /deep/ .el-submenu .el-menu-item { min-width: 0; } /deep/ .el-submenu.sub2 .el-submenu__title { padding-left: 50px !important; i { margin-right: 0px; } } /* /deep/ .el-submenu.sub2 .el-menu-item { text-indent: 12px; } */ } .collapsed { width: 50px; /deep/ .el-submenu__title { .el-icon-arrow-right { display: none; } span[slot="title"] { display: none; } } } </style>
Routing diagram