权限:一个系统,要设计不同的用户角色,超级管理员,普通管理员、... 具有的权限不一样(看见的不一样,能操作的权限不一样)。
举个栗子: 设计一个医院的管理系统,有哪些角色: 护士 医生 主任医生 院长 ..., 这些用户,登录的,看到的东西一样么?(不一样),具有的操作权限一样吗?(不一样)
注意:
权限可以由
后端计算
,前端只要发送ajax,拿到数据,渲染就好了。也可以由
前
端计算
, 整个权限的计算过程,在前端实现。
1.1 登录成功,后端返回用户角色
登录成功,后端返回了用户角色,我们要把当前用户的角色,存入本地。之后,我们需要使用这个角色,把权限计算出来。
local.set("role", role); // 存用户角色 (登录成功的接口里面 存入本地)
1.2 角色权限划分
-
超级管理员: super
-
所有
-
-
普通管理员: normal
-
后台首页
-
订单管理
-
商品管理
-
商品列表
-
商品分类
-
-
账号管理
-
修改密码
-
个人中心
-
-
1.3 配置路由
把权限变成可以配置的通用的方案。
1.3.1 路由一分为三
-
静态路由(默认都可以访问的)
/* *********** 静态路由 *********** */
const routes = [
// 登录
{
path: '/login',
component: Login
},
// 后台首页
{
path: '/',
component: Layout,
redirect: '/home',
meta: { path: "/home", title: "后台首页" },
children: [
{
path: '/home',
component: () => import('@/views/home/Home.vue')
}
]
},
]
-
动态路由(哪个角色,能够访问什么路由,需要计算出来)
在meta中配置权限: 规则是:如果不配置,默认都可以看见, 如果配置了的:就是roles的数组中有这个角色,这个角色就可以访问。
/* ********* 动态路由 ********* */
const dynamicRoutes = [
// 账号管理
{
path: '/account',
component: Layout,
redirect: '/account/account-list',
meta: { path: "/account", title: "账号管理" },
children: [
{
path: '/account/account-list',
component: () => import('@/views/account/AccountList.vue'),
meta: { path: "/account/account-list", title: "账号列表", roles: ['super'] },
},
{
path: '/account/account-add',
component: () => import('@/views/account/AccountAdd.vue'),
meta: { path: "/account/account-add", title: "账号添加", roles: ['super'] },
},
{
path: '/account/password-modify',
component: () => import('@/views/account/PasswordModify.vue'),
meta: { path: "/account/password-modify", title: "修改密码" },
},
{
path: '/account/personal',
component: () => import('@/views/account/Personal.vue'),
meta: { path: "/account/personal", title: "个人中心" },
}
]
},
// 商品管理
{
path: '/goods',
component: Layout,
redirect: '/goods/goods-list',
meta: { path: "/goods", title: "商品管理" },
children: [
{
path: '/goods/goods-list',
component: () => import('@/views/goods/GoodsList.vue'),
meta: { path: "/goods/goods-list", title: "商品列表" },
},
{
path: '/goods/goods-add',
component: () => import('@/views/goods/GoodsAdd.vue'),
meta: { path: "/goods/goods-add", title: "商品添加", roles: ['super'] },
},
{
path: '/goods/goods-cate',
component: () => import('@/views/goods/GoodsCate.vue'),
meta: { path: "/goods/goods-cate", title: "商品分类" },
},
]
},
// 订单管理
{
path: '/order',
component: Layout,
meta: { path: "/order", title: "订单管理" },
children: [
{
path: '',
component: () => import('@/views/order/Order.vue')
},
]
},
// 店铺管理
{
path: '/shop',
component: Layout,
meta: { path: "/shop", title: "店铺管理", roles: ['super'] },
children: [
{
path: '',
component: () => import('@/views/shop/Shop.vue')
},
]
},
// 销售统计
{
path: '/total',
component: Layout,
redirect: '/total/goods-total',
meta: { path: "/total", title: "销售统计", roles: ['super'] },
children: [
{
path: '/total/goods-total',
component: () => import('@/views/total/GoodsTotal.vue'),
meta: { path: "/total/goods-total", title: "商品统计" },
},
{
path: '/total/order-total',
component: () => import('@/views/total/OrderTotal.vue'),
meta: { path: "/total/order-total", title: "订单统计" },
},
]
},
]
- 重定向404路由
/* ********* 重定向404 ********* */
const errorRoutes = [
// 重定向404
{
path: '*',
redirect: '/error404'
},
{
path: '/error404',
component: () => import('@/views/error/Error404.vue')
}
]
1.4 写3个函数,计算有权限访问的路由
1.4.1 写在 router/indexjs中
/* 判断是否有权限 */
const hasPermissioin = (router, role) => {
if (router.meta && router.meta.roles) {
return router.meta.roles.includes(role)
} else {
return true; // 没有配置roles(默认就是可以访问)
}
}
/* 计算出哪些路由是可以访问的 */
/* 计算出哪些路由是可以访问的 */
const calcPermisstionRoutes = (dynamicRoutes, role) => {
// 把有权限的动态路由 计算出来
let temp = dynamicRoutes.filter(v => {
// 如果有权限 就返回true 把这个单个路由对象 放入temp
if (hasPermissioin(v, role)) {
if (v.children && v.children.length) {
// 递归算儿子的权限
v.children = calcPermisstionRoutes(v.children, role)
}
return true;
} else {
// 否则 就是没有权限 就返回false 不放入temp
return false;
}
})
return temp
}
/* 计算出有权限访问的路由 并且 动态添加路由 让算出来的路由可以访问 */
/* 计算出有权限访问的路由 并且 动态添加路由 让算出来的路由可以访问 */
export const createRoutes = () => {
let role = local.get('role') // 取出本地当前用户角色
// 如果没有角色 直接return 不走后面。
if (!role) return;
// 调用一个函数,把能够访问的路由计算出来 传入:两个参数: 所有动态路由 和 当前角色
let accessRoutes = calcPermisstionRoutes(dynamicRoutes, role)
// 把算出来的路由 重新动态的添加到路由实例中 然后这些路由 就可以访问了。
router.addRoutes([...routes, ...accessRoutes, ...errorRoutes])
}
1.4.2 调用两次
-
登录成功,就要调用(在角色存入之后:计算过程需要根据角色来,在跳转之前: 跳转到home之前,就算好了,到home之后,直接看到的就是计算结果了)
// 调用的地方: 存入了角色之后 跳转之前
createRoutes();
-
路由中也要调用一次(防止刷新)
createRoutes();
1.5 计算菜单
/* 计算菜单 */
const calcMenus = (routes) => {
return routes.filter(v => v.isMenu)
}
1.6 解决切换账号bug
只要去登录就刷新一次
# RightHeader.vue
// 刷新(所有代码重置 路由也会清空 重新来 解决切换不同角色账号登录的bug)
window.location.reload();
# router/index.js
// 刷新 解决切换角色的bug
if (from.path !== '/' && to.path === '/login') {
window.location.reload()
}
1.7 动态渲染菜单
-
算出菜单,存入本地
// 根据权限路由计算左侧菜单 let menus = calcMenus([...routes, ...accessRoutes]) // 存入本地 local.set('menus', menus)
-
在左侧导航组件,取出菜单数据,动态渲染
<template>
<div class="left-menu">
<!-- logo -->
<div class="logo-container">
<img width="50" height="50" src="../../assets/imgs/logo.png" alt="logo" />
<span>外卖商家中心</span>
</div>
<!-- 导航 -->
<el-menu
background-color="#304156"
text-color="#eee"
:default-active="$route.path"
unique-opened
router
>
<template v-for="menu in menus">
<!-- 没有儿子的 -->
<el-menu-item
v-if="menu.children && menu.children.length === 1"
:index="menu.meta.path"
:key="menu.path"
>
<i class="iconfont icon-home"></i>
<span slot="title">{{ menu.meta.title }}</span>
</el-menu-item>
<!-- 有儿子的 -->
<el-submenu v-else :index="menu.path" :key="menu.path">
<template slot="title">
<i class="iconfont icon-goods"></i>
<span>{{ menu.meta.title }}</span>
</template>
<el-menu-item
v-for="v in menu.children"
:key="v.path"
:index="v.meta.path"
>{{ v.meta.title }}</el-menu-item
>
</el-submenu>
</template>
</el-menu>
</div>
</template>
<script>
import local from "@/utils/local";
export default {
data() {
return {
menus: [],
};
},
created() {
this.menus = local.get("menus");
console.log(this.menus);
},
};
</script>
<style lang="less" scoped>
.left-menu {
width: 200px;
background-color: #304156;
.logo-container {
height: 60px;
display: flex;
justify-content: center;
align-items: center;
color: #eee;
}
.el-menu {
border-right: 0px;
.el-menu-item {
.iconfont {
margin-right: 10px;
}
}
.el-submenu {
.el-submenu__title {
.iconfont {
margin-right: 10px;
}
}
.el-menu-item {
background-color: #202f3f !important;
&:hover {
color: #fff !important;
background-color: rgba(0, 0, 0, 0.5) !important;
}
}
}
}
}
</style>