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

wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-booster-ui.git


The following commit(s) were added to refs/heads/main by this push:
     new 9c0bb98  feat: support the process dashboard and create the time range 
text widget (#138)
9c0bb98 is described below

commit 9c0bb988e6f80d0c3492a1cd47547de7c1fa3150
Author: Fine0830 <[email protected]>
AuthorDate: Mon Aug 15 16:49:00 2022 +0800

    feat: support the process dashboard and create the time range text widget 
(#138)
---
 src/assets/icons/control.svg                       |   2 +-
 src/assets/icons/{control.svg => library_add.svg}  |   4 +-
 src/assets/icons/time_range.svg                    |  15 +
 src/graphql/fragments/ebpf.ts                      |  26 +-
 src/graphql/fragments/selector.ts                  |  44 +++
 src/graphql/fragments/topology.ts                  |  23 ++
 src/graphql/query/ebpf.ts                          |   6 +
 src/graphql/query/selector.ts                      |   4 +
 src/graphql/query/topology.ts                      |   2 +
 src/hooks/data.ts                                  |   2 +-
 src/hooks/useProcessor.ts                          |   9 +-
 src/locales/lang/en.ts                             |   1 +
 src/locales/lang/es.ts                             |   1 +
 src/locales/lang/zh.ts                             |   1 +
 src/router/dashboard.ts                            |  33 +-
 src/store/data.ts                                  |   8 +
 src/store/modules/dashboard.ts                     |  34 +-
 src/store/modules/ebpf.ts                          |  24 +-
 src/store/modules/selectors.ts                     |  62 +++-
 src/types/ebpf.d.ts                                |  24 +-
 src/types/selector.d.ts                            |  13 +
 src/views/dashboard/configuration/TimeRange.vue    | 161 ++++++++++
 src/views/dashboard/configuration/index.ts         |   2 +
 .../configuration/widget/metric/Index.vue          |  14 +-
 src/views/dashboard/controls/Tab.vue               |  20 +-
 src/views/dashboard/controls/TimeRange.vue         | 187 +++++++++++
 src/views/dashboard/controls/Widget.vue            |  13 +-
 src/views/dashboard/controls/index.ts              |   2 +
 src/views/dashboard/controls/{index.ts => tab.ts}  |   4 +-
 src/views/dashboard/data.ts                        |   7 +
 .../{configuration/index.ts => graphs/topology.ts} |  20 +-
 src/views/dashboard/panel/Tool.vue                 | 344 +++++++++++++++------
 .../utils => components/D3Graph}/linkElement.ts    |   0
 .../utils => components/D3Graph}/nodeElement.ts    |   2 +-
 .../utils => components/D3Graph}/simulation.ts     |   0
 .../{topology/utils => components/D3Graph}/zoom.ts |   0
 .../dashboard/related/components/TaskDetails.vue   |  82 +++++
 src/views/dashboard/related/demand-log/Header.vue  |   1 -
 src/views/dashboard/related/ebpf/Header.vue        |   5 +-
 .../related/ebpf/components/EBPFSchedules.vue      |   7 +-
 .../dashboard/related/ebpf/components/TaskList.vue |  56 +---
 src/views/dashboard/related/event/Header.vue       |   4 +-
 src/views/dashboard/related/log/Header.vue         |   2 +-
 .../related/topology/components/Graph.vue          |  15 +-
 44 files changed, 1062 insertions(+), 224 deletions(-)

diff --git a/src/assets/icons/control.svg b/src/assets/icons/control.svg
index 250d63b..8ecf7f5 100644
--- a/src/assets/icons/control.svg
+++ b/src/assets/icons/control.svg
@@ -12,4 +12,4 @@ 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. -->
-<svg t="1648717513168" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="15451" width="48" height="48"><path 
d="M810.666667 
213.333333v597.333334H213.333333V213.333333h597.333334m85.333333-85.333333H128v768h768V128z
 m-512 170.666667h-85.333333v85.333333h85.333333z m170.666667 
0h-85.333334v85.333333h85.333334z m-170.666667 
170.666666h-85.333333v85.333334h85.333333z m170.666667 
0h-85.333334v85.333334h85.333334z m-170.666667 170.666667h-85.333333v85 [...]
\ No newline at end of file
+<svg t="1648717513168" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="15451" width="48" height="48"><path 
d="M810.666667 
213.333333v597.333334H213.333333V213.333333h597.333334m85.333333-85.333333H128v768h768V128z
 m-512 170.666667h-85.333333v85.333333h85.333333z m170.666667 
0h-85.333334v85.333333h85.333334z m-170.666667 
170.666666h-85.333333v85.333334h85.333333z m170.666667 
0h-85.333334v85.333334h85.333334z m-170.666667 170.666667h-85.333333v85 [...]
\ No newline at end of file
diff --git a/src/assets/icons/control.svg b/src/assets/icons/library_add.svg
similarity index 50%
copy from src/assets/icons/control.svg
copy to src/assets/icons/library_add.svg
index 250d63b..fb4b4f1 100644
--- a/src/assets/icons/control.svg
+++ b/src/assets/icons/library_add.svg
@@ -12,4 +12,6 @@ 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. -->
-<svg t="1648717513168" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="15451" width="48" height="48"><path 
d="M810.666667 
213.333333v597.333334H213.333333V213.333333h597.333334m85.333333-85.333333H128v768h768V128z
 m-512 170.666667h-85.333333v85.333333h85.333333z m170.666667 
0h-85.333334v85.333333h85.333334z m-170.666667 
170.666666h-85.333333v85.333334h85.333333z m170.666667 
0h-85.333334v85.333334h85.333334z m-170.666667 170.666667h-85.333333v85 [...]
\ No newline at end of file
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"; width="24" height="24" 
viewBox="0 0 24 24">
+<path d="M18.984 
11.016v-2.016h-3.984v-3.984h-2.016v3.984h-3.984v2.016h3.984v3.984h2.016v-3.984h3.984zM20.016
 2.016q0.797 0 1.383 0.586t0.586 1.383v12q0 0.797-0.586 1.406t-1.383 
0.609h-12q-0.797 0-1.406-0.609t-0.609-1.406v-12q0-0.797 
0.609-1.383t1.406-0.586h12zM3.984 6v14.016h14.016v1.969h-14.016q-0.797 
0-1.383-0.586t-0.586-1.383v-14.016h1.969z"></path>
+</svg>
diff --git a/src/assets/icons/time_range.svg b/src/assets/icons/time_range.svg
new file mode 100644
index 0000000..5b5238b
--- /dev/null
+++ b/src/assets/icons/time_range.svg
@@ -0,0 +1,15 @@
+<!-- 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. -->
+<svg t="1660294515307" class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="1020" width="48" height="48"><path 
d="M240 512h64c9.6 0 16-6.4 16-16v-32c0-9.6-6.4-16-16-16h-64c-9.6 0-16 6.4-16 
16v32c0 9.6 6.4 16 16 16z m160 0h384c9.6 0 16-6.4 
16-16v-32c0-9.6-6.4-16-16-16H400c-9.6 0-16 6.4-16 16v32c0 9.6 6.4 16 16 16z 
m384 256h-64c-9.6 0-16 6.4-16 16v32c0 9.6 6.4 16 16 16h64c9.6 0 16-6.4 
16-16v-32c0-9.6-6.4-16-16-16z" p-id="1021"></path><path d="M896 128 [...]
\ No newline at end of file
diff --git a/src/graphql/fragments/ebpf.ts b/src/graphql/fragments/ebpf.ts
index 920065d..d5ec74e 100644
--- a/src/graphql/fragments/ebpf.ts
+++ b/src/graphql/fragments/ebpf.ts
@@ -33,12 +33,15 @@ export const createEBPFTask = {
   }`,
 };
 export const queryEBPFTasks = {
-  variable: "$serviceId: ID!",
+  variable:
+    "$serviceId: ID, $serviceInstanceId: ID, $targets: 
[EBPFProfilingTargetType!]",
   query: `
-  queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId) {
+  queryEBPFTasks: queryEBPFProfilingTasks(serviceId: $serviceId, 
serviceInstanceId: $serviceInstanceId, targets: $targets) {
     taskId
     serviceName
     serviceId
+    serviceInstanceId
+    serviceInstanceName
     processLabels
     taskStartTime
     triggerType
@@ -90,3 +93,22 @@ export const analysisEBPFResult = {
     }
   }`,
 };
+
+export const createNetworkProfiling = {
+  variable: "$request: EBPFProfilingNetworkTaskRequest!",
+  query: `
+  createEBPFNetworkProfiling(request: $request) {
+    status
+    errorReason
+    id
+  }`,
+};
+
+export const keepNetworkProfiling = {
+  variable: "$taskId: ID!",
+  query: `
+  keepEBPFNetworkProfiling(taskId: $taskId) {
+    status
+    errorReason
+  }`,
+};
diff --git a/src/graphql/fragments/selector.ts 
b/src/graphql/fragments/selector.ts
index a5c7039..285ab3d 100644
--- a/src/graphql/fragments/selector.ts
+++ b/src/graphql/fragments/selector.ts
@@ -48,6 +48,29 @@ export const Instances = {
   }
   `,
 };
+
+export const Processes = {
+  variable: "$instanceId: ID!, $duration: Duration!",
+  query: `
+  processes: listProcesses(instanceId: $instanceId, duration: $duration) {
+    id
+    value: name
+    label: name
+    serviceId
+    serviceName
+    instanceId
+    instanceName
+    agentId
+    detectType
+    attributes {
+      name
+      value
+    }
+    labels
+  }
+`,
+};
+
 export const Endpoints = {
   variable: "$serviceId: ID!, $keyword: String!",
   query: `
@@ -102,3 +125,24 @@ export const getEndpoint = {
   }
   `,
 };
+
+export const getProcess = {
+  variable: "$processId: ID!",
+  query: `
+  process: getProcess(processId: $processId) {
+    id
+    value: name
+    label: name
+    serviceId
+    serviceName
+    instanceId
+    instanceName
+    agentId
+    detectType
+    attributes {
+      name
+      value
+    }
+  }
+  `,
+};
diff --git a/src/graphql/fragments/topology.ts 
b/src/graphql/fragments/topology.ts
index 7b84951..edbd738 100644
--- a/src/graphql/fragments/topology.ts
+++ b/src/graphql/fragments/topology.ts
@@ -75,3 +75,26 @@ export const InstanceTopology = {
   }
 `,
 };
+export const ProcessTopology = {
+  variable: "$serviceInstanceId: ID!, $duration: Duration!",
+  query: `
+  topology: getProcessTopology(serviceInstanceId: $serviceInstanceId,
+    duration: $duration) {
+    nodes {
+      id
+      name
+      isReal
+      serviceName
+      serviceId
+      serviceInstanceId
+      serviceInstanceName
+    }
+    calls {
+      id
+      source
+      detectPoints
+      target
+    }
+  }
+`,
+};
diff --git a/src/graphql/query/ebpf.ts b/src/graphql/query/ebpf.ts
index 131b57a..052fc7a 100644
--- a/src/graphql/query/ebpf.ts
+++ b/src/graphql/query/ebpf.ts
@@ -21,6 +21,8 @@ import {
   queryEBPFTasks,
   queryEBPFSchedules,
   analysisEBPFResult,
+  createNetworkProfiling,
+  keepNetworkProfiling,
 } from "../fragments/ebpf";
 
 export const getCreateTaskData = `query 
queryCreateTaskData(${queryCreateTaskData.variable}) 
{${queryCreateTaskData.query}}`;
@@ -32,3 +34,7 @@ export const getEBPFTasks = `query 
queryEBPFTasks(${queryEBPFTasks.variable}) {$
 export const getEBPFSchedules = `query 
queryEBPFSchedules(${queryEBPFSchedules.variable}) 
{${queryEBPFSchedules.query}}`;
 
 export const getEBPFResult = `query 
analysisEBPFResult(${analysisEBPFResult.variable}) 
{${analysisEBPFResult.query}}`;
+
+export const newNetworkProfiling = `mutation 
createNetworkProfiling(${createNetworkProfiling.variable}) 
{${createNetworkProfiling.query}}`;
+
+export const aliveNetworkProfiling = `mutation 
keepNetworkProfiling(${keepNetworkProfiling.variable}) 
{${keepNetworkProfiling.query}}`;
diff --git a/src/graphql/query/selector.ts b/src/graphql/query/selector.ts
index cbbccc3..dc8d446 100644
--- a/src/graphql/query/selector.ts
+++ b/src/graphql/query/selector.ts
@@ -22,6 +22,8 @@ import {
   getService,
   getInstance,
   getEndpoint,
+  Processes,
+  getProcess,
 } from "../fragments/selector";
 
 export const queryServices = `query queryServices(${Services.variable}) 
{${Services.query}}`;
@@ -31,3 +33,5 @@ export const queryLayers = `query listLayer 
{${Layers.query}}`;
 export const queryService = `query queryService(${getService.variable}) 
{${getService.query}}`;
 export const queryInstance = `query queryInstance(${getInstance.variable}) 
{${getInstance.query}}`;
 export const queryEndpoint = `query queryInstance(${getEndpoint.variable}) 
{${getEndpoint.query}}`;
+export const queryProcesses = `query queryProcesses(${Processes.variable}) 
{${Processes.query}}`;
+export const queryProcess = `query queryProcess(${getProcess.variable}) 
{${getProcess.query}}`;
diff --git a/src/graphql/query/topology.ts b/src/graphql/query/topology.ts
index 705d94b..bb5333b 100644
--- a/src/graphql/query/topology.ts
+++ b/src/graphql/query/topology.ts
@@ -18,8 +18,10 @@ import {
   InstanceTopology,
   EndpointTopology,
   ServicesTopology,
+  ProcessTopology,
 } from "../fragments/topology";
 
 export const getInstanceTopology = `query 
queryData(${InstanceTopology.variable}) {${InstanceTopology.query}}`;
 export const getEndpointTopology = `query 
queryData(${EndpointTopology.variable}) {${EndpointTopology.query}}`;
 export const getServicesTopology = `query 
queryData(${ServicesTopology.variable}) {${ServicesTopology.query}}`;
+export const getProcessTopology = `query 
queryData(${ProcessTopology.variable}) {${ProcessTopology.query}}`;
diff --git a/src/hooks/data.ts b/src/hooks/data.ts
index f683cf8..73eef8d 100644
--- a/src/hooks/data.ts
+++ b/src/hooks/data.ts
@@ -32,7 +32,7 @@ export enum Calculations {
   Precision = "precision",
   ConvertSeconds = "convertSeconds",
   ConvertMilliseconds = "convertMilliseconds",
-  MsTos = "msTos",
+  MsToS = "msTos",
   Average = "average",
   PercentageAvg = "percentageAvg",
   ApdexAvg = "apdexAvg",
diff --git a/src/hooks/useProcessor.ts b/src/hooks/useProcessor.ts
index ae9d914..c764d42 100644
--- a/src/hooks/useProcessor.ts
+++ b/src/hooks/useProcessor.ts
@@ -383,19 +383,16 @@ export function aggregation(
     case Calculations.Apdex:
       data = val / 10000;
       break;
-    case Calculations.ApdexAvg:
-      data = val / 10000;
-      break;
     case Calculations.ConvertSeconds:
-      data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
+      data = dayjs(val * 1000).format("YYYY-MM-DD HH:mm:ss");
       break;
     case Calculations.ConvertMilliseconds:
-      data = dayjs.unix(val).format("YYYY-MM-DD HH:mm:ss");
+      data = dayjs(val).format("YYYY-MM-DD HH:mm:ss");
       break;
     case Calculations.Precision:
       data = data.toFixed(2);
       break;
-    case Calculations.MsTos:
+    case Calculations.MsToS:
       data = (val / 1000).toFixed(2);
       break;
     case Calculations.SecondToDay:
diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts
index 5125601..cfc9921 100644
--- a/src/locales/lang/en.ts
+++ b/src/locales/lang/en.ts
@@ -149,6 +149,7 @@ const msg = {
     "The name only supports Chinese and English, horizontal lines and 
underscores. The length of the name is limited to 300 characters",
   duplicateName: "Duplicate name",
   enableAssociate: "Enable association",
+  text: "Text",
   seconds: "Seconds",
   hourTip: "Select Hour",
   minuteTip: "Select Minute",
diff --git a/src/locales/lang/es.ts b/src/locales/lang/es.ts
index 5f54bc3..fc70298 100644
--- a/src/locales/lang/es.ts
+++ b/src/locales/lang/es.ts
@@ -145,6 +145,7 @@ const msg = {
   begin: "Inicio",
   associateOptions: "Opciones de asociación",
   widget: "Dispositivo pequeño",
+  text: "Texto",
   duplicateName: "Nombre duplicado",
   nameTip:
     "El nombre sólo admite chino e inglés, líneas horizontales y subrayado, y 
la longitud del nombre no excederá de 300 caracteres",
diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts
index dc77675..1713244 100644
--- a/src/locales/lang/zh.ts
+++ b/src/locales/lang/zh.ts
@@ -146,6 +146,7 @@ const msg = {
   enableAssociate: "启用关联",
   nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
   duplicateName: "重复的名称",
+  text: "文本",
   seconds: "秒",
   hourTip: "选择小时",
   minuteTip: "选择分钟",
diff --git a/src/router/dashboard.ts b/src/router/dashboard.ts
index c985e2f..3fe651f 100644
--- a/src/router/dashboard.ts
+++ b/src/router/dashboard.ts
@@ -182,7 +182,7 @@ export const routesDashboard: Array<RouteRecordRaw> = [
           import(
             /* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
           ),
-        name: "ViewPodRelation",
+        name: "PodRelation",
         meta: {
           notShow: true,
         },
@@ -205,6 +205,37 @@ export const routesDashboard: Array<RouteRecordRaw> = [
           },
         ],
       },
+      {
+        path: "",
+        redirect:
+          
"/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
+        component: () =>
+          import(
+            /* webpackChunkName: "dashboards" */ "@/views/dashboard/Edit.vue"
+          ),
+        name: "ProcessRelation",
+        meta: {
+          notShow: true,
+        },
+        children: [
+          {
+            path: 
"/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name",
+            component: () =>
+              import(
+                /* webpackChunkName: "dashboards" */ 
"@/views/dashboard/Edit.vue"
+              ),
+            name: "ViewProcessRelation",
+          },
+          {
+            path: 
"/dashboard/:layerId/:entity/:serviceId/:podId/:processId/:destServiceId/:destPodId/:destProcessId/:name/tab/:activeTabIndex",
+            component: () =>
+              import(
+                /* webpackChunkName: "dashboards" */ 
"@/views/dashboard/Edit.vue"
+              ),
+            name: "ViewProcessRelationActiveTabIndex",
+          },
+        ],
+      },
     ],
   },
 ];
diff --git a/src/store/data.ts b/src/store/data.ts
index d6d3e01..ce31dd2 100644
--- a/src/store/data.ts
+++ b/src/store/data.ts
@@ -29,3 +29,11 @@ export const TextConfig = {
   fontSize: 14,
   textAlign: "left",
 };
+
+export const TimeRangeConfig = {
+  fontColor: "black",
+  backgroundColor: "white",
+  fontSize: 14,
+  textAlign: "center",
+  text: "text",
+};
diff --git a/src/store/modules/dashboard.ts b/src/store/modules/dashboard.ts
index 450c237..bad5e8e 100644
--- a/src/store/modules/dashboard.ts
+++ b/src/store/modules/dashboard.ts
@@ -21,7 +21,7 @@ import graphql from "@/graphql";
 import query from "@/graphql/fetch";
 import { DashboardItem } from "@/types/dashboard";
 import { useSelectorStore } from "@/store/modules/selectors";
-import { NewControl, TextConfig } from "../data";
+import { NewControl, TextConfig, TimeRangeConfig } from "../data";
 import { AxiosResponse } from "axios";
 import { ElMessage } from "element-plus";
 import { useI18n } from "vue-i18n";
@@ -113,13 +113,27 @@ export const dashboardStore = defineStore({
               : 3,
         };
       }
-      if (["Trace", "Profile", "Log", "DemandLog", "Ebpf"].includes(type)) {
+      if (
+        [
+          "Trace",
+          "Profile",
+          "Log",
+          "DemandLog",
+          "Ebpf",
+          "NetworkProfiling",
+        ].includes(type)
+      ) {
         newItem.h = 36;
       }
       if (type === "Text") {
         newItem.h = 6;
         newItem.graph = TextConfig;
       }
+      if (type === "TimeRange") {
+        newItem.w = 8;
+        newItem.h = 6;
+        newItem.graph = TimeRangeConfig;
+      }
       this.activedGridItem = newItem.i;
       this.selectedGrid = newItem;
       this.layout = this.layout.map((d: LayoutConfig) => {
@@ -170,13 +184,27 @@ export const dashboardStore = defineStore({
           showDepth: true,
         };
       }
-      if (["Trace", "Profile", "Log", "DemandLog", "Ebpf"].includes(type)) {
+      if (
+        [
+          "Trace",
+          "Profile",
+          "Log",
+          "DemandLog",
+          "Ebpf",
+          "NetworkProfiling",
+        ].includes(type)
+      ) {
         newItem.h = 32;
       }
       if (type === "Text") {
         newItem.h = 6;
         newItem.graph = TextConfig;
       }
+      if (type === "TimeRange") {
+        newItem.w = 8;
+        newItem.h = 6;
+        newItem.graph = TextConfig;
+      }
       if (this.layout[idx].children) {
         const items = children.map((d: LayoutConfig) => {
           d.y = d.y + newItem.h;
diff --git a/src/store/modules/ebpf.ts b/src/store/modules/ebpf.ts
index 987422b..e77e570 100644
--- a/src/store/modules/ebpf.ts
+++ b/src/store/modules/ebpf.ts
@@ -25,8 +25,7 @@ import {
 import { store } from "@/store";
 import graphql from "@/graphql";
 import { AxiosResponse } from "axios";
-
-interface EbpfStore {
+interface EbpfState {
   taskList: EBPFTaskList[];
   eBPFSchedules: EBPFProfilingSchedule[];
   currentSchedule: EBPFProfilingSchedule | Record<string, never>;
@@ -40,7 +39,7 @@ interface EbpfStore {
 
 export const ebpfStore = defineStore({
   id: "eBPF",
-  state: (): EbpfStore => ({
+  state: (): EbpfState => ({
     taskList: [],
     eBPFSchedules: [],
     currentSchedule: {},
@@ -53,7 +52,7 @@ export const ebpfStore = defineStore({
   }),
   actions: {
     setSelectedTask(task: EBPFTaskList) {
-      this.selectedTask = task;
+      this.selectedTask = task || {};
     },
     setCurrentSchedule(s: EBPFProfilingSchedule) {
       this.currentSchedule = s;
@@ -84,22 +83,31 @@ export const ebpfStore = defineStore({
       if (res.data.errors) {
         return res.data;
       }
-      this.getTaskList(param.serviceId);
+      this.getTaskList({
+        serviceId: param.serviceId,
+        targets: ["ON_CPU", "OFF_CPU"],
+      });
       return res.data;
     },
-    async getTaskList(serviceId: string) {
-      if (!serviceId) {
+    async getTaskList(params: {
+      serviceId: string;
+      serviceInstanceId: string;
+      targets: string[];
+    }) {
+      if (!params.serviceId) {
         return new Promise((resolve) => resolve({}));
       }
       const res: AxiosResponse = await graphql
         .query("getEBPFTasks")
-        .params({ serviceId });
+        .params(params);
 
       this.tip = "";
       if (res.data.errors) {
         return res.data;
       }
       this.taskList = res.data.data.queryEBPFTasks || [];
+      this.selectedTask = this.taskList[0] || {};
+      this.setSelectedTask(this.selectedTask);
       if (!this.taskList.length) {
         return res.data;
       }
diff --git a/src/store/modules/selectors.ts b/src/store/modules/selectors.ts
index f393052..2e671d8 100644
--- a/src/store/modules/selectors.ts
+++ b/src/store/modules/selectors.ts
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 import { defineStore } from "pinia";
-import { Service, Instance, Endpoint } from "@/types/selector";
+import { Service, Instance, Endpoint, Process } from "@/types/selector";
 import { store } from "@/store";
 import graphql from "@/graphql";
 import { AxiosResponse } from "axios";
@@ -24,11 +24,15 @@ interface SelectorState {
   services: Service[];
   destServices: Service[];
   pods: Array<Instance | Endpoint>;
+  processes: Process[];
+  destProcesses: Process[];
   currentService: Nullable<Service>;
   currentPod: Nullable<Instance | Endpoint>;
+  currentProcess: Nullable<Process>;
   currentDestService: Nullable<Service>;
   currentDestPod: Nullable<Instance | Endpoint>;
   destPods: Array<Instance | Endpoint>;
+  currentDestProcess: Nullable<Process>;
 }
 
 export const selectorStore = defineStore({
@@ -38,10 +42,14 @@ export const selectorStore = defineStore({
     destServices: [],
     pods: [],
     destPods: [],
+    processes: [],
+    destProcesses: [],
     currentService: null,
     currentPod: null,
+    currentProcess: null,
     currentDestService: null,
     currentDestPod: null,
+    currentDestProcess: null,
   }),
   actions: {
     setCurrentService(service: Nullable<Service>) {
@@ -56,6 +64,18 @@ export const selectorStore = defineStore({
     setCurrentDestPod(pod: Nullable<Instance | Endpoint>) {
       this.currentDestPod = pod;
     },
+    setCurrentProcess(process: Nullable<Process>) {
+      this.currentProcess = process;
+    },
+    setCurrentDestProcess(process: Nullable<Process>) {
+      this.currentDestProcess = process;
+    },
+    setDestPods(pods: Array<Instance | Endpoint>) {
+      this.destPods = pods;
+    },
+    setDestProcesses(processes: Array<Process>) {
+      this.destProcesses = processes;
+    },
     async fetchLayers(): Promise<AxiosResponse> {
       const res: AxiosResponse = await graphql.query("queryLayers").params({});
 
@@ -93,6 +113,27 @@ export const selectorStore = defineStore({
       }
       return res.data;
     },
+    async getProcesses(param?: {
+      instanceId: string;
+      isRelation: boolean;
+    }): Promise<Nullable<AxiosResponse>> {
+      const instanceId = param ? param.instanceId : this.currentPod?.id;
+      if (!instanceId) {
+        return null;
+      }
+      const res: AxiosResponse = await graphql.query("queryProcesses").params({
+        instanceId,
+        duration: useAppStoreWithOut().durationTime,
+      });
+      if (!res.data.errors) {
+        if (param && param.isRelation) {
+          this.destProcesses = res.data.data.processes || [];
+          return res.data;
+        }
+        this.processes = res.data.data.processes || [];
+      }
+      return res.data;
+    },
     async getEndpoints(params: {
       keyword?: string;
       serviceId?: string;
@@ -176,6 +217,25 @@ export const selectorStore = defineStore({
         this.pods = [res.data.data.endpoint];
       }
 
+      return res.data;
+    },
+    async getProcess(instanceId: string, isRelation?: boolean) {
+      if (!instanceId) {
+        return;
+      }
+      const res: AxiosResponse = await graphql.query("queryProcess").params({
+        instanceId,
+      });
+      if (!res.data.errors) {
+        if (isRelation) {
+          this.currentDestProcess = res.data.data.process || null;
+          this.destProcesses = [res.data.data.process];
+          return;
+        }
+        this.currentProcess = res.data.data.process || null;
+        this.processes = [res.data.data.process];
+      }
+
       return res.data;
     },
   },
diff --git a/src/types/ebpf.d.ts b/src/types/ebpf.d.ts
index 4085207..5dfca0e 100644
--- a/src/types/ebpf.d.ts
+++ b/src/types/ebpf.d.ts
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+import { Process } from "./selector";
 export interface EBPFTaskCreationRequest {
   serviceId: string;
   processLabels: string[];
@@ -43,18 +44,7 @@ export interface EBPFProfilingSchedule {
   startTime: number;
 }
 
-export type Process = {
-  id: string;
-  name: string;
-  serviceId: string;
-  serviceName: string;
-  instanceId: string;
-  instanceName: string;
-  agentId: string;
-  detectType: string;
-  attributes: { name: string; value: string }[];
-  labels: string[];
-};
+export type Process = Process;
 export type StackElement = {
   id: string;
   originId: string;
@@ -75,3 +65,13 @@ export type AnalyzationTrees = {
   dumpCount: number;
   stackType: string;
 };
+export type ProcessNode = {
+  id: string;
+  name: string;
+  serviceId: string;
+  serviceName: string;
+  serviceInstanceId: string;
+  serviceInstanceName: string;
+  name: string;
+  isReal: boolean;
+};
diff --git a/src/types/selector.d.ts b/src/types/selector.d.ts
index 293ef46..fe4dd16 100644
--- a/src/types/selector.d.ts
+++ b/src/types/selector.d.ts
@@ -46,3 +46,16 @@ export type Service = {
   layers: string[];
   shortName: string;
 };
+
+export type Process = {
+  id: string;
+  name: string;
+  serviceId: string;
+  serviceName: string;
+  instanceId: string;
+  instanceName: string;
+  agentId: string;
+  detectType: string;
+  attributes: { name: string; value: string }[];
+  labels: string[];
+};
diff --git a/src/views/dashboard/configuration/TimeRange.vue 
b/src/views/dashboard/configuration/TimeRange.vue
new file mode 100644
index 0000000..13a493c
--- /dev/null
+++ b/src/views/dashboard/configuration/TimeRange.vue
@@ -0,0 +1,161 @@
+<!-- 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 class="item">
+    <span class="label">{{ t("text") }}</span>
+    <el-input
+      class="input"
+      v-model="text"
+      size="small"
+      @change="changeConfig({ text })"
+    />
+  </div>
+  <div class="item">
+    <span class="label">{{ t("textAlign") }}</span>
+    <Selector
+      :value="textAlign"
+      :options="AlignStyle"
+      size="small"
+      placeholder="Select a color"
+      class="input"
+      @change="changeConfig({ textAlign: $event[0].value })"
+    />
+  </div>
+  <div class="item">
+    <span class="label">{{ t("backgroundColors") }}</span>
+    <Selector
+      :value="backgroundColor"
+      :options="Colors"
+      size="small"
+      placeholder="Select a color"
+      class="input"
+      @change="changeConfig({ backgroundColor: $event[0].value })"
+    />
+  </div>
+  <div class="item">
+    <span class="label">{{ t("fontSize") }}</span>
+    <el-slider
+      class="slider"
+      v-model="fontSize"
+      show-input
+      input-size="small"
+      :min="12"
+      :max="30"
+      :step="1"
+      @change="changeConfig({ fontSize })"
+    />
+  </div>
+  <div class="item">
+    <span class="label">{{ t("fontColors") }}</span>
+    <Selector
+      :value="fontColor"
+      :options="Colors"
+      size="small"
+      placeholder="Select a color"
+      class="input"
+      @change="changeConfig({ fontColor: $event[0].value })"
+    />
+  </div>
+  <div class="footer">
+    <el-button size="small" @click="cancelConfig">
+      {{ t("cancel") }}
+    </el-button>
+    <el-button size="small" type="primary" @click="applyConfig">
+      {{ t("apply") }}
+    </el-button>
+  </div>
+</template>
+<script lang="ts" setup>
+import { useI18n } from "vue-i18n";
+import { ref } from "vue";
+import { useDashboardStore } from "@/store/modules/dashboard";
+
+const { t } = useI18n();
+const dashboardStore = useDashboardStore();
+const originConfig = dashboardStore.selectedGrid;
+const graph = originConfig.graph || {};
+const backgroundColor = ref(graph.backgroundColor || "green");
+const fontColor = ref(graph.fontColor || "white");
+const fontSize = ref<number>(graph.fontSize || 12);
+const textAlign = ref(graph.textAlign || "left");
+const text = ref<string>(graph.text || "");
+const Colors = [
+  {
+    label: "Green",
+    value: "green",
+  },
+  { label: "Blue", value: "blue" },
+  { label: "Red", value: "red" },
+  { label: "Grey", value: "grey" },
+  { label: "White", value: "white" },
+  { label: "Black", value: "black" },
+  { label: "Orange", value: "orange" },
+];
+const AlignStyle = [
+  {
+    label: "Left",
+    value: "left",
+  },
+  { label: "Center", value: "center" },
+  { label: "Right", value: "right" },
+];
+function changeConfig(param: { [key: string]: unknown }) {
+  const { selectedGrid } = dashboardStore;
+  const graph = {
+    ...selectedGrid.graph,
+    ...param,
+  };
+  dashboardStore.selectWidget({ ...selectedGrid, graph });
+}
+function applyConfig() {
+  dashboardStore.setConfigPanel(false);
+  dashboardStore.setConfigs(dashboardStore.selectedGrid);
+}
+
+function cancelConfig() {
+  dashboardStore.selectWidget(originConfig);
+  dashboardStore.setConfigPanel(false);
+}
+</script>
+<style lang="scss" scoped>
+.slider {
+  width: 500px;
+  margin-top: -3px;
+}
+
+.label {
+  font-size: 13px;
+  font-weight: 500;
+  display: block;
+  margin-bottom: 5px;
+}
+
+.input {
+  width: 500px;
+}
+
+.item {
+  margin-bottom: 10px;
+}
+
+.footer {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  border-top: 1px solid #eee;
+  padding: 10px;
+  text-align: right;
+  width: 100%;
+  background-color: #fff;
+}
+</style>
diff --git a/src/views/dashboard/configuration/index.ts 
b/src/views/dashboard/configuration/index.ts
index 6d39f19..0055f82 100644
--- a/src/views/dashboard/configuration/index.ts
+++ b/src/views/dashboard/configuration/index.ts
@@ -19,10 +19,12 @@ import Text from "./Text.vue";
 import Widget from "./Widget.vue";
 import Topology from "./Topology.vue";
 import Event from "./Event.vue";
+import TimeRange from "./TimeRange.vue";
 
 export default {
   Text,
   Widget,
   Topology,
   Event,
+  TimeRange,
 };
diff --git a/src/views/dashboard/configuration/widget/metric/Index.vue 
b/src/views/dashboard/configuration/widget/metric/Index.vue
index 3a63c06..233eb8a 100644
--- a/src/views/dashboard/configuration/widget/metric/Index.vue
+++ b/src/views/dashboard/configuration/widget/metric/Index.vue
@@ -179,12 +179,16 @@ const setVisTypes = computed(() => {
 
 async function setMetricType(chart?: any) {
   const g = chart || dashboardStore.selectedGrid.graph || {};
-  const json = await dashboardStore.fetchMetricList();
-  if (json.errors) {
-    ElMessage.error(json.errors);
-    return;
+  let arr: any[] = states.metricList;
+  if (!chart) {
+    const json = await dashboardStore.fetchMetricList();
+    if (json.errors) {
+      ElMessage.error(json.errors);
+      return;
+    }
+    arr = json.data.metrics;
   }
-  states.metricList = (json.data.metrics || []).filter(
+  states.metricList = (arr || []).filter(
     (d: { catalog: string; type: string }) => {
       if (states.isList) {
         if (d.type === MetricsType.REGULAR_VALUE) {
diff --git a/src/views/dashboard/controls/Tab.vue 
b/src/views/dashboard/controls/Tab.vue
index 370b28a..2188862 100644
--- a/src/views/dashboard/controls/Tab.vue
+++ b/src/views/dashboard/controls/Tab.vue
@@ -110,16 +110,8 @@ import { useRoute } from "vue-router";
 import type { PropType } from "vue";
 import { LayoutConfig } from "@/types/dashboard";
 import { useDashboardStore } from "@/store/modules/dashboard";
-import Topology from "./Topology.vue";
-import Widget from "./Widget.vue";
-import Trace from "./Trace.vue";
-import Profile from "./Profile.vue";
-import Log from "./Log.vue";
-import Text from "./Text.vue";
-import Ebpf from "./Ebpf.vue";
-import Event from "./Event.vue";
+import controls from "./tab";
 import { dragIgnoreFrom } from "../data";
-import DemandLog from "./DemandLog.vue";
 import copy from "@/utils/copy";
 
 const props = {
@@ -132,15 +124,7 @@ const props = {
 export default defineComponent({
   name: "Tab",
   components: {
-    Topology,
-    Widget,
-    Trace,
-    Profile,
-    Log,
-    Text,
-    Ebpf,
-    DemandLog,
-    Event,
+    ...controls,
   },
   props,
   setup(props) {
diff --git a/src/views/dashboard/controls/TimeRange.vue 
b/src/views/dashboard/controls/TimeRange.vue
new file mode 100644
index 0000000..8832f9c
--- /dev/null
+++ b/src/views/dashboard/controls/TimeRange.vue
@@ -0,0 +1,187 @@
+<!-- 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 class="time-range">
+    <div class="header">
+      <el-popover
+        placement="bottom"
+        trigger="click"
+        :width="100"
+        v-if="dashboardStore.editMode"
+      >
+        <template #reference>
+          <span>
+            <Icon iconName="ellipsis_v" size="middle" class="operation" />
+          </span>
+        </template>
+        <div class="tools" @click="editConfig">
+          <span>{{ t("edit") }}</span>
+        </div>
+        <div class="tools" @click="removeTopo">
+          <span>{{ t("delete") }}</span>
+        </div>
+      </el-popover>
+    </div>
+    <div
+      class="body"
+      :style="{
+        backgroundColor: TextColors[graph.backgroundColor],
+        justifyContent: graph.textAlign,
+        color: TextColors[graph.fontColor],
+      }"
+    >
+      <span
+        class="mr-5"
+        :style="{
+          fontSize: graph.fontSize + 'px',
+        }"
+        >{{ graph.text }}
+      </span>
+      <Icon iconName="time_range" size="middle" />
+      <span
+        class="ml-5"
+        :style="{
+          fontSize: graph.fontSize + 'px',
+        }"
+      >
+        {{ content }}
+      </span>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import { computed } from "vue";
+import type { PropType } from "vue";
+import { useI18n } from "vue-i18n";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import { useAppStoreWithOut } from "@/store/modules/app";
+import { TextColors } from "@/views/dashboard/data";
+
+/*global defineProps */
+const props = defineProps({
+  data: {
+    type: Object as PropType<any>,
+    default: () => ({ graph: {} }),
+  },
+  activeIndex: { type: String, default: "" },
+});
+const { t } = useI18n();
+const graph = computed(() => props.data.graph || {});
+const dashboardStore = useDashboardStore();
+const appStore = useAppStoreWithOut();
+const content = computed(() => {
+  const text = [appStore.durationRow.start, appStore.durationRow.end]
+    .map((date: Date) => tf(date, "YYYY-MM-DD HH:mm"))
+    .join(` ~ `);
+  return text;
+});
+
+function removeTopo() {
+  dashboardStore.removeControls(props.data);
+}
+function editConfig() {
+  dashboardStore.setConfigPanel(true);
+  dashboardStore.selectWidget(props.data);
+}
+function tf(time: Date, format: any): string {
+  const local = {
+    dow: 1, // Monday is the first day of the week
+    hourTip: t("hourTip"), // tip of select hour
+    minuteTip: t("minuteTip"), // tip of select minute
+    secondTip: t("secondTip"), // tip of select second
+    yearSuffix: t("yearSuffix"), // format of head
+    monthsHead: t("monthsHead").split("_"), // months of head
+    months: t("months").split("_"), // months of panel
+    weeks: t("weeks").split("_"), // weeks
+    cancelTip: t("cancel"), // default text for cancel button
+    submitTip: t("confirm"), // default text for submit button
+    quarterHourCutTip: t("quarterHourCutTip"),
+    halfHourCutTip: t("halfHourCutTip"),
+    hourCutTip: t("hourCutTip"),
+    dayCutTip: t("dayCutTip"),
+    weekCutTip: t("weekCutTip"),
+    monthCutTip: t("monthCutTip"),
+  };
+  const year = time.getFullYear();
+  const month = time.getMonth();
+  const day = time.getDate();
+  const hours24 = time.getHours();
+  const hours = hours24 % 12 === 0 ? 12 : hours24 % 12;
+  const minutes = time.getMinutes();
+  const seconds = time.getSeconds();
+  const milliseconds = time.getMilliseconds();
+  const dd = (t: number) => `0${t}`.slice(-2);
+  const map: { [key: string]: string | number } = {
+    YYYY: year,
+    MM: dd(month + 1),
+    MMM: local.months[month],
+    MMMM: local.monthsHead[month],
+    M: month + 1,
+    DD: dd(day),
+    D: day,
+    HH: dd(hours24),
+    H: hours24,
+    hh: dd(hours),
+    h: hours,
+    mm: dd(minutes),
+    m: minutes,
+    ss: dd(seconds),
+    s: seconds,
+    S: milliseconds,
+  };
+  return format.replace(/Y+|M+|D+|H+|h+|m+|s+|S+/g, (str: string) => map[str]);
+}
+</script>
+<style lang="scss" scoped>
+.time-range {
+  font-size: 12px;
+  height: 100%;
+  position: relative;
+}
+
+.operation {
+  cursor: pointer;
+}
+
+.header {
+  position: absolute;
+  top: 5px;
+  right: 5px;
+}
+
+.body {
+  padding: 0 20px 0 10px;
+  width: 100%;
+  height: 100%;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  overflow: auto;
+  font-weight: bold;
+}
+
+.tools {
+  padding: 5px 0;
+  color: #999;
+  cursor: pointer;
+  position: relative;
+  text-align: center;
+
+  &:hover {
+    color: #409eff;
+    background-color: #eee;
+  }
+}
+</style>
diff --git a/src/views/dashboard/controls/Widget.vue 
b/src/views/dashboard/controls/Widget.vue
index c165389..ec58266 100644
--- a/src/views/dashboard/controls/Widget.vue
+++ b/src/views/dashboard/controls/Widget.vue
@@ -213,7 +213,10 @@ export default defineComponent({
     watch(
       () => [selectorStore.currentPod, selectorStore.currentDestPod],
       () => {
-        if (dashboardStore.entity === EntityType[0].value) {
+        if (
+          dashboardStore.entity === EntityType[0].value ||
+          dashboardStore.entity === EntityType[7].value
+        ) {
           return;
         }
         if (isList.value) {
@@ -222,6 +225,14 @@ export default defineComponent({
         queryMetrics();
       }
     );
+    watch(
+      () => [selectorStore.currentProcess, selectorStore.currentDestProcess],
+      () => {
+        if (dashboardStore.entity === EntityType[7].value) {
+          queryMetrics();
+        }
+      }
+    );
     watch(
       () => appStore.durationTime,
       () => {
diff --git a/src/views/dashboard/controls/index.ts 
b/src/views/dashboard/controls/index.ts
index 8130b98..4c7754b 100644
--- a/src/views/dashboard/controls/index.ts
+++ b/src/views/dashboard/controls/index.ts
@@ -24,6 +24,7 @@ import Text from "./Text.vue";
 import Ebpf from "./Ebpf.vue";
 import DemandLog from "./DemandLog.vue";
 import Event from "./Event.vue";
+import TimeRange from "./TimeRange.vue";
 
 export default {
   Tab,
@@ -36,4 +37,5 @@ export default {
   Ebpf,
   DemandLog,
   Event,
+  TimeRange,
 };
diff --git a/src/views/dashboard/controls/index.ts 
b/src/views/dashboard/controls/tab.ts
similarity index 95%
copy from src/views/dashboard/controls/index.ts
copy to src/views/dashboard/controls/tab.ts
index 8130b98..07f07ab 100644
--- a/src/views/dashboard/controls/index.ts
+++ b/src/views/dashboard/controls/tab.ts
@@ -15,7 +15,6 @@
  * limitations under the License.
  */
 import Topology from "./Topology.vue";
-import Tab from "./Tab.vue";
 import Widget from "./Widget.vue";
 import Trace from "./Trace.vue";
 import Profile from "./Profile.vue";
@@ -24,9 +23,9 @@ import Text from "./Text.vue";
 import Ebpf from "./Ebpf.vue";
 import DemandLog from "./DemandLog.vue";
 import Event from "./Event.vue";
+import TimeRange from "./TimeRange.vue";
 
 export default {
-  Tab,
   Widget,
   Trace,
   Topology,
@@ -36,4 +35,5 @@ export default {
   Ebpf,
   DemandLog,
   Event,
+  TimeRange,
 };
diff --git a/src/views/dashboard/data.ts b/src/views/dashboard/data.ts
index de15ef0..5e68d84 100644
--- a/src/views/dashboard/data.ts
+++ b/src/views/dashboard/data.ts
@@ -159,6 +159,7 @@ export const EntityType = [
     key: 4,
   },
   { value: "EndpointRelation", label: "Endpoint Relation", key: 4 },
+  { value: "ProcessRelation", label: "Process Relation", key: 5 },
 ];
 export const ListEntity: any = {
   InstanceList: EntityType[3].value,
@@ -207,6 +208,12 @@ export const EndpointTools = [
   { name: "assignment", content: "Add Log", id: "addLog" },
   { name: "event", content: "Add Event", id: "addEvent" },
 ];
+export const ProcessTools = [
+  { name: "playlist_add", content: "Add Widget", id: "addWidget" },
+  { name: "all_inbox", content: "Add Tab", id: "addTab" },
+  { name: "library_books", content: "Add Text", id: "addText" },
+  { name: "time_range", content: "Add Time Range Text", id: "addTimeRange" },
+];
 export const ServiceRelationTools = [
   { name: "playlist_add", content: "Add Widget", id: "addWidget" },
   { name: "all_inbox", content: "Add Tab", id: "addTab" },
diff --git a/src/views/dashboard/configuration/index.ts 
b/src/views/dashboard/graphs/topology.ts
similarity index 77%
copy from src/views/dashboard/configuration/index.ts
copy to src/views/dashboard/graphs/topology.ts
index 6d39f19..0b576ce 100644
--- a/src/views/dashboard/configuration/index.ts
+++ b/src/views/dashboard/graphs/topology.ts
@@ -15,14 +15,18 @@
  * limitations under the License.
  */
 
-import Text from "./Text.vue";
-import Widget from "./Widget.vue";
-import Topology from "./Topology.vue";
-import Event from "./Event.vue";
+import Area from "./Area.vue";
+import Line from "./Line.vue";
+import Bar from "./Bar.vue";
+import TopList from "./TopList.vue";
+import Table from "./Table.vue";
+import Card from "./Card.vue";
 
 export default {
-  Text,
-  Widget,
-  Topology,
-  Event,
+  Line,
+  Bar,
+  TopList,
+  Area,
+  Table,
+  Card,
 };
diff --git a/src/views/dashboard/panel/Tool.vue 
b/src/views/dashboard/panel/Tool.vue
index b240b93..87b483d 100644
--- a/src/views/dashboard/panel/Tool.vue
+++ b/src/views/dashboard/panel/Tool.vue
@@ -14,72 +14,98 @@ See the License for the specific language governing 
permissions and
 limitations under the License. -->
 <template>
   <div class="dashboard-tool flex-h">
-    <div class="flex-h">
-      <div class="selectors-item" v-if="key !== 10">
-        <span class="label">$Service</span>
-        <Selector
-          v-model="states.currentService"
-          :options="selectorStore.services"
-          size="small"
-          placeholder="Select a service"
-          @change="changeService"
-          class="selectors"
-        />
-      </div>
-      <div class="selectors-item" v-if="key === 3 || key === 4">
-        <span class="label">
-          {{
-            ["EndpointRelation", "Endpoint"].includes(dashboardStore.entity)
-              ? "$Endpoint"
-              : "$ServiceInstance"
-          }}
-        </span>
-        <Selector
-          v-model="states.currentPod"
-          :options="selectorStore.pods"
-          size="small"
-          placeholder="Select a data"
-          @change="changePods"
-          @query="searchPods"
-          class="selectorPod"
-          :isRemote="
-            ['EndpointRelation', 'Endpoint'].includes(dashboardStore.entity)
-          "
-        />
-      </div>
-      <div class="selectors-item" v-if="key === 2 || key === 4">
-        <span class="label">$DestinationService</span>
-        <Selector
-          v-model="states.currentDestService"
-          :options="selectorStore.destServices"
-          size="small"
-          placeholder="Select a service"
-          @change="changeDestService"
-          class="selectors"
-        />
+    <div :class="isRelation ? 'flex-v' : 'flex-h'">
+      <div class="flex-h">
+        <div class="selectors-item" v-if="key !== 10">
+          <span class="label">$Service</span>
+          <Selector
+            v-model="states.currentService"
+            :options="selectorStore.services"
+            size="small"
+            placeholder="Select a service"
+            @change="changeService"
+            class="selectors"
+          />
+        </div>
+        <div class="selectors-item" v-if="key === 3 || key === 4 || key === 5">
+          <span class="label">
+            {{
+              ["EndpointRelation", "Endpoint"].includes(dashboardStore.entity)
+                ? "$Endpoint"
+                : "$ServiceInstance"
+            }}
+          </span>
+          <Selector
+            v-model="states.currentPod"
+            :options="selectorStore.pods"
+            size="small"
+            placeholder="Select a data"
+            @change="changePods"
+            @query="searchPods"
+            class="selectorPod"
+            :isRemote="
+              ['EndpointRelation', 'Endpoint'].includes(dashboardStore.entity)
+            "
+          />
+        </div>
+        <div class="selectors-item" v-if="key === 5">
+          <span class="label"> $Process </span>
+          <Selector
+            v-model="states.currentProcess"
+            :options="selectorStore.processes"
+            size="small"
+            placeholder="Select a data"
+            @change="changeProcess"
+            class="selectors"
+          />
+        </div>
       </div>
-      <div class="selectors-item" v-if="key === 4">
-        <span class="label">
-          {{
-            dashboardStore.entity === "EndpointRelation"
-              ? "$DestinationEndpoint"
-              : "$DestinationServiceInstance"
-          }}
-        </span>
-        <Selector
-          v-model="states.currentDestPod"
-          :options="selectorStore.destPods"
-          size="small"
-          placeholder="Select a data"
-          @change="changeDestPods"
-          class="selectorPod"
-          @query="searchDestPods"
-          :isRemote="dashboardStore.entity === 'EndpointRelation'"
-        />
+      <div class="flex-h" :class="isRelation ? 'relation' : ''">
+        <div class="selectors-item" v-if="key === 2 || key === 4 || key === 5">
+          <span class="label">$DestinationService</span>
+          <Selector
+            v-model="states.currentDestService"
+            :options="selectorStore.destServices"
+            size="small"
+            placeholder="Select a service"
+            @change="changeDestService"
+            class="selectors"
+          />
+        </div>
+        <div class="selectors-item" v-if="key === 4 || key === 5">
+          <span class="label">
+            {{
+              dashboardStore.entity === "EndpointRelation"
+                ? "$DestinationEndpoint"
+                : "$DestinationServiceInstance"
+            }}
+          </span>
+          <Selector
+            v-model="states.currentDestPod"
+            :options="selectorStore.destPods"
+            size="small"
+            placeholder="Select a data"
+            @change="changeDestPods"
+            class="selectorPod"
+            @query="searchDestPods"
+            :isRemote="dashboardStore.entity === 'EndpointRelation'"
+          />
+        </div>
+        <div class="selectors-item" v-if="key === 5">
+          <span class="label"> $DestinationProcess </span>
+          <Selector
+            v-model="states.currentDestProcess"
+            :options="selectorStore.destProcesses"
+            size="small"
+            placeholder="Select a data"
+            @change="changeDestProcess"
+            class="selectors"
+          />
+        </div>
       </div>
     </div>
     <div class="flex-h tools" v-loading="loading" v-if="!appStore.isMobile">
-      <div class="tool-icons flex-h" v-if="dashboardStore.editMode">
+      <div class="tool-icons" v-if="dashboardStore.editMode">
         <el-dropdown content="Controls" placement="bottom" :persistent="false">
           <i>
             <Icon class="icon-btn" size="sm" iconName="control" />
@@ -133,6 +159,7 @@ import {
   EndpointRelationTools,
   InstanceRelationTools,
   ServiceRelationTools,
+  ProcessTools,
 } from "../data";
 import { useSelectorStore } from "@/store/modules/selectors";
 import { ElMessage } from "element-plus";
@@ -144,9 +171,8 @@ const dashboardStore = useDashboardStore();
 const selectorStore = useSelectorStore();
 const appStore = useAppStoreWithOut();
 const params = useRoute().params;
-const toolIcons = ref<{ name: string; content: string; id: string }[]>(
-  EndpointRelationTools
-);
+const toolIcons =
+  ref<{ name: string; content: string; id: string }[]>(AllTools);
 const loading = ref<boolean>(false);
 const states = reactive<{
   destService: string;
@@ -154,16 +180,20 @@ const states = reactive<{
   key: number;
   currentService: string;
   currentPod: string;
+  currentProcess: string;
   currentDestService: string;
   currentDestPod: string;
+  currentDestProcess: string;
 }>({
   destService: "",
   destPod: "",
   key: 0,
   currentService: "",
   currentPod: "",
+  currentProcess: "",
   currentDestService: "",
   currentDestPod: "",
+  currentDestProcess: "",
 });
 const key = computed(() => {
   const type = EntityType.find(
@@ -172,6 +202,14 @@ const key = computed(() => {
   return (type && type.key) || 0;
 });
 
+const isRelation = computed(() => {
+  return [
+    EntityType[7].value,
+    EntityType[6].value,
+    EntityType[5].value,
+  ].includes(dashboardStore.entity);
+});
+
 setCurrentDashboard();
 appStore.setEventStack([initSelector]);
 initSelector();
@@ -199,6 +237,7 @@ async function setSelector() {
       EntityType[3].value,
       EntityType[5].value,
       EntityType[6].value,
+      EntityType[7].value,
     ].includes(String(params.entity))
   ) {
     setSourceSelector();
@@ -252,6 +291,7 @@ async function setSourceSelector() {
   if (!(selectorStore.pods.length && selectorStore.pods[0])) {
     selectorStore.setCurrentPod(null);
     states.currentPod = "";
+    states.currentProcess = "";
     return;
   }
   const pod = params.podId || selectorStore.pods[0].id;
@@ -263,9 +303,25 @@ async function setSourceSelector() {
   } else {
     currentPod = selectorStore.pods.find((d: { id: string }) => d.id === pod);
   }
-  if (currentPod) {
-    selectorStore.setCurrentPod(currentPod);
-    states.currentPod = currentPod.label;
+  if (!currentPod) {
+    return;
+  }
+  selectorStore.setCurrentPod(currentPod);
+  states.currentPod = currentPod.label;
+  const process = params.processId || selectorStore.processes[0].id;
+  let currentProcess;
+  if (states.currentProcess) {
+    currentProcess = selectorStore.processes.find(
+      (d: { label: string }) => d.label === states.currentProcess
+    );
+  } else {
+    currentProcess = selectorStore.processes.find(
+      (d: { id: string }) => d.id === process
+    );
+  }
+  if (currentProcess) {
+    selectorStore.setCurrentProcess(currentProcess);
+    states.currentProcess = currentProcess.label;
   }
 }
 
@@ -293,9 +349,25 @@ async function setDestSelector() {
       (d: { id: string }) => d.id === destPod
     );
   }
-  if (currentDestPod) {
-    selectorStore.setCurrentDestPod(currentDestPod);
-    states.currentDestPod = currentDestPod.label;
+  if (!currentDestPod) {
+    return;
+  }
+  selectorStore.setCurrentDestPod(currentDestPod);
+  states.currentDestPod = currentDestPod.label;
+  const destProcess = params.destProcessId || 
selectorStore.destProcesses[0].id;
+  let currentDestProcess;
+  if (states.currentDestProcess) {
+    currentDestProcess = selectorStore.destProcesses.find(
+      (d: { label: string }) => d.label === states.currentProcess
+    );
+  } else {
+    currentDestProcess = selectorStore.destProcesses.find(
+      (d: { id: string }) => d.id === destProcess
+    );
+  }
+  if (currentDestProcess) {
+    selectorStore.setCurrentProcess(currentDestProcess);
+    states.currentProcess = currentDestProcess.label;
   }
 }
 
@@ -325,16 +397,21 @@ async function getServices() {
     );
   }
   selectorStore.setCurrentService(s || null);
-  let d;
+  let d,
+    val = 1;
+  if (key.value === 5) {
+    val = 0;
+  }
   if (states.currentService) {
     d = (selectorStore.services || []).find(
       (d: { label: string }) => d.label === states.currentDestService
     );
   } else {
     d = (selectorStore.services || []).find(
-      (d: unknown, index: number) => index === 1
+      (d: unknown, index: number) => index === val
     );
   }
+
   selectorStore.setCurrentDestService(d || null);
   if (!selectorStore.currentService) {
     return;
@@ -347,62 +424,84 @@ async function getServices() {
       EntityType[3].value,
       EntityType[5].value,
       EntityType[6].value,
+      EntityType[7].value,
     ].includes(dashboardStore.entity)
   ) {
-    fetchPods(e, selectorStore.currentService.id, true);
+    await fetchPods(e, selectorStore.currentService.id, true);
   }
   if (!selectorStore.currentDestService) {
     return;
   }
   states.currentDestService = selectorStore.currentDestService.value;
   if (
-    [EntityType[5].value, EntityType[6].value].includes(dashboardStore.entity)
+    [EntityType[5].value, EntityType[6].value, EntityType[7].value].includes(
+      dashboardStore.entity
+    )
   ) {
-    fetchPods(dashboardStore.entity, selectorStore.currentDestService.id, 
true);
+    await fetchPods(
+      dashboardStore.entity,
+      selectorStore.currentDestService.id,
+      true
+    );
   }
 }
 
-async function changeService(service: any) {
+async function changeService(service: Option[]) {
   if (service[0]) {
     states.currentService = service[0].value;
     selectorStore.setCurrentService(service[0]);
-    const e = dashboardStore.entity.split("Relation")[0];
     selectorStore.setCurrentPod(null);
     states.currentPod = "";
-    fetchPods(e, selectorStore.currentService.id, true);
+    states.currentProcess = "";
+    if (dashboardStore.entity === EntityType[7].value) {
+      fetchPods("Process", selectorStore.currentService.id, true);
+    } else {
+      fetchPods(dashboardStore.entity, selectorStore.currentService.id, true);
+    }
   } else {
     selectorStore.setCurrentService(null);
   }
 }
 
-function changeDestService(service: any) {
+function changeDestService(service: Option[]) {
   if (service[0]) {
     states.currentDestService = service[0].value;
     selectorStore.setCurrentDestService(service[0]);
     selectorStore.setCurrentDestPod(null);
     states.currentDestPod = "";
+    states.currentDestProcess = "";
     fetchPods(dashboardStore.entity, selectorStore.currentDestService.id, 
true);
   } else {
     selectorStore.setCurrentDestService(null);
   }
 }
 
-function changePods(pod: any) {
-  if (pod[0]) {
-    selectorStore.setCurrentPod(pod[0]);
-  } else {
-    selectorStore.setCurrentPod("");
+async function changePods(pod: Option[]) {
+  selectorStore.setCurrentPod(pod[0] || null);
+  if (dashboardStore.entity === EntityType[7].value) {
+    selectorStore.setCurrentProcess(null);
+    states.currentProcess = "";
+    fetchProcess(true);
   }
 }
 
-function changeDestPods(pod: any) {
-  if (pod[0]) {
-    selectorStore.setCurrentDestPod(pod[0]);
-  } else {
-    selectorStore.setCurrentDestPod(null);
+function changeDestPods(pod: Option[]) {
+  selectorStore.setCurrentDestPod(pod[0] || null);
+  if (dashboardStore.entity === EntityType[7].value) {
+    selectorStore.setCurrentDestProcess(null);
+    states.currentDestProcess = "";
+    fetchDestProcess(true);
   }
 }
 
+function changeDestProcess(pod: Option[]) {
+  selectorStore.setCurrentDestProcess(pod[0] || null);
+}
+
+function changeProcess(pod: Option[]) {
+  selectorStore.setCurrentProcess(pod[0] || null);
+}
+
 function changeMode() {
   if (dashboardStore.editMode) {
     ElMessage.warning(t("editWarning"));
@@ -461,6 +560,9 @@ function setTabControls(id: string) {
     case "addEvent":
       dashboardStore.addTabControls("Event");
       break;
+    case "addTimeRange":
+      dashboardStore.addTabControls("TimeRange");
+      break;
     default:
       ElMessage.info("Don't support this control");
       break;
@@ -499,6 +601,9 @@ function setControls(id: string) {
     case "addEvent":
       dashboardStore.addControl("Event");
       break;
+    case "addTimeRange":
+      dashboardStore.addControl("TimeRange");
+      break;
     default:
       dashboardStore.addControl("Widget");
   }
@@ -587,6 +692,14 @@ async function fetchPods(
         states.currentDestPod = selectorStore.currentDestPod.label;
       }
       break;
+    case EntityType[7].value:
+      await fetchPods(EntityType[5].value, serviceId, setPod, param);
+      resp = await fetchDestProcess(setPod);
+      break;
+    case "Process":
+      await fetchPods(EntityType[3].value, serviceId, setPod, param);
+      resp = await fetchProcess(setPod);
+      break;
     default:
       resp = {};
   }
@@ -594,6 +707,48 @@ async function fetchPods(
     ElMessage.error(resp.errors);
   }
 }
+
+async function fetchProcess(setPod: boolean) {
+  const resp = await selectorStore.getProcesses({
+    instanceId: selectorStore.currentPod.id,
+  });
+  if (setPod) {
+    let m;
+    if (states.currentProcess) {
+      m = selectorStore.processes.find(
+        (d: { label: string }) => d.label === states.currentProcess
+      );
+    } else {
+      m = selectorStore.processes.find(
+        (d: { label: string }, index: number) => index === 0
+      );
+    }
+    selectorStore.setCurrentProcess(m || null);
+    states.currentProcess = m && m.label;
+  }
+  return resp;
+}
+async function fetchDestProcess(setPod: boolean) {
+  const resp = await selectorStore.getProcesses({
+    instanceId: selectorStore.currentDestPod.id,
+    isRelation: true,
+  });
+  if (setPod) {
+    let m;
+    if (states.currentDestProcess) {
+      m = selectorStore.destProcesses.find(
+        (d: { label: string }) => d.label === states.currentDestProcess
+      );
+    } else {
+      m = selectorStore.destProcesses.find(
+        (d: { label: string }, index: number) => index === 1
+      );
+    }
+    selectorStore.setCurrentDestProcess(m || null);
+    states.currentDestProcess = m && m.label;
+  }
+  return resp;
+}
 function getTools() {
   switch (params.entity) {
     case EntityType[1].value:
@@ -617,8 +772,11 @@ function getTools() {
     case EntityType[6].value:
       toolIcons.value = EndpointRelationTools;
       break;
+    case EntityType[7].value:
+      toolIcons.value = ProcessTools;
+      break;
     default:
-      toolIcons.value = EndpointRelationTools;
+      toolIcons.value = AllTools;
   }
 }
 function searchPods(query: string) {
@@ -701,4 +859,8 @@ watch(
 .selectorPod {
   width: 300px;
 }
+
+.relation {
+  margin-top: 5px;
+}
 </style>
diff --git a/src/views/dashboard/related/topology/utils/linkElement.ts 
b/src/views/dashboard/related/components/D3Graph/linkElement.ts
similarity index 100%
rename from src/views/dashboard/related/topology/utils/linkElement.ts
rename to src/views/dashboard/related/components/D3Graph/linkElement.ts
diff --git a/src/views/dashboard/related/topology/utils/nodeElement.ts 
b/src/views/dashboard/related/components/D3Graph/nodeElement.ts
similarity index 99%
rename from src/views/dashboard/related/topology/utils/nodeElement.ts
rename to src/views/dashboard/related/components/D3Graph/nodeElement.ts
index 5c761cc..f6c315b 100644
--- a/src/views/dashboard/related/topology/utils/nodeElement.ts
+++ b/src/views/dashboard/related/components/D3Graph/nodeElement.ts
@@ -18,7 +18,7 @@ import icons from "@/assets/img/icons";
 import { Node } from "@/types/topology";
 
 icons["KAFKA-CONSUMER"] = icons.KAFKA;
-export default (d3: any, graph: any, funcs: any, tip: any, legend: any) => {
+export default (d3: any, graph: any, funcs: any, tip: any, legend?: any) => {
   const nodeEnter = graph
     .append("g")
     .call(
diff --git a/src/views/dashboard/related/topology/utils/simulation.ts 
b/src/views/dashboard/related/components/D3Graph/simulation.ts
similarity index 100%
rename from src/views/dashboard/related/topology/utils/simulation.ts
rename to src/views/dashboard/related/components/D3Graph/simulation.ts
diff --git a/src/views/dashboard/related/topology/utils/zoom.ts 
b/src/views/dashboard/related/components/D3Graph/zoom.ts
similarity index 100%
rename from src/views/dashboard/related/topology/utils/zoom.ts
rename to src/views/dashboard/related/components/D3Graph/zoom.ts
diff --git a/src/views/dashboard/related/components/TaskDetails.vue 
b/src/views/dashboard/related/components/TaskDetails.vue
new file mode 100644
index 0000000..786bc35
--- /dev/null
+++ b/src/views/dashboard/related/components/TaskDetails.vue
@@ -0,0 +1,82 @@
+<!-- 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 class="profile-detail flex-v">
+    <div>
+      <h5 class="mb-10">{{ t("task") }}.</h5>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("taskId") }}:</span>
+        <span class="g-sm-8 wba">
+          {{ details.taskId }}
+        </span>
+      </div>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("service") }}:</span>
+        <span class="g-sm-8 wba">
+          {{ details.serviceName }}
+        </span>
+      </div>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("labels") }}:</span>
+        <span class="g-sm-8 wba">
+          {{ details.processLabels.join(";") }}
+        </span>
+      </div>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("monitorTime") }}:</span>
+        <span class="g-sm-8 wba">
+          {{ dateFormat(details.taskStartTime) }}
+        </span>
+      </div>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("monitorDuration") }}:</span>
+        <span class="g-sm-8 wba">
+          {{ details.fixedTriggerDuration / 60 }} min
+        </span>
+      </div>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("triggerType") }}:</span>
+        <span class="g-sm-8 wba">{{ details.triggerType }}</span>
+      </div>
+      <div class="mb-10 clear item">
+        <span class="g-sm-4 grey">{{ t("targetType") }}:</span>
+        <span class="g-sm-8 wba">{{ details.targetType }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref } from "vue";
+import type { PropType } from "vue";
+import dayjs from "dayjs";
+import { useI18n } from "vue-i18n";
+import { EBPFTaskList } from "@/types/ebpf";
+
+/*global defineProps */
+defineProps({
+  details: {
+    type: Object as PropType<EBPFTaskList>,
+    default: () => ({}),
+  },
+});
+const { t } = useI18n();
+const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
+  dayjs(date).format(pattern);
+</script>
+<style lang="scss" scoped>
+.item span {
+  height: 21px;
+}
+</style>
diff --git a/src/views/dashboard/related/demand-log/Header.vue 
b/src/views/dashboard/related/demand-log/Header.vue
index 3e25b19..f4122f9 100644
--- a/src/views/dashboard/related/demand-log/Header.vue
+++ b/src/views/dashboard/related/demand-log/Header.vue
@@ -152,7 +152,6 @@ const keywordsOfContent = ref<string[]>([]);
 const excludingKeywordsOfContent = ref<string[]>([]);
 const contentStr = ref<string>("");
 const excludingContentStr = ref<string>("");
-// const limit = ref<number>(20);
 const state = reactive<any>({
   instance: { value: "", label: "" },
   container: { value: "", label: "" },
diff --git a/src/views/dashboard/related/ebpf/Header.vue 
b/src/views/dashboard/related/ebpf/Header.vue
index 24fa9bb..9e22668 100644
--- a/src/views/dashboard/related/ebpf/Header.vue
+++ b/src/views/dashboard/related/ebpf/Header.vue
@@ -57,7 +57,10 @@ if (props.needQuery) {
 async function searchTasks() {
   const serviceId =
     (selectorStore.currentService && selectorStore.currentService.id) || "";
-  const res = await ebpfStore.getTaskList(serviceId);
+  const res = await ebpfStore.getTaskList({
+    serviceId,
+    targets: ["ON_CPU", "OFF_CPU"],
+  });
 
   if (res.errors) {
     ElMessage.error(res.errors);
diff --git a/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue 
b/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue
index 0cd8f9d..db1d4e9 100644
--- a/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue
+++ b/src/views/dashboard/related/ebpf/components/EBPFSchedules.vue
@@ -79,7 +79,7 @@ limitations under the License. -->
           />
           <el-table-column width="300" label="Attributes">
             <template #default="scope">
-              {{ scope.row.attributes.map((d: {name: string, value: string}) 
=> `${d.name}=${d.value}`).join("; ") }}
+              {{ attributes(scope.row.attributes) }}
             </template>
           </el-table-column>
         </el-table>
@@ -125,6 +125,11 @@ const aggregateType = ref<string>(AggregateTypes[0].value);
 const duration = ref<string[]>([]);
 const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
   dayjs(date).format(pattern);
+const attributes = (attr: { name: string; value: string }[]) => {
+  return attr
+    .map((d: { name: string; value: string }) => `${d.name}=${d.value}`)
+    .join("; ");
+};
 
 function changeLabels(opt: any[]) {
   const arr = opt.map((d) => d.value);
diff --git a/src/views/dashboard/related/ebpf/components/TaskList.vue 
b/src/views/dashboard/related/ebpf/components/TaskList.vue
index 82427b8..cee8dbf 100644
--- a/src/views/dashboard/related/ebpf/components/TaskList.vue
+++ b/src/views/dashboard/related/ebpf/components/TaskList.vue
@@ -30,7 +30,7 @@ limitations under the License. -->
             <td
               class="profile-td"
               :class="{
-                selected: selectedTask.taskId === i.taskId,
+                selected: ebpfStore.selectedTask.taskId === i.taskId,
               }"
             >
               <div class="ell">
@@ -67,66 +67,25 @@ limitations under the License. -->
     fullscreen
     @closed="viewDetail = false"
   >
-    <div class="profile-detail flex-v">
-      <div>
-        <h5 class="mb-10">{{ t("task") }}.</h5>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("taskId") }}:</span>
-          <span class="g-sm-8 wba">
-            {{ selectedTask.taskId }}
-          </span>
-        </div>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("service") }}:</span>
-          <span class="g-sm-8 wba">{{ selectedTask.serviceName }}</span>
-        </div>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("labels") }}:</span>
-          <span class="g-sm-8 wba">
-            {{ selectedTask.processLabels.join(";") }}
-          </span>
-        </div>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("monitorTime") }}:</span>
-          <span class="g-sm-8 wba">
-            {{ dateFormat(selectedTask.taskStartTime) }}
-          </span>
-        </div>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("monitorDuration") }}:</span>
-          <span class="g-sm-8 wba">
-            {{ selectedTask.fixedTriggerDuration / 60 }} min
-          </span>
-        </div>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("triggerType") }}:</span>
-          <span class="g-sm-8 wba">{{ selectedTask.triggerType }}</span>
-        </div>
-        <div class="mb-10 clear item">
-          <span class="g-sm-4 grey">{{ t("targetType") }}:</span>
-          <span class="g-sm-8 wba">{{ selectedTask.targetType }}</span>
-        </div>
-      </div>
-    </div>
+    <TaskDetails :details="ebpfStore.selectedTask" />
   </el-dialog>
 </template>
 <script lang="ts" setup>
-import { ref, watch } from "vue";
+import { ref } from "vue";
 import dayjs from "dayjs";
 import { useI18n } from "vue-i18n";
 import { useEbpfStore } from "@/store/modules/ebpf";
 import { EBPFTaskList } from "@/types/ebpf";
 import { ElMessage } from "element-plus";
+import TaskDetails from "../../components/TaskDetails.vue";
 
 const { t } = useI18n();
 const ebpfStore = useEbpfStore();
 const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
   dayjs(date).format(pattern);
-const selectedTask = ref<EBPFTaskList | Record<string, never>>({});
 const viewDetail = ref<boolean>(false);
 
 async function changeTask(item: EBPFTaskList) {
-  selectedTask.value = item;
   ebpfStore.setSelectedTask(item);
   const res = await ebpfStore.getEBPFSchedules({
     taskId: item.taskId,
@@ -135,13 +94,6 @@ async function changeTask(item: EBPFTaskList) {
     ElMessage.error(res.errors);
   }
 }
-watch(
-  () => ebpfStore.taskList,
-  () => {
-    selectedTask.value = ebpfStore.taskList[0] || {};
-    ebpfStore.setSelectedTask(selectedTask.value);
-  }
-);
 </script>
 <style lang="scss" scoped>
 .profile-task-list {
diff --git a/src/views/dashboard/related/event/Header.vue 
b/src/views/dashboard/related/event/Header.vue
index 8155043..0bddcd4 100644
--- a/src/views/dashboard/related/event/Header.vue
+++ b/src/views/dashboard/related/event/Header.vue
@@ -146,10 +146,10 @@ async function queryEvents() {
   let endpoint = state.endpoint.value,
     instance = state.instance.value;
   if (dashboardStore.entity === EntityType[2].value) {
-    endpoint = selectorStore.currentPod.id;
+    endpoint = selectorStore.currentPod && selectorStore.currentPod.id;
   }
   if (dashboardStore.entity === EntityType[3].value) {
-    instance = selectorStore.currentPod.id;
+    instance = selectorStore.currentPod && selectorStore.currentPod.id;
   }
   if (!selectorStore.currentService) {
     return;
diff --git a/src/views/dashboard/related/log/Header.vue 
b/src/views/dashboard/related/log/Header.vue
index d91cbe8..c93770e 100644
--- a/src/views/dashboard/related/log/Header.vue
+++ b/src/views/dashboard/related/log/Header.vue
@@ -145,7 +145,7 @@ import { ErrorCategory } from "./data";
 import { LayoutConfig } from "@/types/dashboard";
 import { DurationTime } from "@/types/app";
 
-/*global  defineProps, Recordable */
+/*global defineProps, Recordable */
 const props = defineProps({
   needQuery: { type: Boolean, default: true },
   data: {
diff --git a/src/views/dashboard/related/topology/components/Graph.vue 
b/src/views/dashboard/related/topology/components/Graph.vue
index a750173..39ec45d 100644
--- a/src/views/dashboard/related/topology/components/Graph.vue
+++ b/src/views/dashboard/related/topology/components/Graph.vue
@@ -94,10 +94,17 @@ import {
 import { useI18n } from "vue-i18n";
 import * as d3 from "d3";
 import d3tip from "d3-tip";
-import zoom from "../utils/zoom";
-import { simulationInit, simulationSkip } from "../utils/simulation";
-import nodeElement from "../utils/nodeElement";
-import { linkElement, anchorElement, arrowMarker } from "../utils/linkElement";
+import zoom from "../../components/D3Graph/zoom";
+import {
+  simulationInit,
+  simulationSkip,
+} from "../../components/D3Graph/simulation";
+import nodeElement from "../../components/D3Graph/nodeElement";
+import {
+  linkElement,
+  anchorElement,
+  arrowMarker,
+} from "../../components/D3Graph/linkElement";
 import { Node, Call } from "@/types/topology";
 import { useSelectorStore } from "@/store/modules/selectors";
 import { useTopologyStore } from "@/store/modules/topology";

Reply via email to