mirror of https://github.com/halo-dev/halo
refactor: use tanstack query to refactor theme-related fetching (#3588)
#### 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 ```pull/3576/head^2
parent
bd9f590c1e
commit
5b3b473cb3
|
@ -17,11 +17,11 @@ import LazyImage from "@/components/image/LazyImage.vue";
|
|||
import ThemePreviewModal from "./preview/ThemePreviewModal.vue";
|
||||
import ThemeUploadModal from "./ThemeUploadModal.vue";
|
||||
import ThemeListItem from "./components/ThemeListItem.vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import type { Theme } from "@halo-dev/api-client";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { onBeforeRouteLeave } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -44,11 +44,8 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const activeTab = ref("installed");
|
||||
const themes = ref<Theme[]>([] as Theme[]);
|
||||
const loading = ref(false);
|
||||
const themeUploadVisible = ref(false);
|
||||
const creating = ref(false);
|
||||
const refreshInterval = ref();
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
return activeTab.value === "installed"
|
||||
|
@ -56,51 +53,36 @@ const modalTitle = computed(() => {
|
|||
: t("core.theme.list_modal.titles.not_installed_themes");
|
||||
});
|
||||
|
||||
const handleFetchThemes = async (options?: { mute?: boolean }) => {
|
||||
try {
|
||||
clearInterval(refreshInterval.value);
|
||||
|
||||
if (!options?.mute) {
|
||||
loading.value = true;
|
||||
}
|
||||
const {
|
||||
data: themes,
|
||||
isLoading,
|
||||
isFetching,
|
||||
refetch,
|
||||
} = useQuery<Theme[]>({
|
||||
queryKey: ["themes", activeTab],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.theme.listThemes({
|
||||
page: 0,
|
||||
size: 0,
|
||||
uninstalled: activeTab.value !== "installed",
|
||||
});
|
||||
themes.value = data.items;
|
||||
|
||||
return data.items;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchInterval(data) {
|
||||
if (activeTab.value !== "installed") {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const deletedThemes = themes.value.filter(
|
||||
const deletingThemes = data?.filter(
|
||||
(theme) => !!theme.metadata.deletionTimestamp
|
||||
);
|
||||
|
||||
if (deletedThemes.length) {
|
||||
refreshInterval.value = setInterval(() => {
|
||||
handleFetchThemes({ mute: true });
|
||||
}, 3000);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch themes", e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
clearInterval(refreshInterval.value);
|
||||
return deletingThemes?.length ? 3000 : false;
|
||||
},
|
||||
enabled: computed(() => props.visible),
|
||||
});
|
||||
|
||||
watch(
|
||||
() => activeTab.value,
|
||||
() => {
|
||||
handleFetchThemes();
|
||||
}
|
||||
);
|
||||
|
||||
const handleCreateTheme = async (theme: Theme) => {
|
||||
try {
|
||||
creating.value = true;
|
||||
|
@ -120,7 +102,7 @@ const handleCreateTheme = async (theme: Theme) => {
|
|||
console.error("Failed to create theme", error);
|
||||
} finally {
|
||||
creating.value = false;
|
||||
handleFetchThemes();
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -137,19 +119,8 @@ const handleSelectTheme = (theme: Theme) => {
|
|||
onVisibleChange(false);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
handleFetchThemes();
|
||||
} else {
|
||||
clearInterval(refreshInterval.value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
handleFetchThemes,
|
||||
handleFetchThemes: refetch,
|
||||
});
|
||||
|
||||
// preview
|
||||
|
@ -193,15 +164,15 @@ const handleOpenInstallModal = () => {
|
|||
:label="$t('core.theme.list_modal.tabs.installed')"
|
||||
class="-mx-[16px]"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!themes.length" appear name="fade">
|
||||
<VLoading v-if="isLoading" />
|
||||
<Transition v-else-if="!themes?.length" appear name="fade">
|
||||
<VEmpty
|
||||
:message="$t('core.theme.list_modal.empty.message')"
|
||||
:title="$t('core.theme.list_modal.empty.title')"
|
||||
>
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton :loading="loading" @click="handleFetchThemes()">
|
||||
<VButton :loading="isFetching" @click="refetch()">
|
||||
{{ $t("core.common.buttons.refresh") }}
|
||||
</VButton>
|
||||
<VButton
|
||||
|
@ -233,7 +204,7 @@ const handleOpenInstallModal = () => {
|
|||
:is-selected="
|
||||
theme.metadata.name === selectedTheme?.metadata?.name
|
||||
"
|
||||
@reload="handleFetchThemes({ mute: true })"
|
||||
@reload="refetch"
|
||||
@preview="handleOpenPreview(theme)"
|
||||
@upgrade="handleOpenUpgradeModal(theme)"
|
||||
/>
|
||||
|
@ -246,14 +217,14 @@ const handleOpenInstallModal = () => {
|
|||
:label="$t('core.theme.list_modal.tabs.not_installed')"
|
||||
class="-mx-[16px]"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!themes.length" appear name="fade">
|
||||
<VLoading v-if="isLoading" />
|
||||
<Transition v-else-if="!themes?.length" appear name="fade">
|
||||
<VEmpty
|
||||
:title="$t('core.theme.list_modal.not_installed_empty.title')"
|
||||
>
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton :loading="loading" @click="handleFetchThemes">
|
||||
<VButton :loading="isFetching" @click="refetch">
|
||||
{{ $t("core.common.buttons.refresh") }}
|
||||
</VButton>
|
||||
</VSpace>
|
||||
|
@ -372,7 +343,7 @@ const handleOpenInstallModal = () => {
|
|||
v-if="visible"
|
||||
v-model:visible="themeUploadVisible"
|
||||
:upgrade-theme="themeToUpgrade"
|
||||
@close="handleFetchThemes"
|
||||
@close="refetch"
|
||||
/>
|
||||
|
||||
<ThemePreviewModal
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
import { storeToRefs } from "pinia";
|
||||
import { computed, markRaw, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useQuery } from "@tanstack/vue-query";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
@ -55,33 +56,34 @@ interface SettingTab {
|
|||
const { activatedTheme } = storeToRefs(useThemeStore());
|
||||
|
||||
const previewFrame = ref<HTMLIFrameElement | null>(null);
|
||||
const themes = ref<Theme[]>([] as Theme[]);
|
||||
const themesVisible = ref(false);
|
||||
const switching = ref(false);
|
||||
const selectedTheme = ref<Theme>();
|
||||
|
||||
const handleFetchThemes = async () => {
|
||||
try {
|
||||
const { data: themes } = useQuery<Theme[]>({
|
||||
queryKey: ["themes"],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.theme.listThemes({
|
||||
page: 0,
|
||||
size: 0,
|
||||
uninstalled: false,
|
||||
});
|
||||
themes.value = data.items;
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch themes", e);
|
||||
}
|
||||
};
|
||||
return data.items;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: computed(() => props.visible),
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
handleFetchThemes();
|
||||
selectedTheme.value = props.theme || activatedTheme?.value;
|
||||
} else {
|
||||
themesVisible.value = false;
|
||||
settingsVisible.value = false;
|
||||
setTimeout(() => {
|
||||
themesVisible.value = false;
|
||||
settingsVisible.value = false;
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -121,39 +123,42 @@ const modalTitle = computed(() => {
|
|||
});
|
||||
|
||||
// theme settings
|
||||
const setting = ref<Setting>();
|
||||
const configMap = ref<ConfigMap>();
|
||||
const saving = ref(false);
|
||||
const settingTabs = ref<SettingTab[]>([] as SettingTab[]);
|
||||
const activeSettingTab = ref("");
|
||||
const settingsVisible = ref(false);
|
||||
|
||||
const { data: setting, refetch: handleFetchSettings } = useQuery<Setting>({
|
||||
queryKey: ["theme-setting", selectedTheme],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.theme.fetchThemeSetting({
|
||||
name: selectedTheme?.value?.metadata.name as string,
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: computed(() => !!selectedTheme.value?.spec.settingName),
|
||||
});
|
||||
|
||||
const { data: configMap, refetch: handleFetchConfigMap } = useQuery<ConfigMap>({
|
||||
queryKey: ["theme-configMap", selectedTheme],
|
||||
queryFn: async () => {
|
||||
const { data } = await apiClient.theme.fetchThemeConfig({
|
||||
name: selectedTheme?.value?.metadata.name as string,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
enabled: computed(() => !!selectedTheme.value?.spec.configMapName),
|
||||
});
|
||||
|
||||
const { formSchema, configMapFormData, convertToSave } = useSettingFormConvert(
|
||||
setting,
|
||||
configMap,
|
||||
activeSettingTab
|
||||
);
|
||||
|
||||
const handleFetchSettings = async () => {
|
||||
if (!selectedTheme?.value) return;
|
||||
|
||||
const { data } = await apiClient.theme.fetchThemeSetting({
|
||||
name: selectedTheme?.value?.metadata.name,
|
||||
});
|
||||
|
||||
setting.value = data;
|
||||
};
|
||||
|
||||
const handleFetchConfigMap = async () => {
|
||||
if (!selectedTheme?.value) return;
|
||||
|
||||
const { data } = await apiClient.theme.fetchThemeConfig({
|
||||
name: selectedTheme?.value?.metadata.name,
|
||||
});
|
||||
|
||||
configMap.value = data;
|
||||
};
|
||||
|
||||
const handleSaveConfigMap = async () => {
|
||||
saving.value = true;
|
||||
|
||||
|
@ -164,7 +169,7 @@ const handleSaveConfigMap = async () => {
|
|||
return;
|
||||
}
|
||||
|
||||
const { data: newConfigMap } = await apiClient.theme.updateThemeConfig({
|
||||
await apiClient.theme.updateThemeConfig({
|
||||
name: selectedTheme?.value?.metadata.name,
|
||||
configMap: configMapToUpdate,
|
||||
});
|
||||
|
@ -172,7 +177,7 @@ const handleSaveConfigMap = async () => {
|
|||
Toast.success(t("core.common.toast.save_success"));
|
||||
|
||||
await handleFetchSettings();
|
||||
configMap.value = newConfigMap;
|
||||
await handleFetchConfigMap();
|
||||
|
||||
saving.value = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue