committed by
GitHub
2464 changed files with 144456 additions and 155369 deletions
@ -0,0 +1,27 @@ |
|||||
|
import { defHttp } from '/@/utils/http/axios'; |
||||
|
import { |
||||
|
IdentitySessionDto, |
||||
|
GetUserSessionsInput |
||||
|
} from './model'; |
||||
|
|
||||
|
/** |
||||
|
* 查询会话列表 |
||||
|
* @param { GetUserSessionsInput } input 查询参数 |
||||
|
* @returns { Promise<PagedResultDto<IdentitySessionDto>> } |
||||
|
*/ |
||||
|
export const getSessions = (input?: GetUserSessionsInput): Promise<PagedResultDto<IdentitySessionDto>> => { |
||||
|
return defHttp.get<PagedResultDto<IdentitySessionDto>>({ |
||||
|
url: '/api/identity/sessions', |
||||
|
params: input, |
||||
|
}); |
||||
|
}; |
||||
|
/** |
||||
|
* 撤销会话 |
||||
|
* @param { string } sessionId 会话id |
||||
|
* @returns { Promise<void> } |
||||
|
*/ |
||||
|
export const revokeSession = (sessionId: string): Promise<void> => { |
||||
|
return defHttp.delete<void>({ |
||||
|
url: `/api/identity/sessions/${sessionId}/revoke`, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
export interface IdentitySessionDto extends EntityDto<string> { |
||||
|
sessionId: string; |
||||
|
device: string; |
||||
|
deviceInfo: string; |
||||
|
userId: string; |
||||
|
clientId?: string; |
||||
|
ipAddresses?: string; |
||||
|
signedIn: Date; |
||||
|
lastAccessed?: Date; |
||||
|
} |
||||
|
|
||||
|
export interface GetUserSessionsInput extends PagedAndSortedResultRequestDto { |
||||
|
userId?: string; |
||||
|
device?: string; |
||||
|
clientId?: string; |
||||
|
} |
||||
@ -0,0 +1,141 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
v-bind="$attrs" |
||||
|
@register="registerModal" |
||||
|
:width="800" |
||||
|
:height="500" |
||||
|
:title="L('IdentitySessions')" |
||||
|
:mask-closable="false" |
||||
|
:can-fullscreen="false" |
||||
|
:show-ok-btn="false" |
||||
|
> |
||||
|
<div :style="getContentStyle" ref="contentWrapRef" class="session"> |
||||
|
<ScrollContainer ref="contentScrollRef"> |
||||
|
<template v-for="identitySession in identitySessions" :key="identitySession.id"> |
||||
|
<Card style="height: 100%"> |
||||
|
<template #title> |
||||
|
<div class="session__tile"> |
||||
|
<span>{{ identitySession.device }}</span> |
||||
|
<div style="padding-left: 5px;"> |
||||
|
<Tag |
||||
|
v-if="identitySession.sessionId === abpStore.getApplication.currentUser.sessionId" |
||||
|
color="#87d068" |
||||
|
>{{ L('CurrentSession') }}</Tag> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<Descriptions bordered size="small" :column="1"> |
||||
|
<DescriptionItem :label="L('DisplayName:SessionId')">{{ identitySession.sessionId }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:Device')">{{ identitySession.device }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:DeviceInfo')">{{ identitySession.deviceInfo }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:ClientId')">{{ identitySession.clientId }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:IpAddresses')">{{ identitySession.ipAddresses }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:SignedIn')">{{ formatToDateTime(identitySession.signedIn) }}</DescriptionItem> |
||||
|
<DescriptionItem v-if="identitySession.lastAccessed" :label="L('DisplayName:LastAccessed')">{{ formatToDateTime(identitySession.lastAccessed) }}</DescriptionItem> |
||||
|
</Descriptions> |
||||
|
<template #extra> |
||||
|
<Button |
||||
|
v-if="identitySession.sessionId !== abpStore.getApplication.currentUser.sessionId" |
||||
|
danger |
||||
|
@click="handleRevokeSession(identitySession)" |
||||
|
>{{ L('RevokeSession') }}</Button> |
||||
|
</template> |
||||
|
</Card> |
||||
|
</template> |
||||
|
</ScrollContainer> |
||||
|
</div> |
||||
|
<template #footer> |
||||
|
<APagination |
||||
|
ref="paginationRef" |
||||
|
:pageSizeOptions="['10', '25', '50', '100']" |
||||
|
:total="identitySessionTotal" |
||||
|
@change="fetchSessions" |
||||
|
@showSizeChange="fetchSessions" |
||||
|
/> |
||||
|
</template> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import type { CSSProperties } from 'vue'; |
||||
|
import { computed, ref } from 'vue'; |
||||
|
import { Button, Card, Descriptions, Pagination, Tag } from 'ant-design-vue'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { ScrollContainer } from '/@/components/Container'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useContentHeight } from '/@/hooks/web/useContentHeight'; |
||||
|
import { useAbpStoreWithOut } from '/@/store/modules/abp'; |
||||
|
import { getSessions, revokeSession } from '/@/api/account/profiles'; |
||||
|
import { IdentitySessionDto } from '/@/api/identity/sessions/model'; |
||||
|
import { formatPagedRequest } from '/@/utils/http/abp/helper'; |
||||
|
import { formatToDateTime } from '/@/utils/dateUtil'; |
||||
|
|
||||
|
const DescriptionItem = Descriptions.Item; |
||||
|
const APagination = Pagination; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
autoContentHeight: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const contentWrapRef = ref<any>(); |
||||
|
const contentScrollRef = ref<any>(); |
||||
|
const paginationRef = ref<any>(); |
||||
|
const getContentHeight = computed(() => props.autoContentHeight); |
||||
|
const { contentHeight } = useContentHeight(getContentHeight, contentWrapRef, [paginationRef], []); |
||||
|
const getContentStyle = computed((): CSSProperties => { |
||||
|
return { |
||||
|
width: '100%', |
||||
|
height: `${contentHeight.value}px`, |
||||
|
}; |
||||
|
}); |
||||
|
const identitySessions = ref<IdentitySessionDto[]>([]); |
||||
|
const identitySessionTotal = ref(0); |
||||
|
const abpStore = useAbpStoreWithOut(); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const { L } = useLocalization(['AbpIdentity', 'AbpUi']); |
||||
|
const [registerModal] = useModalInner(() => { |
||||
|
fetchSessions(); |
||||
|
}); |
||||
|
|
||||
|
function fetchSessions(page: number = 1, pageSize: number = 10) { |
||||
|
const request = { |
||||
|
skipCount: page, |
||||
|
maxResultCount: pageSize, |
||||
|
}; |
||||
|
formatPagedRequest(request); |
||||
|
getSessions({ |
||||
|
skipCount: request.skipCount, |
||||
|
maxResultCount: request.maxResultCount, |
||||
|
}).then((res) => { |
||||
|
identitySessions.value = res.items; |
||||
|
identitySessionTotal.value = res.totalCount; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleRevokeSession(session: IdentitySessionDto) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('SessionWillBeRevokedMessage'), |
||||
|
onOk: async () => { |
||||
|
await revokeSession(session.sessionId); |
||||
|
createMessage.success(L('SuccessfullyRevoked')); |
||||
|
fetchSessions(); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.session { |
||||
|
.session__tile { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
@ -0,0 +1,150 @@ |
|||||
|
<template> |
||||
|
<BasicModal |
||||
|
v-bind="$attrs" |
||||
|
@register="registerModal" |
||||
|
:width="800" |
||||
|
:height="500" |
||||
|
:title="L('IdentitySessions')" |
||||
|
:mask-closable="false" |
||||
|
:can-fullscreen="false" |
||||
|
:show-ok-btn="false" |
||||
|
> |
||||
|
<div v-if="identitySessions.length <= 0"> |
||||
|
<Empty /> |
||||
|
</div> |
||||
|
<div v-else :style="getContentStyle" ref="contentWrapRef" class="session"> |
||||
|
<ScrollContainer ref="contentScrollRef"> |
||||
|
<template v-for="identitySession in identitySessions" :key="identitySession.id"> |
||||
|
<Card style="height: 100%"> |
||||
|
<template #title> |
||||
|
<div class="session__tile"> |
||||
|
<span>{{ identitySession.device }}</span> |
||||
|
<div style="padding-left: 5px;"> |
||||
|
<Tag |
||||
|
v-if="identitySession.sessionId === abpStore.getApplication.currentUser.sessionId" |
||||
|
color="#87d068" |
||||
|
>{{ L('CurrentSession') }}</Tag> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<Descriptions bordered size="small" :column="1"> |
||||
|
<DescriptionItem :label="L('DisplayName:SessionId')">{{ identitySession.sessionId }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:Device')">{{ identitySession.device }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:DeviceInfo')">{{ identitySession.deviceInfo }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:ClientId')">{{ identitySession.clientId }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:IpAddresses')">{{ identitySession.ipAddresses }}</DescriptionItem> |
||||
|
<DescriptionItem :label="L('DisplayName:SignedIn')">{{ formatToDateTime(identitySession.signedIn) }}</DescriptionItem> |
||||
|
<DescriptionItem v-if="identitySession.lastAccessed" :label="L('DisplayName:LastAccessed')">{{ formatToDateTime(identitySession.lastAccessed) }}</DescriptionItem> |
||||
|
</Descriptions> |
||||
|
<template #extra> |
||||
|
<Button |
||||
|
v-auth="['AbpIdentity.IdentitySessions.Revoke']" |
||||
|
v-if="identitySession.sessionId !== abpStore.getApplication.currentUser.sessionId" |
||||
|
danger |
||||
|
@click="handleRevokeSession(identitySession)" |
||||
|
>{{ L('RevokeSession') }}</Button> |
||||
|
</template> |
||||
|
</Card> |
||||
|
</template> |
||||
|
</ScrollContainer> |
||||
|
</div> |
||||
|
<template #footer> |
||||
|
<APagination |
||||
|
v-if="identitySessions.length > 0" |
||||
|
ref="paginationRef" |
||||
|
:pageSizeOptions="['10', '25', '50', '100']" |
||||
|
:total="identitySessionTotal" |
||||
|
@change="fetchSessions" |
||||
|
@showSizeChange="fetchSessions" |
||||
|
/> |
||||
|
</template> |
||||
|
</BasicModal> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import type { CSSProperties } from 'vue'; |
||||
|
import { computed, ref } from 'vue'; |
||||
|
import { Button, Card, Descriptions, Empty, Pagination, Tag } from 'ant-design-vue'; |
||||
|
import { BasicModal, useModalInner } from '/@/components/Modal'; |
||||
|
import { ScrollContainer } from '/@/components/Container'; |
||||
|
import { useMessage } from '/@/hooks/web/useMessage'; |
||||
|
import { useLocalization } from '/@/hooks/abp/useLocalization'; |
||||
|
import { useContentHeight } from '/@/hooks/web/useContentHeight'; |
||||
|
import { useAbpStoreWithOut } from '/@/store/modules/abp'; |
||||
|
import { getSessions, revokeSession } from '/@/api/identity/sessions'; |
||||
|
import { IdentitySessionDto } from '/@/api/identity/sessions/model'; |
||||
|
import { formatPagedRequest } from '/@/utils/http/abp/helper'; |
||||
|
import { formatToDateTime } from '/@/utils/dateUtil'; |
||||
|
|
||||
|
const DescriptionItem = Descriptions.Item; |
||||
|
const APagination = Pagination; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
autoContentHeight: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const userId = ref(''); |
||||
|
const contentWrapRef = ref<any>(); |
||||
|
const contentScrollRef = ref<any>(); |
||||
|
const paginationRef = ref<any>(); |
||||
|
const getContentHeight = computed(() => props.autoContentHeight); |
||||
|
const { contentHeight } = useContentHeight(getContentHeight, contentWrapRef, [paginationRef], []); |
||||
|
const getContentStyle = computed((): CSSProperties => { |
||||
|
return { |
||||
|
width: '100%', |
||||
|
height: `${contentHeight.value}px`, |
||||
|
}; |
||||
|
}); |
||||
|
const identitySessions = ref<IdentitySessionDto[]>([]); |
||||
|
const identitySessionTotal = ref(0); |
||||
|
const abpStore = useAbpStoreWithOut(); |
||||
|
const { createConfirm, createMessage } = useMessage(); |
||||
|
const { L } = useLocalization(['AbpIdentity', 'AbpUi']); |
||||
|
const [registerModal] = useModalInner((data: { userId: string }) => { |
||||
|
userId.value = data.userId; |
||||
|
identitySessions.value = []; |
||||
|
fetchSessions(); |
||||
|
}); |
||||
|
|
||||
|
function fetchSessions(page: number = 1, pageSize: number = 10) { |
||||
|
const request = { |
||||
|
skipCount: page, |
||||
|
maxResultCount: pageSize, |
||||
|
}; |
||||
|
formatPagedRequest(request); |
||||
|
getSessions({ |
||||
|
userId: userId.value, |
||||
|
skipCount: request.skipCount, |
||||
|
maxResultCount: request.maxResultCount, |
||||
|
}).then((res) => { |
||||
|
identitySessions.value = res.items; |
||||
|
identitySessionTotal.value = res.totalCount; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function handleRevokeSession(session: IdentitySessionDto) { |
||||
|
createConfirm({ |
||||
|
iconType: 'warning', |
||||
|
title: L('AreYouSure'), |
||||
|
content: L('SessionWillBeRevokedMessage'), |
||||
|
onOk: async () => { |
||||
|
await revokeSession(session.sessionId); |
||||
|
createMessage.success(L('SuccessfullyRevoked')); |
||||
|
fetchSessions(); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.session { |
||||
|
.session__tile { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue