Skip to content

AccessControl

简介

@ifanrx/dashboard 配备了一套基于 RBAC 的权限控制方案,在项目内可按需使用

通过 @ifanrx/scaffolder 创建的 dashboard 项目已经内置了权限控制,无需额外配置

搭建权限系统

对于非脚手架生成的项目,可根据以下步骤接入权限系统

创建数据表

以下数据表都只提供 JSON Schema,可复制到知晓云或者表设计工具内快速建表

admin

Click me to view the code
json
{
  "name": "admin",
  "description": "管理员账号",
  "row_read_perm": ["user:{created_by}"],
  "row_write_perm": [],
  "write_perm": [],
  "schema": {
    "fields": [
      {
        "name": "id",
        "type": "id",
        "description": "id"
      },
      {
        "name": "status",
        "type": "string",
        "description": "管理员状态",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": [
            {
              "type": "choices",
              "value": ["active", "inactive"],
              "remark": ["启用", "禁用(软删)"]
            }
          ]
        }
      },
      {
        "name": "name",
        "type": "string",
        "description": " 管理员名称",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": false,
          "rules": []
        }
      },
      {
        "name": "email",
        "type": "string",
        "description": "管理员邮箱",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "roles",
        "type": "array",
        "description": " 当前管理员分配的角色",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "default": [],
        "constraints": {
          "required": true,
          "rules": []
        },
        "items": {
          "type": "string"
        }
      },
      {
        "name": "root",
        "type": "boolean",
        "description": "是否为超级管理员",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": false,
          "rules": []
        },
        "default": false
      },
      {
        "name": "updated_by",
        "type": "object",
        "description": "操作者信息快照",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "created_by",
        "type": "integer",
        "description": "created_by"
      },
      {
        "name": "created_at",
        "type": "integer",
        "description": "created_at"
      },
      {
        "name": "updated_at",
        "type": "integer",
        "description": "updated_at"
      }
    ]
  },
  "indexes": [
    {
      "fields": ["email"]
    }
  ],
  "design_tool": {
    "fields": [
      {
        "name": "id",
        "type": "id",
        "description": "id"
      },
      {
        "name": "status",
        "type": "string",
        "description": "管理员状态",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": [
            {
              "type": "choices",
              "value": ["active", "inactive"],
              "remark": ["启用", "禁用(软删)"]
            }
          ]
        }
      },
      {
        "name": "name",
        "type": "string",
        "description": " 管理员名称",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": false,
          "rules": []
        }
      },
      {
        "name": "email",
        "type": "string",
        "description": "管理员邮箱",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "roles",
        "type": "array",
        "description": " 当前管理员分配的角色",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "default": [],
        "constraints": {
          "required": true,
          "rules": []
        },
        "items": {
          "type": "string"
        }
      },
      {
        "name": "root",
        "type": "boolean",
        "description": "是否为超级管理员",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": false,
          "rules": []
        },
        "default": false
      },
      {
        "name": "updated_by",
        "type": "object",
        "description": "操作者信息快照",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "created_by",
        "type": "integer",
        "description": "created_by"
      },
      {
        "name": "created_at",
        "type": "integer",
        "description": "created_at"
      },
      {
        "name": "updated_at",
        "type": "integer",
        "description": "updated_at"
      }
    ]
  }
}

role

Click me to view the code
json
{
  "name": "role",
  "description": "角色,每条记录即为一种角色",
  "row_read_perm": ["user:*"],
  "row_write_perm": [],
  "write_perm": ["user:*"],
  "schema": {
    "fields": [
      {
        "name": "id",
        "type": "id",
        "description": "id"
      },
      {
        "name": "name",
        "type": "string",
        "description": "角色名称",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "privileges",
        "type": "array",
        "description": "角色拥有权限 id",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "default": [],
        "constraints": {
          "required": true,
          "rules": []
        },
        "items": {
          "type": "string"
        }
      },
      {
        "name": "updated_by",
        "type": "object",
        "description": "操作者信息",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "created_by",
        "type": "integer",
        "description": "created_by"
      },
      {
        "name": "created_at",
        "type": "integer",
        "description": "created_at"
      },
      {
        "name": "updated_at",
        "type": "integer",
        "description": "updated_at"
      }
    ]
  },
  "design_tool": {
    "fields": [
      {
        "name": "id",
        "type": "id",
        "description": "id"
      },
      {
        "name": "name",
        "type": "string",
        "description": "角色名称",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "privileges",
        "type": "array",
        "description": "角色拥有权限 id",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "default": [],
        "constraints": {
          "required": true,
          "rules": []
        },
        "items": {
          "type": "string"
        }
      },
      {
        "name": "updated_by",
        "type": "object",
        "description": "操作者信息",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "created_by",
        "type": "integer",
        "description": "created_by"
      },
      {
        "name": "created_at",
        "type": "integer",
        "description": "created_at"
      },
      {
        "name": "updated_at",
        "type": "integer",
        "description": "updated_at"
      }
    ]
  }
}

privilege

Click me to view the code
json
{
  "name": "privilege",
  "description": "权限",
  "row_read_perm": ["user:*"],
  "row_write_perm": [],
  "write_perm": ["user:*"],
  "schema": {
    "fields": [
      {
        "name": "id",
        "type": "id",
        "description": "id"
      },
      {
        "name": "category",
        "type": "string",
        "description": "权限所属类目",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "value",
        "type": "string",
        "description": "权限内容,由 action-object 组成",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "display",
        "type": "string",
        "description": "用户实际看到的权限描述,通常由权限同步脚本生成,value.replace(/-/g, '')",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "uuid",
        "type": "string",
        "description": "权限 id,在 definePrivileges 时填入",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "created_by",
        "type": "integer",
        "description": "created_by"
      },
      {
        "name": "created_at",
        "type": "integer",
        "description": "created_at"
      },
      {
        "name": "updated_at",
        "type": "integer",
        "description": "updated_at"
      }
    ]
  },
  "design_tool": {
    "fields": [
      {
        "name": "id",
        "type": "id",
        "description": "id"
      },
      {
        "name": "category",
        "type": "string",
        "description": "权限所属类目",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "value",
        "type": "string",
        "description": "权限内容,由 action-object 组成",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "display",
        "type": "string",
        "description": "用户实际看到的权限描述,通常由权限同步脚本生成,value.replace(/-/g, '')",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "uuid",
        "type": "string",
        "description": "权限 id,在 definePrivileges 时填入",
        "acl": {
          "clientReadOnly": false,
          "clientVisible": true,
          "creatorVisible": false
        },
        "constraints": {
          "required": true,
          "rules": []
        }
      },
      {
        "name": "created_by",
        "type": "integer",
        "description": "created_by"
      },
      {
        "name": "created_at",
        "type": "integer",
        "description": "created_at"
      },
      {
        "name": "updated_at",
        "type": "integer",
        "description": "updated_at"
      }
    ]
  }
}

在项目内接入模块

定义路由

在项目根路径下创建 route.js 文件

Details
jsx
import {
  defineRouteList,
  DEFAULT_PRIVILEGE,
  definePrivilege,
} from '@ifanrx/dashboard'

// 获取当前 pages 内容,需在 defineRouteList 传入
const pages = import.meta.glob(['@/pages/*.jsx', '@/pages/*/index.jsx'])

export const ROUTE = {
  WELCOME: '/welcome',
  ADMIN_LIST: '/admin-list',
  ADMIN_ADD: '/admin-edit',
  ADMIN_EDIT: '/admin-edit/:id',
  ADMIN_ROLE_LIST: '/admin-role-list',
  ADMIN_ROLE_ADD: '/admin-role-edit',
  ADMIN_ROLE_EDIT: '/admin-role-edit/:id',
}

/** @type {import('@ifanrx/dashboard').Route[]} */
const routeOptions = [
  {
    path: ROUTE.WELCOME,
    menuTitle: '欢迎',
    // 若当前页面所有人可访问,可使用 DEFAULT_PRIVILEGE 填充给 privileges
    privileges: [DEFAULT_PRIVILEGE],
    // icon 需传入构造函数,不支持使用组件
    icon: SmileFilled,
  },
  {
    menuTitle: '管理员',
    icon: TeamOutlined,
    children: [
      {
        path: ROUTE.ADMIN_LIST,
        menuTitle: '管理员列表',
        privileges: [
          // 可通过 definePrivilege 定义新的权限,会在下文 definePrivilege 章节中说明
          definePrivilege(
            '管理员',
            '查看-管理员列表',
            'c6d42cb1-b26d-4f49-a535-3de416f3f5a6'
          ),
          // privileges 可配置多个权限,当前登录用户拥有其中一个权限即可访问该路由
          definePrivilege(
            '管理员',
            '编辑-管理员信息',
            '80659315-600e-4c4c-90f5-2bab8a9125c2'
          ),
        ],
        children: [
          {
            path: ROUTE.ADMIN_ADD,
            name: '新增管理员',
            privileges: [
              // 使用重复权限时,无需重复定义,可使用 getPrivilege 来获取权限
              getPrivilege('管理员', '编辑-管理员信息'),
            ],
          },
          {
            path: ROUTE.ADMIN_EDIT,
            name: '编辑管理员',
            privileges: [getPrivilege('管理员', '编辑-管理员信息')],
          },
        ],
      },
      {
        path: ROUTE.ADMIN_ROLE_LIST,
        menuTitle: '管理员角色',
        privileges: [
          definePrivilege(
            '管理员',
            '查看-管理员角色',
            '31169c04-b72b-458e-91b8-52e5506e9bbe'
          ),
        ],
        children: [
          {
            path: ROUTE.ADMIN_ROLE_ADD,
            name: '新增管理员角色',
            privileges: [
              definePrivilege(
                '管理员',
                '新增-管理员角色',
                'b0c6a603-c85c-4f22-aa43-9d9b075bebb7'
              ),
            ],
          },
          {
            path: ROUTE.ADMIN_ROLE_EDIT,
            name: '编辑管理员角色',
            privileges: [
              definePrivilege(
                '管理员',
                '编辑-管理员角色',
                '637338fc-d6f9-4e20-b3b4-88912f481340'
              ),
            ],
          },
        ],
      },
    ],
  },
]

export const routeList = defineRouteList(routeOptions, pages)
Route 定义
属性类型可选说明
privilegesPrivilege[]路由的权限配置。所有路由都必须配置此属性。若需要表示无权限也可访问,需要将 DEFAULT_PRIVILEGE 配置进 privileges。上级路由会继承所有下级路由的 privileges
menuTitlestring菜单栏显示的标题。配置此字段表示该路由需要显示在菜单栏中。menuTitlename 必须至少配置一个,用于 RouterBreadcrumb 内部依赖。
namestring路由名称。若 menuTitle 为空,则需要配置 namemenuTitlename 必须至少配置一个,用于 RouterBreadcrumb 内部依赖。
pathstring路由路径。
iconimport('react').ReactNode配置菜单栏内的图标,需要使用图标的构造函数。
childrenRoute[]下级路由配置。
renderFunction自定义菜单栏内路由节点的渲染方法。
lazyany为空时会根据 path 自动在 src/pages 目录内找到同名文件。亦可指定文件,使用 lazyLoader 方法引入。
privilegeControllerPrivilegeControllerprivileges 不满足于判断是否显示该路由,可通过该函数进行扩展。返回 true 表示路由可见,仅支持同步函数。
keystringgenRoute 自动生成。
parentKeystringgenRoute 自动生成。
hasPrivilegebooleangenRoute 自动生成。根据 privilegesprivilegeController 控制。
menuItemRenderMenuItemRender自定义菜单项渲染方法。

接入 AccessRouterProvider

在项目 App.jsx 文件内引入以下模块以及代码

Details
jsx
import {
  acl as aclState,
  initAcl,
  AccessRouterProvider,
  AccessRouterLayout,
} from '@ifanrx/dashboard'

export default function App() {
  // your code...

  const acl = useSnapshot(aclState)

  useMount(() => {
    // routeList 指通过 defineRouteList 生成的产物
    initAcl(routeList)
  })

  return (
    // your code...
    {!isEmpty(acl.accessRouteList) ? (
      <AccessRouterProvider accessRouteList={acl.accessRouteList}>
        <AccessRouterLayout
          accessRouteList={acl.accessRouteList}
          logo="https://cloud-minapp-39778.cloud.ifanrusercontent.com/1n8GDl96Mg4usjDQ.png"
          title={PROJECT_NAME}
          userprofile={acl.userprofile}
        />
      </AccessRouterProvider>
    ) : null}
    // your code...
  )
}

禁用权限系统

通过 @ifanrx/scaffolder 创建的 dashboard 项目已经内置了权限控制,如果需要禁用权限,可在 App.jsx 内找到 initAcl 函数的调用,传入 withoutAccess: true 即可

jsx
export default function App() {
  // your code...
  useMount(() => {
    initAcl(routeList, {withoutAccess: true})
  })
  // your code...
}

禁用权限系统后,项目不再需要创建 admin/privilege/role 表,所有用户均可访问所有页面

用户状态管理

acl

基于 valtio 代理的用户状态,包含用户信息、用户权限集合、用户可访问的路由列表

js
export const acl = proxy({
  /**
   * 当前登录用户的信息
   * @type {Userprofile|null}
   */
  userprofile: null,
  /**
   * 当前登录用户可访问的路由信息
   * @type {import('./define-route-list').Route[]}
   */
  accessRouteList: [],
  /**
   * 当前登录用户的权限集
   * @type {import('./define-route-list').Privilege[]}
   */
  privileges: [DEFAULT_PRIVILEGE],
  /**
   * 当前登录用户在 admin 表的 record
   * @type {Admin}
   */
  admin: null,
})

使用前需使用 initAcl 对 acl 进行初始化

initAcl

acl 进行初始化,包括用户信息获取、权限获取、路由权限校验

js
/**
 * @typedef InitAclOptions
 * @prop {boolean} [withoutAccess = false] 禁用权限路由,当 withoutAccess 为 true 时,不对路由权限进行校验,defineRouteList 也无需传 privileges 配置
 */

/**
 * @callback InitAcl
 * @param {import('./define-route-list').Route[]} routeList
 * @param {InitAclOptions} options
 * @return {Promise<Acl>}
 */

其中 routeList 是 defineRouteList 定义的路由列表

options.withoutAccess 为 true 时,不对路由权限进行校验,defineRouteList 也无需传 privileges 配置

example

jsx
const routeList = defineRouteList([...])

export default function App() {
  useMount(() => {
    initAcl(routeList, {withoutAccess: false})
  })
}

privilege

definePrivilege

definePrivilege 用于创建新的权限

ts
/**
 * @template {string} Category
 * @template {string} Value
 * @template {string} Uuid
 * @param {Category} category
 * @param {Value} value action-objects
 * @param {Uuid} uuid
 * @example
 * definePrivilege('管理员', '查看-管理员列表', '9af166f0-3322-4804-93ff-24e5bdabb8e9')
 * @return {{category: Category, value: Value, Uuid: string}}
 */

category 为分类,一般为顶级菜单名称,如:管理员、活动管理

value 的组成一般情况下由 [action-object] 组成,如:查看-活动列表、编辑-活动、新增-活动、删除-活动。@ifanrx/dashboard 内的 definePrivilege 必须使用 [action-object] 来定义 value,如项目内有特殊需求,可在参数不变的前提下自行定义 definePrivilege 方法,修改 value 的校验条件

uuid 为唯一索引,每个 privilege 必须有自己的 uuid 且不可重复

TIP

可使用 VSCode UUID Generator 插件来生成 uuid,或者其他任何方法都可以,但是需要保证 uuid 的唯一性

example

js
export const routeList = defineRouteList([
  {
    path: ROUTE.ADMIN_LIST,
    menuTitle: '管理员列表',
    privileges: [
      definePrivilege(
        '管理员',
        '查看-管理员列表',
        'bc3505f9-ef5d-4647-987b-b07ead208205'
      ),
    ],
    icon: SmileFilled,
  },
])

getPrivilege

getPrivilege 用于获取已存在的权限,如果在这之前已经通过 definePrivilege 一个权限了,之后再次使用这个权限时,应使用 getPrivilege,此时不再需要使用 uuid,会根据 category 以及 value 来进行索引

ts
declare function getPrivilege<Category extends string, Value extends string>(
  category: Category,
  value: Value
): {category: Category; value: Value}

example

js
export const routeList = defineRouteList([
  {
    path: ROUTE.ADMIN_LIST,
    menuTitle: '管理员列表',
    privileges: [
      definePrivilege(
        '管理员',
        '查看-管理员列表',
        'bc3505f9-ef5d-4647-987b-b07ead208205'
      ),
    ],
    icon: SmileFilled,
  },
  {
    path: ROUTE.CHILD_TRADE_UNION_ADMIN_LIST,
    menuTitle: '下级工会管理员列表',
    privileges: [getPrivilege('管理员', '查看-管理员列表')],
    icon: SmileFilled,
  },
])

hasPrivilege

校验用户是否拥有某个权限,同时支持接受单个权限或者一个权限集合作为参数

example

jsx
// hasPrivilege({category: '管理员', value: '查看-管理员列表'})
// hasPrivilege([{category: '管理员', value: '查看-管理员列表'}, {category: '管理员', value: '修改-管理员列表'}])

function updateAdmin() {
  if (!hasPrivilege({category: '管理员', value: '修改-管理员'})) {
    message.error('没有权限修改管理员信息')
    return
  }

  // ...
}

function AdminList() {
  if (
    !hasPrivilege([
      {category: '管理员', value: '查看-管理员列表'},
      {category: '管理员', value: '修改-管理员列表'},
    ])
  ) {
    return <WithoutAccess>没有权限</WithoutAccess>
  }

  return <Table />
}

push-privilege

push-privilege 是一个脚本,可以将项目内通过 definePrivilege 定义的 privilege 收集并上传到 privilege 表内,执行脚本前需要确保已经 创建数据表 以及已经通过 mincloud login 登录知晓云

使用 definePrivilege 定义新权限后,一定要执行该脚本将最新的权限集合同步到数据表,否则无法给管理员设置对应权限

用法如下:

bash
push-privilege // 同步 privilege 到生产环境数据表
push-privilege --envId YOUR_ENV_ID // 同步 privilege 到知晓云测试环境数据表,需要把 YOUR_ENV_ID 替换为实际的 env id
push-privilege --qa // 同步 privilege 到知晓云 QA 应用,需确保已经通过 mincloud login --qa 登录知晓云

通过 @ifanrx/scaffolder 创建的项目已经在 script 内置了这几个命令,可以直接使用

json
{
  "script": {
    "push-privilege": "push-privilege",
    "push-privilege:dev": "push-privilege --envId YOUR_ENV_ID",
    "push-privilege:qa": "push-privilege --qa"
  }
}

权限路由

defineRouteList

权限系统内使用的路由列表必须是通过 defineRouteList 定义出来的,作用是根据 path 给每个路由挂载页面组件,函数定义如下:

ts
/**
 * @callback DefineRouteList
 * @param {Route[]} routeOptions
 * @param {object} pages
 * @return {Route[]}
 */

routeOptions 定义查看 Route 定义

pages 为项目内 pages 目录下的文件模块,pages = import.meta.glob(['@/pages/*.jsx', '@/pages/*\/index.jsx]),是固定搭配,如果项目目录结构不同,自行修改目录名称即可

example

定义路由

lazyLoader

在 defineRouteList 内配置路由 path 时,会在 pages 内匹配对应的页面文件,如:path 为 /admin-list 时,会尝试匹配 /pages/admin-list.jsx 或者 /pages/admin-list/index.jsx,都匹配不到时报 Page Not Found

当页面文件路径与上述规则不匹配时,可在定义路由时手动传入 lazy 参数来匹配页面文件,如:

js
const pages = import.meta.glob(['@/pages/*.jsx', '@/pages/*/index.jsx'])

const ROUTE = {
  PRIZE_LOG: '/prize-log/:type/:id',
  // 根据奖品 id 查询奖品发放记录
  PRIZE_LOG_WITH_PRIZE_ID: '/prize-log/prize/:id',
  // 根据活动 id 查询奖品发放记录
  PRIZE_LOG_WITH_ACTIVITY_ID: '/prize-log/activity/:id',
}

export const routeList = defineRouteList([
  {
    path: ROUTE.PRIZE_LOG_WITH_PRIZE_ID,
    name: '发放记录',
    privileges: [
      definePrivilege(
        '活动管理',
        '查看-发放记录',
        'bc3505f9-ef5d-4647-987b-b07ead208205'
      ),
    ],
    lazy: lazyLoader(ROUTE.PRIZE_LOG, pages),
  },
  {
    path: ROUTE.PRIZE_LOG_WITH_ACTIVITY_ID,
    menuTitle: '发放记录',
    privileges: [getPrivilege('活动管理', '查看-发放记录')],
    lazy: lazyLoader(ROUTE.PRIZE_LOG, pages),
  },
])

DEFAULT_PRIVILEGE

用于配置不需要权限的路由

example

js
export const routeList = defineRouteList([
  {
    path: ROUTE.WELCOME,
    menuTitle: '欢迎',
    // 若当前页面所有人可访问,可使用 DEFAULT_PRIVILEGE 填充给 privileges
    privileges: [DEFAULT_PRIVILEGE],
  },
])

权限控制组件

Privilege

Privilege(options): ReactElement<any, string | JSXElementConstructor<any>>

用于包裹需要校验访问权限的组件

默认情况下,当用户拥有权限时渲染 children,无权限时不进行任何渲染,可通过 fallback 自定义无权限时的渲染组件

当 children 为 function 时,可接收 hasPrivilege 参数,需开发者根据 hasPrivilege 值自行返回组件

参数

NameTypeDefault valueDescription
optionsObjectundefined
options.childrenundefined | Functionundefined
options.fallbackundefined | null | ReactElement<any, string | JSXElementConstructor<any>>null无权限时渲染组件,默认不渲染,当 children 为 Function 时,fallback 优先级高于 children
options.privilegesPrivilege[][]访问该模块所需权限集

返回值

ReactElement<any, string | JSXElementConstructor<any>>

示例

  • 有权限时显示组件,无权限时隐藏组件
jsx
<Privilege privileges={[getPrivilege('管理员', '查看-管理员列表')]}>
  <AdminList />
</Privilege>
  • 有权限时显示组件,无权限时显示 fallback 内容
jsx
<Privilege
  fallback={<WithoutAccess />}
  privileges={[getPrivilege('管理员', '查看-管理员列表')]}
>
  <AdminList />
</Privilege>
  • 自定义
jsx
<Privilege privileges={privileges}>
  {({hasPrivilege}) => (
    <Button
      {...buttonProps}
      onClick={() => (hasPrivilege ? success() : fail())}
    >
      权限按钮
    </Button>
  )}
</Privilege>

源码

access-router/privilege.jsx

PrivilegeButton

PrivilegeButton(options): ReactElement<any, string | JSXElementConstructor<any>>

根据用户是否有权限控制按钮组件点击事件

默认情况下,当用户拥有权限时会正常出发 onClick,无权限时会弹窗提醒用户,可通过 deniedMsg 修改弹窗内容或者通过 onDenied 自定义无权限时回调

本组件继承 antd Button 所有 props

参数

NameType
optionsButtonProps & Options

返回值

ReactElement<any, string | JSXElementConstructor<any>>

示例

  • 基础用法
jsx
<PrivilegeButton
  privileges={[getPrivilege('管理员', '新增-管理员')]}
  size="large"
  type="primary"
  onClick={() => navigate(ROUTE.ADMIN_ADD)}
>
  新增
</PrivilegeButton>
  • 自定义 onDenied
jsx
<PrivilegeButton
  privileges={[getPrivilege('管理员', '新增-管理员')]}
  size="large"
  type="primary"
  onClick={() => navigate(ROUTE.ADMIN_ADD)}
  onDenied={() =>
    modal.confirm({
      title: '暂无权限',
      content: '点击「确定」像管理员申请权限',
      onOk: () => navigate(ROUTE.XXXX),
    })
  }
>
  新增
</PrivilegeButton>

源码

access-router/privilege-button.jsx