This is an automated email from the ASF dual-hosted git repository.

wuzhiguo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bigtop-manager.git


The following commit(s) were added to refs/heads/main by this push:
     new 68a055d2 BIGTOP-4409: Fix duplicate menus (#209)
68a055d2 is described below

commit 68a055d27314d28672a7d8dfa00519e4d037b943
Author: Fdefined <[email protected]>
AuthorDate: Tue Apr 15 15:28:14 2025 +0800

    BIGTOP-4409: Fix duplicate menus (#209)
---
 .../components/component-installer.vue             |  11 +-
 bigtop-manager-ui/src/components/job/index.vue     |  71 +++++++--
 .../src/components/service-management/configs.vue  |   1 -
 .../src/components/user-avatar/index.vue           |   3 -
 bigtop-manager-ui/src/layouts/header.vue           |  36 ++---
 bigtop-manager-ui/src/layouts/index.vue            |  28 ++--
 bigtop-manager-ui/src/layouts/sider.vue            | 131 +++++++++-------
 bigtop-manager-ui/src/locales/en_US/cluster.ts     |   4 +-
 bigtop-manager-ui/src/locales/en_US/menu.ts        |   2 +-
 bigtop-manager-ui/src/locales/zh_CN/menu.ts        |   2 +-
 .../cluster/components/check-workflow.vue          |  11 +-
 .../src/pages/cluster-manage/cluster/create.vue    |  11 +-
 .../src/pages/cluster-manage/cluster/service.vue   |   2 +-
 .../src/pages/cluster-manage/components/index.vue  |  15 +-
 .../cluster-manage/infrastructures/service.vue     |   2 +-
 bigtop-manager-ui/src/pages/login/index.vue        |  13 +-
 bigtop-manager-ui/src/plugins/index.ts             |   3 +-
 bigtop-manager-ui/src/router/guard.ts              |  18 ++-
 bigtop-manager-ui/src/router/routes/index.ts       |   1 +
 .../src/router/routes/modules/clusters.ts          |  77 +++++----
 .../src/router/routes/modules/system.ts            |  18 ++-
 bigtop-manager-ui/src/store/cluster/index.ts       |  12 +-
 bigtop-manager-ui/src/store/menu/index.ts          | 174 ++++++---------------
 bigtop-manager-ui/src/utils/router-util.ts         |  27 +---
 24 files changed, 324 insertions(+), 349 deletions(-)

diff --git 
a/bigtop-manager-ui/src/components/create-service/components/component-installer.vue
 
b/bigtop-manager-ui/src/components/create-service/components/component-installer.vue
index af08100d..78f282d1 100644
--- 
a/bigtop-manager-ui/src/components/create-service/components/component-installer.vue
+++ 
b/bigtop-manager-ui/src/components/create-service/components/component-installer.vue
@@ -75,10 +75,12 @@
     }
   }
 
-  const pollJobDetails = (execFunc: Function, interval: number = 200): void => 
{
+  const pollJobDetails = (execFunc: Function, interval: number = 1000): void 
=> {
     let firstPoll = true
     let firstPollCompleted = false
-    const intervalId = setInterval(async () => {
+    let intervalId: NodeJS.Timeout
+
+    const poll = async () => {
       if (firstPoll) {
         spinning.value = true
         firstPoll = false
@@ -91,7 +93,10 @@
       if (result) {
         clearInterval(intervalId)
       }
-    }, interval)
+    }
+
+    intervalId = setInterval(poll, interval)
+    poll()
   }
 
   const viewLogs = (stage: StageVO, task: TaskVO) => {
diff --git a/bigtop-manager-ui/src/components/job/index.vue 
b/bigtop-manager-ui/src/components/job/index.vue
index 3f2bfe00..842145b8 100644
--- a/bigtop-manager-ui/src/components/job/index.vue
+++ b/bigtop-manager-ui/src/components/job/index.vue
@@ -27,10 +27,12 @@
   import CustomProgress from './custom-progress.vue'
   import type { JobVO, StageVO, StateType, TaskListParams, TaskVO } from 
'@/api/job/types'
   import type { ClusterVO } from '@/api/cluster/types'
+  import type { ListParams } from '@/api/types'
 
   interface BreadcrumbItem {
     id: string
     name: ComputedRef<string> | string
+    pagination: ListParams
   }
   const POLLING_INTERVAL = 3000
   const { t } = useI18n()
@@ -39,17 +41,15 @@
   const breadcrumbs = ref<BreadcrumbItem[]>([
     {
       name: computed(() => t('job.job_list')),
-      id: `clusterId-${clusterInfo.id}`
+      id: `clusterId-${clusterInfo.id}`,
+      pagination: {
+        pageNum: 1,
+        pageSize: 10,
+        sort: 'desc'
+      }
     }
   ])
-  const status = shallowRef<Record<StateType, string>>({
-    Pending: 'installing',
-    Processing: 'processing',
-    Failed: 'failed',
-    Canceled: 'canceled',
-    Successful: 'success'
-  })
-  const apiMap = shallowRef([
+  const apiMap = ref([
     {
       key: 'clusterId',
       api: getJobList
@@ -63,6 +63,13 @@
       api: getTaskList
     }
   ])
+  const status = shallowRef<Record<StateType, string>>({
+    Pending: 'installing',
+    Processing: 'processing',
+    Failed: 'failed',
+    Canceled: 'canceled',
+    Successful: 'success'
+  })
   const logsViewState = reactive<LogViewProps>({
     open: false
   })
@@ -100,13 +107,14 @@
   const apiParams = computed(
     (): TaskListParams =>
       breadcrumbs.value.reduce((pre, val, index) => {
+        const apiMapKey = apiMap.value[index].key
         return Object.assign(pre, {
-          [`${apiMap.value[index].key}`]: 
val.id.replace(`${apiMap.value[index].key}-`, '')
+          [`${apiMapKey}`]: val.id.replace(`${apiMapKey}-`, '')
         })
       }, {} as TaskListParams)
   )
 
-  const { dataSource, loading, filtersParams, paginationProps, onChange } = 
useBaseTable<JobVO | StageVO | TaskVO>({
+  const { dataSource, loading, paginationProps, onChange } = 
useBaseTable<JobVO | StageVO | TaskVO>({
     columns: columns.value
   })
 
@@ -114,14 +122,15 @@
     if (breadcrumb.id !== currBreadcrumb.value?.id) {
       const index = breadcrumbs.value.findIndex((v) => v.id === breadcrumb.id)
       breadcrumbs.value.splice(index + 1, breadcrumbLen.value - index - 1)
+      updataPaginationsInBaseTable(currBreadcrumb.value?.pagination)
       stopPolling()
       startPolling()
     }
   }
 
   const updateBreadcrumbs = (data: BreadcrumbItem) => {
+    // task log open
     if (breadcrumbLen.value + 1 > apiMap.value.length) {
-      // task log open
       logsViewState.open = true
       logsViewState.subTitle = data.name as string
       logsViewState.payLoad = {
@@ -130,18 +139,29 @@
       }
       return
     }
+
     const currId = `${apiMap.value[breadcrumbLen.value].key}-${data.id}`
     const index = breadcrumbs.value.findIndex((v) => v.id === currId)
     if (index === -1) {
-      breadcrumbs.value.push({
-        id: currId,
-        name: data.name as string
-      })
+      const newBreadcrumb = createBreadcrumbItem({ id: currId, name: data.name 
as string })
+      breadcrumbs.value.push(newBreadcrumb)
       stopPolling()
+      updataPaginationsInBaseTable()
       startPolling()
     }
   }
 
+  const createBreadcrumbItem = (item: { id: string; name: string }) => {
+    return {
+      id: item.id,
+      name: item.name,
+      pagination: {
+        pageNum: 1,
+        pageSize: 10
+      }
+    }
+  }
+
   const getListData = async (isFirstCall = false) => {
     if (!paginationProps.value) {
       loading.value = false
@@ -152,7 +172,7 @@
         loading.value = true
       }
       const { api } = apiMap.value[breadcrumbs.value.length - 1]
-      const res = await api(apiParams.value, { ...filtersParams.value, sort: 
'desc' })
+      const res = await api(apiParams.value, currBreadcrumb.value!.pagination)
       dataSource.value = res.content
       paginationProps.value.total = res.total
     } catch (error) {
@@ -163,7 +183,24 @@
     }
   }
 
+  const updataPaginationsInBaseTable = (pagination: ListParams = { pageNum: 1, 
pageSize: 10 }) => {
+    if (paginationProps.value) {
+      paginationProps.value.current = pagination.pageNum
+      paginationProps.value.pageSize = pagination.pageSize
+    }
+  }
+
+  const updataPaginationsInBreadcrumbs = (pagination: ListParams) => {
+    const index = breadcrumbs.value.findIndex((v) => v.id === 
currBreadcrumb.value?.id)
+    if (index != -1) {
+      breadcrumbs.value[index].pagination.pageNum = pagination.pageNum
+      breadcrumbs.value[index].pagination.pageSize = pagination.pageSize
+    }
+  }
+
   const tableChange: TableProps['onChange'] = (...args) => {
+    const { current: pageNum, pageSize } = args[0]
+    updataPaginationsInBreadcrumbs({ pageNum, pageSize })
     onChange(...args)
     stopPolling()
     startPolling()
diff --git a/bigtop-manager-ui/src/components/service-management/configs.vue 
b/bigtop-manager-ui/src/components/service-management/configs.vue
index 8515026b..346e2b89 100644
--- a/bigtop-manager-ui/src/components/service-management/configs.vue
+++ b/bigtop-manager-ui/src/components/service-management/configs.vue
@@ -127,7 +127,6 @@
     await getServiceDetail()
     configs.value = attrs.configs as ServiceConfig[]
     filterConfigs.value = [...configs.value]
-    activeKey.value = filterConfigs.value.map((v) => v.id!)
     debouncedOnSearch.value = debounce(filterConfigurations, 300)
   })
 
diff --git a/bigtop-manager-ui/src/components/user-avatar/index.vue 
b/bigtop-manager-ui/src/components/user-avatar/index.vue
index 8e67e101..403ab283 100644
--- a/bigtop-manager-ui/src/components/user-avatar/index.vue
+++ b/bigtop-manager-ui/src/components/user-avatar/index.vue
@@ -24,12 +24,10 @@
   import { storeToRefs } from 'pinia'
   import router from '@/router'
   import { useI18n } from 'vue-i18n'
-  import { useMenuStore } from '@/store/menu'
 
   const i18n = useI18n()
 
   const userStore = useUserStore()
-  const menuStore = useMenuStore()
   const { userVO } = storeToRefs(userStore)
 
   const logout = async () => {
@@ -37,7 +35,6 @@
     try {
       await userStore.logout()
       message.success(i18n.t('login.logout_success'))
-      menuStore.cleanUpMenu()
       await router.push({ path: '/login' })
     } catch (e) {
       console.warn(e)
diff --git a/bigtop-manager-ui/src/layouts/header.vue 
b/bigtop-manager-ui/src/layouts/header.vue
index 201da620..7b7f18dd 100644
--- a/bigtop-manager-ui/src/layouts/header.vue
+++ b/bigtop-manager-ui/src/layouts/header.vue
@@ -18,38 +18,19 @@
 -->
 
 <script setup lang="ts">
-  import { ref, toRefs } from 'vue'
+  import { ref } from 'vue'
+  import { storeToRefs } from 'pinia'
+  import { useMenuStore } from '@/store/menu/index'
   import SelectLang from '@/components/select-lang/index.vue'
   import UserAvatar from '@/components/user-avatar/index.vue'
   import AiAssistant from '@/components/ai-assistant/index.vue'
 
-  import { RouteRecordRaw } from 'vue-router'
-
-  interface Props {
-    headerSelectedKey: string
-    headerMenus: RouteRecordRaw[]
-  }
-
-  interface Emits {
-    (event: 'onHeaderClick', key: string): void
-  }
-
-  const props = withDefaults(defineProps<Props>(), {
-    headerSelectedKey: '',
-    headerMenus: () => []
-  })
-
-  const { headerSelectedKey, headerMenus } = toRefs(props)
   const githubUrl = import.meta.env.VITE_GITHUB_URL
   const bigtopMangerDocURL = import.meta.env.VITE_BIGTOP_MANAGER_DOC_URL
+  const menuStore = useMenuStore()
   const spaceSize = ref(16)
   const aiAssistantRef = ref<InstanceType<typeof AiAssistant> | null>(null)
-
-  const emits = defineEmits<Emits>()
-
-  const handleHeaderSelect = ({ key }: any) => {
-    emits('onHeaderClick', key)
-  }
+  const { headerSelectedKey, headerMenus } = storeToRefs(menuStore)
 
   const handleCommunication = () => {
     aiAssistantRef.value?.controlVisible()
@@ -62,7 +43,12 @@
       <svg-icon name="bm_logo" />
     </h1>
     <div class="header-menu">
-      <a-menu :selected-keys="[headerSelectedKey]" theme="dark" 
mode="horizontal" @select="handleHeaderSelect">
+      <a-menu
+        :selected-keys="[headerSelectedKey]"
+        theme="dark"
+        mode="horizontal"
+        @select="({ key }) => menuStore.onHeaderClick(key)"
+      >
         <a-menu-item v-for="route of headerMenus" :key="route.path">
           {{ $t(route.meta?.title || '') }}
         </a-menu-item>
diff --git a/bigtop-manager-ui/src/layouts/index.vue 
b/bigtop-manager-ui/src/layouts/index.vue
index 37bb1ed5..11e3604a 100644
--- a/bigtop-manager-ui/src/layouts/index.vue
+++ b/bigtop-manager-ui/src/layouts/index.vue
@@ -18,43 +18,37 @@
 -->
 
 <script setup lang="ts">
-  import { onMounted } from 'vue'
   import LayoutFooter from '@/layouts/footer.vue'
   import LayoutHeader from '@/layouts/header.vue'
   import LayoutSider from '@/layouts/sider.vue'
+
   import { useUserStore } from '@/store/user'
   import { useMenuStore } from '@/store/menu'
-  import { useStackStore } from '@/store/stack'
   import { useInstalledStore } from '@/store/installed'
-  import { storeToRefs } from 'pinia'
+  import { useClusterStore } from '@/store/cluster'
+  import { useStackStore } from '@/store/stack'
+  import { onMounted } from 'vue'
 
   const userStore = useUserStore()
   const menuStore = useMenuStore()
   const installedStore = useInstalledStore()
   const stackStore = useStackStore()
-  const { headerSelectedKey, headerMenus, siderMenuSelectedKey, siderMenus } = 
storeToRefs(menuStore)
+  const clusterStore = useClusterStore()
 
-  onMounted(() => {
+  onMounted(async () => {
+    stackStore.loadStacks()
+    await clusterStore.loadClusters()
     installedStore.getServicesOfInfra()
     userStore.getUserInfo()
-    menuStore.setUpMenu()
-    stackStore.loadStacks()
+    menuStore.setupMenu()
   })
 </script>
 
 <template>
   <a-layout class="layout">
-    <layout-header
-      :header-selected-key="headerSelectedKey"
-      :header-menus="headerMenus"
-      @on-header-click="menuStore.onHeaderClick"
-    />
+    <layout-header />
     <a-layout>
-      <layout-sider
-        :sider-menu-selected-key="siderMenuSelectedKey"
-        :sider-menus="siderMenus"
-        @on-sider-click="menuStore.onSiderClick"
-      />
+      <layout-sider />
       <a-layout class="layout-inner">
         <router-view :key="$route.fullPath" />
         <layout-footer />
diff --git a/bigtop-manager-ui/src/layouts/sider.vue 
b/bigtop-manager-ui/src/layouts/sider.vue
index 09b55480..060a7b06 100644
--- a/bigtop-manager-ui/src/layouts/sider.vue
+++ b/bigtop-manager-ui/src/layouts/sider.vue
@@ -18,100 +18,116 @@
 -->
 
 <script setup lang="ts">
-  import { toRefs, ref, watch } from 'vue'
-  import { useRouter } from 'vue-router'
-  import { RouteExceptions } from '@/enums'
-  import { useMenuStore } from '@/store/menu'
-  import type { MenuItem } from '@/store/menu/types'
+  import { useRouter, useRoute } from 'vue-router'
+  import { useMenuStore } from '@/store/menu/index'
+  import { storeToRefs } from 'pinia'
+  import { computed, ref, watch } from 'vue'
+  import { useClusterStore } from '@/store/cluster'
   import type { ClusterStatusType } from '@/api/cluster/types'
 
-  interface Props {
-    siderMenuSelectedKey: string
-    siderMenus: MenuItem[]
-  }
-
-  const props = withDefaults(defineProps<Props>(), {
-    siderMenuSelectedKey: ''
-  })
-
-  const { siderMenuSelectedKey } = toRefs(props)
   const router = useRouter()
+  const route = useRoute()
   const menuStore = useMenuStore()
-  const menus = ref<MenuItem[]>([])
-  const emits = defineEmits(['onSiderClick'])
-  const clusterStatus = ref<Record<ClusterStatusType, string>>({
-    1: 'success',
-    2: 'error',
-    3: 'warning'
-  })
+  const clusterStore = useClusterStore()
+  const routeParamsLen = ref(0)
+  const openKeys = ref<string[]>(['clusters'])
+  const { siderMenuSelectedKey, headerSelectedKey, siderMenus, 
routePathFromClusters } = storeToRefs(menuStore)
+  const { clusters, clusterCount } = storeToRefs(clusterStore)
+
+  const showCreateClusterBtn = computed(() => headerSelectedKey.value === 
'/cluster-manage')
+  const selectMenuKeyFromClusters = computed(() => 
siderMenuSelectedKey.value.includes(routePathFromClusters.value))
+  const clusterStatus = computed(
+    (): Record<ClusterStatusType, string> => ({
+      1: 'success',
+      2: 'error',
+      3: 'warning'
+    })
+  )
 
   watch(
-    () => props.siderMenus,
-    (val) => {
-      menus.value = []
-      menus.value = val
+    () => route,
+    (newRoute) => {
+      const { params, path, meta } = newRoute
+      routeParamsLen.value = Object.keys(params).length
+      if (path.includes(routePathFromClusters.value) && routeParamsLen.value > 
0 && clusterCount.value > 0) {
+        const cluster = clusters.value.find((v) => `${v.id}` === params.id)
+        cluster && (siderMenuSelectedKey.value = 
`${routePathFromClusters.value}/${cluster.name}/${cluster.id}`)
+      } else {
+        siderMenuSelectedKey.value = meta.activeMenu ?? path
+      }
     },
     {
-      deep: true
+      deep: true,
+      immediate: true
     }
   )
 
-  const toggleActivatedIcon = (menuItem: MenuItem) => {
+  const toggleActivatedIcon = (menuItem: { key: string; icon: string }) => {
     const { key, icon } = menuItem
-    if (menuStore.isDynamicRouteMatched) {
-      return key === RouteExceptions.SPECIAL_ROUTE_PATH ? `${icon}_activated` 
: icon
-    } else {
-      return key === siderMenuSelectedKey.value ? `${icon}_activated` : icon
-    }
+    const matchedRouteFromClusters = selectMenuKeyFromClusters.value && 
routeParamsLen.value > 0
+    return key === siderMenuSelectedKey.value || (matchedRouteFromClusters && 
key === 'clusters')
+      ? `${icon}_activated`
+      : icon
   }
 
   const addCluster = () => {
     router.push({ name: 'CreateCluster' })
   }
-
-  const onSiderClick = ({ key }: any) => {
-    emits('onSiderClick', key)
-  }
 </script>
 
 <template>
   <a-layout-sider class="sider">
-    <a-menu :selected-keys="[siderMenuSelectedKey]" mode="inline" 
@select="onSiderClick">
-      <template v-for="menuItem in menus" :key="menuItem.key">
-        <a-sub-menu
-          v-if="menuItem.children && menuItem.name === 
RouteExceptions.SPECIAL_ROUTE_NAME"
-          :key="menuItem.key"
-        >
+    <a-menu
+      v-model="openKeys"
+      :selected-keys="[siderMenuSelectedKey]"
+      mode="inline"
+      @select="({ key }) => menuStore.onSiderClick(key)"
+    >
+      <template v-for="menuItem in siderMenus" :key="menuItem.path">
+        <a-sub-menu v-if="menuItem.name === 'Clusters'" :key="menuItem.path">
           <template #icon>
-            <svg-icon style="height: 16px; width: 16px" 
:name="toggleActivatedIcon(menuItem)" />
+            <svg-icon
+              style="height: 16px; width: 16px"
+              :name="
+                toggleActivatedIcon({
+                  key: (menuItem.redirect as string) || menuItem.path,
+                  icon: menuItem.meta?.icon || ''
+                })
+              "
+            />
           </template>
           <template #title>
-            <span>{{ $t(menuItem.label) }}</span>
+            <span>{{ $t(menuItem.meta!.title!) }}</span>
           </template>
-          <a-menu-item v-for="child in menuItem.children" :key="child.key">
+          <a-menu-item
+            v-for="child in clusters"
+            :key="`${routePathFromClusters}/${child.name}/${child.id}`"
+            :title="child.displayName"
+          >
             <template #icon>
-              <div
-                style="height: 10px; margin-inline: 7px; display: flex; 
justify-content: center; align-items: flex-end"
-              >
+              <div class="cluster-status">
                 <status-dot :size="8" :color="clusterStatus[child.status as 
ClusterStatusType] as any" />
               </div>
             </template>
             <div>
-              <span>{{ child.label }}</span>
+              <span>{{ child.displayName }}</span>
             </div>
           </a-menu-item>
         </a-sub-menu>
         <template v-else>
-          <a-menu-item :key="menuItem.key">
+          <a-menu-item v-if="!menuItem.meta?.hidden" :key="menuItem.redirect">
             <template #icon>
-              <svg-icon style="height: 16px; width: 16px" 
:name="toggleActivatedIcon(menuItem)" />
+              <svg-icon
+                style="height: 16px; width: 16px"
+                :name="toggleActivatedIcon({ key: menuItem.redirect as string, 
icon: menuItem.meta?.icon || '' })"
+              />
             </template>
-            <span>{{ $t(menuItem.label) }}</span>
+            <span>{{ $t(menuItem.meta!.title!) }}</span>
           </a-menu-item>
         </template>
       </template>
     </a-menu>
-    <div v-show="menuStore.isCreateClusterVisible">
+    <div v-if="showCreateClusterBtn">
       <a-divider />
       <div class="create-option">
         <a-button type="primary" ghost @click="addCluster">
@@ -125,6 +141,13 @@
 </template>
 
 <style scoped lang="scss">
+  .cluster-status {
+    height: 10px;
+    margin-inline: 7px;
+    display: flex;
+    justify-content: center;
+    align-items: flex-end;
+  }
   @mixin reset-sider-menu {
     width: 100%;
     border-radius: 0;
diff --git a/bigtop-manager-ui/src/locales/en_US/cluster.ts 
b/bigtop-manager-ui/src/locales/en_US/cluster.ts
index ad9de32b..11979d82 100644
--- a/bigtop-manager-ui/src/locales/en_US/cluster.ts
+++ b/bigtop-manager-ui/src/locales/en_US/cluster.ts
@@ -29,12 +29,12 @@ export default {
   description: 'Description',
   root_directory: 'Root Directory',
   user_group: 'User Group',
-  config_source: 'Configuration Source',
+  config_source: 'Repository',
   install_dependencies: 'Install Dependencies',
   add_host: 'Add Host',
   edit_host: 'Edit Host',
   view_log: 'View Log',
-  source: 'Source',
+  source: 'Repository',
   show_hosts_resolved: 'Resolved Hostnames',
   cluster_unavailable_message: 'Sorry, you need to create cluster to use this 
feature.'
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/menu.ts 
b/bigtop-manager-ui/src/locales/en_US/menu.ts
index 192158c8..19d20bf2 100644
--- a/bigtop-manager-ui/src/locales/en_US/menu.ts
+++ b/bigtop-manager-ui/src/locales/en_US/menu.ts
@@ -20,7 +20,7 @@
 export default {
   cluster: 'Cluster',
   infra: 'Infrastructure',
-  component: 'Component',
+  stacks: 'Stacks',
   host: 'Host',
   create: 'Create Cluster',
   system: 'System',
diff --git a/bigtop-manager-ui/src/locales/zh_CN/menu.ts 
b/bigtop-manager-ui/src/locales/zh_CN/menu.ts
index 170f822a..e206c3dd 100644
--- a/bigtop-manager-ui/src/locales/zh_CN/menu.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/menu.ts
@@ -20,7 +20,7 @@
 export default {
   cluster: '集群管理',
   infra: '基础服务',
-  component: '组件信息',
+  stacks: '组件栈',
   host: '主机管理',
   create: '添加集群',
   system: '系统管理',
diff --git 
a/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/check-workflow.vue
 
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/check-workflow.vue
index 7e3e4974..d09f541f 100644
--- 
a/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/check-workflow.vue
+++ 
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/components/check-workflow.vue
@@ -77,10 +77,12 @@
     }
   }
 
-  const pollJobDetails = (execFunc: Function, interval: number = 200): void => 
{
+  const pollJobDetails = (execFunc: Function, interval: number = 1000): void 
=> {
     let firstPoll = true
     let firstPollCompleted = false
-    const intervalId = setInterval(async () => {
+    let intervalId: NodeJS.Timeout
+
+    const poll = async () => {
       if (firstPoll) {
         spinning.value = true
         firstPoll = false
@@ -93,7 +95,10 @@
       if (result) {
         clearInterval(intervalId)
       }
-    }, interval)
+    }
+
+    intervalId = setInterval(poll, interval)
+    poll()
   }
 
   const viewLogs = (stage: StageVO, task: TaskVO) => {
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/create.vue 
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/create.vue
index 944f2f98..2c58d5b8 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/create.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/create.vue
@@ -124,7 +124,9 @@
 
   const pollUntilInstalled = (interval: number = 1000): void => {
     let isInitialized = false
-    const intervalId = setInterval(async () => {
+    let intervalId: NodeJS.Timeout
+
+    const poll = async () => {
       if (!isInitialized) {
         stepData.value[current.value] = 
stepData.value[current.value].map((item: HostReq) => ({
           ...item,
@@ -137,7 +139,10 @@
         installing.value = false
         clearInterval(intervalId)
       }
-    }, interval)
+    }
+
+    intervalId = setInterval(poll, interval)
+    poll()
   }
 
   const mergeByHostname = (arr1: any[], arr2: any[]): any[] => {
@@ -167,7 +172,7 @@
   // }
 
   const onSave = () => {
-    menuStore.updateSiderMenu()
+    menuStore.updateSider()
   }
 </script>
 
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue 
b/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
index be4e1a71..03165b57 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/cluster/service.vue
@@ -178,7 +178,7 @@
           </div>
           <div class="header-restart-status">
             <span class="small-gray">{{ `${$t('common.restart')}` }}</span>
-            <status-dot :color="CommonStatus[statusColors[item.status]]" />
+            <status-dot :color="item.restartFlag ? 'error' : 'success'" />
             <span class="small">{{ `${item.restartFlag ? $t('common.required') 
: $t('common.not_required')}` }}</span>
           </div>
         </div>
diff --git a/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue 
b/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue
index 07e54bd9..e020f244 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/components/index.vue
@@ -19,11 +19,12 @@
 <script setup lang="ts">
   import { ref, reactive, computed, onMounted, watchEffect } from 'vue'
   import useBaseTable from '@/composables/use-base-table'
-  import type { TableColumnType } from 'ant-design-vue'
+  import SetSource from 
'@/pages/cluster-manage/cluster/components/set-source.vue'
   import { useI18n } from 'vue-i18n'
   import { useStackStore } from '@/store/stack'
   import { storeToRefs } from 'pinia'
-  import { ServiceVO } from '@/api/service/types'
+  import type { ServiceVO } from '@/api/service/types'
+  import type { TableColumnType } from 'ant-design-vue'
 
   const { t } = useI18n()
   const stackStore = useStackStore()
@@ -34,10 +35,13 @@
     stackSelected: 'Bigtop',
     stackGroup: ['Bigtop', 'Infra', 'Extra']
   })
+
+  const data = ref<ServiceVO[]>([])
+  const setSourceRef = ref<InstanceType<typeof SetSource>>()
+
   const currentStack = computed(() =>
     stacks.value.find((stack) => stack.stackName.toUpperCase() === 
store.stackSelected.toUpperCase())
   )
-  const data = ref<ServiceVO[]>([])
   const columns = computed((): TableColumnType[] => [
     {
       title: '#',
@@ -84,7 +88,7 @@
   })
 
   const handleSetSource = () => {
-    // setSourceRef.value?.handleOpen()
+    setSourceRef.value?.handleOpen()
   }
 
   onMounted(() => {
@@ -95,7 +99,7 @@
 <template>
   <div class="cluster-components">
     <div>
-      <div class="menu-title">{{ $t('menu.component') }}</div>
+      <div class="menu-title">{{ $t('menu.stacks') }}</div>
     </div>
     <div class="cluster-components-header">
       <a-radio-group v-model:value="store.stackSelected" button-style="solid">
@@ -118,6 +122,7 @@
         </template>
       </a-table>
     </div>
+    <set-source ref="setSourceRef" />
   </div>
 </template>
 
diff --git 
a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue 
b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
index 107b0694..18a0d459 100644
--- a/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
+++ b/bigtop-manager-ui/src/pages/cluster-manage/infrastructures/service.vue
@@ -162,7 +162,7 @@
             </div>
             <div class="header-restart-status">
               <span class="small-gray">{{ `${$t('common.restart')}` }}</span>
-              <status-dot :color="CommonStatus[statusColors[item.status]]" />
+              <status-dot :color="item.restartFlag ? 'error' : 'success'" />
               <span class="small">{{ `${item.restartFlag ? 
$t('common.required') : $t('common.not_required')}` }}</span>
             </div>
           </div>
diff --git a/bigtop-manager-ui/src/pages/login/index.vue 
b/bigtop-manager-ui/src/pages/login/index.vue
index 4065f9e4..ab0fc41f 100644
--- a/bigtop-manager-ui/src/pages/login/index.vue
+++ b/bigtop-manager-ui/src/pages/login/index.vue
@@ -18,14 +18,17 @@
 -->
 
 <script setup lang="ts">
+  import { useRouter } from 'vue-router'
   import { reactive, shallowRef } from 'vue'
-  import SelectLang from '@/components/select-lang/index.vue'
   import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
   import { login } from '@/api/login'
-  import router from '@/router'
-  import md5 from 'md5'
   import { message } from 'ant-design-vue'
   import { useI18n } from 'vue-i18n'
+  import md5 from 'md5'
+  import SelectLang from '@/components/select-lang/index.vue'
+
+  const i18n = useI18n()
+  const router = useRouter()
 
   const formRef = shallowRef()
   const submitLoading = shallowRef(false)
@@ -36,7 +39,6 @@
     remember: true
   })
 
-  const i18n = useI18n()
   const submit = async () => {
     submitLoading.value = true
     const hide = message.loading(i18n.t('login.logging_in'), 0)
@@ -52,9 +54,8 @@
       } else {
         sessionStorage.setItem('Token', res.token)
       }
-
       message.success(i18n.t('login.login_success'))
-      await router.push('/')
+      router.push('/')
     } catch (e) {
       console.warn(e)
     } finally {
diff --git a/bigtop-manager-ui/src/plugins/index.ts 
b/bigtop-manager-ui/src/plugins/index.ts
index 7ece92ec..b81d6464 100644
--- a/bigtop-manager-ui/src/plugins/index.ts
+++ b/bigtop-manager-ui/src/plugins/index.ts
@@ -25,15 +25,16 @@ import Antd, { message } from 'ant-design-vue'
 import components from '@/components/common'
 import directives from '@/directives'
 import VueDOMPurifyHTML from 'vue-dompurify-html'
+
 interface PluginOptions {
   antdMessageMaxCount: number
 }
 
 export default {
   install(app: App, options: PluginOptions) {
+    app.use(pinia)
     app.use(Antd)
     app.use(router)
-    app.use(pinia)
     app.use(i18n)
     app.use(directives)
     app.use(components)
diff --git a/bigtop-manager-ui/src/router/guard.ts 
b/bigtop-manager-ui/src/router/guard.ts
index 60639814..6f7030f9 100644
--- a/bigtop-manager-ui/src/router/guard.ts
+++ b/bigtop-manager-ui/src/router/guard.ts
@@ -17,14 +17,24 @@
  * under the License.
  */
 
-import type { Router } from 'vue-router'
-
+import type { NavigationGuardNext, Router } from 'vue-router'
+import { useClusterStore } from '@/store/cluster'
 function setCommonGuard(router: Router) {
-  router.beforeEach(async (_to, _from, next) => {
-    next()
+  router.beforeEach(async (to, from, next) => {
+    if (to.name === 'Clusters' && from.name !== 'Login') {
+      checkClusterSelect(next)
+    } else {
+      next()
+    }
   })
 }
 
+function checkClusterSelect(next: NavigationGuardNext) {
+  const clusterStore = useClusterStore()
+  clusterStore.clusterCount === 0 && clusterStore.loadClusters()
+  clusterStore.clusterCount === 0 && next({ name: 'Default' })
+}
+
 function createRouterGuard(router: Router) {
   /** common guard */
   setCommonGuard(router)
diff --git a/bigtop-manager-ui/src/router/routes/index.ts 
b/bigtop-manager-ui/src/router/routes/index.ts
index 2b1e70df..a68de3ed 100644
--- a/bigtop-manager-ui/src/router/routes/index.ts
+++ b/bigtop-manager-ui/src/router/routes/index.ts
@@ -28,6 +28,7 @@ export const dynamicRoutes: RouteRecordRaw[] = 
mergeRouteModules(dynamicRoutesFi
 
 const routes: RouteRecordRaw[] = [
   {
+    name: 'Login',
     path: '/login',
     component: () => import('@/pages/login/index.vue')
   },
diff --git a/bigtop-manager-ui/src/router/routes/modules/clusters.ts 
b/bigtop-manager-ui/src/router/routes/modules/clusters.ts
index c5ab3450..7e5139b9 100644
--- a/bigtop-manager-ui/src/router/routes/modules/clusters.ts
+++ b/bigtop-manager-ui/src/router/routes/modules/clusters.ts
@@ -24,22 +24,15 @@ import pageView from '@/layouts/index.vue'
 const routes: RouteRecordRaw[] = [
   {
     path: '/cluster-manage',
+    name: 'ClusterManage',
     component: pageView,
-    redirect: RouteExceptions.SPECIAL_ROUTE_PATH,
+    redirect: '/cluster-manage/clusters',
     meta: {
       title: 'menu.cluster'
     },
     children: [
       {
-        name: RouteExceptions.DEFAULT_ROUTE_NAME,
-        path: 'default',
-        component: () => import('@/layouts/default.vue'),
-        meta: {
-          hidden: true
-        }
-      },
-      {
-        name: RouteExceptions.SPECIAL_ROUTE_NAME,
+        name: 'Clusters',
         path: 'clusters',
         redirect: '',
         meta: {
@@ -47,9 +40,17 @@ const routes: RouteRecordRaw[] = [
           title: 'menu.cluster'
         },
         children: [
+          {
+            name: 'Default',
+            path: 'default',
+            component: () => import('@/layouts/default.vue'),
+            meta: {
+              hidden: true
+            }
+          },
           {
             name: 'ClusterDetail',
-            path: RouteExceptions.DYNAMIC_ROUTE_MATCH,
+            path: ':cluster/:id',
             component: () => 
import('@/pages/cluster-manage/cluster/index.vue'),
             meta: {
               hidden: true
@@ -65,7 +66,7 @@ const routes: RouteRecordRaw[] = [
           },
           {
             name: 'CreateService',
-            path: 
`${RouteExceptions.DYNAMIC_ROUTE_MATCH}/create-service/:creationMode?`,
+            path: ':cluster/:id/create-service/:creationMode?',
             component: () => import('@/components/create-service/create.vue'),
             meta: {
               hidden: true
@@ -73,7 +74,7 @@ const routes: RouteRecordRaw[] = [
           },
           {
             name: 'ServiceDetail',
-            path: 
`${RouteExceptions.DYNAMIC_ROUTE_MATCH}/service-detail/:service/:serviceId`,
+            path: ':cluster/:id/service-detail/:service/:serviceId',
             component: () => 
import('@/components/service-management/index.vue'),
             meta: {
               hidden: true
@@ -81,15 +82,7 @@ const routes: RouteRecordRaw[] = [
           },
           {
             name: 'CreateComponent',
-            path: 
`${RouteExceptions.DYNAMIC_ROUTE_MATCH}/create-component/:service/:serviceId/:creationMode?/:type`,
-            component: () => import('@/components/create-service/create.vue'),
-            meta: {
-              hidden: true
-            }
-          },
-          {
-            name: 'CreateComponent',
-            path: 
`${RouteExceptions.DYNAMIC_ROUTE_MATCH}/create-component/:service/:serviceId/:creationMode?/:type`,
+            path: 
':cluster/:id/create-component/:service/:serviceId/:creationMode?/:type',
             component: () => import('@/components/create-service/create.vue'),
             meta: {
               hidden: true
@@ -112,7 +105,7 @@ const routes: RouteRecordRaw[] = [
             component: () => 
import('@/pages/cluster-manage/infrastructures/index.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/infrastructures'
+              activeMenu: '/cluster-manage/infrastructures/list'
             }
           },
           {
@@ -121,7 +114,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('@/components/create-service/create.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/infrastructures'
+              activeMenu: '/cluster-manage/infrastructures/list'
             }
           },
           {
@@ -130,16 +123,7 @@ const routes: RouteRecordRaw[] = [
             component: () => 
import('@/components/service-management/index.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/infrastructures'
-            }
-          },
-          {
-            name: 'CreateInfraComponent',
-            path: 
'/create-infra-service/create-infra-component/:id/:cluster/:service/:serviceId/:creationMode/:type',
-            component: () => import('@/components/create-service/create.vue'),
-            meta: {
-              hidden: true,
-              activeMenu: '/cluster-manage/infrastructures'
+              activeMenu: '/cluster-manage/infrastructures/list'
             }
           },
           {
@@ -148,7 +132,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('@/components/create-service/create.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/infrastructures'
+              activeMenu: '/cluster-manage/infrastructures/list'
             }
           }
         ]
@@ -156,11 +140,22 @@ const routes: RouteRecordRaw[] = [
       {
         name: 'Components',
         path: 'components',
-        component: () => import('@/pages/cluster-manage/components/index.vue'),
+        redirect: '/cluster-manage/components/list',
         meta: {
           icon: 'components',
-          title: 'menu.component'
-        }
+          title: 'menu.stacks'
+        },
+        children: [
+          {
+            name: 'ComponentList',
+            path: 'list',
+            component: () => 
import('@/pages/cluster-manage/components/index.vue'),
+            meta: {
+              hidden: true,
+              activeMenu: '/cluster-manage/components/list'
+            }
+          }
+        ]
       },
       {
         name: 'Hosts',
@@ -177,7 +172,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('@/pages/cluster-manage/hosts/index.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/hosts'
+              activeMenu: '/cluster-manage/hosts/list'
             }
           },
           {
@@ -186,7 +181,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('@/pages/cluster-manage/hosts/create.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/hosts'
+              activeMenu: '/cluster-manage/hosts/list'
             }
           },
           {
@@ -195,7 +190,7 @@ const routes: RouteRecordRaw[] = [
             component: () => import('@/pages/cluster-manage/hosts/detail.vue'),
             meta: {
               hidden: true,
-              activeMenu: '/cluster-manage/hosts'
+              activeMenu: '/cluster-manage/hosts/list'
             }
           }
         ]
diff --git a/bigtop-manager-ui/src/router/routes/modules/system.ts 
b/bigtop-manager-ui/src/router/routes/modules/system.ts
index 0bfaf729..5a591a13 100644
--- a/bigtop-manager-ui/src/router/routes/modules/system.ts
+++ b/bigtop-manager-ui/src/router/routes/modules/system.ts
@@ -22,7 +22,8 @@ import pageView from '@/layouts/index.vue'
 
 const routes: RouteRecordRaw[] = [
   {
-    path: '/system-manage/',
+    name: 'SystemManage',
+    path: '/system-manage',
     component: pageView,
     redirect: '/system-manage/llm-config',
     meta: {
@@ -32,11 +33,22 @@ const routes: RouteRecordRaw[] = [
       {
         name: 'LlmConfig',
         path: 'llm-config',
-        component: () => import('@/pages/system-manage/llm-config/index.vue'),
+        redirect: '/system-manage/llm-config/list',
         meta: {
           icon: 'llm_config',
           title: 'menu.llm_config'
-        }
+        },
+        children: [
+          {
+            name: 'LlmConfigList',
+            path: 'list',
+            component: () => 
import('@/pages/system-manage/llm-config/index.vue'),
+            meta: {
+              hidden: true,
+              activeMenu: '/system-manage/llm-config/list'
+            }
+          }
+        ]
       }
     ]
   }
diff --git a/bigtop-manager-ui/src/store/cluster/index.ts 
b/bigtop-manager-ui/src/store/cluster/index.ts
index 4380c728..b9a77030 100644
--- a/bigtop-manager-ui/src/store/cluster/index.ts
+++ b/bigtop-manager-ui/src/store/cluster/index.ts
@@ -35,6 +35,7 @@ export const useClusterStore = defineStore(
     const loading = ref(false)
     const currCluster = ref<ClusterVO>({})
     const clusterId = computed(() => (route.params.id as string) || undefined)
+    const clusterCount = computed(() => clusters.value.length)
 
     watch(
       () => clusters.value,
@@ -60,10 +61,9 @@ export const useClusterStore = defineStore(
       try {
         loading.value = true
         currCluster.value = await getCluster(parseInt(clusterId.value))
-        currCluster.value.id != undefined && (await 
serviceStore.getServices(currCluster.value.id))
+        await serviceStore.getServices(currCluster.value.id!)
       } catch (error) {
         currCluster.value = {}
-        clusters.value = []
         console.log('error :>> ', error)
       } finally {
         loading.value = false
@@ -71,13 +71,19 @@ export const useClusterStore = defineStore(
     }
 
     const loadClusters = async () => {
-      clusters.value = await getClusterList()
+      try {
+        clusters.value = await getClusterList()
+      } catch (error) {
+        clusters.value.length = 0
+        console.log('error :>> ', error)
+      }
     }
 
     return {
       clusters,
       loading,
       currCluster,
+      clusterCount,
       addCluster,
       delCluster,
       loadClusters,
diff --git a/bigtop-manager-ui/src/store/menu/index.ts 
b/bigtop-manager-ui/src/store/menu/index.ts
index eec8d342..ab265c16 100644
--- a/bigtop-manager-ui/src/store/menu/index.ts
+++ b/bigtop-manager-ui/src/store/menu/index.ts
@@ -17,15 +17,11 @@
  * under the License.
  */
 
-import { computed, ref, watchEffect } from 'vue'
-import { useRoute, useRouter } from 'vue-router'
+import { nextTick, ref, shallowRef } from 'vue'
+import { RouteRecordRaw, useRoute, useRouter } from 'vue-router'
 import { dynamicRoutes as dr } from '@/router/routes/index'
-import { defineStore, storeToRefs } from 'pinia'
-import { useClusterStore } from '@/store/cluster/index'
-import { RouteExceptions } from '@/enums'
-import { findActivePath } from './helper'
-import type { MenuItem } from './types'
-import cloneDeep from 'lodash/cloneDeep'
+import { defineStore } from 'pinia'
+import { useClusterStore } from '../cluster'
 
 export const useMenuStore = defineStore(
   'menu',
@@ -33,136 +29,61 @@ export const useMenuStore = defineStore(
     const router = useRouter()
     const route = useRoute()
     const clusterStore = useClusterStore()
-    const { clusters } = storeToRefs(clusterStore)
-
-    const baseRoutesMap = ref<Map<string, MenuItem[]>>(new Map())
-    const headerMenus = ref(dr.filter((v) => v.meta?.title && !v.meta.hidden))
-    const headerSelectedKey = ref(headerMenus.value[0].path)
-
-    const hasCluster = computed(() => clusters.value.length > 0)
-    const isCreateClusterVisible = computed(() => 
RouteExceptions.SPECIAL_ROUTE_PATH.includes(route.matched[0].path))
-    const isDynamicRouteMatched = computed(() => {
-      const path = route.matched.at(-1)?.path
-      return path?.includes(RouteExceptions.DYNAMIC_ROUTE_MATCH)
-    })
-
-    const updateSiderItemByClusters = (formatSider: MenuItem[]) => {
-      formatSider.forEach((item) => {
-        if (item.name == RouteExceptions.SPECIAL_ROUTE_NAME) {
-          item.children = clusters.value.map((v) => {
-            return {
-              icon: '',
-              key: `${item.key}/${v.displayName}/${v.id}`,
-              label: v.displayName || '',
-              title: v.displayName || '',
-              activeMenu: item.key,
-              status: v.status || 3
-            }
-          })
-        }
-      })
+    const baseRoutesMap = ref<Record<string, RouteRecordRaw>>({})
+    const headerMenus = ref<RouteRecordRaw[]>([])
+    const siderMenus = ref<RouteRecordRaw[]>([])
+    const headerSelectedKey = ref()
+    const siderMenuSelectedKey = ref()
+    const routePathFromClusters = shallowRef('/cluster-manage/clusters')
+
+    const buildMenuMap = () => {
+      baseRoutesMap.value = dr.reduce((buildRes, { path, name, meta, children 
}) => {
+        !meta?.hidden && (buildRes[path] = { path, name, meta, children })
+        return buildRes
+      }, {})
     }
 
-    const siderMenus = computed((): MenuItem[] => {
-      const siderMenuTemplate = 
baseRoutesMap.value.get(headerSelectedKey.value) || []
-      const formatSider = cloneDeep(siderMenuTemplate)
-      if (hasCluster.value) {
-        updateSiderItemByClusters(formatSider as MenuItem[])
-      }
-      return formatSider
-    })
-
-    const siderMenuSelectedKey = ref(findActivePath(siderMenus.value[0]))
-
-    const redirectRouterToDefault = () => {
-      siderMenuSelectedKey.value = ''
-      router.replace({ name: RouteExceptions.DEFAULT_ROUTE_NAME })
+    const setupHeader = () => {
+      headerSelectedKey.value = route.matched[0].path ?? '/cluster-manage'
+      headerMenus.value = Object.values(baseRoutesMap.value)
+      siderMenus.value = baseRoutesMap.value[headerSelectedKey.value].children 
|| []
     }
 
-    watchEffect(() => {
-      // resolve highlight menu
-      let activeMenu = route.meta.activeMenu || route.path
-      if (route.name === 'CreateService') {
-        activeMenu = route.path.split('/').slice(0, -2).join('/')
-      }
-      if (route.name === 'ServiceDetail') {
-        activeMenu = route.path.split('/').slice(0, -3).join('/')
-      }
-      if (route.name === 'CreateComponent') {
-        activeMenu = route.path.split('/').slice(0, -5).join('/')
-      }
-      const matchedNames = [RouteExceptions.SPECIAL_ROUTE_NAME, 
RouteExceptions.DEFAULT_ROUTE_NAME] as string[]
-      headerSelectedKey.value = route.matched[0].path
-
-      if (matchedNames.includes(route.name as string)) {
-        if (hasCluster.value && isDynamicRouteMatched.value) {
-          siderMenuSelectedKey.value = findActivePath(siderMenus.value[0])
-        } else {
-          siderMenuSelectedKey.value = activeMenu
-          redirectRouterToDefault()
-        }
+    const setupSider = () => {
+      siderMenus.value = baseRoutesMap.value[headerSelectedKey.value].children 
|| []
+      if (siderMenus.value[0].redirect) {
+        siderMenuSelectedKey.value = siderMenus.value[0].redirect
       } else {
-        if (!activeMenu || (isDynamicRouteMatched.value && !hasCluster.value)) 
{
-          redirectRouterToDefault()
-          return
+        if (clusterStore.clusters[0]) {
+          const { id, name } = clusterStore.clusters[0]
+          onSiderClick(`${routePathFromClusters.value}/${name}/${id}`)
+        } else {
+          onSiderClick(`${routePathFromClusters.value}/default`)
         }
-        siderMenuSelectedKey.value = activeMenu
       }
-    })
-
-    const formatRouteToMenu = (tree: any[], upPath = ''): MenuItem[] => {
-      return tree
-        .filter(({ meta }) => !meta?.hidden)
-        .map(({ path, name, meta, children }) => {
-          const fullPath = `${upPath}${path}`
-          return {
-            icon: meta.icon,
-            key: fullPath,
-            label: meta.title || '',
-            title: meta.title || '',
-            name: name || '',
-            children: children && children.length > 0 ? 
formatRouteToMenu(children, fullPath) : []
-          }
-        })
-    }
-
-    const setBaseRoutesMap = () => {
-      dr.forEach((route) => {
-        if (!route.meta?.hidden) {
-          const exist = baseRoutesMap.value.get(route.path) || []
-          baseRoutesMap.value.set(route.path, [...exist, 
...formatRouteToMenu(route.children || [], route.path)])
-        }
-      })
     }
 
     const onHeaderClick = (key: string) => {
       headerSelectedKey.value = key
-      if (!hasCluster.value && route.name == 
RouteExceptions.SPECIAL_ROUTE_NAME) {
-        return
-      }
-      siderMenuSelectedKey.value = findActivePath(siderMenus.value[0])
-      siderMenuSelectedKey.value && router.push(siderMenuSelectedKey.value)
+      router.push({ path: key })
+      setupSider()
     }
 
     const onSiderClick = (key: string) => {
-      siderMenuSelectedKey.value = key
-      router.push(key)
-    }
-
-    const updateSiderMenu = async (isDelete = false) => {
-      isDelete ? await clusterStore.delCluster() : await 
clusterStore.addCluster()
-      siderMenuSelectedKey.value = siderMenus.value[0].children?.at(-1)?.key 
|| RouteExceptions.SPECIAL_ROUTE_PATH
-      router.push(siderMenuSelectedKey.value)
+      router.push({ path: key })
     }
 
-    const setUpMenu = async () => {
-      setBaseRoutesMap()
+    const updateSider = async () => {
       await clusterStore.loadClusters()
-      updateSiderMenu()
+      const { id, name } = clusterStore.clusters[clusterStore.clusterCount - 1]
+      await nextTick()
+      onSiderClick(`${routePathFromClusters.value}/${name}/${id}`)
     }
 
-    const cleanUpMenu = () => {
-      baseRoutesMap.value.clear()
+    const setupMenu = async () => {
+      buildMenuMap()
+      setupHeader()
+      setupSider()
     }
 
     return {
@@ -170,20 +91,17 @@ export const useMenuStore = defineStore(
       siderMenus,
       headerSelectedKey,
       siderMenuSelectedKey,
-      isCreateClusterVisible,
-      isDynamicRouteMatched,
-      setUpMenu,
-      cleanUpMenu,
-      setBaseRoutesMap,
+      routePathFromClusters,
+      setupMenu,
+      updateSider,
       onHeaderClick,
-      onSiderClick,
-      updateSiderMenu
+      onSiderClick
     }
   },
   {
     persist: {
-      storage: localStorage,
-      paths: ['siderMenus']
+      storage: sessionStorage,
+      paths: ['headerMenus', 'siderMenus']
     }
   }
 )
diff --git a/bigtop-manager-ui/src/utils/router-util.ts 
b/bigtop-manager-ui/src/utils/router-util.ts
index 150cfcd1..661332ef 100644
--- a/bigtop-manager-ui/src/utils/router-util.ts
+++ b/bigtop-manager-ui/src/utils/router-util.ts
@@ -29,30 +29,5 @@ export const mergeRouteModules = (routeModules: 
Record<string, unknown>): RouteR
     mergedRoutes.push(...moduleRoutes)
   }
 
-  return getMergeRoutes(mergedRoutes)
-}
-
-const getMergeRoutes = <T extends RouteRecordRaw>(routes: T[]): T[] => {
-  const mergedMap = new Map()
-  routes.forEach((route: T) => {
-    const firstLevelPath = route.path.split('/')[1]
-    if (!mergedMap.has(firstLevelPath)) {
-      mergedMap.set(firstLevelPath, {
-        path: `/${firstLevelPath}/`,
-        component: route.component,
-        meta: route.meta,
-        children: [],
-        redirect: route.redirect || undefined
-      })
-    }
-    if (route.children) {
-      const existingRoute = mergedMap.get(firstLevelPath)
-      route.children.forEach((child) => {
-        if (!existingRoute.children.some((c: T) => c.path === child.path)) {
-          existingRoute.children.push(child)
-        }
-      })
-    }
-  })
-  return Array.from(mergedMap.values())
+  return mergedRoutes
 }

Reply via email to