Browse Source

feat(permissions): 增加权限管理模态框

pull/1047/head
colin 1 year ago
parent
commit
2d585e9e3f
  1. 36
      apps/vben5/packages/@abp/permission/package.json
  2. 37
      apps/vben5/packages/@abp/permission/src/api/permissions.ts
  3. 7
      apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionModal.vue
  4. 7
      apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionTable.vue
  5. 7
      apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionModal.vue
  6. 7
      apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionTable.vue
  7. 3
      apps/vben5/packages/@abp/permission/src/components/index.ts
  8. 301
      apps/vben5/packages/@abp/permission/src/components/permissions/PermissionModal.vue
  9. 2
      apps/vben5/packages/@abp/permission/src/index.ts
  10. 1
      apps/vben5/packages/@abp/permission/src/types/index.ts
  11. 60
      apps/vben5/packages/@abp/permission/src/types/permissions.ts
  12. 143
      apps/vben5/packages/@abp/permission/src/utils/index.ts
  13. 6
      apps/vben5/packages/@abp/permission/tsconfig.json
  14. 4
      apps/vben5/vben-admin.code-workspace

36
apps/vben5/packages/@abp/permission/package.json

@ -0,0 +1,36 @@
{
"name": "@abp/permission",
"version": "8.3.2",
"homepage": "https://github.com/colinin/abp-next-admin",
"bugs": "https://github.com/colinin/abp-next-admin/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/colinin/abp-next-admin.git",
"directory": "packages/@abp/permission"
},
"license": "MIT",
"type": "module",
"sideEffects": [
"**/*.css"
],
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
}
},
"dependencies": {
"@abp/core": "workspace:*",
"@abp/request": "workspace:*",
"@abp/ui": "workspace:*",
"@ant-design/icons-vue": "catalog:",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"ant-design-vue": "catalog:",
"vue": "catalog:*"
}
}

37
apps/vben5/packages/@abp/permission/src/api/permissions.ts

@ -0,0 +1,37 @@
import type {
PermissionProvider,
PermissionResultDto,
PermissionsUpdateDto,
} from '../types/permissions';
import { requestClient } from '@abp/request';
/**
*
* @param provider
* @returns
*/
export function getApi(
provider: PermissionProvider,
): Promise<PermissionResultDto> {
return requestClient.get<PermissionResultDto>(
`/api/permission-management/permissions`,
{
params: provider,
},
);
}
/**
*
* @param provider
* @param input
*/
export function updateApi(
provider: PermissionProvider,
input: PermissionsUpdateDto,
): Promise<void> {
return requestClient.put(`/api/permission-management/permissions`, input, {
params: provider,
});
}

7
apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionModal.vue

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div></div>
</template>
<style scoped></style>

7
apps/vben5/packages/@abp/permission/src/components/definitions/groups/PermissionGroupDefinitionTable.vue

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div></div>
</template>
<style scoped></style>

7
apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionModal.vue

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div></div>
</template>
<style scoped></style>

7
apps/vben5/packages/@abp/permission/src/components/definitions/permissions/PermissionDefinitionTable.vue

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div></div>
</template>
<style scoped></style>

3
apps/vben5/packages/@abp/permission/src/components/index.ts

@ -0,0 +1,3 @@
export { default as PermissionGroupDefinitionTable } from './definitions/groups/PermissionGroupDefinitionTable.vue';
export { default as PermissionDefinitionTable } from './definitions/permissions/PermissionDefinitionTable.vue';
export { default as PermissionModal } from './permissions/PermissionModal.vue';

301
apps/vben5/packages/@abp/permission/src/components/permissions/PermissionModal.vue

@ -0,0 +1,301 @@
<script setup lang="ts">
import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
import type {
DataNode,
EventDataNode,
} from 'ant-design-vue/es/vc-tree/interface';
import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
import type { PermissionTree } from '../../types/permissions';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { Card, Checkbox, Divider, message, Tabs, Tree } from 'ant-design-vue';
import { getApi, updateApi } from '../../api/permissions';
import {
generatePermissionTree,
getGrantedPermissionKeys,
getGrantPermissionCount,
getGrantPermissionsCount,
getParentList,
getPermissionCount,
getPermissionsCount,
toPermissionList,
} from '../../utils';
defineOptions({
name: 'PermissionModal',
});
const TabPane = Tabs.TabPane;
interface ModalState {
displayName?: string;
providerKey?: string;
providerName: string;
readonly?: boolean;
}
const modelState = ref<ModalState>();
const expandNodeKeys = ref<string[]>([]);
const checkedNodeKeys = ref<string[]>([]);
const permissionTree = ref<PermissionTree[]>([]);
const getPermissionTab = computed(() => {
return (tree: PermissionTree) => {
const grantCount = getGrantPermissionCount(tree);
return `${tree.displayName} (${grantCount})`;
};
});
const getPermissionState = computed(() => {
const treeList = permissionTree.value;
const grantCount = getGrantPermissionsCount(treeList);
const permissionCount = getPermissionsCount(treeList);
return {
checked: grantCount === permissionCount,
indeterminate: grantCount > 0 && grantCount < permissionCount,
};
});
const getPermissionNodeState = computed(() => {
return (tree: PermissionTree) => {
const grantCount = getGrantPermissionCount(tree);
const permissionCount = getPermissionCount(tree);
return {
checked: grantCount === permissionCount,
indeterminate: grantCount > 0 && grantCount < permissionCount,
};
};
});
const [Modal, modalApi] = useVbenModal({
class: 'w-1/2',
closeOnClickModal: false,
closeOnPressEscape: false,
draggable: true,
fullscreenButton: false,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
const permissions = toPermissionList(permissionTree.value);
try {
modalApi.setState({
closable: false,
confirmLoading: true,
});
await updateApi(
{
providerKey: modelState.value!.providerKey,
providerName: modelState.value!.providerName,
},
{
permissions,
},
);
message.success($t('AbpUi.SavedSuccessfully'));
modalApi.close();
} finally {
modalApi.setState({
closable: true,
confirmLoading: false,
});
}
},
async onOpenChange(isOpen: boolean) {
permissionTree.value = [];
modelState.value = undefined;
if (isOpen) {
const state = modalApi.getData<ModalState>();
modelState.value = state;
const dto = await getApi({
providerKey: state.providerKey,
providerName: state.providerName,
});
modalApi.setState({
title: `${$t('AbpPermissionManagement.Permissions')} - ${state.displayName ?? dto.entityDisplayName}`,
});
permissionTree.value = generatePermissionTree(dto.groups);
checkedNodeKeys.value = getGrantedPermissionKeys(permissionTree.value);
}
},
title: $t('AbpPermissionManagement.Permissions'),
});
/** 全选所有节点权限 */
function onCheckAll(e: CheckboxChangeEvent) {
checkedNodeKeys.value = [];
permissionTree.value.forEach((current) => {
const children = getChildren(current.children);
children.forEach((permission) => {
permission.isGranted = e.target.checked;
if (e.target.checked) {
checkedNodeKeys.value.push(permission.name);
}
});
current.isGranted = e.target.checked;
});
}
/** 全选当前节点权限 */
function onCheckNodeAll(e: CheckboxChangeEvent, permission: PermissionTree) {
const children = getChildren(permission.children ?? []);
children.forEach((permission) => {
permission.isGranted = e.target.checked;
});
const childKeys = children.map((node) => node.name);
checkedNodeKeys.value = e.target.checked
? [...checkedNodeKeys.value, ...childKeys]
: checkedNodeKeys.value.filter((key) => !childKeys.includes(key));
permission.isGranted = e.target.checked;
}
function onSelectNode(
_: any,
info: {
node: EventDataNode;
selectedNodes: DataNode[];
},
) {
const nodeKey = String(info.node.key);
const index = expandNodeKeys.value.indexOf(nodeKey);
expandNodeKeys.value =
index === -1
? [...expandNodeKeys.value, nodeKey]
: expandNodeKeys.value.filter((key) => key !== nodeKey);
}
function onCheckNode(permission: PermissionTree, _keys: any, info: CheckInfo) {
const nodeKey = String(info.node.key);
const index = checkedNodeKeys.value.indexOf(nodeKey);
checkedNodeKeys.value =
index === -1
? [...checkedNodeKeys.value, nodeKey]
: checkedNodeKeys.value.filter((key) => key !== nodeKey);
const currentPermission = info.node.dataRef as unknown as PermissionTree;
//
checkParentGrant(permission, currentPermission, info.checked);
//
checkChildrenGrant(currentPermission, info.checked);
//
currentPermission.isGranted = info.checked;
}
function checkChildrenGrant(current: PermissionTree, isGranted: boolean) {
const children = getChildren(current.children);
//
if (!isGranted) {
const childKeys: string[] = [];
children.forEach((node) => {
!isGranted && (node.isGranted = false);
childKeys.push(node.name);
});
checkedNodeKeys.value = checkedNodeKeys.value.filter(
(key) => !childKeys.includes(key),
);
}
}
function checkParentGrant(
root: PermissionTree,
current: PermissionTree,
isGranted: boolean,
) {
if (!isGranted || !current.parentName) {
return;
}
const parentNodes = getParentList(root.children, current.parentName);
if (parentNodes) {
const parentKeys: string[] = [];
parentNodes.forEach((node) => {
node.isGranted = true;
parentKeys.push(node.name);
if (!checkedNodeKeys.value.includes(node.name)) {
parentKeys.push(node.name);
}
});
checkedNodeKeys.value = [...checkedNodeKeys.value, ...parentKeys];
}
}
function getChildren(permissions: PermissionTree[]): PermissionTree[] {
const children: PermissionTree[] = [];
permissions.forEach((permission) => {
children.push(permission);
permission.children && children.push(...getChildren(permission.children));
});
return children;
}
</script>
<template>
<Modal>
<div class="flex flex-col content-center justify-center">
<div>
<Checkbox
:disabled="modelState?.readonly"
@change="onCheckAll"
v-bind="getPermissionState"
>
{{ $t('AbpPermissionManagement.SelectAllInAllTabs') }}
</Checkbox>
</div>
<Divider />
<Tabs tab-position="left" type="card">
<template v-for="permission in permissionTree" :key="permission.name">
<TabPane
:tab="getPermissionTab(permission)"
:tab-key="permission.name"
>
<Card :bordered="false" :title="permission.displayName">
<div class="flex flex-col">
<Checkbox
:disabled="modelState?.readonly"
v-bind="getPermissionNodeState(permission)"
@change="(e) => onCheckNodeAll(e, permission)"
>
{{ $t('AbpPermissionManagement.SelectAllInThisTab') }}
</Checkbox>
<Divider />
<Tree
:check-strictly="true"
:checkable="true"
:checked-keys="checkedNodeKeys"
:disabled="modelState?.readonly"
:expanded-keys="expandNodeKeys"
:field-names="{
key: 'name',
title: 'displayName',
children: 'children',
}"
:tree-data="permission.children"
@check="(keys, info) => onCheckNode(permission, keys, info)"
@select="onSelectNode"
/>
</div>
</Card>
</TabPane>
</template>
</Tabs>
</div>
</Modal>
</template>
<style lang="scss" scoped>
:deep(.ant-tabs) {
max-height: 34rem;
.ant-tabs-nav {
max-width: 14rem;
}
.ant-tabs-content-holder {
overflow: hidden auto !important;
}
}
</style>

2
apps/vben5/packages/@abp/permission/src/index.ts

@ -0,0 +1,2 @@
export * from './components';
export * from './types';

1
apps/vben5/packages/@abp/permission/src/types/index.ts

@ -0,0 +1 @@
export * from './permissions';

60
apps/vben5/packages/@abp/permission/src/types/permissions.ts

@ -0,0 +1,60 @@
interface PermissionProvider {
providerKey?: string;
providerName: string;
}
interface PermissionDto {
allowedProviders: string[];
displayName: string;
grantedProviders: PermissionProvider[];
isGranted: boolean;
name: string;
parentName?: string;
}
interface PermissionGroupDto {
displayName: string;
name: string;
permissions: PermissionDto[];
}
interface PermissionUpdateDto {
/** 是否授权 */
isGranted: boolean;
/** 权限名称 */
name: string;
}
interface PermissionsUpdateDto {
permissions: PermissionUpdateDto[];
}
interface PermissionResultDto {
entityDisplayName: string;
groups: PermissionGroupDto[];
}
interface PermissionTree {
/** 子节点 */
children: PermissionTree[];
/** 是否禁用 */
disabled: boolean;
/** 显示名称 */
displayName: string;
/** 是否授权 */
isGranted?: boolean;
isRoot: boolean;
/** 权限标识 */
name: string;
/** 父节点 */
parentName?: string;
}
export type {
PermissionDto,
PermissionGroupDto,
PermissionProvider,
PermissionResultDto,
PermissionsUpdateDto,
PermissionTree,
};

143
apps/vben5/packages/@abp/permission/src/utils/index.ts

@ -0,0 +1,143 @@
import type {
PermissionDto,
PermissionGroupDto,
PermissionTree,
} from '../types/permissions';
import { listToTree } from '@abp/core';
export function generatePermissionTree(
permissionGroups: PermissionGroupDto[],
): PermissionTree[] {
const trees: PermissionTree[] = [];
permissionGroups.forEach((g) => {
const tree: PermissionTree = {
disabled: false,
displayName: g.displayName,
isRoot: true,
name: g.name,
parentName: g.name,
children: [],
};
tree.children = listToTree(g.permissions, {
id: 'name',
pid: 'parentName',
});
trees.push(tree);
});
return trees;
}
export function findNode(
children: PermissionTree[],
key: string,
): PermissionTree | undefined {
let findC: PermissionTree | undefined;
for (const child of children) {
if (child.name === key) {
findC = child;
return findC;
}
findC = findNode(child.children, key);
if (findC) {
return findC;
}
}
return findC;
}
export function getPermissionsCount(children: PermissionTree[]): number {
let count = 0;
children.forEach((c) => {
count += getPermissionCount(c);
});
return count;
}
export function getPermissionCount(tree: PermissionTree): number {
let count = tree.children.length;
tree.children.forEach((c) => {
count += getPermissionCount(c);
});
return count;
}
export function getGrantPermissionsCount(children: PermissionTree[]): number {
let count = 0;
children.forEach((c) => {
count += getGrantPermissionCount(c);
});
return count;
}
export function getGrantPermissionCount(tree: PermissionTree): number {
return getGrantedPermissionKeys(tree.children).length;
}
export function getGrantedPermissionKeys(children: PermissionTree[]): string[] {
const keys: string[] = [];
children.forEach((c) => {
if (c.isGranted === true) {
keys.push(c.name);
}
keys.push(...getGrantedPermissionKeys(c.children));
});
return keys;
}
export function getParentList(
children: PermissionTree[],
name: string,
): PermissionTree[] | undefined {
for (const child of children) {
if (child.name === name) {
return [child];
}
if (child.children) {
const node = getParentList(child.children, name);
if (node) {
return [...node, child];
}
}
}
}
export function toPermissionList(treeList: PermissionTree[]) {
const permissions: PermissionDto[] = [];
for (const element of treeList) {
if (!element.isRoot && element.isGranted !== undefined) {
permissions.push({
allowedProviders: [],
displayName: element.displayName,
grantedProviders: [],
isGranted: element.isGranted,
name: element.name,
});
}
permissions.push(...toPermissionList(element.children));
}
return permissions;
}
export function updateParentGrant(
tree: PermissionTree,
name: string,
grant: boolean,
) {
const parentList = getParentList(tree.children, name);
if (parentList && Array.isArray(parentList)) {
for (const element of parentList) {
element.isGranted = grant;
}
}
}
export function updateChildrenGrant(
children: PermissionTree[],
grant: boolean,
) {
for (const child of children) {
child.isGranted = grant;
updateChildrenGrant(child.children, grant);
}
}

6
apps/vben5/packages/@abp/permission/tsconfig.json

@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": ["src"],
"exclude": ["node_modules"]
}

4
apps/vben5/vben-admin.code-workspace

@ -192,5 +192,9 @@
"name": "@abp/account",
"path": "packages/@abp/account",
},
{
"name": "@abp/permission",
"path": "packages/@abp/permission",
},
],
}

Loading…
Cancel
Save