优化
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { Permission } from '@/modules/iam/types'
|
||||
import { buildPermissionTree, getDescendantIds, summarizePermissionSelection } from '@/modules/iam/utils/permissionTree'
|
||||
import { buildNavigationPermissionTree, buildPermissionTree, getDescendantIds, summarizePermissionSelection } from '@/modules/iam/utils/permissionTree'
|
||||
import { adminNavigation } from '@/core/navigation/adminNavigation'
|
||||
|
||||
const permissions: Permission[] = [
|
||||
{ id: 'action-b', parentId: 'menu', name: '操作 B', code: 'demo:b', type: '按钮', path: '', method: '-', status: '启用', sort: 20 },
|
||||
@@ -52,4 +53,24 @@ describe('permissionTree', () => {
|
||||
actionCount: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('groups assignable permissions by the current admin navigation and hides technical containers', () => {
|
||||
const rolePermissions: Permission[] = [
|
||||
{ id: 'supplier', parentId: null, name: '供应商旧名称', code: 'platform:supplier:list', type: '菜单', path: '/suppliers', method: 'GET', status: '启用', sort: 10 },
|
||||
{ id: 'supplier-create', parentId: 'supplier', name: '新增供应商', code: 'platform:supplier:create', type: '按钮', path: '', method: 'POST', status: '启用', sort: 10 },
|
||||
{ id: 'express', parentId: null, name: '快递公司', code: 'platform:express:list', type: '菜单', path: '/master-data/express-companies', method: 'GET', status: '启用', sort: 20 },
|
||||
{ id: 'counterparty', parentId: null, name: '往来单位', code: 'platform:counterparty:list', type: '菜单', path: '/master-data/counterparties', method: 'GET', status: '启用', sort: 30 },
|
||||
{ id: 'technical', parentId: null, name: '服务接口', code: 'platform:technical:invoke', type: '接口', path: '/internal', method: 'POST', status: '启用', sort: 40 },
|
||||
]
|
||||
|
||||
const tree = buildNavigationPermissionTree(rolePermissions, adminNavigation)
|
||||
const general = tree.find((node) => node.name === '通用')
|
||||
|
||||
expect(tree.some((node) => node.name === '供应商中心')).toBe(false)
|
||||
expect(general?.permissionId).toBeUndefined()
|
||||
expect(general?.children?.map((node) => node.name)).toEqual(['供应商列表', '快递公司', '往来单位'])
|
||||
expect(general?.children?.[0]?.children?.map((node) => node.name)).toEqual(['供应商旧名称', '新增供应商'])
|
||||
expect(tree.some((node) => node.name === '其他权限')).toBe(false)
|
||||
expect(JSON.stringify(tree)).not.toContain('platform:technical:invoke')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import type { Permission } from '@/modules/iam/types'
|
||||
import type { AdminNavigationNode } from '@/core/navigation/adminNavigation'
|
||||
|
||||
export type PermissionTreeNode = Permission & { children?: PermissionTreeNode[] }
|
||||
export type PermissionMatcher = (permission: Permission) => boolean
|
||||
export interface RolePermissionTreeNode {
|
||||
id: string
|
||||
permissionId?: string
|
||||
name: string
|
||||
code: string
|
||||
children?: RolePermissionTreeNode[]
|
||||
}
|
||||
|
||||
function groupByParent(permissions: Permission[]) {
|
||||
const childrenByParent = new Map<string | null, Permission[]>()
|
||||
@@ -73,3 +81,61 @@ export function summarizePermissionSelection(permissions: Permission[], selected
|
||||
actionCount: selectedPermissions.filter((permission) => ['按钮', '接口'].includes(permission.type)).length,
|
||||
}
|
||||
}
|
||||
|
||||
export function buildNavigationPermissionTree(
|
||||
permissions: Permission[],
|
||||
navigation: AdminNavigationNode[],
|
||||
): RolePermissionTreeNode[] {
|
||||
const childrenByParent = groupByParent(permissions)
|
||||
const permissionByCode = new Map(permissions.map((permission) => [permission.code, permission]))
|
||||
const usedPermissionIds = new Set<string>()
|
||||
|
||||
function permissionLeaves(permission: Permission, ancestors: Set<string>): RolePermissionTreeNode[] {
|
||||
if (ancestors.has(permission.id)) return []
|
||||
|
||||
usedPermissionIds.add(permission.id)
|
||||
const nextAncestors = new Set(ancestors).add(permission.id)
|
||||
const children = (childrenByParent.get(permission.id) ?? [])
|
||||
.flatMap((child) => permissionLeaves(child, nextAncestors))
|
||||
|
||||
return [{ id: permission.id, permissionId: permission.id, name: permission.name, code: permission.code }, ...children]
|
||||
}
|
||||
|
||||
function navigationNode(node: AdminNavigationNode): RolePermissionTreeNode | null {
|
||||
if (node.hidden) return null
|
||||
|
||||
if (node.children?.length) {
|
||||
const children = node.children
|
||||
.map(navigationNode)
|
||||
.filter((child): child is RolePermissionTreeNode => child !== null)
|
||||
if (!children.length) return null
|
||||
return {
|
||||
id: `navigation-group:${node.key}`,
|
||||
name: node.title,
|
||||
code: '',
|
||||
children,
|
||||
}
|
||||
}
|
||||
|
||||
const codes = [...new Set([node.permission, ...(node.permissionAnyOf ?? [])].filter(Boolean))] as string[]
|
||||
const permissionNodes = codes
|
||||
.map((code) => permissionByCode.get(code))
|
||||
.filter((permission): permission is Permission => permission !== undefined)
|
||||
.flatMap((permission) => usedPermissionIds.has(permission.id) ? [] : permissionLeaves(permission, new Set()))
|
||||
|
||||
if (permissionNodes.length) {
|
||||
return {
|
||||
id: `navigation-menu:${node.key}`,
|
||||
name: node.title,
|
||||
code: '',
|
||||
children: permissionNodes,
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const navigationGroups = navigation
|
||||
.map(navigationNode)
|
||||
.filter((node): node is RolePermissionTreeNode => node !== null)
|
||||
return navigationGroups
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user