基于Vue+element-ui实现后台管理系统 菜单权限控制+按钮权限控制
路由配置import Vue from "vue";import Router from "vue-router";Vue.use(Router);/* Layout */import Layout from "@/layout";/*** Note: sub-menu only appear when route children.length >= 1* Detail see: http
·
路由配置
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
/* Layout */
import Layout from "@/layout";
// 不需要后台返回的公共路由
export const constantRoutes = [
{
path: "/login",
component: () => import("@/views/login/index"),
hidden: true
},
{
path: "/404",
component: () => import("@/views/404"),
hidden: true
},
{
path: "/",
component: () => import("@/layout"),
redirect: "/dashboard",
children: [
{
path: "dashboard",
name: "Dashboard",
component: () => import("@/views/dashboard/index"),
meta: { title: "首页", icon: "dashboard" }
}
]
},
{
path: "/clue",
component: () => import("@/layout"),
redirect: "/clue/private",
name: "Clue",
hidden: true,
meta: {
title: "线索管理",
icon: "example"
},
children: [
{
path: "private/add",
name: "PrivateAdd",
hidden: true,
component: () => import("@/views/clue/private-sea/add"),
meta: { title: "单条新增", icon: "table" }
},
{
path: "turnCustomer",
name: "turnCustomer",
hidden: true,
component: () => import("@/views/clue/private-sea/turn-customer"),
meta: { title: "角色新增", icon: "table" }
},
{
path: "clueDetails",
name: "clueDetails",
hidden: true,
component: () => import("@/views/clue/private-sea/clue-details"),
meta: {
title: "查看线索",
icon: "table"
}
},
{
path: "followClue",
name: "followClue",
hidden: true,
component: () => import("@/views/clue/private-sea/follow-clue"),
meta: { title: "跟进线索", icon: "table" }
}
]
},
{
path: "/productManagement",
component: () => import("@/layout"),
redirect: "/productManagement/list",
name: "ProductManagement",
hidden: true,
meta: { title: "商品管理", icon: "table" },
children: [
{
path: "add",
name: "ProductManagementAdd",
hidden: true,
component: () => import("@/views/product/add"),
meta: { title: "商品新增", icon: "table" }
},
{
path: "edit",
name: "ProductManagementEdit",
hidden: true,
component: () => import("@/views/product/edit"),
meta: { title: "商品编辑", icon: "table" }
}
]
},
{
path: "/authority",
component: () => import("@/layout"),
redirect: "/authority/roleManage",
name: "Authority",
meta: { title: "权限管理", icon: "eye-open" },
hidden: true,
children: [
{
path: "roleManage/add",
name: "RoleManageAdd",
hidden: true,
component: () => import("@/views/authority/role-manage/add-or-edit"),
meta: { title: "角色新增", icon: "table" }
},
{
path: "roleManage/edit",
name: "RoleManageEdit",
hidden: true,
component: () => import("@/views/authority/role-manage/add-or-edit"),
meta: { title: "角色编辑", icon: "table" }
},
{
path: "user/add",
name: "UserAdd",
hidden: true,
component: () => import("@/views/authority/user-manage/add-or-edit"),
meta: { title: "用户新增", icon: "table" }
},
{
path: "user/edit",
name: "UserEdit",
hidden: true,
component: () => import("@/views/authority/user-manage/add-or-edit"),
meta: { title: "用户编辑", icon: "table" }
}
]
}
// {
// path: "/performance",
// component: () => import("@/layout"),
// redirect: "/performance/assess",
// name: "Performance",
// meta: { title: "绩效管理", icon: "link" },
// hidden: true,
// children: [
// ]
// }
// 404 page must be placed at the end !!!
// { path: "*", redirect: "/404", hidden: true }
];
// 需要后台返回的异步路由
export const asyncRoutes = [
{
path: "/productManagement",
component: () => import("@/layout"),
redirect: "/productManagement/list",
name: "ProductManagement",
meta: { title: "商品管理", icon: "link" },
children: [
{
path: "list",
name: "ProductList",
component: () => import("@/views/product/index"),
meta: { title: "商品列表", icon: "table", btnPermissions: [1, 2, 3] }
},
{
path: "parts",
name: "ProductParts",
component: () => import("@/views/product/parts"),
meta: { title: "配件管理", icon: "table" }
}
]
},
{
path: "/clue",
component: () => import("@/layout"),
redirect: "/clue/private",
name: "Clue",
meta: { title: "线索管理", icon: "example" },
children: [
{
path: "private",
name: "Private",
component: () => import("@/views/clue/private-sea/index"),
meta: {
title: "私海管理",
icon: "table",
keepAlive: true, //该字段表示该页面需要缓存
isBack: false //判断是否是返回
}
},
{
path: "public",
name: "Public",
component: () => import("@/views/clue/public-sea/index"),
meta: { title: "公海管理", icon: "tree" }
}
]
},
{
path: "/authority",
component: () => import("@/layout"),
redirect: "/authority/roleManage",
name: "Authority",
meta: { title: "权限管理", icon: "eye-open" },
children: [
{
path: "roleManage",
name: "RoleManage",
component: () => import("@/views/authority/role-manage/index"),
meta: { title: "角色管理", icon: "table" }
},
{
path: "user",
name: "UserManage",
component: () => import("@/views/authority/user-manage/index"),
meta: { title: "用户管理", icon: "tree" }
}
]
},
{
path: "/performance",
component: () => import("@/layout"),
redirect: "/performance/assess",
name: "Performance",
meta: { title: "绩效管理", icon: "link" },
children: [
{
path: "assess",
name: "Assess",
component: () => import("@/views/performance/assess"),
meta: { title: "绩效考核", icon: "form" }
},
{
path: "mine",
name: "Mine",
component: () => import("@/views/performance/mine"),
meta: { title: "绩效查询", icon: "nested" }
}
]
},
// 404 page must be placed at the end !!!
{ path: "*", redirect: "/404", hidden: true }
];
const createRouter = () =>
new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
});
const 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 default router;
这里可能很多朋友会出现这样的问题 我在配置的时候同样踩了坑
vue 动态路由 addRoutes 刷新页面404(空白)问题
1:静态路由中注释掉{ path: '*', redirect: '/404', hidden: true }(如果有)
2:放在动态路由中添加
登录页面
<el-button
type="primary"
style="width:100%;margin-bottom:30px;"
@click.native.prevent="handleLogin">登录</el-button>
data() {
return {
loading:false,
loginForm: {
username: '',
password: ''
},
}
},
methods: {
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store
.dispatch('user/login', this.loginForm) //通过vuex 登录获取动态路由配置
.then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
this.$store
.dispatch('user/getSettings')
.then(result => {})
.catch(err => {})
})
.catch(() => {
this.loading = false
})
} else {
return false
}
})
}
}
}
login({ commit }, userInfo) {
const { username, password } = userInfo;
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password })
.then(response => {
const { data } = response;
commit("SET_TOKEN", data); // 存储token
setToken(data);
resolve();
})
.catch(error => {
reject(error);
});
});
},
// 获取动态路由菜单
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getUserInfo(state.token)
.then(response => {
const { data } = response;
if (!data) {
reject("Verification failed, please Login again.");
}
localStorage.setItem(
"adminUserListBoList",
JSON.stringify(data.adminUserListBoList)
);
const { sonMenu: roles, username, avatar } = data;
// const { roles, username, avatar } = 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", username);
commit("SET_AVATAR", avatar);
resolve(data);
})
.catch(error => {
reject(error);
});
});
},
permission.js 文件 store中的
import { asyncRoutes, constantRoutes } from "@/router";
console.log("async routes: --", asyncRoutes);
console.log("constantRoutes : --", constantRoutes);
// /**
// *
// * Use meta.role to determine if the current user has permission
// * @param roles
// * @param route
// */
// function hasPermission(roles, route) {
// if (route.meta && route.meta.roles) {
// return roles.some(role => route.meta.roles.includes(role))
// } else {
// return true
// }
// }
// export function mapAsyncRoutes(routes) {
// return routes.map(route => {
// const tmp = { ...route }
// if (typeof tmp.component === 'string') {
// const path = tmp.component
// tmp.component = () => import(`@/${path}`)
// }
// if (tmp.children && tmp.children.length) {
// tmp.children = mapAsyncRoutes(tmp.children)
// }
// return tmp
// })
// }
// /**
// * Filter asynchronous routing tables by recursion
// * @param routes asyncRoutes
// * @param roles
// */
// export function filterAsyncRoutes(routes, roles) {
//
//
// const res = []
// routes.forEach(route => {
// const tmp = { ...route }
// if (hasPermission(roles, tmp)) {
// if (tmp.children) {
// tmp.children = filterAsyncRoutes(tmp.children, roles)
// }
// res.push(tmp)
// }
// })
// return res
// }
/**
*
* @param {Array} userRouter 后台返回的用户权限json
* @param {Array} allRouter 前端配置好的所有动态路由的集合
* @return {Array} realRoutes 过滤后的路由
*/
export function recursionRouter(userRouter = [], allRouter = []) {
var realRoutes = [];
allRouter.forEach((v, i) => {
userRouter.forEach((item, index) => {
if (item.ename == v.name) {
if (item.children && item.children.length > 0) {
v.children = recursionRouter(item.children, v.children);
}
// v.meta={...v.meta,...{btnPermissions:[1,2,3]}} // 这里可以通过后台返回数据做按钮控制权限 btnPermissions:[1,2,3] 为后台返回的数组
realRoutes.push(v);
}
});
});
// console.log(realRoutes);
return realRoutes;
}
const state = {
routes: [],
addRoutes: []
};
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes;
state.routes = constantRoutes.concat(routes);
console.log("all routes====>", state.routes);
}
};
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes;
// if (roles.includes('admin')) {
// accessedRoutes = asyncRoutes || []
// } else {
// accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
// }
// 拿到后台的路由表和前端本地存的路由表 映射出来有权限的路由
accessedRoutes = recursionRouter(roles, asyncRoutes);
console.log('哈哈'+JSON.stringify( roles));
commit("SET_ROUTES", accessedRoutes);
resolve(accessedRoutes);
});
}
};
export default {
namespaced: true,
state,
mutations,
actions
};
同main.js级创建 permission.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, filterRouter } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') { // 路由拦截
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const { sonMenu: roles } = await store.dispatch('user/getInfo') // 通过vuex获取动态路由菜单
// const { roles } = await store.dispatch('user/getInfo')
// const { roles } = store.getters.roles
//
// generate accessible routes map based on roles
const accessRoutes = await store.dispatch('permission/generateRoutes', roles) // 通过vuex创建动态路由
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
// next({ path: '/' })
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
// next(`/`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
main.js
import Vue from 'vue'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n
import '@/styles/index.scss' // global css
import App from './App'
import store from './store'
import router from './router'
import '@/icons' // icon
import '@/permission' // permission control
/**
* If you don't want to use mock-server
* you want to use MockJs for mock api
* you can execute: mockXHR()
*
* Currently MockJs will be used in the production environment,
* please remove it before going online! ! !
*/
// import { mockXHR } from '../mock'
// if (process.env.NODE_ENV === 'production') {
// mockXHR()
// }
// set ElementUI lang to EN
Vue.use(ElementUI, { locale })
import * as filters from './filters' // 引入全局filters
// 注册全局 utility filters
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
Vue.config.productionTip = false
//后台返回菜单的同时一起返回按钮权限的数组 如:[1,2,3,4] //对应增、删、查、改
//前端处理数据把按钮权限放入路由的 meta 中。
//创建自定指令v-permission,进行判断对比。
// 使用 <el-button v-permission="[1]">删除 </el-button>
Vue.directive('permission', {
inserted(el, binding, vnode) {
const {value} = binding
const roles = vnode.context.$route.meta.btnPermissions || [] //获取mate中的权限
console.log(value,roles);
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`需要的权限, v-permission="[1,2,3,4]"`)
}
},
})
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
…个别地方省略了 有不懂得 可以评论区留言
更多推荐
已为社区贡献2条内容
所有评论(0)