activemenu怎么拼 vue_vue-element-admin登录逻辑,以及动态添加路由,显示侧边栏
這段時間在研究element-admin,感覺這個庫有許多值得學習的地方,我學習這個庫的方法是,先看它的路由,順著路由,摸清它的邏輯,有點像順藤摸瓜。
這個庫分的模塊非常清晰,適合多人合作開發項目,但是如果一個人去用這個庫開發后臺,步驟顯的有點繁瑣,特別是調用后端接口,之前一個方法就調用到了,但是用這個庫,需要先后分幾步調用。
比如說調用一個登陸接口:點擊登陸按鈕----》調用store中的方法----》調用api中對應登陸的方法---》request.js中封裝的axios方法
4步!!!!,讓我看來確實是有點繁瑣,這個問題到后面解決,通過自己封裝的axios方法,直接調用后臺接口,目前不知道會不會遇到其它問題。好了,接下來進入正題!!!
接下來先介紹一下,element-admin的登錄邏輯
1、先看登錄方法里寫什么:
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
//調用user模塊紅的login
console.log("點擊登陸按鈕")
this.$store.dispatch('user/login', this.loginForm).then(() => {
console.log("登錄成功");
this.$router.push({ path: this.redirect || '/' });
this.loading = false;
}).catch(() => {
this.loading = false;
})
} else {
console.log('error submit!!');
return false;
}
})
}
通過上面紅色代碼,可以看出,點擊過登錄按鈕后,調用了$store里的一個方法,名叫login
2、下面來看看$store里的這個login方法:
import { login, logout, getInfo,self} from '@/api/user'
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo;
return new Promise((resolve, reject) => {
console.log("vuex中的請求")
login({ username: username.trim(), password: password }).then(response => {
console.log('vuex中')
console.log(response);
const { data } = response;
commit('SET_TOKEN', data.token);//存在vueX中
setToken(data.token);//存在cookie中
resolve();
}).catch(error => {
console.log(error);
reject(error);
})
})
},
咿,怎么兩個login,熟悉vuex的話應該知道,第一個login是$store中的方法,第二個login方法是,api里的login方法,用來調用接口的
3、再來看看api中的login方法:
import request from '@/utils/request'
export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
})
}
上面是api中的login方法,它調用了request.js,request.js是封裝了axios,是用來請求后臺接口的,如果這個接口請求成功了,就回到了第一步中的.then()方法中
代碼是,路由跳轉到首頁,進入正式頁面!!!
重點來了!!!
之所以是稱之為權限,也就是必須滿足一定的條件才能夠訪問到正常頁面,那么如果不滿足呢?如果我沒有token,讓不讓我進正常頁面呢??
肯定是不讓的,那我沒有token,該去哪?答案是還待在登錄頁,哪都去不了,那么這些處理應該在哪寫呢?答案是,permission.js模塊
這個js在main.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'] // 沒有重定向白名單
router.beforeEach(async(to, from, next) => {
// 開始進度條
NProgress.start()
// 設置頁面標題
document.title = getPageTitle(to.meta.title)
// 確定用戶是否已登錄
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// 如果已登錄,則重定向到主頁
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name;
console.log(hasGetUserInfo);
if (hasGetUserInfo) {
console.log("有用戶信息");
next();
} else {
console.log("無用戶信息")
try {
// 獲得用戶信息
await store.dispatch('user/getInfo');
//實際是請求用戶信息后返回,這里是模擬數據,直接從store中取
const roles=store.getters.roles;
store.dispatch('permission/GenerateRoutes', { roles }).then(() => { // 生成可訪問的路由表
router.addRoutes(store.getters.addRouters); // 動態添加可訪問路由表
router.options.routes=store.getters.routers;
next({ ...to, replace: true });// hack方法 確保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
// next()
} catch (error) {
// 刪除token,進入登錄頁面重新登錄
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}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// 完成進度條
NProgress.done()
})
一看代碼好多,不過不要怕,我來分析一下它的情況,不就是點if else嘛
從上面代碼看,每次路由跳轉,都要從cookie中取token,
那么可以分兩種情況,有token和無token
有token:再看看是不是去登錄頁的,登錄頁肯定不能攔截的,如果是登錄頁就直接放行。如果不是登錄頁,就要看看本地有沒有用戶信息,看看cookie中有沒有用戶信息(不一定是token,也可能是localstorage)。如果有用戶信息,放行。如果沒有用戶信息,就調用接口去獲取登錄信息,然后后面還有一點代碼,涉及到了動態添加路由(這里先說到這,后面具體說動態添加權限路由的事)。獲取到用戶信息后放行。如果在獲取用戶信息的過程中報錯,則回到登錄頁
無token:先看看用戶要進入的頁面是不是在白名單內,一般登錄、注冊、忘記密碼都是在白名單內的,這些頁面,在無token的情況下也是直接放行。如果不在白名單內,滾回登錄頁。
以上就是element-admin的登錄邏輯了。不知道能否幫助到你,但是寫下來,讓自己的思路更清晰,也是不錯的。
下面來說一下,element-admin的動態權限路由,顯示側邊欄是什么邏輯
首先要了解一下,側邊欄是如何渲染出來的,看看layout/components/slibar/index.vue有這樣一段代碼:
計算屬性中有這樣一段代碼:
routes() {
return this.$router.options.routes
},
這個routes,是路由的元信息!!!是一個數組
看到這就應該明白,側邊欄是如何渲染出來的,
再來看看router.js里的代碼:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* 注意: 子菜單只在路由子菜單時出現。長度> = 1
* 參考網址: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true 如果設置為true,項目將不會顯示在側欄中(默認為false)
* alwaysShow: true 如果設置為true,將始終顯示根菜單
* 如果不設置alwaysShow, 當項目有多個子路由時,它將成為嵌套模式,否則不顯示根菜單
* redirect: noRedirect 如果設置noRedirect,則不會在面包屑中重定向
* name:'router-name' the name is used by (must set!!!)
* meta : {
roles: ['admin','editor'] 控制頁面角色(可以設置多個角色)
title: 'title' 名稱顯示在側邊欄和面包屑(推薦集)
icon: 'svg-name' 圖標顯示在側欄中
breadcrumb: false 如果設置為false,則該項將隱藏在breadcrumb中(默認為true)
activeMenu: '/example/list' 如果設置路徑,側欄將突出顯示您設置的路徑
}
*/
/**
* constantRoutes
* 沒有權限要求的基本頁
* 所有角色都可以訪問
* 不需要動態判斷權限的路由
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/self',
children: [{
path: 'self',
name: 'Self',
component: () => import('@/views/self/index'),
meta: { title: '首頁', icon: 'dashboard' }
}]
},
{
path: '/example',
component: Layout,
redirect: '/example/table',
name: 'Example',
meta: { title: 'Example', icon: 'example' },
children: [
{
path: 'table',
name: 'Table',
component: () => import('@/views/table/index'),
meta: { title: 'Table', icon: 'table'}
},
{
path: 'tree',
name: 'Tree',
component: () => import('@/views/tree/index'),
meta: { title: 'Tree', icon: 'tree',breadcrumb: true},
hidden: false,//在側邊欄上顯示 true為不顯示 當父路由的字路由為1個時,不顯示父路由,直接顯示子路由
alwaysShow:false,//默認是false 設置為true時會忽略設置的權限 一致顯示在跟路由上
}
]
},
{
path: '/form',
component: Layout,
children: [
{
path: 'index',
name: 'Form',
component: () => import('@/views/form/index'),
meta: { title: 'Form', icon: 'form' }
}
]
},
{
path: '/nested',
component: Layout,
redirect: '/nested/menu1',
name: 'Nested',
meta: {
title: 'Nested',
icon: 'nested'
},
children: [
{
path: 'menu1',
component: () => import('@/views/nested/menu1/index'), // Parent router-view
name: 'Menu1',
meta: { title: 'Menu1' },
children: [
{
path: 'menu1-1',
component: () => import('@/views/nested/menu1/menu1-1'),
name: 'Menu1-1',
meta: { title: 'Menu1-1' }
},
{
path: 'menu1-2',
component: () => import('@/views/nested/menu1/menu1-2'),
name: 'Menu1-2',
meta: { title: 'Menu1-2' },
children: [
{
path: 'menu1-2-1',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
name: 'Menu1-2-1',
meta: { title: 'Menu1-2-1' }
},
{
path: 'menu1-2-2',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
name: 'Menu1-2-2',
meta: { title: 'Menu1-2-2' }
}
]
},
{
path: 'menu1-3',
component: () => import('@/views/nested/menu1/menu1-3'),
name: 'Menu1-3',
meta: { title: 'Menu1-3' }
}
]
},
]
},
{
path: 'external-link',
component: Layout,
children: [
{
path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
meta: { title: 'External Link', icon: 'link' }
}
]
},
{
path: '/self',
component: Layout,
children: [
{
path: 'index',
name: 'self',
component: () => import('@/views/self'),
meta: { title: 'self', icon: 'form' }
}
]
},
// 404頁面必須放在最后!!
// { path: '*', redirect: '/404', hidden: true }
]
// 創建路由
const createRouter = () => new Router({
// mode: 'history', // 需要服務支持
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
var router = createRouter()
// 重置路由
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
//異步掛載的路由
//動態需要根據權限加載的路由表
export const asyncRouterMap = [
{
path: '/permission',
component: Layout,
name: 'permission',
redirect: '/permission/index222',
meta: {title:'permission', role: ['admin','super_editor'] }, //頁面需要的權限
children: [
{
path: 'index222',
component: () => import('@/views/self'),
name: 'index222',
meta: {title:'權限測試1',role: ['admin','super_editor'] } //頁面需要的權限
},
{
path: 'index333',
component: () => import('@/views/self'),
name: 'index333',
meta: {title:'權限測試2',role: ['admin','super_editor'] } //頁面需要的權限
}
]
},
{ path: '*', redirect: '/404', hidden: true }
];
export default router
注意以上代碼中紅色的代碼,這個routes中分兩塊路由配置,一塊是固定的,無權限的路由配置,也就是不管是管理員身份,還是超級管理員身份,都會顯示的路由配置。
第二塊是,帶權限的路由配置,根據用戶權限來顯示側邊欄。注意,帶權限的配置里的meta中都有role項,代表是權限
首先,我們在獲取用戶信息時,會得到這個用戶有哪些權限,是一個數組,假如是這樣的
commit('SET_ROLES',['admin','super_editor']);//自定義權限
這個用戶的權限有這些(admin、super_editor),然后再根據用戶權限來篩選出符合的動態添加的路由,
什么時候篩選呢?
這就用到登錄時的攔截器了,上面遇到過,就在哪執行,來看看那里都是寫了一些什么代碼:
拿到這看看:
// 獲得用戶信息
await store.dispatch('user/getInfo');
//實際是請求用戶信息后返回,這里是模擬數據,直接從store中取
const roles=store.getters.roles;
store.dispatch('permission/GenerateRoutes', { roles }).then(() => { // 生成可訪問的路由表
router.addRoutes(store.getters.addRouters); // 動態添加可訪問路由表
router.options.routes=store.getters.routers;
next({ ...to, replace: true });// hack方法 確保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
routes其實就是上面的兩個權限組成的數組,然后傳入了GenerateRoutes方法內,(注意es6語法,看不懂的去了解一下es6),再看看GenerateRoutes中的代碼:
import { asyncRouterMap, constantRoutes } from '@/router';
function hasPermission(roles, route) {
if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0)
} else {
return true
}
}
const permission = {
namespaced: true,
state: {
routers: constantRoutes,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRoutes.concat(routers);
}
},
actions: {
GenerateRoutes({ commit }, data) {//roles是用戶所帶的權限
return new Promise(resolve => {
const { roles } = data;
const accessedRouters = asyncRouterMap.filter(v => {
// if (roles.indexOf('admin') >= 0) {
// return true;
// };
if (hasPermission(roles, v)) {
if (v.children && v.children.length > 0) {
v.children = v.children.filter(child => {
if (hasPermission(roles, child)) {
return child
}
return false;
});
return v
} else {
return v
}
}
return false;
});
commit('SET_ROUTERS', accessedRouters);
resolve();
})
}
}
};
export default permission;
GenerateRoutes方法辦了一件事,就是把動態路由配置里符合用戶權限的配置篩選出來,組成一個數組,然后,和固定路由合并到了一起,存到了vuex中,
然后調用了這兩句代碼:
router.addRoutes(store.getters.addRouters); // 動態添加可訪問路由表
router.options.routes=store.getters.routers;
router.addRoutes()方法是,動態添加路由配置,參數是符合路由配置的數組,
然后將路由元信息,變成合并后的路由元信息,因為渲染側邊欄需要用到,
這兩句代碼,順序不能變,缺一不可,缺了addRoutes,點擊側邊欄的動態路由會無反應,缺了options,側邊欄顯示不出來動態添加的路由!!!!
以上就是element-admin的動態路由了,睡覺。。。。
標簽:vue,admin,router,element,meta,store,import,path,路由
來源: https://www.cnblogs.com/fqh123/p/11094296.html
總結
以上是生活随笔為你收集整理的activemenu怎么拼 vue_vue-element-admin登录逻辑,以及动态添加路由,显示侧边栏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 推特又出故障!用户发推被提示已达上限
- 下一篇: 002 使用Appender扩展logg