VUE后台管理系统实现权限管理

10 篇文章 2 订阅
6 篇文章 0 订阅

一、前言

不管你是开发一款APP或者是微信小程序等其他的应用程序,都需要搭配一个后台管理系统,那么在后台管理系统中有一个比较重要的模块那就是用户权限管理了,接下来我们来实现这个功能。

二、技术栈

前端:前端我用的是VUE3Element-PlusVitePinia等,在这里就不一一列出来了
后端node.js、接口我是用node写的(不出意外的话,讲不到后端的接口)
数据库MySQL

介绍就到这里吧!

三、数据库

首先是我们的数据库,我会尽量写的详细。

1、用户表

我这里就只列出重要的字段了
可以根据自己的需要扩展其他字段如:手机号、性别、邮箱、创建时间等等

字段名类型必填注释
idint用户ID
usernamevarchar用户名称
passwordvarchar用户密码
roleIdsvarchar角色ID(可多选)
isFullFuncint是否全功能(1:是、2:否)默认:2
2、角色表
字段名类型必填注释
idint角色ID
roleNamevarchar角色名称
roleRemarkvarchar角色备注
menusvarchar菜单ID
menuPowersvarchar菜单权限ID
roleStatusint角色状态(1:开启、2:禁用)默认:1
3、菜单表
字段名类型必填注释
idint菜单ID
menuNamevarchar菜单名称
menuPathvarchar菜单路径
menuTypeint菜单类型(1:目录、2:页面)
menuStatusint菜单类型(1:开启、2:禁用)默认:1
parentIdint上级菜单ID
parentNamevarchar上级菜单名称
4、菜单权限表
字段名类型必填注释
idint菜单权限ID
menuPowerNamevarchar菜单权限名称
menuPowerMarkvarchar菜单权限标识
menuIdint所属菜单
menuNameint所属菜单名称

流程图
在这里插入图片描述
图片有点潦草了
大致就是角色授权我们的菜单按钮的权限,然后用户可以绑定多个角色,嗯…对就是这样。

前端代码

在这里插入图片描述
首先是四个表单的增删改查,这些代码我就不贴出来了,没用。

核心代码

在我们项目中的permission.ts也就是你写路由全局守卫的地方

import router from '@/router';
import usePermissionStore from '@/store/modules/permission';
import { useUserStoreHook } from '@/store/modules/user'; // 注意自己路径

const whiteList = ['/login']; // 白名单路由
const userStore = useUserStoreHook()
const permissionStore = usePermissionStore()
// 路由全局守卫
router.beforeEach(async (to, from, next) => {
  NProgress.start();// 这个是页面加载时进度条
  if (Cookies.get('Token')) {
    // 登录成功,跳转到首页
    if (to.path === '/login') {
      next({ path: '/' }); // 首页路径
      NProgress.done();
    }else{
      // 确定用户是否已通过getInfo获得其权限角色
      const hasGetUserInfo = userStore.roles.length > 0;
      if (hasGetUserInfo) {
        if (to.matched.length === 0) {
          from.name ? next({ name: from.name as any }) : next('/401');
        } else {
          next();
        }
      } else {
        try {
          let roles: any = [];
          roles = await userStore.getUserInfo() // 返回的是用户授权的菜单列表
          const accessRoutes: any = await permissionStore.generateRoutes(roles); // 通过用户授权的菜单列表过滤路由
          accessRoutes.forEach((route: any) => {
            router.addRoute(route); // 添加路由
          });
          next();
        } catch (error) {
          // 移除 token 并跳转登录页
          await removeToken(); // 这个方法我就不贴出来了,就是移除你登录时存的Token
          next(`/login`); // 跳转登陆页
          NProgress.done();
        }
      }
    }
  }else{
    // 未登录时可以访问白名单页面
    if (whiteList.indexOf(to.path) !== -1) {
      next();
    } else { // 否则跳转登陆页面
      next(`/login`); // 跳转登陆页
      NProgress.done();
    }
  }
});

router.afterEach(() => {
  NProgress.done();
});

store/index.ts

import type { App } from 'vue';
import { createPinia } from "pinia";
const store = createPinia();
// 全局挂载store
export function setupStore(app: App<Element>) {
    app.use(store);
}
export { store };
// 需要在main.ts挂载


// main.ts 直接写这里了
import { setupStore } from '@/store';
const app = createApp(App);
setupStore(app);

store/modules/types.ts

import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
export interface PermissionState {
  routes: RouteRecordRaw[];
  addRoutes: RouteRecordRaw[];
}

export interface UserState {
  roles: string[];
  power: string[];
  isFullFunc: Number;
}

store/modules/user.ts

import { defineStore } from 'pinia';
import { UserState } from './types';
import { store } from '@/store';

const useUserStore = defineStore({
  id:'user',
  state: (): UserState => ({
    roles: [],
    power: [],
    isFullFunc: 2
  }),
  actions:{
    getUserInfo() {
      return new Promise((resolve, reject) => {
        userPower().then(res => { // 请求接口
          if (res.data.isFullFunc == 1) { // 等等 1 代表用户全功能
            this.isFullFunc = res.data.isFullFunc
            resolve(['admin']) // 随便返回个什么,不是空就行
          } else {
            this.roles = res.data.menuData // 菜单权限
            this.power = res.data.menuPowerData // 菜单按钮权限
            resolve(res.data.menuData) // 返回菜单权限
          }
        }).catch(res => {
          reject(res)
        })
      });
    },
  }
})
export default useUserStore;

// 非setup
export function useUserStoreHook() {
  return useUserStore(store);
}

// 这里是接口返回的数据格式
/**
res:{
  data:{
	isFullFunc:1,
	menuData:[
	  {
	  	{
    	  "id": 1,
    	  "menuName": "首页",
    	  "menuPath": "/",
    	  "menuType": 2,
    	  "menuStatus": 1,
    	  "parentId": 0,
    	  "parentName": null,
    	  "createDate": 1682435267868
	    }
	    ...等等
	  }
    ],
    menuPowerData:[
      {
        {
    	  "id": 1,
    	  "menuPowerName": "用户管理 - 新增",
    	  "menuPowerMark": "USER-ADD",
    	  "menuId": 3,
    	  "menuName": "用户管理",
    	  "createDate": 1682472332893
		}
		...等等
      }
    ]
  }
}
*/

store/modules/permission.ts

// 路由处理的逻辑写的不是很好,后续优化了会更新
import { PermissionState } from './types';
import { RouteRecordRaw } from 'vue-router';
import { defineStore } from 'pinia';
import { store } from '@/store';
import { constantRoutes } from '@/router';

const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
  // routes 所有路由
  // roles 授权的路由
  const res: RouteRecordRaw[] = [];
  if (roles[0] == 'admin') { // 表示全功能,添加所有的路由
    routes.forEach(route => {
      const tmp = { ...route } as any;
      res.push(tmp);
    });
  } else {
    routes.forEach((route: any) => {
      // 添加不存在在菜单权限中的路由
      const tmp = { ...route } as any;
      if (tmp.path == '/redirect' || tmp.path == '/login' || tmp.path == '/') {
        res.push(tmp)
      }
      roles.forEach((item: any) => {
        if (item.menuPath != '/') {
          if (route.path === item.menuPath) {
            // 一级路由
            let data = {
              path: route.path,
              component: route.component,
              redirect: route.redirect,
              meta: route.meta,
              children: [],
            }
            // 子路由
            route.children.forEach((tab: any) => {
              roles.forEach((item2: any) => {
                if (item2.menuPath === tab.path) {
                  data.children.push(tab)
                }
              })
            })
            res.push(data)
          }
        }
      })
    });
  }
  return res;
};

const usePermissionStore = defineStore({
  id: 'permission',
  state: (): PermissionState => ({
    routes: [],
    addRoutes: [],
  }),
  actions: {
    setRoutes(routes: RouteRecordRaw[]) {
      this.addRoutes = routes;
      this.routes = routes;
    },

    generateRoutes(roles: string[]) {
      return new Promise((resolve, reject) => {
        if (roles[0] == 'admin') {
          const asyncRoutes = constantRoutes;
          const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
          this.setRoutes(accessedRoutes);
          resolve(accessedRoutes);
        } else {
          const asyncRoutes = constantRoutes;
          const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
          this.setRoutes(accessedRoutes);
          resolve(accessedRoutes);
        }
      });
    },
  },
});

export default usePermissionStore;

// 非setup
export function usePermissionStoreHook() {
  return usePermissionStore(store);
}

到这里我们项目的菜单权限就实现了。
接下来实现我们的按钮权限

创建一个directives.ts文件

import type { App } from "vue";
import { useUserStoreHook } from '@/store/modules/user';

const userStore = useUserStoreHook()

// 判断单个按钮权限
const hasPermission = (userPermission: any) => {
    let permissionList = userStore.$state.power // 获取权限时存的菜单按钮的权限
    return permissionList.some((i: any) => i.menuPowerMark == userPermission)
};

export function setAllDirectives(app: App) {
	// 注册自定义指令
    app.directive('has', {
        mounted(el, binding, vnode, prevVnode) {
            if (userStore.$state.isFullFunc == 1) {
                return true
            } else {
                if (!hasPermission(binding.value)) {
                    el.parentNode.removeChild(el); // 无权限时删除dom元素
                }
            }
        },
    })
}

// 同样需要在main.ts中引入

// main.ts
import { setAllDirectives } from '@/utils/directives' // 导入全局自定义指令
setAllDirectives(app) // 全局注册自定义指令

自定义指令的使用

<el-button type="primary" @click="roleAdd" v-has="'ROLE-ADD'">新增</el-button>
<!-- v-has 直接传我们的按钮标识就好咯 -->

表单就是这样子的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结尾!

代码仅提供逻辑,直接搬代码可能会有问题哦。

谢谢观看~

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
BootDo是高效率,低封装,面向学习型,面向微服的开源JavaEE开发框架。 BootDo是在SpringBoot基础上搭建的一个Java基础开发平台,MyBatis为数据访问层,ApacheShiro为权限授权层,Ehcahe对常用数据进行缓存。 BootDo主要定位于后台管理系统学习交流,已内置后台管理系统的基础功能和高效的代码生成工具,包括:系统权限组件、数据权限组件、数据字典组件、核心工具组件、视图操作组件、工作流组件、代码生成等。前端界面风格采用了结构简单、性能优良、页面美观大气的TwitterBootstrap页面展示框架。采用分层设计、双重验证、提交数据安全编码、密码加密、访问验证、数据权限验证。使用Maven做项目管理,提高项目的易开发性、扩展性。 BootDo目前包括以下四大模块,系统管理(SYS)模块、内容管理(CMS)模块、在线办公(OA)模块、代码生成(GEN)模块。系统管理模块,包括企业组织架构(用户管理、机构管理、区域管理)、菜单管理、角色权限管理、字典管理等功能;内容管理模块,包括内容管理(文章、链接),栏目管理、站点管理、公共留言、文件管理、前端网站展示等功能;在线办公模块,提供简单的请假流程实例;代码生成模块,完成重复的工作。 BootDo提供了常用工具进行封装,包括日志工具、缓存工具、服务器端验证、数据字典、当前组织机构数据(用户、机构、区域)以及其它常用小工具等。另外还提供一个强大的在线代码生成工具。 内置功能: 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 机构管理:配置系统组织机构(公司、部门、小组),树结构展现,可随意调整上下级。 区域管理:系统城市区域模型,如:国家、省市、地市、区县的维护。 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 字典管理:对系统中经常使用的一些较为固定的数据进行维护,如:是否、男女、类别、级别等。 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 工作流引擎:实现业务工单流转、在线流程设计器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值