From 4ce5d0c2cfa05b310ea4de59636c5de392692525 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Thu, 23 Mar 2023 21:44:34 +0800 Subject: [PATCH] refactor: use tanstack query to refactor user-related fetching (#3548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement #### What this PR does / why we need it: 使用 [TanStack Query](https://github.com/TanStack/query) 重构用户相关数据请求的相关逻辑。 #### Which issue(s) this PR fixes: Ref https://github.com/halo-dev/halo/issues/3360 #### Special notes for your reviewer: 测试方式: 1. 测试用户管理列表的数据请求 + 条件筛选是否正常。 2. 测试修改当前登录用户和其他用户的信息和密码是否正常。 #### Does this PR introduce a user-facing change? ```release-note None ``` --- console/src/modules/system/users/UserList.vue | 234 ++++++++---------- .../users/components/UserEditingModal.vue | 13 +- .../users/layouts/UserProfileLayout.vue | 33 ++- 3 files changed, 123 insertions(+), 157 deletions(-) diff --git a/console/src/modules/system/users/UserList.vue b/console/src/modules/system/users/UserList.vue index 851c32e2f..113971ca8 100644 --- a/console/src/modules/system/users/UserList.vue +++ b/console/src/modules/system/users/UserList.vue @@ -24,9 +24,9 @@ import { import UserEditingModal from "./components/UserEditingModal.vue"; import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue"; import GrantPermissionModal from "./components/GrantPermissionModal.vue"; -import { computed, onMounted, onUnmounted, ref, watch } from "vue"; +import { computed, onMounted, ref, watch } from "vue"; import { apiClient } from "@/utils/api-client"; -import type { Role, User, ListedUserList } from "@halo-dev/api-client"; +import type { Role, User, ListedUser } from "@halo-dev/api-client"; import { rbacAnnotations } from "@/constants/annotations"; import { formatDatetime } from "@/utils/date"; import { useRouteQuery } from "@vueuse/router"; @@ -36,6 +36,7 @@ import { getNode } from "@formkit/core"; import FilterTag from "@/components/filter/FilterTag.vue"; import { useFetchRole } from "../roles/composables/use-role"; import FilterCleanButton from "@/components/filter/FilterCleanButton.vue"; +import { useQuery } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; const { currentUserHasPermission } = usePermission(); @@ -46,46 +47,86 @@ const editingModal = ref(false); const passwordChangeModal = ref(false); const grantPermissionModal = ref(false); -const users = ref({ - page: 1, - size: 20, - total: 0, - items: [], - first: true, - last: false, - hasNext: false, - hasPrevious: false, - totalPages: 0, -}); -const loading = ref(false); const selectedUserNames = ref([]); const selectedUser = ref(); const keyword = ref(""); -const refreshInterval = ref(); const userStore = useUserStore(); const ANONYMOUSUSER_NAME = "anonymousUser"; const DELETEDUSER_NAME = "ghost"; -const handleFetchUsers = async (options?: { - mute?: boolean; - page?: number; -}) => { - try { - clearInterval(refreshInterval.value); +// Filters +function handleKeywordChange() { + const keywordNode = getNode("keywordInput"); + if (keywordNode) { + keyword.value = keywordNode._value as string; + } + page.value = 1; +} - if (!options?.mute) { - loading.value = true; - } +function handleClearKeyword() { + keyword.value = ""; + page.value = 1; +} - if (options?.page) { - users.value.page = options.page; - } +interface SortItem { + label: string; + value: string; +} +const SortItems: SortItem[] = [ + { + label: t("core.user.filters.sort.items.create_time_desc"), + value: "creationTimestamp,desc", + }, + { + label: t("core.user.filters.sort.items.create_time_asc"), + value: "creationTimestamp,asc", + }, +]; + +const selectedSortItem = ref(); + +function handleSortItemChange(sortItem?: SortItem) { + selectedSortItem.value = sortItem; + page.value = 1; +} + +const { roles } = useFetchRole(); +const selectedRole = ref(); + +function handleRoleChange(role?: Role) { + selectedRole.value = role; + page.value = 1; +} + +function handleClearFilters() { + selectedRole.value = undefined; + selectedSortItem.value = undefined; + keyword.value = ""; + page.value = 1; +} + +const hasFilters = computed(() => { + return selectedRole.value || selectedSortItem.value || keyword.value; +}); + +const page = ref(1); +const size = ref(20); +const total = ref(0); + +const { + data: users, + isLoading, + isFetching, + refetch, +} = useQuery({ + queryKey: ["users", page, size, keyword, selectedSortItem, selectedRole], + queryFn: async () => { const { data } = await apiClient.user.listUsers({ - page: users.value.page, - size: users.value.size, + page: page.value, + size: size.value, keyword: keyword.value, fieldSelector: [ `name!=${ANONYMOUSUSER_NAME}`, @@ -97,36 +138,22 @@ const handleFetchUsers = async (options?: { role: selectedRole.value?.metadata.name, }); - users.value = data; + total.value = data.total; - const deletedUsers = users.value.items.filter( + return data.items; + }, + refetchOnWindowFocus: false, + refetchInterval(data) { + const deletingUsers = data?.filter( (user) => !!user.user.metadata.deletionTimestamp ); - if (deletedUsers.length) { - refreshInterval.value = setInterval(() => { - handleFetchUsers({ mute: true }); - }, 3000); - } - } catch (e) { - console.error("Failed to fetch users", e); - } finally { + return deletingUsers?.length ? 3000 : false; + }, + onSuccess() { selectedUser.value = undefined; - loading.value = false; - } -}; - -const handlePaginationChange = async ({ - page, - size, -}: { - page: number; - size: number; -}) => { - users.value.page = page; - users.value.size = size; - await handleFetchUsers(); -}; + }, +}); const handleDelete = async (user: User) => { Dialog.warning({ @@ -145,7 +172,7 @@ const handleDelete = async (user: User) => { } catch (e) { console.error("Failed to delete user", e); } finally { - await handleFetchUsers(); + await refetch(); } }, }); @@ -169,7 +196,7 @@ const handleDeleteInBatch = async () => { }); }) ); - await handleFetchUsers(); + await refetch(); selectedUserNames.value.length = 0; Toast.success(t("core.common.toast.delete_success")); }, @@ -177,7 +204,7 @@ const handleDeleteInBatch = async () => { }; watch(selectedUserNames, (newValue) => { - checkedAll.value = newValue.length === users.value.items?.length; + checkedAll.value = newValue.length === users.value?.length; }); const checkSelection = (user: User) => { @@ -192,7 +219,7 @@ const handleCheckAllChange = (e: Event) => { if (checked) { selectedUserNames.value = - users.value.items.map((user) => { + users.value?.map((user) => { return user.user.metadata.name; }) || []; } else { @@ -207,7 +234,7 @@ const handleOpenCreateModal = (user: User) => { const onEditingModalClose = () => { routeQueryAction.value = undefined; - handleFetchUsers(); + refetch(); }; const handleOpenPasswordChangeModal = (user: User) => { @@ -220,14 +247,6 @@ const handleOpenGrantPermissionModal = (user: User) => { grantPermissionModal.value = true; }; -onMounted(() => { - handleFetchUsers(); -}); - -onUnmounted(() => { - clearInterval(refreshInterval.value); -}); - // Route query action const routeQueryAction = useRouteQuery("action"); @@ -239,62 +258,6 @@ onMounted(() => { editingModal.value = true; } }); - -// Filters -function handleKeywordChange() { - const keywordNode = getNode("keywordInput"); - if (keywordNode) { - keyword.value = keywordNode._value as string; - } - handleFetchUsers({ page: 1 }); -} - -function handleClearKeyword() { - keyword.value = ""; - handleFetchUsers({ page: 1 }); -} - -interface SortItem { - label: string; - value: string; -} - -const SortItems: SortItem[] = [ - { - label: t("core.user.filters.sort.items.create_time_desc"), - value: "creationTimestamp,desc", - }, - { - label: t("core.user.filters.sort.items.create_time_asc"), - value: "creationTimestamp,asc", - }, -]; - -const selectedSortItem = ref(); - -function handleSortItemChange(sortItem?: SortItem) { - selectedSortItem.value = sortItem; - handleFetchUsers({ page: 1 }); -} - -const { roles } = useFetchRole(); -const selectedRole = ref(); - -function handleRoleChange(role?: Role) { - selectedRole.value = role; - handleFetchUsers({ page: 1 }); -} - -function handleClearFilters() { - selectedRole.value = undefined; - selectedSortItem.value = undefined; - keyword.value = ""; - handleFetchUsers({ page: 1 }); -} - -const hasFilters = computed(() => { - return selectedRole.value || selectedSortItem.value || keyword.value; -}); - + - +