mirror of https://github.com/halo-dev/halo
perf: improve plugin detail and settings page
parent
bcd997231a
commit
088196e6ae
|
@ -1,3 +1,2 @@
|
||||||
export { default as BlankLayout } from "./BlankLayout.vue";
|
export { default as BlankLayout } from "./BlankLayout.vue";
|
||||||
export { default as BasicLayout } from "./BasicLayout.vue";
|
export { default as BasicLayout } from "./BasicLayout.vue";
|
||||||
export { default as PluginLayout } from "./PluginLayout.vue";
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { pluginLabels } from "@/constants/labels";
|
||||||
import { rbacAnnotations } from "@/constants/annotations";
|
import { rbacAnnotations } from "@/constants/annotations";
|
||||||
import { usePluginLifeCycle } from "./composables/use-plugin";
|
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);
|
const { changeStatus, isStarted } = usePluginLifeCycle(plugin);
|
||||||
|
|
||||||
interface RoleTemplateGroup {
|
interface RoleTemplateGroup {
|
||||||
|
@ -23,7 +23,7 @@ const handleFetchRoles = async () => {
|
||||||
const { data } = await apiClient.extension.role.listv1alpha1Role({
|
const { data } = await apiClient.extension.role.listv1alpha1Role({
|
||||||
page: 0,
|
page: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
labelSelector: [`${pluginLabels.NAME}=${plugin.value.metadata.name}`],
|
labelSelector: [`${pluginLabels.NAME}=${plugin?.value?.metadata.name}`],
|
||||||
});
|
});
|
||||||
pluginRoleTemplates.value = data.items;
|
pluginRoleTemplates.value = data.items;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -51,7 +51,7 @@ const pluginRoleTemplateGroups = computed<RoleTemplateGroup[]>(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (plugin.value.metadata?.name) {
|
if (plugin?.value) {
|
||||||
handleFetchRoles();
|
handleFetchRoles();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -62,7 +62,7 @@ watchEffect(() => {
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-medium leading-6 text-gray-900">插件信息</h3>
|
<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">
|
<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>
|
<VTag>
|
||||||
{{ isStarted ? "已启用" : "未启用" }}
|
{{ isStarted ? "已启用" : "未启用" }}
|
||||||
</VTag>
|
</VTag>
|
||||||
|
@ -79,7 +79,7 @@ watchEffect(() => {
|
||||||
>
|
>
|
||||||
<dt class="text-sm font-medium text-gray-900">名称</dt>
|
<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">
|
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
|
||||||
{{ plugin?.spec?.displayName }}
|
{{ plugin?.spec.displayName }}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -87,7 +87,7 @@ watchEffect(() => {
|
||||||
>
|
>
|
||||||
<dt class="text-sm font-medium text-gray-900">版本</dt>
|
<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">
|
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
|
||||||
{{ plugin?.spec?.version }}
|
{{ plugin?.spec.version }}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -95,7 +95,7 @@ watchEffect(() => {
|
||||||
>
|
>
|
||||||
<dt class="text-sm font-medium text-gray-900">Halo 版本要求</dt>
|
<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">
|
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
|
||||||
{{ plugin?.spec?.requires }}
|
{{ plugin?.spec.requires }}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -103,8 +103,8 @@ watchEffect(() => {
|
||||||
>
|
>
|
||||||
<dt class="text-sm font-medium text-gray-900">提供方</dt>
|
<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">
|
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
|
||||||
<a :href="plugin?.spec?.homepage" target="_blank">
|
<a :href="plugin?.spec.homepage" target="_blank">
|
||||||
{{ plugin?.spec?.author }}
|
{{ plugin?.spec.author }}
|
||||||
</a>
|
</a>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
@ -114,7 +114,7 @@ watchEffect(() => {
|
||||||
<dt class="text-sm font-medium text-gray-900">协议</dt>
|
<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">
|
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
|
||||||
<ul
|
<ul
|
||||||
v-if="plugin?.spec?.license && plugin?.spec?.license.length"
|
v-if="plugin?.spec.license && plugin?.spec.license.length"
|
||||||
class="list-inside list-disc"
|
class="list-inside list-disc"
|
||||||
>
|
>
|
||||||
<li v-for="(license, index) in plugin.spec.license" :key="index">
|
<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"
|
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>
|
<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>
|
<span>无</span>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
// core libs
|
||||||
import { computed, inject, ref, watchEffect } from "vue";
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useSettingForm } from "@halo-dev/admin-shared";
|
import { apiClient, useSettingForm } from "@halo-dev/admin-shared";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { VButton } from "@halo-dev/components";
|
import { VButton } from "@halo-dev/components";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import type { Ref } from "vue";
|
|
||||||
import type { Plugin } from "@halo-dev/api-client";
|
import type { Plugin } from "@halo-dev/api-client";
|
||||||
|
import { useRouteParams } from "@vueuse/router";
|
||||||
|
|
||||||
const plugin = inject<Ref<Plugin>>("plugin", ref({} as Plugin));
|
const name = useRouteParams<string>("name");
|
||||||
const group = inject<Ref<string | undefined>>("activeTab");
|
const group = useRouteParams<string>("group");
|
||||||
|
|
||||||
const settingName = computed(() => plugin.value.spec?.settingName);
|
const plugin = ref<Plugin | undefined>();
|
||||||
const configMapName = computed(() => plugin.value.spec?.configMapName);
|
|
||||||
|
const settingName = computed(() => plugin?.value?.spec.settingName);
|
||||||
|
const configMapName = computed(() => plugin?.value?.spec.configMapName);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
settings,
|
settings,
|
||||||
|
@ -31,16 +33,28 @@ const formSchema = computed(() => {
|
||||||
if (!settings?.value?.spec) {
|
if (!settings?.value?.spec) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return settings.value.spec.find((item) => item.group === group?.value)
|
return settings.value.spec.find((item) => item.group === group.value)
|
||||||
?.formSchema;
|
?.formSchema;
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(async () => {
|
const handleFetchPlugin = async () => {
|
||||||
|
try {
|
||||||
|
const { data } =
|
||||||
|
await apiClient.extension.plugin.getpluginHaloRunV1alpha1Plugin({
|
||||||
|
name: name.value,
|
||||||
|
});
|
||||||
|
plugin.value = data;
|
||||||
|
|
||||||
if (settingName.value && configMapName.value) {
|
if (settingName.value && configMapName.value) {
|
||||||
await handleFetchSettings();
|
await handleFetchSettings();
|
||||||
await handleFetchConfigMap();
|
await handleFetchConfigMap();
|
||||||
}
|
}
|
||||||
});
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch plugin and settings", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await handleFetchPlugin();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-white p-4 sm:px-6">
|
<div class="bg-white p-4 sm:px-6">
|
||||||
|
|
|
@ -13,10 +13,10 @@ import { formatDatetime } from "@/utils/date";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
plugin: Plugin | null;
|
plugin?: Plugin;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
plugin: null,
|
plugin: undefined,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -12,18 +12,18 @@ interface usePluginLifeCycleReturn {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePluginLifeCycle(
|
export function usePluginLifeCycle(
|
||||||
plugin: Ref<Plugin | null>
|
plugin?: Ref<Plugin | undefined>
|
||||||
): usePluginLifeCycleReturn {
|
): usePluginLifeCycleReturn {
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
|
|
||||||
const isStarted = computed(() => {
|
const isStarted = computed(() => {
|
||||||
return (
|
return (
|
||||||
plugin.value?.status?.phase === "STARTED" && plugin.value?.spec.enabled
|
plugin?.value?.status?.phase === "STARTED" && plugin.value?.spec.enabled
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeStatus = () => {
|
const changeStatus = () => {
|
||||||
if (!plugin.value) return;
|
if (!plugin?.value) return;
|
||||||
|
|
||||||
const pluginToUpdate = cloneDeep(plugin.value);
|
const pluginToUpdate = cloneDeep(plugin.value);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export function usePluginLifeCycle(
|
||||||
};
|
};
|
||||||
|
|
||||||
const uninstall = () => {
|
const uninstall = () => {
|
||||||
if (!plugin.value) return;
|
if (!plugin?.value) return;
|
||||||
|
|
||||||
const { enabled } = plugin.value.spec;
|
const { enabled } = plugin.value.spec;
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
// 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 { RouterView, useRoute, useRouter } from "vue-router";
|
||||||
import { apiClient } from "../utils/api-client";
|
import { apiClient } from "@halo-dev/admin-shared";
|
||||||
|
|
||||||
// libs
|
// libs
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useSettingForm } from "../composables";
|
import { useSettingForm } from "@halo-dev/admin-shared";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { VButton, VCard, VPageHeader, VTabbar } from "@halo-dev/components";
|
import { VButton, VCard, VPageHeader, VTabbar } from "@halo-dev/components";
|
||||||
import { BasicLayout } from "../layouts";
|
import { BasicLayout } from "@halo-dev/admin-shared";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import type { Plugin } from "@halo-dev/api-client";
|
import type { Plugin } from "@halo-dev/api-client";
|
||||||
import type { FormKitSettingSpec } from "../types/formkit";
|
import type { FormKitSettingSpec } from "@halo-dev/admin-shared";
|
||||||
|
|
||||||
interface PluginTab {
|
interface PluginTab {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -41,15 +41,15 @@ const initialTabs: PluginTab[] = [
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const plugin = ref<Plugin>({} as Plugin);
|
const plugin = ref<Plugin>();
|
||||||
const tabs = ref<PluginTab[]>(cloneDeep(initialTabs));
|
const tabs = ref<PluginTab[]>(cloneDeep(initialTabs));
|
||||||
const activeTab = ref<string>();
|
const activeTab = ref<string>();
|
||||||
|
|
||||||
provide<Ref<Plugin>>("plugin", plugin);
|
provide<Ref<Plugin | undefined>>("plugin", plugin);
|
||||||
provide<Ref<string | undefined>>("activeTab", activeTab);
|
provide<Ref<string | undefined>>("activeTab", activeTab);
|
||||||
|
|
||||||
const settingName = computed(() => plugin.value.spec?.settingName);
|
const settingName = computed(() => plugin.value?.spec.settingName);
|
||||||
const configMapName = computed(() => plugin.value.spec?.configMapName);
|
const configMapName = computed(() => plugin.value?.spec.configMapName);
|
||||||
|
|
||||||
const { settings, handleFetchSettings } = useSettingForm(
|
const { settings, handleFetchSettings } = useSettingForm(
|
||||||
settingName,
|
settingName,
|
||||||
|
@ -71,15 +71,16 @@ const handleFetchPlugin = async () => {
|
||||||
const handleTabChange = (id: string) => {
|
const handleTabChange = (id: string) => {
|
||||||
const tab = tabs.value.find((item) => item.id === id);
|
const tab = tabs.value.find((item) => item.id === id);
|
||||||
if (tab) {
|
if (tab) {
|
||||||
|
activeTab.value = tab.id;
|
||||||
router.push(tab.route);
|
router.push(tab.route);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTabChange = (routeName: string) => {
|
const handleTriggerTabChange = () => {
|
||||||
if (routeName === "PluginSetting") {
|
if (route.name === "PluginSetting") {
|
||||||
const tab = tabs.value.find((tab) => {
|
const tab = tabs.value.find((tab) => {
|
||||||
return (
|
return (
|
||||||
tab.route.name === routeName &&
|
tab.route.name === route.name &&
|
||||||
tab.route.params?.group === route.params.group
|
tab.route.params?.group === route.params.group
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -87,7 +88,7 @@ const onTabChange = (routeName: string) => {
|
||||||
activeTab.value = tab.id;
|
activeTab.value = tab.id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.push({ name: "PluginDetail" });
|
handleTabChange(tabs.value[0].id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tab = tabs.value.find((tab) => tab.route.name === route.name);
|
const tab = tabs.value.find((tab) => tab.route.name === route.name);
|
||||||
|
@ -99,6 +100,7 @@ onMounted(async () => {
|
||||||
await handleFetchSettings();
|
await handleFetchSettings();
|
||||||
|
|
||||||
tabs.value = cloneDeep(initialTabs);
|
tabs.value = cloneDeep(initialTabs);
|
||||||
|
|
||||||
if (settings.value && settings.value.spec) {
|
if (settings.value && settings.value.spec) {
|
||||||
tabs.value = [
|
tabs.value = [
|
||||||
...tabs.value,
|
...tabs.value,
|
||||||
|
@ -115,16 +117,16 @@ onMounted(async () => {
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
] as PluginTab[];
|
] as PluginTab[];
|
||||||
onTabChange(route.name as string);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
handleTriggerTabChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch([() => route.name, () => route.params], () => {
|
||||||
() => route.name,
|
handleTriggerTabChange();
|
||||||
async (newRouteName) => {
|
});
|
||||||
onTabChange(newRouteName as string);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<BasicLayout>
|
<BasicLayout>
|
||||||
|
@ -150,7 +152,18 @@ watch(
|
||||||
</template>
|
</template>
|
||||||
</VCard>
|
</VCard>
|
||||||
<div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</BasicLayout>
|
</BasicLayout>
|
|
@ -1,9 +1,5 @@
|
||||||
import {
|
import { BasicLayout, BlankLayout, definePlugin } from "@halo-dev/admin-shared";
|
||||||
BasicLayout,
|
import PluginLayout from "./layouts/PluginLayout.vue";
|
||||||
BlankLayout,
|
|
||||||
definePlugin,
|
|
||||||
PluginLayout,
|
|
||||||
} from "@halo-dev/admin-shared";
|
|
||||||
import PluginList from "./PluginList.vue";
|
import PluginList from "./PluginList.vue";
|
||||||
import PluginSetting from "./PluginSetting.vue";
|
import PluginSetting from "./PluginSetting.vue";
|
||||||
import PluginDetail from "./PluginDetail.vue";
|
import PluginDetail from "./PluginDetail.vue";
|
||||||
|
|
Loading…
Reference in New Issue