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

nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-celeborn.git


The following commit(s) were added to refs/heads/main by this push:
     new 22c70124e [CELEBORN-1307][FOLLOWUP] Introduce worker detail module for 
dashboard frontend
22c70124e is described below

commit 22c70124e973ff2a7852e3f638fa23abbe973699
Author: tiny-dust <[email protected]>
AuthorDate: Thu Mar 14 17:35:36 2024 +0800

    [CELEBORN-1307][FOLLOWUP] Introduce worker detail module for dashboard 
frontend
    
    ### What changes were proposed in this pull request?
    
    Introduce worker detail module for dashboard frontend:
    
    To detail page:
    
![output](https://github.com/apache/incubator-celeborn/assets/49502875/64f158c7-d791-4427-81c8-2ca767f4778f)
    
    Storages:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/ee83256c-b925-479a-aa96-3252d5808c3a)
    
    Memory:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/9d33095f-9e6c-4b41-af51-06c840c56146)
    
    Application:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/54f786f6-433c-402f-aa80-da9c13710322)
    
    Configuration:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/2ece1933-45f7-4de6-b483-471c57aae249)
    
    FlameGraph:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/259be92a-46a8-4052-bf6b-7ec0c5ede92c)
    
    ThreadDump:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/e7260fbe-5e95-4718-9ba3-15ec5e61f0f7)
    
    Metrics:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/2d117ff0-621f-48c3-90c6-fa33567b67da)
    
    Logs:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/b8fc7c30-614f-4dec-8370-a058c7ea1c66)
    
    LogList:
    
![image](https://github.com/apache/incubator-celeborn/assets/49502875/96e2f9a4-ffbb-428b-885b-bd163c99f6a8)
    
    ### Why are the changes needed?
    
    Show more `worker` information.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    Local test.
    
    Closes #2393 from tiny-dust/worker-detail.
    
    Authored-by: tiny-dust <[email protected]>
    Signed-off-by: SteNicholas <[email protected]>
---
 web/LICENSE                                        |  2 +-
 web/licenses/LICENSE-@vicons-antd                  | 21 +++++++
 web/licenses/LICENSE-dayjs                         | 21 -------
 web/package.json                                   |  2 +-
 web/pnpm-lock.yaml                                 | 14 ++---
 .../api/models/application/types/application.ts    |  7 +++
 web/src/api/models/worker/index.ts                 |  6 +-
 web/src/api/models/worker/types/detail.ts          | 55 +++++++++++++++++
 web/src/api/models/worker/types/index.ts           |  1 +
 web/src/composables/form.ts                        | 10 ++--
 web/src/composables/pagination.ts                  |  5 ++
 web/src/layouts/components/menus/index.vue         |  2 +-
 web/src/router/modules/worker.ts                   | 15 ++++-
 web/src/utils/{datetime.ts => index.ts}            |  6 +-
 .../modules/worker.ts => utils/transform.ts}       | 13 ++--
 .../application/components/ApplicationList.vue     |  6 +-
 .../components/ApplicationSearchForm.vue           | 29 +++++----
 .../worker/components/WorkerDetailContent.vue      | 65 ++++++++++++++++++++
 .../components/WorkerDetailOverview.vue}           | 55 ++++-------------
 web/src/views/worker/components/WorkerForm.vue     | 23 +++++---
 web/src/views/worker/components/WorkerTable.vue    | 66 ++++++++++++++-------
 .../components/detail/WorkerDetailConfig.vue}      | 69 ++++++++++------------
 .../components/detail/WorkerDetailLogFile.vue}     | 40 +++++++------
 .../components/detail/WorkerDetailMemory.vue}      | 40 +++++++------
 .../components/detail/WorkerDetailStorage.vue}     | 54 +++++++++++------
 .../worker/components/detail}/index.ts             | 20 +++----
 web/src/views/worker/components/index.ts           | 11 +++-
 web/src/views/worker/detail.vue                    | 65 ++++++++++++++++++++
 28 files changed, 483 insertions(+), 240 deletions(-)

diff --git a/web/LICENSE b/web/LICENSE
index 111d3221d..d2d9cc7f3 100644
--- a/web/LICENSE
+++ b/web/LICENSE
@@ -233,7 +233,7 @@ MIT licenses
    unplugin-vue-components 0.26.0 
https://www.npmjs.com/package/unplugin-vue-components/v/0.26.0 MIT
    vite 5.0.12 https://www.npmjs.com/package/vite/v/5.0.12 MIT
    vue-tsc 1.8.27 https://www.npmjs.com/package/vue-tsc/v/1.8.27 MIT
-   dayjs 1.11.10 https://www.npmjs.com/package/dayjs/v/1.11.10 MIT
+   @vicons/antd 0.12.0 https://www.npmjs.com/package/@vicons/antd/v/0.12.0 MIT
 
 ========================================
 Apache 2.0 licenses
diff --git a/web/licenses/LICENSE-@vicons-antd 
b/web/licenses/LICENSE-@vicons-antd
new file mode 100644
index 000000000..8fad6ec3e
--- /dev/null
+++ b/web/licenses/LICENSE-@vicons-antd
@@ -0,0 +1,21 @@
+MIT LICENSE
+
+Copyright (c) 2018-present Ant UED, https://xtech.antfin.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/web/licenses/LICENSE-dayjs b/web/licenses/LICENSE-dayjs
deleted file mode 100644
index caf931549..000000000
--- a/web/licenses/LICENSE-dayjs
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2018-present, iamkun
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/web/package.json b/web/package.json
index c8da831b5..fb154acda 100644
--- a/web/package.json
+++ b/web/package.json
@@ -13,7 +13,6 @@
     "format": "prettier --write src/"
   },
   "dependencies": {
-    "dayjs": "^1.11.10",
     "pinia": "^2.1.7",
     "vue": "^3.3.11",
     "vue-router": "^4.2.5"
@@ -23,6 +22,7 @@
     "@tsconfig/node18": "^18.2.2",
     "@types/node": "^18.19.3",
     "@varlet/axle": "^0.6.0",
+    "@vicons/antd": "^0.12.0",
     "@vitejs/plugin-vue": "^4.5.2",
     "@vitejs/plugin-vue-jsx": "^3.1.0",
     "@vue/eslint-config-prettier": "^8.0.0",
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index 581542ee6..6132f69eb 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -22,9 +22,6 @@ settings:
   excludeLinksFromLockfile: false
 
 dependencies:
-  dayjs:
-    specifier: ^1.11.10
-    version: 1.11.10
   pinia:
     specifier: ^2.1.7
     version: 2.1.7([email protected])([email protected])
@@ -48,6 +45,9 @@ devDependencies:
   '@varlet/axle':
     specifier: ^0.6.0
     version: 0.6.0([email protected])
+  '@vicons/antd':
+    specifier: ^0.12.0
+    version: 0.12.0
   '@vitejs/plugin-vue':
     specifier: ^4.5.2
     version: 4.5.2([email protected])([email protected])
@@ -1063,6 +1063,10 @@ packages:
     resolution: {integrity: 
sha512-MgLDRYfrGgKqu8Vg2kfRVRGRZE2d8CH0f75vph5h4AG+Clm+A7FgUZLaVTriMWuS6wXrrrceSgX5kNmb/lFShQ==}
     dev: true
 
+  /@vicons/[email protected]:
+    resolution: {integrity: 
sha512-C0p6aO1EmGG1QHrqgUWQS1No20934OdWSRQshM5NIDK5H1On6tC26U0hT6Rmp40KfUsvhvX5YW8BoWJdNFifPg==}
+    dev: true
+
   /@vitejs/[email protected]([email protected])([email protected]):
     resolution: {integrity: 
sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==}
     engines: {node: ^14.18.0 || >=16.0.0}
@@ -1563,10 +1567,6 @@ packages:
       '@babel/runtime': 7.23.8
     dev: true
 
-  /[email protected]:
-    resolution: {integrity: 
sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
-    dev: false
-
   /[email protected]:
     resolution: {integrity: 
sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
     dev: true
diff --git a/web/src/api/models/application/types/application.ts 
b/web/src/api/models/application/types/application.ts
index 7f329d12b..e3cf8d01b 100644
--- a/web/src/api/models/application/types/application.ts
+++ b/web/src/api/models/application/types/application.ts
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+import type { PaginationProps } from 'naive-ui'
+
 export interface ApplicationOverview {
   runningNum: number
   totalShuffleSize: string
@@ -39,3 +41,8 @@ export interface ApplicationResponse {
   applicationInfos: Application[]
   totalCount: number
 }
+
+export interface ApplicationTab {
+  pagination: PaginationProps
+  data: Application[]
+}
diff --git a/web/src/api/models/worker/index.ts 
b/web/src/api/models/worker/index.ts
index 0934472f3..1d0769e72 100644
--- a/web/src/api/models/worker/index.ts
+++ b/web/src/api/models/worker/index.ts
@@ -17,7 +17,7 @@
 
 import { api } from '@/api/request'
 import type { PaginationType } from '@/api/types'
-import type { Worker, WorkerOverview } from './types'
+import type { Worker, WorkerDetail, WorkerOverview } from './types'
 
 export * from './types'
 
@@ -28,3 +28,7 @@ export const getWorkerOverview = () => {
 export const getWorkerList = () => {
   return api<PaginationType<{ workerInfos: Worker[] }>>('/worker/list', 'get')
 }
+
+export const getWorkerDetail = () => {
+  return api<WorkerDetail>('/worker/detail', 'get')
+}
diff --git a/web/src/api/models/worker/types/detail.ts 
b/web/src/api/models/worker/types/detail.ts
new file mode 100644
index 000000000..de3a10eeb
--- /dev/null
+++ b/web/src/api/models/worker/types/detail.ts
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+export interface WorkerDetail {
+  diskInfos: WorkerDiskInfo[]
+  dynamicConfigs: Record<string, string>
+  fetchPort: number
+  flameGraph: string
+  hostname: string
+  ip: string
+  logFiles: Record<string, string>
+  logs: string
+  memoryInfo: WorkerMemoryInfo
+  metrics: string
+  pushPort: number
+  replicatePort: number
+  startTime: Date
+  state: string
+  staticConfigs: Record<string, string>
+  threadDump: string
+}
+
+export interface WorkerDiskInfo {
+  diskType: string
+  mountPoint: string
+  totalSpace: string
+  usedPercent: string
+  usedSpace: string
+}
+
+export interface WorkerMemoryInfo {
+  diskBuffer: string
+  pausePushDataThreshold: string
+  pauseReplicateThreshold: string
+  readBuffer: string
+  resumeThreshold: string
+  sortMemory: string
+  totalNativeMemory: string
+  usedDirectMemory: string
+  [key: string]: string
+}
diff --git a/web/src/api/models/worker/types/index.ts 
b/web/src/api/models/worker/types/index.ts
index 5677157cc..ad4504000 100644
--- a/web/src/api/models/worker/types/index.ts
+++ b/web/src/api/models/worker/types/index.ts
@@ -15,4 +15,5 @@
  * limitations under the License.
  */
 
+export * from './detail'
 export * from './worker'
diff --git a/web/src/composables/form.ts b/web/src/composables/form.ts
index c51ae4e4c..128c45881 100644
--- a/web/src/composables/form.ts
+++ b/web/src/composables/form.ts
@@ -15,20 +15,18 @@
  * limitations under the License.
  */
 
-import { getCurrentInstance, unref } from 'vue'
-
-export const useFormEvent = (formData: Record<string, any>) => {
-  const instance = getCurrentInstance()
+import { unref, type SetupContext } from 'vue'
 
+export const useFormEvent = (formData: Record<string, any>, ctx: SetupContext) 
=> {
   const doSearch = () => {
-    instance?.emit('search', formData)
+    ctx.emit('search', formData)
   }
 
   const resetSearch = () => {
     Object.keys(formData).forEach((key) => {
       formData[key] = ''
     })
-    instance?.emit('reset', unref(formData))
+    ctx.emit('reset', unref(formData))
   }
 
   return {
diff --git a/web/src/composables/pagination.ts 
b/web/src/composables/pagination.ts
index 2719f5923..778e99f07 100644
--- a/web/src/composables/pagination.ts
+++ b/web/src/composables/pagination.ts
@@ -18,6 +18,7 @@
 import type { PaginationProps } from 'naive-ui'
 
 export interface UsePaginationOptions {
+  params?: Record<string, string>
   currentPage?: number
   pageSize?: number
   onLoadData?: Function
@@ -25,6 +26,7 @@ export interface UsePaginationOptions {
 
 export function usePagination(options: UsePaginationOptions) {
   const {
+    params,
     currentPage: defaultCurrentPage = 1,
     pageSize: defaultPageSize = 30,
     onLoadData
@@ -77,6 +79,9 @@ export function usePagination(options: UsePaginationOptions) {
   }
 
   onMounted(() => {
+    if (params) {
+      searchParams.value = params
+    }
     loadData()
   })
 
diff --git a/web/src/layouts/components/menus/index.vue 
b/web/src/layouts/components/menus/index.vue
index 70b876ee1..1daa9e448 100644
--- a/web/src/layouts/components/menus/index.vue
+++ b/web/src/layouts/components/menus/index.vue
@@ -34,7 +34,7 @@ defineProps({
 })
 
 const path = computed(() => {
-  return router.currentRoute.value.fullPath
+  return `/${router.currentRoute.value.fullPath.split('/')[1]}`
 })
 
 const updateValue = (value: string) => {
diff --git a/web/src/router/modules/worker.ts b/web/src/router/modules/worker.ts
index 965b50419..af679c7f9 100644
--- a/web/src/router/modules/worker.ts
+++ b/web/src/router/modules/worker.ts
@@ -19,5 +19,18 @@ export default {
   path: '/worker',
   name: 'worker',
   meta: { title: 'Worker' },
-  component: () => import('@/views/worker/index.vue')
+  children: [
+    {
+      path: '/worker',
+      name: 'worker',
+      meta: { title: 'Worker' },
+      component: () => import('@/views/worker/index.vue')
+    },
+    {
+      path: '/worker/detail',
+      name: 'workerDetail',
+      meta: { title: 'WorkerDetail' },
+      component: () => import('@/views/worker/detail.vue')
+    }
+  ]
 }
diff --git a/web/src/utils/datetime.ts b/web/src/utils/index.ts
similarity index 83%
rename from web/src/utils/datetime.ts
rename to web/src/utils/index.ts
index 05147dc06..af9f968c6 100644
--- a/web/src/utils/datetime.ts
+++ b/web/src/utils/index.ts
@@ -15,8 +15,4 @@
  * limitations under the License.
  */
 
-import dayjs from 'dayjs'
-
-export const normalizeDatetime = (datetime?: string) => {
-  return datetime ? dayjs(datetime).format('YYYY-MM-DD HH:mm:ss') : ''
-}
+export * from './transform'
diff --git a/web/src/router/modules/worker.ts b/web/src/utils/transform.ts
similarity index 82%
copy from web/src/router/modules/worker.ts
copy to web/src/utils/transform.ts
index 965b50419..92494424d 100644
--- a/web/src/router/modules/worker.ts
+++ b/web/src/utils/transform.ts
@@ -15,9 +15,12 @@
  * limitations under the License.
  */
 
-export default {
-  path: '/worker',
-  name: 'worker',
-  meta: { title: 'Worker' },
-  component: () => import('@/views/worker/index.vue')
+export function objectToArray(obj: Record<string, any>) {
+  const keys = Object.keys(obj)
+  return keys.map((key) => {
+    return {
+      key,
+      value: obj[key]
+    }
+  })
 }
diff --git a/web/src/views/application/components/ApplicationList.vue 
b/web/src/views/application/components/ApplicationList.vue
index 8e475a565..e5b526106 100644
--- a/web/src/views/application/components/ApplicationList.vue
+++ b/web/src/views/application/components/ApplicationList.vue
@@ -17,9 +17,9 @@
 -->
 
 <script setup lang="ts">
-import type { PropType } from 'vue'
+import type { Application } from '@/api'
 import type { DataTableColumns, PaginationProps } from 'naive-ui'
-import type { Application } from '@/api/models/application/types'
+import type { PropType } from 'vue'
 
 defineOptions({
   name: 'ApplicationDataTable'
@@ -67,5 +67,5 @@ const columns: DataTableColumns<Application> = [
 </script>
 
 <template>
-  <n-data-table :columns="columns" :data="data" :pagination="pagination" />
+  <n-data-table scroll-x="1000" :columns="columns" :data="data" 
:pagination="pagination" />
 </template>
diff --git a/web/src/views/application/components/ApplicationSearchForm.vue 
b/web/src/views/application/components/ApplicationSearchForm.vue
index 6de56434b..7f13d2a70 100644
--- a/web/src/views/application/components/ApplicationSearchForm.vue
+++ b/web/src/views/application/components/ApplicationSearchForm.vue
@@ -16,20 +16,27 @@
 * limitations under the License.
 -->
 
-<script setup lang="ts">
+<script lang="ts">
 import { useFormEvent } from '@/composables'
 
-defineOptions({
-  name: 'ApplicationSearchForm'
-})
+export default defineComponent({
+  name: 'ApplicationSearchForm',
+  setup(_, ctx) {
+    const searchForm = reactive({
+      appId: '',
+      subUser: '',
+      tenant: ''
+    })
 
-const searchForm = reactive({
-  appId: '',
-  subUser: '',
-  tenant: ''
-})
+    const { doSearch, resetSearch } = useFormEvent(searchForm, ctx)
 
-const { doSearch, resetSearch } = useFormEvent(searchForm)
+    return {
+      searchForm,
+      doSearch,
+      resetSearch
+    }
+  }
+})
 </script>
 
 <template>
@@ -39,7 +46,7 @@ const { doSearch, resetSearch } = useFormEvent(searchForm)
     label-placement="left"
     @keydown.enter="doSearch"
   >
-    <n-grid :x-gap="24" :y-gap="24" :cols="6">
+    <n-grid :x-gap="24" :y-gap="24" cols="1 400:2 800:4 1200:6">
       <n-form-item-gi label="AppId" path="appId">
         <n-input v-model:value="searchForm.appId" placeholder="" clearable />
       </n-form-item-gi>
diff --git a/web/src/views/worker/components/WorkerDetailContent.vue 
b/web/src/views/worker/components/WorkerDetailContent.vue
new file mode 100644
index 000000000..259fde526
--- /dev/null
+++ b/web/src/views/worker/components/WorkerDetailContent.vue
@@ -0,0 +1,65 @@
+<!--
+* 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.
+-->
+
+<script setup lang="ts">
+import type { ApplicationTab, WorkerDetail } from '@/api'
+import { ApplicationListService } from '@/views/application/components'
+import type { PropType } from 'vue'
+import {
+  WorkerDetailConfigService,
+  WorkerDetailLogFileService,
+  WorkerDetailMemoryService,
+  WorkerDetailStorageService
+} from '.'
+
+defineProps({
+  data: {
+    type: Object as PropType<WorkerDetail>,
+    default: () => ({})
+  },
+  application: {
+    type: Object as PropType<ApplicationTab>,
+    default: () => {}
+  }
+})
+</script>
+<template>
+  <n-card title="" style="margin-bottom: 16px">
+    <n-tabs type="line" animated>
+      <n-tab-pane name="Storages" tab="Storages">
+        <WorkerDetailStorageService :data="data.diskInfos" />
+      </n-tab-pane>
+      <n-tab-pane name="Memory" tab="Memory">
+        <WorkerDetailMemoryService :data="data.memoryInfo" />
+      </n-tab-pane>
+      <n-tab-pane name="Application" tab="Application">
+        <ApplicationListService :="application" />
+      </n-tab-pane>
+      <n-tab-pane name="Configuration" tab="Configuration">
+        <WorkerDetailConfigService :dynamic="data.dynamicConfigs" 
:static="data.staticConfigs" />
+      </n-tab-pane>
+      <n-tab-pane name="FlameGraph" tab="FlameGraph"> {{ data.flameGraph }} 
</n-tab-pane>
+      <n-tab-pane name="ThreadDump" tab="ThreadDump"> {{ data.threadDump }} 
</n-tab-pane>
+      <n-tab-pane name="Metrics" tab="Metrics"> {{ data.metrics }} 
</n-tab-pane>
+      <n-tab-pane name="Logs" tab="Logs"> {{ data.logs }} </n-tab-pane>
+      <n-tab-pane name="LogList" tab="LogList">
+        <WorkerDetailLogFileService :data="data.logFiles" />
+      </n-tab-pane>
+    </n-tabs>
+  </n-card>
+</template>
diff --git a/web/src/views/application/components/ApplicationList.vue 
b/web/src/views/worker/components/WorkerDetailOverview.vue
similarity index 53%
copy from web/src/views/application/components/ApplicationList.vue
copy to web/src/views/worker/components/WorkerDetailOverview.vue
index 8e475a565..314513ab5 100644
--- a/web/src/views/application/components/ApplicationList.vue
+++ b/web/src/views/worker/components/WorkerDetailOverview.vue
@@ -17,55 +17,26 @@
 -->
 
 <script setup lang="ts">
+import type { WorkerDetail } from '@/api'
 import type { PropType } from 'vue'
-import type { DataTableColumns, PaginationProps } from 'naive-ui'
-import type { Application } from '@/api/models/application/types'
-
-defineOptions({
-  name: 'ApplicationDataTable'
-})
 
 defineProps({
   data: {
-    type: Array as PropType<Application[]>,
-    default: () => []
-  },
-  pagination: {
-    type: Object as PropType<PaginationProps>,
+    type: Object as PropType<WorkerDetail>,
     default: () => ({})
   }
 })
-
-const columns: DataTableColumns<Application> = [
-  {
-    title: 'AppId',
-    key: 'appId',
-    sorter: true
-  },
-  {
-    title: 'SubUser',
-    key: 'subUser'
-  },
-  {
-    title: 'Tenant',
-    key: 'tenant'
-  },
-  {
-    title: 'HeartbeatTime',
-    key: 'heartbeatTime'
-  },
-  {
-    title: 'ShuffleSize',
-    key: 'shuffleSize'
-  },
-  {
-    title: 'ActiveShuffle',
-    key: 'shuffleFileCount',
-    sorter: true
-  }
-]
 </script>
-
 <template>
-  <n-data-table :columns="columns" :data="data" :pagination="pagination" />
+  <n-card hoverable>
+    <n-descriptions label-placement="left" title="Worker Detail" :column="4">
+      <n-descriptions-item label="HostName"> {{ data.hostname }} 
</n-descriptions-item>
+      <n-descriptions-item label="IP"> {{ data.ip }} </n-descriptions-item>
+      <n-descriptions-item label="State"> {{ data.state }} 
</n-descriptions-item>
+      <n-descriptions-item label="StartTime"> {{ data.startTime }} 
</n-descriptions-item>
+      <n-descriptions-item label="FetchPort"> {{ data.fetchPort }} 
</n-descriptions-item>
+      <n-descriptions-item label="PushPort"> {{ data.pushPort }} 
</n-descriptions-item>
+      <n-descriptions-item label="ReplicatePort"> {{ data.replicatePort }} 
</n-descriptions-item>
+    </n-descriptions>
+  </n-card>
 </template>
diff --git a/web/src/views/worker/components/WorkerForm.vue 
b/web/src/views/worker/components/WorkerForm.vue
index cb8c3eb3b..269bd3a88 100644
--- a/web/src/views/worker/components/WorkerForm.vue
+++ b/web/src/views/worker/components/WorkerForm.vue
@@ -16,20 +16,29 @@
 * limitations under the License.
 -->
 
-<script lang="ts" setup>
+<script lang="ts">
 import { useFormEvent } from '@/composables'
 
-const formData = reactive({
-  ip: '',
-  state: ''
+export default defineComponent({
+  name: 'WorkerForm',
+  setup(_, ctx) {
+    const formData = reactive({
+      ip: '',
+      state: ''
+    })
+    const { doSearch, resetSearch } = useFormEvent(formData, ctx)
+    return {
+      formData,
+      doSearch,
+      resetSearch
+    }
+  }
 })
-
-const { doSearch, resetSearch } = useFormEvent(formData)
 </script>
 
 <template>
   <n-form :model="formData" :show-feedback="false" label-placement="left" 
@keydown.enter="doSearch">
-    <n-grid :x-gap="24" :y-gap="24" :cols="6">
+    <n-grid :x-gap="24" :y-gap="24" cols="1 400:2 800:4 1200:6">
       <n-form-item-gi label="IP" path="ip">
         <n-input v-model:value="formData.ip" placeholder="" clearable />
       </n-form-item-gi>
diff --git a/web/src/views/worker/components/WorkerTable.vue 
b/web/src/views/worker/components/WorkerTable.vue
index d4c6e8048..b5462bc8e 100644
--- a/web/src/views/worker/components/WorkerTable.vue
+++ b/web/src/views/worker/components/WorkerTable.vue
@@ -18,9 +18,19 @@
 
 <script setup lang="ts">
 import type { Worker } from '@/api'
-import { NButton, type DataTableColumns, type PaginationProps } from 'naive-ui'
+import { QuestionCircleOutlined } from '@vicons/antd'
+import {
+  NButton,
+  NIcon,
+  NSpace,
+  NTooltip,
+  type DataTableColumns,
+  type PaginationProps
+} from 'naive-ui'
 import type { PropType } from 'vue'
 
+const router = useRouter()
+
 defineOptions({
   name: 'WorkerTable'
 })
@@ -36,6 +46,27 @@ defineProps({
   }
 })
 
+const toWorkerDetail = ({ ip, rpcPort, pushPort, fetchPort, replicatePort }: 
Worker) => {
+  const worker = `${ip}:${rpcPort}:${pushPort}:${fetchPort}:${replicatePort}`
+  router.push({
+    name: 'workerDetail',
+    query: { worker }
+  })
+}
+
+const renderToolTip = () => {
+  return h(NSpace, null, {
+    default: () => [
+      h('span', null, { default: () => 'Ports' }),
+      h(NTooltip, null, {
+        default: () => 'RpcPort:PushPort:FetchPort:ReplicatePort',
+        trigger: () =>
+          h(NIcon, { style: { paddingTop: '4px' } }, { default: () => 
h(QuestionCircleOutlined) })
+      })
+    ]
+  })
+}
+
 const columns: DataTableColumns<Worker> = [
   {
     title: 'Hostname/IP',
@@ -57,24 +88,12 @@ const columns: DataTableColumns<Worker> = [
     key: 'heartbeatTime'
   },
   {
-    title: 'RPCPort',
-    key: 'rpcPort'
-  },
-  {
-    title: 'RestPort',
-    key: 'restPort'
-  },
-  {
-    title: 'FetchPort',
-    key: 'fetchPort'
-  },
-  {
-    title: 'PushPort',
-    key: 'pushPort'
-  },
-  {
-    title: 'ReplicatePort',
-    key: 'replicatePort'
+    key: 'ports',
+    title: renderToolTip,
+    render({ rpcPort, pushPort, fetchPort, replicatePort }) {
+      const ports = `${rpcPort}:${pushPort}:${fetchPort}:${replicatePort}`
+      return ports
+    }
   },
   {
     title: 'Version',
@@ -83,13 +102,16 @@ const columns: DataTableColumns<Worker> = [
   {
     title: 'More',
     key: 'more',
-    render: () => {
+    render: (row) => {
       return h(
         NButton,
         {
           text: true,
           type: 'primary',
-          size: 'small'
+          size: 'small',
+          onClick: () => {
+            toWorkerDetail(row)
+          }
         },
         { default: () => 'More' }
       )
@@ -99,5 +121,5 @@ const columns: DataTableColumns<Worker> = [
 </script>
 
 <template>
-  <n-data-table :columns="columns" :data="data" remote 
:pagination="pagination" />
+  <n-data-table :scroll-x="1000" :columns="columns" :data="data" remote 
:pagination="pagination" />
 </template>
diff --git a/web/src/views/application/components/ApplicationList.vue 
b/web/src/views/worker/components/detail/WorkerDetailConfig.vue
similarity index 52%
copy from web/src/views/application/components/ApplicationList.vue
copy to web/src/views/worker/components/detail/WorkerDetailConfig.vue
index 8e475a565..3326c828d 100644
--- a/web/src/views/application/components/ApplicationList.vue
+++ b/web/src/views/worker/components/detail/WorkerDetailConfig.vue
@@ -16,56 +16,49 @@
 * limitations under the License.
 -->
 
-<script setup lang="ts">
+<script lang="ts" setup>
+import { objectToArray } from '@/utils'
 import type { PropType } from 'vue'
-import type { DataTableColumns, PaginationProps } from 'naive-ui'
-import type { Application } from '@/api/models/application/types'
 
-defineOptions({
-  name: 'ApplicationDataTable'
-})
-
-defineProps({
-  data: {
-    type: Array as PropType<Application[]>,
-    default: () => []
+const props = defineProps({
+  static: {
+    type: Object as PropType<Record<string, string>>,
+    default: () => ({})
   },
-  pagination: {
-    type: Object as PropType<PaginationProps>,
+  dynamic: {
+    type: Object as PropType<Record<string, string>>,
     default: () => ({})
   }
 })
 
-const columns: DataTableColumns<Application> = [
-  {
-    title: 'AppId',
-    key: 'appId',
-    sorter: true
-  },
-  {
-    title: 'SubUser',
-    key: 'subUser'
-  },
-  {
-    title: 'Tenant',
-    key: 'tenant'
-  },
-  {
-    title: 'HeartbeatTime',
-    key: 'heartbeatTime'
-  },
+const staticData = computed(() => {
+  return objectToArray(props.static)
+})
+
+const dynamicData = computed(() => {
+  return objectToArray(props.dynamic)
+})
+
+const columns = [
   {
-    title: 'ShuffleSize',
-    key: 'shuffleSize'
+    title: 'Key',
+    key: 'key',
+    width: 150
   },
   {
-    title: 'ActiveShuffle',
-    key: 'shuffleFileCount',
-    sorter: true
+    title: 'Value',
+    key: 'value',
+    width: 150
   }
 ]
 </script>
-
 <template>
-  <n-data-table :columns="columns" :data="data" :pagination="pagination" />
+  <n-flex :wrap="false">
+    <n-card title="Static Config">
+      <n-data-table :columns="columns" :data="staticData" :pagination="false" 
/>
+    </n-card>
+    <n-card title="Dynamic Config">
+      <n-data-table :columns="columns" :data="dynamicData" :pagination="false" 
/>
+    </n-card>
+  </n-flex>
 </template>
diff --git a/web/src/layouts/components/menus/index.vue 
b/web/src/views/worker/components/detail/WorkerDetailLogFile.vue
similarity index 65%
copy from web/src/layouts/components/menus/index.vue
copy to web/src/views/worker/components/detail/WorkerDetailLogFile.vue
index 70b876ee1..ffb04720b 100644
--- a/web/src/layouts/components/menus/index.vue
+++ b/web/src/views/worker/components/detail/WorkerDetailLogFile.vue
@@ -16,32 +16,34 @@
 * limitations under the License.
 -->
 
-<script setup lang="ts">
-import type { MenuOption } from 'naive-ui'
+<script lang="ts" setup>
+import { objectToArray } from '@/utils'
 import type { PropType } from 'vue'
 
-const router = useRouter()
-
-defineOptions({
-  name: 'SiderMenu'
-})
-
-defineProps({
-  menus: {
-    type: Array as PropType<MenuOption[]>,
-    default: () => []
+const props = defineProps({
+  data: {
+    type: Object as PropType<Record<string, string>>,
+    default: () => ({})
   }
 })
 
-const path = computed(() => {
-  return router.currentRoute.value.fullPath
+const tableData = computed(() => {
+  return objectToArray(props.data)
 })
 
-const updateValue = (value: string) => {
-  router.push(value)
-}
+const columns = [
+  {
+    title: 'Key',
+    key: 'key',
+    width: 150
+  },
+  {
+    title: 'Value',
+    key: 'value',
+    width: 150
+  }
+]
 </script>
-
 <template>
-  <n-menu :options="menus" :default-value="path" @update:value="updateValue" />
+  <n-data-table :columns="columns" :data="tableData" :pagination="false" />
 </template>
diff --git a/web/src/layouts/components/menus/index.vue 
b/web/src/views/worker/components/detail/WorkerDetailMemory.vue
similarity index 63%
copy from web/src/layouts/components/menus/index.vue
copy to web/src/views/worker/components/detail/WorkerDetailMemory.vue
index 70b876ee1..6ac1d4b6a 100644
--- a/web/src/layouts/components/menus/index.vue
+++ b/web/src/views/worker/components/detail/WorkerDetailMemory.vue
@@ -16,32 +16,36 @@
 * limitations under the License.
 -->
 
-<script setup lang="ts">
-import type { MenuOption } from 'naive-ui'
+<script lang="ts" setup>
+import type { WorkerMemoryInfo } from '@/api'
+import { objectToArray } from '@/utils'
 import type { PropType } from 'vue'
 
-const router = useRouter()
-
-defineOptions({
-  name: 'SiderMenu'
-})
-
-defineProps({
-  menus: {
-    type: Array as PropType<MenuOption[]>,
-    default: () => []
+const props = defineProps({
+  data: {
+    type: Object as PropType<WorkerMemoryInfo>,
+    default: () => ({})
   }
 })
 
-const path = computed(() => {
-  return router.currentRoute.value.fullPath
+const tableData = computed(() => {
+  return objectToArray(props.data)
 })
 
-const updateValue = (value: string) => {
-  router.push(value)
-}
+const columns = [
+  {
+    title: 'Name',
+    key: 'key',
+    width: 150
+  },
+  {
+    title: 'Value',
+    key: 'value',
+    width: 150
+  }
+]
 </script>
 
 <template>
-  <n-menu :options="menus" :default-value="path" @update:value="updateValue" />
+  <n-data-table :columns="columns" :data="tableData" :pagination="false" />
 </template>
diff --git a/web/src/layouts/components/menus/index.vue 
b/web/src/views/worker/components/detail/WorkerDetailStorage.vue
similarity index 56%
copy from web/src/layouts/components/menus/index.vue
copy to web/src/views/worker/components/detail/WorkerDetailStorage.vue
index 70b876ee1..27875f44e 100644
--- a/web/src/layouts/components/menus/index.vue
+++ b/web/src/views/worker/components/detail/WorkerDetailStorage.vue
@@ -16,32 +16,48 @@
 * limitations under the License.
 -->
 
-<script setup lang="ts">
-import type { MenuOption } from 'naive-ui'
+<script lang="ts" setup>
+import type { WorkerDiskInfo } from '@/api'
+import type { DataTableColumns } from 'naive-ui'
 import type { PropType } from 'vue'
 
-const router = useRouter()
-
-defineOptions({
-  name: 'SiderMenu'
-})
-
 defineProps({
-  menus: {
-    type: Array as PropType<MenuOption[]>,
+  data: {
+    type: Array as PropType<WorkerDiskInfo[]>,
     default: () => []
   }
 })
 
-const path = computed(() => {
-  return router.currentRoute.value.fullPath
-})
-
-const updateValue = (value: string) => {
-  router.push(value)
-}
+const columns: DataTableColumns<WorkerDiskInfo> = [
+  {
+    title: 'DiskInfo',
+    key: 'index',
+    render: (_, index) => {
+      return `DiskInfo${index + 1}`
+    }
+  },
+  {
+    title: 'MountPoint',
+    key: 'mountPoint'
+  },
+  {
+    title: 'TotalSpace',
+    key: 'totalSpace'
+  },
+  {
+    title: 'UsedSpace',
+    key: 'usedSpace'
+  },
+  {
+    title: 'UsedPercent',
+    key: 'usedPercent'
+  },
+  {
+    title: 'DiskType',
+    key: 'diskType'
+  }
+]
 </script>
-
 <template>
-  <n-menu :options="menus" :default-value="path" @update:value="updateValue" />
+  <n-data-table scroll-x="800" :columns="columns" :data="data" 
:pagination="false" />
 </template>
diff --git a/web/src/api/models/worker/index.ts 
b/web/src/views/worker/components/detail/index.ts
similarity index 67%
copy from web/src/api/models/worker/index.ts
copy to web/src/views/worker/components/detail/index.ts
index 0934472f3..ea2ac2e75 100644
--- a/web/src/api/models/worker/index.ts
+++ b/web/src/views/worker/components/detail/index.ts
@@ -15,16 +15,14 @@
  * limitations under the License.
  */
 
-import { api } from '@/api/request'
-import type { PaginationType } from '@/api/types'
-import type { Worker, WorkerOverview } from './types'
+import WorkerDetailConfigService from './WorkerDetailConfig.vue'
+import WorkerDetailLogFileService from './WorkerDetailLogFile.vue'
+import WorkerDetailMemoryService from './WorkerDetailMemory.vue'
+import WorkerDetailStorageService from './WorkerDetailStorage.vue'
 
-export * from './types'
-
-export const getWorkerOverview = () => {
-  return api<WorkerOverview>('/worker/overview', 'get')
-}
-
-export const getWorkerList = () => {
-  return api<PaginationType<{ workerInfos: Worker[] }>>('/worker/list', 'get')
+export {
+  WorkerDetailConfigService,
+  WorkerDetailLogFileService,
+  WorkerDetailMemoryService,
+  WorkerDetailStorageService
 }
diff --git a/web/src/views/worker/components/index.ts 
b/web/src/views/worker/components/index.ts
index 843b8d446..bc843c4c9 100644
--- a/web/src/views/worker/components/index.ts
+++ b/web/src/views/worker/components/index.ts
@@ -15,7 +15,16 @@
  * limitations under the License.
  */
 
+import WorkerDetailContentService from './WorkerDetailContent.vue'
+import WorkerDetailOverviewService from './WorkerDetailOverview.vue'
 import WorkerFormService from './WorkerForm.vue'
 import WorkerTableService from './WorkerTable.vue'
 
-export { WorkerFormService, WorkerTableService }
+export * from './detail'
+
+export {
+  WorkerDetailContentService,
+  WorkerDetailOverviewService,
+  WorkerFormService,
+  WorkerTableService
+}
diff --git a/web/src/views/worker/detail.vue b/web/src/views/worker/detail.vue
new file mode 100644
index 000000000..504b149c7
--- /dev/null
+++ b/web/src/views/worker/detail.vue
@@ -0,0 +1,65 @@
+<!--
+* 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.
+-->
+
+<script lang="ts" setup>
+import { getApplicationList, getWorkerDetail, type ApplicationTab } from 
'@/api'
+import { usePagination } from '@/composables'
+import { useHasLoading } from '@varlet/axle/use'
+import { WorkerDetailContentService, WorkerDetailOverviewService } from 
'./components'
+
+defineOptions({
+  name: 'WorkerDetail'
+})
+
+const route = useRoute()
+const worker = route.query.worker as string
+
+const { data, loading: isDetailLoading } = getWorkerDetail().use({
+  params: { worker }
+})
+
+const {
+  data: appResponse,
+  loading: isAppListLoading,
+  getData: loadApplicationList
+} = getApplicationList().use({ immediate: false })
+
+const { pagination } = usePagination({
+  onLoadData: loadApplicationList,
+  params: {
+    worker
+  }
+})
+
+const application = computed<ApplicationTab>(() => {
+  return {
+    data: appResponse.value?.applicationInfos ?? [],
+    pagination: pagination.value
+  }
+})
+
+const loading = useHasLoading(isDetailLoading, isAppListLoading)
+</script>
+<template>
+  <n-spin :show="loading">
+    <n-flex :style="{ gap: '24px' }" vertical>
+      <WorkerDetailOverviewService :data="data" />
+      <WorkerDetailContentService :data="data" :application="application" />
+    </n-flex>
+  </n-spin>
+</template>


Reply via email to