The preface
Recently, the project demand is not very much, I think the company’s back office management project is too bloated, help it lose weight.
How you intended, but she was ruthless.
JQ + Bootstrapt non-mainstream underwear, Template-Web hardcore coat, HTML + CSS + JS honest long underwear, CDN nameplate bag, look good at a glance, isn’t it? Follow her back home, a door silly! .
If you want to love, love deeply. If you don’t love, bye.
Obviously I chose the latter, so let’s get down to business.
Project Architecture – Permission configuration
Analysis of the
Generally a relatively simple background management system is the system login user roles are few, maintenance personnel are not many, may be admin and editor two, this kind of permission is relatively simple, in the meta tag of the route is almost enough to add a role, for example
{
path: '/dynamic'.component: Layout,
name: 'dynamic'.meta: {
title: 'Dynamic management'.icon: 'icon-duihuakuang'.roles: ['admin'.'editor'] // you can set roles in root nav
},
children: [{path: 'dynamicList'.component: () = > import(' '),
name: 'dynamicList'.meta: { title: 'Dynamic List'.noCache: true}}, {path: 'rootdynamic'.component: () = > import(' '),
name: 'rootdynamic'.meta: { title: 'Robot Dynamic List'.noCache: true}}}]Copy the code
But this one is a little different, a little more complex roles of permission level, and more users. So that’s obviously not going to happen.
thinking
The login process is as follows
Obtain the token- > obtain the user information with the token and return the route from the backend -> filter the permission route from the front-end -> login success -> dynamically display the route of the user
We have no token, there is a scheduled task on the back end, the backend interface returns 403 when expired, and the front end directly redirects to the login page, so the first step does not matter, but in this article we still go through the normal process (in the project, we also need to be flexible, take an unusual way to get all the way!) .
practice
Access token
Do you store your data locally in localStorage or using Vuex? (Looking forward to your opinion)
I’ve written down here that the choice is up to the individual (don’t use vuex for the sake of using vuex, most projects really don’t have to)
login({ commit }, userInfo) {
const {
username,
password,
verification
} = userInfo
return new Promise((resolve, reject) = > {
login({
userName: username.trim(),
passWord: password,
captcha: verification
}).then(response= > {
if (response) {
//localStorage.setItem('info', JSON.stringify(response.data))
// Pretend to have a token. If you have a token, write the one given by the back end
commit('SET_TOKEN'.new Date().getTime())
commit('SET_LEVEL', response.data.level)
setToken(new Date().getTime())
resolve(response)
} else {
this.$message({
type: 'error'.message: response.errMsg
})
}
}).catch(error= > {
reject(error)
})
})
},
Copy the code
Get the token to obtain the user information and the back-end return route
getInfo({ commit, state }) {
return new Promise((resolve, reject) = > {
getInfo(state.token).then(response= > {
// Route information is stored locally
localStorage.setItem('sidebars'.JSON.stringify(response.data))
const data = JSON.parse(localStorage.getItem('info'))
const {
roleName,
userName,
department
} = data
commit('SET_ROLES', roleName)
commit('SET_NAME', userName)
commit('SET_AVATAR'.'avatar')
commit('SET_INTRODUCTION', department)
resolve(data)
}).catch(error= > {
reject(error)
})
})
}
Copy the code
Front-end filter permission routes
The route returned by the backend is of type tree, which obviously cannot be taken directly.
In that case, do it yourself. Front-end router. Js has two routing menus: constantRoutes and asyncRoutes
- ConstantRoutes: Routes that each user has, such as login page, home page, 404…
- AsyncRoutes: All permission pages
export const constantRoutes = [
{
path: '/redirect'.component: Layout,
hidden: true.children: [{path: '/redirect/:path(.*)'.component: () = > import('@/views/redirect/index')}]}, {path: '/login'.component: () = > import('@/views/login/index'),
hidden: true
},
{
path: '/ 404'.component: () = > import('@/views/error-page/404'),
hidden: true
},
{
path: '/'.component: Layout,
redirect: '/dashboard'.children: [{path: 'dashboard'.component: () = > import('@/views/dashboard/index'),
name: 'Dashboard'.meta: { title: 'home'.icon: 'icon-shouye'.affix: true}}]}]export const asyncRoutes = [
]
const router = createRouter()
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
Copy the code
Import ‘./permission’ in main.js
Permission-js: Permission-js: permission-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 } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'.'/auth-redirect'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// Start the progress bar
NProgress.start()
// Set the page title
document.title = getPageTitle(to.meta.title)
// Determines whether the user is 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 or her permission role through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// Get the user's personal information
const roles = await store.dispatch('user/getInfo')
// Permission route matching
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// Add a route dynamically
router.addRoutes(accessRoutes)
// The hack method ensures that the addRoutes are complete
// Set replace:true so that the navigation does not leave a historynext({ ... to,replace: true})}catch (error) {
/ / initialization
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login? redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if(whiteList.indexOf(to.path) ! = = -1) {
// In the free login whitelist, enter directly
next()
} else {
// Other pages that do not have access will be redirected to the login page.
next(`/login? redirect=${to.path}`)
// next()
NProgress.done()
}
}
})
router.afterEach(() = > {
// finish progress bar
NProgress.done()
})
Copy the code
Look at the generateRoutes where roles is actually the user information, prove that you got the backend routing and then the next step
generateRoutes({ commit }, roles) {
return new Promise(resolve= > {
let accessedRoutes
if (roles) {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
/** Parse (localstorage.getitem ('sidebars')) const Permission = [] data.foreach (element => {if (element.children) { element.children.forEach(item => { item.children.forEach(i => { permission.push(i.menuId) }) }) } }) commit('SET_PERMISSION', permission) */
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
Copy the code
Take a closer look at key route filtering and adding dynamic routes (see appendix for full documentation)
/** * Recursively filter asynchronous routing table *@param routes asyncRoutes
* @param roles* /
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route= > {
consttmp = { ... route }if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
/** * Compare with the backend routing menu to determine whether the current user has permissions *@param roles
* @param route* /
function hasPermission(roles, route) {
// Get the route back from the backend and recurse the menu name
const data = JSON.parse(localStorage.getItem('sidebars'))
const name = []
data.forEach(element= > {
name.push(element.menuName)
if (element.children) {
element.children.forEach(item= > {
name.push(item.menuName)
})
}
})
// Compare the route returned by the backend with the current route. If yes, pass. If no, pass
if (route.meta && route.meta.title) {
return name.some(item= > {
return route.meta.title === item
})
} else {
return false}}Copy the code
Successful login dynamically displays the route of the user
Since there are page permissions, in theory, there should be functional permissions in fact, there should also be functional permissions, that is, he may not be able to add in the page, only to see the list, this is the page permission point configuration, generally using custom instructions is more convenient
Permission configuration – Page function permission point configuration
The function permission points in the route returned by the back end are extracted by recursion
So let’s first import the file
MainJS
import directives from '@/utils/directives.js'
Vue.use(directives)
Copy the code
And then write the logic
Encapsulate the directives in directives
// Page function permission judgment
function checkPermission(el, binding) {
const { value } = binding
if (value) {
// This is saved by the way when I filter routes
const permissionArr = store.getters && store.getters.permissionArr
const permissionRoles = Number(value)
const hasPermission = permissionArr.some(val= > {
return permissionRoles === val
})
if(! hasPermission) { el.parentNode && el.parentNode.removeChild(el) } }else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`)}}export default (Vue) => {
Vue.directive('permission', {
inserted(el, binding) {// Initialize the call
checkPermission(el, binding)
}
Update (el, binding) {// This is called when refreshing
// checkPermission(el, binding)
// }})}Copy the code
The last page uses
<el-button
v-for="(item) in dataSource.tool"
:key="item.key"
v-permission="item.permission"
class="filter-item"
type="primary"
@click="handleAdd(item.name)"
>
{{ item.name }}
</el-button>
Copy the code
At the end
Talk about architecture framework choices
Originally, I wanted to use React +ant, but I feel that react is js file, which is not my appetite. (Personally, I feel that the js file hierarchy is not very clear, relatively speaking, vUE has a lower cost of familiarity)
Since we have a choice, let’s use Vue,
Vue3.0 works, but it will take time to perfect it, given the uncertainties
Let’s go with the solid 2.x version and Element UI.
Permission configuration ends here, what is the problem we can leave a message to discuss together
Now I am refactoring the page coding, but I also encountered some problems, such as the display problem of Amap combined with Echarts, the role level problem of the role page, and the secondary encapsulation of the TABLE component.
I’ll share it with you when I have enough potholes.
This is the end of Vue refactoring project permission configuration, thank you for reading!! See you next time
The appendix
Permission section of vuex
import {
asyncRoutes,
constantRoutes
} from '@/router'
/** * Compare with the backend routing menu to determine whether the current user has permissions *@param roles
* @param route* /
function hasPermission(roles, route) {
const data = JSON.parse(localStorage.getItem('sidebars'))
const name = []
data.forEach(element= > {
name.push(element.menuName)
if (element.children) {
element.children.forEach(item= > {
name.push(item.menuName)
})
}
})
if (route.meta && route.meta.title) {
return name.some(item= > {
return route.meta.title === item
})
} else {
return false}}/** * Recursively filter asynchronous routing table *@param routes asyncRoutes
* @param roles* /
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route= > {
consttmp = { ... route }if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [].addRoutes: [].permissionArr: []}const mutations = {
SET_ROUTES: (state, routes) = > {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
},
SET_PERMISSION: (state, arr) = > {
state.permissionArr = arr
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve= > {
let accessedRoutes
if (roles) {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
const data = JSON.parse(localStorage.getItem('sidebars'))
const permission = []
data.forEach(element= > {
if (element.children) {
element.children.forEach(item= > {
item.children.forEach(i= > {
permission.push(i.menuId)
})
})
}
})
commit('SET_ROUTES', accessedRoutes)
commit('SET_PERMISSION', permission)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
Copy the code
Write in the last
I am Liangcheng A, a front end, who loves technology and life.
I’m glad to meet you.
If you want to learn more, please click here and look forward to your little ⭐⭐
-
Feel free to correct any mistakes in the comments section, and if this post helped you, feel free to like and follow 😊
-
This article was originally published in Nuggets and cannot be reproduced without permission at 💌