Browse Source

feat(users): 增加用户声明管理

pull/1051/head
colin 1 year ago
parent
commit
e6ccd8b629
  1. 13
      apps/vben5/packages/@abp/identity/src/api/claim-types.ts
  2. 54
      apps/vben5/packages/@abp/identity/src/api/users.ts
  3. 27
      apps/vben5/packages/@abp/identity/src/components/claim-types/ClaimTypeModal.vue
  4. 4
      apps/vben5/packages/@abp/identity/src/components/claim-types/ClaimTypeTable.vue
  5. 8
      apps/vben5/packages/@abp/identity/src/components/organization-units/SelectMemberModal.vue
  6. 29
      apps/vben5/packages/@abp/identity/src/components/roles/RoleModal.vue
  7. 4
      apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue
  8. 119
      apps/vben5/packages/@abp/identity/src/components/users/UserClaimEditModal.vue
  9. 159
      apps/vben5/packages/@abp/identity/src/components/users/UserClaimModal.vue
  10. 20
      apps/vben5/packages/@abp/identity/src/components/users/UserModal.vue
  11. 16
      apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue
  12. 17
      apps/vben5/packages/@abp/identity/src/types/users.ts
  13. 2
      apps/vben5/packages/@abp/permission/src/components/permissions/PermissionModal.vue

13
apps/vben5/packages/@abp/identity/src/api/claim-types.ts

@ -1,4 +1,4 @@
import type { PagedResultDto } from '@abp/core';
import type { ListResultDto, PagedResultDto } from '@abp/core';
import type {
GetIdentityClaimTypePagedListInput,
@ -72,3 +72,14 @@ export function getPagedListApi(
},
);
}
/**
*
*/
export function getAssignableClaimsApi(): Promise<
ListResultDto<IdentityClaimTypeDto>
> {
return requestClient.get<ListResultDto<IdentityClaimTypeDto>>(
`/api/identity/claim-types/actived-list`,
);
}

54
apps/vben5/packages/@abp/identity/src/api/users.ts

@ -4,6 +4,10 @@ import type { IdentityRoleDto, OrganizationUnitDto } from '../types';
import type {
ChangeUserPasswordInput,
GetUserPagedListInput,
IdentityUserClaimCreateDto,
IdentityUserClaimDeleteDto,
IdentityUserClaimDto,
IdentityUserClaimUpdateDto,
IdentityUserCreateDto,
IdentityUserDto,
IdentityUserUpdateDto,
@ -147,3 +151,53 @@ export function getRolesApi(
`/api/identity/users/${id}/roles`,
);
}
/**
*
* @param id id
*/
export function getClaimsApi(
id: string,
): Promise<ListResultDto<IdentityUserClaimDto>> {
return requestClient.get<ListResultDto<IdentityUserClaimDto>>(
`/api/identity/users/${id}/claims`,
);
}
/**
*
* @param id id
* @param input dto
*/
export function deleteClaimApi(
id: string,
input: IdentityUserClaimDeleteDto,
): Promise<void> {
return requestClient.delete(`/api/identity/users/${id}/claims`, {
params: input,
});
}
/**
*
* @param id id
* @param input dto
*/
export function createClaimApi(
id: string,
input: IdentityUserClaimCreateDto,
): Promise<void> {
return requestClient.post(`/api/identity/users/${id}/claims`, input);
}
/**
*
* @param id id
* @param input dto
*/
export function updateClaimApi(
id: string,
input: IdentityUserClaimUpdateDto,
): Promise<void> {
return requestClient.put(`/api/identity/users/${id}/claims`, input);
}

27
apps/vben5/packages/@abp/identity/src/components/claim-types/ClaimTypeModal.vue

@ -79,24 +79,23 @@ const [Modal, modalApi] = useVbenModal({
},
onOpenChange: async (isOpen: boolean) => {
if (isOpen) {
const { values } = modalApi.getData<Record<string, any>>();
if (values?.id) {
modalApi.setState({ loading: true });
return getApi(values.id)
.then((dto) => {
formModel.value = dto;
modalApi.setState({
title: `${$t('AbpIdentity.DisplayName:ClaimType')} - ${dto.name}`,
});
})
.finally(() => {
modalApi.setState({ loading: false });
});
}
formModel.value = { ...defaultModel };
modalApi.setState({
title: $t('AbpIdentity.IdentityClaim:New'),
});
const claimTypeDto = modalApi.getData<IdentityClaimTypeDto>();
if (claimTypeDto?.id) {
modalApi.setState({ loading: true });
try {
const dto = await getApi(claimTypeDto.id);
formModel.value = dto;
modalApi.setState({
title: `${$t('AbpIdentity.DisplayName:ClaimType')} - ${dto.name}`,
});
} finally {
modalApi.setState({ loading: false });
}
}
}
},
title: 'ClaimType',

4
apps/vben5/packages/@abp/identity/src/components/claim-types/ClaimTypeTable.vue

@ -157,9 +157,7 @@ const handleAdd = () => {
};
const handleEdit = (row: IdentityClaimTypeDto) => {
roleModalApi.setData({
values: row,
});
roleModalApi.setData(row);
roleModalApi.open();
};

8
apps/vben5/packages/@abp/identity/src/components/organization-units/SelectMemberModal.vue

@ -1,4 +1,6 @@
<script setup lang="ts">
import type { VxeGridListeners, VxeGridProps } from '@abp/ui';
import type { IdentityUserDto } from '../../types/users';
import { defineEmits, defineOptions, nextTick, ref, toValue } from 'vue';
@ -6,11 +8,7 @@ import { defineEmits, defineOptions, nextTick, ref, toValue } from 'vue';
import { useVbenModal, type VbenFormProps } from '@vben/common-ui';
import { $t } from '@vben/locales';
import {
useVbenVxeGrid,
type VxeGridListeners,
type VxeGridProps,
} from '@abp/ui';
import { useVbenVxeGrid } from '@abp/ui';
import { getUnaddedUserListApi } from '../../api/organization-units';

29
apps/vben5/packages/@abp/identity/src/components/roles/RoleModal.vue

@ -54,24 +54,23 @@ const [Modal, modalApi] = useVbenModal({
},
onOpenChange: async (isOpen: boolean) => {
if (isOpen) {
const { values } = modalApi.getData<Record<string, any>>();
if (values?.id) {
modalApi.setState({ loading: true });
return getApi(values.id)
.then((dto) => {
formModel.value = dto;
modalApi.setState({
title: $t('AbpIdentity.RoleSubject', [dto.name]),
});
})
.finally(() => {
modalApi.setState({ loading: false });
});
}
formModel.value = { ...defaultModel };
modalApi.setState({
title: $t('NewRole'),
title: $t('AbpIdentity.NewRole'),
});
const roleDto = modalApi.getData<IdentityRoleDto>();
if (roleDto?.id) {
try {
modalApi.setState({ loading: true });
const dto = await getApi(roleDto.id);
formModel.value = dto;
modalApi.setState({
title: $t('AbpIdentity.RoleSubject', [dto.name]),
});
} finally {
modalApi.setState({ loading: false });
}
}
}
},
title: 'Roles',

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

@ -117,9 +117,7 @@ const handleAdd = () => {
};
const handleEdit = (row: IdentityRoleDto) => {
roleModalApi.setData({
values: row,
});
roleModalApi.setData(row);
roleModalApi.open();
};

119
apps/vben5/packages/@abp/identity/src/components/users/UserClaimEditModal.vue

@ -0,0 +1,119 @@
<script setup lang="ts">
import type { IdentityUserClaimDto } from '../../types/users';
import { useVbenForm, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { getAssignableClaimsApi } from '../../api/claim-types';
import { createClaimApi, updateClaimApi } from '../../api/users';
interface IdentityUserClaimVto extends IdentityUserClaimDto {
userId: string;
}
const emits = defineEmits<{
(event: 'change', data: IdentityUserClaimDto): void;
}>();
const [Form, formApi] = useVbenForm({
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
handleSubmit: onSubmit,
schema: [
{
component: 'Select',
fieldName: 'claimType',
label: $t('AbpIdentity.DisplayName:ClaimType'),
rules: 'required',
},
{
component: 'Textarea',
fieldName: 'claimValue',
label: $t('AbpIdentity.DisplayName:ClaimValue'),
rules: 'required',
},
],
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
draggable: true,
fullscreenButton: false,
async onConfirm() {
await formApi.validateAndSubmitForm();
},
async onOpenChange(isOpen: boolean) {
if (isOpen) {
try {
modalApi.setState({ loading: true });
const claimVto = modalApi.getData<IdentityUserClaimVto>();
formApi.setValues({
...claimVto,
newClaimValue: claimVto.claimValue,
});
// ,
formApi.updateSchema([
{
disabled: !!claimVto.id,
fieldName: 'claimType',
},
]);
//
!claimVto.id && (await initAssignableClaims());
} finally {
modalApi.setState({ loading: false });
}
}
},
title: $t('AbpIdentity.ManageClaim'),
});
/** 初始化可用声明类型 */
async function initAssignableClaims() {
const { items } = await getAssignableClaimsApi();
formApi.updateSchema([
{
componentProps: {
fieldNames: {
label: 'name',
value: 'name',
},
options: items,
},
fieldName: 'claimType',
},
]);
}
/** 提交声明类型变更 */
async function onSubmit(values: Record<string, any>) {
try {
modalApi.setState({ confirmLoading: true });
const claimVto = modalApi.getData<IdentityUserClaimVto>();
const api = claimVto.id
? updateClaimApi(claimVto.userId, {
claimType: claimVto.claimType,
claimValue: claimVto.claimValue,
newClaimValue: values.claimValue,
})
: createClaimApi(claimVto.userId, {
claimType: values.claimType,
claimValue: values.claimValue,
});
await api;
emits('change', values as IdentityUserClaimDto);
modalApi.close();
} finally {
modalApi.setState({ confirmLoading: false });
}
}
</script>
<template>
<Modal>
<Form />
</Modal>
</template>
<style scoped></style>

159
apps/vben5/packages/@abp/identity/src/components/users/UserClaimModal.vue

@ -0,0 +1,159 @@
<script setup lang="ts">
import type { VxeGridProps } from '@abp/ui';
import type { IdentityUserClaimDto, IdentityUserDto } from '../../types';
import { defineAsyncComponent, h } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { useVbenVxeGrid } from '@abp/ui';
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue';
import { Button, Popconfirm } from 'ant-design-vue';
import { deleteClaimApi, getClaimsApi } from '../../api/users';
import { IdentityUserPermissions } from '../../constants/permissions';
const ClaimEditModal = defineAsyncComponent(
() => import('./UserClaimEditModal.vue'),
);
const [Modal, modalApi] = useVbenModal({
draggable: true,
fullscreenButton: false,
onCancel() {
modalApi.close();
},
onConfirm: async () => {},
showCancelButton: false,
showConfirmButton: false,
title: $t('AbpIdentity.ManageClaim'),
});
const gridOptions: VxeGridProps<IdentityUserClaimDto> = {
columns: [
{
field: 'claimType',
minWidth: 120,
title: $t('AbpIdentity.DisplayName:ClaimType'),
},
{
field: 'claimValue',
title: $t('AbpIdentity.DisplayName:ClaimValue'),
width: 'auto',
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: $t('AbpUi.Actions'),
width: 180,
},
],
exportConfig: {},
keepSource: true,
pagerConfig: {
enabled: false,
},
proxyConfig: {
ajax: {
query: async () => {
const { id } = modalApi.getData<IdentityUserDto>();
return await getClaimsApi(id);
},
},
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {
custom: true,
export: true,
// import: true,
refresh: true,
zoom: true,
},
};
const [Grid, { query }] = useVbenVxeGrid({
gridOptions,
});
const [UserClaimEditModal, editModalApi] = useVbenModal({
connectedComponent: ClaimEditModal,
});
function onCreate() {
const { id } = modalApi.getData<IdentityUserDto>();
editModalApi.setData({
userId: id,
});
editModalApi.open();
}
async function onDelete(row: IdentityUserClaimDto) {
const { id } = modalApi.getData<IdentityUserDto>();
await deleteClaimApi(id, {
claimType: row.claimType,
claimValue: row.claimValue,
}).then(() => query());
}
function onUpdate(row: IdentityUserClaimDto) {
const { id } = modalApi.getData<IdentityUserDto>();
editModalApi.setData({
userId: id,
...row,
});
editModalApi.open();
}
</script>
<template>
<Modal>
<Grid>
<template #toolbar-tools>
<Button
type="primary"
v-access:code="[IdentityUserPermissions.ManageClaims]"
@click="onCreate"
>
{{ $t('AbpIdentity.AddClaim') }}
</Button>
</template>
<template #action="{ row }">
<div class="flex flex-row">
<div class="basis-1/2">
<Button
:icon="h(EditOutlined)"
block
type="link"
v-access:code="[IdentityUserPermissions.ManageClaims]"
@click="onUpdate(row)"
>
{{ $t('AbpUi.Edit') }}
</Button>
</div>
<div class="basis-1/2">
<Popconfirm
:title="$t('AbpIdentity.WillDeleteClaim', [row.claimType])"
@confirm="onDelete(row)"
>
<Button
:icon="h(DeleteOutlined)"
block
danger
type="link"
v-access:code="[IdentityUserPermissions.Delete]"
>
{{ $t('AbpUi.Delete') }}
</Button>
</Popconfirm>
</div>
</div>
</template>
</Grid>
<UserClaimEditModal @change="() => query()" />
</Modal>
</template>
<style scoped></style>

20
apps/vben5/packages/@abp/identity/src/components/users/UserModal.vue

@ -93,15 +93,18 @@ const [Modal, modalApi] = useVbenModal({
formModel.value = { ...defaultModel };
modalApi.setState({
loading: true,
title: $t('NewUser'),
title: $t('AbpIdentity.NewUser'),
});
try {
const { values } = modalApi.getData<Record<string, any>>();
const userDto = modalApi.getData<IdentityUserDto>();
const manageRolePolicy = checkManageRolePolicy();
if (values?.id) {
await initUserInfo(values.id);
manageRolePolicy && (await initUserRoles(values.id));
checkManageOuPolicy() && (await initOrganizationUnitTree(values.id));
if (userDto?.id) {
await initUserInfo(userDto.id);
manageRolePolicy && (await initUserRoles(userDto.id));
checkManageOuPolicy() && (await initOrganizationUnitTree(userDto.id));
modalApi.setState({
title: `${$t('AbpIdentity.Users')} - ${userDto.userName}`,
});
}
manageRolePolicy && (await initAssignableRoles());
} finally {
@ -275,7 +278,10 @@ async function onLoadOuChildren(node: EventDataNode) {
height: '338px',
}"
:render="(item) => item.title"
:titles="[$t('AbpIdentity.Assigned'), $t('AbpIdentity.Available')]"
:titles="[
$t('AbpIdentityServer.Assigned'),
$t('AbpIdentityServer.Available'),
]"
class="tree-transfer"
/>
</TabPane>

16
apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue

@ -19,6 +19,7 @@ import {
EditOutlined,
EllipsisOutlined,
LockOutlined,
PlusOutlined,
UnlockOutlined,
} from '@ant-design/icons-vue';
import { Button, Dropdown, Menu, Modal } from 'ant-design-vue';
@ -32,6 +33,7 @@ defineOptions({
const UserModal = defineAsyncComponent(() => import('./UserModal.vue'));
const LockModal = defineAsyncComponent(() => import('./UserLockModal.vue'));
const ClaimModal = defineAsyncComponent(() => import('./UserClaimModal.vue'));
const PasswordModal = defineAsyncComponent(
() => import('./UserPasswordModal.vue'),
);
@ -151,6 +153,9 @@ const [UserLockModal, lockModalApi] = useVbenModal({
const [UserPasswordModal, pwdModalApi] = useVbenModal({
connectedComponent: PasswordModal,
});
const [UserClaimModal, claimModalApi] = useVbenModal({
connectedComponent: ClaimModal,
});
const [UserPermissionModal, permissionModalApi] = useVbenModal({
connectedComponent: PermissionModal,
});
@ -166,9 +171,7 @@ const handleAdd = () => {
};
const handleEdit = (row: IdentityUserDto) => {
userModalApi.setData({
values: row,
});
userModalApi.setData(row);
userModalApi.open();
};
@ -190,6 +193,11 @@ const handleUnlock = async (row: IdentityUserDto) => {
const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
switch (info.key) {
case 'claims': {
claimModalApi.setData(row);
claimModalApi.open();
break;
}
case 'lock': {
lockModalApi.setData(row);
lockModalApi.open();
@ -223,6 +231,7 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
<Grid :table-title="$t('AbpIdentity.Users')">
<template #toolbar-tools>
<Button
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[IdentityUserPermissions.Create]"
@click="handleAdd"
@ -332,6 +341,7 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
</template>
</Grid>
<UserLockModal @change="query" />
<UserClaimModal @change="query" />
<UserEditModal @change="() => query()" />
<UserPasswordModal @change="query" />
<UserPermissionModal />

17
apps/vben5/packages/@abp/identity/src/types/users.ts

@ -6,6 +6,8 @@ import type {
PagedAndSortedResultRequestDto,
} from '@abp/core';
import type { IdentityClaimDto } from './claim-types';
/** 用户对象接口 */
interface IUser {
/** 邮件地址 */
@ -90,10 +92,25 @@ interface GetUserPagedListInput extends PagedAndSortedResultRequestDto {
type IdentityUserCreateDto = IdentityUserCreateOrUpdateDto;
type IdentityUserUpdateDto = IdentityUserCreateOrUpdateDto;
interface IdentityUserClaimDto extends IdentityClaimDto {
id: string;
}
interface IdentityUserClaimUpdateDto extends IdentityClaimDto {
newClaimValue: string;
}
type IdentityUserClaimDeleteDto = IdentityClaimDto;
type IdentityUserClaimCreateDto = IdentityClaimDto;
export type {
ChangeMyPasswordInput,
ChangeUserPasswordInput,
GetUserPagedListInput,
IdentityUserClaimCreateDto,
IdentityUserClaimDeleteDto,
IdentityUserClaimDto,
IdentityUserClaimUpdateDto,
IdentityUserCreateDto,
IdentityUserDto,
IdentityUserOrganizationUnitUpdateDto,

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

@ -115,6 +115,7 @@ const [Modal, modalApi] = useVbenModal({
const state = modalApi.getData<ModalState>();
modelState.value = state;
modalApi.setState({
confirmLoading: true,
loading: true,
});
try {
@ -129,6 +130,7 @@ const [Modal, modalApi] = useVbenModal({
checkedNodeKeys.value = getGrantedPermissionKeys(permissionTree.value);
} finally {
modalApi.setState({
confirmLoading: false,
loading: false,
});
}

Loading…
Cancel
Save