使用Vue實現後臺管理系統的動態路由以及側邊欄選單
介紹及說明
1、背景介紹:最近因為公司的專案,對一個後臺管理系統進行前端重構,原專案是SSM架構的前後端沒有分離,前端用JSP和jQuery來寫的,在完成第一期之後,我強烈要求前後端分離,並使用vue來元件化開發。所以經過幾天的摸索完成了專案的整體搭建和基礎建設。
2、專案要求:因為原先的專案的選單都是後臺來動態生成的,所以用vue來寫也要從後臺獲取路由表,前端再處理成我們想要的路由格式,再完成側邊欄選單;並且前端頁面裡有一個模組專門來管理生成選單和許可權(也就是前端自定義一些選單資訊傳給後端,後端把資料存到資料庫,並根據角色許可權處理完資料再返回路由表給前端,前端就可以動態生成側邊欄選單了)。
實現過程
首先本專案是基於vue-element-admin的後臺管理模板( vue-element-admin),這個模板很多功能都有現成的,很方便,拉取下來之後就可以基於這個進行開發了,這個模板有很詳細的文件說明( vue-element-admin使用文件),裡面也有動態路由的整合方案,本專案選擇的就是路由全部通過後端來生成,前端來自定義需要的路由。
步驟如下:
1、使用者登入成功之後(登入的功能,模板裡也寫的很好,換成公司的登入介面就可以直接用),再根據使用者名稱獲取使用者的許可權,然後根據使用者許可權獲取後端的路由資料:
1)許可權處理和生成動態路由:src/permission.js檔案
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // 進度條
import 'nprogress/nprogress.css' // 進度條樣式
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
// import { getRoutes } from './api/role'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login', '/auth-redirect'] // 沒有重定向白名單
router.beforeEach(async(to, from, next) => {
// 開始進度條
NProgress.start()
// 設定頁面標題
document.title = getPageTitle(to.meta.title)
// 確定使用者是否已登入,獲取token
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// 如果已登入,則重定向到主頁
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)
// console.log(accessRoutes)
// 動態新增可訪問路由
router.options.routes = router.options.routes.concat(accessRoutes)
router.addRoutes(accessRoutes)
// hack方法 以確保addRoutes是完整的
next({ ...to, replace: true })
// console.log(router)
} catch (error) {
// 刪除令牌,進入登入頁面重新登入
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* 沒有令牌*/
if (whiteList.indexOf(to.path) !== -1) {
// 去往免費的登入白名單
next()
} else {
// 沒有訪問許可權的其他頁面被重定向到登入頁面
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
2)獲取使用者許可權的程式碼:src/store/modules/user.js
// 獲取使用者許可權資訊
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.code).then(response => {
const { data } = response
// console.log(data)
if (!data) {
reject('驗證失敗,請重新登入.')
}
let roles = []
roles = roles.concat(data.usrGroupcode)
const name = data.userName
// // 角色必須是非空陣列
if (!roles || roles.length <= 0) {
reject('getInfo: 角色必須是非空陣列!')
}
const avatar = data.userCode
commit('SET_NAME', name)
commit('SET_ROLES', roles)
commit('SET_CODE', avatar)
const users = {
roles: roles,
introduction: '',
code: avatar,
name: name
}
// console.log(users)
resolve(users)
}).catch(error => {
reject(error)
})
})
},
2、處理後端返回的路由資訊並存到vuex裡:src/store/modules/permission.js
import { constantRoutes } from '@/router'
import { getMenu } from '../../api/role'
import Layout from '@/layout'
/**
* 將後端content欄位轉成元件物件
*/
function _import(file) {
return () => import(`@/views/${file}/index.vue`)
}
/**
* 通過遞迴過濾非同步路由表
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes) {
const res = []
// eslint-disable-next-line no-empty
for (let i = 0; i < routes.length; i++) {
res.push({
path: routes[i].content === 'Layout' ? `/${routes[i].path}` : routes[i].path,
component: routes[i].content === 'Layout' ? Layout : _import(routes[i].content),
name: routes[i].path,
meta: {
title: routes[i].menuname,
icon: routes[i].icon
},
children: routes[i].children && routes[i].children.length ? filterAsyncRoutes(routes[i].children) : []
})
}
return res
}
const state = {
routes: constantRoutes,
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise((resolve, reject) => {
let accessedRoutes
let asyncRoutes = []
getMenu(roles[0]).then(res => {
if (res.msg === 'SUCCESS') {
asyncRoutes = res.data
} else {
asyncRoutes = []
}
// console.log(asyncRoutes)
accessedRoutes = filterAsyncRoutes(asyncRoutes)
// 最後新增
const unfound = { path: '*', redirect: '/404', hidden: true }
accessedRoutes.push(unfound)
// console.log(accessedRoutes)
// console.log('store:accessedRoutes')
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
}).catch(error => {
reject(error)
})
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
基礎路由和統一路由:src/router/index.js
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: '/auth-redirect',
component: () => import('@/views/login/auth-redirect'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/error-page/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error-page/401'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'home',
meta: { title: '首頁', affix: true },
hidden: true,
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard'),
hidden: true
}
]
}
]
3、然後再通過遞迴生成側邊欄選單:src/layout
這個時候列印路由資訊是可以得到正常的路由表資訊;根據路由表生成側邊欄:
this.$router.options.routes 可以獲取路由表資訊,具體邏輯vue-element-admin模板裡有現成的,這裡就不解釋了。
最後說一下後端返回的資料結構:
[{
applicationid: 1035
children: [ //子節點,結構和當前一樣
0:{
applicationid: 1035
children: [
0: {
applicationid: 1035
children: null
content: "system/system-management/menu"
formid: 101100
formtype: 1
icon: ""
inactived: 1
memo: "EAM"
menuid: 101100
menuname: "選單管理"
parentmenuid: 101000
parentrowid: "01KBGO"
path: "menu"
roles: ""
rowid: "01KBGP"
seq: 101100
treecontrol: "01KBGP01KBGO01KBG9"
}
]
content: "system/system-management"
formid: 101000
formtype: 1
icon: ""
inactived: 1
memo: "EAM"
menuid: 101000
menuname: "系統設定"
parentmenuid: 100000
parentrowid: "01KBG9"
path: "system-management"
roles: ""
rowid: "01KBGO"
seq: 101000
treecontrol: "01KBGO01KBG9"
}
]
content: "Layout" //對應元件的名稱
formid: 100000
formtype: 1
icon: "" //選單的圖示
inactived: 1 //是否啟用
memo: "EAM"
menuid: 100000 //選單的id
menuname: "系統管理" //選單的名稱
parentmenuid: null //父級選單id
parentrowid: ""
path: "system" //路由
roles: ""
rowid: "01KBG9"
seq: 100000 //排序
treecontrol: "01KBG9"
}]
相關文章
- vue 動態載入路由,渲染左側選單欄Vue路由
- Vue後臺管理開發之側邊欄(初稿)Vue
- TornadoFx實現側邊欄選單效果
- css3實現側邊滑動選單CSSS3
- vue後臺管理之動態載入路由Vue路由
- Vue管理系統前端系列六動態路由-許可權管理實現Vue前端路由
- 後臺管理系統vue.js路由Vue.js路由
- vue 動態選單以及動態路由載入、重新整理採的坑Vue路由
- VUE3後臺管理系統【路由鑑權】Vue路由
- Vue Element Admin 新增側邊欄Vue
- vue實現後臺管理系統頁面功能和頁面路由許可權的控制Vue路由
- (系列十)Vue3中選單和路由的結合使用,實現選單的動態切換(附原始碼)Vue路由原始碼
- 使用動態路由實現許可權管理路由
- 適合移動手機的jQuery多級側邊欄選單外掛jQuery
- 採用JavaScript+XML實現具有樹形選單功能的論壇側邊導航欄JavaScriptXML
- 使用Vue搭建電商後臺管理系統Vue
- React 折騰記 - (3) 結合Mobx實現一個比較靠譜的動態tab水平選單,同時關聯側邊欄React
- React 折騰記 – (3) 結合Mobx實現一個比較靠譜的動態tab水平選單,同時關聯側邊欄React
- 影片直播原始碼,預設展開側邊欄選單原始碼
- vue+element-ui實現動態的許可權管理和選單渲染VueUI
- VUE動態路由和按鈕的實現Vue路由
- vue後臺管理系統學習(6)--路由和許可權Vue路由
- 簡簡單單的Vue3(外掛開發,路由系統,狀態管理)Vue路由
- JavaScript側欄緩動分享選單詳解JavaScript
- VUE 實現 Studio 管理後臺(四):狀態模式實現視窗停靠,靈動、自由Vue模式
- vue-element-admin配置側邊欄以及主要模組背景色等Vue
- ElementUI側邊欄導航選單隱藏顯示問題UI
- Vue速成--專案實戰(後臺管理系統)Vue
- 簡單的後臺管理系統vue-cli3.0+element-uiVueUI
- vue後臺管理系統程式碼Vue
- HTML歷理 ICbA的側欄選單HTML
- Go 實現世界盃後臺管理系統Go
- Flutter drawer側邊欄Flutter
- 後臺管理系統CMS模組-後端實現後端
- React 折騰記 - (4) 側邊欄聯動Tabs選單-增強版(結合Mobx)React
- jQuery簡單實用的響應式固定側邊欄外掛jQuery
- 使用vue3.0和element實現後臺管理模板Vue
- 10款精選的後臺管理系統