ant design pro 从服务器拉取菜单后,菜单权限控制,按钮权限控制

作者:aSuncat
原文:https://www.yuque.com/gm9oz0/htn2dd/rkv8p1

前提

从服务器请求菜单:https://blog.csdn.net/aSuncat/article/details/109557489

需求

权限控制分3个方面

  • 点击左边菜单:如果没有权限则右边内容显示403页面
  • 浏览器输入:如果输入的是没有权限的url,则右边内容显示403页面
  • 内容中按钮的权限:如果右边内容中的按钮没有权限,则该按钮不显示

权限

左边菜单点击

src/layouts/BasicLayout.tsx
menuDataRender={() => menuData}改成menuDataRender={() => menuDataRender(menuData)}

右边内容部分权限(如按钮权限)

服务器返回的菜单数据有authority

如服务器返回的数据如下

const data = [
 {
           path: '/',
           redirect: '/welcome',
         },
         {
           path: '/welcome',
           name: '哈哈哈',
           authority: ['user'],
         },
         {
           path: '/admin',
           name: '管理员',
           authority: ['admin', 'user'],
           children: [
             {
               path: '/admin/sub-page',
               name: '二级管理员',
               authority: ['admin'],
               children: [
                 {
                   name: '前台',
                   authKey: 'item-category-front',
                 }
               ]
             },
           ],
         },
]

则页面改动如下:
src/layouts/BasicLayout.tsx

  // get children authority
  const authorized = useMemo(
    () => {
      if (!menuDataRef.current[0]) {
        const currentAuthority = menuData[0] && formatMenuInfo(menuData, 'menu', location.pathname || '', 'authority')
        return {
          authority: currentAuthority
        } || {
          authority: undefined
        }
      }
      return getMatchMenu(location.pathname || '/', menuDataRef.current).pop() || {
        authority: undefined,
      }
    },
    [location.pathname, loading],
  );

新增utils/menuData.ts

import {
  MenuDataItem,
} from '@ant-design/pro-layout';
interface flattenMenuDataType {
  path: string;
  name: string;
  authority: string;
}
interface flattenBtnData {
  key: string,
  name: string,
}
const updateFlatten = (list:MenuDataItem[], flattenMenuData:flattenMenuDataType[], flattenBtnData:flattenBtnData[]) => {
  list.forEach(l => {
    if (l.path) {
      const obj:flattenMenuDataType = {
        path: l.path,
        name: l.name,
        authority: l.authority
      }
      flattenMenuData.push(obj)
    }
    if (l.key) {
      const keyObj:flattenBtnData = {
        key: l.key,
        name: l.name,
      }
      flattenBtnData.push(keyObj)
    }
    if (l.children) {
      updateFlatten(l.children, flattenMenuData, flattenBtnData)
    }
  })
}

export const flattenMenu = (menuData:MenuDataItem[]) => {
  let flattenMenuData:flattenMenuDataType[] = []
  let flattenBtnData:flattenBtnData[] = []
  updateFlatten(menuData, flattenMenuData, flattenBtnData)
  return { flattenMenuData, flattenBtnData }
}

/**
 * @param menuData 服务器返回的菜单信息
 * @param type  处理类型:type:menu,菜单,type:content, 按钮
 * @param equalType 比较的对照值,如path为/user,则传入/user
 * @param key 最终返回的属性值,如path一样,返回authority
 */
export const formatMenuInfo = (menuData:MenuDataItem[], type:string, equalType:string, key:string) => {
  const { flattenMenuData, flattenBtnData } = flattenMenu(menuData)
  let retVal
  if (type==='menu') {
    flattenMenuData.forEach(l => {
      if(l.path === equalType) {
        retVal = l[key]
      }
    })
  } else {
    flattenBtnData.forEach(l => {
      if(l.key === equalType) {
        retVal = l[key]
      }
    })
  }
  return retVal
}

服务器返回的数据没有authority(推荐,我在项目中用的是这个)

如服务器返回数据如下

const data = [
 {
           path: '/',
           redirect: '/welcome',
         },
         {
           path: '/welcome',
           name: '欢迎页',
            children: [
           {
             name: '查看',
             authKey: 'key-btn1',
           },
           {
             name: '添加',
             authKey: 'key-btn-add',
           },
         ],
         },
         {
           path: '/admin',
           name: '管理员',
           type: 'menu',
           children: [
             {
               path: '/admin/sub-page',
               name: '二级管理员',
               children: [
                 {
                   name: '前台',
                   authKey: 'item-category-front',
                 }
               ]
             },
           ],
         },
]

则页面改动如下:
src/pages/layouts/BasicLayout.tsx

  // get children authority
  const authorized = useMemo(
    () => {
      if (!menuDataRef.current[0]) {
        const currentAuthority = menuData[0] && pathAuthority(menuData, 'menu', location.pathname || '', 'path')
        return {
          authority: currentAuthority
        } || {
          authority: undefined
        }
      }
      return getMatchMenu(location.pathname || '/', menuDataRef.current).pop() || {
        authority: undefined,
      }
    },
    [location.pathname, loading],
  );

src/utils/menuData.ts

import {
  MenuDataItem,
} from '@ant-design/pro-layout';
interface flattenMenuDataType {
  path: string;
  name: string;
  authority: string;
}
interface flattenBtnData {
  key: string,
  name: string,
}
const updateFlatten = (list:MenuDataItem[], flattenMenuData:flattenMenuDataType[], flattenBtnData:flattenBtnData[]) => {
  list.forEach(l => {
    if (l.path) {
      const obj:flattenMenuDataType = {
        path: l.path,
        name: l.name,
        authority: l.authority
      }
      flattenMenuData.push(obj)
    }
    if (l.key) {
      const keyObj:flattenBtnData = {
        key: l.key,
        name: l.name,
      }
      flattenBtnData.push(keyObj)
    }
    if (l.children) {
      updateFlatten(l.children, flattenMenuData, flattenBtnData)
    }
  })
}

export const flattenMenu = (menuData:MenuDataItem[]) => {
  let flattenMenuData:flattenMenuDataType[] = []
  let flattenBtnData:flattenBtnData[] = []
  updateFlatten(menuData, flattenMenuData, flattenBtnData)
  return { flattenMenuData, flattenBtnData }
}

/**
 * @param menuData 服务器返回的菜单信息
 * @param type  处理类型:type:menu,菜单,type:content, 按钮
 * @param equalType 比较的对照值,如path为/user,则传入/user
 * @param key 最终返回的属性值,如path一样,返回authority
 */
export const formatMenuInfo = (menuData:MenuDataItem[], type:string, equalType:string, key:string) => {
  const { flattenMenuData, flattenBtnData } = flattenMenu(menuData)
  let retVal
  if (type==='menu') {
    flattenMenuData.forEach(l => {
      if(l.path === equalType) {
        retVal = l[key]
      }
    })
  } else {
    flattenBtnData.forEach(l => {
      if(l.key === equalType) {
        retVal = l[key]
      }
    })
  }
  return retVal
}

export const formatArrToUniq = (menuData:MenuDataItem[], type:string, key:string) => {
  const { flattenMenuData, flattenBtnData } = flattenMenu(menuData)
  let arr:string[] = []
  const data:any[] = type === 'menu' ? flattenMenuData : flattenBtnData
  data.forEach(l => {
    if (l[key]) {
      arr.push(l[key])
    }
  })
  return arr
}

export const pathAuthority = (menuData:MenuDataItem[], type:string, compareKey:string, key:string) => {
  let retVal
  const arr = formatArrToUniq(menuData, type, key)
  if (arr.indexOf(compareKey) > -1) {
    retVal = undefined
  } else {
    retVal = 'other-uniq-authority'
  }
  return retVal
}

按钮权限

按钮权限utils

新增src/utils/content-auth/index.ts

export { AuthProvider, AuthConsumer } from './auth'
// export { AuthProvider, AuthConsumer, AuthWrapper } from './auth'

export hasAuth from './utils/validate'
export flattenAuth from './utils/flatten'

新增src/utils/content-auth/auth.ts

import React, { Context, createContext, useContext } from 'react'
import flattenAuth from './utils/flatten'
import hasAuth from './utils/validate'

interface propsType {
  name: string;
  children: React.ReactNode
}

const AuthContext:Context<any> = createContext({
  allAuth: [],
})

export const AuthConsumer = (props:propsType) => {
  const context = useContext(AuthContext)
  // const allAuth = flattenAuth([{context.allAuth}]).operations
  // const hasAuthResult = hasAuth(context.allAuth, props.name)
  // const hasAuthResult = hasAuth(allAuth, props.name)
  const { allAuth } = context
  const all = flattenAuth([{resources: allAuth}]).operations
  const hasAuthResult = hasAuth(all, props.name)
  return hasAuthResult ? props.children : null
}

export const AuthProvider = AuthContext.Provider

新增src/utils/content-auth/utils/vlidate.ts

export default function validate (list:string[] = [], name:string) {
  return list.indexOf(name) > -1
}

新增src/utils/content-auth/utils/flatten.ts

// const apiReg = /^\/api/

interface listType {
  authKey?: string,
  name: string,
  type: string,
  children: listType[]
}

function separate (list:listType[], operations:string[]) {
  list.forEach(l => {
    l.authKey && operations.push(l.authKey)
    if (l.children) {
      separate(l.children, operations)
    }
  })
}

export default function flatten(list:listType[]) {
  let operations:string[] = []
  list.forEach(l => {
    let { resources }: {resources: listType[]} = l
    separate(resources, operations)
  })
  return { operations }
}

页面中按钮权限写法

import { AuthConsumer } from '@/utils/content-auth';
  <AuthConsumer name="key-btn1">
          <button>这是个小按钮,其实我后面还藏了一个小按钮(因为它的权限不够)</button>
        </AuthConsumer>
        <AuthConsumer name="key-btn2">
          <button>这是个小按钮2</button>
        </AuthConsumer>
  • 4
    点赞
  • 15
    收藏
  • 打赏
    打赏
  • 9
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论 9

打赏作者

aSuncat

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值