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 eafe759ce..70fbb9d38 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 @@ -59,7 +59,8 @@ "bindSettings": "Bind Settings", "noticeSettings": "Notice Settings", "authenticatorSettings": "Authenticator Settings", - "changeAvatar": "Change Avatar" + "changeAvatar": "Change Avatar", + "sessionSettings": "Session Settings" }, "profile": "My Profile" } 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 b05654d95..483ad8cde 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 @@ -59,7 +59,8 @@ "bindSettings": "账号绑定", "noticeSettings": "新消息通知", "authenticatorSettings": "身份验证程序", - "changeAvatar": "更改头像" + "changeAvatar": "更改头像", + "sessionSettings": "会话管理" }, "profile": "个人中心" } diff --git a/apps/vben5/packages/@abp/account/package.json b/apps/vben5/packages/@abp/account/package.json index a663164aa..d677830fb 100644 --- a/apps/vben5/packages/@abp/account/package.json +++ b/apps/vben5/packages/@abp/account/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@abp/core": "workspace:*", + "@abp/identity": "workspace:*", "@abp/request": "workspace:*", "@abp/ui": "workspace:*", "@ant-design/icons-vue": "catalog:", diff --git a/apps/vben5/packages/@abp/account/src/api/index.ts b/apps/vben5/packages/@abp/account/src/api/index.ts index 90f884fc6..2aa1d0ab1 100644 --- a/apps/vben5/packages/@abp/account/src/api/index.ts +++ b/apps/vben5/packages/@abp/account/src/api/index.ts @@ -1,4 +1,5 @@ export { useAccountApi } from './useAccountApi'; +export { useMySessionApi } from './useMySessionApi'; export { useProfileApi } from './useProfileApi'; export { useTokenApi } from './useTokenApi'; export { useUserInfoApi } from './useUserInfoApi'; diff --git a/apps/vben5/packages/@abp/account/src/api/useMySessionApi.ts b/apps/vben5/packages/@abp/account/src/api/useMySessionApi.ts new file mode 100644 index 000000000..8a752a12b --- /dev/null +++ b/apps/vben5/packages/@abp/account/src/api/useMySessionApi.ts @@ -0,0 +1,41 @@ +import type { PagedResultDto } from '@abp/core'; +import type { GetMySessionsInput, IdentitySessionDto } from '@abp/identity'; + +import { useRequest } from '@abp/request'; + +export function useMySessionApi() { + const { cancel, request } = useRequest(); + + /** + * 查询会话列表 + * @param { GetMySessionsInput } input 查询参数 + * @returns { Promise> } 用户会话列表 + */ + function getSessionsApi( + input?: GetMySessionsInput, + ): Promise> { + return request>( + '/api/account/my-profile/sessions', + { + method: 'GET', + params: input, + }, + ); + } + /** + * 撤销会话 + * @param { string } sessionId 会话id + * @returns { Promise } + */ + function revokeSessionApi(sessionId: string): Promise { + return request(`/api/account/my-profile/sessions/${sessionId}/revoke`, { + method: 'DELETE', + }); + } + + return { + cancel, + getSessionsApi, + revokeSessionApi, + }; +} diff --git a/apps/vben5/packages/@abp/account/src/components/MySetting.vue b/apps/vben5/packages/@abp/account/src/components/MySetting.vue index eda031603..f8fb3cd0d 100644 --- a/apps/vben5/packages/@abp/account/src/components/MySetting.vue +++ b/apps/vben5/packages/@abp/account/src/components/MySetting.vue @@ -12,12 +12,25 @@ import { useUserStore } from '@vben/stores'; import { Card, Menu, message, Modal } from 'ant-design-vue'; import { useProfileApi } from '../api/useProfileApi'; -import AuthenticatorSettings from './components/AuthenticatorSettings.vue'; -import BasicSettings from './components/BasicSettings.vue'; -import BindSettings from './components/BindSettings.vue'; -import NoticeSettings from './components/NoticeSettings.vue'; -import SecuritySettings from './components/SecuritySettings.vue'; +const AuthenticatorSettings = defineAsyncComponent( + () => import('./components/AuthenticatorSettings.vue'), +); +const BasicSettings = defineAsyncComponent( + () => import('./components/BasicSettings.vue'), +); +const BindSettings = defineAsyncComponent( + () => import('./components/BindSettings.vue'), +); +const NoticeSettings = defineAsyncComponent( + () => import('./components/NoticeSettings.vue'), +); +const SecuritySettings = defineAsyncComponent( + () => import('./components/SecuritySettings.vue'), +); +const SessionSettings = defineAsyncComponent( + () => import('./components/SessionSettings.vue'), +); const { getApi, updateApi } = useProfileApi(); const userStore = useUserStore(); const { query } = useRoute(); @@ -37,6 +50,10 @@ const menuItems = reactive([ key: 'bind', label: $t('abp.account.settings.bindSettings'), }, + { + key: 'session', + label: $t('abp.account.settings.sessionSettings'), + }, { key: 'notice', label: $t('abp.account.settings.noticeSettings'), @@ -137,6 +154,7 @@ onMounted(async () => { + diff --git a/apps/vben5/packages/@abp/account/src/components/components/SessionSettings.vue b/apps/vben5/packages/@abp/account/src/components/components/SessionSettings.vue new file mode 100644 index 000000000..ce036c58e --- /dev/null +++ b/apps/vben5/packages/@abp/account/src/components/components/SessionSettings.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/apps/vben5/packages/@abp/identity/src/api/index.ts b/apps/vben5/packages/@abp/identity/src/api/index.ts index 78cb73ef3..bb910e41d 100644 --- a/apps/vben5/packages/@abp/identity/src/api/index.ts +++ b/apps/vben5/packages/@abp/identity/src/api/index.ts @@ -4,3 +4,4 @@ export { useRolesApi } from './useRolesApi'; export { useSecurityLogsApi } from './useSecurityLogsApi'; export { useUserLookupApi } from './useUserLookupApi'; export { useUsersApi } from './useUsersApi'; +export { useUserSessionsApi } from './useUserSessionsApi'; diff --git a/apps/vben5/packages/@abp/identity/src/api/useUserSessionsApi.ts b/apps/vben5/packages/@abp/identity/src/api/useUserSessionsApi.ts new file mode 100644 index 000000000..2125f70bd --- /dev/null +++ b/apps/vben5/packages/@abp/identity/src/api/useUserSessionsApi.ts @@ -0,0 +1,42 @@ +import type { PagedResultDto } from '@abp/core'; + +import type { GetUserSessionsInput, IdentitySessionDto } from '../types'; + +import { useRequest } from '@abp/request'; + +export function useUserSessionsApi() { + const { cancel, request } = useRequest(); + + /** + * 查询会话列表 + * @param { GetUserSessionsInput } input 查询参数 + * @returns { Promise> } 用户会话列表 + */ + function getSessionsApi( + input?: GetUserSessionsInput, + ): Promise> { + return request>( + '/api/identity/sessions', + { + method: 'GET', + params: input, + }, + ); + } + /** + * 撤销会话 + * @param { string } sessionId 会话id + * @returns { Promise } + */ + function revokeSessionApi(sessionId: string): Promise { + return request(`/api/identity/sessions/${sessionId}/revoke`, { + method: 'DELETE', + }); + } + + return { + cancel, + getSessionsApi, + revokeSessionApi, + }; +} diff --git a/apps/vben5/packages/@abp/identity/src/components/index.ts b/apps/vben5/packages/@abp/identity/src/components/index.ts index 35188be4d..bf3947b28 100644 --- a/apps/vben5/packages/@abp/identity/src/components/index.ts +++ b/apps/vben5/packages/@abp/identity/src/components/index.ts @@ -2,4 +2,5 @@ export { default as ClaimTypeTable } from './claim-types/ClaimTypeTable.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 SessionTable } from './sessions/SessionTable.vue'; export { default as UserTable } from './users/UserTable.vue'; diff --git a/apps/vben5/packages/@abp/identity/src/components/sessions/SessionTable.vue b/apps/vben5/packages/@abp/identity/src/components/sessions/SessionTable.vue new file mode 100644 index 000000000..dd131764f --- /dev/null +++ b/apps/vben5/packages/@abp/identity/src/components/sessions/SessionTable.vue @@ -0,0 +1,158 @@ + + + + + diff --git a/apps/vben5/packages/@abp/identity/src/components/users/UserSessionDrawer.vue b/apps/vben5/packages/@abp/identity/src/components/users/UserSessionDrawer.vue new file mode 100644 index 000000000..1e196c616 --- /dev/null +++ b/apps/vben5/packages/@abp/identity/src/components/users/UserSessionDrawer.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue b/apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue index a72b74654..f786ee42f 100644 --- a/apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue +++ b/apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue @@ -26,7 +26,10 @@ import { import { Button, Dropdown, Menu, message, Modal } from 'ant-design-vue'; import { useUsersApi } from '../../api/useUsersApi'; -import { IdentityUserPermissions } from '../../constants/permissions'; +import { + IdentitySessionPermissions, + IdentityUserPermissions, +} from '../../constants/permissions'; defineOptions({ name: 'UserTable', @@ -46,6 +49,7 @@ const PasswordIcon = createIconifyIcon('carbon:password'); const MenuOutlined = createIconifyIcon('heroicons-outline:menu-alt-3'); const ClaimOutlined = createIconifyIcon('la:id-card-solid'); const PermissionsOutlined = createIconifyIcon('icon-park-outline:permissions'); +const SessionIcon = createIconifyIcon('carbon:prompt-session'); const AuditLogIcon = createIconifyIcon('fluent-mdl2:compliance-audit'); const getLockEnd = computed(() => { @@ -166,6 +170,11 @@ const [UserPermissionModal, permissionModalApi] = useVbenModal({ const [UserChangeDrawer, userChangeDrawerApi] = useVbenDrawer({ connectedComponent: EntityChangeDrawer, }); +const [UserSessionDrawer, userSessionDrawerApi] = useVbenDrawer({ + connectedComponent: defineAsyncComponent( + () => import('./UserSessionDrawer.vue'), + ), +}); const [Grid, { query }] = useVbenVxeGrid({ formOptions, gridEvents, @@ -240,6 +249,11 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => { permissionModalApi.open(); break; } + case 'session': { + userSessionDrawerApi.setData(row); + userSessionDrawerApi.open(); + break; + } case 'unlock': { handleUnlock(row); break; @@ -330,6 +344,13 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => { > {{ $t('AbpPermissionManagement.Permissions') }} + + {{ $t('AbpIdentity.IdentitySessions') }} + query()" /> + diff --git a/apps/vben5/packages/@abp/identity/src/constants/permissions.ts b/apps/vben5/packages/@abp/identity/src/constants/permissions.ts index 36a0b622a..54cc5565a 100644 --- a/apps/vben5/packages/@abp/identity/src/constants/permissions.ts +++ b/apps/vben5/packages/@abp/identity/src/constants/permissions.ts @@ -46,6 +46,12 @@ export const SecurityLogPermissions = { /** 删除 */ Delete: 'AbpAuditing.SecurityLog.Delete', }; +/** 用户会话权限 */ +export const IdentitySessionPermissions = { + Default: 'AbpIdentity.IdentitySessions', + /** 移除 */ + Revoke: 'AbpIdentity.IdentitySessions.Revoke', +}; /** * 搜索用户权限 * @deprecated 后台服务删除权限后将无法使用. diff --git a/apps/vben5/packages/@abp/identity/src/types/index.ts b/apps/vben5/packages/@abp/identity/src/types/index.ts index 6fbf916c4..86f1233c6 100644 --- a/apps/vben5/packages/@abp/identity/src/types/index.ts +++ b/apps/vben5/packages/@abp/identity/src/types/index.ts @@ -2,4 +2,5 @@ export * from './claim-types'; export * from './organization-units'; export * from './roles'; export * from './security-logs'; +export * from './sessions'; export * from './users'; diff --git a/apps/vben5/packages/@abp/identity/src/types/sessions.ts b/apps/vben5/packages/@abp/identity/src/types/sessions.ts new file mode 100644 index 000000000..64f9f07fe --- /dev/null +++ b/apps/vben5/packages/@abp/identity/src/types/sessions.ts @@ -0,0 +1,25 @@ +import type { EntityDto, PagedAndSortedResultRequestDto } from '@abp/core'; + +interface IdentitySessionDto extends EntityDto { + clientId?: string; + device: string; + deviceInfo: string; + ipAddresses?: string; + lastAccessed?: Date; + sessionId: string; + signedIn: Date; + userId: string; +} + +interface GetUserSessionsInput extends PagedAndSortedResultRequestDto { + clientId?: string; + device?: string; + userId?: string; +} + +interface GetMySessionsInput extends PagedAndSortedResultRequestDto { + clientId?: string; + device?: string; +} + +export type { GetMySessionsInput, GetUserSessionsInput, IdentitySessionDto };