后台系统里的角色管理可以设置某个角色对哪些页面有权限,相对应的侧边栏要呼应,我是用vueadminpro这个框架,他里面配置好了动态路由获取和roles权限控制以及根据roles数组拦截路由,但是我把后端接口写到他留好的位置并没有实现,这个都会贴出
两个接口,一个是全部的菜单路由,一个是某个角色的页面权限,返回内容如下
第一个
[
{
"id": 1,
"parent_id": 0,
"level": 1,
"path": "/distribution-generalization",
"component": "Layout",
"name": "分销概括",
"meta_title": "分销概括",
"meta_icon": "apps-line"
},
{
"id": 6,
"parent_id": 1,
"level": 2,
"path": "/generalization",
"component": "Distribution-generalization/index",
"name": "分销概括",
"meta_title": "分销概括",
"meta_icon": null
},
{
"id": 7,
"parent_id": 2,
"level": 2,
"path": "/distributor",
"component": "Distributor-management/index",
"name": "分销员列表",
"meta_title": "分销员列表",
"meta_icon": null
},
{
"id": 8,
"parent_id": 2,
"level": 2,
"path": "/audit",
"component": "Distributor-management/Distributor-audit/index",
"name": "分销员审核",
"meta_title": "分销员审核",
"meta_icon": null
},
{
"id": 9,
"parent_id": 2,
"level": 2,
"path": "/level",
"component": "Distributor-management/Distribution-level/index",
"name": "分销等级",
"meta_title": "分销员等级",
"meta_icon": null
},
]
//第二个
[
{
"id": 1,
"parent_id": 0,
"level": 1,
"name": "分销概括",
"checked": true
},
{
"id": 2,
"parent_id": 0,
"level": 1,
"name": "分销员管理",
"checked": true
},
{
"id": 6,
"parent_id": 2,
"level": 2,
"name": "分销员列表",
"checked": false
},
{
"id": 7,
"parent_id": 2,
"level": 2,
"name": "分销员审核",
"checked": false
},
{
"id": 8,
"parent_id": 2,
"level": 2,
"name": "分销员等级",
"checked": false
},
]
动态路由配置如下
src/store/modules/routes.js——这个setRoutes里配置后端路由,其他的都是框架写好的没有改动
/**
* @description 路由拦截状态管理,目前两种模式:all模式与intelligence模式,其中partialRoutes是菜单暂未使用
*/
import Vue from 'vue'
import { asyncRoutes, constantRoutes, resetRouter } from '@/router'
import { getList } from '@/api/router'
import { convertRouter, filterRoutes } from '@/utils/routes'
import { authentication, rolesControl } from '@/config'
import { isArray } from '@/utils/validate'
import {generaMenu} from './permission.js'
import { translateDataToTree } from '@/utils'
const state = () => ({
routes: [],
activeName: '',
})
const getters = {
routes: (state) => state.routes,
activeName: (state) => state.activeName,
}
const mutations = {
/**
* @description 多模式设置路由
* @param {*} state
* @param {*} routes
*/
setRoutes(state, routes) {
state.routes = routes
},
/**
* @description 修改Meta
* @param {*} state
* @param options
*/
changeMenuMeta(state, options) {
function handleRoutes(routes) {
return routes.map((route) => {
if (route.name === options.name) Object.assign(route.meta, options.meta)
if (route.children && route.children.length)
route.children = handleRoutes(route.children)
return route
})
}
state.routes = handleRoutes(state.routes)
},
/**
* @description 修改 activeName
* @param {*} state
* @param activeName 当前激活菜单
*/
changeActiveName(state, activeName) {
state.activeName = activeName
},
}
const actions = {
/**
* @description 多模式设置路由
* @param {*} { commit }
* @param mode
* @returns
*/
async setRoutes({ commit }, mode = 'none') {
// 默认前端路由
let routes = [...asyncRoutes]
// 设置游客路由关闭路由拦截(不需要可以删除)
const control = mode === 'visit' ? false : rolesControl
// 设置后端路由(不需要可以删除)
if (authentication === 'all') {
// const {
// data: { list },
// } = await getList()
//new
await getList().then((res)=>{
console.log('我是store路由')
list = generaMenu(asyncRoutes,res)
})
if (!isArray(list))
Vue.prototype.$baseMessage(
'路由格式返回有误!',
'error',
'vab-hey-message-error'
)
if (list[list.length - 1].path !== '*')
list.push({ path: '*', redirect: '/404', meta: { hidden: true } })
routes = convertRouter(list)
}
// 根据权限和rolesControl过滤路由
const accessRoutes = filterRoutes([...constantRoutes, ...routes], control)
// 设置菜单所需路由
commit('setRoutes', JSON.parse(JSON.stringify(accessRoutes)))
// 根据可访问路由重置Vue Router
await resetRouter(accessRoutes)
},
/**
* @description 修改Route Meta
* @param {*} { commit }
* @param options
*/
changeMenuMeta({ commit }, options = {}) {
commit('changeMenuMeta', options)
},
/**
* @description 修改 activeName
* @param {*} { commit }
* @param activeName 当前激活菜单
*/
changeActiveName({ commit }, activeName) {
commit('changeActiveName', activeName)
},
}
export default { state, getters, mutations, actions }
//这个是其他页面的方法我复制到这里方便看
export function generaMenu(routes, data) {
const len = data.length
// eslint-disable-next-line no-const-assign
for (let i = 0; i < len; i++) {
/* alert(data[i].path)
alert(data[i].meta.title)*/
const menu = {
path: data[i].path + '',
// component: () => import(`${item.component}`),
name: data[i].name + '',
component: Layout,
// alwaysShow: data[i].alwaysShow,
// redirect: data[i].redirect + '',
// hidden: true,
meta: { title: data[i].meta_title + '', icon: data[i].meta_icon },
children: []
}
// alert('data[i].children' + data[i].children)
if (data[i].children) {
const children = data[i].children
for (let j = 0; j < children.length; j++) {
const viewPath = children[j].component
const childrenList = {
path: children[j].path + '',
component: (resolve) => require([`@/views/${viewPath}`], resolve),
name: children[j].name + '',
// hidden: true,
// component: () => import(`@/views/${viewPath}`),
meta: { title: children[j].meta_title + '', icon: children[j].meta_icon }
}
menu.children.push(childrenList)
}
}
routes.push(menu)
}
}
src/utils/routes.js
src/store/modules/user.js
/**
* @description 登录、获取用户信息、退出登录、清除token逻辑,不建议修改
*/
/**
* @description 获取用户信息接口 这个接口非常非常重要,如果没有明确底层前逻辑禁止修改此方法,错误的修改可能造成整个框架无法正常使用
* @param {*} { commit, dispatch, state }
* @returns
*/
async getUserInfo({
commit,
dispatch
}) {
const {
data: {
username,
avatar,
roles,
permissions
},
} = await getUserInfo()
/**
* 检验返回数据是否正常,无对应参数,将使用默认用户名,头像,Roles和Permissions
* username {String}
* avatar {String}
* roles {List}
* ability {List}
*/
if (
(username && !isString(username)) ||
(avatar && !isString(avatar)) ||
(roles && !isArray(roles)) ||
(permissions && !isArray(permissions))
) {
const err = 'getUserInfo核心接口异常,请检查返回JSON格式是否正确'
Vue.prototype.$baseMessage(err, 'error', 'vab-hey-message-error')
throw err
} else {
// 如不使用username用户名,可删除以下代码
if (username) commit('setUsername', username)
// 如不使用avatar头像,可删除以下代码
if (avatar) commit('setAvatar', avatar)
// 如不使用roles权限控制,可删除以下代码
if (roles) dispatch('acl/setRole', roles, {
root: true
})
// 如不使用permissions权限控制,可删除以下代码
if (permissions)
dispatch('acl/setPermission', permissions, {
root: true
})
}
},
export default {
state,
getters,
mutations,
actions
}
mock/controller/user.js
src/vab/plugins/permissions.js
/**
* @description 路由守卫,目前两种模式:all模式与intelligence模式
*/
import router from '@/router'
import store from '@/store'
import VabProgress from 'nprogress'
import 'nprogress/nprogress.css'
import getPageTitle from '@/utils/pageTitle'
import {
toLoginRoute
} from '@/utils/routes'
import {
authentication,
loginInterception,
routesWhiteList,
supportVisit,
} from '@/config'
VabProgress.configure({
easing: 'ease',
speed: 500,
trickleSpeed: 200,
showSpinner: false,
})
router.beforeEach(async (to, from, next) => {
const {
showProgressBar
} = store.getters['settings/theme']
if (showProgressBar) VabProgress.start()
let hasToken = store.getters['user/token']
// console.log('permissionToken',hasToken)
if (!loginInterception) hasToken = true
if (hasToken) {
if (store.getters['routes/routes'].length) {
// 禁止已登录用户返回登录页
if (to.path === '/login') {
next({
path: '/'
})
if (showProgressBar) VabProgress.done()
} else next()
// // 检查用户是否已登录,如果已登录则重定向到主页
//获取原型对象上的push函数
// const originalPush = VueRouter.prototype.push
// //修改原型对象中的push方法
// VueRouter.prototype.push = function push(location) {
// return originalPush.call(this, location).catch(err => err)
// }
// if (!to.meta.isPublic && !localStorage.token){
// return next('/login')
// }
// next()
} else {
try {
if (loginInterception) await store.dispatch('user/getUserInfo')
// config/setting.config.js loginInterception为false(关闭登录拦截时)时,创建虚拟角色
else await store.dispatch('user/setVirtualRoles')
// 根据路由模式获取路由并根据权限过滤
await store.dispatch('routes/setRoutes', authentication)
next({
...to,
replace: true
})
} catch (err) {
console.error('vue-admin-beautiful错误拦截:', err)
await store.dispatch('user/resetAll')
next(toLoginRoute(to.path))
}
}
} else {
console.log('没有token',hasToken)
if (routesWhiteList.includes(to.path)) {
// 设置游客路由(不需要可以删除)
if (supportVisit && !store.getters['routes/routes'].length) {
await store.dispatch('routes/setRoutes', 'visit')
next({
...to,
replace: true
})
} else next()
} else next(toLoginRoute(to.path))
}
})
router.afterEach((to) => {
document.title = getPageTitle(to.meta.title)
if (VabProgress.status) VabProgress.done()
})
页面渲染部分
<template>
<el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
<el-form ref="form" label-width="80px" :model="form" :rules="rules">
<el-form-item label="角色码" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="菜单">
<div class="vab-tree-border">
<el-table border :data="list" ref="table" row-key="id" @selection-change="setTabelSelected">
<el-table-column align="center" show-overflow-tooltip type="selection"
:reserve-selection="true" />
<el-table-column align="center" label="序号" width="55">
<template #default="{ $index }">
{{ $index + 1 }}
</template>
</el-table-column>
<el-table-column align="center" label="角色菜单" prop="name" show-overflow-tooltip />
</el-table>
<!-- <el-tree ref="tree" :data="list" :default-checked-keys="[
'/',
'/distribution-generalization',
'/distributors',
'/business-card-management',
'/business-card-management',
'/withdrawal-management',
'/vab',
'/other',
'/mall',
'/setting',
'/error',
]" :default-expanded-keys="[]" node-key="path" show-checkbox> -->
<!-- <template #default="{ data }">
<span>{{ data.name }}</span>
<span>{{ data.meta.title }}</span>
</template> -->
<!-- </el-tree> -->
</div>
</el-form-item>
<!-- <el-form-item label="按钮权限">
<el-checkbox-group v-model="form.btnRolesCheckedList">
<el-checkbox v-for="item in btnRoles" :key="item.value" :label="item.value">
{{ item.lable }}
</el-checkbox>
</el-checkbox-group>
</el-form-item> -->
</el-form>
<template #footer>
<el-button @click="close">取 消</el-button>
<el-button type="primary" @click="save">确 定</el-button>
</template>
</el-dialog>
</template>
<script>
import {
doEdit,
getListMenus,
getAllList,
doAdd
} from '@/api/roleManagement'
import {
getList
} from '@/api/router'
export default {
name: 'RoleManagementEdit',
data() {
return {
form: {
btnRolesCheckedList: [],
},
rules: {
name: [{
required: true,
trigger: 'blur',
message: '请输入角色码'
}],
},
title: '',
dialogFormVisible: false,
list: [],
ListMenus: [],
// 存放选中的数据
selectedList: [],
/* btnRoles demo */
btnRoles: [{
lable: '读',
value: 'read:system',
},
{
lable: '写',
value: 'write:system',
},
{
lable: '删',
value: 'delete:system',
},
],
}
},
created() {
// this.fetchData()
},
methods: {
showEdit(row) {
// this.selectedList = []
if (!row) {
this.title = '添加'
this.form = Object.assign({}, row)
this.fetchData()
} else {
this.title = '编辑'
this.form = Object.assign({}, row)
this.fetchData()
}
this.dialogFormVisible = true
},
close() {
this.selectedList = []
this.$refs['form'].resetFields() //重置表单
this.form = this.$options.data().form
this.dialogFormVisible = false
},
//获取数据
fetchData() {
const params = "id=" + this.form.id
getListMenus(params).then((res) => {
this.list = res
//选中初始回显
this.list.forEach(item => {
this.$refs.table.clearSelection(); // 清除之前的选中状态
if (item.checked == true) {
// this.selectedList.push(item)
// this.$refs.table.toggleRowSelection(item);
this.selectedList = this.list.reduce((accumulator, item) => {
if (item.checked == true && !accumulator.find(selectedItem =>
selectedItem.id === item.id)) {
accumulator.push(item);
}
return accumulator;
}, []);
}
})
this.selectedList.forEach(item => {
this.$refs.table.toggleRowSelection(item);
});
if (this.selectedList !== 0) {
this.test()
}
});
},
test() {
getAllList().then((res) => {
// 使用filter方法筛选出包含指定id的对象
const filteredObjects = res.filter(obj1 => {
return this.selectedList.some(obj2 => obj2.id === obj1.id);
});
// 输出筛选后的对象数组
console.log(this.selectedList)
console.log('filteredObjects');
console.log(filteredObjects);
})
},
// rows 将存放选中的数据( selectedList )传给setTabelSelected方法
setTabelSelected(row) {
// 检查当前行是否已经在selectedList中
// console.log(this.selectedList)
const index = this.selectedList.findIndex(item => item.id === row.id);
const obj = this.selectedList.find(item => item.id === row.id);
if (index === -1) {
// 如果不存在,添加到selectedList中
this.selectedList = row;
} else {
// 如果存在,从selectedList中移除
this.selectedList.splice(index, 1);
}
// this.ListMenus=row
// console.log('this.selectedList')
// console.log(this.selectedList)
},
save() {
this.$refs['form'].validate(async (valid) => {
if (valid && this.selectedList.length !== 0) {
const params = this.form.id
// 使用map方法从每个对象中提取id,并创建一个新数组
const idsExtracted = this.selectedList.map(item => item.id);
this.form.menu_ids = idsExtracted
console.log('this.form提交')
console.log(this.form)
if (this.title == '编辑') {
doEdit(params, this.form).then((res) => {
console.log('编辑')
this.$emit('fetch-data')
})
}
if (this.title == '添加') {
doAdd(this.form).then((res) => {
console.log('添加')
this.$emit('fetch-data')
})
}
this.close()
}
})
},
},
}
</script>
<style lang="scss" scoped>
.vab-tree-border {
height: 200px;
padding: $base-padding;
overflow-y: auto;
border: 1px solid #dcdfe6;
border-radius: $base-border-radius;
}
</style>