Browse Source

feat(vben5): 实现角色菜单管理

pull/1188/head
colin 10 months ago
parent
commit
c1fb38a9c9
  1. 3
      apps/vben5/apps/app-antd/src/adapter/component/index.ts
  2. 1
      apps/vben5/packages/@abp/identity/package.json
  3. 14
      apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue
  4. 2
      apps/vben5/packages/@abp/platform/src/api/index.ts
  5. 50
      apps/vben5/packages/@abp/platform/src/api/useRoleMenusApi.ts
  6. 50
      apps/vben5/packages/@abp/platform/src/api/useUserMenusApi.ts
  7. 1
      apps/vben5/packages/@abp/platform/src/components/index.ts
  8. 216
      apps/vben5/packages/@abp/platform/src/components/menus/MenuAllotModal.vue
  9. 3
      apps/vben5/packages/@abp/platform/src/components/menus/types.ts
  10. 52
      apps/vben5/packages/@abp/platform/src/types/menus.ts

3
apps/vben5/apps/app-antd/src/adapter/component/index.ts

@ -38,6 +38,7 @@ import {
Switch,
Textarea,
TimePicker,
Tree,
TreeSelect,
Upload,
} from 'ant-design-vue';
@ -78,6 +79,7 @@ export type ComponentType =
| 'Switch'
| 'Textarea'
| 'TimePicker'
| 'Tree'
| 'TreeSelect'
| 'Upload'
| BaseFormComponentType;
@ -154,6 +156,7 @@ async function initComponentAdapter() {
Switch,
Textarea: withDefaultPlaceholder(Textarea, 'input'),
TimePicker,
Tree,
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
Upload,
FeatureStateCheck,

1
apps/vben5/packages/@abp/identity/package.json

@ -24,6 +24,7 @@
"@abp/core": "workspace:*",
"@abp/data-protection": "workspace:*",
"@abp/permissions": "workspace:*",
"@abp/platform": "workspace:*",
"@abp/request": "workspace:*",
"@abp/ui": "workspace:*",
"@ant-design/icons-vue": "catalog:",

14
apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue

@ -16,6 +16,7 @@ import { $t } from '@vben/locales';
import { AuditLogPermissions, EntityChangeDrawer } from '@abp/auditing';
import { Events, useAbpStore, useEventBus, useFeatures } from '@abp/core';
import { PermissionModal } from '@abp/permissions';
import { MenuAllotModal } from '@abp/platform';
import { useVbenVxeGrid } from '@abp/ui';
import {
DeleteOutlined,
@ -53,6 +54,9 @@ const [RolePermissionModal, permissionModalApi] = useVbenModal({
const [RoleClaimModal, claimModalApi] = useVbenModal({
connectedComponent: ClaimModal,
});
const [RoleMenuModal, menuModalApi] = useVbenModal({
connectedComponent: MenuAllotModal,
});
const [RoleRuleDrawer, roleRuleDrawerApi] = useVbenDrawer({
connectedComponent: RuleModal,
});
@ -178,13 +182,18 @@ const handleMenuClick = async (row: IdentityRoleDto, info: MenuInfo) => {
roleRuleDrawerApi.open();
break;
}
case 'menus': {
menuModalApi.setData({
identity: row.name,
});
menuModalApi.open();
break;
}
case 'permissions': {
const roles = abpStore.application?.currentUser.roles ?? [];
permissionModalApi.setData({
displayName: row.name,
providerKey: row.name,
providerName: 'R',
readonly: roles.includes(row.name),
});
permissionModalApi.open();
break;
@ -296,6 +305,7 @@ function onPermissionChange(_name: string, key: string) {
<RolePermissionModal @change="onPermissionChange" />
<RoleChangeDrawer />
<RoleRuleDrawer />
<RoleMenuModal subject="role" />
</template>
<style lang="scss" scoped></style>

2
apps/vben5/packages/@abp/platform/src/api/index.ts

@ -2,4 +2,6 @@ export { useDataDictionariesApi } from './useDataDictionariesApi';
export { useEmailMessagesApi } from './useEmailMessagesApi';
export { useLayoutsApi } from './useLayoutsApi';
export { useMenusApi } from './useMenusApi';
export { useRoleMenusApi } from './useRoleMenusApi';
export { useSmsMessagesApi } from './useSmsMessagesApi';
export { useUserMenusApi } from './useUserMenusApi';

50
apps/vben5/packages/@abp/platform/src/api/useRoleMenusApi.ts

@ -0,0 +1,50 @@
import type { ListResultDto } from '@abp/core';
import type {
MenuDto,
MenuGetByRoleInput,
SetRoleMenuInput,
SetRoleMenuStartupInput,
} from '../types/menus';
import { useRequest } from '@abp/request';
export function useRoleMenusApi() {
const { cancel, request } = useRequest();
function getAllApi(
input: MenuGetByRoleInput,
): Promise<ListResultDto<MenuDto>> {
return request<ListResultDto<MenuDto>>(
`/api/platform/menus/by-role/${input.role}/${input.framework}`,
{
method: 'GET',
params: input,
},
);
}
function setMenusApi(input: SetRoleMenuInput): Promise<void> {
return request('/api/platform/menus/by-role', {
data: input,
method: 'PUT',
});
}
function setStartupMenuApi(
meudId: string,
input: SetRoleMenuStartupInput,
): Promise<void> {
return request(`/api/platform/menus/startup/${meudId}/by-role`, {
data: input,
method: 'PUT',
});
}
return {
cancel,
getAllApi,
setMenusApi,
setStartupMenuApi,
};
}

50
apps/vben5/packages/@abp/platform/src/api/useUserMenusApi.ts

@ -0,0 +1,50 @@
import type { ListResultDto } from '@abp/core';
import type {
MenuDto,
MenuGetByUserInput,
SetUserMenuInput,
SetUserMenuStartupInput,
} from '../types/menus';
import { useRequest } from '@abp/request';
export function useUserMenusApi() {
const { cancel, request } = useRequest();
function getAllApi(
input: MenuGetByUserInput,
): Promise<ListResultDto<MenuDto>> {
return request<ListResultDto<MenuDto>>(
`/api/platform/menus/by-user/${input.userId}/${input.framework}`,
{
method: 'GET',
params: input,
},
);
}
function setMenusApi(input: SetUserMenuInput): Promise<void> {
return request('/api/platform/menus/by-user', {
data: input,
method: 'PUT',
});
}
function setStartupMenuApi(
meudId: string,
input: SetUserMenuStartupInput,
): Promise<void> {
return request(`/api/platform/menus/startup/${meudId}/by-user`, {
data: input,
method: 'PUT',
});
}
return {
cancel,
getAllApi,
setMenusApi,
setStartupMenuApi,
};
}

1
apps/vben5/packages/@abp/platform/src/components/index.ts

@ -1,5 +1,6 @@
export { default as DataDictionaryTable } from './data-dictionaries/DataDictionaryTable.vue';
export { default as LayoutTable } from './layouts/LayoutTable.vue';
export { default as MenuAllotModal } from './menus/MenuAllotModal.vue';
export { default as MenuTable } from './menus/MenuTable.vue';
export { default as EmailMessageTable } from './messages/email/EmailMessageTable.vue';
export { default as SmsMessageTable } from './messages/sms/SmsMessageTable.vue';

216
apps/vben5/packages/@abp/platform/src/components/menus/MenuAllotModal.vue

@ -0,0 +1,216 @@
<script setup lang="ts">
import type { MenuSubject } from './types';
import { ref } from 'vue';
import { useVbenForm, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { listToTree } from '@abp/core';
import { message } from 'ant-design-vue';
import {
useDataDictionariesApi,
useMenusApi,
useRoleMenusApi,
useUserMenusApi,
} from '../../api';
interface ModalState {
identity: string;
}
defineOptions({
name: 'MenuAllotModal',
});
const props = defineProps<{
subject: MenuSubject;
}>();
const checkedMenuIds = ref<string[]>([]);
const { getAllApi: getAllMenusApi } = useMenusApi();
const { getAllApi: getUserMenusApi, setMenusApi: setUserMenusApi } =
useUserMenusApi();
const { getAllApi: getRoleMenusApi, setMenusApi: setRoleMenusApi } =
useRoleMenusApi();
const { getByNameApi: getDataDictionaryByNameApi } = useDataDictionariesApi();
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
},
handleSubmit: onSubmit,
schema: [
{
component: 'ApiSelect',
componentProps: {
allowClear: true,
api: () => getDataDictionaryByNameApi('UI Framework'),
labelField: 'displayName',
onChange: onInitMenus,
resultField: 'items',
valueField: 'name',
},
dependencies: {
if: (values) => {
return !values?.id;
},
triggerFields: ['id'],
},
fieldName: 'framework',
label: $t('AppPlatform.DisplayName:UIFramework'),
rules: 'selectRequired',
},
{
component: 'TreeSelect',
componentProps: {
allowClear: true,
blockNode: true,
fieldNames: {
label: 'displayName',
value: 'id',
},
},
dependencies: {
if(values) {
return !!values.framework;
},
triggerFields: ['framework'],
},
fieldName: 'startupMenuId',
label: $t('AppPlatform.Menu:SetStartup'),
},
{
component: 'Tree',
componentProps: {
allowClear: true,
blockNode: true,
checkable: true,
checkStrictly: true,
fieldNames: {
key: 'id',
title: 'displayName',
},
onCheck: onMenuChecked,
},
dependencies: {
if(values) {
return !!values.framework;
},
triggerFields: ['framework'],
},
fieldName: 'menuIds',
label: $t('AppPlatform.DisplayName:Menus'),
modelPropName: 'checkedKeys',
},
],
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
onConfirm: async () => {
await formApi.validateAndSubmitForm();
},
});
function onMenuChecked(checkedKeys: { checked: string[] }) {
checkedMenuIds.value = checkedKeys.checked;
}
async function onInitMenus(framework?: string) {
if (!framework) {
formApi.updateSchema([
{
componentProps: {
treeData: [],
},
fieldName: 'menuIds',
},
{
componentProps: {
treeData: [],
},
fieldName: 'startupMenuId',
},
]);
formApi.setFieldValue('menuIds', []);
return;
}
const state = modalApi.getData<ModalState>();
const api =
props.subject === 'user'
? getUserMenusApi({
framework,
userId: state.identity,
})
: getRoleMenusApi({
framework,
role: state.identity,
});
const [allMenus, identityMenus] = await Promise.all([
getAllMenusApi({
framework,
}),
api,
]);
const treeData = listToTree(allMenus.items, {
id: 'id',
pid: 'parentId',
});
formApi.updateSchema([
{
componentProps: {
treeData,
},
fieldName: 'menuIds',
},
{
componentProps: {
treeData,
},
fieldName: 'startupMenuId',
},
]);
checkedMenuIds.value = identityMenus.items.map((item) => item.id);
formApi.setFieldValue('menuIds', checkedMenuIds.value);
const startupMenu = identityMenus.items.find((item) => item.startup);
formApi.setFieldValue('startupMenuId', startupMenu?.id);
}
async function onSubmit(values: Record<string, any>) {
try {
modalApi.setState({ submitting: true });
const state = modalApi.getData<ModalState>();
const api =
props.subject === 'user'
? setUserMenusApi({
framework: values.framework,
menuIds: checkedMenuIds.value,
startupMenuId: values.startupMenuId,
userId: state.identity,
})
: setRoleMenusApi({
framework: values.framework,
menuIds: checkedMenuIds.value,
roleName: state.identity,
startupMenuId: values.startupMenuId,
});
await api;
message.success($t('AbpUi.SavedSuccessfully'));
modalApi.close();
} finally {
modalApi.setState({ submitting: false });
}
}
</script>
<template>
<Modal :title="$t('AppPlatform.Menu:Manage')">
<Form />
</Modal>
</template>
<style scoped></style>

3
apps/vben5/packages/@abp/platform/src/components/menus/types.ts

@ -0,0 +1,3 @@
type MenuSubject = 'role' | 'user';
export type { MenuSubject };

52
apps/vben5/packages/@abp/platform/src/types/menus.ts

@ -26,6 +26,44 @@ interface MenuCreateDto extends MenuCreateOrUpdateDto {
layoutId: string;
}
interface MenuGetInput {
framework?: string;
}
interface MenuGetByUserInput {
framework: string;
userId: string;
}
interface MenuGetByRoleInput {
framework: string;
role: string;
}
interface SetUserMenuInput {
framework?: string;
menuIds: string[];
startupMenuId?: string;
userId: string;
}
interface SetUserMenuStartupInput {
framework?: string;
userId: string;
}
interface SetRoleMenuInput {
framework?: string;
menuIds: string[];
roleName: string;
startupMenuId?: string;
}
interface SetRoleMenuStartupInput {
framework?: string;
roleName: string;
}
type MenuUpdateDto = MenuCreateOrUpdateDto;
interface MenuGetAllInput {
@ -36,4 +74,16 @@ interface MenuGetAllInput {
sorting?: string;
}
export type { MenuCreateDto, MenuDto, MenuGetAllInput, MenuUpdateDto };
export type {
MenuCreateDto,
MenuDto,
MenuGetAllInput,
MenuGetByRoleInput,
MenuGetByUserInput,
MenuGetInput,
MenuUpdateDto,
SetRoleMenuInput,
SetRoleMenuStartupInput,
SetUserMenuInput,
SetUserMenuStartupInput,
};

Loading…
Cancel
Save