perf: automatic refresh of the list of users and roles (#797)

#### What type of PR is this?

/kind improvement
/milestone 2.1.x

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

优化用户和角色列表,如果包含正在删除的内容会自动刷新。

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

Fixes https://github.com/halo-dev/halo/issues/3036


#### Special notes for your reviewer:

测试方式:

1. 创建若干角色和用户。
2. 测试删除之后是否会自动刷新列表。

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


```release-note
优化 Console 端的用户和角色列表,如果包含正在删除的内容会自动刷新。
```
pull/784/head^2
Ryan Wang 2022-12-25 13:04:31 +08:00 committed by GitHub
parent f7ece9135a
commit da06195d08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 11 deletions

View File

@ -137,6 +137,8 @@ const handleDelete = async (role: Role) => {
Toast.success("删除成功"); Toast.success("删除成功");
} catch (e) { } catch (e) {
console.error("Failed to delete role", e); console.error("Failed to delete role", e);
} finally {
handleFetchRoles();
} }
}, },
}); });
@ -296,11 +298,6 @@ const handleDelete = async (role: Role) => {
></VEntityField> ></VEntityField>
</template> </template>
<template #end> <template #end>
<VEntityField v-if="role.metadata.deletionTimestamp">
<template #description>
<VStatusDot v-tooltip="``" state="warning" animate />
</template>
</VEntityField>
<!-- TODO: 支持显示用户数量 --> <!-- TODO: 支持显示用户数量 -->
<VEntityField v-if="false" description="0 个用户" /> <VEntityField v-if="false" description="0 个用户" />
<VEntityField> <VEntityField>
@ -310,6 +307,11 @@ const handleDelete = async (role: Role) => {
</VTag> </VTag>
</template> </template>
</VEntityField> </VEntityField>
<VEntityField v-if="role.metadata.deletionTimestamp">
<template #description>
<VStatusDot v-tooltip="``" state="warning" animate />
</template>
</VEntityField>
<VEntityField> <VEntityField>
<template #description> <template #description>
<span class="truncate text-xs tabular-nums text-gray-500"> <span class="truncate text-xs tabular-nums text-gray-500">

View File

@ -1,5 +1,5 @@
import type { Role } from "@halo-dev/api-client"; import type { Role } from "@halo-dev/api-client";
import type { ComputedRef, Ref } from "vue"; import { onUnmounted, type ComputedRef, type Ref } from "vue";
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import { roleLabels } from "@/constants/labels"; import { roleLabels } from "@/constants/labels";
import { rbacAnnotations } from "@/constants/annotations"; import { rbacAnnotations } from "@/constants/annotations";
@ -55,16 +55,32 @@ interface useRoleTemplateSelectionReturn {
export function useFetchRole(): useFetchRoleReturn { export function useFetchRole(): useFetchRoleReturn {
const roles = ref<Role[]>([]); const roles = ref<Role[]>([]);
const loading = ref(false); const loading = ref(false);
const refreshInterval = ref();
const handleFetchRoles = async () => { const handleFetchRoles = async (options?: { mute?: boolean }) => {
try { try {
loading.value = true; clearInterval(refreshInterval.value);
if (!options?.mute) {
loading.value = true;
}
const { data } = await apiClient.extension.role.listv1alpha1Role({ const { data } = await apiClient.extension.role.listv1alpha1Role({
page: 0, page: 0,
size: 0, size: 0,
labelSelector: [`!${roleLabels.TEMPLATE}`], labelSelector: [`!${roleLabels.TEMPLATE}`],
}); });
roles.value = data.items; roles.value = data.items;
const deletedRoles = roles.value.filter(
(role) => !!role.metadata.deletionTimestamp
);
if (deletedRoles.length) {
refreshInterval.value = setInterval(() => {
handleFetchRoles({ mute: true });
}, 3000);
}
} catch (e) { } catch (e) {
console.error("Failed to fetch roles", e); console.error("Failed to fetch roles", e);
} finally { } finally {
@ -74,6 +90,10 @@ export function useFetchRole(): useFetchRoleReturn {
onMounted(handleFetchRoles); onMounted(handleFetchRoles);
onUnmounted(() => {
clearInterval(refreshInterval.value);
});
return { return {
roles, roles,
loading, loading,

View File

@ -21,7 +21,7 @@ import {
import UserEditingModal from "./components/UserEditingModal.vue"; import UserEditingModal from "./components/UserEditingModal.vue";
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue"; import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
import GrantPermissionModal from "./components/GrantPermissionModal.vue"; import GrantPermissionModal from "./components/GrantPermissionModal.vue";
import { computed, onMounted, ref, watch } from "vue"; import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import { apiClient } from "@/utils/api-client"; import { apiClient } from "@/utils/api-client";
import type { User, UserList } from "@halo-dev/api-client"; import type { User, UserList } from "@halo-dev/api-client";
import { rbacAnnotations } from "@/constants/annotations"; import { rbacAnnotations } from "@/constants/annotations";
@ -52,14 +52,19 @@ const users = ref<UserList>({
const loading = ref(false); const loading = ref(false);
const selectedUserNames = ref<string[]>([]); const selectedUserNames = ref<string[]>([]);
const selectedUser = ref<User>(); const selectedUser = ref<User>();
const refreshInterval = ref();
const userStore = useUserStore(); const userStore = useUserStore();
let fuse: Fuse<User> | undefined = undefined; let fuse: Fuse<User> | undefined = undefined;
const handleFetchUsers = async () => { const handleFetchUsers = async (options?: { mute?: boolean }) => {
try { try {
loading.value = true; clearInterval(refreshInterval.value);
if (!options?.mute) {
loading.value = true;
}
const { data } = await apiClient.extension.user.listv1alpha1User({ const { data } = await apiClient.extension.user.listv1alpha1User({
page: users.value.page, page: users.value.page,
@ -71,6 +76,16 @@ const handleFetchUsers = async () => {
keys: ["spec.displayName", "metadata.name", "spec.email"], keys: ["spec.displayName", "metadata.name", "spec.email"],
useExtendedSearch: true, useExtendedSearch: true,
}); });
const deletedUsers = users.value.items.filter(
(user) => !!user.metadata.deletionTimestamp
);
if (deletedUsers.length) {
refreshInterval.value = setInterval(() => {
handleFetchUsers({ mute: true });
}, 3000);
}
} catch (e) { } catch (e) {
console.error("Failed to fetch users", e); console.error("Failed to fetch users", e);
} finally { } finally {
@ -199,6 +214,10 @@ onMounted(() => {
handleFetchUsers(); handleFetchUsers();
}); });
onUnmounted(() => {
clearInterval(refreshInterval.value);
});
// Route query action // Route query action
const routeQueryAction = useRouteQuery<string | undefined>("action"); const routeQueryAction = useRouteQuery<string | undefined>("action");