Browse Source

feat(vben5-identity): increased organizational implementation

pull/1043/head
colin 1 year ago
parent
commit
06fd819a48
  1. 8
      apps/vben5/apps/app-antd/.vite/deps/_metadata.json
  2. 3
      apps/vben5/apps/app-antd/.vite/deps/package.json
  3. 3
      apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json
  4. 3
      apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json
  5. 14
      apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts
  6. 4
      apps/vben5/apps/app-antd/src/views/identity/claim-types/index.vue
  7. 15
      apps/vben5/apps/app-antd/src/views/identity/organization-units/index.vue
  8. 4
      apps/vben5/apps/app-antd/src/views/identity/roles/index.vue
  9. 4
      apps/vben5/apps/app-antd/src/views/identity/security-logs/index.vue
  10. 4
      apps/vben5/apps/app-antd/src/views/identity/users/index.vue
  11. 1
      apps/vben5/packages/@abp/core/src/utils/index.ts
  12. 37
      apps/vben5/packages/@abp/core/src/utils/tree.ts
  13. 227
      apps/vben5/packages/@abp/identity/src/api/organization-units.ts
  14. 16
      apps/vben5/packages/@abp/identity/src/api/roles.ts
  15. 16
      apps/vben5/packages/@abp/identity/src/api/users.ts
  16. 4
      apps/vben5/packages/@abp/identity/src/components/claim-types/ClaimTypeModal.vue
  17. 2
      apps/vben5/packages/@abp/identity/src/components/claim-types/ClaimTypeTable.vue
  18. 5
      apps/vben5/packages/@abp/identity/src/components/index.ts
  19. 11
      apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitModal.vue
  20. 29
      apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitPage.vue
  21. 181
      apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitRoleTable.vue
  22. 36
      apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitTable.vue
  23. 203
      apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitTree.vue
  24. 183
      apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitUserTable.vue
  25. 134
      apps/vben5/packages/@abp/identity/src/components/organization-units/SelectMemberModal.vue
  26. 127
      apps/vben5/packages/@abp/identity/src/components/organization-units/SelectRoleModal.vue
  27. 4
      apps/vben5/packages/@abp/identity/src/components/roles/RoleModal.vue
  28. 7
      apps/vben5/packages/@abp/identity/src/components/roles/RoleSelectModal.vue
  29. 6
      apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue
  30. 3
      apps/vben5/packages/@abp/identity/src/components/security-logs/SecurityLogTable.vue
  31. 6
      apps/vben5/packages/@abp/identity/src/components/users/UserModal.vue
  32. 4
      apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue
  33. 5
      apps/vben5/packages/@abp/identity/src/types/index.ts
  34. 96
      apps/vben5/packages/@abp/identity/src/types/organization-units.ts
  35. 0
      apps/vben5/packages/@abp/identity/src/types/roles.ts
  36. 0
      apps/vben5/packages/@abp/identity/src/types/users.ts

8
apps/vben5/apps/app-antd/.vite/deps/_metadata.json

@ -0,0 +1,8 @@
{
"hash": "62932afa",
"configHash": "fdef0d8d",
"lockfileHash": "5ba0e736",
"browserHash": "cfcea6c4",
"optimized": {},
"chunks": {}
}

3
apps/vben5/apps/app-antd/.vite/deps/package.json

@ -0,0 +1,3 @@
{
"type": "module"
}

3
apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json

@ -7,7 +7,8 @@
"user": "User",
"role": "Role",
"claimTypes": "Claim Types",
"securityLogs": "Security Logs"
"securityLogs": "Security Logs",
"organizationUnits": "Organization Units"
}
}
}

3
apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json

@ -7,7 +7,8 @@
"user": "用户",
"role": "角色",
"claimTypes": "身份标识",
"securityLogs": "安全日志"
"securityLogs": "安全日志",
"organizationUnits": "组织机构"
}
}
}

14
apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts

@ -32,7 +32,7 @@ const routes: RouteRecordRaw[] = [
path: '/manage/identity',
children: [
{
component: () => import('#/views/identity/user/index.vue'),
component: () => import('#/views/identity/users/index.vue'),
name: 'Users',
path: '/manage/identity/users',
meta: {
@ -41,7 +41,7 @@ const routes: RouteRecordRaw[] = [
},
},
{
component: () => import('#/views/identity/role/index.vue'),
component: () => import('#/views/identity/roles/index.vue'),
name: 'Roles',
path: '/manage/identity/roles',
meta: {
@ -69,6 +69,16 @@ const routes: RouteRecordRaw[] = [
icon: 'carbon:security',
},
},
{
component: () =>
import('#/views/identity/organization-units/index.vue'),
name: 'OrganizationUnits',
path: '/manage/identity/organization-units',
meta: {
title: $t('abp.manage.identity.organizationUnits'),
icon: 'clarity:organization-line',
},
},
],
},
],

4
apps/vben5/apps/app-antd/src/views/identity/claim-types/index.vue

@ -2,6 +2,10 @@
import { Page } from '@vben/common-ui';
import { ClaimTypeTable } from '@abp/identity';
defineOptions({
name: 'IdentityClaimTypes',
});
</script>
<template>

15
apps/vben5/apps/app-antd/src/views/identity/organization-units/index.vue

@ -0,0 +1,15 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { OrganizationUnitPage } from '@abp/identity';
defineOptions({
name: 'IdentityOrganizationUnits',
});
</script>
<template>
<Page>
<OrganizationUnitPage />
</Page>
</template>

4
apps/vben5/apps/app-antd/src/views/identity/role/index.vue → apps/vben5/apps/app-antd/src/views/identity/roles/index.vue

@ -2,6 +2,10 @@
import { Page } from '@vben/common-ui';
import { RoleTable } from '@abp/identity';
defineOptions({
name: 'IdentityRoles',
});
</script>
<template>

4
apps/vben5/apps/app-antd/src/views/identity/security-logs/index.vue

@ -2,6 +2,10 @@
import { Page } from '@vben/common-ui';
import { SecurityLogTable } from '@abp/identity';
defineOptions({
name: 'IdentitySecurityLogs',
});
</script>
<template>

4
apps/vben5/apps/app-antd/src/views/identity/user/index.vue → apps/vben5/apps/app-antd/src/views/identity/users/index.vue

@ -2,6 +2,10 @@
import { Page } from '@vben/common-ui';
import { UserTable } from '@abp/identity';
defineOptions({
name: 'IdentityUsers',
});
</script>
<template>

1
apps/vben5/packages/@abp/core/src/utils/index.ts

@ -1 +1,2 @@
export * from './date';
export * from './tree';

37
apps/vben5/packages/@abp/core/src/utils/tree.ts

@ -0,0 +1,37 @@
interface TreeHelperConfig {
children: string;
id: string;
pid: string;
}
const DEFAULT_CONFIG: TreeHelperConfig = {
id: 'id',
pid: 'pid',
children: 'children',
};
const getConfig = (config: Partial<TreeHelperConfig>) =>
Object.assign({}, DEFAULT_CONFIG, config);
// tree from list
export function listToTree<T = any>(
list: any[],
config: Partial<TreeHelperConfig> = {},
): T[] {
const conf = getConfig(config) as TreeHelperConfig;
const nodeMap = new Map();
const result: T[] = [];
const { id, pid, children } = conf;
for (const node of list) {
node[children] = node[children] || [];
nodeMap.set(node[id], node);
}
for (const node of list) {
const parent = nodeMap.get(node[pid]);
(parent ? parent[children] : result).push(node);
if (parent) {
parent.hasChildren = true;
}
}
return result;
}

227
apps/vben5/packages/@abp/identity/src/api/organization-units.ts

@ -0,0 +1,227 @@
import type { ListResultDto, PagedResultDto } from '@abp/core';
import type { IdentityRoleDto, IdentityUserDto } from '../types';
import type {
GetIdentityRolesInput,
GetIdentityUsersInput,
GetOrganizationUnitPagedListInput,
GetUnaddedRoleListInput,
GetUnaddedUserListInput,
OrganizationUnitAddRoleDto,
OrganizationUnitAddUserDto,
OrganizationUnitCreateDto,
OrganizationUnitDto,
OrganizationUnitGetChildrenDto,
OrganizationUnitUpdateDto,
} from '../types/organization-units';
import { requestClient } from '@abp/request';
/**
*
* @param input
* @returns
*/
export function createApi(
input: OrganizationUnitCreateDto,
): Promise<OrganizationUnitDto> {
return requestClient.post<OrganizationUnitDto>(
'/api/identity/organization-units',
input,
);
}
/**
*
* @param id id
*/
export function deleteApi(id: string): Promise<void> {
return requestClient.delete(`/api/identity/organization-units/${id}`);
}
/**
*
* @param id id
* @returns
*/
export function getApi(id: string): Promise<OrganizationUnitDto> {
return requestClient.get<OrganizationUnitDto>(
`/api/identity/organization-units/${id}`,
);
}
/**
*
* @param id id
* @returns
*/
export function updateApi(
id: string,
input: OrganizationUnitUpdateDto,
): Promise<OrganizationUnitDto> {
return requestClient.put<OrganizationUnitDto>(
`/api/identity/organization-units/${id}`,
input,
);
}
/**
*
* @param input
* @returns
*/
export function getPagedListApi(
input?: GetOrganizationUnitPagedListInput,
): Promise<PagedResultDto<OrganizationUnitDto>> {
return requestClient.get<PagedResultDto<OrganizationUnitDto>>(
`/api/identity/organization-units`,
{
params: input,
},
);
}
/**
*
* @returns
*/
export function getRootListApi(): Promise<ListResultDto<OrganizationUnitDto>> {
return requestClient.get<ListResultDto<OrganizationUnitDto>>(
`/api/identity/organization-units/root-node`,
);
}
/**
*
* @returns
*/
export function getAllListApi(): Promise<ListResultDto<OrganizationUnitDto>> {
return requestClient.get<ListResultDto<OrganizationUnitDto>>(
`/api/identity/organization-units/all`,
);
}
/**
*
* @param input
* @returns
*/
export function getChildrenApi(
input: OrganizationUnitGetChildrenDto,
): Promise<ListResultDto<OrganizationUnitDto>> {
return requestClient.get<ListResultDto<OrganizationUnitDto>>(
`/api/identity/organization-units/find-children`,
{
params: input,
},
);
}
/**
*
* @param id id
* @param input
* @returns
*/
export function getUserListApi(
id: string,
input?: GetIdentityUsersInput,
): Promise<PagedResultDto<IdentityUserDto>> {
return requestClient.get<PagedResultDto<IdentityUserDto>>(
`/api/identity/organization-units/${id}/users`,
{
params: input,
},
);
}
/**
*
* @param input
* @returns
*/
export function getUnaddedUserListApi(
input: GetUnaddedUserListInput,
): Promise<PagedResultDto<IdentityUserDto>> {
return requestClient.get<PagedResultDto<IdentityUserDto>>(
`/api/identity/organization-units/${input.id}/unadded-users`,
{
params: input,
},
);
}
/**
*
* @param id id
* @param input id列表
*/
export function addMembers(
id: string,
input: OrganizationUnitAddUserDto,
): Promise<void> {
return requestClient.post(
`/api/identity/organization-units/${id}/users`,
input,
);
}
/**
*
* @param id id
* @param input
* @returns
*/
export function getRoleListApi(
id: string,
input?: GetIdentityRolesInput,
): Promise<PagedResultDto<IdentityRoleDto>> {
return requestClient.get<PagedResultDto<IdentityRoleDto>>(
`/api/identity/organization-units/${id}/roles`,
{
params: input,
},
);
}
/**
*
* @param input
* @returns
*/
export function getUnaddedRoleListApi(
input: GetUnaddedRoleListInput,
): Promise<PagedResultDto<IdentityRoleDto>> {
return requestClient.get<PagedResultDto<IdentityRoleDto>>(
`/api/identity/organization-units/${input.id}/unadded-roles`,
{
params: input,
},
);
}
/**
*
* @param id id
* @param input id列表
*/
export function addRoles(
id: string,
input: OrganizationUnitAddRoleDto,
): Promise<void> {
return requestClient.post(
`/api/identity/organization-units/${id}/roles`,
input,
);
}
/**
*
* @param id id
* @param parentId id
*/
export function moveTo(id: string, parentId?: string): Promise<void> {
return requestClient.put(`api/identity/organization-units/${id}/move`, {
parentId,
});
}

16
apps/vben5/packages/@abp/identity/src/api/role.ts → apps/vben5/packages/@abp/identity/src/api/roles.ts

@ -5,7 +5,7 @@ import type {
IdentityRoleCreateDto,
IdentityRoleDto,
IdentityRoleUpdateDto,
} from '../types/role';
} from '../types/roles';
import { requestClient } from '@abp/request';
@ -64,3 +64,17 @@ export function getPagedListApi(
},
);
}
/**
*
* @param id id
* @param ouId id
*/
export function removeOrganizationUnit(
id: string,
ouId: string,
): Promise<void> {
return requestClient.delete(
`/api/identity/roles/${id}/organization-units/${ouId}`,
);
}

16
apps/vben5/packages/@abp/identity/src/api/user.ts → apps/vben5/packages/@abp/identity/src/api/users.ts

@ -5,7 +5,7 @@ import type {
IdentityUserCreateDto,
IdentityUserDto,
IdentityUserUpdateDto,
} from '../types/user';
} from '../types/users';
import { requestClient } from '@abp/request';
@ -64,3 +64,17 @@ export function getPagedListApi(
},
);
}
/**
*
* @param id id
* @param ouId id
*/
export function removeOrganizationUnit(
id: string,
ouId: string,
): Promise<void> {
return requestClient.delete(
`/api/identity/users/${id}/organization-units/${ouId}`,
);
}

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

@ -22,7 +22,7 @@ import { createApi, getApi, updateApi } from '../../api/claim-types';
import { ValueType } from '../../types/claim-types';
defineOptions({
name: 'RoleModal',
name: 'ClaimTypeModal',
});
const emits = defineEmits<{
(event: 'change', data: IdentityClaimTypeDto): void;
@ -138,7 +138,7 @@ const [Modal, modalApi] = useVbenModal({
<FormItem :label="$t('AbpIdentity.IdentityClaim:Description')">
<Textarea
v-model:value="formModel.description"
:autosize="{ minRows: 2 }"
:auto-size="{ minRows: 2 }"
/>
</FormItem>
</Form>

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

@ -17,7 +17,7 @@ import { deleteApi, getPagedListApi } from '../../api/claim-types';
import { ValueType } from '../../types/claim-types';
defineOptions({
name: 'UserTable',
name: 'ClaimTypeTable',
});
const ClaimTypeModal = defineAsyncComponent(

5
apps/vben5/packages/@abp/identity/src/components/index.ts

@ -1,4 +1,5 @@
export { default as ClaimTypeTable } from './claim-types/ClaimTypeTable.vue';
export { default as RoleTable } from './role/RoleTable.vue';
export { default as OrganizationUnitPage } from './organization-units/OrganizationUnitPage.vue';
export { default as RoleTable } from './roles/RoleTable.vue';
export { default as SecurityLogTable } from './security-logs/SecurityLogTable.vue';
export { default as UserTable } from './user/UserTable.vue';
export { default as UserTable } from './users/UserTable.vue';

11
apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitModal.vue

@ -0,0 +1,11 @@
<script setup lang="ts">
defineOptions({
name: 'OrganizationUnitModal',
});
</script>
<template>
<div></div>
</template>
<style scoped></style>

29
apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitPage.vue

@ -0,0 +1,29 @@
<script setup lang="ts">
import { ref } from 'vue';
import OrganizationUnitTable from './OrganizationUnitTable.vue';
import OrganizationUnitTree from './OrganizationUnitTree.vue';
defineOptions({
name: 'OrganizationUnitPage',
});
const selectedKey = ref<string>();
const onSelected = (id?: string) => {
selectedKey.value = id;
};
</script>
<template>
<div class="flex flex-row gap-2">
<div class="basis-1/3">
<OrganizationUnitTree @selected="onSelected" />
</div>
<div class="basis-2/3">
<OrganizationUnitTable :selected-key="selectedKey" />
</div>
</div>
</template>
<style scoped></style>

181
apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitRoleTable.vue

@ -0,0 +1,181 @@
<script setup lang="ts">
import type { IdentityRoleDto } from '../../types/roles';
import { computed, defineAsyncComponent, h, nextTick, watchEffect } from 'vue';
import { useAccess } from '@vben/access';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import {
useVbenVxeGrid,
type VxeGridListeners,
type VxeGridProps,
} from '@abp/ui';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
import { Button, Modal } from 'ant-design-vue';
import { addRoles, getRoleListApi } from '../../api/organization-units';
import { removeOrganizationUnit } from '../../api/roles';
defineOptions({
name: 'OrganizationUnitRoleTable',
});
const props = defineProps<{
selectedKey?: string;
}>();
const SelectRoleModal = defineAsyncComponent(
() => import('./SelectRoleModal.vue'),
);
const { hasAccessByCodes } = useAccess();
const getAddRoleEnabled = computed(() => {
return (
props.selectedKey &&
hasAccessByCodes(['AbpIdentity.OrganizationUnits.ManageRoles'])
);
});
const gridOptions: VxeGridProps<IdentityRoleDto> = {
columns: [
{
field: 'name',
minWidth: '100px',
title: $t('AbpIdentity.DisplayName:RoleName'),
},
{
field: 'action',
fixed: 'right',
slots: { default: 'actions' },
title: $t('AbpUi.Actions'),
width: 180,
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
if (!props.selectedKey) {
return {
totalCount: 0,
items: [],
};
}
return await getRoleListApi(props.selectedKey!, {
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
},
},
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {
custom: true,
export: true,
// import: true,
refresh: true,
zoom: true,
},
};
const gridEvents: VxeGridListeners<IdentityRoleDto> = {
cellClick: () => {},
};
const [Grid, { query, setLoading }] = useVbenVxeGrid({
gridEvents,
gridOptions,
});
const [RoleModal, roleModalApi] = useVbenModal({
connectedComponent: SelectRoleModal,
});
const onRefresh = () => {
nextTick(query);
};
const onDelete = (row: IdentityRoleDto) => {
Modal.confirm({
centered: true,
content: $t('AbpIdentity.OrganizationUnit:AreYouSureRemoveRole', [
row.name,
]),
onOk: () => {
setLoading(true);
return removeOrganizationUnit(row.id, props.selectedKey!)
.then(onRefresh)
.finally(() => setLoading(false));
},
title: $t('AbpUi.AreYouSure'),
});
};
const onShowRole = () => {
roleModalApi.setData({
id: props.selectedKey,
});
roleModalApi.open();
};
const onCreateRole = (roles: IdentityRoleDto[]) => {
roleModalApi.setState({
closable: false,
confirmLoading: true,
});
addRoles(props.selectedKey!, {
roleIds: roles.map((item) => item.id),
})
.then(() => {
roleModalApi.close();
query();
})
.finally(() => {
roleModalApi.setState({
closable: true,
confirmLoading: false,
});
});
};
watchEffect(() => {
props.selectedKey && onRefresh();
!props.selectedKey && onRefresh();
});
</script>
<template>
<Grid :table-title="$t('AbpIdentity.Roles')">
<template #toolbar-tools>
<Button
v-if="getAddRoleEnabled"
:icon="h(PlusOutlined)"
type="primary"
@click="onShowRole"
>
{{ $t('AbpIdentity.OrganizationUnit:AddRole') }}
</Button>
</template>
<template #actions="{ row }">
<Button
:icon="h(DeleteOutlined)"
danger
type="link"
v-access:code="['AbpIdentity.OrganizationUnits.ManageRoles']"
@click="onDelete(row)"
>
{{ $t('AbpUi.Delete') }}
</Button>
</template>
</Grid>
<RoleModal @confirm="onCreateRole" />
</template>
<style scoped></style>

36
apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitTable.vue

@ -0,0 +1,36 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Card, Tabs } from 'ant-design-vue';
import OrganizationUnitRoleTable from './OrganizationUnitRoleTable.vue';
import OrganizationUnitUserTable from './OrganizationUnitUserTable.vue';
defineOptions({
name: 'OrganizationUnitTable',
});
const props = defineProps<{
selectedKey?: string;
}>();
const TabPane = Tabs.TabPane;
const activeTab = ref<'roles' | 'users'>('users');
</script>
<template>
<Card>
<Tabs v-model:active-key="activeTab">
<TabPane key="users" :tab="$t('AbpIdentity.Users')" />
<TabPane key="roles" :tab="$t('AbpIdentity.Roles')" />
</Tabs>
<OrganizationUnitUserTable
v-if="activeTab === 'users'"
:selected-key="props.selectedKey"
/>
<OrganizationUnitRoleTable v-else :selected-key="props.selectedKey" />
</Card>
</template>
<style scoped></style>

203
apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitTree.vue

@ -0,0 +1,203 @@
<script setup lang="ts">
import type {
AntTreeNodeDropEvent,
DataNode,
EventDataNode,
} from 'ant-design-vue/es/tree';
import type { Key } from 'ant-design-vue/es/vc-table/interface';
import { h, onMounted, ref, watchEffect } from 'vue';
import { createIconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
RedoOutlined,
} from '@ant-design/icons-vue';
import { Button, Card, Dropdown, Menu, Modal, Tree } from 'ant-design-vue';
import {
deleteApi,
getChildrenApi,
getRootListApi,
moveTo,
} from '../../api/organization-units';
defineOptions({
name: 'OrganizationUnitTree',
});
const emits = defineEmits<{
(event: 'selected', id?: string): void;
}>();
const MenuItem = Menu.Item;
const PermissionsOutlined = createIconifyIcon('icon-park-outline:permissions');
interface ContextMenuActionMap {
[key: string]: (id: string) => Promise<void> | void;
}
const actionsMap: ContextMenuActionMap = {
create: onCreate,
delete: onDelete,
permissions: onPermissions,
refresh: onRefresh,
update: onUpdate,
};
const organizationUnits = ref<DataNode[]>([]);
const expandedKeys = ref<string[]>([]);
const loadedKeys = ref<string[]>([]);
const selectedKey = ref<string>();
/** 刷新组织机构树 */
async function onRefresh() {
loadedKeys.value = [];
expandedKeys.value = [];
const { items } = await getRootListApi();
organizationUnits.value = items.map((item) => {
return {
isLeaf: false,
key: item.id,
title: item.displayName,
children: [],
};
});
}
/** 加载组织机构树节点 */
async function onLoad(node: EventDataNode) {
const nodeKey = String(node.key);
const { items } = await getChildrenApi({ id: nodeKey });
node.dataRef!.isLeaf = items.length === 0;
node.dataRef!.children = items.map((item): DataNode => {
return {
isLeaf: false,
key: item.id,
title: item.displayName,
children: [],
};
});
organizationUnits.value = [...organizationUnits.value];
expandedKeys.value.push(nodeKey);
loadedKeys.value.push(nodeKey);
}
/** 右键点击事件 */
function onRightClick() {
//
}
/** 创建组织机构树 */
function onCreate(parentId?: string) {
!parentId && console.warn('create root method not implemented!');
parentId && console.warn('create children method not implemented!');
}
/** 编辑组织机构树 */
function onUpdate(id: string) {
console.warn('update method not implemented!', id);
}
/** 编辑组织机构树权限 */
function onPermissions(id: string) {
console.warn('permissions method not implemented!', id);
}
/** 删除组织机构 */
function onDelete(id: string) {
Modal.confirm({
centered: true,
content: $t('AbpUi.ItemWillBeDeletedMessage'),
maskClosable: false,
onOk: () => {
return deleteApi(id).then(() => onRefresh());
},
title: $t('AbpUi.AreYouSure'),
});
}
/** 右键菜单点击事件 */
function onMenuClick(id: string, eventKey: Key) {
actionsMap[eventKey]!(id);
}
/** 组织机构选择变化事件 */
function onSelectChange(selectedKeys: Key[]) {
if (selectedKeys.length === 0) {
selectedKey.value = undefined;
return;
}
selectedKey.value = String(selectedKeys[0]);
}
/** 组织机构节点拖动事件 */
function onDrop(info: AntTreeNodeDropEvent) {
if (!info.dragNode.eventKey) {
return;
}
const eventKey = String(info.dragNode.eventKey);
const api =
info.dropPosition === -1
? moveTo(eventKey) // parent
: moveTo(eventKey, String(info.node.eventKey)); // children
api.then(() => onRefresh());
}
onMounted(onRefresh);
watchEffect(() => {
emits('selected', selectedKey.value);
});
</script>
<template>
<Card :title="$t('AbpIdentity.OrganizationUnit:Tree')">
<template #extra>
<Button :icon="h(PlusOutlined)" type="primary" @click="() => onCreate()">
{{ $t('AbpIdentity.OrganizationUnit:AddRoot') }}
</Button>
</template>
<Tree
:expanded-keys="expandedKeys"
:load-data="onLoad"
:loaded-keys="loadedKeys"
:tree-data="organizationUnits"
block-node
draggable
@drop="onDrop"
@right-click="onRightClick"
@select="onSelectChange"
>
<template #title="{ key: treeKey, title }">
<Dropdown :trigger="['contextmenu']">
<span> {{ title }}</span>
<template #overlay>
<Menu @click="({ key: menuKey }) => onMenuClick(treeKey, menuKey)">
<MenuItem key="update" :icon="h(EditOutlined)">
{{ $t('AbpUi.Edit') }}
</MenuItem>
<MenuItem key="create" :icon="h(PlusOutlined)">
{{ $t('AbpIdentity.OrganizationUnit:AddChildren') }}
</MenuItem>
<MenuItem key="delete" :icon="h(DeleteOutlined)">
{{ $t('AbpUi.Delete') }}
</MenuItem>
<MenuItem key="permissions" :icon="h(PermissionsOutlined)">
{{ $t('AbpIdentity.Permissions') }}
</MenuItem>
<MenuItem key="refresh" :icon="h(RedoOutlined)">
{{ $t('AbpIdentity.OrganizationUnit:RefreshRoot') }}
</MenuItem>
</Menu>
</template>
</Dropdown>
</template>
</Tree>
</Card>
</template>
<style scoped></style>

183
apps/vben5/packages/@abp/identity/src/components/organization-units/OrganizationUnitUserTable.vue

@ -0,0 +1,183 @@
<script setup lang="ts">
import type { VxeGridListeners, VxeGridProps } from '@abp/ui';
import type { IdentityUserDto } from '../../types/users';
import { computed, defineAsyncComponent, h, nextTick, watchEffect } from 'vue';
import { useAccess } from '@vben/access';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { useVbenVxeGrid } from '@abp/ui';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
import { Button, Modal } from 'ant-design-vue';
import { addMembers, getUserListApi } from '../../api/organization-units';
import { removeOrganizationUnit } from '../../api/users';
defineOptions({
name: 'OrganizationUnitUserTable',
});
const props = defineProps<{
selectedKey?: string;
}>();
const SelectMemberModal = defineAsyncComponent(
() => import('./SelectMemberModal.vue'),
);
const { hasAccessByCodes } = useAccess();
const getAddMemberEnabled = computed(() => {
return (
props.selectedKey &&
hasAccessByCodes(['AbpIdentity.OrganizationUnits.ManageUsers'])
);
});
const gridOptions: VxeGridProps<IdentityUserDto> = {
columns: [
{
field: 'userName',
minWidth: '100px',
title: $t('AbpIdentity.DisplayName:UserName'),
},
{
field: 'email',
minWidth: '120px',
title: $t('AbpIdentity.DisplayName:Email'),
},
{
field: 'action',
fixed: 'right',
slots: { default: 'actions' },
title: $t('AbpUi.Actions'),
width: 120,
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
if (!props.selectedKey) {
return {
totalCount: 0,
items: [],
};
}
return await getUserListApi(props.selectedKey!, {
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
},
},
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {
custom: true,
export: true,
// import: true,
refresh: true,
zoom: true,
},
};
const gridEvents: VxeGridListeners<IdentityUserDto> = {
cellClick: () => {},
};
const [Grid, { query, setLoading }] = useVbenVxeGrid({
gridEvents,
gridOptions,
});
const [MemberModal, userModalApi] = useVbenModal({
connectedComponent: SelectMemberModal,
});
const onRefresh = () => {
nextTick(query);
};
const onDelete = (row: IdentityUserDto) => {
Modal.confirm({
centered: true,
content: $t('AbpIdentity.OrganizationUnit:AreYouSureRemoveUser', [
row.userName,
]),
onOk: () => {
setLoading(true);
return removeOrganizationUnit(row.id, props.selectedKey!)
.then(onRefresh)
.finally(() => setLoading(false));
},
title: $t('AbpUi.AreYouSure'),
});
};
const onShowMember = () => {
userModalApi.setData({
id: props.selectedKey,
});
userModalApi.open();
};
const onCreateMember = (users: IdentityUserDto[]) => {
userModalApi.setState({
closable: false,
confirmLoading: true,
});
addMembers(props.selectedKey!, {
userIds: users.map((item) => item.id),
})
.then(() => {
userModalApi.close();
query();
})
.finally(() => {
userModalApi.setState({
closable: true,
confirmLoading: false,
});
});
};
watchEffect(() => {
props.selectedKey && onRefresh();
!props.selectedKey && onRefresh();
});
</script>
<template>
<Grid :table-title="$t('AbpIdentity.Users')">
<template #toolbar-tools>
<Button
v-if="getAddMemberEnabled"
:icon="h(PlusOutlined)"
type="primary"
@click="onShowMember"
>
{{ $t('AbpIdentity.OrganizationUnit:AddMember') }}
</Button>
</template>
<template #actions="{ row }">
<Button
:icon="h(DeleteOutlined)"
danger
type="link"
v-access:code="['AbpIdentity.OrganizationUnits.ManageUsers']"
@click="onDelete(row)"
>
{{ $t('AbpUi.Delete') }}
</Button>
</template>
</Grid>
<MemberModal @confirm="onCreateMember" />
</template>
<style scoped></style>

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

@ -0,0 +1,134 @@
<script setup lang="ts">
import type { IdentityUserDto } from '../../types/users';
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 { getUnaddedUserListApi } from '../../api/organization-units';
defineOptions({
name: 'SelectMemberModal',
});
const emits = defineEmits<{
(event: 'confirm', items: IdentityUserDto[]): void;
}>();
const selectedUsers = ref<IdentityUserDto[]>([]);
const formOptions: VbenFormProps = {
//
collapsed: false,
resetButtonOptions: {
show: false,
},
schema: [
{
component: 'Input',
componentProps: {
allowClear: true,
autocomplete: 'off',
},
fieldName: 'filter',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpUi.Search'),
},
],
//
showCollapseButton: false,
//
submitOnEnter: true,
};
const [Modal, modalApi] = useVbenModal({
closeOnClickModal: false,
closeOnPressEscape: false,
draggable: true,
fullscreenButton: false,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
emits('confirm', toValue(selectedUsers));
},
onOpenChange: async (isOpen: boolean) => {
isOpen && nextTick(onRefresh);
},
title: $t('AbpIdentity.Users'),
});
const gridOptions: VxeGridProps<IdentityUserDto> = {
checkboxConfig: {
highlight: true,
labelField: 'userName',
},
columns: [
{
align: 'left',
field: 'userName',
minWidth: '100px',
title: $t('AbpIdentity.DisplayName:UserName'),
type: 'checkbox',
},
{
align: 'left',
field: 'email',
minWidth: '120px',
title: $t('AbpIdentity.DisplayName:Email'),
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
const state = modalApi.getData<Record<string, any>>();
return await getUnaddedUserListApi({
id: state.id,
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
},
},
autoLoad: false,
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {},
};
const gridEvents: VxeGridListeners<IdentityUserDto> = {
checkboxAll: (e) => {
selectedUsers.value = e.records;
},
checkboxChange: (e) => {
selectedUsers.value = e.records;
},
};
const [Grid, { query }] = useVbenVxeGrid({
formOptions,
gridEvents,
gridOptions,
});
function onRefresh() {
nextTick(query);
}
</script>
<template>
<Modal>
<Grid />
</Modal>
</template>
<style scoped></style>

127
apps/vben5/packages/@abp/identity/src/components/organization-units/SelectRoleModal.vue

@ -0,0 +1,127 @@
<script setup lang="ts">
import type { IdentityRoleDto } from '../../types/roles';
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 { getUnaddedRoleListApi } from '../../api/organization-units';
defineOptions({
name: 'SelectRoleModal',
});
const emits = defineEmits<{
(event: 'confirm', items: IdentityRoleDto[]): void;
}>();
const selectedRoles = ref<IdentityRoleDto[]>([]);
const formOptions: VbenFormProps = {
//
collapsed: false,
resetButtonOptions: {
show: false,
},
schema: [
{
component: 'Input',
componentProps: {
allowClear: true,
autocomplete: 'off',
},
fieldName: 'filter',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpUi.Search'),
},
],
//
showCollapseButton: false,
//
submitOnEnter: true,
};
const [Modal, modalApi] = useVbenModal({
closeOnClickModal: false,
closeOnPressEscape: false,
draggable: true,
fullscreenButton: false,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
emits('confirm', toValue(selectedRoles));
},
onOpenChange: async (isOpen: boolean) => {
isOpen && nextTick(onRefresh);
},
title: $t('AbpIdentity.Roles'),
});
const gridOptions: VxeGridProps<IdentityRoleDto> = {
checkboxConfig: {
highlight: true,
labelField: 'name',
},
columns: [
{
align: 'left',
field: 'name',
title: $t('AbpIdentity.DisplayName:RoleName'),
type: 'checkbox',
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
const state = modalApi.getData<Record<string, any>>();
return await getUnaddedRoleListApi({
id: state.id,
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
},
},
autoLoad: false,
response: {
total: 'totalCount',
list: 'items',
},
},
toolbarConfig: {},
};
const gridEvents: VxeGridListeners<IdentityRoleDto> = {
checkboxAll: (e) => {
selectedRoles.value = e.records;
},
checkboxChange: (e) => {
selectedRoles.value = e.records;
},
};
const [Grid, { query }] = useVbenVxeGrid({
formOptions,
gridEvents,
gridOptions,
});
function onRefresh() {
nextTick(query);
}
</script>
<template>
<Modal>
<Grid />
</Modal>
</template>
<style scoped></style>

4
apps/vben5/packages/@abp/identity/src/components/role/RoleModal.vue → apps/vben5/packages/@abp/identity/src/components/roles/RoleModal.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { FormInstance } from 'ant-design-vue';
import type { IdentityRoleDto } from '../../types/role';
import type { IdentityRoleDto } from '../../types/roles';
import { defineEmits, defineOptions, ref, toValue } from 'vue';
@ -10,7 +10,7 @@ import { $t } from '@vben/locales';
import { Checkbox, Form, Input, message } from 'ant-design-vue';
import { createApi, getApi, updateApi } from '../../api/role';
import { createApi, getApi, updateApi } from '../../api/roles';
defineOptions({
name: 'RoleModal',

7
apps/vben5/packages/@abp/identity/src/components/roles/RoleSelectModal.vue

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

6
apps/vben5/packages/@abp/identity/src/components/role/RoleTable.vue → apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { VbenFormProps, VxeGridListeners, VxeGridProps } from '@abp/ui';
import type { IdentityRoleDto } from '../../types/role';
import type { IdentityRoleDto } from '../../types/roles';
import { defineAsyncComponent, h } from 'vue';
@ -12,10 +12,10 @@ import { useVbenVxeGrid } from '@abp/ui';
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue';
import { Button, Modal, Tag } from 'ant-design-vue';
import { deleteApi, getPagedListApi } from '../../api/role';
import { deleteApi, getPagedListApi } from '../../api/roles';
defineOptions({
name: 'UserTable',
name: 'RoleTable',
});
const RoleModal = defineAsyncComponent(() => import('./RoleModal.vue'));

3
apps/vben5/packages/@abp/identity/src/components/security-logs/SecurityLogTable.vue

@ -15,7 +15,7 @@ import { Button, Modal } from 'ant-design-vue';
import { deleteApi, getPagedListApi } from '../../api/security-logs';
defineOptions({
name: 'UserTable',
name: 'SecurityLogTable',
});
const formOptions: VbenFormProps = {
@ -204,7 +204,6 @@ const handleDelete = (row: SecurityLogDto) => {
</div>
</template>
</Grid>
<EntityEditModal @change="() => query()" />
</template>
<style lang="scss" scoped>

6
apps/vben5/packages/@abp/identity/src/components/user/UserModal.vue → apps/vben5/packages/@abp/identity/src/components/users/UserModal.vue

@ -1,8 +1,8 @@
<script setup lang="ts">
import type { FormInstance } from 'ant-design-vue';
import type { IdentityRoleDto } from '../../types/role';
import type { IdentityUserDto } from '../../types/user';
import type { IdentityRoleDto } from '../../types/roles';
import type { IdentityUserDto } from '../../types/users';
import { defineEmits, defineOptions, ref, toValue } from 'vue';
@ -20,7 +20,7 @@ import {
Transfer,
} from 'ant-design-vue';
import { createApi, getApi, updateApi } from '../../api/user';
import { createApi, getApi, updateApi } from '../../api/users';
defineOptions({
name: 'UserModal',

4
apps/vben5/packages/@abp/identity/src/components/user/UserTable.vue → apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { VbenFormProps, VxeGridListeners, VxeGridProps } from '@abp/ui';
import type { IdentityUserDto } from '../../types/user';
import type { IdentityUserDto } from '../../types/users';
import { defineAsyncComponent, h } from 'vue';
@ -14,7 +14,7 @@ import { useVbenVxeGrid } from '@abp/ui';
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue';
import { Button, Modal } from 'ant-design-vue';
import { deleteApi, getPagedListApi } from '../../api/user';
import { deleteApi, getPagedListApi } from '../../api/users';
defineOptions({
name: 'UserTable',

5
apps/vben5/packages/@abp/identity/src/types/index.ts

@ -1,4 +1,5 @@
export * from './claim-types';
export * from './organization-units';
export * from './roles';
export * from './security-logs';
export * from './user';
export * from './user';
export * from './users';

96
apps/vben5/packages/@abp/identity/src/types/organization-units.ts

@ -0,0 +1,96 @@
import type {
AuditedEntityDto,
PagedAndSortedResultRequestDto,
} from '@abp/core';
import type { GetUserPagedListInput } from './users';
/** 组织机构 */
interface OrganizationUnitDto extends AuditedEntityDto<string> {
/** 编号 */
code: string;
/** 显示名称 */
displayName: string;
/** 父节点标识 */
parentId?: string;
}
/** 组织机构分页查询对象 */
interface GetOrganizationUnitPagedListInput
extends PagedAndSortedResultRequestDto {
/** 过滤字符 */
filter?: string;
}
/** 组织机构创建对象 */
interface OrganizationUnitCreateDto {
/** 显示名称 */
displayName: string;
/** 父节点标识 */
parentId?: string;
}
/** 组织机构变更对象 */
interface OrganizationUnitUpdateDto {
/** 显示名称 */
displayName: string;
}
/** 组织机构增加部门对象 */
interface OrganizationUnitAddRoleInput {
/** 角色标识列表 */
roleIds: string[];
}
/** 组织机构增加用户对象 */
interface OrganizationUnitAddUserInput {
/** 用户标识列表 */
userIds: string[];
}
interface OrganizationUnitGetChildrenDto {
/** 上级组织机构id */
id: string;
/** 递归查询子级 */
recursive?: boolean;
}
interface GetIdentityUsersInput extends PagedAndSortedResultRequestDto {
filter?: string;
}
interface GetIdentityRolesInput extends PagedAndSortedResultRequestDto {
filter?: string;
}
interface GetUnaddedUserListInput extends GetUserPagedListInput {
id: string;
}
interface GetUnaddedRoleListInput extends GetUserPagedListInput {
id: string;
}
interface OrganizationUnitAddUserDto {
userIds: string[];
}
interface OrganizationUnitAddRoleDto {
roleIds: string[];
}
export type {
GetIdentityRolesInput,
GetIdentityUsersInput,
GetOrganizationUnitPagedListInput,
GetUnaddedRoleListInput,
GetUnaddedUserListInput,
OrganizationUnitAddRoleDto,
OrganizationUnitAddRoleInput,
OrganizationUnitAddUserDto,
OrganizationUnitAddUserInput,
OrganizationUnitCreateDto,
OrganizationUnitDto,
OrganizationUnitGetChildrenDto,
OrganizationUnitUpdateDto,
};

0
apps/vben5/packages/@abp/identity/src/types/role.ts → apps/vben5/packages/@abp/identity/src/types/roles.ts

0
apps/vben5/packages/@abp/identity/src/types/user.ts → apps/vben5/packages/@abp/identity/src/types/users.ts

Loading…
Cancel
Save