前言

在项目中,一些功能涉及重要的数据管理,为了确保数据安全,我们需要加入权限机制来限制用户操作。作为前端,我们的职责是配合后端提供的权限数据,在页面上实现各种权限限制。

需求分析

在实际业务中,主要需要在两个地方进行权限控制:

  1. 侧边菜单栏 - 控制菜单项的显示与隐藏

  2. 页面内操作 - 控制按钮、弹窗等元素的权限

问题识别

通过分析实际场景,主要面临以下问题:

  • 权限数据的获取时机和存储位置

  • 路由访问权限的限制

  • 重定向逻辑处理

  • 侧边菜单显示控制

  • 团队协作的权限部署方案

解决方案

1. 权限获取与存储

在路由守卫中获取权限数据,存储在 Vuex 中:

javascript

// router.beforeEach 中的权限验证
router.beforeEach(async (to, from, next) => {
  // 如果未获取权限列表,则先获取
  if (!store.getters.getPermissionList.length) {
    await store.dispatch('getPermissionList')
  }
  
  const permissionList = store.getters.getPermissionList
  const { meta } = to
  
  // 如果路由需要权限且用户无权限,则跳转无权限页面
  if (meta && meta.permission && !hasPermission(permissionList, meta.permission)) {
    next('/no-permission')
  } else {
    next()
  }
})

// 权限验证方法
function hasPermission(permissionList, permission) {
  if (Array.isArray(permission)) {
    return permission.some(item => permissionList.includes(item))
  }
  return permissionList.includes(permission)
}

2. 路由配置与权限设置

在路由配置中通过 meta 字段定义权限要求:

javascript

const routes = [
  {
    path: '/drug',
    name: '药品管理',
    meta: { permission: ['drug_manage'] },
    children: [
      {
        path: 'in',
        name: '入库管理',
        meta: { permission: ['drug_in'] }
      },
      {
        path: 'out', 
        name: '出库管理',
        meta: { permission: ['drug_out'] }
      }
    ]
  }
]

3. 动态重定向解决方案

为了解决权限动态变化导致的重定向问题,利用 Vue Router 的动态重定向功能:

javascript

// 动态重定向生成器
function createDynamicRedirect(routeConfig) {
  return function(to) {
    const permissionList = store.getters.getPermissionList
    const firstPermissionChild = routeConfig.children.find(child => 
      !child.meta?.permission || hasPermission(permissionList, child.meta.permission)
    )
    return firstPermissionChild ? firstPermissionChild.path : '/no-permission'
  }
}

// 路由配置示例
{
  path: '/drug',
  redirect: createDynamicRedirect(routeConfig)
}

递归处理路由配置的完整方案:

javascript

function processRouterConfig(routes) {
  return routes.map(route => {
    if (route.children && route.children.length > 0) {
      // 处理子路由
      route.children = processRouterConfig(route.children)
      
      // 如果没有设置 redirect,自动设置动态重定向
      if (!route.redirect) {
        route.redirect = createDynamicRedirect(route)
      }
    }
    return route
  })
}

// 应用配置
const finalRoutes = processRouterConfig(originalRoutes)

4. 侧边栏显示控制

将处理后的路由配置存储到 Vuex,侧边栏基于处理后的配置渲染:

javascript

// 处理侧边栏显示逻辑
function generateSidebarRoutes(routes, permissionList) {
  return routes.filter(route => {
    // 无权限要求或有权限则显示
    if (!route.meta?.permission || hasPermission(permissionList, route.meta.permission)) {
      // 处理子菜单
      if (route.children) {
        route.children = generateSidebarRoutes(route.children, permissionList)
        // 如果所有子项都无权限,则不显示父级
        return route.children.length > 0
      }
      return true
    }
    return false
  })
}

5. 团队权限部署方案

提供多种权限控制方式,方便团队成员使用:

方法一:权限验证方法

javascript

// Vue 原型上挂载权限验证方法
Vue.prototype.$permission = function(permission) {
  const permissionList = this.$store.getters.getPermissionList
  return hasPermission(permissionList, permission)
}

// 在组件中使用
export default {
  methods: {
    handleOperation() {
      if (this.$permission('operation_permission')) {
        // 执行操作
      }
    }
  }
}
方法二:权限指令

javascript

// 注册权限指令
Vue.directive('permission', {
  inserted: function(el, binding) {
    const permission = binding.value
    const permissionList = this.$store.getters.getPermissionList
    
    if (!hasPermission(permissionList, permission)) {
      el.classList.add('hide-by-permission')
    }
  }
})

// 在模板中使用
<button v-permission="'delete_permission'">删除</button>
方法三:权限组件

javascript

// 权限组件
const Permission = {
  functional: true,
  render: function(createElement, context) {
    const { props, scopedSlots } = context
    const permissionList = context.parent.$store.getters.getPermissionList
    
    if (hasPermission(permissionList, props.value)) {
      return scopedSlots.default ? scopedSlots.default() : null
    }
    return null
  }
}

// 注册组件
Vue.component('Permission', Permission)

// 在模板中使用
<Permission value="edit_permission">
  <button>编辑</button>
</Permission>

总结

通过以上方案,我们系统性地解决了前端权限控制的各类问题:

  1. 权限时机与存储 - 在路由守卫中获取,存储在 Vuex 中统一管理

  2. 路由限制 - 通过路由守卫和 meta 配置实现访问控制

  3. 重定向问题 - 利用动态重定向函数智能跳转

  4. 侧边栏显示 - 基于处理后的路由配置动态生成菜单

  5. 团队协作 - 提供多种易用的权限部署方式

Logo

前往低代码交流专区

更多推荐