首页 > 其他分享 >RuoYi-App根据不同角色权限实现功能按钮显隐

RuoYi-App根据不同角色权限实现功能按钮显隐

时间:2024-09-27 16:23:51浏览次数:13  
标签:el const permission 显隐 route RuoYi children state App

文章目录

需求

根据角色不同,移动端显示不同功能,效果如下:
在这里插入图片描述

代码实现

该功能和管理后台不太一样;ruoyi自带的管理后台可以控制每个功能按钮,以及不同角色之间的数据权限;但是移动端不需要,只需要控制某个模块的显隐就行,所以只需要修改前端代码进行控制显隐即可;具体逻辑如下:
1、后台将指定角色功能权限保存到数据库
2、app端登录时查询该用户下所对应角色的功能权限,并保存到缓存中
3、跳转到指定路由时,通过自定义功能组件中的权限值和缓存中的数据进行对比,如果存在则显示,不存在则隐藏

注意

该方法不支持微信小程序,因为微信小程序不支持Vue.directive(‘x’, x) 自定义指令,欢迎打脸,请附上打脸链接。谢谢!!! 下面有适用于小程序以及各个端的使用方法

上代码(不适应小程序)

1、查询后台角色权限,并保存到缓存

store/modules 文件夹下新增 permission.js 文件,代码如下

import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink'

const permission = {
  state: {
    routes: [],
    addRoutes: [],
    defaultRoutes: [],
    topbarRouters: [],
    sidebarRouters: []
  },
  mutations: {
    SET_ROUTES: (state, routes) => {
      state.addRoutes = routes
      state.routes = constantRoutes.concat(routes)
    },
    SET_DEFAULT_ROUTES: (state, routes) => {
      state.defaultRoutes = constantRoutes.concat(routes)
    },
    SET_TOPBAR_ROUTES: (state, routes) => {
      state.topbarRouters = routes
    },
    SET_SIDEBAR_ROUTERS: (state, routes) => {
      state.sidebarRouters = routes
    },
  },
  actions: {
    // 生成路由
    GenerateRoutes({ commit }) {
      return new Promise(resolve => {
        // 向后端请求路由数据
        getRouters().then(res => {
          const sdata = JSON.parse(JSON.stringify(res.data))
          const rdata = JSON.parse(JSON.stringify(res.data))
          const sidebarRoutes = filterAsyncRouter(sdata)
          const rewriteRoutes = filterAsyncRouter(rdata, false, true)
          const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
          rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
          router.addRoutes(asyncRoutes);
          commit('SET_ROUTES', rewriteRoutes)
          commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
          commit('SET_DEFAULT_ROUTES', sidebarRoutes)
          commit('SET_TOPBAR_ROUTES', sidebarRoutes)
          resolve(rewriteRoutes)
        })
      })
    }
  }
}

// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
  return asyncRouterMap.filter(route => {
    if (type && route.children) {
      route.children = filterChildren(route.children)
    }
    if (route.component) {
      // Layout ParentView 组件特殊处理
      if (route.component === 'Layout') {
        route.component = Layout
      } else if (route.component === 'ParentView') {
        route.component = ParentView
      } else if (route.component === 'InnerLink') {
        route.component = InnerLink
      } else {
        route.component = loadView(route.component)
      }
    }
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    } else {
      delete route['children']
      delete route['redirect']
    }
    return true
  })
}

function filterChildren(childrenMap, lastRouter = false) {
  var children = []
  childrenMap.forEach((el, index) => {
    if (el.children && el.children.length) {
      if (el.component === 'ParentView' && !lastRouter) {
        el.children.forEach(c => {
          c.path = el.path + '/' + c.path
          if (c.children && c.children.length) {
            children = children.concat(filterChildren(c.children, c))
            return
          }
          children.push(c)
        })
        return
      }
    }
    if (lastRouter) {
      el.path = lastRouter.path + '/' + el.path
      if (el.children && el.children.length) {
        children = children.concat(filterChildren(el.children, el))
        return
      }
    }
    children = children.concat(el)
  })
  return children
}

// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
  const res = []
  routes.forEach(route => {
    if (route.permissions) {
      if (auth.hasPermiOr(route.permissions)) {
        res.push(route)
      }
    } else if (route.roles) {
      if (auth.hasRoleOr(route.roles)) {
        res.push(route)
      }
    }
  })
  return res
}

export const loadView = (view) => {
  if (process.env.NODE_ENV === 'development') {
    return (resolve) => require([`@/views/${view}`], resolve)
  } else {
    // 使用 import 实现生产环境的路由懒加载
    return () => import(`@/views/${view}`)
  }
}

export default permission

2、获取缓存数据,用于权限比对

修改 store/getters.js文件,代码如下:

const getters = {
	token: state => state.user.token,
	avatar: state => state.user.avatar,
	name: state => state.user.name,
	userId: state => state.user.userId,
	nickName: state => state.user.nickName,
	roles: state => state.user.roles,
	permissions: state => state.user.permissions,

	intoArray: state => state.intoStore.intoArray,
	detailArray: state => state.intoStore.detailArray,

	permission_routes: state => state.permission.routes,
	topbarRouters: state => state.permission.topbarRouters,
	defaultRoutes: state => state.permission.defaultRoutes,
	sidebarRouters: state => state.permission.sidebarRouters,
}
export default getters

3、自定义权限组件

在store文件夹下新增 directive/index.js、directive/permission/hasPermi.js、directive/permission/hasRole.js三个js文件,代码依次如下:

index.js
import hasRole from './permission/hasRole'
import hasPermi from './permission/hasPermi'

const install = function (Vue) {
  Vue.directive('hasRole', hasRole)
  Vue.directive('hasPermi', hasPermi)
}

if (window.Vue) {
  window['hasRole'] = hasRole
  window['hasPermi'] = hasPermi
  Vue.use(install); // eslint-disable-line
}

export default install
hasPermi.js
 /**
 * v-hasPermi 操作权限处理
 * Copyright (c) 2019 hgkc
 */

import store from '@/store'

export default {
  inserted(el, binding, vnode) {
    const { value } = binding
    const all_permission = "*:*:*";
    const permissions = store.getters && store.getters.permissions

    if (value && value instanceof Array && value.length > 0) {
      const permissionFlag = value

      const hasPermissions = permissions.some(permission => {
        return all_permission === permission || permissionFlag.includes(permission)
      })

      if (!hasPermissions) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`请设置操作权限标签值`)
    }
  }
}

hasRole.js
 /**
 * v-hasRole 角色权限处理
 * Copyright (c) 2019 hgkc
 */

import store from '@/store'

export default {
  inserted(el, binding, vnode) {
    const { value } = binding
    const super_admin = "admin";
    const roles = store.getters && store.getters.roles

    if (value && value instanceof Array && value.length > 0) {
      const roleFlag = value

      const hasRole = roles.some(role => {
        return super_admin === role || roleFlag.includes(role)
      })

      if (!hasRole) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error(`请设置角色权限标签值"`)
    }
  }
}

4、引入自定义功能组件

修改main.js文件,代码如下

import Vue from 'vue'
import App from './App'
import store from './store' // store
import plugins from './plugins' // plugins
import './permission' // permission
import directive from './store/directive'
Vue.use(plugins)
import uView from "uview-ui";
Vue.use(uView);
Vue.use(directive);

Vue.config.productionTip = false
Vue.prototype.$store = store

App.mpType = 'app'

const app = new Vue({
  ...App
})


app.$mount()

上代码(适应多端)

利用函数传参的方式去实现该功能

1、查询后台角色权限,并保存到缓存

store/modules 文件夹下新增 permission.js 文件,代码如下

import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink'

const permission = {
  state: {
    routes: [],
    addRoutes: [],
    defaultRoutes: [],
    topbarRouters: [],
    sidebarRouters: []
  },
  mutations: {
    SET_ROUTES: (state, routes) => {
      state.addRoutes = routes
      state.routes = constantRoutes.concat(routes)
    },
    SET_DEFAULT_ROUTES: (state, routes) => {
      state.defaultRoutes = constantRoutes.concat(routes)
    },
    SET_TOPBAR_ROUTES: (state, routes) => {
      state.topbarRouters = routes
    },
    SET_SIDEBAR_ROUTERS: (state, routes) => {
      state.sidebarRouters = routes
    },
  },
  actions: {
    // 生成路由
    GenerateRoutes({ commit }) {
      return new Promise(resolve => {
        // 向后端请求路由数据
        getRouters().then(res => {
          const sdata = JSON.parse(JSON.stringify(res.data))
          const rdata = JSON.parse(JSON.stringify(res.data))
          const sidebarRoutes = filterAsyncRouter(sdata)
          const rewriteRoutes = filterAsyncRouter(rdata, false, true)
          const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
          rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
          router.addRoutes(asyncRoutes);
          commit('SET_ROUTES', rewriteRoutes)
          commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
          commit('SET_DEFAULT_ROUTES', sidebarRoutes)
          commit('SET_TOPBAR_ROUTES', sidebarRoutes)
          resolve(rewriteRoutes)
        })
      })
    }
  }
}

// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
  return asyncRouterMap.filter(route => {
    if (type && route.children) {
      route.children = filterChildren(route.children)
    }
    if (route.component) {
      // Layout ParentView 组件特殊处理
      if (route.component === 'Layout') {
        route.component = Layout
      } else if (route.component === 'ParentView') {
        route.component = ParentView
      } else if (route.component === 'InnerLink') {
        route.component = InnerLink
      } else {
        route.component = loadView(route.component)
      }
    }
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    } else {
      delete route['children']
      delete route['redirect']
    }
    return true
  })
}

function filterChildren(childrenMap, lastRouter = false) {
  var children = []
  childrenMap.forEach((el, index) => {
    if (el.children && el.children.length) {
      if (el.component === 'ParentView' && !lastRouter) {
        el.children.forEach(c => {
          c.path = el.path + '/' + c.path
          if (c.children && c.children.length) {
            children = children.concat(filterChildren(c.children, c))
            return
          }
          children.push(c)
        })
        return
      }
    }
    if (lastRouter) {
      el.path = lastRouter.path + '/' + el.path
      if (el.children && el.children.length) {
        children = children.concat(filterChildren(el.children, el))
        return
      }
    }
    children = children.concat(el)
  })
  return children
}

// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
  const res = []
  routes.forEach(route => {
    if (route.permissions) {
      if (auth.hasPermiOr(route.permissions)) {
        res.push(route)
      }
    } else if (route.roles) {
      if (auth.hasRoleOr(route.roles)) {
        res.push(route)
      }
    }
  })
  return res
}

export const loadView = (view) => {
  if (process.env.NODE_ENV === 'development') {
    return (resolve) => require([`@/views/${view}`], resolve)
  } else {
    // 使用 import 实现生产环境的路由懒加载
    return () => import(`@/views/${view}`)
  }
}

export default permission

2、获取缓存数据,用于权限比对

修改 store/getters.js文件,代码如下:

const getters = {
	token: state => state.user.token,
	avatar: state => state.user.avatar,
	name: state => state.user.name,
	userId: state => state.user.userId,
	nickName: state => state.user.nickName,
	roles: state => state.user.roles,
	permissions: state => state.user.permissions,

	intoArray: state => state.intoStore.intoArray,
	detailArray: state => state.intoStore.detailArray,

	permission_routes: state => state.permission.routes,
	topbarRouters: state => state.permission.topbarRouters,
	defaultRoutes: state => state.permission.defaultRoutes,
	sidebarRouters: state => state.permission.sidebarRouters,
}
export default getters

3、定义函数

utils 目录下创建 permission.js文件,代码如下

import store from '@/store'

/**
 * 字符权限校验
 * @param {Array} value 校验值
 * @returns {Boolean}
 */
export function checkPermi(value) {
	if (value && value instanceof Array && value.length > 0) {
		const permissions = store.getters && store.getters.permissions
		const permissionDatas = value
		const all_permission = "*:*:*"

		const hasPermission = permissions.some(permission => {
			return all_permission === permission || permissionDatas.includes(permission)
		})

		return hasPermission;
	} else {
		console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
		return false
	}
}

/**
 * 角色权限校验
 * @param {Array} value 校验值
 * @returns {Boolean}
 */
export function checkRole(value) {
	if (value && value instanceof Array && value.length > 0) {
		const roles = store.getters && store.getters.roles
		const permissionRoles = value
		const super_admin = "admin"

		const hasRole = roles.some(role => {
			return super_admin === role || permissionRoles.includes(role)
		})
		return hasRole;
	} else {
		console.error(`need roles! Like checkRole="['admin','editor']"`)
		return false
	}
}

4、引用函数

  1. 在需要权限控制的页面引用permission.js
import { checkPermi } from "@/utils/permission"

在这里插入图片描述
2. 在data中定义权限数组

rukuPermission:['system:user:resetPwd:aa']

在这里插入图片描述
3. 在methods中定义函数

checkPermission(permission){
	return checkPermi(permission);
}

在这里插入图片描述
4. 用v-if控制显隐

v-if="checkPermission(rukuPermission)"

在这里插入图片描述

验证是否可用

在需要根据权限显隐的功能按钮上使用 v-hasPermi ;如下图:
在这里插入图片描述
截图中的权限标识在后台管理系统中进行维护:
在这里插入图片描述

标签:el,const,permission,显隐,route,RuoYi,children,state,App
From: https://blog.csdn.net/liao3399084/article/details/142554343

相关文章

  • uniapp h5端地图导航功能
    <template> <viewclass="container"> <viewclass="content"> <map:scale="14":show-location="true":show-compass="true"class="map-content" :latitude="position.lati......
  • APP实战:海博TV
    登录进去发现有ROOT检测,虽然不会影响我们的登录,但是能绕过就绕过好了ROOT函数定位直接去定位ToastvarToast=Java.use("android.widget.Toast");Toast.show.implementation=function(){showstack();console.log("Toast.show.implementation");returnthis.s......
  • [微信小程序原创项目]基于Springboot+Vue+Uniapp的通用商城小程序、商城管理系统
    项目提供:完整源码+数据库sql文件+数据库表Excel文件关注我的B站:程序员阿水呀,带小白学习更多干货教程!1、项目功能描述本项目为双角色,用户和管理员,用户使用小程序前台,管理员使用web后台。1.1注册注册功能:填写用户名、密码进行注册。仅开放小程序端即用户端进行注册。......
  • uniapp [全端兼容] - 详细实现拍照或相册选取图片后插入水印功能,手机拍照或相册上传图
    前言网上的教程乱七八糟且兼容性太差,本文提供优质示例。在uni-app全平台兼容(H5网页网站、支付宝/微信小程序、安卓App、苹果App、nvue)开发中,详解手机从相册选取上传图像后加入水印功能,手机拍摄照相后也可以加入水印,Uniapp给图片添加水印,获取上传或拍摄的图片信息后,为......
  • 【25届毕设选题推荐】基于uniapp的简易旅行旅游系统(源码+部署+LW文档)
    前言:我是天码编程,从事计算机开发行业数年,专注Java程序设计开发、源码分享、技术指导和毕业设计,欢迎各位前来交流讨论......
  • PHP爬虫APP程序:打造智能化数据抓取工具
    在信息爆炸的时代,数据的重要性日益凸显。PHP作为一种广泛使用的服务器端脚本语言,因其强大的功能和灵活性,成为开发爬虫程序的理想选择。本文将探讨如何使用PHP构建一个爬虫APP程序,以及其背后的思维逻辑和实现步骤。什么是PHP爬虫APP程序?PHP爬虫APP程序是一个利用PHP编写的应......
  • APP集成人脸识别接口-C#人脸识别API接口
    人脸识别技术是一种基于生物特征的识别技术,它通过捕捉和分析人脸特征来识别或验证个体身份。这项技术主要依赖于计算机视觉、图像处理和人工智能算法的结合,一般由第三方人工智能接口平台来提供,例如:翔云、阿里云等平台。人脸识别技术的应用可以大大提升身份验证的准确性,减......
  • uniapp [全端兼容] - 详细实现日历“平铺方式“直接在页面上显示出来,而并非嵌套在弹出
    前言如果您需要“纯弹框式”日历,请访问这篇文章。在uni-app全平台兼容(H5网页网站、支付宝/微信小程序、安卓App、苹果App、nvue)开发中,详解实现让日历以平铺、全屏的形式直接放到页面上,而并非常见的弹框及弹出式窗口才能打开日历进行选择,uniApp不套在弹框里的日历插......
  • 天地图移动端部署(一):创建一个基础地图服务(uni-app环境)
    前言:在一家测绘公司上班,接手了一个移动端APP项目,用uni-app开发的,地图服务用天地图底层支持,嗯,测绘用天地图十分合理。“这地图看起来糊糊的,你给换成XX地图吧。”老大某天跟我说。圣谕下达,开始拉代码,读代码。嗯,依旧是一坨的“清朝”项目代码,一堆的log,一堆的警告,一堆的if,就......
  • 深入理解 Nuxt.js 中的 app:error 钩子
    title:深入理解Nuxt.js中的app:error钩子date:2024/9/27updated:2024/9/27author:cmdragonexcerpt:摘要:本文深入讲解了Nuxt.js框架中的app:error钩子,介绍其在处理web应用中致命错误的重要作用、使用方法及实际应用场景。通过创建Nuxt项目、定义插件、触发错误与测......