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

rickyma pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-uniffle.git


The following commit(s) were added to refs/heads/master by this push:
     new c48af78e9 [#1401] feat(dashboard): Shuffle server page list sorting 
function (#1868)
c48af78e9 is described below

commit c48af78e98ad4c49be403b157a77cca75cbe06fe
Author: yl09099 <33595968+yl09...@users.noreply.github.com>
AuthorDate: Tue Jul 9 16:22:32 2024 +0800

    [#1401] feat(dashboard): Shuffle server page list sorting function (#1868)
    
    ### What changes were proposed in this pull request?
    
    On the Shuffle Server list page, you can sort the Shuffle Server by column 
to facilitate you to view information about the Shuffle Server.You can sort IP, 
used memory, pre-allocated memory, available memory, heartbeat time, and so on.
    
![image](https://github.com/apache/incubator-uniffle/assets/33595968/a9a04744-29e3-40a7-b3f1-a2586fc8dad0)
    
    ### Why are the changes needed?
    
    Fix: #1401
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes.
    
    ### How was this patch tested?
    
    Page view.
---
 .../src/main/webapp/src/pages/ApplicationPage.vue  |  54 ++++++-
 .../main/webapp/src/pages/ShuffleServerPage.vue    |   1 +
 .../src/pages/serverstatus/ActiveNodeListPage.vue  | 106 -------------
 .../serverstatus/DecommissionednodeListPage.vue    | 106 -------------
 .../serverstatus/DecommissioningNodeListPage.vue   | 106 -------------
 .../src/pages/serverstatus/ExcludeNodeList.vue     |  20 ++-
 .../webapp/src/pages/serverstatus/LostNodeList.vue | 107 -------------
 .../webapp/src/pages/serverstatus/NodeListPage.vue | 169 +++++++++++++++++++++
 .../pages/serverstatus/UnhealthyNodeListPage.vue   | 106 -------------
 dashboard/src/main/webapp/src/router/index.js      |  16 +-
 10 files changed, 239 insertions(+), 552 deletions(-)

diff --git a/dashboard/src/main/webapp/src/pages/ApplicationPage.vue 
b/dashboard/src/main/webapp/src/pages/ApplicationPage.vue
index fdaa009ab..66d0f814e 100644
--- a/dashboard/src/main/webapp/src/pages/ApplicationPage.vue
+++ b/dashboard/src/main/webapp/src/pages/ApplicationPage.vue
@@ -32,28 +32,42 @@
     <el-divider />
     <el-tag>User App ranking</el-tag>
     <div>
-      <el-table :data="pageData.userAppCount" height="250" style="width: 100%">
-        <el-table-column prop="userName" label="UserName" min-width="180" />
-        <el-table-column prop="appNum" label="Totality" min-width="180" />
+      <el-table
+        :data="pageData.userAppCount"
+        height="250"
+        style="width: 100%"
+        :default-sort="sortAppCollect"
+        @sort-change="sortAppCollectChangeEvent"
+      >
+        <el-table-column prop="userName" label="UserName" min-width="180" 
sortable fixed />
+        <el-table-column prop="appNum" label="Totality" min-width="180" 
sortable />
       </el-table>
     </div>
     <el-divider />
     <el-tag>Apps</el-tag>
     <div>
-      <el-table :data="pageData.appInfoData" height="250" style="width: 100%">
-        <el-table-column prop="appId" label="AppId" min-width="180" />
-        <el-table-column prop="userName" label="UserName" min-width="180" />
-        <el-table-column
+      <el-table
+        :data="pageData.appInfoData"
+        height="250"
+        style="width: 100%"
+        :default-sort="sortApp"
+        @sort-change="sortAppChangeEvent"
+      >
+        <el-table-column prop="appId" label="AppId" min-width="180" sortable 
fixed />
+        <el-table-column prop="userName" label="UserName" min-width="180" 
sortable />
+        <el-table-columnsortable
           prop="registrationTime"
           label="Registration Time"
           min-width="180"
           :formatter="dateFormatter"
+          sortable
         />
         <el-table-column
           prop="updateTime"
           label="Update Time"
           min-width="180"
           :formatter="dateFormatter"
+          sortable
         />
       </el-table>
     </div>
@@ -107,7 +121,31 @@ export default {
         getAppTotalPage()
       }
     })
-    return { pageData, dateFormatter }
+
+    const sortAppCollect = reactive({})
+    const sortAppCollectChangeEvent = (sortInfo) => {
+      for (const sortColumnKey in sortAppCollect) {
+        delete sortAppCollect[sortColumnKey]
+      }
+      sortAppCollect[sortInfo.prop] = sortInfo.order
+    }
+
+    const sortApp = reactive({})
+    const sortAppChangeEvent = (sortInfo) => {
+      for (const sortColumnKey in sortApp) {
+        delete sortApp[sortColumnKey]
+      }
+      sortApp[sortInfo.prop] = sortInfo.order
+    }
+
+    return {
+      pageData,
+      sortAppCollect,
+      sortAppCollectChangeEvent,
+      sortApp,
+      sortAppChangeEvent,
+      dateFormatter
+    }
   }
 }
 </script>
diff --git a/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue 
b/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
index a58b7995f..6aa63615b 100644
--- a/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
+++ b/dashboard/src/main/webapp/src/pages/ShuffleServerPage.vue
@@ -121,6 +121,7 @@ export default {
         UNHEALTHY: 0
       }
     })
+
     const currentServerStore = useCurrentServerStore()
 
     async function getShufflegetStatusTotalPage() {
diff --git 
a/dashboard/src/main/webapp/src/pages/serverstatus/ActiveNodeListPage.vue 
b/dashboard/src/main/webapp/src/pages/serverstatus/ActiveNodeListPage.vue
deleted file mode 100644
index 8b08c13d2..000000000
--- a/dashboard/src/main/webapp/src/pages/serverstatus/ActiveNodeListPage.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<template>
-  <div>
-    <el-table :data="pageData.tableData" height="550" style="width: 100%">
-      <el-table-column prop="id" label="Id" min-width="140" />
-      <el-table-column prop="ip" label="IP" min-width="80" />
-      <el-table-column prop="grpcPort" label="GrpcPort" min-width="80" />
-      <el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
-      <el-table-column prop="usedMemory" label="UsedMem" min-width="80" 
:formatter="memFormatter" />
-      <el-table-column
-        prop="preAllocatedMemory"
-        label="PreAllocatedMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column
-        prop="availableMemory"
-        label="AvailableMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column prop="eventNumInFlush" label="FlushNum" min-width="80" 
/>
-      <el-table-column prop="status" label="Status" min-width="80" />
-      <el-table-column
-        prop="registrationTime"
-        label="RegistrationTime"
-        min-width="80"
-        :formatter="dateFormatter"
-      />
-      <el-table-column
-        prop="timestamp"
-        label="HeartbeatTime"
-        min-width="80"
-        :formatter="dateFormatter"
-      />
-      <el-table-column prop="tags" label="Tags" min-width="80" />
-    </el-table>
-  </div>
-</template>
-<script>
-import { onMounted, reactive } from 'vue'
-import { getShuffleActiveNodes } from '@/api/api'
-import { memFormatter, dateFormatter } from '@/utils/common'
-import { useCurrentServerStore } from '@/store/useCurrentServerStore'
-
-export default {
-  setup() {
-    const pageData = reactive({
-      tableData: [
-        {
-          id: '',
-          ip: '',
-          grpcPort: 0,
-          nettyPort: 0,
-          usedMemory: 0,
-          preAllocatedMemory: 0,
-          availableMemory: 0,
-          eventNumInFlush: 0,
-          tags: '',
-          status: '',
-          registrationTime: '',
-          timestamp: ''
-        }
-      ]
-    })
-    const currentServerStore = useCurrentServerStore()
-
-    async function getShuffleActiveNodesPage() {
-      const res = await getShuffleActiveNodes()
-      pageData.tableData = res.data.data
-    }
-
-    // The system obtains data from global variables and requests the 
interface to obtain new data after data changes.
-    currentServerStore.$subscribe((mutable, state) => {
-      if (state.currentServer) {
-        getShuffleActiveNodesPage()
-      }
-    })
-
-    onMounted(() => {
-      // If the coordinator address to request is not found in the global 
variable, the request is not initiated.
-      if (currentServerStore.currentServer) {
-        getShuffleActiveNodesPage()
-      }
-    })
-
-    return { pageData, memFormatter, dateFormatter }
-  }
-}
-</script>
diff --git 
a/dashboard/src/main/webapp/src/pages/serverstatus/DecommissionednodeListPage.vue
 
b/dashboard/src/main/webapp/src/pages/serverstatus/DecommissionednodeListPage.vue
deleted file mode 100644
index dae4a6c18..000000000
--- 
a/dashboard/src/main/webapp/src/pages/serverstatus/DecommissionednodeListPage.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<template>
-  <div>
-    <el-table :data="pageData.tableData" height="550" style="width: 100%">
-      <el-table-column prop="id" label="Id" min-width="140" />
-      <el-table-column prop="ip" label="IP" min-width="80" />
-      <el-table-column prop="grpcPort" label="Port" min-width="80" />
-      <el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
-      <el-table-column prop="usedMemory" label="UsedMem" min-width="80" 
:formatter="memFormatter" />
-      <el-table-column
-        prop="preAllocatedMemory"
-        label="PreAllocatedMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column
-        prop="availableMemory"
-        label="AvailableMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column prop="eventNumInFlush" label="FlushNum" min-width="80" 
/>
-      <el-table-column prop="status" label="Status" min-width="80" />
-      <el-table-column
-          prop="registrationTime"
-          label="RegistrationTime"
-          min-width="80"
-          :formatter="dateFormatter"
-      />
-      <el-table-column
-        prop="timestamp"
-        label="HeartbeatTime"
-        min-width="80"
-        :formatter="dateFormatter"
-      />
-      <el-table-column prop="tags" label="Tags" min-width="80" />
-    </el-table>
-  </div>
-</template>
-<script>
-import { onMounted, reactive } from 'vue'
-import { getShuffleDecommissionedList } from '@/api/api'
-import { memFormatter, dateFormatter } from '@/utils/common'
-import { useCurrentServerStore } from '@/store/useCurrentServerStore'
-
-export default {
-  setup() {
-    const pageData = reactive({
-      tableData: [
-        {
-          id: '',
-          ip: '',
-          grpcPort: 0,
-          nettyPort: 0,
-          usedMemory: 0,
-          preAllocatedMemory: 0,
-          availableMemory: 0,
-          eventNumInFlush: 0,
-          tags: '',
-          status: '',
-          registrationTime: '',
-          timestamp: ''
-        }
-      ]
-    })
-    const currentServerStore = useCurrentServerStore()
-
-    async function getShuffleDecommissionedListPage() {
-      const res = await getShuffleDecommissionedList()
-      pageData.tableData = res.data.data
-    }
-
-    // The system obtains data from global variables and requests the 
interface to obtain new data after data changes.
-    currentServerStore.$subscribe((mutable, state) => {
-      if (state.currentServer) {
-        getShuffleDecommissionedListPage()
-      }
-    })
-
-    onMounted(() => {
-      // If the coordinator address to request is not found in the global 
variable, the request is not initiated.
-      if (currentServerStore.currentServer) {
-        getShuffleDecommissionedListPage()
-      }
-    })
-
-    return { pageData, memFormatter, dateFormatter }
-  }
-}
-</script>
diff --git 
a/dashboard/src/main/webapp/src/pages/serverstatus/DecommissioningNodeListPage.vue
 
b/dashboard/src/main/webapp/src/pages/serverstatus/DecommissioningNodeListPage.vue
deleted file mode 100644
index 58e52aa6f..000000000
--- 
a/dashboard/src/main/webapp/src/pages/serverstatus/DecommissioningNodeListPage.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<template>
-  <div>
-    <el-table :data="pageData.tableData" height="550" style="width: 100%">
-      <el-table-column prop="id" label="Id" min-width="140" />
-      <el-table-column prop="ip" label="IP" min-width="80" />
-      <el-table-column prop="grpcPort" label="Port" min-width="80" />
-      <el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
-      <el-table-column prop="usedMemory" label="UsedMem" min-width="80" 
:formatter="memFormatter" />
-      <el-table-column
-        prop="preAllocatedMemory"
-        label="PreAllocatedMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column
-        prop="availableMemory"
-        label="AvailableMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column prop="eventNumInFlush" label="FlushNum" min-width="80" 
/>
-      <el-table-column prop="status" label="Status" min-width="80" />
-      <el-table-column
-          prop="registrationTime"
-          label="RegistrationTime"
-          min-width="80"
-          :formatter="dateFormatter"
-      />
-      <el-table-column
-        prop="timestamp"
-        label="HeartbeatTime"
-        min-width="80"
-        :formatter="dateFormatter"
-      />
-      <el-table-column prop="tags" label="Tags" min-width="80" />
-    </el-table>
-  </div>
-</template>
-<script>
-import { onMounted, reactive } from 'vue'
-import { getShuffleDecommissioningList } from '@/api/api'
-import { memFormatter, dateFormatter } from '@/utils/common'
-import { useCurrentServerStore } from '@/store/useCurrentServerStore'
-
-export default {
-  setup() {
-    const pageData = reactive({
-      tableData: [
-        {
-          id: '',
-          ip: '',
-          grpcPort: 0,
-          nettyPort: 0,
-          usedMemory: 0,
-          preAllocatedMemory: 0,
-          availableMemory: 0,
-          eventNumInFlush: 0,
-          tags: '',
-          status: '',
-          registrationTime: '',
-          timestamp: ''
-        }
-      ]
-    })
-    const currentServerStore = useCurrentServerStore()
-
-    async function getShuffleDecommissioningListPage() {
-      const res = await getShuffleDecommissioningList()
-      pageData.tableData = res.data.data
-    }
-
-    // The system obtains data from global variables and requests the 
interface to obtain new data after data changes.
-    currentServerStore.$subscribe((mutable, state) => {
-      if (state.currentServer) {
-        getShuffleDecommissioningListPage()
-      }
-    })
-
-    onMounted(() => {
-      // If the coordinator address to request is not found in the global 
variable, the request is not initiated.
-      if (currentServerStore.currentServer) {
-        getShuffleDecommissioningListPage()
-      }
-    })
-
-    return { pageData, memFormatter, dateFormatter }
-  }
-}
-</script>
diff --git 
a/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue 
b/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue
index 4e3b7f861..2b3a5c5ad 100644
--- a/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue
+++ b/dashboard/src/main/webapp/src/pages/serverstatus/ExcludeNodeList.vue
@@ -17,8 +17,14 @@
 
 <template>
   <div>
-    <el-table :data="pageData.tableData" height="550" style="width: 100%">
-      <el-table-column prop="id" label="ExcludeNodeId" min-width="180" />
+    <el-table
+      :data="pageData.tableData"
+      height="550"
+      style="width: 100%"
+      :default-sort="sortColumn"
+      @sort-change="sortChangeEvent"
+    >
+      <el-table-column prop="id" label="ExcludeNodeId" min-width="180" 
:sortable="true" />
     </el-table>
   </div>
 </template>
@@ -57,7 +63,15 @@ export default {
       }
     })
 
-    return { pageData }
+    const sortColumn = reactive({})
+    const sortChangeEvent = (sortInfo) => {
+      for (const sortColumnKey in sortColumn) {
+        delete sortColumn[sortColumnKey]
+      }
+      sortColumn[sortInfo.prop] = sortInfo.order
+    }
+
+    return { pageData, sortColumn, sortChangeEvent }
   }
 }
 </script>
diff --git a/dashboard/src/main/webapp/src/pages/serverstatus/LostNodeList.vue 
b/dashboard/src/main/webapp/src/pages/serverstatus/LostNodeList.vue
deleted file mode 100644
index 2bb8de2f7..000000000
--- a/dashboard/src/main/webapp/src/pages/serverstatus/LostNodeList.vue
+++ /dev/null
@@ -1,107 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<template>
-  <div>
-    <el-table :data="pageData.tableData" height="550" style="width: 100%">
-      <el-table-column prop="id" label="Id" min-width="140" />
-      <el-table-column prop="ip" label="IP" min-width="80" />
-      <el-table-column prop="grpcPort" label="Port" min-width="80" />
-      <el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
-      <el-table-column prop="usedMemory" label="UsedMem" min-width="80" 
:formatter="memFormatter" />
-      <el-table-column
-        prop="preAllocatedMemory"
-        label="PreAllocatedMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column
-        prop="availableMemory"
-        label="AvailableMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column prop="eventNumInFlush" label="FlushNum" min-width="80" 
/>
-      <el-table-column prop="status" label="Status" min-width="80" />
-      <el-table-column
-          prop="registrationTime"
-          label="RegistrationTime"
-          min-width="80"
-          :formatter="dateFormatter"
-      />
-      <el-table-column
-        prop="timestamp"
-        label="HeartbeatTime"
-        min-width="80"
-        :formatter="dateFormatter"
-      />
-      <el-table-column prop="tags" label="Tags" min-width="80" />
-    </el-table>
-  </div>
-</template>
-<script>
-import { onMounted, reactive } from 'vue'
-import { getShuffleLostList } from '@/api/api'
-import { memFormatter, dateFormatter } from '@/utils/common'
-import { useCurrentServerStore } from '@/store/useCurrentServerStore'
-
-export default {
-  setup() {
-    const pageData = reactive({
-      tableData: [
-        {
-          id: '',
-          ip: '',
-          grpcPort: 0,
-          nettyPort: 0,
-          usedMemory: 0,
-          preAllocatedMemory: 0,
-          availableMemory: 0,
-          eventNumInFlush: 0,
-          tags: '',
-          status: '',
-          registrationTime: '',
-          timestamp: ''
-        }
-      ]
-    })
-
-    const currentServerStore = useCurrentServerStore()
-
-    async function getShuffleLostListPage() {
-      const res = await getShuffleLostList()
-      pageData.tableData = res.data.data
-    }
-
-    // The system obtains data from global variables and requests the 
interface to obtain new data after data changes.
-    currentServerStore.$subscribe((mutable, state) => {
-      if (state.currentServer) {
-        getShuffleLostListPage()
-      }
-    })
-
-    onMounted(() => {
-      // If the coordinator address to request is not found in the global 
variable, the request is not initiated.
-      if (currentServerStore.currentServer) {
-        getShuffleLostListPage()
-      }
-    })
-
-    return { pageData, memFormatter, dateFormatter }
-  }
-}
-</script>
diff --git a/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue 
b/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
new file mode 100644
index 000000000..d0a134c9d
--- /dev/null
+++ b/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
@@ -0,0 +1,169 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~    http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<template>
+  <div>
+    <el-table
+      :data="listPageData.tableData"
+      height="550"
+      style="width: 100%"
+      :default-sort="sortColumn"
+      @sort-change="sortChangeEvent"
+    >
+      <el-table-column prop="id" label="Id" min-width="140" sortable fixed />
+      <el-table-column prop="ip" label="IP" min-width="80" sortable />
+      <el-table-column prop="grpcPort" label="Port" min-width="80" />
+      <el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
+      <el-table-column
+        prop="usedMemory"
+        label="UsedMem"
+        min-width="80"
+        :formatter="memFormatter"
+        sortable
+      />
+      <el-table-column
+        prop="preAllocatedMemory"
+        label="PreAllocatedMem"
+        min-width="100"
+        :formatter="memFormatter"
+        sortable
+      />
+      <el-table-column
+        prop="availableMemory"
+        label="AvailableMem"
+        min-width="80"
+        :formatter="memFormatter"
+        sortable
+      />
+      <el-table-column prop="eventNumInFlush" label="FlushNum" min-width="80" 
sortable />
+      <el-table-column prop="status" label="Status" min-width="80" sortable />
+      <el-table-column
+        prop="registrationTime"
+        label="RegistrationTime"
+        min-width="120"
+        :formatter="dateFormatter"
+      />
+      <el-table-column
+        prop="timestamp"
+        label="HeartbeatTime"
+        min-width="120"
+        :formatter="dateFormatter"
+        sortable
+      />
+      <el-table-column prop="tags" label="Tags" min-width="140" />
+    </el-table>
+  </div>
+</template>
+<script>
+import { onMounted, reactive, watch } from 'vue'
+import { memFormatter, dateFormatter } from '@/utils/common'
+import {
+  getShuffleActiveNodes,
+  getShuffleDecommissionedList,
+  getShuffleDecommissioningList,
+  getShuffleLostList,
+  getShuffleUnhealthyList
+} from '@/api/api'
+import { useRouter } from 'vue-router'
+import { useCurrentServerStore } from '@/store/useCurrentServerStore'
+
+export default {
+  setup() {
+    const router = useRouter()
+    const currentServerStore = useCurrentServerStore()
+    const sortColumn = reactive({})
+    const listPageData = reactive({
+      tableData: [
+        {
+          id: '',
+          ip: '',
+          grpcPort: 0,
+          nettyPort: 0,
+          usedMemory: 0,
+          preAllocatedMemory: 0,
+          availableMemory: 0,
+          eventNumInFlush: 0,
+          tags: '',
+          status: '',
+          registrationTime: '',
+          timestamp: ''
+        }
+      ]
+    })
+
+    async function getShuffleActiveNodesPage() {
+      const res = await getShuffleActiveNodes()
+      listPageData.tableData = res.data.data
+    }
+
+    async function getShuffleDecommissionedListPage() {
+      const res = await getShuffleDecommissionedList()
+      listPageData.tableData = res.data.data
+    }
+
+    async function getShuffleDecommissioningListPage() {
+      const res = await getShuffleDecommissioningList()
+      listPageData.tableData = res.data.data
+    }
+
+    async function getShuffleLostListPage() {
+      const res = await getShuffleLostList()
+      listPageData.tableData = res.data.data
+    }
+
+    async function getShuffleUnhealthyListPage() {
+      const res = await getShuffleUnhealthyList()
+      listPageData.tableData = res.data.data
+    }
+
+    const loadPageData = () => {
+      if (router.currentRoute.value.name === 'activeNodeList') {
+        getShuffleActiveNodesPage()
+      } else if (router.currentRoute.value.name === 'decommissioningNodeList') 
{
+        getShuffleDecommissioningListPage()
+      } else if (router.currentRoute.value.name === 'decommissionedNodeList') {
+        getShuffleDecommissionedListPage()
+      } else if (router.currentRoute.value.name === 'unhealthyNodeList') {
+        getShuffleUnhealthyListPage()
+      } else if (router.currentRoute.value.name === 'lostNodeList') {
+        getShuffleLostListPage()
+      }
+    }
+
+    onMounted(() => {
+      // If the coordinator address to request is not found in the global 
variable, the request is not initiated.
+      if (currentServerStore.currentServer) {
+        loadPageData()
+      }
+    })
+
+    watch(router.currentRoute, () => {
+      if (currentServerStore.currentServer) {
+        loadPageData()
+      }
+    })
+
+    const sortChangeEvent = (sortInfo) => {
+      for (const sortColumnKey in sortColumn) {
+        delete sortColumn[sortColumnKey]
+      }
+      sortColumn[sortInfo.prop] = sortInfo.order
+    }
+    return { listPageData, sortColumn, sortChangeEvent, memFormatter, 
dateFormatter }
+  }
+}
+</script>
diff --git 
a/dashboard/src/main/webapp/src/pages/serverstatus/UnhealthyNodeListPage.vue 
b/dashboard/src/main/webapp/src/pages/serverstatus/UnhealthyNodeListPage.vue
deleted file mode 100644
index 6073b994d..000000000
--- a/dashboard/src/main/webapp/src/pages/serverstatus/UnhealthyNodeListPage.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~    http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<template>
-  <div>
-    <el-table :data="pageData.tableData" height="550" style="width: 100%">
-      <el-table-column prop="id" label="Id" min-width="140" />
-      <el-table-column prop="ip" label="IP" min-width="80" />
-      <el-table-column prop="grpcPort" label="Port" min-width="80" />
-      <el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
-      <el-table-column prop="usedMemory" label="UsedMem" min-width="80" 
:formatter="memFormatter" />
-      <el-table-column
-        prop="preAllocatedMemory"
-        label="PreAllocatedMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column
-        prop="availableMemory"
-        label="AvailableMem"
-        min-width="80"
-        :formatter="memFormatter"
-      />
-      <el-table-column prop="eventNumInFlush" label="FlushNum" min-width="80" 
/>
-      <el-table-column prop="status" label="Status" min-width="80" />
-      <el-table-column
-          prop="registrationTime"
-          label="RegistrationTime"
-          min-width="80"
-          :formatter="dateFormatter"
-      />
-      <el-table-column
-        prop="timestamp"
-        label="HeartbeatTime"
-        min-width="80"
-        :formatter="dateFormatter"
-      />
-      <el-table-column prop="tags" label="Tags" min-width="80" />
-    </el-table>
-  </div>
-</template>
-<script>
-import { onMounted, reactive } from 'vue'
-import { getShuffleUnhealthyList } from '@/api/api'
-import { memFormatter, dateFormatter } from '@/utils/common'
-import { useCurrentServerStore } from '@/store/useCurrentServerStore'
-
-export default {
-  setup() {
-    const pageData = reactive({
-      tableData: [
-        {
-          id: '',
-          ip: '',
-          grpcPort: 0,
-          nettyPort: 0,
-          usedMemory: 0,
-          preAllocatedMemory: 0,
-          availableMemory: 0,
-          eventNumInFlush: 0,
-          tags: '',
-          status: '',
-          registrationTime: '',
-          timestamp: ''
-        }
-      ]
-    })
-    const currentServerStore = useCurrentServerStore()
-
-    async function getShuffleUnhealthyListPage() {
-      const res = await getShuffleUnhealthyList()
-      pageData.tableData = res.data.data
-    }
-
-    // The system obtains data from global variables and requests the 
interface to obtain new data after data changes.
-    currentServerStore.$subscribe((mutable, state) => {
-      if (state.currentServer) {
-        getShuffleUnhealthyListPage()
-      }
-    })
-
-    onMounted(() => {
-      // If the coordinator address to request is not found in the global 
variable, the request is not initiated.
-      if (currentServerStore.currentServer) {
-        getShuffleUnhealthyListPage()
-      }
-    })
-
-    return { pageData, memFormatter, dateFormatter }
-  }
-}
-</script>
diff --git a/dashboard/src/main/webapp/src/router/index.js 
b/dashboard/src/main/webapp/src/router/index.js
index 950c11181..16ecaadc2 100644
--- a/dashboard/src/main/webapp/src/router/index.js
+++ b/dashboard/src/main/webapp/src/router/index.js
@@ -19,12 +19,8 @@ import { createRouter, createWebHashHistory } from 
'vue-router'
 import ApplicationPage from '@/pages/ApplicationPage.vue'
 import CoordinatorServerPage from '@/pages/CoordinatorServerPage.vue'
 import ShuffleServerPage from '@/pages/ShuffleServerPage.vue'
-import ActiveNodeListPage from '@/pages/serverstatus/ActiveNodeListPage'
-import DecommissioningNodeListPage from 
'@/pages/serverstatus/DecommissioningNodeListPage'
-import DecommissionednodeListPage from 
'@/pages/serverstatus/DecommissionednodeListPage'
-import LostNodeList from '@/pages/serverstatus/LostNodeList'
-import UnhealthyNodeListPage from '@/pages/serverstatus/UnhealthyNodeListPage'
 import ExcludeNodeList from '@/pages/serverstatus/ExcludeNodeList'
+import NodeListPage from '@/pages/serverstatus/NodeListPage.vue'
 
 const routes = [
   {
@@ -41,27 +37,27 @@ const routes = [
       {
         path: '/shuffleserverpage/activeNodeList',
         name: 'activeNodeList',
-        component: ActiveNodeListPage
+        component: NodeListPage
       },
       {
         path: '/shuffleserverpage/decommissioningNodeList',
         name: 'decommissioningNodeList',
-        component: DecommissioningNodeListPage
+        component: NodeListPage
       },
       {
         path: '/shuffleserverpage/decommissionedNodeList',
         name: 'decommissionedNodeList',
-        component: DecommissionednodeListPage
+        component: NodeListPage
       },
       {
         path: '/shuffleserverpage/lostNodeList',
         name: 'lostNodeList',
-        component: LostNodeList
+        component: NodeListPage
       },
       {
         path: '/shuffleserverpage/unhealthyNodeList',
         name: 'unhealthyNodeList',
-        component: UnhealthyNodeListPage
+        component: NodeListPage
       },
       {
         path: '/shuffleserverpage/excludeNodeList',

Reply via email to