管理不同权限用户的左侧菜单展示以及权限按钮的启用 / 禁用之其中一种解决方案
一、权限管理方案设计
1. 权限模型
推荐采用 RBAC(基于角色的访问控制) 模型,把用户分配到不同角色,角色再关联对应的权限。
-
权限项:是最小的控制单元,像
view_user
、edit_product
这种。 -
角色:由多个权限项组合而成,例如
admin
、editor
、viewer
。 - 用户:和一个或多个角色相对应。
2. 权限数据存储
-
前端存储:登录成功后,从后端获取权限数据,然后存到 Vuex/Pinia 或者 localStorage 中。
-
权限数据结构示例:
javascript
{
user: { id: 1, name: "张三", role: "admin" },
permissions: ["view_user", "edit_user", "view_product", "edit_product"]
}
二、左侧菜单动态展示
1. 菜单配置
创建一个菜单配置文件,把权限和菜单项关联起来。
javascript
// src/config/menu.js
export const menuList = [
{
path: "/dashboard",
name: "Dashboard",
icon: "dashboard",
permission: "view_dashboard" // 访问该菜单所需权限
},
{
path: "/user",
name: "用户管理",
icon: "user",
permission: "view_user",
children: [
{
path: "/user/list",
name: "用户列表",
permission: "view_user"
},
{
path: "/user/add",
name: "添加用户",
permission: "add_user"
}
]
},
// 其他菜单项...
];
2. 菜单组件实现
在组件里依据用户权限过滤菜单项。
vue
<!-- src/components/Sidebar.vue -->
<template>
<div class="sidebar">
<el-menu :default-active="activeMenu" mode="vertical">
<template v-for="item in filteredMenu">
<!-- 一级菜单 -->
<el-menu-item
v-if="!item.children && hasPermission(item.permission)"
:key="item.path"
:index="item.path"
>
<i :class="item.icon"></i>
<span slot="title">{{ item.name }}</span>
</el-menu-item>
<!-- 子菜单 -->
<el-submenu
v-else-if="item.children && hasPermission(item.permission)"
:key="item.path"
:index="item.path"
>
<template slot="title">
<i :class="item.icon"></i>
<span>{{ item.name }}</span>
</template>
<el-menu-item
v-for="child in item.children"
:key="child.path"
:index="child.path"
v-if="hasPermission(child.permission)"
>
{{ child.name }}
</el-menu-item>
</el-submenu>
</template>
</el-menu>
</div>
</template>
<script>
import { mapState } from "vuex";
import { menuList } from "@/config/menu";
export default {
computed: {
...mapState(["permissions"]),
// 过滤后的菜单项
filteredMenu() {
return menuList.filter(item => this.hasPermission(item.permission));
}
},
methods: {
// 权限检查方法
hasPermission(permission) {
// 如果没有设置权限,默认可见
if (!permission) return true;
// 检查用户是否拥有该权限
return this.permissions.includes(permission);
}
}
};
</script>
三、权限按钮的展示与禁用
1. 自定义指令实现
借助自定义指令来控制按钮的显示和禁用状态。
javascript
// src/directives/permission.js
export const permission = {
inserted(el, binding, vnode) {
const { value } = binding;
const permissions = vnode.context.$store.state.permissions;
if (value) {
// 检查是否有该权限
const hasPermission = permissions.includes(value);
if (!hasPermission) {
// 没有权限:隐藏按钮
el.parentNode && el.parentNode.removeChild(el);
// 或者禁用按钮(根据需求选择)
// el.disabled = true;
// el.classList.add('is-disabled');
}
} else {
console.error('需要指定权限标识!');
el.parentNode && el.parentNode.removeChild(el);
}
}
};
2. 全局注册指令
在 main.js
里全局注册这个指令。
javascript
// src/main.js
import Vue from "vue";
import { permission } from "./directives/permission";
Vue.directive("permission", permission);
3. 在组件中使用
vue
<!-- 使用示例 -->
<template>
<div>
<!-- 有权限时显示 -->
<el-button
v-permission="'add_user'"
type="primary"
@click="addUser"
>
添加用户
</el-button>
<!-- 无权限时禁用 -->
<el-button
:disabled="!hasPermission('edit_user')"
type="success"
@click="editUser"
>
编辑用户
</el-button>
</div>
</template>
<script>
export default {
methods: {
hasPermission(permission) {
return this.$store.state.permissions.includes(permission);
}
}
};
</script>
四、路由权限控制
对路由访问权限进行控制,防止用户手动输入 URL 访问受限页面。
javascript
// src/router/index.js
import router from "./router";
import store from "./store";
router.beforeEach((to, from, next) => {
// 获取用户权限
const permissions = store.state.permissions;
// 检查路由是否需要权限
if (to.meta.permission) {
if (permissions.includes(to.meta.permission)) {
next(); // 有权限,放行
} else {
next({ path: "/403" }); // 无权限,跳转到403页面
}
} else {
next(); // 无需权限,直接放行
}
});
五、权限管理流程
- 用户登录:用户输入账号密码登录系统。
- 权限验证:后端验证用户身份,返回用户角色和权限信息。
- 权限存储:前端把权限信息存到 Vuex/Pinia 或者 localStorage 中。
- 菜单渲染:根据用户权限动态渲染左侧菜单。
- 按钮控制:在组件里通过自定义指令或者方法控制按钮的显示和禁用。
- 路由拦截:对路由进行拦截,防止未授权访问。
六、优缺点分析
优点
- 可扩展性强:能够轻松添加新的角色和权限,而不用修改大量代码。
- 维护便捷:权限配置集中管理,降低了维护成本。
- 安全性高:从菜单、按钮、路由三个层面进行权限控制,有效防止越权访问。
缺点
- 初期配置复杂:需要设计合理的权限模型和数据结构。
- 性能影响:在复杂应用中,频繁的权限检查可能会对性能产生一定影响。