You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2021/04/06 03:43:39 UTC

[skywalking-rocketbot-ui] branch master updated: feat: implement Events in dashboard (#452)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d339675  feat: implement Events in dashboard (#452)
d339675 is described below

commit d339675c61ff0444f101edaedb92bd8a7bbca5b0
Author: Qiuxia Fan <fi...@outlook.com>
AuthorDate: Tue Apr 6 11:43:32 2021 +0800

    feat: implement Events in dashboard (#452)
---
 src/assets/lang/en.ts                              |  17 +-
 src/assets/lang/zh.ts                              |  17 +-
 src/components/rk-select.vue                       |   3 +-
 src/constants/constant.ts                          |  10 +
 src/graph/fragments/dashboard.ts                   |  25 ++
 src/graph/query/dashboard.ts                       |   3 +
 src/main.ts                                        |   1 +
 src/store/modules/dashboard/dashboard-data.ts      | 108 ++++-
 src/store/modules/dashboard/mutation-types.ts      |   9 +
 src/store/modules/global/index.ts                  |  40 +-
 src/store/modules/global/selectors.ts              | 110 +++--
 src/types/dashboard.d.ts                           |  35 +-
 src/utils/{dateFormatStep.ts => dateFormat.ts}     |  37 ++
 .../components/dashboard/charts/chart-bar.vue      |  41 ++
 .../charts/{chart-bar.vue => chart-heap.vue}       | 101 ++---
 .../components/dashboard/charts/chart-heatmap.vue  |   1 +
 .../components/dashboard/charts/chart-line.vue     |  41 ++
 .../components/dashboard/charts/chart-sankey.vue   |  67 +++
 src/views/components/dashboard/constant.ts         |  22 +
 src/views/components/dashboard/dashboard-comp.vue  |   2 +-
 src/views/components/dashboard/dashboard-item.vue  |  69 ++-
 .../dashboard/tool-bar/dashboard-events.vue        | 490 +++++++++++++++++++++
 .../dashboard/tool-bar/tool-bar-btns.vue           |  75 ++--
 .../components/dashboard/tool-bar/tool-bar.vue     | 201 ++++++---
 src/views/components/dashboard/tool-group.vue      |   4 +-
 src/views/components/dashboard/tool-nav.vue        |  55 +--
 src/views/components/log/log-bar.vue               |   7 +-
 src/views/components/log/log-conditions.vue        |   2 +-
 src/views/components/log/log-service-detail.vue    |   1 -
 src/views/components/profile/profile-task.vue      |   5 +-
 src/views/components/topology/topo-aside.vue       |   6 +-
 .../topology/topo-endpoint-dependency.vue          |   2 +-
 src/views/components/trace/trace-search.vue        |   2 +-
 src/views/containers/dashboard.vue                 |  14 +-
 src/views/containers/topology/endpoint/index.vue   |  85 ++--
 src/views/containers/topology/instance/index.vue   |  98 +++--
 vue.config.js                                      |   1 -
 37 files changed, 1426 insertions(+), 381 deletions(-)

diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index 645d8c1..04b36fe 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -99,7 +99,7 @@ const m = {
   range: 'Range',
   timeRange: 'Time Range',
   duration: 'Duration',
-  startTime: 'startTime',
+  startTime: 'Start Time',
   start: 'Start',
   spans: 'Spans',
   spanInfo: 'Span Info',
@@ -199,7 +199,22 @@ const m = {
   logsTagsTip: `Only tags defined in the core/default/searchableLogsTags are searchable.
   Check more details on the Configuration Vocabulary page`,
   keywordsOfContentLogTips: 'Current storage of SkyWalking OAP server does not support this.',
+  setEvent: 'Set Event',
   instanceAttributes: 'Instance Attributes',
+  serviceEvents: 'Service Events',
+  select: 'Select',
+  eventID: 'Event ID',
+  eventName: 'Event Name',
+  endTime: 'End Time',
+  instanceEvents: 'Instance Events',
+  endpointEvents: 'Endpoint Events',
+  enableEvents: 'Enable Events',
+  disableEvents: 'Disable Events',
+  eventSeries: 'Events Series',
+  eventsType: 'Event Type',
+  eventsMessage: 'Event Message',
+  eventsParameters: 'Event Parameters',
+  eventDetail: 'Event Detail',
   value: 'Value',
   tableHeader: 'Header Names',
   tableValues: 'Table Values',
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index f222588..7b4a3d7 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -197,7 +197,22 @@ const m = {
   viewLogs: '查看日志',
   logsTagsTip: '只有core/default/searchableLogsTags中定义的标记才可搜索。查看配置词汇表页面上的更多详细信息。',
   keywordsOfContentLogTips: 'SkyWalking OAP服务器的当前存储不支持此操作',
-  instanceAttributes: '查看实例属性',
+  setEvent: '设置事件',
+  instanceAttributes: '实例属性',
+  serviceEvents: '服务事件',
+  select: '选择',
+  eventID: '事件ID',
+  eventName: '事件名称',
+  endTime: '结束事件',
+  instanceEvents: '实例事件',
+  endpointEvents: '端点事件',
+  enableEvents: '启动事件',
+  disableEvents: '禁用事件',
+  eventSeries: '事件系列',
+  eventsType: '事件类型',
+  eventsMessage: '事件消息',
+  eventsParameters: '事件参数',
+  eventDetail: '事件详情',
   value: '数值',
   tableHeader: '表头名称',
   tableValues: '表值',
diff --git a/src/components/rk-select.vue b/src/components/rk-select.vue
index 4b69653..528c05a 100644
--- a/src/components/rk-select.vue
+++ b/src/components/rk-select.vue
@@ -46,7 +46,7 @@ limitations under the License. -->
           <use xlink:href="#clear"></use>
         </svg>
       </div>
-      <div class="rk-opt-wrapper scroll_bar_style">
+      <div class="rk-opt-wrapper">
         <div
           class="rk-opt ell"
           @click="handleSelect(i)"
@@ -104,7 +104,6 @@ limitations under the License. -->
 <style lang="scss" scoped>
   .rk-bar-select {
     position: relative;
-    height: 30px;
     justify-content: space-between;
     border: 1px solid #ddd;
     background: #fff;
diff --git a/src/constants/constant.ts b/src/constants/constant.ts
index 022719c..f67961e 100644
--- a/src/constants/constant.ts
+++ b/src/constants/constant.ts
@@ -31,3 +31,13 @@ export enum TimeType {
   HOUR_TIME = 'HOUR',
   DAY_TIME = 'DAY',
 }
+
+export enum PageEventsType {
+  DASHBOARD_EVENTS = 'dashboardEvents',
+  TOPO_ENDPOINT_EVENTS = 'topoEndpointEvents',
+  TOPO_INSTANCE_EVENTS = 'topoInstanceEvents',
+}
+export enum PageTypes {
+  DASHBOARD = 'Dashboard',
+  LOG = 'Log',
+}
diff --git a/src/graph/fragments/dashboard.ts b/src/graph/fragments/dashboard.ts
index 2b762d7..2b46e60 100644
--- a/src/graph/fragments/dashboard.ts
+++ b/src/graph/fragments/dashboard.ts
@@ -42,6 +42,31 @@ export const addTemplate = {
   `,
 };
 
+export const fetchEvents = {
+  variable: ['$condition: EventQueryCondition'],
+  query: `
+  fetchEvents: queryEvents(condition: $condition) {
+    events {
+      uuid
+      source {
+        service
+        serviceInstance
+        endpoint
+      }
+      name
+      type
+      message
+      parameters {
+        key
+        value
+      }
+      startTime
+      endTime
+    }
+    total
+  }`,
+};
+
 export const changeTemplate = {
   variable: '$setting: DashboardSetting!',
   query: `
diff --git a/src/graph/query/dashboard.ts b/src/graph/query/dashboard.ts
index 219577e..d95f6c1 100644
--- a/src/graph/query/dashboard.ts
+++ b/src/graph/query/dashboard.ts
@@ -27,6 +27,7 @@ import {
   querySortMetrics,
   queryMetricsValue,
   queryMetricsValues,
+  fetchEvents,
 } from '../fragments/dashboard';
 
 export const queryTypeOfMetrics = `query queryTypeOfMetrics(${TypeOfMetrics.variable}) {${TypeOfMetrics.query}}`;
@@ -53,3 +54,5 @@ export const sortMetrics = `query queryData(${querySortMetrics.variable}) {${que
 export const readMetricsValue = `query queryData(${queryMetricsValue.variable}) {${queryMetricsValue.query}}`;
 
 export const readMetricsValues = `query queryData(${queryMetricsValues.variable}) {${queryMetricsValues.query}}`;
+
+export const queryEvents = `query queryData(${fetchEvents.variable}) {${fetchEvents.query}}`;
diff --git a/src/main.ts b/src/main.ts
index 1ee8746..0b4c5e2 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -33,6 +33,7 @@ import 'echarts/lib/chart/heatmap';
 import 'echarts/lib/chart/sankey';
 import 'echarts/lib/component/legend';
 import 'echarts/lib/component/tooltip';
+import 'echarts/lib/component/markArea';
 import VModal from 'vue-js-modal';
 import { queryOAPTimeInfo } from './utils/localtime';
 import './assets';
diff --git a/src/store/modules/dashboard/dashboard-data.ts b/src/store/modules/dashboard/dashboard-data.ts
index 1d17125..298a96c 100644
--- a/src/store/modules/dashboard/dashboard-data.ts
+++ b/src/store/modules/dashboard/dashboard-data.ts
@@ -16,25 +16,112 @@
  */
 
 import { ActionTree, MutationTree, Commit, Dispatch } from 'vuex';
-import { CompsTree } from '@/types/dashboard';
+import { CompsTree, Event } from '@/types/dashboard';
 import { AxiosResponse } from 'axios';
 import graph from '@/graph';
 import dashboardLayout from './dashboard-data-layout';
 import dashboardQuery from './dashboard-data-query';
+import { QueryEventCondition } from '../../../types/dashboard';
+import { dateFormatTime } from '@/utils/dateFormat';
+import { DurationTime, Option } from '@/types/global';
+import * as types from './mutation-types';
+import { PageEventsType } from '@/constants/constant';
 
+const EntityType = ['Service', 'ServiceInstance', 'Endpoint'];
 export interface State {
   current: number;
   group: number;
   tree: CompsTree[];
+  serviceEvents: Event[];
+  serviceInstanceEvents: Event[];
+  endpointEvents: Event[];
+  enableEvents: boolean;
+  eventsPageType: string;
+  currentSeriesType: Option[];
 }
 
 const initState: State = {
+  serviceEvents: [],
+  endpointEvents: [],
+  serviceInstanceEvents: [],
+  enableEvents: false,
+  eventsPageType: PageEventsType.DASHBOARD_EVENTS,
+  currentSeriesType: [],
   ...dashboardLayout.state,
 };
 
 // mutations
 const mutations: MutationTree<any> = {
   ...dashboardLayout.mutations,
+  [types.SET_DASHBOARD_EVENTS](state: State, param: { events: Event[]; type: string; duration: DurationTime }) {
+    const events = param.events.map((d: Event, index: number) => {
+      d.entityType = param.type;
+      d.startTime = dateFormatTime(new Date(Number(d.startTime)), param.duration.step);
+      d.endTime = dateFormatTime(new Date(Number(d.endTime)), param.duration.step);
+      if (index > 2) {
+        return d;
+      }
+      for (const item of state.currentSeriesType) {
+        if (item.key === param.type) {
+          d.checked = true;
+        }
+      }
+      return d;
+    });
+    if (param.type === EntityType[0]) {
+      state.serviceEvents = events;
+    } else if (param.type === EntityType[1]) {
+      state.serviceInstanceEvents = events;
+    } else {
+      state.endpointEvents = events;
+    }
+  },
+  [types.SET_CHECKED_EVENTS](state: State, selectedEvents: Event[]) {
+    for (const event of selectedEvents) {
+      if (event.entityType === EntityType[0]) {
+        for (const item of state.serviceEvents) {
+          if (event.uuid === item.uuid && event.entityType === item.entityType) {
+            item.checked = event.checked;
+            break;
+          }
+        }
+      }
+      if (event.entityType === EntityType[1]) {
+        for (const item of state.serviceInstanceEvents) {
+          if (event.uuid === item.uuid && event.entityType === item.entityType) {
+            item.checked = event.checked;
+            break;
+          }
+        }
+      }
+      if (event.entityType === EntityType[2]) {
+        for (const item of state.endpointEvents) {
+          if (event.uuid === item.uuid && event.entityType === item.entityType) {
+            item.checked = event.checked;
+            break;
+          }
+        }
+      }
+    }
+  },
+  [types.SET_ENABLE_EVENTS](state: State, enable: boolean) {
+    state.enableEvents = enable;
+  },
+  [types.SET_EVENTS_PAGE_TYPE](state: State, type: string) {
+    state.eventsPageType = type;
+  },
+  [types.SET_CURRENT_SERIES_TYPE](state: State, data: { item: Option; index: number }) {
+    if (data.index > -1) {
+      state.currentSeriesType.splice(data.index, 1);
+    } else {
+      state.currentSeriesType.push(data.item);
+    }
+  },
+  [types.SET_CLEAR_SELECTED_EVENTS](state: State) {
+    for (const item of [...state.serviceEvents, ...state.serviceInstanceEvents, ...state.endpointEvents]) {
+      item.checked = false;
+    }
+  },
 };
 
 // actions
@@ -108,15 +195,22 @@ const actions: ActionTree<State, any> = {
         return res.data.data.getAllTemplates || [];
       });
   },
-  ADD_TEMPLATE(context, params) {
+  GET_EVENT(context: { commit: Commit }, params: { condition: QueryEventCondition; type: string }) {
     return graph
-      .query('mutationAddTemplate')
-      .params({ setting: params })
+      .query('queryEvents')
+      .params({ condition: params.condition })
       .then((res: AxiosResponse) => {
-        if (!res.data.data) {
-          return;
+        if (!(res.data.data && res.data.data.fetchEvents)) {
+          context.commit('SET_DASHBOARD_EVENTS', { events: [], type: params.type, duration: params.condition.time });
+          return [];
         }
-        return res.data.data.addTemplate || [];
+        context.commit('SET_DASHBOARD_EVENTS', {
+          events: res.data.data.fetchEvents.events,
+          type: params.type,
+          duration: params.condition.time,
+        });
+
+        return res.data.data.fetchEvents.events || [];
       });
   },
 };
diff --git a/src/store/modules/dashboard/mutation-types.ts b/src/store/modules/dashboard/mutation-types.ts
index aa18646..eef9b1c 100644
--- a/src/store/modules/dashboard/mutation-types.ts
+++ b/src/store/modules/dashboard/mutation-types.ts
@@ -36,6 +36,12 @@ export const SET_INSTANCE_INFO = 'SET_INSTANCE_INFO';
 export const SET_TEMPLATES = 'SET_TEMPLATES';
 export const UPDATE_DASHBOARD = 'UPDATE_DASHBOARD';
 export const SET_PAGE_TYPE = 'SET_PAGE_TYPE';
+export const SET_ALL_ENDPOINT_EVENTS = 'SET_ALL_ENDPOINT_EVENTS';
+export const SET_ALL_SERVICE_EVENTS = 'SET_ALL_SERVICE_EVENTS';
+export const SET_ALL_INSTANCE_EVENTS = 'SET_ALL_INSTANCE_EVENTS';
+export const SET_EVENTS_PAGE_TYPE = 'SET_EVENTS_PAGE_TYPE';
+export const SET_CURRENT_SERIES_TYPE = 'SET_CURRENT_SERIES_TYPE';
+export const SET_CLEAR_SELECTED_EVENTS = 'SET_CLEAR_SELECTED_EVENTS';
 
 // comp
 export const SET_CURRENT_GROUP = 'SET_CURRENT_GROUP';
@@ -55,3 +61,6 @@ export const SWICH_CURRENTCOMP = 'SWICH_CURRENTCOMP';
 export const SET_GROUP_QUERY = 'SET_GROUP_QUERY';
 export const EDIT_COMP_CONFIG = 'EDIT_COMP_CONFIG';
 export const SET_COMPS_ID = 'SET_COMPS_ID';
+export const SET_DASHBOARD_EVENTS = 'SET_DASHBOARD_EVENTS';
+export const SET_CHECKED_EVENTS = 'SET_CHECKED_EVENTS';
+export const SET_ENABLE_EVENTS = 'SET_ENABLE_EVENTS';
diff --git a/src/store/modules/global/index.ts b/src/store/modules/global/index.ts
index 68f91a3..0d7cf44 100644
--- a/src/store/modules/global/index.ts
+++ b/src/store/modules/global/index.ts
@@ -19,48 +19,10 @@ import * as types from '@/store/mutation-types';
 import { Duration, DurationTime } from '@/types/global';
 import getDurationRow from '@/utils/datetime';
 import getLocalTime from '@/utils/localtime';
-import dateFormatStep from '@/utils/dateFormatStep';
+import dateFormatStep, { dateFormatTime } from '@/utils/dateFormat';
 import { ActionTree, Commit, MutationTree } from 'vuex';
 
 let timer: any = null;
-
-const dateFormatTime = (date: Date, step: string): string => {
-  const year = date.getFullYear();
-  const monthTemp = date.getMonth() + 1;
-  let month: string = `${monthTemp}`;
-  if (monthTemp < 10) {
-    month = `0${monthTemp}`;
-  }
-  if (step === 'MONTH') {
-    return `${year}-${month}`;
-  }
-  const dayTemp = date.getDate();
-  let day: string = `${dayTemp}`;
-  if (dayTemp < 10) {
-    day = `0${dayTemp}`;
-  }
-  if (step === 'DAY') {
-    return `${month}-${day}`;
-  }
-  const hourTemp = date.getHours();
-  let hour: string = `${hourTemp}`;
-  if (hourTemp < 10) {
-    hour = `0${hourTemp}`;
-  }
-  if (step === 'HOUR') {
-    return `${month}-${day} ${hour}`;
-  }
-  const minuteTemp = date.getMinutes();
-  let minute: string = `${minuteTemp}`;
-  if (minuteTemp < 10) {
-    minute = `0${minuteTemp}`;
-  }
-  if (step === 'MINUTE') {
-    return `${hour}:${minute}\n${month}-${day}`;
-  }
-  return '';
-};
-
 export interface State {
   durationRow: Duration;
   eventStack: any;
diff --git a/src/store/modules/global/selectors.ts b/src/store/modules/global/selectors.ts
index 84eecfb..72832f2 100644
--- a/src/store/modules/global/selectors.ts
+++ b/src/store/modules/global/selectors.ts
@@ -19,22 +19,20 @@ import { Commit, ActionTree, MutationTree, Dispatch } from 'vuex';
 import * as types from '../dashboard/mutation-types';
 import { AxiosResponse } from 'axios';
 import graph from '@/graph';
-import { Duration } from '@/types/global';
+import { Duration, DurationTime, Option } from '@/types/global';
+import { PageTypes } from '@/constants/constant';
 
-interface Options {
-  key: string;
-  label: string;
-}
+const EntityType = ['Service', 'ServiceInstance', 'Endpoint'];
 export interface State {
-  services: Options[];
-  currentService: Options;
-  databases: Options[];
-  currentDatabase: Options;
-  endpoints: Options[];
-  currentEndpoint: Options;
-  instances: Options[];
-  currentInstance: Options;
-  updateDashboard: object;
+  services: Option[];
+  currentService: Option;
+  databases: Option[];
+  currentDatabase: Option;
+  endpoints: Option[];
+  currentEndpoint: Option;
+  instances: Option[];
+  currentInstance: Option;
+  updateDashboard: { key: string; label?: string | undefined };
   pageType: string;
 }
 
@@ -49,26 +47,28 @@ const initState: State = {
   currentInstance: { key: '', label: '' },
   databases: [],
   currentDatabase: { key: '', label: '' },
-  updateDashboard: {},
+  updateDashboard: { key: '' },
   pageType: '',
 };
 
 // mutations
 const mutations: MutationTree<State> = {
-  [types.SET_SERVICES](state: State, data: Options[]) {
+  [types.SET_SERVICES](state: State, data: Option[]) {
     state.services = state.pageType === LOG ? [{ label: 'All', key: '' }, ...data] : data;
     state.currentService = state.services[0] || {};
   },
-  [types.SET_CURRENT_SERVICE](state: State, service: Options) {
+  [types.SET_CURRENT_SERVICE](state: State, service: Option) {
     state.currentService = service;
-    state.updateDashboard = service;
+    if (state.pageType !== PageTypes.DASHBOARD) {
+      state.updateDashboard = service;
+    }
   },
 
-  [types.UPDATE_DASHBOARD](state: State) {
-    state.updateDashboard = { key: new Date().getTime() };
+  [types.UPDATE_DASHBOARD](state: State, param?: { key: string }) {
+    state.updateDashboard = param || { key: String(new Date().getTime()) };
   },
 
-  [types.SET_ENDPOINTS](state: State, data: Options[]) {
+  [types.SET_ENDPOINTS](state: State, data: Option[]) {
     state.endpoints = state.pageType === LOG ? [{ label: 'All', key: '' }, ...data] : data;
     if (!state.endpoints.length) {
       state.currentEndpoint = { key: '', label: '' };
@@ -76,11 +76,11 @@ const mutations: MutationTree<State> = {
     }
     state.currentEndpoint = state.endpoints[0];
   },
-  [types.SET_CURRENT_ENDPOINT](state: State, endpoint: Options) {
+  [types.SET_CURRENT_ENDPOINT](state: State, endpoint: Option) {
     state.currentEndpoint = endpoint;
     state.updateDashboard = endpoint;
   },
-  [types.SET_INSTANCES](state: State, data: Options[]) {
+  [types.SET_INSTANCES](state: State, data: Option[]) {
     state.instances = state.pageType === LOG ? [{ label: 'All', key: '' }, ...data] : data;
     if (!state.instances.length) {
       state.currentInstance = { key: '', label: '' };
@@ -88,11 +88,11 @@ const mutations: MutationTree<State> = {
     }
     state.currentInstance = state.instances[0];
   },
-  [types.SET_CURRENT_INSTANCE](state: State, instance: Options) {
+  [types.SET_CURRENT_INSTANCE](state: State, instance: Option) {
     state.currentInstance = instance;
     state.updateDashboard = instance;
   },
-  [types.SET_DATABASES](state: State, data: Options[]) {
+  [types.SET_DATABASES](state: State, data: Option[]) {
     state.databases = data;
     if (!data.length) {
       state.currentDatabase = { key: '', label: '' };
@@ -100,7 +100,7 @@ const mutations: MutationTree<State> = {
     }
     state.currentDatabase = data[0];
   },
-  [types.SET_CURRENT_DATABASE](state: State, service: Options) {
+  [types.SET_CURRENT_DATABASE](state: State, service: Option) {
     state.currentDatabase = service;
     state.updateDashboard = service;
   },
@@ -111,7 +111,7 @@ const mutations: MutationTree<State> = {
 
 // actions
 const actions: ActionTree<State, any> = {
-  GET_SERVICES(context: { commit: Commit }, params: { duration: any; keyword: string }) {
+  GET_SERVICES(context: { commit: Commit }, params: { duration: DurationTime; keyword: string }) {
     if (!params.keyword) {
       params.keyword = '';
     }
@@ -123,7 +123,7 @@ const actions: ActionTree<State, any> = {
       });
   },
   GET_SERVICE_ENDPOINTS(
-    context: { commit: Commit; state: any },
+    context: { commit: Commit; state: State },
     params: { keyword: string; currentService?: { key: string; label: string } },
   ) {
     if (!context.state.currentService.key) {
@@ -143,7 +143,7 @@ const actions: ActionTree<State, any> = {
         context.commit(types.SET_ENDPOINTS, res.data.data.getEndpoints);
       });
   },
-  GET_SERVICE_INSTANCES(context: { commit: Commit; state: any }, params: any) {
+  GET_SERVICE_INSTANCES(context: { commit: Commit; state: State }, params: any) {
     if (!context.state.currentService.key) {
       context.commit(types.SET_INSTANCES, []);
       return;
@@ -163,15 +163,55 @@ const actions: ActionTree<State, any> = {
         context.commit(types.SET_DATABASES, res.data.data.services);
       });
   },
-  SELECT_SERVICE(context: { commit: Commit; dispatch: Dispatch }, params: any) {
+  SELECT_SERVICE(
+    context: { commit: Commit; dispatch: Dispatch; state: State },
+    params: { service: Option; duration: DurationTime; callback?: any },
+  ) {
     context.commit('SET_CURRENT_SERVICE', params.service);
-    context.dispatch('GET_SERVICE_ENDPOINTS', {});
-    context.dispatch('GET_SERVICE_INSTANCES', { duration: params.duration });
+    context.dispatch('GET_SERVICE_ENDPOINTS', {}).then(() => {
+      if (context.state.pageType !== PageTypes.DASHBOARD || !params.callback) {
+        return;
+      }
+      params.callback({
+        condition: {
+          time: params.duration,
+          size: 20,
+          source: {
+            service: params.service.label,
+            endpoint: context.state.currentEndpoint.label,
+          },
+        },
+        type: EntityType[2],
+      });
+    });
+    context.dispatch('GET_SERVICE_INSTANCES', { duration: params.duration }).then(() => {
+      if (context.state.pageType === PageTypes.DASHBOARD && !params.callback) {
+        context.commit('UPDATE_DASHBOARD', params.service);
+      }
+      if (context.state.pageType !== PageTypes.DASHBOARD || !params.callback) {
+        return;
+      }
+      params
+        .callback({
+          condition: {
+            time: params.duration,
+            size: 20,
+            source: {
+              service: params.service.label,
+              serviceInstance: context.state.currentInstance.label,
+            },
+          },
+          type: EntityType[1],
+        })
+        .then(() => {
+          context.commit('UPDATE_DASHBOARD', params.service);
+        });
+    });
   },
-  SELECT_ENDPOINT(context: { commit: Commit; dispatch: Dispatch; state: any; rootState: any }, params: any) {
+  SELECT_ENDPOINT(context: { commit: Commit; dispatch: Dispatch; state: State; rootState: any }, params: any) {
     context.commit('SET_CURRENT_ENDPOINT', params.endpoint);
   },
-  SELECT_INSTANCE(context: { commit: Commit; dispatch: Dispatch; state: any; rootState: any }, params: any) {
+  SELECT_INSTANCE(context: { commit: Commit; dispatch: Dispatch; state: State; rootState: any }, params: any) {
     context.commit('SET_CURRENT_INSTANCE', params.instance);
   },
   SELECT_DATABASE(context: { commit: Commit; dispatch: Dispatch }, params: any) {
@@ -229,7 +269,7 @@ const actions: ActionTree<State, any> = {
         return res.data.data.getServiceInstances;
       });
   },
-  GET_ITEM_SERVICES(context, params: { duration: any; keyword: string }) {
+  GET_ITEM_SERVICES(context, params: { duration: DurationTime; keyword: string }) {
     if (!params.keyword) {
       params.keyword = '';
     }
diff --git a/src/types/dashboard.d.ts b/src/types/dashboard.d.ts
index ace8a2f..a96a432 100644
--- a/src/types/dashboard.d.ts
+++ b/src/types/dashboard.d.ts
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Option } from './global';
+import { Duration } from './global';
 export interface Value {
   value: number;
 }
@@ -67,3 +67,36 @@ export interface DashboardTemplate {
   activated: boolean;
   disabled: boolean;
 }
+
+export interface QueryEventCondition {
+  uuid: string;
+  source: SourceInput;
+  name: string;
+  type: EventType;
+  time: Duration;
+  order: string;
+  size: number;
+}
+
+type SourceInput = {
+  service: String;
+  serviceInstance: String;
+  endpoint: String;
+};
+export enum EventType {
+  Normal,
+  Error,
+}
+
+export type Event = {
+  uuid: string;
+  source: SourceInput;
+  name: string;
+  type: string;
+  message: string;
+  parameters: { key: string; value: string }[];
+  startTime: number | string;
+  endTime: number | string;
+  entityType?: string;
+  checked?: boolean;
+};
diff --git a/src/utils/dateFormatStep.ts b/src/utils/dateFormat.ts
similarity index 66%
rename from src/utils/dateFormatStep.ts
rename to src/utils/dateFormat.ts
index 90967d9..3ad6952 100644
--- a/src/utils/dateFormatStep.ts
+++ b/src/utils/dateFormat.ts
@@ -51,3 +51,40 @@ export default function dateFormatStep(date: Date, step: string, monthDayDiff?:
   }
   return '';
 }
+
+export const dateFormatTime = (date: Date, step: string): string => {
+  const year = date.getFullYear();
+  const monthTemp = date.getMonth() + 1;
+  let month: string = `${monthTemp}`;
+  if (monthTemp < 10) {
+    month = `0${monthTemp}`;
+  }
+  if (step === 'MONTH') {
+    return `${year}-${month}`;
+  }
+  const dayTemp = date.getDate();
+  let day: string = `${dayTemp}`;
+  if (dayTemp < 10) {
+    day = `0${dayTemp}`;
+  }
+  if (step === 'DAY') {
+    return `${month}-${day}`;
+  }
+  const hourTemp = date.getHours();
+  let hour: string = `${hourTemp}`;
+  if (hourTemp < 10) {
+    hour = `0${hourTemp}`;
+  }
+  if (step === 'HOUR') {
+    return `${month}-${day} ${hour}`;
+  }
+  const minuteTemp = date.getMinutes();
+  let minute: string = `${minuteTemp}`;
+  if (minuteTemp < 10) {
+    minute = `0${minuteTemp}`;
+  }
+  if (step === 'MINUTE') {
+    return `${hour}:${minute}\n${month}-${day}`;
+  }
+  return '';
+};
diff --git a/src/views/components/dashboard/charts/chart-bar.vue b/src/views/components/dashboard/charts/chart-bar.vue
index b438f81..16dbb49 100644
--- a/src/views/components/dashboard/charts/chart-bar.vue
+++ b/src/views/components/dashboard/charts/chart-bar.vue
@@ -18,17 +18,40 @@ limitations under the License. -->
 </template>
 <script lang="ts">
   import { Vue, Component, Prop } from 'vue-property-decorator';
+  import { Event } from '@/types/dashboard';
 
   @Component
   export default class ChartBar extends Vue {
     @Prop() private data!: any;
     @Prop() private intervalTime!: any;
+    @Prop() private itemEvents!: Event[];
     public resize() {
       const chart: any = this.$refs.chart;
       chart.myChart.resize();
     }
     get option() {
       const keys = Object.keys(this.data || {}).filter((i: any) => Array.isArray(this.data[i]) && this.data[i].length);
+      const startP = keys.length > 1 ? 50 : 15;
+      const diff = 15;
+      const markAreas = (this.itemEvents || []).map((event: Event, index: number) => {
+        return [
+          {
+            name: `${event.name}:${event.type}`,
+            xAxis: event.startTime,
+            y: startP + diff * index,
+            itemStyle: {
+              borderWidth: 2,
+              borderColor: event.type === 'Normal' ? '#5dc859' : '#FF0087',
+              color: event.type === 'Normal' ? '#5dc859' : '#FF0087',
+            },
+          },
+          {
+            name: event.message,
+            xAxis: event.endTime,
+            y: startP + diff * (index + 1),
+          },
+        ];
+      });
       const temp = keys.map((i: string, index: number) => {
         return {
           data: this.data[i].map((item: any, itemIndex: number) => [this.intervalTime[itemIndex], item]),
@@ -41,6 +64,23 @@ limitations under the License. -->
             width: 1.5,
             type: 'dotted',
           },
+          markArea:
+            index === 0
+              ? {
+                  silent: false,
+                  data: markAreas,
+                  label: {
+                    show: false,
+                    width: 60,
+                  },
+                  emphasis: {
+                    label: {
+                      position: 'bottom',
+                      show: true,
+                    },
+                  },
+                }
+              : undefined,
         };
       });
       let color: string[] = [];
@@ -79,6 +119,7 @@ limitations under the License. -->
           backgroundColor: 'rgb(50,50,50)',
           textStyle: {
             fontSize: 13,
+            color: '#ccc',
           },
           enterable: true,
           extraCssText: 'max-height: 300px; overflow: auto;',
diff --git a/src/views/components/dashboard/charts/chart-bar.vue b/src/views/components/dashboard/charts/chart-heap.vue
similarity index 52%
copy from src/views/components/dashboard/charts/chart-bar.vue
copy to src/views/components/dashboard/charts/chart-heap.vue
index b438f81..a38025f 100644
--- a/src/views/components/dashboard/charts/chart-bar.vue
+++ b/src/views/components/dashboard/charts/chart-heap.vue
@@ -14,85 +14,39 @@ See the License for the specific language governing permissions and
 limitations under the License. -->
 
 <template>
-  <RkEcharts ref="chart" :option="option" :autoResize="true" />
+  <rk-panel :title="title">
+    <RkEcharts height="215px" :option="responseConfig" />
+  </rk-panel>
 </template>
+
 <script lang="ts">
-  import { Vue, Component, Prop } from 'vue-property-decorator';
+  import Vue from 'vue';
+  import { Component, Prop } from 'vue-property-decorator';
 
   @Component
-  export default class ChartBar extends Vue {
-    @Prop() private data!: any;
+  export default class ChartHeap extends Vue {
+    @Prop() private title!: string;
+    @Prop() private stateDashboard!: any;
     @Prop() private intervalTime!: any;
-    public resize() {
-      const chart: any = this.$refs.chart;
-      chart.myChart.resize();
-    }
-    get option() {
-      const keys = Object.keys(this.data || {}).filter((i: any) => Array.isArray(this.data[i]) && this.data[i].length);
-      const temp = keys.map((i: string, index: number) => {
-        return {
-          data: this.data[i].map((item: any, itemIndex: number) => [this.intervalTime[itemIndex], item]),
-          name: i,
-          type: 'bar',
-          symbol: 'none',
-          barMaxWidth: 10,
-          stack: '总量',
-          lineStyle: {
-            width: 1.5,
-            type: 'dotted',
-          },
-        };
-      });
-      let color: string[] = [];
-      switch (keys.length) {
-        case 2:
-          color = ['#FF6A84', '#a0b1e6'];
-          break;
-        case 1:
-          color = ['#3f96e3'];
-          break;
-        default:
-          color = [
-            '#30A4EB',
-            '#45BFC0',
-            '#FFCC55',
-            '#FF6A84',
-            '#a0a7e6',
-            '#c23531',
-            '#2f4554',
-            '#61a0a8',
-            '#d48265',
-            '#91c7ae',
-            '#749f83',
-            '#ca8622',
-            '#bda29a',
-            '#6e7074',
-            '#546570',
-            '#c4ccd3',
-          ];
-          break;
-      }
+    get responseConfig() {
       return {
-        color,
+        color: ['#3f96e3', '#3fbde3'],
         tooltip: {
           trigger: 'axis',
           backgroundColor: 'rgb(50,50,50)',
           textStyle: {
             fontSize: 13,
+            color: '#ccc',
           },
-          enterable: true,
-          extraCssText: 'max-height: 300px; overflow: auto;',
         },
         legend: {
-          type: 'scroll',
-          show: keys.length === 1 ? false : true,
           icon: 'circle',
           top: 0,
           left: 0,
           itemWidth: 12,
         },
         grid: {
-          top: keys.length === 1 ? 15 : 40,
+          top: 10,
           left: 0,
           right: 10,
           bottom: 5,
@@ -115,7 +69,34 @@ limitations under the License. -->
           splitLine: { lineStyle: { color: '#c1c5ca41', type: 'dashed' } },
           axisLabel: { color: '#9da5b2', fontSize: '11' },
         },
-        series: temp,
+        series: [
+          {
+            data: this.stateDashboard.instanceInfo.heap.map((i: any, index: number) => [
+              this.intervalTime[index],
+              (i.value / 1048576).toFixed(2),
+            ]),
+            name: this.stateDashboard.instanceInfo.heap.length ? 'Value' : null,
+            type: 'line',
+            symbol: 'none',
+            areaStyle: {},
+            lineStyle: {
+              width: 1.5,
+            },
+          },
+          {
+            data: this.stateDashboard.instanceInfo.heap.map((i: any, index: number) => [
+              this.intervalTime[index],
+              ((this.stateDashboard.instanceInfo.maxHeap[index].value - i.value) / 1048576).toFixed(2),
+            ]),
+            name: this.stateDashboard.instanceInfo.heap.length ? 'Free' : null,
+            type: 'line',
+            symbol: 'none',
+            areaStyle: {},
+            lineStyle: {
+              width: 1.5,
+            },
+          },
+        ],
       };
     }
   }
diff --git a/src/views/components/dashboard/charts/chart-heatmap.vue b/src/views/components/dashboard/charts/chart-heatmap.vue
index 5d95b9c..27d6c2f 100644
--- a/src/views/components/dashboard/charts/chart-heatmap.vue
+++ b/src/views/components/dashboard/charts/chart-heatmap.vue
@@ -64,6 +64,7 @@ limitations under the License. -->
           formatter: (a: any) => `${a.data[1] * 100}${this.item.unit}  [ ${a.data[2]} ]`,
           textStyle: {
             fontSize: 13,
+            color: '#ccc',
           },
         },
         grid: {
diff --git a/src/views/components/dashboard/charts/chart-line.vue b/src/views/components/dashboard/charts/chart-line.vue
index feef6a4..1ba0622 100644
--- a/src/views/components/dashboard/charts/chart-line.vue
+++ b/src/views/components/dashboard/charts/chart-line.vue
@@ -18,18 +18,41 @@ limitations under the License. -->
 </template>
 <script lang="ts">
   import { Vue, Component, Prop } from 'vue-property-decorator';
+  import { Event } from '@/types/dashboard';
 
   @Component
   export default class ChartLine extends Vue {
     @Prop() private data!: any;
     @Prop() private type!: string;
     @Prop() private intervalTime!: any;
+    @Prop() private itemEvents!: Event[];
     public resize() {
       const chart: any = this.$refs.chart;
       chart.myChart.resize();
     }
     get option() {
       const keys = Object.keys(this.data || {}).filter((i: any) => Array.isArray(this.data[i]) && this.data[i].length);
+      const startP = keys.length > 1 ? 50 : 15;
+      const diff = 10;
+      const markAreas = (this.itemEvents || []).map((event: Event, index: number) => {
+        return [
+          {
+            name: `${event.name}:${event.type}`,
+            xAxis: event.startTime,
+            y: startP + diff * index,
+            itemStyle: {
+              borderWidth: 2,
+              borderColor: event.type === 'Normal' ? '#5dc859' : '#FF0087',
+              color: event.type === 'Normal' ? '#5dc859' : '#FF0087',
+            },
+          },
+          {
+            name: event.message,
+            xAxis: event.endTime,
+            y: startP + diff * (index + 1),
+          },
+        ];
+      });
       const temp = keys.map((i: any, index: number) => {
         const serie: any = {
           data: this.data[i].map((item: any, itemIndex: number) => [this.intervalTime[itemIndex], item]),
@@ -41,6 +64,23 @@ limitations under the License. -->
             width: 1.5,
             type: 'solid',
           },
+          markArea:
+            index === 0
+              ? {
+                  silent: false,
+                  data: markAreas,
+                  label: {
+                    show: false,
+                    width: 60,
+                  },
+                  emphasis: {
+                    label: {
+                      position: 'bottom',
+                      show: true,
+                    },
+                  },
+                }
+              : undefined,
         };
         if (this.type === 'areaChart') {
           serie.areaStyle = {
@@ -85,6 +125,7 @@ limitations under the License. -->
           backgroundColor: 'rgb(50,50,50)',
           textStyle: {
             fontSize: 13,
+            color: '#ccc',
           },
           enterable: true,
           extraCssText: 'max-height: 300px; overflow: auto;',
diff --git a/src/views/components/dashboard/charts/chart-sankey.vue b/src/views/components/dashboard/charts/chart-sankey.vue
new file mode 100644
index 0000000..13cd062
--- /dev/null
+++ b/src/views/components/dashboard/charts/chart-sankey.vue
@@ -0,0 +1,67 @@
+<!-- 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>
+  <RkEcharts ref="chart" :option="option" :autoResize="true" />
+</template>
+
+<script lang="ts">
+  import Vue from 'vue';
+  import { Component, Prop } from 'vue-property-decorator';
+
+  @Component
+  export default class ChartSankey extends Vue {
+    @Prop() private title!: string;
+    @Prop() private data!: any;
+    @Prop() private intervalTime!: any;
+    get option() {
+      return {
+        tooltip: {
+          trigger: 'item',
+          triggerOn: 'mousemove',
+          backgroundColor: 'rgb(50,50,50)',
+          textStyle: {
+            fontSize: 13,
+            color: '#ccc',
+          },
+          formatter: (a: any) => a.data.tip,
+        },
+        series: [
+          {
+            type: 'sankey',
+            left: '30px',
+            top: '20px',
+            bottom: '20px',
+            label: {
+              formatter: (a: any) => a.data.content,
+            },
+            animation: false,
+            color: ['#bf99f8', '#3fe1da', '#6be6c1', '#3fcfdc', '#626c91', '#3fbcde', '#a0a7e6', '#3fa9e1', '#96dee8'],
+            data: this.data.nodes,
+            links: this.data.calls,
+            itemStyle: {
+              borderWidth: 0,
+            },
+            lineStyle: {
+              color: 'source',
+              opacity: 0.12,
+            },
+          },
+        ],
+        visualMap: this.data.visualMap,
+      };
+    }
+  }
+</script>
diff --git a/src/views/components/dashboard/constant.ts b/src/views/components/dashboard/constant.ts
index fcd3950..e104c49 100644
--- a/src/views/components/dashboard/constant.ts
+++ b/src/views/components/dashboard/constant.ts
@@ -29,3 +29,25 @@ export enum QueryTypes {
   READHEATMAP = 'readHeatMap',
   ReadSampledRecords = 'readSampledRecords',
 }
+export const UpdateDashboardEvents = 'UpdateDashboardEvents';
+export const SeriesTypes = [
+  { key: 'Service', label: 'Service Events' },
+  { key: 'Endpoint', label: 'Service Endpoint Events' },
+  { key: 'ServiceInstance', label: 'Service Instance Events' },
+];
+export const EventsHeaders = [
+  { text: 'eventID', class: 'uuid' },
+  { text: 'eventName', class: 'name' },
+  { text: 'eventsType', class: 'type' },
+  { text: 'startTime', class: 'startTime' },
+  { text: 'endTime', class: 'endTime' },
+];
+export const EventsDetailHeaders = [
+  { text: 'eventID', class: 'uuid' },
+  { text: 'eventName', class: 'name' },
+  { text: 'eventsType', class: 'type' },
+  { text: 'startTime', class: 'startTime' },
+  { text: 'endTime', class: 'endTime' },
+  { text: 'eventsMessage', class: 'message' },
+  { text: 'eventsParameters', class: 'parameters' },
+];
diff --git a/src/views/components/dashboard/dashboard-comp.vue b/src/views/components/dashboard/dashboard-comp.vue
index d03d57d..6614a5f 100644
--- a/src/views/components/dashboard/dashboard-comp.vue
+++ b/src/views/components/dashboard/dashboard-comp.vue
@@ -17,7 +17,7 @@ limitations under the License. -->
     <nav class="rk-dashboard-comp-nav mb-15">
       <a
         class="rk-dashboard-comp-nav-i b mr-20"
-        v-if="value.length"
+        v-show="value.length"
         @click="
           current = key;
           configMode = false;
diff --git a/src/views/components/dashboard/dashboard-item.vue b/src/views/components/dashboard/dashboard-item.vue
index 8df84fc..dabf924 100644
--- a/src/views/components/dashboard/dashboard-item.vue
+++ b/src/views/components/dashboard/dashboard-item.vue
@@ -15,23 +15,21 @@ limitations under the License. -->
 <template>
   <div class="rk-dashboard-item" :class="`g-sm-${width}`" :style="`height:${height}px;`">
     <div class="rk-dashboard-item-title ell">
-      <svg class="icon cp red r" v-show="rocketGlobal.edit" @click="deleteItem(index)">
-        <use xlink:href="#file-deletion"></use>
-      </svg>
+      <span v-show="rocketGlobal.edit" @click="deleteItem(index)">
+        <rk-icon class="r edit red" icon="file-deletion" />
+      </span>
       <span>{{ title }}</span>
       <span v-show="unit"> ( {{ unit }} ) </span>
       <span v-show="status === 'UNKNOWN'" class="item-status">( {{ $t('unknownMetrics') }} )</span>
       <span v-show="!rocketGlobal.edit && !pageTypes.includes(type)" @click="editComponentConfig">
-        <svg class="icon cp r">
-          <use xlink:href="#lock"></use>
-        </svg>
+        <rk-icon class="r edit" icon="keyboard_control" v-tooltip:bottom="{ content: $t('editConfig') }" />
       </span>
       <span v-show="!rocketGlobal.edit && itemConfig.chartType === 'ChartTable'" @click="copyTable">
         <rk-icon class="r cp" icon="review-list" />
       </span>
     </div>
-    <div class="rk-dashboard-item-body">
-      <div style="height:100%; width:100%">
+    <div class="rk-dashboard-item-body" ref="chartBody">
+      <div style="height:100%;width:100%">
         <component
           :is="rocketGlobal.edit ? 'ChartEdit' : itemConfig.chartType"
           ref="chart"
@@ -40,6 +38,7 @@ limitations under the License. -->
           :intervalTime="intervalTime"
           :data="chartSource"
           :type="type"
+          :itemEvents="itemEvents"
           @updateStatus="(type, value) => setStatus(type, value)"
         ></component>
       </div>
@@ -70,11 +69,14 @@ limitations under the License. -->
   import charts from './charts';
   import dayjs from 'dayjs';
 
-  import { QueryTypes } from './constant';
+  import { QueryTypes, UpdateDashboardEvents } from './constant';
   import { TopologyType, ObjectsType } from '../../../constants/constant';
   import { CalculationType } from './charts/constant';
   import { State as globalState } from '@/store/modules/global';
   import { State as optionState } from '@/store/modules/global/selectors';
+  import { State as rocketData } from '@/store/modules/dashboard/dashboard-data';
+  import { Event } from '@/types/dashboard';
+  import { EntityType } from './charts/constant';
   import copy from '@/utils/copy';
 
   @Component({
@@ -82,6 +84,7 @@ limitations under the License. -->
   })
   export default class DashboardItem extends Vue {
     @State('rocketbot') private rocketGlobal!: globalState;
+    @State('rocketData') private rocketData!: rocketData;
     @Mutation('EDIT_COMP_CONFIG') private EDIT_COMP_CONFIG: any;
     @Mutation('DELETE_COMP') private DELETE_COMP: any;
     @Mutation('rocketTopo/DELETE_TOPO_ENDPOINT') private DELETE_TOPO_ENDPOINT: any;
@@ -95,7 +98,7 @@ limitations under the License. -->
     @Prop() private updateObjects!: string;
     @Prop() private rocketOption!: optionState;
 
-    private pageTypes = [TopologyType.TOPOLOGY_ENDPOINT, TopologyType.TOPOLOGY_INSTANCE] as any[];
+    private pageTypes = [TopologyType.TOPOLOGY_ENDPOINT, TopologyType.TOPOLOGY_INSTANCE] as string[];
     private dialogConfigVisible = false;
     private status = 'UNKNOWN';
     private title = 'Title';
@@ -104,6 +107,7 @@ limitations under the License. -->
     private height = 300;
     private chartSource: any = {};
     private itemConfig: any = {};
+    private itemEvents: Event[] = [];
 
     private created() {
       this.status = this.item.metricType;
@@ -112,6 +116,7 @@ limitations under the License. -->
       this.height = this.item.height;
       this.unit = this.item.unit;
       this.itemConfig = this.item;
+      this.itemEvents = this.eventsFilter();
       const types = [
         ObjectsType.UPDATE_INSTANCES,
         ObjectsType.UPDATE_ENDPOINTS,
@@ -339,8 +344,49 @@ limitations under the License. -->
       }
     }
 
+    private eventsFilter() {
+      const allEvents = [
+        ...this.rocketData.serviceEvents,
+        ...this.rocketData.serviceInstanceEvents,
+        ...this.rocketData.endpointEvents,
+      ];
+
+      let events = allEvents.filter(
+        (item) =>
+          this.itemConfig.entityType === item.entityType &&
+          item.checked &&
+          ((item.source.service === this.rocketOption.currentService.label &&
+            (item.source.serviceInstance === this.rocketOption.currentInstance.label ||
+              item.source.endpoint === this.rocketOption.currentEndpoint.label)) ||
+            (item.entityType === EntityType[0].key && item.source.service === this.rocketOption.currentService.label)),
+      );
+      events = events.filter((d: Event, index: number) => index < this.setEventsLength());
+
+      return events;
+    }
+
+    private setEventsLength() {
+      const body: any = this.$refs.chartBody;
+      if (!body) {
+        return 0;
+      }
+      const keys = Object.keys(this.chartSource || {}).filter(
+        (i: any) => Array.isArray(this.chartSource[i]) && this.chartSource[i].length,
+      );
+      const startP = keys.length > 1 ? 50 : 15;
+      const endP = keys.length > 1 ? 0 : 40;
+      const eventNum = parseInt(String((body.offsetHeight - startP - endP) / 10), 10);
+
+      return eventNum;
+    }
+
+    // watch selectors and events
     @Watch('rocketOption.updateDashboard')
     private watchCurrentSelectors() {
+      this.itemEvents = this.eventsFilter();
+      if (this.rocketOption.updateDashboard.key.includes(UpdateDashboardEvents)) {
+        return;
+      }
       setTimeout(() => {
         this.chartRender();
       }, 1000);
@@ -362,6 +408,9 @@ limitations under the License. -->
     flex-direction: column;
     padding-left: 5px;
     padding-right: 5px;
+    .edit {
+      cursor: pointer;
+    }
   }
   .dashboard-item-shadow {
     background-color: #448dfe15;
diff --git a/src/views/components/dashboard/tool-bar/dashboard-events.vue b/src/views/components/dashboard/tool-bar/dashboard-events.vue
new file mode 100644
index 0000000..2c271da
--- /dev/null
+++ b/src/views/components/dashboard/tool-bar/dashboard-events.vue
@@ -0,0 +1,490 @@
+<!-- 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="event-list flex-h">
+    <div class="rk-dashboard-tool-btn" @click="viewEventsList" v-show="enableEvents">
+      <rk-icon class="lg" icon="settings" v-tooltip:left="{ content: $t('setEvent') }" />
+    </div>
+    <div class="rk-dashboard-tool-btn" @click="setEnbleEvents">
+      <rk-icon
+        class="lg"
+        :class="enableEvents ? 'blue' : ''"
+        icon="format_indent_increase"
+        v-tooltip:left="{ content: enableEvents ? $t('disableEvents') : $t('enableEvents') }"
+      />
+    </div>
+    <rk-sidebox width="1000px" :fixed="true" :show.sync="dialogEventVisible" @closeSideboxCallback="updateEvents">
+      <div class="config-box">
+        <div class="series-type" v-show="type === pageEventsType.DASHBOARD_EVENTS">
+          <label class="title">{{ $t('eventSeries') }}</label>
+          <RkSelect
+            :mode="'multiple'"
+            :current="rocketComps.currentSeriesType"
+            :data="seriesTypes"
+            @onChoose="(item) => changeSeriesType(item)"
+          />
+        </div>
+        <div v-show="type === pageEventsType.DASHBOARD_EVENTS">
+          <div class="title">{{ $t('serviceEvents') }}</div>
+          <ul>
+            <li class="header">
+              <span class="check">
+                <input type="checkbox" v-model="checkAllServiceEvents" @click="checkServiceEvents" />
+              </span>
+              <span v-for="(item, index) of eventsHeaders" :class="item.class" :key="item.class + index">{{
+                $t(item.text)
+              }}</span>
+            </li>
+            <li v-show="!rocketComps.serviceEvents.length">{{ $t('noData') }}</li>
+            <li v-for="event in rocketComps.serviceEvents" :key="event.uuid" @click="viewEventDetail(event)">
+              <span class="check">
+                <input type="checkbox" :checked="!!event.checked" @click="selectEvents(event)" />
+              </span>
+              <span v-for="(item, index) of eventsHeaders" :class="item.class" :key="event.uuid + index">{{
+                event[item.class]
+              }}</span>
+            </li>
+          </ul>
+        </div>
+        <div v-show="type !== pageEventsType.TOPO_ENDPOINT_EVENTS">
+          <div class="title">{{ $t('instanceEvents') }}</div>
+          <ul>
+            <li class="header">
+              <span class="check">
+                <input type="checkbox" v-model="checkAllInstanceEvents" @click="checkInstanceEvents" />
+              </span>
+              <span v-for="(item, index) of eventsHeaders" :class="item.class" :key="item.class + index">{{
+                $t(item.text)
+              }}</span>
+            </li>
+            <li v-show="!rocketComps.serviceInstanceEvents.length">{{ $t('noData') }}</li>
+            <li v-for="event in rocketComps.serviceInstanceEvents" :key="event.uuid" @click="viewEventDetail(event)">
+              <span class="check">
+                <input type="checkbox" :checked="!!event.checked" @click="selectEvents(event)" />
+              </span>
+              <span v-for="(item, index) of eventsHeaders" :class="item.class" :key="event.uuid + index">{{
+                event[item.class]
+              }}</span>
+            </li>
+          </ul>
+        </div>
+        <div v-show="type !== pageEventsType.TOPO_INSTANCE_EVENTS">
+          <div class="title">{{ $t('endpointEvents') }}</div>
+          <ul>
+            <li class="header">
+              <span class="check">
+                <input type="checkbox" v-model="checkAllEndpointEvents" @click="checkEndpointEvents" />
+              </span>
+              <span v-for="(item, index) of eventsHeaders" :class="item.class" :key="item.class + index">{{
+                $t(item.text)
+              }}</span>
+            </li>
+            <li v-show="!rocketComps.endpointEvents.length">{{ $t('noData') }}</li>
+            <li v-for="event in rocketComps.endpointEvents" :key="event.uuid" @click="viewEventDetail(event)">
+              <span class="check">
+                <input type="checkbox" :checked="!!event.checked" @click="selectEvents(event)" />
+              </span>
+              <span v-for="(item, index) of eventsHeaders" :class="item.class" :key="event.uuid + index">{{
+                event[item.class]
+              }}</span>
+            </li>
+          </ul>
+        </div>
+        <div class="save-btn bg-blue" @click="updateEvents">{{ $t('setEvent') }}</div>
+      </div>
+    </rk-sidebox>
+    <rk-sidebox :width="'1000px'" :show.sync="showEventDetail" :title="$t('eventDetail')">
+      <div class="event-detail">
+        <div class="mb-10 rk-flex" v-for="(item, index) in eventsDetailHeaders" :key="index">
+          <span>{{ $t(item.text) }}: </span>
+          <span v-if="item.class === 'parameters'">
+            <span v-for="(item, index) of currentEvent[item.class]" :key="index"
+              >{{ item.key }}={{ item.value }};
+            </span>
+          </span>
+          <span v-else>{{ currentEvent[item.class] }}</span>
+        </div>
+      </div>
+    </rk-sidebox>
+  </div>
+</template>
+<script lang="ts">
+  import { Mutation, Action } from 'vuex-class';
+  import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
+  import { State as rocketData } from '@/store/modules/dashboard/dashboard-data';
+  import { Event } from '@/types/dashboard';
+  import { DurationTime, Option } from '@/types/global';
+  import { UpdateDashboardEvents, SeriesTypes, EventsHeaders, EventsDetailHeaders } from '../constant';
+  import { EntityType } from '../charts/constant';
+  import { State as optionState } from '@/store/modules/global/selectors';
+  import { PageEventsType } from '@/constants/constant';
+
+  @Component
+  export default class DashboardEvent extends Vue {
+    @Prop() private rocketComps!: rocketData;
+    @Prop() private stateDashboard!: optionState;
+    @Prop() private durationTime!: DurationTime;
+    @Prop() private type!: string;
+    @Mutation('SET_CHECKED_EVENTS') private SET_CHECKED_EVENTS: any;
+    @Mutation('UPDATE_DASHBOARD') private UPDATE_DASHBOARD: any;
+    @Mutation('SET_ENABLE_EVENTS') private SET_ENABLE_EVENTS: any;
+    @Mutation('SET_DASHBOARD_EVENTS') private SET_DASHBOARD_EVENTS: any;
+    @Mutation('SET_EVENTS_PAGE_TYPE') private SET_EVENTS_PAGE_TYPE!: (type: string) => void;
+    @Mutation('SET_CURRENT_SERIES_TYPE') private SET_CURRENT_SERIES_TYPE!: (data: {
+      item: Option;
+      index: number;
+    }) => void;
+    @Mutation('SET_CLEAR_SELECTED_EVENTS') private SET_CLEAR_SELECTED_EVENTS!: () => void;
+    @Mutation('SET_EVENTS') private SET_EVENTS: any;
+    @Action('GET_EVENT') private GET_EVENT: any;
+
+    private dialogEventVisible: boolean = false;
+    private enableEvents: boolean = false;
+    private selectedEvents: Event[] = [];
+    private checkAllServiceEvents: boolean = false;
+    private checkAllInstanceEvents: boolean = false;
+    private checkAllEndpointEvents: boolean = false;
+    private showEventDetail: boolean = false;
+    private currentEvent: Event | {} = {};
+    private pageEventsType = PageEventsType;
+    private seriesTypes = SeriesTypes;
+    private eventsHeaders = EventsHeaders;
+    private eventsDetailHeaders = EventsDetailHeaders;
+
+    private created() {
+      this.initEvents();
+      this.SET_EVENTS([
+        () => {
+          if (!this.enableEvents) {
+            return;
+          }
+          this.fetchEvents();
+        },
+      ]);
+    }
+
+    private initEvents() {
+      this.SET_EVENTS_PAGE_TYPE(this.type);
+      if (this.type === this.pageEventsType.DASHBOARD_EVENTS) {
+        this.SET_CURRENT_SERIES_TYPE({ item: this.seriesTypes[0], index: -1 });
+        this.SET_CURRENT_SERIES_TYPE({ item: this.seriesTypes[2], index: -1 });
+      } else if (this.type === this.pageEventsType.TOPO_ENDPOINT_EVENTS) {
+        this.SET_CURRENT_SERIES_TYPE({ item: this.seriesTypes[1], index: -1 });
+      } else {
+        this.SET_CURRENT_SERIES_TYPE({ item: this.seriesTypes[2], index: -1 });
+      }
+      this.updateAllChecked();
+    }
+
+    private viewEventsList() {
+      this.dialogEventVisible = true;
+      this.updateAllChecked();
+    }
+
+    private updateAllChecked() {
+      this.checkAllServiceEvents = this.checkAllEvents(this.rocketComps.serviceEvents);
+      this.checkAllEndpointEvents = this.checkAllEvents(this.rocketComps.endpointEvents);
+      this.checkAllInstanceEvents = this.checkAllEvents(this.rocketComps.serviceInstanceEvents);
+    }
+
+    private checkAllEvents(events: Event[]) {
+      if (!events.length) {
+        return false;
+      }
+      const selectedServiceEvents = events.filter((item: Event) => item.checked);
+      if (selectedServiceEvents.length === events.length) {
+        return true;
+      } else {
+        return false;
+      }
+    }
+
+    private viewEventDetail(event: Event) {
+      this.showEventDetail = true;
+      this.currentEvent = event;
+    }
+
+    private changeSeriesType(item: Option) {
+      const index = this.rocketComps.currentSeriesType.findIndex((d: Option) => item.key === d.key);
+
+      this.SET_CURRENT_SERIES_TYPE({ item, index });
+      this.SET_CLEAR_SELECTED_EVENTS();
+      const selectedEvents: Event[] = [];
+      for (const type of this.rocketComps.currentSeriesType) {
+        if (type.key === this.seriesTypes[0].key) {
+          selectedEvents.push(...this.rocketComps.serviceEvents.slice(0, 3));
+        } else if (type.key === this.seriesTypes[1].key) {
+          selectedEvents.push(...this.rocketComps.endpointEvents.slice(0, 3));
+        } else {
+          selectedEvents.push(...this.rocketComps.serviceInstanceEvents.slice(0, 3));
+        }
+      }
+      this.selectedEvents = selectedEvents.map((d: Event) => {
+        d.checked = true;
+        return d;
+      });
+      this.SET_CHECKED_EVENTS(this.selectedEvents);
+      this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+      this.selectedEvents = [];
+      this.updateAllChecked();
+    }
+
+    private setEnbleEvents() {
+      this.enableEvents = !this.enableEvents;
+      this.SET_ENABLE_EVENTS(this.enableEvents);
+      if (!this.enableEvents) {
+        this.clearAllEvents();
+        this.checkAllServiceEvents = false;
+        this.checkAllInstanceEvents = false;
+        this.checkAllEndpointEvents = false;
+        this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+        return;
+      }
+      this.fetchEvents();
+    }
+
+    private clearAllEvents() {
+      this.SET_DASHBOARD_EVENTS({ events: [], type: EntityType[0].key });
+      this.SET_DASHBOARD_EVENTS({ events: [], type: EntityType[2].key });
+      this.SET_DASHBOARD_EVENTS({ events: [], type: EntityType[3].key });
+    }
+
+    private fetchEvents() {
+      if (this.type === PageEventsType.DASHBOARD_EVENTS) {
+        Promise.all([this.fetchServiceEvents(), this.fetchInstanceEvents(), this.fetchEndpointEvents()]).then(() => {
+          this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+        });
+        return;
+      }
+      if (this.type === PageEventsType.TOPO_INSTANCE_EVENTS) {
+        this.fetchInstanceEvents().then(() => {
+          this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+        });
+        return;
+      }
+      if (this.type === PageEventsType.TOPO_ENDPOINT_EVENTS) {
+        this.fetchEndpointEvents().then(() => {
+          this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+        });
+      }
+    }
+
+    private fetchServiceEvents() {
+      return this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboard.currentService.label,
+          },
+        },
+        type: EntityType[0].key,
+      });
+    }
+
+    private fetchInstanceEvents() {
+      return this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboard.currentService.label,
+            serviceInstance: this.stateDashboard.currentInstance.label,
+          },
+        },
+        type: EntityType[3].key,
+      });
+    }
+
+    private fetchEndpointEvents() {
+      return this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboard.currentService.label,
+            endpoint: this.stateDashboard.currentEndpoint.label,
+          },
+        },
+        type: EntityType[2].key,
+      }).then(() => {
+        if (this.type === PageEventsType.DASHBOARD_EVENTS) {
+          return;
+        }
+        this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+      });
+    }
+
+    private selectEvents(data: Event, e: any) {
+      window.event ? (window.event.cancelBubble = true) : e.stopPropagation();
+      const index = this.selectedEvents.findIndex(
+        (item: Event) => item.uuid === data.uuid && item.entityType === data.entityType,
+      );
+
+      data.checked = !data.checked;
+      if (index > -1) {
+        this.selectedEvents[index].checked = data.checked;
+      } else {
+        this.selectedEvents.push(data);
+      }
+    }
+
+    private updateEvents() {
+      this.SET_CHECKED_EVENTS(this.selectedEvents);
+      this.UPDATE_DASHBOARD({ key: UpdateDashboardEvents + new Date().getTime() });
+      this.selectedEvents = [];
+      this.dialogEventVisible = false;
+    }
+
+    private checkServiceEvents() {
+      for (const event of this.rocketComps.serviceEvents) {
+        if (this.checkAllServiceEvents) {
+          event.checked = false;
+        } else {
+          event.checked = true;
+        }
+        this.selectedEvents = this.selectedEvents.filter(
+          (item: Event) => !(item.entityType === EntityType[0].label && item.uuid === event.uuid),
+        );
+      }
+      if (!this.checkAllServiceEvents) {
+        this.selectedEvents.push(...this.rocketComps.serviceEvents);
+      }
+    }
+
+    private checkInstanceEvents() {
+      for (const event of this.rocketComps.serviceInstanceEvents) {
+        if (this.checkAllInstanceEvents) {
+          event.checked = false;
+        } else {
+          event.checked = true;
+        }
+        this.selectedEvents = this.selectedEvents.filter(
+          (item: Event) => !(item.entityType === EntityType[3].label && item.uuid === event.uuid),
+        );
+      }
+      if (!this.checkAllInstanceEvents) {
+        this.selectedEvents.push(...this.rocketComps.serviceInstanceEvents);
+      }
+    }
+
+    private checkEndpointEvents() {
+      for (const event of this.rocketComps.endpointEvents) {
+        if (this.checkAllEndpointEvents) {
+          event.checked = false;
+        } else {
+          event.checked = true;
+        }
+        this.selectedEvents = this.selectedEvents.filter(
+          (item: Event) => !(item.entityType === EntityType[2].label && item.uuid === event.uuid),
+        );
+      }
+      if (!this.checkAllEndpointEvents) {
+        this.selectedEvents.push(...this.rocketComps.endpointEvents);
+      }
+    }
+
+    private beforeDestroy() {
+      this.clearAllEvents();
+      this.SET_ENABLE_EVENTS(false);
+      this.SET_EVENTS([]);
+    }
+
+    @Watch('durationTime')
+    private watchDurationTime() {
+      if (!this.enableEvents) {
+        return;
+      }
+      this.fetchEvents();
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .event-list {
+    color: #444;
+  }
+  .rk-dashboard-tool-btn {
+    background-color: rgba(255, 255, 255, 0.07);
+    border-radius: 4px;
+    margin-left: 5px;
+    padding: 3px;
+    color: #efefef;
+    cursor: pointer;
+  }
+  .rk-sidebox-title {
+    color: #444;
+  }
+  .config-box {
+    color: #444;
+    .series-type {
+      margin-bottom: 20px;
+      width: 400px;
+    }
+    .title {
+      font-size: 14px;
+      font-weight: bold;
+    }
+    .save-btn {
+      width: 120px;
+      height: 30px;
+      line-height: 30px;
+      text-align: center;
+      border-radius: 4px;
+      color: #fff;
+      cursor: pointer;
+    }
+    ul {
+      max-height: 200px;
+      min-height: 100px;
+      overflow: auto;
+      margin-bottom: 20px;
+      .header {
+        font-weight: bold;
+      }
+      .check {
+        width: 30px;
+        input {
+          cursor: pointer;
+        }
+      }
+      .starTime,
+      .endTime {
+        width: 150px;
+      }
+      .uuid,
+      .parameters {
+        width: 280px;
+      }
+      .message {
+        width: 220px;
+      }
+    }
+    li {
+      cursor: pointer;
+      span {
+        width: 150px;
+        height: 20px;
+        line-height: 20px;
+        text-align: center;
+        display: inline-block;
+        border-bottom: 1px solid #ccc;
+        overflow: hidden;
+      }
+    }
+  }
+  .event-detail {
+    color: #444;
+  }
+</style>
diff --git a/src/views/components/dashboard/tool-bar/tool-bar-btns.vue b/src/views/components/dashboard/tool-bar/tool-bar-btns.vue
index 28e9b40..8d1f5ad 100644
--- a/src/views/components/dashboard/tool-bar/tool-bar-btns.vue
+++ b/src/views/components/dashboard/tool-bar/tool-bar-btns.vue
@@ -15,39 +15,25 @@ limitations under the License. -->
 
 <template>
   <div class="flex-h btn-box">
-    <div class="rk-dashboard-bar-btn">
-      <span v-tooltip:bottom="{ content: rocketGlobal.edit ? 'view' : 'edit' }">
-        <svg
-          class="icon lg vm cp rk-btn ghost"
-          :style="`color:${!rocketGlobal.edit ? '' : '#ffc107'}`"
-          @click="handleSetEdit"
-        >
-          <use :xlink:href="!rocketGlobal.edit ? '#lock' : '#lock-open'"></use>
-        </svg>
-      </span>
+    <div class="rk-dashboard-bar-btn" @click="handleSetEdit">
+      <rk-icon
+        class="lg"
+        :style="`color:${!rocketGlobal.edit ? '' : '#ffc107'}`"
+        :icon="!rocketGlobal.edit ? 'lock' : 'lock-open'"
+        v-tooltip:bottom="{ content: rocketGlobal.edit ? 'view' : 'edit' }"
+      />
     </div>
-    <div class="rk-dashboard-bar-btn">
-      <span v-tooltip:bottom="{ content: 'import' }">
-        <input id="tool-bar-file" type="file" name="file" title="" accept=".json" @change="importData" />
-        <label class="rk-btn ghost input-label" for="tool-bar-file">
-          <svg class="icon lg vm cp " :style="`marginTop: 0px`">
-            <use :xlink:href="'#folder_open'"></use>
-          </svg>
-        </label>
-      </span>
+    <div class="rk-dashboard-bar-btn" v-tooltip:bottom="{ content: 'import' }">
+      <input id="tool-bar-file" type="file" name="file" title="" accept=".json" @change="importData" />
+      <label for="tool-bar-file">
+        <rk-icon class="lg import" icon="folder_open" />
+      </label>
     </div>
-    <div class="rk-dashboard-bar-btn">
-      <span v-tooltip:bottom="{ content: 'export' }">
-        <svg class="icon lg vm cp rk-btn ghost" @click="exportData">
-          <use :xlink:href="'#save_alt'"></use>
-        </svg>
-      </span>
+    <div class="rk-dashboard-bar-btn" @click="exportData">
+      <rk-icon class="lg" icon="save_alt" v-tooltip:bottom="{ content: 'export' }" />
     </div>
-
-    <div class="rk-dashboard-bar-btn">
-      <svg class="icon lg vm cp rk-btn ghost" @click="handleOption">
-        <use xlink:href="#retry"></use>
-      </svg>
+    <div class="rk-dashboard-bar-btn" @click="handleOption">
+      <rk-icon class="lg" icon="retry" v-tooltip:bottom="{ content: 'auto' }" />
     </div>
   </div>
 </template>
@@ -57,15 +43,17 @@ limitations under the License. -->
   import { Action, Mutation } from 'vuex-class';
   import { readFile } from '@/utils/readFile';
   import { saveFile } from '@/utils/saveFile';
-  @Component({})
+  import { DurationTime } from '@/types/global';
+  import { State as rocketData } from '@/store/modules/dashboard/dashboard-data';
+  import { State as rocketGlobal } from '@/store/modules/global';
+  import { PageTypes } from '@/constants/constant';
+
+  @Component
   export default class ToolBarBtns extends Vue {
-    @Prop() private compType!: any;
-    @Prop() private dashboardType!: any;
-    @Prop() private rocketGlobal!: any;
-    @Prop() private rocketComps!: any;
-    @Prop() private durationTime!: any;
-    @Prop() private rocketOption: any;
-    @Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
+    @Prop() private compType!: string;
+    @Prop() private rocketGlobal!: rocketGlobal;
+    @Prop() private rocketComps!: rocketData;
+    @Prop() private durationTime!: DurationTime;
     @Mutation('IMPORT_TREE') private IMPORT_TREE: any;
     @Action('SET_EDIT') private SET_EDIT: any;
     @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
@@ -74,13 +62,16 @@ limitations under the License. -->
       return this.MIXHANDLE_GET_OPTION({
         compType: this.compType,
         duration: this.durationTime,
+        pageType: PageTypes.DASHBOARD,
         keywordServiceName:
           this.rocketComps.tree[this.rocketComps.group] && this.rocketComps.tree[this.rocketComps.group].serviceGroup,
       });
     }
+
     private handleSetEdit() {
       this.SET_EDIT(!this.rocketGlobal.edit);
     }
+
     private async importData(event: any) {
       try {
         const data: any = await readFile(event);
@@ -110,9 +101,10 @@ limitations under the License. -->
 
 <style lang="scss" scoped>
   .rk-dashboard-bar-btn {
-    padding: 0 5px;
+    padding: 0 8px;
     border-right: 2px solid #252a2f;
     height: 19px;
+    cursor: pointer;
   }
   #tool-bar-file {
     display: none;
@@ -120,8 +112,13 @@ limitations under the License. -->
   .input-label {
     display: inline;
     line-height: inherit;
+    cursor: pointer;
   }
   .btn-box {
     height: 58px;
   }
+  .import {
+    margin-top: 0;
+    cursor: pointer;
+  }
 </style>
diff --git a/src/views/components/dashboard/tool-bar/tool-bar.vue b/src/views/components/dashboard/tool-bar/tool-bar.vue
index 3d67af7..6708612 100644
--- a/src/views/components/dashboard/tool-bar/tool-bar.vue
+++ b/src/views/components/dashboard/tool-bar/tool-bar.vue
@@ -18,59 +18,69 @@ limitations under the License. -->
       :rocketGlobal="rocketGlobal"
       :rocketComps="rocketComps"
       :compType="compType"
-      :dashboardType="dashboardType"
       :durationTime="durationTime"
-      :rocketOption="rocketOption"
     />
-    <div class="flex-h" v-if="compType === dashboardType.SERVICE">
-      <div class="sm grey service-search">
-        <div>{{ $t('serviceGroup') }}</div>
-        <input
-          type="text"
-          :value="rocketComps.tree[rocketComps.group].serviceGroup"
-          @change="searchServices($event.target.value)"
+    <div class="dashboard-selectors flex-h" v-if="compType === dashboardType.SERVICE">
+      <div class="flex-h">
+        <div class="sm grey service-search">
+          <div>{{ $t('serviceGroup') }}</div>
+          <input
+            type="text"
+            :value="rocketComps.tree[rocketComps.group].serviceGroup"
+            @change="searchServices($event.target.value)"
+          />
+        </div>
+        <ToolBarSelect
+          @onChoose="selectService"
+          :title="$t('currentService')"
+          :current="stateDashboard.currentService"
+          :data="stateDashboard.services"
+          icon="package"
+        />
+        <ToolBarEndpointSelect
+          @onChoose="selectEndpoint"
+          :title="$t('currentEndpoint')"
+          :current="stateDashboard.currentEndpoint"
+          :data="stateDashboard.endpoints"
+          :currentService="stateDashboard.currentService"
+          icon="code"
+        />
+        <ToolBarSelect
+          @onChoose="selectInstance"
+          :title="$t('currentInstance')"
+          :current="stateDashboard.currentInstance"
+          :data="stateDashboard.instances"
+          icon="disk"
         />
+        <a
+          class="rk-view-instance-attributes r"
+          @click="() => (dialogAttributesVisible = true)"
+          v-tooltip:bottom="{ content: $t('instanceAttributes') }"
+        >
+          <rk-icon icon="info_outline" />
+        </a>
+        <rk-sidebox
+          width="600px"
+          :fixed="true"
+          :title="`${$t('instanceAttributes')} of ${stateDashboard.currentInstance.label}`"
+          :show.sync="dialogAttributesVisible"
+          class="instance-attributes-box"
+        >
+          <div
+            class="instance-attr"
+            v-for="(attr, index) in stateDashboard.currentInstance.attributes"
+            :key="attr.name + index"
+          >
+            {{ attr.name + ' : ' + attr.value }}
+          </div>
+        </rk-sidebox>
       </div>
-      <ToolBarSelect
-        @onChoose="selectService"
-        :title="$t('currentService')"
-        :current="stateDashboard.currentService"
-        :data="stateDashboard.services"
-        icon="package"
-      />
-      <ToolBarEndpointSelect
-        @onChoose="selectEndpoint"
-        :title="$t('currentEndpoint')"
-        :current="stateDashboard.currentEndpoint"
-        :data="stateDashboard.endpoints"
-        :currentService="stateDashboard.currentService"
-        icon="code"
+      <DashboardEvent
+        :rocketComps="rocketComps"
+        :stateDashboard="stateDashboard"
+        :durationTime="durationTime"
+        :type="pageEventsType.DASHBOARD_EVENTS"
       />
-      <ToolBarSelect
-        @onChoose="selectInstance"
-        :title="$t('currentInstance')"
-        :current="stateDashboard.currentInstance"
-        :data="stateDashboard.instances"
-        icon="disk"
-      />
-      <a class="rk-view-instance-attributes r" @click="() => (dialogAttributesVisible = true)">
-        <span class="vm">{{ $t('instanceAttributes') }}</span>
-      </a>
-      <rk-sidebox
-        width="50%"
-        :fixed="true"
-        :title="`${$t('instanceAttributes')} of ${stateDashboard.currentInstance.label}`"
-        :show.sync="dialogAttributesVisible"
-        class="instance-attributes-box"
-      >
-        <div
-          class="instance-attr"
-          v-for="(attr, index) in stateDashboard.currentInstance.attributes"
-          :key="attr.name + index"
-        >
-          {{ attr.name + ' : ' + attr.value }}
-        </div>
-      </rk-sidebox>
     </div>
     <div class="flex-h" v-else-if="compType === dashboardType.BROWSER">
       <ToolBarSelect
@@ -113,18 +123,23 @@ limitations under the License. -->
   import ToolBarSelect from './tool-bar-select.vue';
   import ToolBarEndpointSelect from './tool-bar-endpoint-select.vue';
   import ToolBarBtns from './tool-bar-btns.vue';
-  import { State, Action, Mutation } from 'vuex-class';
+  import { Action, Mutation } from 'vuex-class';
+  import { EntityType } from '../charts/constant';
+  import { DurationTime, Option } from '@/types/global';
+  import { State as rocketData } from '@/store/modules/dashboard/dashboard-data';
+  import { State as rocketGlobal } from '@/store/modules/global';
+  import { State as optionState } from '@/store/modules/global/selectors';
+  import DashboardEvent from './dashboard-events.vue';
   import { DASHBOARDTYPE } from '../constant';
+  import { PageEventsType, PageTypes } from '@/constants/constant';
 
-  @Component({ components: { ToolBarSelect, ToolBarBtns, ToolBarEndpointSelect } })
+  @Component({ components: { ToolBarSelect, ToolBarBtns, ToolBarEndpointSelect, DashboardEvent } })
   export default class ToolBar extends Vue {
-    @Prop() private compType!: any;
-    @Prop() private stateDashboard!: any;
-    @Prop() private rocketGlobal!: any;
-    @Prop() private rocketComps!: any;
-    @Prop() private durationTime!: any;
-    @State('rocketOption') private rocketOption: any;
-    @Mutation('ADD_COMP') private ADD_COMP: any;
+    @Prop() private compType!: string;
+    @Prop() private stateDashboard!: optionState;
+    @Prop() private rocketGlobal!: rocketGlobal;
+    @Prop() private rocketComps!: rocketData;
+    @Prop() private durationTime!: DurationTime;
     @Mutation('SET_CURRENT_SERVICE_GROUP') private SET_CURRENT_SERVICE_GROUP: any;
     @Mutation('UPDATE_DASHBOARD') private UPDATE_DASHBOARD: any;
     @Action('SELECT_SERVICE') private SELECT_SERVICE: any;
@@ -132,8 +147,10 @@ limitations under the License. -->
     @Action('SELECT_ENDPOINT') private SELECT_ENDPOINT: any;
     @Action('SELECT_INSTANCE') private SELECT_INSTANCE: any;
     @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
+    @Action('GET_EVENT') private GET_EVENT: any;
+    private dialogAttributesVisible: boolean = false;
     private dashboardType = DASHBOARDTYPE;
-    private dialogAttributesVisible = false;
+    private pageEventsType = PageEventsType;
     get lastKey() {
       const current = this.rocketComps.tree[this.rocketComps.group].children[this.rocketComps.current].children;
       if (!current.length) {
@@ -141,14 +158,60 @@ limitations under the License. -->
       }
       return current[current.length - 1].k;
     }
-    private selectService(i: any) {
-      this.SELECT_SERVICE({ service: i, duration: this.durationTime });
+    private selectService(i: Option) {
+      if (!this.rocketComps.enableEvents) {
+        this.SELECT_SERVICE({ service: i, duration: this.durationTime });
+        return;
+      }
+      this.SELECT_SERVICE({ service: i, duration: this.durationTime, callback: this.GET_EVENT });
+      this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: i.label,
+          },
+        },
+        type: EntityType[0].key,
+      });
     }
-    private selectEndpoint(i: any) {
-      this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
+    private selectEndpoint(i: Option) {
+      if (!this.rocketComps.enableEvents) {
+        this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
+        return;
+      }
+      this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboard.currentService.label,
+            endpoint: i.label,
+          },
+        },
+        type: EntityType[2].key,
+      }).then(() => {
+        this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
+      });
     }
-    private selectInstance(i: any) {
-      this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
+    private selectInstance(i: Option) {
+      if (!this.rocketComps.enableEvents) {
+        this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
+        return;
+      }
+      this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboard.currentService.label,
+            serviceInstance: i.label,
+          },
+        },
+        type: EntityType[3].key,
+      }).then(() => {
+        this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
+      });
     }
     private searchServices(value: string) {
       this.SET_CURRENT_SERVICE_GROUP(value);
@@ -156,6 +219,7 @@ limitations under the License. -->
         compType: this.dashboardType.SERVICE,
         duration: this.durationTime,
         keywordServiceName: value,
+        pageType: PageTypes.DASHBOARD,
       }).then(() => {
         this.UPDATE_DASHBOARD();
       });
@@ -168,6 +232,10 @@ limitations under the License. -->
     flex-shrink: 0;
     color: #efefef;
     background-color: #333840;
+    .dashboard-selectors {
+      width: calc(100% - 150px);
+      justify-content: space-between;
+    }
     .instance-attributes-box {
       color: #252a2f;
     }
@@ -190,10 +258,11 @@ limitations under the License. -->
       }
     }
     .rk-view-instance-attributes {
-      background-color: #484b55;
+      background-color: rgba(255, 255, 255, 0.07);
       border-radius: 4px;
       margin-left: 5px;
-      padding: 5px 10px;
+      padding: 3px;
+      color: #efefef;
     }
   }
 </style>
diff --git a/src/views/components/dashboard/tool-group.vue b/src/views/components/dashboard/tool-group.vue
index 7e6c105..67588a3 100644
--- a/src/views/components/dashboard/tool-group.vue
+++ b/src/views/components/dashboard/tool-group.vue
@@ -63,8 +63,7 @@ limitations under the License. -->
   import { Component, Prop } from 'vue-property-decorator';
   import { Mutation, Action, Getter } from 'vuex-class';
   import { DASHBOARDTYPE } from './constant';
-  import { readFile } from '@/utils/readFile';
-  import { saveFile } from '@/utils/saveFile';
+  import { PageTypes } from '@/constants/constant';
 
   @Component({})
   export default class ToolGroup extends Vue {
@@ -118,6 +117,7 @@ limitations under the License. -->
         compType: this.compType,
         duration: this.durationTime,
         keywordServiceName: serviceGroup,
+        pageType: PageTypes.DASHBOARD,
       });
     }
     private handleHide() {
diff --git a/src/views/components/dashboard/tool-nav.vue b/src/views/components/dashboard/tool-nav.vue
index 99b008c..58f903e 100644
--- a/src/views/components/dashboard/tool-nav.vue
+++ b/src/views/components/dashboard/tool-nav.vue
@@ -19,15 +19,9 @@ limitations under the License. -->
       :key="index"
       class="mr-20"
     >
-      <a
-        class="rk-dashboard-nav-i b"
-        @click="
-          SET_CURRENT_COMPS(index);
-          RUN_EVENTS({});
-        "
-        :class="{ active: rocketComps.current == index }"
-        >{{ i.name }}</a
-      >
+      <a class="rk-dashboard-nav-i b" @click="changeComps(index)" :class="{ active: rocketComps.current == index }">{{
+        i.name
+      }}</a>
       <svg
         v-if="rocketGlobal.edit && rocketComps.current !== index"
         class="ml-5 icon cp red vm"
@@ -44,50 +38,28 @@ limitations under the License. -->
         <div class="mb-10 vm">{{ $t('createTab') }}</div>
         <div class="sm grey mb-5 mr-10">{{ $t('tabName') }}</div>
         <input class="mb-5 rk-dashboard-nav-input" type="text" v-model="name" />
-        <!-- <div class="sm grey mb-5 mr-10">{{ $t('template') }}</div>
-        <label class="dib mb-5 mr-10 sm"
-          ><input type="radio" v-model="template" value="nouse" />{{ $t('nouse') }}</label
-        >
-        <label class="dib mb-5 mr-10 sm"
-          ><input type="radio" v-model="template" value="global" />{{ $t('global') }}</label
-        >
-        <label class="dib mb-5 mr-10 sm" v-if="type === 'service'"
-          ><input type="radio" v-model="template" value="service" />{{ $t('service') }}</label
-        >
-        <label class="dib mb-5 mr-10 sm" v-if="type === 'service'"
-          ><input type="radio" v-model="template" value="endpoint" />{{ $t('endpoint') }}</label
-        >
-        <label class="dib mb-5 mr-10 sm" v-if="type === 'service'"
-          ><input type="radio" v-model="template" value="instance" />{{ $t('instance') }}</label
-        >
-        <label class="dib mb-5 mr-10 sm" v-if="type === 'database'"
-          ><input type="radio" v-model="template" value="database" />{{ $t('database') }}</label
-        > -->
         <a class="rk-btn r vm long tc confirm" @click="handleCreate">{{ $t('confirm') }}</a>
       </div>
     </a>
     <a class="rk-dashboard-import mr-10">
       <input id="tool-nav-file" class="ipt" type="file" name="file" title="" accept=".json" @change="importData" />
       <label for="tool-nav-file" class="input-label">
-        <svg class="icon open vm">
-          <use xlink:href="#folder_open"></use>
-        </svg>
+        <rk-icon class="open vm" icon="folder_open" />
       </label>
     </a>
-    <a>
-      <svg class="icon vm" @click="exportData">
-        <use xlink:href="#save_alt"></use>
-      </svg>
+    <a class="mr-10" @click="exportData">
+      <rk-icon icon="save_alt" />
     </a>
   </nav>
 </template>
 
 <script lang="ts">
   import Vue from 'vue';
-  import { Component, Prop, Model } from 'vue-property-decorator';
-  import { State, Mutation, Action } from 'vuex-class';
+  import { Component, Prop } from 'vue-property-decorator';
+  import { Getter, Mutation, Action } from 'vuex-class';
   import { readFile } from '@/utils/readFile';
   import { saveFile } from '@/utils/saveFile';
+  import { DASHBOARDTYPE } from './constant';
 
   @Component
   export default class ToolNav extends Vue {
@@ -98,9 +70,11 @@ limitations under the License. -->
     @Mutation('DELETE_COMPS_TREE') private DELETE_COMPS_TREE: any;
     @Mutation('ADD_COMPS_TREE') private ADD_COMPS_TREE: any;
     @Action('RUN_EVENTS') private RUN_EVENTS: any;
+    @Getter('durationTime') private durationTime: any;
     private name: string = '';
-    // private template: string = 'nouse';
     private show: boolean = false;
+    private dashboardType = DASHBOARDTYPE;
+
     get type() {
       return this.rocketComps.tree[this.rocketComps.group].type;
     }
@@ -114,7 +88,10 @@ limitations under the License. -->
       }
       this.ADD_COMPS_TREE({ name: this.name });
       this.handleHide();
-      // this.template = 'nouse';
+    }
+    private changeComps(index: number) {
+      this.SET_CURRENT_COMPS(index);
+      this.RUN_EVENTS({});
     }
     private async importData(event: any) {
       try {
diff --git a/src/views/components/log/log-bar.vue b/src/views/components/log/log-bar.vue
index 1e54f64..7de635a 100644
--- a/src/views/components/log/log-bar.vue
+++ b/src/views/components/log/log-bar.vue
@@ -76,7 +76,6 @@ limitations under the License. -->
 </template>
 
 <script lang="ts">
-  import { Duration, Option } from '@/types/global';
   import { Component, Vue } from 'vue-property-decorator';
   import { Action, Getter, Mutation, State } from 'vuex-class';
   import TraceSelect from '../common/trace-select.vue';
@@ -85,6 +84,7 @@ limitations under the License. -->
   import LogConditions from './log-conditions.vue';
   import { State as logState } from '@/store/modules/log/index';
   import { State as optionState } from '@/store/modules/global/selectors';
+  import { PageTypes } from '@/constants/constant';
 
   @Component({
     components: { TraceSelect, ToolBarSelect, ToolBarEndpointSelect, LogConditions },
@@ -107,14 +107,13 @@ limitations under the License. -->
     private pageNum: number = 1;
     private cateGoryBrowser = 'browser';
     private showConditionsBox = true;
-    private logPage = 'Log';
     private pageSize = 20;
 
     private beforeMount() {
       this.MIXHANDLE_GET_OPTION({
         compType: this.logState.type.key,
         duration: this.durationTime,
-        pageType: this.logPage,
+        pageType: PageTypes.LOG,
       })
         .then(() => {
           this.QUERY_LOGS_BYKEYWORDS();
@@ -146,7 +145,7 @@ limitations under the License. -->
       this.MIXHANDLE_GET_OPTION({
         compType: i.key,
         duration: this.durationTime,
-        pageType: this.logPage,
+        pageType: PageTypes.LOG,
       }).then(() => {
         this.queryLogs();
       });
diff --git a/src/views/components/log/log-conditions.vue b/src/views/components/log/log-conditions.vue
index 5035275..529743e 100644
--- a/src/views/components/log/log-conditions.vue
+++ b/src/views/components/log/log-conditions.vue
@@ -116,7 +116,7 @@ limitations under the License. -->
   import { Mutation, State } from 'vuex-class';
   import { State as globalState } from '@/store/modules/global/index';
   import { State as logState } from '@/store/modules/log/index';
-  import dateFormatStep from '@/utils/dateFormatStep';
+  import dateFormatStep from '@/utils/dateFormat';
 
   @Component({
     components: {},
diff --git a/src/views/components/log/log-service-detail.vue b/src/views/components/log/log-service-detail.vue
index 08c1515..0a521a6 100644
--- a/src/views/components/log/log-service-detail.vue
+++ b/src/views/components/log/log-service-detail.vue
@@ -42,7 +42,6 @@ limitations under the License. -->
 
 <script lang="ts">
   import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
-  import { Mutation, State } from 'vuex-class';
   import LogTable from './log-table/log-table.vue';
   import { ServiceLogDetail } from './log-table/log-constant';
   import { formatJson } from '../../../utils/formatJson';
diff --git a/src/views/components/profile/profile-task.vue b/src/views/components/profile/profile-task.vue
index 343c3a7..8015b21 100644
--- a/src/views/components/profile/profile-task.vue
+++ b/src/views/components/profile/profile-task.vue
@@ -125,10 +125,7 @@ limitations under the License. -->
   }
 </script>
 
-<style lang="scss">
-  .rk-profile-task {
-    margin: 20px;
-  }
+<style lang="scss" scoped>
   label {
     display: inline-block;
     margin: 10px 0;
diff --git a/src/views/components/topology/topo-aside.vue b/src/views/components/topology/topo-aside.vue
index 22f2964..83c883e 100644
--- a/src/views/components/topology/topo-aside.vue
+++ b/src/views/components/topology/topo-aside.vue
@@ -16,7 +16,11 @@ limitations under the License. -->
 <template>
   <aside class="link-topo-aside">
     <Radial v-if="radioStatus" :datas="{ nodes: stateTopo.nodes, calls: stateTopo.calls }" />
-    <svg class="link-topo-aside-btn icon cp lg" @click="showRadial()" :style="`position:absolute;left:580px;${radioStatus ? 'background-color: #357de9;' : ''}`">
+    <svg
+      class="link-topo-aside-btn icon cp lg"
+      @click="showRadial()"
+      :style="`position:absolute;left:580px;${radioStatus ? 'background-color: #357de9;' : ''}`"
+    >
       <use xlink:href="#issues" />
     </svg>
     <svg
diff --git a/src/views/components/topology/topo-endpoint-dependency.vue b/src/views/components/topology/topo-endpoint-dependency.vue
index 54839b1..97db853 100644
--- a/src/views/components/topology/topo-endpoint-dependency.vue
+++ b/src/views/components/topology/topo-endpoint-dependency.vue
@@ -100,7 +100,7 @@ limitations under the License. -->
     height: 100%;
     display: flex;
     flex-direction: column;
-    overflow: scroll;
+    overflow: auto;
     .endpoint-dependency-chart {
       height: 80%;
       min-height: 500px;
diff --git a/src/views/components/trace/trace-search.vue b/src/views/components/trace/trace-search.vue
index 526c1e9..c144176 100644
--- a/src/views/components/trace/trace-search.vue
+++ b/src/views/components/trace/trace-search.vue
@@ -110,7 +110,7 @@ limitations under the License. -->
   import TraceSelect from '../common/trace-select.vue';
   import { State as traceState } from '@/store/modules/trace/index';
   import { State as globalState } from '@/store/modules/global/index';
-  import dateFormatStep from '@/utils/dateFormatStep';
+  import dateFormatStep from '@/utils/dateFormat';
 
   @Component({ components: { TraceSelect } })
   export default class TraceSearch extends Vue {
diff --git a/src/views/containers/dashboard.vue b/src/views/containers/dashboard.vue
index 51f2866..ee65932 100644
--- a/src/views/containers/dashboard.vue
+++ b/src/views/containers/dashboard.vue
@@ -44,7 +44,7 @@ limitations under the License. -->
 </template>
 
 <script lang="ts">
-  import { Component, Vue, Watch } from 'vue-property-decorator';
+  import { Component, Vue } from 'vue-property-decorator';
   import { Action, Getter, State, Mutation } from 'vuex-class';
   import ToolBar from '@/views/components/dashboard/tool-bar/tool-bar.vue';
   import ToolGroup from '@/views/components/dashboard/tool-group.vue';
@@ -54,6 +54,7 @@ limitations under the License. -->
   import { State as globalState } from '@/store/modules/global';
   import { State as optionState } from '@/store/modules/global/selectors';
   import { State as dataState } from '@/store/modules/dashboard/dashboard-data';
+  import { PageTypes } from '@/constants/constant';
 
   interface ITemplate {
     name: string;
@@ -76,7 +77,7 @@ limitations under the License. -->
     @State('rocketData') private rocketComps!: dataState;
     @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
     @Action('GET_ALL_TEMPLATES') private GET_ALL_TEMPLATES: any;
-    // @Action('ADD_TEMPLATE') private ADD_TEMPLATE: any;
+    @Action('GET_EVENT') private GET_EVENT: any;
     @Getter('durationTime') private durationTime: any;
     @Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
     @Mutation('ADD_COMP') private ADD_COMP: any;
@@ -104,17 +105,10 @@ limitations under the License. -->
         duration: this.durationTime,
         keywordServiceName:
           this.rocketComps.tree[this.rocketComps.group] && this.rocketComps.tree[this.rocketComps.group].serviceGroup,
+        pageType: PageTypes.DASHBOARD,
       });
     }
     private beforeMount() {
-      // this.ADD_TEMPLATE({
-      //   name: 'Topology Instance',
-      //   type: 'TOPOLOGY_INSTANCE',
-      //   active: true,
-      //   configuration: JSON.stringify(TopologyInstanceTemp),
-      // }).then((data: any) => {
-      //   console.log(data);
-      // });
       this.GET_ALL_TEMPLATES().then((allTemplate: ITemplate[]) => {
         const dashboardTemplate = allTemplate.filter((item: ITemplate) => item.type === 'DASHBOARD');
         const templatesConfig = dashboardTemplate.map((item: ITemplate) => JSON.parse(item.configuration)).flat(1);
diff --git a/src/views/containers/topology/endpoint/index.vue b/src/views/containers/topology/endpoint/index.vue
index 10afc9f..4cc39bf 100644
--- a/src/views/containers/topology/endpoint/index.vue
+++ b/src/views/containers/topology/endpoint/index.vue
@@ -45,14 +45,24 @@ limitations under the License. -->
           </span>
         </div>
       </span>
-      <ToolBarSelect :selectable="false" :title="$t('currentService')" :current="current" icon="package" />
-      <ToolBarEndpointSelect
-        @onChoose="selectEndpoint"
-        :title="$t('currentEndpoint')"
-        :current="stateDashboardOption.currentEndpoint"
-        :data="stateDashboardOption.endpoints"
-        icon="code"
-      />
+      <div class="rk-dashboard-bar-tool flex-h">
+        <div class="flex-h">
+          <ToolBarSelect :selectable="false" :title="$t('currentService')" :current="current" icon="package" />
+          <ToolBarEndpointSelect
+            @onChoose="selectEndpoint"
+            :title="$t('currentEndpoint')"
+            :current="stateDashboardOption.currentEndpoint"
+            :data="stateDashboardOption.endpoints"
+            icon="code"
+          />
+        </div>
+        <DashboardEvent
+          :rocketComps="rocketComps"
+          :stateDashboard="stateDashboardOption"
+          :durationTime="durationTime"
+          :type="pageEventsType.TOPO_ENDPOINT_EVENTS"
+        />
+      </div>
     </div>
     <endpoints-survey :endpointComps="endpointComps" :updateObjects="updateObjects" />
   </div>
@@ -60,7 +70,7 @@ limitations under the License. -->
 
 <script lang="ts">
   import Vue from 'vue';
-  import { Component, Watch, Prop } from 'vue-property-decorator';
+  import { Component, Prop } from 'vue-property-decorator';
   import { Action, Getter, State, Mutation } from 'vuex-class';
   import EndpointsSurvey from './endpoints-survey.vue';
   import ToolBarSelect from '@/views/components/dashboard/tool-bar/tool-bar-select.vue';
@@ -68,36 +78,57 @@ limitations under the License. -->
   import { readFile } from '@/utils/readFile';
   import { saveFile } from '@/utils/saveFile';
   import { ObjectsType } from '../../../../constants/constant';
-
-  interface Endpoint {
-    label: string;
-    key: string;
-    name?: string;
-  }
+  import DashboardEvent from '@/views/components/dashboard/tool-bar/dashboard-events.vue';
+  import { State as optionState } from '@/store/modules/global/selectors';
+  import { State as rocketData } from '@/store/modules/dashboard/dashboard-data';
+  import { State as rocketbotGlobal } from '@/store/modules/global';
+  import { DurationTime, Option } from '@/types/global';
+  import { EntityType } from '@/views/components/dashboard/charts/constant';
+  import { PageEventsType } from '@/constants/constant';
 
   @Component({
     components: {
       EndpointsSurvey,
       ToolBarSelect,
       ToolBarEndpointSelect,
+      DashboardEvent,
     },
   })
   export default class WindowEndpoint extends Vue {
-    @State('rocketOption') private stateDashboardOption!: any;
-    @State('rocketData') private rocketComps!: any;
-    @State('rocketbot') private rocketGlobal: any;
-    @Getter('durationTime') private durationTime: any;
+    @Prop() private current!: { key: number | string; label: number | string };
+    @Prop() private endpointComps: any;
+    @Prop() private updateObjects!: string;
+    @State('rocketOption') private stateDashboardOption!: optionState;
+    @State('rocketData') private rocketComps!: rocketData;
+    @State('rocketbot') private rocketGlobal!: rocketbotGlobal;
+    @Getter('durationTime') private durationTime!: DurationTime;
     @Action('SELECT_ENDPOINT') private SELECT_ENDPOINT: any;
     @Mutation('SET_CURRENT_SERVICE') private SET_CURRENT_SERVICE: any;
     @Mutation('SET_EDIT') private SET_EDIT: any;
     @Action('GET_SERVICE_ENDPOINTS') private GET_SERVICE_ENDPOINTS: any;
     @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
-    @Prop() private current!: { key: number | string; label: number | string };
-    @Prop() private endpointComps: any;
-    @Prop() private updateObjects!: string;
+    @Action('GET_EVENT') private GET_EVENT: any;
+
+    private pageEventsType = PageEventsType;
 
-    private selectEndpoint(i: any) {
-      this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
+    private selectEndpoint(i: Option) {
+      if (!this.rocketComps.enableEvents) {
+        this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
+        return;
+      }
+      this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboardOption.currentService.label,
+            endpoint: i.label,
+          },
+        },
+        type: EntityType[2].key,
+      }).then(() => {
+        this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
+      });
     }
 
     private beforeMount() {
@@ -135,7 +166,11 @@ limitations under the License. -->
   }
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
+  .rk-dashboard-bar-tool {
+    width: calc(100% - 160px);
+    justify-content: space-between;
+  }
   .rk-dashboard-bar {
     flex-shrink: 0;
     color: #efefef;
diff --git a/src/views/containers/topology/instance/index.vue b/src/views/containers/topology/instance/index.vue
index f73ab20..b0fae5b 100644
--- a/src/views/containers/topology/instance/index.vue
+++ b/src/views/containers/topology/instance/index.vue
@@ -16,7 +16,7 @@ limitations under the License. -->
 <template>
   <div style="height: 100%">
     <div class="rk-dashboard-bar flex-h">
-      <span class="flex-h">
+      <div class="flex-h">
         <div class="rk-dashboard-bar-btn">
           <span v-tooltip:bottom="{ content: rocketGlobal.edit ? 'view' : 'edit' }">
             <svg
@@ -45,60 +45,91 @@ limitations under the License. -->
             </svg>
           </span>
         </div>
-      </span>
-      <ToolBarSelect :selectable="false" :title="$t('currentService')" :current="current" icon="package" />
-      <ToolBarSelect
-        @onChoose="selectInstance"
-        :title="$t('currentInstance')"
-        :current="stateDashboardOption.currentInstance"
-        :data="stateDashboardOption.instances"
-        icon="disk"
-      />
+      </div>
+      <div class="rk-dashboard-bar-tool flex-h">
+        <div class="flex-h">
+          <ToolBarSelect :selectable="false" :title="$t('currentService')" :current="current" icon="package" />
+          <ToolBarSelect
+            @onChoose="selectInstance"
+            :title="$t('currentInstance')"
+            :current="stateDashboardOption.currentInstance"
+            :data="stateDashboardOption.instances"
+            icon="disk"
+          />
+        </div>
+        <DashboardEvent
+          :rocketComps="rocketComps"
+          :stateDashboard="stateDashboardOption"
+          :durationTime="durationTime"
+          :type="pageEventsType.TOPO_INSTANCE_EVENTS"
+        />
+      </div>
     </div>
     <instances-survey :instanceComps="instanceComps" :updateObjects="updateObjects" />
   </div>
 </template>
 
 <script lang="ts">
+  import Vue from 'vue';
+  import { Component, Prop } from 'vue-property-decorator';
+  import { Action, Getter, State, Mutation } from 'vuex-class';
   import InstancesSurvey from './instances-survey.vue';
   import ToolBarSelect from '@/views/components/dashboard/tool-bar/tool-bar-select.vue';
   import ToolBarEndpointSelect from '@/views/components/dashboard/tool-bar/tool-bar-endpoint-select.vue';
-  import Vue from 'vue';
-  import { Component, PropSync, Watch, Prop } from 'vue-property-decorator';
-  import { Action, Getter, State, Mutation } from 'vuex-class';
   import { readFile } from '@/utils/readFile';
   import { saveFile } from '@/utils/saveFile';
-  import { ObjectsType } from '../../../../constants/constant';
-
-  interface Instance {
-    label: string;
-    key: string;
-    name?: string;
-  }
+  import { ObjectsType } from '@/constants/constant';
+  import { EntityType } from '@/views/components/dashboard/charts/constant';
+  import { DurationTime, Option } from '@/types/global';
+  import { State as optionState } from '@/store/modules/global/selectors';
+  import { State as rocketData } from '@/store/modules/dashboard/dashboard-data';
+  import { State as rocketbotGlobal } from '@/store/modules/global';
+  import DashboardEvent from '@/views/components/dashboard/tool-bar/dashboard-events.vue';
+  import { PageEventsType } from '@/constants/constant';
 
   @Component({
     components: {
       InstancesSurvey,
       ToolBarSelect,
       ToolBarEndpointSelect,
+      DashboardEvent,
     },
   })
   export default class WindowInstance extends Vue {
-    @State('rocketOption') private stateDashboardOption!: any;
-    @State('rocketData') private rocketComps!: any;
-    @State('rocketbot') private rocketGlobal: any;
-    @Getter('durationTime') private durationTime: any;
+    @Prop() private current!: { key: number | string; label: number | string };
+    @Prop() private instanceComps: any;
+    @Prop() private updateObjects!: string;
+    @State('rocketOption') private stateDashboardOption!: optionState;
+    @State('rocketData') private rocketComps!: rocketData;
+    @State('rocketbot') private rocketGlobal!: rocketbotGlobal;
+    @Getter('durationTime') private durationTime!: DurationTime;
     @Action('SELECT_INSTANCE') private SELECT_INSTANCE: any;
     @Action('GET_SERVICE_INSTANCES') private GET_SERVICE_INSTANCES: any;
     @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
+    @Action('GET_EVENT') private GET_EVENT: any;
     @Mutation('SET_EDIT') private SET_EDIT: any;
     @Mutation('SET_CURRENT_SERVICE') private SET_CURRENT_SERVICE: any;
-    @Prop() private current!: { key: number | string; label: number | string };
-    @Prop() private instanceComps: any;
-    @Prop() private updateObjects!: string;
 
-    private selectInstance(i: any) {
-      this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
+    private pageEventsType = PageEventsType;
+
+    private selectInstance(i: Option) {
+      if (!this.rocketComps.enableEvents) {
+        this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
+        return;
+      }
+      this.GET_EVENT({
+        condition: {
+          time: this.durationTime,
+          size: 20,
+          source: {
+            service: this.stateDashboardOption.currentService.label,
+            serviceInstance: i.label,
+          },
+        },
+        type: EntityType[3].key,
+      }).then(() => {
+        this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
+      });
     }
 
     private beforeMount() {
@@ -136,6 +167,15 @@ limitations under the License. -->
 </script>
 
 <style lang="scss" scoped>
+  .rk-dashboard-bar-tool {
+    width: calc(100% - 160px);
+    justify-content: space-between;
+  }
+  .rk-dashboard-bar {
+    flex-shrink: 0;
+    color: #efefef;
+    background-color: #333840;
+  }
   .rk-dashboard-bar-btn {
     padding: 0 5px;
     border-right: 2px solid #252a2f;
diff --git a/vue.config.js b/vue.config.js
index 7fa74f0..ee2b28d 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -21,7 +21,6 @@ module.exports = {
     proxy: {
       '/graphql': {
         target: `${process.env.SW_PROXY_TARGET || 'http://127.0.0.1:12800'}`,
-
         changeOrigin: true,
       },
     },