最近項目需求不是不少,想到公司後臺管理項目有些過於臃腫,相幫它減減肥。javascript
奈何君有意,了卻她無情。css
jQ+bootstrapt的非主流內衣、template-web的硬核外套、html+css+js的老實牌秋褲、cdn銘牌的各類包包,一眼看上去還不錯是否是。跟着她回到家,一開門人傻了!......html
若要愛請深愛,若不愛請拜拜。前端
顯然我選擇了後者,我們步入正題哈 next()vue
通常比較簡單的後臺管理系統就是系統登陸用戶角色較少,維護人員不是不少,可能就admin和editor兩種,這種權限比較簡單,在路由的meta標籤加個role差很少就夠了,舉個栗子java
{
path: '/dynamic',
component: Layout,
name: 'dynamic',
meta: {
title: '動態管理',
icon: 'icon-duihuakuang',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'dynamicList',
component: () => import(''),
name: 'dynamicList',
meta: { title: '動態列表', noCache: true }
},
{
path: 'rootdynamic',
component: () => import(''),
name: 'rootdynamic',
meta: { title: '機器人動態列表', noCache: true }
}
]
}
複製代碼
可是我這個有點不同,複雜一點角色分權限等級,並且用戶較多。因此說這樣的顯然沒法實現。react
通常登陸流程是git
獲取token--->拿token獲取用戶信息以及後端返回路由--->前端篩選權限路由--->登陸成功--->動態顯示該用戶的路由github
咱們沒有token,後端有定時任務,過時後端接口都返回403,前端就直接重定向到登陸頁,因此第一步要不要無所謂了,可是在這文裏仍是走正常流程(項目上你們也要靈活應變,不走尋常路才能走遍全部路!)。web
思考:你的數據到底是存儲到本地localStorage
仍是用Vuex
?(期待你的見解)
我這裏都寫下怎麼選擇取決與我的(不要爲了使用vuex而使用vuex,大多數項目真的不必)
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))
//僞裝有token,你有就把後端給的寫上
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)
})
})
},
複製代碼
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
//路由信息存本地
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', '頭像')
commit('SET_INTRODUCTION', department)
resolve(data)
}).catch(error => {
reject(error)
})
})
}
複製代碼
後端返回的路由是tree類型的數據,顯然不能直接拿過來。
既然這樣,本身動手吧。前端router.js
分constantRoutes
和asyncRoutes
兩個路由菜單
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: '首頁', 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
複製代碼
在main.js
裏面引入import './permission'
來看看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) => {
// 開始進度條
NProgress.start()
// 設置頁面標題
document.title = getPageTitle(to.meta.title)
// 肯定用戶是否已登陸
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
// 肯定用戶是否已經過getInfo得到其權限角色
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// 拿到用戶我的信息
const roles = await store.dispatch('user/getInfo')
// 權限路由匹配
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 動態添加路由
router.addRoutes(accessRoutes)
// hack方法 確保addRoutes已完成
// 設置replace:true,這樣導航就不會留下歷史記錄
next({ ...to, replace: true })
} catch (error) {
// 初始化
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) {
// 在免費登陸白名單中,直接進入
next()
} else {
// 其餘沒有訪問權限的頁面將重定向到登陸頁面。
next(`/login?redirect=${to.path}`)
// next()
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
複製代碼
看看generateRoutes
這裏的roles其實就是用戶信息,證實拿到了後端路由而後下一步
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_PERMISSION', permission) */
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
複製代碼
下面詳細看看重點路由篩選,添加動態路由(完整文件見附錄
)
/** * 遞歸過濾異步路由表 * @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 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)
})
}
})
//將後端返回的和當前路由對比有的話就經過,進入動態路由無的話就pass
if (route.meta && route.meta.title) {
return name.some(item => {
return route.meta.title === item
})
} else {
return false
}
}
複製代碼
既然有了頁面權限,理論上應該有功能權限 其實應該還會有功能權限點,就是頁面內他或許不能新增,只能看到列表,這種是頁面內權限點配置,通常採用自定義指令的方式比較方便
將後端返回的路由中的功能權限點經過遞歸提取出來
mainjs
裏面引入
import directives from '@/utils/directives.js'
Vue.use(directives)
複製代碼
在directives.js
裏面封裝下指令
// 頁面功能權限判斷
function checkPermission(el, binding) {
const { value } = binding
if (value) {
//這個是我在篩選路由時順便存下的
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) {//初始化調用
checkPermission(el, binding)
}
// update(el, binding) {//這是在刷新的時候調用
// checkPermission(el, binding)
// }
})
}
複製代碼
<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>
複製代碼
原本想用react+ant
的,可是奈何感受react
都是js文件不太對個人胃口, (我的感受都是js文件層次不是很清晰,相對來說vue的熟悉成本較低)
既然有的選,那我們就用Vue
,
Vue3.0
雖然能夠用,可是考慮到不肯定因素,還須要時間去完善它
我們選穩的2.x版本,而後UI庫就Element UI
吧。
權限配置到這裏就結束了,有什麼問題你們能夠留言一塊兒討論
如今正在重構頁面coding,也遇到一些問題,高德地圖結合echarts展現問題、角色頁面角色等級問題、二次封裝table組件。
等我攢夠一波坑,再來和你們分享。
Vue重構項目之權限配置篇到此結束,謝謝你的閱讀!!!我們下期見
vuex裏面的permission部分
import {
asyncRoutes,
constantRoutes
} from '@/router'
/** * 經過和後端路由菜單比較肯定當前用戶是否有權限 * @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
}
}
/** * 遞歸過濾異步路由表 * @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
}
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
}
複製代碼
我是涼城a,一個前端,熱愛技術也熱愛生活。
與你相逢,我很開心。
文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊和關注😊
本文首發於掘金,未經許可禁止轉載💌