feat: ui hasPermission (#3270)

This commit is contained in:
shaohuzhang1 2025-06-16 18:23:46 +08:00 committed by GitHub
parent 3540ad8550
commit 11dfcd4d60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 167 additions and 16 deletions

View File

@ -1,4 +1,3 @@
import { ComplexPermission } from '@/utils/permission/type'
import { PermissionConst, RoleConst } from '@/utils/permission/data' import { PermissionConst, RoleConst } from '@/utils/permission/data'
const ApplicationDetailRouter = { const ApplicationDetailRouter = {

View File

@ -46,6 +46,11 @@ export const routes: Array<RouteRecordRaw> = [
name: 'ResetPassword', name: 'ResetPassword',
component: () => import('@/views/login/ResetPassword.vue'), component: () => import('@/views/login/ResetPassword.vue'),
}, },
{
path: '/permission',
name: 'permission',
component: () => import('@/views/Permission.vue'),
},
// { // {
// path: '/:pathMatch(.*)', // path: '/:pathMatch(.*)',
// name: '404', // name: '404',

View File

@ -93,15 +93,27 @@ const useUserStore = defineStore('user', {
getPermissions() { getPermissions() {
if (this.userInfo) { if (this.userInfo) {
if (this.isEE()) { if (this.isEE()) {
return [...this.userInfo?.permissions, 'x-pack-ee'] return [...this.userInfo?.permissions, 'X-PACK-EE']
} else if (this.isPE()) { } else if (this.isPE()) {
return [...this.userInfo?.permissions, 'x-pack-pe'] return [...this.userInfo?.permissions, 'X-PACK-PE']
} }
return this.userInfo?.permissions return this.userInfo?.permissions
} else { } else {
return [] return []
} }
}, },
getEdition() {
if (this.userInfo) {
if (this.isEE()) {
return 'X-PACK-EE'
} else if (this.isPE()) {
return 'X-PACK-PE'
} else {
return 'X-PACK-CE'
}
}
return 'X-PACK-CE'
},
getRole() { getRole() {
if (this.userInfo) { if (this.userInfo) {
return this.userInfo?.role return this.userInfo?.role

View File

@ -1,4 +1,4 @@
import { Permission, Role } from '@/utils/permission/type' import { Permission, Role, Edition } from '@/utils/permission/type'
const PermissionConst = { const PermissionConst = {
USER_READ: new Permission('USER:READ'), USER_READ: new Permission('USER:READ'),
USER_CREATE: new Permission('USER:CREATE'), USER_CREATE: new Permission('USER:CREATE'),
@ -10,4 +10,9 @@ const RoleConst = {
WORKSPACE_MANAGE: new Role('WORKSPACE_MANAGE'), WORKSPACE_MANAGE: new Role('WORKSPACE_MANAGE'),
USER: new Role('USER'), USER: new Role('USER'),
} }
export { PermissionConst, RoleConst } const EditionConst = {
IS_PE: new Edition('X-PACK-PE'),
IS_EE: new Edition('X-PACK-EE'),
IS_CE: new Edition('X-PACK-CE'),
}
export { PermissionConst, RoleConst, EditionConst }

View File

@ -1,21 +1,30 @@
import useStore from '@/stores' import useStore from '@/stores'
import { Role, Permission, ComplexPermission } from '@/utils/permission/type' import {
Role,
Permission,
ComplexPermission,
Edition,
type PF,
type CPF,
type CRF,
} from '@/utils/permission/type'
import { isFunction } from '@/utils/common' import { isFunction } from '@/utils/common'
type PF = () => Role | string | Permission | ComplexPermission
/** /**
* *
* @param permission * @param permission
* @returns True false * @returns True false
*/ */
const hasPermissionChild = (permission: Role | string | Permission | ComplexPermission | PF) => { const hasPermissionChild = (
permission: Role | string | Permission | ComplexPermission | Edition | PF,
) => {
const { user } = useStore() const { user } = useStore()
const permissions = user.getPermissions() const permissions = user.getPermissions()
const role: Array<string> = user.getRole() const role: Array<string> = user.getRole()
const edition = user.getEdition()
if (!permission) { if (!permission) {
return true return true
} }
if (isFunction(permission)) { if (isFunction(permission)) {
permission = (permission as PF)() permission = (permission as PF)()
} }
@ -25,10 +34,22 @@ const hasPermissionChild = (permission: Role | string | Permission | ComplexPerm
if (permission instanceof Permission) { if (permission instanceof Permission) {
return permissions.includes(permission.permission) return permissions.includes(permission.permission)
} }
if (permission instanceof Edition) {
return permission.edition === edition
}
if (permission instanceof ComplexPermission) { if (permission instanceof ComplexPermission) {
const permissionOk = permission.permissionList.some((p) => permissions.includes(p)) const permissionOk = permission.permissionList.some((p) =>
const roleOk = role.some((r) => permission.roleList.includes(r)) permissions.includes(isFunction(p) ? (p as CPF)().toString() : p.toString()),
return permission.compare === 'AND' ? permissionOk && roleOk : permissionOk || roleOk )
const roleList = permission.roleList
const roleOk = roleList.some((r) =>
role.includes(isFunction(r) ? (r as CRF)().toString() : r.toString()),
)
const editionOK = permission.editionList.includes(edition.toString())
return permission.compare === 'AND'
? permissionOk && roleOk && editionOK
: (permissionOk || roleOk) && editionOK
} }
if (typeof permission === 'string') { if (typeof permission === 'string') {
return permissions.includes(permission) return permissions.includes(permission)
@ -45,10 +66,11 @@ const hasPermissionChild = (permission: Role | string | Permission | ComplexPerm
*/ */
export const hasPermission = ( export const hasPermission = (
permission: permission:
| Array<Role | string | Permission | ComplexPermission | PF> | Array<Role | string | Permission | ComplexPermission | Edition | PF>
| Role | Role
| string | string
| Permission | Permission
| Edition
| ComplexPermission | ComplexPermission
| PF, | PF,
compare: 'OR' | 'AND', compare: 'OR' | 'AND',

View File

@ -1,4 +1,7 @@
import useStore from '@/stores' import useStore from '@/stores'
export type PF = () => Role | string | Permission | ComplexPermission
export type CRF = () => Role | string
export type CPF = () => Permission | string
/** /**
* *
*/ */
@ -13,6 +16,13 @@ export class Role {
const { user } = useStore() const { user } = useStore()
return new Role(`${this.role}:/WORKSPACE/${user.getWorkspaceId()}`) return new Role(`${this.role}:/WORKSPACE/${user.getWorkspaceId()}`)
} }
getWorkspaceRoleString = () => {
const { user } = useStore()
return `${this.role}:/WORKSPACE/${user.getWorkspaceId()}`
}
toString() {
return this.role
}
} }
/** /**
* *
@ -43,20 +53,44 @@ export class Permission {
const { user } = useStore() const { user } = useStore()
return `${this.permission}:/WORKSPACE/${user.getWorkspaceId()}/${resource}/${resource_id}` return `${this.permission}:/WORKSPACE/${user.getWorkspaceId()}/${resource}/${resource_id}`
} }
toString() {
return this.permission
} }
}
/** /**
* *
*/ */
export class ComplexPermission { export class ComplexPermission {
roleList: Array<string> roleList: Array<string | Role | CRF>
permissionList: Array<string> permissionList: Array<string | Permission | CPF>
editionList: Array<string | Edition>
compare: 'OR' | 'AND' compare: 'OR' | 'AND'
constructor(roleList: Array<string>, permissionList: Array<string>, compare: 'OR' | 'AND') { constructor(
roleList: Array<string | Role | CRF>,
permissionList: Array<string | Permission | CPF>,
editionList: Array<string | Edition>,
compare: 'OR' | 'AND',
) {
this.roleList = roleList this.roleList = roleList
this.permissionList = permissionList this.permissionList = permissionList
this.editionList = editionList
this.compare = compare this.compare = compare
} }
} }
/**
*
*/
export class Edition {
edition: string
constructor(edition: string) {
this.edition = edition
}
toString() {
return this.edition
}
}

View File

@ -0,0 +1,74 @@
<template>
<div>说明: v-hasPermission 是使用v-show 本质上组件是渲染的 v-if="hasPermission('xxxx')"</div>
<div>这种方式组件不会渲染(用于比如像组件挂载的时候需要调用接口,不想让组件渲染)</div>
<div>比如工作空间的下拉列表组件使用v-if 示例 企业版组件:</div>
<button v-if="hasPermission(EditionConst.IS_CE, 'OR')">我是社区版组件</button>
<button v-hasPermission="EditionConst.IS_CE">我是社区版组件</button>
<!-- ================我是企业版组件================== -->
<button v-if="hasPermission(EditionConst.IS_EE, 'OR')">我是企业版组件</button>
<button v-hasPermission="EditionConst.IS_EE">我是企业版组件</button>
<!-- ================企业版组件 并且是ADMIN角色================== -->
<button v-if="hasPermission([EditionConst.IS_EE, RoleConst.ADMIN], 'AND')">
我是企业版并且是ADMIN角色
</button>
<button
v-hasPermission="new ComplexPermission([RoleConst.ADMIN], [], [EditionConst.IS_EE], 'AND')"
>
我是企业版并且是ADMIN角色
</button>
<!-- ================企业版组件 并且是当前工作空间管理员================== -->
<button
v-if="hasPermission([EditionConst.IS_EE, RoleConst.WORKSPACE_MANAGE.getWorkspaceRole], 'AND')"
>
我是企业版并且拥有当前工作空间管理员角色
</button>
<button
v-hasPermission="
new ComplexPermission(
[RoleConst.WORKSPACE_MANAGE.getWorkspaceRole],
[],
[EditionConst.IS_EE],
'OR',
)
"
>
我是企业版并且拥有当前工作空间管理员角色
</button>
<!-- ================企业版组件 并且是当前工作空间管理员 或者有用户只读================== -->
<button
v-if="
hasPermission(
new ComplexPermission(
[RoleConst.WORKSPACE_MANAGE.getWorkspaceRole],
[PermissionConst.USER_READ],
[EditionConst.IS_EE],
'OR',
),
'OR',
)
"
>
我是企业版 并且是当前工作空间管理员 或者有用户只读
</button>
<button
v-hasPermission="
new ComplexPermission(
[RoleConst.WORKSPACE_MANAGE.getWorkspaceRole],
[PermissionConst.USER_READ],
[EditionConst.IS_EE],
'OR',
)
"
>
我是企业版并且是当前工作空间管理员 或者有用户只读
</button>
</template>
<script setup lang="ts">
import { PermissionConst, EditionConst, RoleConst } from '@/utils/permission/data'
import { hasPermission } from '@/utils/permission/index'
import { ComplexPermission } from '@/utils/permission/type'
</script>
<style lang="scss" scoped></style>