文章目录
1、实现思路
- 前端调用 登录接口 成功登录,后端返回 当前登录 当前登录用户 所拥有的菜单信息、token等
- 前端拿到 用户 菜单信息 动态存储到 VUEX 中
- 在 permission.js 权限js文件 实现动态路由
2、具体实现
2.1 后端返回的 菜单信息json格式
json中 除了 :id、parent 字段 以外其他字段都是 vue-router字段,和 router.js 文件json几乎一致
建议 刚开始开发先别 实现这个功能,因为这个功能后续实现 很简单很快,等全部功能开发好,直接把 路由 router.js 路由json 信息保存到数据库直接就可以用了
{
"code": 0,
"msg": "请求成功",
"data": {
"menu": [
{
"id": 1,// 这个字段是 数据库表ID
"parent": 0, // 父级菜单ID,0 表示为一级菜单,没有父级
"children": [
{
"id": 2,
"parent": 1,
"children": [],
"name": "SN管理-SN字典管理",
"path": "/dict/index",
"component": "use/sn-manage/dict/index",
"redirect": "",
"meta": {
"icon": "edit",
"title": "字典管理"
},
"hidden": false
},
{
"id": 3,
"parent": 1,
"children": [],
"name": "SN管理-序列号生成",
"path": "/generate/index",
"component": "use/sn-manage/generate/index",
"redirect": "",
"meta": {
"icon": "guide",
"title": "序列号生成"
},
"hidden": false,
},
{
"id": 8,
"parent": 1,
"children": [],
"name": "SN管理-序列号列表",
"path": "/generate/record",
"component": "use/sn-manage/generate/record",
"meta": {
"icon": "user",
"title": "序列号列表"
},
"hidden": false
}
],
"name": "SN管理444",
"path": "/sn-manage",
"component": "Layout",
"redirect": "/sn-manage",
"meta": {
"icon": "dashboard",
"title": "SN管理"
},
"hidden": false
},
{
"id": 4,
"parent": 0,
"children": [
{
"id": 5,
"parent": 4,
"children": [],
"name": "管理员列表",
"path": "/pmsApi/admin/index",
"component": "use/admin/index",
"meta": {
"icon": "edit",
"title": "管理员列表"
},
"hidden": false
}
],
"name": "管理员管理",
"path": "/admin-manage",
"component": "Layout",
"redirect": "/admin-manage",
"meta": {
"icon": "user",
"title": "管理员管理"
},
"hidden": false,
},
{
"id": 9,
"parent": 0,
"children": [
{
"id": 11,
"parent": 9,
"children": [],
"name": "菜单管理",
"path": "/system/menu/index",
"component": "use/system-manage/menu/index",
"meta": {
"icon": "tree-table",
"title": "菜单管理"
},
"hidden": false
},
{
"id": 12,
"parent": 9,
"children": [],
"name": "操作人员管理",
"path": "/system/operator/index",
"component": "use/system-manage/operator/index",
"meta": {
"icon": "list",
"title": "操作人员管理"
},
"hidden": false
}
],
"name": "系统管理",
"path": "/system",
"component": "Layout",
"meta": {
"icon": "tree",
"title": "系统管理"
},
"hidden": false,
}
]
}
}
2.2 修改 vue-element-admin 登陆成功后的逻辑代码
这一步作用是: 把 用户的菜单 json信息保存在 vuex中
修改 user.js, 路径:src/store/modules/user.js
里面有两个方法
方法一:actions 调用的是 login接口
方法二:getInfo 调用的是 info接口
登录成功后,逻辑代码 写在哪个方法都可以,这两个方法都是处理登录成功后的操作
我这边就写在 getinfo 里面
user.js
代码
import {
login, logout, getInfo } from '@/api/user'
import {
getToken, setToken, removeToken } from '@/utils/auth'
import router, {
resetRouter } from '@/router'
import {
MessageBox, Message } from 'element-ui'
import request from "@/utils/request.js";
const state = {
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: [],
isSuper: false, // 是否为超级管理员,
menu: [], //
}
const mutations = {
SET_MENU: (state, i) =>{
// 管理员菜单
state.menu = i
},
SET_IS_SUPER: (state, i) =>{
// 设置是否为超级管理员
state.isSuper = i
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
const actions = {
// user login
login({
commit }, userInfo) {
const {
username, password, code } = userInfo
return new Promise((resolve, reject) => {
login({
username: username.trim(), password: password, code: code }).then(response => {
// 验证登录逻辑
console.info('data', response)
if(response.code !== 0){
Message({
message: response.msg,
type: 'error',
duration: 5 * 1000
})
reject({
})
}
let token = response.data.token;
commit('SET_TOKEN', token)
setToken(token)
resolve()
}).catch(error => {
console.info('登录异常')
reject(error)
})
console.info('完成请求登录')
})
},
getInfo({
commit, state }) {
return new Promise((resolve, reject) => {
//此方法是login登陆成功后执行用写死的数据代替返回值,注意框架结构!
getInfo(state.token).then(response => {
console.info('请求数据', response)
const data = {
roles: ['admin'],
introduction: 'I am a super administrator',
avatar: response.data.avatar,
name: response.data.name,
isSuperAdmin: response.data.isSuperAdmin, // 后端返回的 当前用户是否为超级管理员
menu: response.data.menu // 后端返回的 当前用户 菜单JSON数组
}
if (!data) {
reject('Verification failed, please Login again.')
}
const {
roles, name, avatar, introduction, isSuperAdmin, menu} = data
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
// 把 是否为超级管理员保存到 vuex中
commit('SET_IS_SUPER', isSuperAdmin)
// 把 菜单JSON数组 保存到 vuex中
commit('SET_MENU', menu)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({
commit, state, dispatch }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resetRouter()
// reset visited views and cached views
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
dispatch('tagsView/delAllViews', null, {
root: true })
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({
commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
},
// dynamically modify permissions
async changeRoles({
commit, dispatch }, role) {
const token = role + '-token'
commit('SET_TOKEN', token)
setToken(token)
const {
roles } = await dispatch('getInfo')
resetRouter()
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, {
root: true })
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, {
root: true })
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
2.3 修改 permission.js 权限文件
文件路径: src/store/modules/permission.js
这一步是最后一步,拿到vuex保存的 当前用户菜单JSON数组,实现 动态路由
重点 是 filterAsyncRouter
方法
permission.js
代码
import {
asyncRoutes, constantRoutes } from '@/router'
import store from '@/store'
import Layout from '@/layout'
import request from '@/utils/request'
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
console.info('hasPermission')
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
console.info('filterAsyncRoutes')
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: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({
commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
let isSuper = store.state.user.isSuper
console.info('当前登录用户是否为超级管理员', isSuper)
let userRoutes = []
if (isSuper === false) {
// 不是超级管理员
userRoutes = filterAsyncRouter(store.state.user.menu)
commit('SET_ROUTES', userRoutes)
resolve(userRoutes)
}else{
// 超级管理员,全部权限
userRoutes = accessedRoutes
commit('SET_ROUTES', userRoutes)
resolve(userRoutes)
}
console.info('设置动态菜单权限', userRoutes)
})
}
}
/**
* 获取一个字符串值在指定字符串第n次出现的位置
* @param str 字符串
* @param cha 查找的字符
* @param num 第一次出现, 从0开始
* @returns {*}
*/
function find(str, cha, num) {
var x = str.indexOf(cha)
for (var i = 0; i < num; i++) {
x = str.indexOf(cha, x + 1)
}
return x
}
/**
* 将 用户菜单JSON信息 转换为 router 可识别的路由json信息
* @param t 管理员菜单JSON数组
* @returns {*}
*/
function filterAsyncRouter(t) {
t.filter(index => {
if (index.component === 'Layout') {
index.component = Layout
} else {
// 不是路由菜单,转换对应 vue组件
// @/views/sn-manage/dict/index
let component = index.component
console.info(component)
//
/**
* 截取 示例 @/views/sn-manage/dict/index 截取出 @/viesw/ 以外的字符,因为 拼接会异常
* 新逻辑: 创建页面的时候去掉 @/viesw/
* @type {*|string}
* let test = component.substr(find(component, '/', 1) + 1)
*/
index.component = require(`@/views/${
component}.vue`).default
}
// 递归子菜单
if (index.children && index.children.length) {
index.children = filterAsyncRouter(index.children)
}
return true
})
return t
}
export default {
namespaced: true,
state,
mutations,
actions
}
ok 了