前言:
需求:需要根據不用的使用者匹配不同的管理許可權,既:匹配不同的操作導航,尤其體現在後臺管理系統內,如果僅僅只是在導航選單內不予顯示,仍然是可以通過路徑直接開啟頁面,因為其路由資訊已經在路由資訊物件(new Router({}))函式中進行了註冊
當然 這裡可以通過全域性導航守衛來區分不同的使用者,允許其進入不同的路徑,但是這隻能進行簡單的許可權判斷,且前端已經寫死,靈活性不高,不能針對每個使用者,做定製化許可權區分
專案地址:github.com/cgq001/vue-…
歡迎star, 留著也許就用到了,畢竟許可權管理 還是很普遍的嘛
使用到的規則
1、動態設定許可權的UI展示
這裡使用element-ui的Three樹形控制元件,資料結構如下:
data: [{
id: 1,
label: '一級 1',
children: [{
id: 4,
label: '二級 1-1',
children: [{
id: 9,
label: '三級 1-1-1'
}, {
id: 10,
label: '三級 1-1-2'
}]
}]
}, {
id: 2,
label: '一級 2',
children: [{
id: 5,
label: '二級 2-1'
}, {
id: 6,
label: '二級 2-2'
}]
}, {
id: 3,
label: '一級 3',
children: [{
id: 7,
label: '二級 3-1'
}, {
id: 8,
label: '二級 3-2'
}]
}]
複製程式碼
這裡選擇這個樹形控制元件,是因為其資料結構和我們的 註冊路由資訊結構十分接近,不需要再重新修改路由資訊的資料結構,即可完美的 展現在 Three樹形控制元件裡
2、將側邊導航所需要的路由資訊物件 抽離成一個陣列,根據後臺返回的陣列,篩選出對應的路由資訊,通過addRoutes新增到路由資訊物件裡,即可完成路由資訊的動態新增
詳情
// 在router.js 路由檔案 新建陣列路由儲存 '/' 父路由下的所有子路由(這裡所有的動態路由均為 '/' 的子路由)
let routerLists=[
{
id:1,
path: '',
label: '首頁',
redirect: '/index', //重定向到
meta:{
title: '首頁',
table: true,
display: false,
icon: 'el-icon-s-home'
}
},
{
id: 2,
path: '/index',
name: 'index',
label: '首頁',
component: _import('Index/Index'),
meta:{
title: '首頁',
table: true,
display:true,
icon: 'el-icon-s-home'
}
},
{
id: 3,
path: '/shop',
name: 'shop',
label: '商品管理',
component: _import('Shop/Shop'),
meta:{
title: '商品列表',
table: true,
display:true,
icon: 'el-icon-s-operation'
}
},
{
id:20,
path: '/admin',
label: '管理員列表',
component: _import('admin/index'),
meta:{
title: '管理員列表',
table: true,
display:true,
icon: 'el-icon-s-custom'
},
children:[
{
id:21,
path: '/admin/index',
label: '管理員列表',
component: _import('admin/admin'),
meta:{
title: '管理員列表',
table: true,
display:true,
icon:'el-icon-tickets'
}
},
{
id:22,
path: '/admin/adminlist',
label: '新增管理員',
component: _import('admin/adminlist'),
meta:{
title: '新增管理員',
table: true,
display:true,
icon:'el-icon-document-remove'
}
}
]
}
]
//定義 上面陣列的父路由
let routerAlls=[ //這裡為routerLists的父路由
{
path: '/',
component: Home
}
]
1 .許可權配置表
// 先把路由資訊物件字串化,然後去除component欄位 ,再傳遞給 許可權配置表
let routerListString =JSON.stringify(routerLists)
let src= routerListStr(routerListString)
store.commit('serRouterList',src)
let arr=[1,2,3,20,21,22] //這裡為許可權列表陣列(既後臺根據使用者身份返回的對應的路由陣列)
//根據許可權配置表(arr陣列)和動態路由資訊物件(routerLists陣列) 獲取本使用者的路由資訊表,並新增到 routerAlls 路由的二級路由裡
2.獲取動態路由
routerAlls[0].children = routerListFun(arr,routerLists)
//獲取 路徑 '/' 的子路由,並新增至 routerAlls 這裡的 routerListFun函式 為 根據許可權列表陣列(arr)篩選動態路由資訊物件(routerLists) PS:函式內容見 文章末尾:附錄
3.獲取側邊導航欄 的 渲染選單 陣列
//根據許可權配置表陣列(arr)和動態路由資訊物件(routerLists) 獲取本使用者的選單列表
let mentParse =JSON.parse(JSON.stringify(src))
let menuList = routerListFun(arr,mentParse) //這裡routerListFun函式注意去除返回陣列中的component
store.commit('setMents',menuList) //將其新增到vuex
// 註冊路由
let routers =new Router({
mode: 'history',
// base: process.env.BASE_URL,
routes: [
{
path: '/loading',
name: 'loading',
component: () => import('../views/Load/Loading.vue'),
meta:{
title: '登陸',
table: false
}
}
]
})
// 將篩選後的路由資訊物件新增至路由表
routers.addRoutes(routerAlls)
// 進行全域性導航守衛
routers.beforeEach((to,from,next)=>{
if(to.path != '/loading'){
let username=store.state.load.userList.username
if(username){
next()
}else{
next({
path:'/loading',
query:{
path:to.path
}
})
}
}else{
next()
}
})
export default routers;
複製程式碼
附錄
1.動態路由書寫規則
* 路由書寫規則
* 1、只有一級路由(實際為二級路由):
* {
id: 2, //ID全域性不能重複
path: '/index', //路由路徑 全域性不能重複
name: 'index', //名字,全域性不能重複,存在二級路由,則一級路由不能有名字
label: '首頁', // 頁面名稱(用於許可權配置時 顯示名稱使用)
component: _import('Index/Index'), // 檔案地址(此處對應的是views目錄)
meta:{
title: '首頁', //頁面名稱(橫向teble標籤切換)
table: true, // 是否顯示 teable 切換按鈕
display:true, // 是否在側邊導航選單顯示
icon: 'el-icon-s-home' // 側邊導航的icon圖示
}
}
2、包含二級路由(實際為三級路由)
{
id:20, //ID全域性不能重複
path: '/admin', //路由路徑 全域性不能重複(此處為父級))
label: '管理員列表', // 頁面名稱(用於許可權配置時 顯示名稱使用)
component: _import('admin/index'), // 檔案地址(此處對應的是views目錄)注意:此檔案下 應包含(router-view 標籤 來顯示子頁面)
meta:{
title: '管理員列表', //頁面名稱(橫向teble標籤切換)
table: true,
display:true, // 是否在側邊導航選單顯示(注意 這裡是父級,如果為false,則子級不在摺疊)
icon: 'el-icon-s-custom' // 側邊導航的icon圖示
},
children:[
{
id:21, //ID全域性不能重複
path: '/admin/index', //路由路徑 全域性不能重複(此處為父級))
label: '管理員列表', // 頁面名稱(用於許可權配置時 顯示名稱使用)
component: _import('admin/admin'), // 檔案地址(此處對應的是views目錄)
meta:{
title: '管理員列表', //頁面名稱(橫向teble標籤切換)
table: true, // 是否顯示 teable 切換按鈕
display:true, // 是否在側邊導航選單顯示
icon:'el-icon-tickets' // 側邊導航的icon圖示
}
}
]
}
複製程式碼
2.routerListFun、routerListStr函式
// 根據後臺返回的許可權陣列,篩選對應的路由資訊物件
export function routerListFun(arr,allList){
let arrArray=[]
for(let i=0;i<arr.length;i++){
for(let k=0;k<allList.length;k++){
if(arr[i] == allList[k].id){
arrArray.push(allList[k])
}
}
}
for(let i=0;i<arrArray.length;i++){
if(arrArray[i].children && arrArray[i].children.length>0){
arrArray[i].childrens=[]
for(let k=0;k<arrArray[i].children.length;k++){
for(let j=0;j<arr.length;j++){
if(arrArray[i].children[k].id == arr[j]){
arrArray[i].childrens.push(arrArray[i].children[k])
}
}
}
arrArray[i].children=arrArray[i].childrens
}
}
return arrArray;
}
// 根據全部的路由資訊物件 返回 許可權列表
export function routerListStr(routerStr){
let routerJson= JSON.parse(routerStr)
for(let i=0;i<routerJson.length;i++){
if(routerJson[i].component){
routerJson[i].component=''
}
if(routerJson[i].children && routerJson[i].children.length>0){
for(let k=0;k<routerJson[i].children.length;k++){
routerJson[i].children[k].component=''
}
}
}
return routerJson;
}
複製程式碼
3.路由渲染
<!-- 這裡的 routerList 為 從 router.js動態獲取到了路由資訊 -->
<div v-for="item in routerList" :key="item.id" >
<!--包含二級導航-->
<el-submenu :index="item.id.toString()" v-if="item.children && item.children.length > 0 && item.meta.display==true">
<template slot="title">
<i :class="item.meta.icon"></i>
<span>{{item.label}}</span>
</template>
<el-menu-item v-show="items.meta.display" v-for="(items,indexs) in item.children" :key="indexs" :index="items.path">
<i :class="items.meta.icon"></i>
{{items.label}}
</el-menu-item>
</el-submenu>
<!-- 如果 二級導航的 父級 設定display:false 則直選渲染二級導航為一級導航 -->
<el-menu-item v-for="(items,indexs) in item.children" :key="indexs" :index="items.path" v-show="items.meta.display" v-else-if="item.children && item.children.length > 0 && item.meta.display==false" >
<i :class="item.meta.icon"></i>
<span slot="title">{{item.label}}</span>
</el-menu-item>
<!-- 渲染一級導航 -->
<el-menu-item v-show="item.meta.display" :index="item.path" v-else >
<i :class="item.meta.icon"></i>
<span slot="title">{{item.label}}</span>
</el-menu-item>
</div>
複製程式碼