Browse Source

feat(namespace): implement namespace loading and filtering in NamespaceTabs and Nodes components

0xJacky 4 days ago
parent
commit
ccad4d1907

+ 37 - 4
app/src/components/NamespaceTabs/NamespaceTabs.vue

@@ -1,15 +1,48 @@
 <script setup lang="ts">
 import type { Namespace } from '@/api/namespace'
 import { message } from 'ant-design-vue'
+import namespaceApi from '@/api/namespace'
 import nodeApi from '@/api/node'
 import { useNodeAvailabilityStore } from '@/pinia/moudule/nodeAvailability'
 
-const props = defineProps<{
-  namespaces: Namespace[]
+defineProps<{
+  hideNodeInfo?: boolean
 }>()
 
 const modelValue = defineModel<string | number>('activeKey')
 const nodeStore = useNodeAvailabilityStore()
+const namespaces = ref<Namespace[]>([])
+
+// Load all namespaces on mount (handle pagination)
+async function loadAllNamespaces() {
+  const allNamespaces: Namespace[] = []
+  let currentPage = 1
+  let hasMore = true
+
+  while (hasMore) {
+    try {
+      const response = await namespaceApi.getList({ page: currentPage })
+      allNamespaces.push(...response.data)
+
+      if (response.pagination && response.pagination.current_page < response.pagination.total_pages) {
+        currentPage++
+      }
+      else {
+        hasMore = false
+      }
+    }
+    catch (error) {
+      console.error('Failed to load namespaces:', error)
+      hasMore = false
+    }
+  }
+
+  namespaces.value = allNamespaces
+}
+
+onMounted(() => {
+  loadAllNamespaces()
+})
 
 const loading = ref({
   reload: false,
@@ -20,7 +53,7 @@ const loading = ref({
 const currentNamespace = computed(() => {
   if (!modelValue.value || modelValue.value === 0)
     return null
-  return props.namespaces.find(g => g.id === Number(modelValue.value))
+  return namespaces.value.find(g => g.id === Number(modelValue.value))
 })
 
 // Get the list of nodes in the current group
@@ -86,7 +119,7 @@ async function handleRestartNginx() {
 
     <!-- Display node information -->
     <ACard
-      v-if="modelValue && modelValue !== 0 && syncNodes.length > 0"
+      v-if="!hideNodeInfo && modelValue && modelValue !== 0 && syncNodes.length > 0"
       :title="$gettext('Sync Nodes')"
       size="small"
       class="mb-4"

+ 53 - 1
app/src/views/dashboard/Nodes.vue

@@ -1,11 +1,14 @@
 <script setup lang="ts">
 import type { Ref } from 'vue'
+import type { Namespace } from '@/api/namespace'
 import type { Node } from '@/api/node'
 import Icon, { LinkOutlined, ThunderboltOutlined } from '@ant-design/icons-vue'
 import analytic from '@/api/analytic'
+import namespaceApi from '@/api/namespace'
 import nodeApi from '@/api/node'
 import logo from '@/assets/img/logo.png'
 import pulse from '@/assets/svg/pulse.svg?component'
+import NamespaceTabs from '@/components/NamespaceTabs'
 import { formatDateTime } from '@/lib/helper'
 import { useSettingsStore } from '@/pinia'
 import { useNodeAvailabilityStore } from '@/pinia/moudule/nodeAvailability'
@@ -14,6 +17,7 @@ import NodeAnalyticItem from './components/NodeAnalyticItem.vue'
 
 const nodeStore = useNodeAvailabilityStore()
 const data = ref([]) as Ref<Node[]>
+const activeNamespaceKey = ref<string | number>(0)
 
 const nodeMap = computed(() => {
   const o = {} as Record<number, Node>
@@ -25,10 +29,56 @@ const nodeMap = computed(() => {
   return o
 })
 
+// Get namespaces to filter nodes
+const namespaces = ref([]) as Ref<Namespace[]>
+
+// Filtered nodes based on active namespace
+const filteredNodes = computed(() => {
+  if (activeNamespaceKey.value === 0) {
+    return data.value
+  }
+
+  const currentNamespace = namespaces.value.find(ns => ns.id === Number(activeNamespaceKey.value))
+  if (!currentNamespace || !currentNamespace.sync_node_ids) {
+    return []
+  }
+
+  return data.value.filter(node => currentNamespace.sync_node_ids.includes(node.id))
+})
+
+// Load all namespaces (handle pagination)
+async function loadAllNamespaces() {
+  const allNamespaces: Namespace[] = []
+  let currentPage = 1
+  let hasMore = true
+
+  while (hasMore) {
+    try {
+      const response = await namespaceApi.getList({ page: currentPage })
+      allNamespaces.push(...response.data)
+
+      if (response.pagination && response.pagination.current_page < response.pagination.total_pages) {
+        currentPage++
+      }
+      else {
+        hasMore = false
+      }
+    }
+    catch (error) {
+      console.error('Failed to load namespaces:', error)
+      hasMore = false
+    }
+  }
+
+  namespaces.value = allNamespaces
+}
+
 onMounted(() => {
   nodeApi.getList({ enabled: true }).then(r => {
     data.value.push(...r.data)
   })
+
+  loadAllNamespaces()
 })
 
 onMounted(() => {
@@ -80,9 +130,11 @@ const visible = computed(() => {
     :title="$gettext('Nodes')"
     :bordered="false"
   >
+    <NamespaceTabs v-model:active-key="activeNamespaceKey" class="mb-4" hide-node-info />
+
     <AList
       item-layout="horizontal"
-      :data-source="data"
+      :data-source="filteredNodes"
       class="env-list"
     >
       <template #renderItem="{ item }">

+ 1 - 23
app/src/views/site/site_list/SiteList.vue

@@ -1,8 +1,6 @@
 <script setup lang="tsx">
-import type { Namespace } from '@/api/namespace'
 import { StdCurd } from '@uozi-admin/curd'
 import { message } from 'ant-design-vue'
-import namespace from '@/api/namespace'
 import site from '@/api/site'
 import NamespaceTabs from '@/components/NamespaceTabs'
 import { ConfigStatus } from '@/constants'
@@ -17,31 +15,11 @@ const curd = ref()
 const inspectConfig = ref()
 
 const namespaceId = ref(Number.parseInt(route.query.namespace_id as string) || 0)
-const namespaces = ref<Namespace[]>([])
 
 watch(route, () => {
   inspectConfig.value?.test()
 })
 
-onMounted(async () => {
-  let page = 1
-  while (true) {
-    try {
-      const { data, pagination } = await namespace.getList({ page })
-      if (!data || !pagination)
-        return
-      namespaces.value.push(...data)
-      if (data.length < pagination?.per_page) {
-        return
-      }
-      page++
-    }
-    catch {
-      return
-    }
-  }
-})
-
 function destroy(site_name: string) {
   site.deleteItem(site_name).then(() => {
     curd.value.refresh()
@@ -97,7 +75,7 @@ function handle_click_duplicate(name: string) {
       </template>
       <template #beforeCardBody>
         <InspectConfig ref="inspectConfig" />
-        <NamespaceTabs v-model:active-key="namespaceId" :namespaces="namespaces" />
+        <NamespaceTabs v-model:active-key="namespaceId" />
       </template>
       <template #afterActions="{ record }">
         <AButton

+ 1 - 23
app/src/views/stream/StreamList.vue

@@ -1,8 +1,6 @@
 <script setup lang="tsx">
-import type { Namespace } from '@/api/namespace'
 import { StdCurd } from '@uozi-admin/curd'
 import { message } from 'ant-design-vue'
-import namespace from '@/api/namespace'
 import stream from '@/api/stream'
 import NamespaceTabs from '@/components/NamespaceTabs'
 import InspectConfig from '@/views/config/InspectConfig.vue'
@@ -16,26 +14,6 @@ const curd = ref()
 const inspect_config = ref()
 
 const namespaceId = ref(Number.parseInt(route.query.namespace_id as string) || 0)
-const namespaces = ref<Namespace[]>([])
-
-onMounted(async () => {
-  let page = 1
-  while (true) {
-    try {
-      const { data, pagination } = await namespace.getList({ page })
-      if (!data || !pagination)
-        return
-      namespaces.value.push(...data)
-      if (data.length < pagination?.per_page) {
-        return
-      }
-      page++
-    }
-    catch {
-      return
-    }
-  }
-})
 
 watch(route, () => {
   inspect_config.value?.test()
@@ -106,7 +84,7 @@ function handleAddStream() {
 
       <template #beforeCardBody>
         <InspectConfig ref="inspect_config" />
-        <NamespaceTabs v-model:active-key="namespaceId" :namespaces="namespaces" />
+        <NamespaceTabs v-model:active-key="namespaceId" />
       </template>
 
       <template #afterActions="{ record }">