perf: improve plugin detail and settings page

pull/3445/head
Ryan Wang 2022-09-10 17:23:51 +08:00
parent bcd997231a
commit 088196e6ae
7 changed files with 80 additions and 58 deletions

View File

@ -1,3 +1,2 @@
export { default as BlankLayout } from "./BlankLayout.vue";
export { default as BasicLayout } from "./BasicLayout.vue";
export { default as PluginLayout } from "./PluginLayout.vue";

View File

@ -8,7 +8,7 @@ import { pluginLabels } from "@/constants/labels";
import { rbacAnnotations } from "@/constants/annotations";
import { usePluginLifeCycle } from "./composables/use-plugin";
const plugin = inject<Ref<Plugin>>("plugin", ref({} as Plugin));
const plugin = inject<Ref<Plugin | undefined>>("plugin");
const { changeStatus, isStarted } = usePluginLifeCycle(plugin);
interface RoleTemplateGroup {
@ -23,7 +23,7 @@ const handleFetchRoles = async () => {
const { data } = await apiClient.extension.role.listv1alpha1Role({
page: 0,
size: 0,
labelSelector: [`${pluginLabels.NAME}=${plugin.value.metadata.name}`],
labelSelector: [`${pluginLabels.NAME}=${plugin?.value?.metadata.name}`],
});
pluginRoleTemplates.value = data.items;
} catch (e) {
@ -51,7 +51,7 @@ const pluginRoleTemplateGroups = computed<RoleTemplateGroup[]>(() => {
});
watchEffect(() => {
if (plugin.value.metadata?.name) {
if (plugin?.value) {
handleFetchRoles();
}
});
@ -62,7 +62,7 @@ watchEffect(() => {
<div>
<h3 class="text-lg font-medium leading-6 text-gray-900">插件信息</h3>
<p class="mt-1 flex max-w-2xl items-center gap-2 text-sm text-gray-500">
<span>{{ plugin?.spec?.version }}</span>
<span>{{ plugin?.spec.version }}</span>
<VTag>
{{ isStarted ? "已启用" : "未启用" }}
</VTag>
@ -79,7 +79,7 @@ watchEffect(() => {
>
<dt class="text-sm font-medium text-gray-900">名称</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ plugin?.spec?.displayName }}
{{ plugin?.spec.displayName }}
</dd>
</div>
<div
@ -87,7 +87,7 @@ watchEffect(() => {
>
<dt class="text-sm font-medium text-gray-900">版本</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ plugin?.spec?.version }}
{{ plugin?.spec.version }}
</dd>
</div>
<div
@ -95,7 +95,7 @@ watchEffect(() => {
>
<dt class="text-sm font-medium text-gray-900">Halo 版本要求</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ plugin?.spec?.requires }}
{{ plugin?.spec.requires }}
</dd>
</div>
<div
@ -103,8 +103,8 @@ watchEffect(() => {
>
<dt class="text-sm font-medium text-gray-900">提供方</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<a :href="plugin?.spec?.homepage" target="_blank">
{{ plugin?.spec?.author }}
<a :href="plugin?.spec.homepage" target="_blank">
{{ plugin?.spec.author }}
</a>
</dd>
</div>
@ -114,7 +114,7 @@ watchEffect(() => {
<dt class="text-sm font-medium text-gray-900">协议</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<ul
v-if="plugin?.spec?.license && plugin?.spec?.license.length"
v-if="plugin?.spec.license && plugin?.spec.license.length"
class="list-inside list-disc"
>
<li v-for="(license, index) in plugin.spec.license" :key="index">
@ -132,7 +132,7 @@ watchEffect(() => {
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-6 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">模型定义</dt>
<dd class="mt-1 sm:col-span-2 sm:mt-0">
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<span></span>
</dd>
</div>

View File

@ -1,22 +1,24 @@
<script lang="ts" setup>
// core libs
import { computed, inject, ref, watchEffect } from "vue";
import { computed, ref } from "vue";
// hooks
import { useSettingForm } from "@halo-dev/admin-shared";
import { apiClient, useSettingForm } from "@halo-dev/admin-shared";
// components
import { VButton } from "@halo-dev/components";
// types
import type { Ref } from "vue";
import type { Plugin } from "@halo-dev/api-client";
import { useRouteParams } from "@vueuse/router";
const plugin = inject<Ref<Plugin>>("plugin", ref({} as Plugin));
const group = inject<Ref<string | undefined>>("activeTab");
const name = useRouteParams<string>("name");
const group = useRouteParams<string>("group");
const settingName = computed(() => plugin.value.spec?.settingName);
const configMapName = computed(() => plugin.value.spec?.configMapName);
const plugin = ref<Plugin | undefined>();
const settingName = computed(() => plugin?.value?.spec.settingName);
const configMapName = computed(() => plugin?.value?.spec.configMapName);
const {
settings,
@ -31,16 +33,28 @@ const formSchema = computed(() => {
if (!settings?.value?.spec) {
return;
}
return settings.value.spec.find((item) => item.group === group?.value)
return settings.value.spec.find((item) => item.group === group.value)
?.formSchema;
});
watchEffect(async () => {
if (settingName.value && configMapName.value) {
await handleFetchSettings();
await handleFetchConfigMap();
const handleFetchPlugin = async () => {
try {
const { data } =
await apiClient.extension.plugin.getpluginHaloRunV1alpha1Plugin({
name: name.value,
});
plugin.value = data;
if (settingName.value && configMapName.value) {
await handleFetchSettings();
await handleFetchConfigMap();
}
} catch (e) {
console.error("Failed to fetch plugin and settings", e);
}
});
};
await handleFetchPlugin();
</script>
<template>
<div class="bg-white p-4 sm:px-6">

View File

@ -13,10 +13,10 @@ import { formatDatetime } from "@/utils/date";
const props = withDefaults(
defineProps<{
plugin: Plugin | null;
plugin?: Plugin;
}>(),
{
plugin: null,
plugin: undefined,
}
);

View File

@ -12,18 +12,18 @@ interface usePluginLifeCycleReturn {
}
export function usePluginLifeCycle(
plugin: Ref<Plugin | null>
plugin?: Ref<Plugin | undefined>
): usePluginLifeCycleReturn {
const dialog = useDialog();
const isStarted = computed(() => {
return (
plugin.value?.status?.phase === "STARTED" && plugin.value?.spec.enabled
plugin?.value?.status?.phase === "STARTED" && plugin.value?.spec.enabled
);
});
const changeStatus = () => {
if (!plugin.value) return;
if (!plugin?.value) return;
const pluginToUpdate = cloneDeep(plugin.value);
@ -46,7 +46,7 @@ export function usePluginLifeCycle(
};
const uninstall = () => {
if (!plugin.value) return;
if (!plugin?.value) return;
const { enabled } = plugin.value.spec;

View File

@ -1,23 +1,23 @@
<script lang="ts" setup>
// core libs
import { computed, onMounted, provide, ref, watch } from "vue";
import { computed, nextTick, onMounted, provide, ref, watch } from "vue";
import { RouterView, useRoute, useRouter } from "vue-router";
import { apiClient } from "../utils/api-client";
import { apiClient } from "@halo-dev/admin-shared";
// libs
import cloneDeep from "lodash.clonedeep";
// hooks
import { useSettingForm } from "../composables";
import { useSettingForm } from "@halo-dev/admin-shared";
// components
import { VButton, VCard, VPageHeader, VTabbar } from "@halo-dev/components";
import { BasicLayout } from "../layouts";
import { BasicLayout } from "@halo-dev/admin-shared";
// types
import type { Ref } from "vue";
import type { Plugin } from "@halo-dev/api-client";
import type { FormKitSettingSpec } from "../types/formkit";
import type { FormKitSettingSpec } from "@halo-dev/admin-shared";
interface PluginTab {
id: string;
@ -41,15 +41,15 @@ const initialTabs: PluginTab[] = [
const route = useRoute();
const router = useRouter();
const plugin = ref<Plugin>({} as Plugin);
const plugin = ref<Plugin>();
const tabs = ref<PluginTab[]>(cloneDeep(initialTabs));
const activeTab = ref<string>();
provide<Ref<Plugin>>("plugin", plugin);
provide<Ref<Plugin | undefined>>("plugin", plugin);
provide<Ref<string | undefined>>("activeTab", activeTab);
const settingName = computed(() => plugin.value.spec?.settingName);
const configMapName = computed(() => plugin.value.spec?.configMapName);
const settingName = computed(() => plugin.value?.spec.settingName);
const configMapName = computed(() => plugin.value?.spec.configMapName);
const { settings, handleFetchSettings } = useSettingForm(
settingName,
@ -71,15 +71,16 @@ const handleFetchPlugin = async () => {
const handleTabChange = (id: string) => {
const tab = tabs.value.find((item) => item.id === id);
if (tab) {
activeTab.value = tab.id;
router.push(tab.route);
}
};
const onTabChange = (routeName: string) => {
if (routeName === "PluginSetting") {
const handleTriggerTabChange = () => {
if (route.name === "PluginSetting") {
const tab = tabs.value.find((tab) => {
return (
tab.route.name === routeName &&
tab.route.name === route.name &&
tab.route.params?.group === route.params.group
);
});
@ -87,7 +88,7 @@ const onTabChange = (routeName: string) => {
activeTab.value = tab.id;
return;
}
router.push({ name: "PluginDetail" });
handleTabChange(tabs.value[0].id);
return;
}
const tab = tabs.value.find((tab) => tab.route.name === route.name);
@ -99,6 +100,7 @@ onMounted(async () => {
await handleFetchSettings();
tabs.value = cloneDeep(initialTabs);
if (settings.value && settings.value.spec) {
tabs.value = [
...tabs.value,
@ -115,16 +117,16 @@ onMounted(async () => {
};
}),
] as PluginTab[];
onTabChange(route.name as string);
}
await nextTick();
handleTriggerTabChange();
});
watch(
() => route.name,
async (newRouteName) => {
onTabChange(newRouteName as string);
}
);
watch([() => route.name, () => route.params], () => {
handleTriggerTabChange();
});
</script>
<template>
<BasicLayout>
@ -150,7 +152,18 @@ watch(
</template>
</VCard>
<div>
<RouterView :key="activeTab" />
<RouterView :key="activeTab" v-slot="{ Component }">
<template v-if="Component">
<Suspense>
<component :is="Component"></component>
<template #fallback>
<div class="flex h-32 w-full justify-center bg-white">
<span class="text-sm text-gray-600">加载中...</span>
</div>
</template>
</Suspense>
</template>
</RouterView>
</div>
</div>
</BasicLayout>

View File

@ -1,9 +1,5 @@
import {
BasicLayout,
BlankLayout,
definePlugin,
PluginLayout,
} from "@halo-dev/admin-shared";
import { BasicLayout, BlankLayout, definePlugin } from "@halo-dev/admin-shared";
import PluginLayout from "./layouts/PluginLayout.vue";
import PluginList from "./PluginList.vue";
import PluginSetting from "./PluginSetting.vue";
import PluginDetail from "./PluginDetail.vue";