feat: add support for one-click revocation of login status of other devices (#7268)

#### What type of PR is this?

/kind feature
/milestone 2.20.x
/area ui

#### What this PR does / why we need it:

<img width="586" alt="image" src="https://github.com/user-attachments/assets/48d07035-7cb0-4e08-a7c1-4a5d91069d41" />

#### Which issue(s) this PR fixes:

Fixes #7267 

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?

```release-note
支持批量撤销其他设备的登录状态
```
pull/7276/head
Ryan Wang 2025-03-07 14:44:59 +08:00 committed by GitHub
parent 62f479253e
commit 8b4141078e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 61 additions and 4 deletions

View File

@ -1371,6 +1371,12 @@ core:
description: >- description: >-
Are you sure you want to revoke this device? After revoking, this Are you sure you want to revoke this device? After revoking, this
device will be logged out device will be logged out
revoke_others:
title: Revoke all other devices
description: >-
Are you sure you want to revoke all other devices? After you revoke,
other devices will be logged out
toast_success: Login status of other devices has been revoked
uc_notification: uc_notification:
title: Notifications title: Notifications
tabs: tabs:

View File

@ -1282,6 +1282,10 @@ core:
revoke: revoke:
title: 撤销设备 title: 撤销设备
description: 确定要撤销该设备吗?撤销之后,此设备会退出登录 description: 确定要撤销该设备吗?撤销之后,此设备会退出登录
revoke_others:
title: 撤销其他所有设备
description: 确定要撤销其他所有设备吗?撤销之后,其他设备会退出登录
toast_success: 已撤销其他设备的登录状态
uc_notification: uc_notification:
title: 消息 title: 消息
tabs: tabs:

View File

@ -1267,6 +1267,10 @@ core:
revoke: revoke:
title: 撤銷設備 title: 撤銷設備
description: 確定要撤銷該設備嗎?撤銷之後,此裝置會退出登錄 description: 確定要撤銷該設備嗎?撤銷之後,此裝置會退出登錄
revoke_others:
title: 撤銷其他所有設備
toast_success: 已撤銷其他設備的登入狀態
description: 確定要撤銷其他所有設備嗎?撤銷之後,其他設備會登出登入
uc_notification: uc_notification:
title: 訊息 title: 訊息
tabs: tabs:

View File

@ -1,9 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ucApiClient } from "@halo-dev/api-client"; import { ucApiClient } from "@halo-dev/api-client";
import { VLoading } from "@halo-dev/components"; import { Dialog, Toast, VButton, VLoading } from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query"; import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import DeviceListItem from "./components/DeviceListItem.vue"; import DeviceListItem from "./components/DeviceListItem.vue";
const { t } = useI18n();
const queryClient = useQueryClient();
const { data, isLoading } = useQuery({ const { data, isLoading } = useQuery({
queryKey: ["uc:devices"], queryKey: ["uc:devices"],
queryFn: async () => { queryFn: async () => {
@ -18,12 +23,45 @@ const { data, isLoading } = useQuery({
return hasDeletingData ? 1000 : false; return hasDeletingData ? 1000 : false;
}, },
}); });
const otherDevices = computed(() => {
if (!data.value) {
return [];
}
return data.value.filter((device) => !device.currentDevice);
});
function handleRevokeOtherDevices() {
Dialog.warning({
title: t("core.uc_profile.device.operations.revoke_others.title"),
description: t(
"core.uc_profile.device.operations.revoke_others.description"
),
confirmType: "danger",
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
async onConfirm() {
for (const device of otherDevices.value) {
await ucApiClient.security.device.revokeDevice({
deviceId: device.device.metadata.name,
});
}
queryClient.invalidateQueries({ queryKey: ["uc:devices"] });
Toast.success(
t("core.uc_profile.device.operations.revoke_others.toast_success")
);
},
});
}
</script> </script>
<template> <template>
<VLoading v-if="isLoading" /> <VLoading v-if="isLoading" />
<Transition v-else appear name="fade"> <TransitionGroup v-else appear name="fade">
<ul <ul
class="box-border h-full w-full divide-y divide-gray-100 overflow-hidden rounded-base border" class="box-border h-full w-full divide-y divide-gray-100 overflow-hidden rounded-base border"
role="list" role="list"
@ -32,5 +70,10 @@ const { data, isLoading } = useQuery({
<DeviceListItem :device="device" /> <DeviceListItem :device="device" />
</li> </li>
</ul> </ul>
</Transition> <div v-if="otherDevices.length" class="mt-5">
<VButton @click="handleRevokeOtherDevices">
{{ $t("core.uc_profile.device.operations.revoke_others.title") }}
</VButton>
</div>
</TransitionGroup>
</template> </template>