diff --git a/apps/vben5/apps/app-antd/package.json b/apps/vben5/apps/app-antd/package.json index f12c4b965..2a5979c5f 100644 --- a/apps/vben5/apps/app-antd/package.json +++ b/apps/vben5/apps/app-antd/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "@abp/account": "workspace:*", + "@abp/auditing": "workspace:*", "@abp/core": "workspace:*", "@abp/identity": "workspace:*", "@abp/request": "workspace:*", diff --git a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json index 199e139ab..f995ca518 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json @@ -8,7 +8,8 @@ "role": "Role", "claimTypes": "Claim Types", "securityLogs": "Security Logs", - "organizationUnits": "Organization Units" + "organizationUnits": "Organization Units", + "auditLogs": "Audit Logs" } } } diff --git a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json index fd9d57776..3f6b04aa2 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json @@ -8,7 +8,8 @@ "role": "角色", "claimTypes": "身份标识", "securityLogs": "安全日志", - "organizationUnits": "组织机构" + "organizationUnits": "组织机构", + "auditLogs": "审计日志" } } } diff --git a/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts b/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts index 6fd11549e..457a439c5 100644 --- a/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts +++ b/apps/vben5/apps/app-antd/src/router/routes/modules/abp.ts @@ -81,6 +81,15 @@ const routes: RouteRecordRaw[] = [ }, ], }, + { + meta: { + title: $t('abp.manage.identity.auditLogs'), + icon: 'fluent-mdl2:compliance-audit', + }, + name: 'AuditingAuditLogs', + path: '/manage/audit-logs', + component: () => import('#/views/auditing/audit-logs/index.vue'), + }, ], }, ], diff --git a/apps/vben5/apps/app-antd/src/views/auditing/audit-logs/index.vue b/apps/vben5/apps/app-antd/src/views/auditing/audit-logs/index.vue new file mode 100644 index 000000000..2ee3b0880 --- /dev/null +++ b/apps/vben5/apps/app-antd/src/views/auditing/audit-logs/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/apps/vben5/packages/@abp/auditing/package.json b/apps/vben5/packages/@abp/auditing/package.json new file mode 100644 index 000000000..79f93552d --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/package.json @@ -0,0 +1,37 @@ +{ + "name": "@abp/auditing", + "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/auditing" + }, + "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:*", + "vxe-table": "catalog:" + } +} diff --git a/apps/vben5/packages/@abp/auditing/src/api/audit-logs.ts b/apps/vben5/packages/@abp/auditing/src/api/audit-logs.ts new file mode 100644 index 000000000..d400b91a1 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/api/audit-logs.ts @@ -0,0 +1,36 @@ +import type { PagedResultDto } from '@abp/core'; + +import type { AuditLogDto, AuditLogGetListInput } from '../types/audit-logs'; + +import { requestClient } from '@abp/request'; + +/** + * 获取审计日志 + * @param id 日志id + */ +export function getApi(id: string): Promise { + return requestClient.get(`/api/auditing/audit-log/${id}`); +} + +/** + * 获取审计日志分页列表 + * @param input 参数 + */ +export function getPagedListApi( + input: AuditLogGetListInput, +): Promise> { + return requestClient.get>( + '/api/auditing/audit-log', + { + params: input, + }, + ); +} + +/** + * 删除审计日志 + * @param id 日志id + */ +export function deleteApi(id: string): Promise { + return requestClient.delete(`/api/auditing/audit-log/${id}`); +} diff --git a/apps/vben5/packages/@abp/auditing/src/api/entity-changes.ts b/apps/vben5/packages/@abp/auditing/src/api/entity-changes.ts new file mode 100644 index 000000000..b80499d61 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/api/entity-changes.ts @@ -0,0 +1,23 @@ +import type { ListResultDto } from '@abp/core'; + +import type { + EntityChangeGetWithUsernameInput, + EntityChangeWithUsernameDto, +} from '../types/entity-changes'; + +import { requestClient } from '@abp/request'; + +/** + * 获取包含用户名称的实体变更列表 + * @param input 参数 + */ +export function getListWithUsernameApi( + input: EntityChangeGetWithUsernameInput, +): Promise> { + return requestClient.get>( + '/api/auditing/entity-changes/with-username', + { + params: input, + }, + ); +} diff --git a/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogDrawer.vue b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogDrawer.vue new file mode 100644 index 000000000..80f441f92 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogDrawer.vue @@ -0,0 +1,240 @@ + + + + + diff --git a/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue new file mode 100644 index 000000000..90a2848de --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue @@ -0,0 +1,326 @@ + + + + + diff --git a/apps/vben5/packages/@abp/auditing/src/components/audit-logs/mapping.ts b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/mapping.ts new file mode 100644 index 000000000..01f6c8da0 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/components/audit-logs/mapping.ts @@ -0,0 +1,57 @@ +const httpMethodOptions = [ + { label: 'GET', value: 'GET' }, + { label: 'PUT', value: 'PUT' }, + { label: 'POST', value: 'POST' }, + { label: 'PATCH', value: 'PATCH' }, + { label: 'DELETE', value: 'DELETE' }, + { label: 'OPTIONS', value: 'OPTIONS' }, + { label: 'HEAD', value: 'HEAD' }, +]; + +const httpStatusCodeOptions = [ + { label: '100 - Continue', value: 100 }, + { label: '101 - Switching Protocols', value: 101 }, + { label: '200 - OK', value: 200 }, + { label: '201 - Created', value: 201 }, + { label: '202 - Accepted', value: 202 }, + { label: '203 - Non Authoritative Information', value: 203 }, + { label: '204 - No Content', value: 204 }, + { label: '205 - Reset Content', value: 205 }, + { label: '206 - Partial Content', value: 206 }, + { label: '300 - Multiple Choices', value: 300 }, + { label: '301 - Moved Permanently', value: 301 }, + { label: '302 - Found & Redirect', value: 302 }, + { label: '303 - See Other', value: 303 }, + { label: '304 - Not Modified', value: 304 }, + { label: '305 - Use Proxy', value: 305 }, + { label: '306 - Switch Proxy', value: 306 }, + { label: '307 - Temporary Redirect', value: 307 }, + { label: '308 - Permanent Redirect', value: 308 }, + { label: '400 - Bad Request', value: 400 }, + { label: '401 - Unauthorized', value: 401 }, + { label: '402 - Payment Required', value: 402 }, + { label: '403 - Forbidden', value: 403 }, + { label: '404 - Not Found', value: 404 }, + { label: '405 - Method Not Allowed', value: 405 }, + { label: '406 - Not Acceptable', value: 406 }, + { label: '407 - Proxy Authentication Required', value: 407 }, + { label: '408 - Request Timeout', value: 408 }, + { label: '409 - Conflict', value: 409 }, + { label: '410 - Gone', value: 410 }, + { label: '411 - Length Required', value: 411 }, + { label: '412 - Precondition Failed', value: 412 }, + { label: '413 - Request Entity Too Large', value: 413 }, + { label: '414 - Request Uri Too Long', value: 414 }, + { label: '415 - Unsupported Media Type', value: 415 }, + { label: '416 - Requested Range Not Satisfiable', value: 416 }, + { label: '417 - Expectation Failed', value: 417 }, + { label: '426 - Upgrade Required', value: 426 }, + { label: '500 - Internal Server Error', value: 500 }, + { label: '501 - Not mplemented', value: 501 }, + { label: '502 - Bad Gateway', value: 502 }, + { label: '503 - Service Unavailable', value: 503 }, + { label: '504 - Gateway Timeout', value: 504 }, + { label: '505 - Http Version Not Supported', value: 505 }, +]; + +export { httpMethodOptions, httpStatusCodeOptions }; diff --git a/apps/vben5/packages/@abp/auditing/src/components/entity-changes/EntityChangeDrawer.vue b/apps/vben5/packages/@abp/auditing/src/components/entity-changes/EntityChangeDrawer.vue new file mode 100644 index 000000000..a01b23883 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/components/entity-changes/EntityChangeDrawer.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/apps/vben5/packages/@abp/auditing/src/components/entity-changes/EntityChangeTable.vue b/apps/vben5/packages/@abp/auditing/src/components/entity-changes/EntityChangeTable.vue new file mode 100644 index 000000000..f1c341341 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/components/entity-changes/EntityChangeTable.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/apps/vben5/packages/@abp/auditing/src/components/index.ts b/apps/vben5/packages/@abp/auditing/src/components/index.ts new file mode 100644 index 000000000..1d99b4a46 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/components/index.ts @@ -0,0 +1,2 @@ +export { default as AuditLogTable } from './audit-logs/AuditLogTable.vue'; +export { default as EntityChangeDrawer } from './entity-changes/EntityChangeDrawer.vue'; diff --git a/apps/vben5/packages/@abp/auditing/src/constants/index.ts b/apps/vben5/packages/@abp/auditing/src/constants/index.ts new file mode 100644 index 000000000..c85954d3e --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/constants/index.ts @@ -0,0 +1 @@ +export * from './permissions'; diff --git a/apps/vben5/packages/@abp/auditing/src/constants/permissions.ts b/apps/vben5/packages/@abp/auditing/src/constants/permissions.ts new file mode 100644 index 000000000..00e8dc003 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/constants/permissions.ts @@ -0,0 +1,6 @@ +/** 审计日志权限 */ +export const AuditLogPermissions = { + Default: 'AbpAuditing.AuditLog', + /** 删除 */ + Delete: 'AbpAuditing.AuditLog.Delete', +}; diff --git a/apps/vben5/packages/@abp/auditing/src/hooks/useAuditlogs.ts b/apps/vben5/packages/@abp/auditing/src/hooks/useAuditlogs.ts new file mode 100644 index 000000000..97dc83cdc --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/hooks/useAuditlogs.ts @@ -0,0 +1,62 @@ +import { computed } from 'vue'; + +import { useLocalization } from '@abp/core'; + +import { ChangeType } from '../types/entity-changes'; + +export function useAuditlogs() { + const { L } = useLocalization(['AbpAuditLogging', 'AbpUi']); + + const changeTypeColorMap = { + [ChangeType.Created]: { color: '#87d068', value: L('Created') }, + [ChangeType.Deleted]: { color: 'red', value: L('Deleted') }, + [ChangeType.Updated]: { color: '#108ee9', value: L('Updated') }, + }; + + const methodColorMap: { [key: string]: string } = { + DELETE: 'red', + GET: 'blue', + OPTIONS: 'cyan', + PATCH: 'pink', + POST: 'green', + PUT: 'orange', + }; + const getChangeTypeColor = computed(() => { + return (changeType: ChangeType) => changeTypeColorMap[changeType].color; + }); + const getChangeTypeValue = computed(() => { + return (changeType: ChangeType) => changeTypeColorMap[changeType].value; + }); + const getHttpMethodColor = computed(() => { + return (method?: string) => { + return method ? methodColorMap[method] : ''; + }; + }); + const getHttpStatusCodeColor = computed(() => { + return (statusCode?: number) => { + if (!statusCode) { + return ''; + } + if (statusCode >= 200 && statusCode < 300) { + return '#87d068'; + } + if (statusCode >= 300 && statusCode < 400) { + return '#108ee9'; + } + if (statusCode >= 400 && statusCode < 500) { + return 'orange'; + } + if (statusCode >= 500) { + return 'red'; + } + return 'cyan'; + }; + }); + + return { + getChangeTypeColor, + getChangeTypeValue, + getHttpMethodColor, + getHttpStatusCodeColor, + }; +} diff --git a/apps/vben5/packages/@abp/auditing/src/index.ts b/apps/vben5/packages/@abp/auditing/src/index.ts new file mode 100644 index 000000000..c688f3a89 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/index.ts @@ -0,0 +1,3 @@ +export * from './components'; +export * from './constants'; +export * from './types'; diff --git a/apps/vben5/packages/@abp/auditing/src/types/audit-logs.ts b/apps/vben5/packages/@abp/auditing/src/types/audit-logs.ts new file mode 100644 index 000000000..58c1ab751 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/types/audit-logs.ts @@ -0,0 +1,62 @@ +import type { + ExtraPropertyDictionary, + PagedAndSortedResultRequestDto, +} from '@abp/core'; + +import type { EntityChangeDto } from './entity-changes'; + +interface Action { + [key: string]: any; + executionDuration?: number; + executionTime: Date; + extraProperties?: ExtraPropertyDictionary; + id: string; + methodName?: string; + parameters?: string; + serviceName?: string; +} + +interface AuditLogDto { + [key: string]: any; + actions?: Action[]; + applicationName?: string; + browserInfo?: string; + clientId?: string; + clientIpAddress?: string; + clientName?: string; + comments?: string; + correlationId?: string; + entityChanges?: EntityChangeDto[]; + exceptions?: string; + executionDuration?: number; + executionTime?: Date; + extraProperties?: ExtraPropertyDictionary; + httpMethod?: string; + httpStatusCode?: number; + id: string; + impersonatorTenantId?: string; + impersonatorUserId?: string; + tenantId?: string; + tenantName?: string; + url?: string; + userId?: string; + userName?: string; +} +interface AuditLogGetListInput extends PagedAndSortedResultRequestDto { + applicationName?: string; + clientId?: string; + clientIpAddress?: string; + correlationId?: string; + endTime?: Date; + hasException?: boolean; + httpMethod?: string; + httpStatusCode?: number; + maxExecutionDuration?: number; + minExecutionDuration?: number; + startTime?: Date; + url?: string; + userId?: string; + userName?: string; +} + +export type { Action, AuditLogDto, AuditLogGetListInput }; diff --git a/apps/vben5/packages/@abp/auditing/src/types/entity-changes.ts b/apps/vben5/packages/@abp/auditing/src/types/entity-changes.ts new file mode 100644 index 000000000..0ae5d4680 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/types/entity-changes.ts @@ -0,0 +1,63 @@ +import type { + ExtraPropertyDictionary, + PagedAndSortedResultRequestDto, +} from '@abp/core'; + +export enum ChangeType { + Created = 0, + Deleted = 2, + Updated = 1, +} + +interface PropertyChange { + id: string; + newValue?: string; + originalValue?: string; + propertyName?: string; + propertyTypeFullName?: string; +} + +interface EntityChangeDto { + [key: string]: any; + changeTime?: Date; + changeType: ChangeType; + entityId?: string; + entityTenantId?: string; + entityTypeFullName?: string; + extraProperties?: ExtraPropertyDictionary; + id: string; + propertyChanges?: PropertyChange[]; +} + +interface EntityChangeWithUsernameDto { + entityChange: EntityChangeDto; + userName?: string; +} + +interface EntityChangeGetListInput extends PagedAndSortedResultRequestDto { + auditLogId?: string; + changeType?: ChangeType; + endTime?: Date; + entityId?: string; + entityTypeFullName?: string; + startTime?: Date; +} + +interface EntityChangeGetWithUsernameInput { + entityId?: string; + entityTypeFullName?: string; +} + +interface RestoreEntityInput { + entityChangeId?: string; + entityId: string; +} + +export type { + EntityChangeDto, + EntityChangeGetListInput, + EntityChangeGetWithUsernameInput, + EntityChangeWithUsernameDto, + PropertyChange, + RestoreEntityInput, +}; diff --git a/apps/vben5/packages/@abp/auditing/src/types/index.ts b/apps/vben5/packages/@abp/auditing/src/types/index.ts new file mode 100644 index 000000000..900570716 --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './audit-logs'; +export * from './entity-changes'; diff --git a/apps/vben5/packages/@abp/auditing/tsconfig.json b/apps/vben5/packages/@abp/auditing/tsconfig.json new file mode 100644 index 000000000..ce1a891fb --- /dev/null +++ b/apps/vben5/packages/@abp/auditing/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@vben/tsconfig/web.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/apps/vben5/packages/@abp/core/package.json b/apps/vben5/packages/@abp/core/package.json index 8beec67f0..b46de0539 100644 --- a/apps/vben5/packages/@abp/core/package.json +++ b/apps/vben5/packages/@abp/core/package.json @@ -35,6 +35,7 @@ } }, "dependencies": { + "@vueuse/core": "catalog:", "dayjs": "catalog:", "lodash": "catalog:", "pinia": "catalog:", diff --git a/apps/vben5/packages/@abp/core/src/hooks/index.ts b/apps/vben5/packages/@abp/core/src/hooks/index.ts index 1582c45c5..2d4c33225 100644 --- a/apps/vben5/packages/@abp/core/src/hooks/index.ts +++ b/apps/vben5/packages/@abp/core/src/hooks/index.ts @@ -1,3 +1,4 @@ export * from './useLocalization'; export * from './useSettings'; export * from './useValidation'; +export * from './useWindowSizeFn'; diff --git a/apps/vben5/packages/@abp/core/src/hooks/useWindowSizeFn.ts b/apps/vben5/packages/@abp/core/src/hooks/useWindowSizeFn.ts new file mode 100644 index 000000000..9a617221a --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/hooks/useWindowSizeFn.ts @@ -0,0 +1,45 @@ +import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core'; + +interface WindowSizeOptions { + immediate?: boolean; + listenerOptions?: AddEventListenerOptions | boolean; + once?: boolean; +} + +interface Fn { + (...arg: T[]): R; +} + +export function useWindowSizeFn( + fn: Fn, + wait = 150, + options?: WindowSizeOptions, +) { + let handler = () => { + fn(); + }; + const handleSize = useDebounceFn(handler, wait); + handler = handleSize; + + const start = () => { + if (options && options.immediate) { + handler(); + } + window.addEventListener('resize', handler, { + passive: true, + }); + }; + + const stop = () => { + window.removeEventListener('resize', handler); + }; + + tryOnMounted(() => { + start(); + }); + + tryOnUnmounted(() => { + stop(); + }); + return [start, stop]; +} diff --git a/apps/vben5/packages/@abp/core/src/types/index.ts b/apps/vben5/packages/@abp/core/src/types/index.ts index 97fa56fd8..cde3fc4a5 100644 --- a/apps/vben5/packages/@abp/core/src/types/index.ts +++ b/apps/vben5/packages/@abp/core/src/types/index.ts @@ -3,4 +3,5 @@ export * from './global'; export * from './localization'; export * from './rules'; export * from './settings'; +export * from './table'; export * from './validations'; diff --git a/apps/vben5/packages/@abp/core/src/types/table.ts b/apps/vben5/packages/@abp/core/src/types/table.ts new file mode 100644 index 000000000..9d76b4da6 --- /dev/null +++ b/apps/vben5/packages/@abp/core/src/types/table.ts @@ -0,0 +1,3 @@ +type SortOrder = '' | 'asc' | 'desc' | null; + +export type { SortOrder }; diff --git a/apps/vben5/packages/@abp/identity/package.json b/apps/vben5/packages/@abp/identity/package.json index 0a1fad4a5..b19e896c6 100644 --- a/apps/vben5/packages/@abp/identity/package.json +++ b/apps/vben5/packages/@abp/identity/package.json @@ -20,6 +20,7 @@ } }, "dependencies": { + "@abp/auditing": "workspace:*", "@abp/core": "workspace:*", "@abp/permission": "workspace:*", "@abp/request": "workspace:*", diff --git a/apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue b/apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue index 5e8a8d044..434b6f481 100644 --- a/apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue +++ b/apps/vben5/packages/@abp/identity/src/components/roles/RoleTable.vue @@ -7,10 +7,11 @@ import type { IdentityRoleDto } from '../../types/roles'; import { defineAsyncComponent, h } from 'vue'; import { useAccess } from '@vben/access'; -import { useVbenModal } from '@vben/common-ui'; +import { useVbenDrawer, useVbenModal } from '@vben/common-ui'; import { createIconifyIcon } from '@vben/icons'; import { $t } from '@vben/locales'; +import { AuditLogPermissions, EntityChangeDrawer } from '@abp/auditing'; import { useAbpStore } from '@abp/core'; import { PermissionModal } from '@abp/permission'; import { useVbenVxeGrid } from '@abp/ui'; @@ -32,9 +33,10 @@ const MenuItem = Menu.Item; const MenuOutlined = createIconifyIcon('heroicons-outline:menu-alt-3'); const ClaimOutlined = createIconifyIcon('la:id-card-solid'); const PermissionsOutlined = createIconifyIcon('icon-park-outline:permissions'); +const AuditLogIcon = createIconifyIcon('fluent-mdl2:compliance-audit'); + const RoleModal = defineAsyncComponent(() => import('./RoleModal.vue')); const ClaimModal = defineAsyncComponent(() => import('./RoleClaimModal.vue')); - const abpStore = useAbpStore(); const { hasAccessByCodes } = useAccess(); const [RolePermissionModal, permissionModalApi] = useVbenModal({ @@ -43,6 +45,9 @@ const [RolePermissionModal, permissionModalApi] = useVbenModal({ const [RoleClaimModal, claimModalApi] = useVbenModal({ connectedComponent: ClaimModal, }); +const [RoleChangeDrawer, roleChangeDrawerApi] = useVbenDrawer({ + connectedComponent: EntityChangeDrawer, +}); const formOptions: VbenFormProps = { // 默认展开 @@ -143,6 +148,14 @@ const handleMenuClick = async (row: IdentityRoleDto, info: MenuInfo) => { claimModalApi.open(); break; } + case 'entity-changes': { + roleChangeDrawerApi.setData({ + entityId: row.id, + entityTypeFullName: 'Volo.Abp.Identity.IdentityRole', + }); + roleChangeDrawerApi.open(); + break; + } case 'permissions': { const roles = abpStore.application?.currentUser.roles ?? []; permissionModalApi.setData({ @@ -237,6 +250,13 @@ const handleMenuClick = async (row: IdentityRoleDto, info: MenuInfo) => { > {{ $t('AppPlatform.Menu:Manage') }} + + {{ $t('AbpAuditLogging.EntitiesChanged') }} +