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 2022/07/19 03:57:30 UTC

[skywalking-booster-ui] branch main updated: feat: Enhance associations for the Event widget (#120)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 42ead4a  feat: Enhance associations for the Event widget (#120)
42ead4a is described below

commit 42ead4a572c6499fa281b08bb7f38ab6dcb72e8f
Author: Fine0830 <fi...@outlook.com>
AuthorDate: Tue Jul 19 11:57:26 2022 +0800

    feat: Enhance associations for the Event widget (#120)
---
 package-lock.json                                  | 22 +-----
 package.json                                       |  3 +-
 src/components/Graph.vue                           | 64 ++++++++++++++++-
 src/hooks/useEcharts.ts                            |  2 -
 src/locales/lang/en.ts                             |  1 +
 src/locales/lang/es.ts                             |  1 +
 src/locales/lang/zh.ts                             |  1 +
 src/store/modules/event.ts                         |  3 +
 src/types/dashboard.d.ts                           | 11 ++-
 src/utils/echarts.ts                               | 11 +--
 src/views/dashboard/configuration/Event.vue        | 82 ++++++++++++++++++++++
 src/views/dashboard/configuration/index.ts         |  2 +
 .../configuration/widget/AssociateOptions.vue      | 35 +++------
 src/views/dashboard/controls/Event.vue             |  7 ++
 src/views/dashboard/graphs/Area.vue                |  8 ++-
 src/views/dashboard/graphs/Bar.vue                 | 52 ++------------
 src/views/dashboard/graphs/Line.vue                | 52 ++------------
 src/views/dashboard/related/event/Content.vue      | 48 +++++++++++--
 18 files changed, 247 insertions(+), 158 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 217941f..59d7203 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,8 +22,7 @@
         "vue-grid-layout": "^3.0.0-beta1",
         "vue-i18n": "^9.1.9",
         "vue-router": "^4.0.0-0",
-        "vue-types": "^4.1.1",
-        "vuex": "^4.0.0-0"
+        "vue-types": "^4.1.1"
       },
       "devDependencies": {
         "@types/d3": "^7.1.0",
@@ -27338,17 +27337,6 @@
         "vue": "^2.0.0 || ^3.0.0"
       }
     },
-    "node_modules/vuex": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
-      "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
-      "dependencies": {
-        "@vue/devtools-api": "^6.0.0-beta.11"
-      },
-      "peerDependencies": {
-        "vue": "^3.0.2"
-      }
-    },
     "node_modules/w3c-hr-time": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -50552,14 +50540,6 @@
         "is-plain-object": "5.0.0"
       }
     },
-    "vuex": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
-      "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
-      "requires": {
-        "@vue/devtools-api": "^6.0.0-beta.11"
-      }
-    },
     "w3c-hr-time": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
diff --git a/package.json b/package.json
index 040092a..2877874 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,7 @@
     "vue-grid-layout": "^3.0.0-beta1",
     "vue-i18n": "^9.1.9",
     "vue-router": "^4.0.0-0",
-    "vue-types": "^4.1.1",
-    "vuex": "^4.0.0-0"
+    "vue-types": "^4.1.1"
   },
   "devDependencies": {
     "@types/d3": "^7.1.0",
diff --git a/src/components/Graph.vue b/src/components/Graph.vue
index 85638ba..0222dca 100644
--- a/src/components/Graph.vue
+++ b/src/components/Graph.vue
@@ -46,8 +46,12 @@ const props = defineProps({
   },
   filters: {
     type: Object as PropType<{
-      value: number | string;
-      dataIndex: number;
+      duration: {
+        startTime: string;
+        endTime: string;
+      };
+      isRange: boolean;
+      dataIndex?: number;
       sourceId: string;
     }>,
   },
@@ -73,7 +77,7 @@ onMounted(async () => {
     });
     document.addEventListener(
       "click",
-      () => {
+      (event: Event) => {
         if (instance.isDisposed()) {
           return;
         }
@@ -84,6 +88,27 @@ onMounted(async () => {
           type: "updateAxisPointer",
           currTrigger: "leave",
         });
+        if (
+          ["vis-item-overflow", "vis-item-content"].includes(
+            (event.target as HTMLDivElement).className
+          )
+        ) {
+          return;
+        }
+        const series = (window as any).structuredClone(props.option.series);
+        for (const temp of series) {
+          if (temp.markArea) {
+            delete temp.markArea;
+          }
+        }
+        const options = {
+          ...props.option,
+          series,
+        };
+        if (JSON.stringify(options) === JSON.stringify(props.option)) {
+          return;
+        }
+        setOptions(options);
       },
       true
     );
@@ -110,6 +135,39 @@ watch(
       return;
     }
     if (props.filters) {
+      if (props.filters.isRange) {
+        const markArea = {
+          silent: true,
+          itemStyle: {
+            opacity: 0.3,
+          },
+          data: [
+            [
+              {
+                xAxis: props.filters.duration.startTime,
+              },
+              {
+                xAxis: props.filters.duration.endTime,
+              },
+            ],
+          ],
+        };
+        const series = (window as any).structuredClone(props.option.series);
+        for (const [key, temp] of series.entries()) {
+          if (key === 0) {
+            temp.markArea = markArea;
+          }
+        }
+        const options = {
+          ...props.option,
+          series,
+        };
+        if (JSON.stringify(options) === JSON.stringify(props.option)) {
+          return;
+        }
+        setOptions(options);
+        return;
+      }
       instance.dispatchAction({
         type: "showTip",
         dataIndex: props.filters.dataIndex,
diff --git a/src/hooks/useEcharts.ts b/src/hooks/useEcharts.ts
index 01f7d7e..2469865 100644
--- a/src/hooks/useEcharts.ts
+++ b/src/hooks/useEcharts.ts
@@ -18,7 +18,6 @@ import {
   BarSeriesOption,
   LineSeriesOption,
   HeatmapSeriesOption,
-  PieSeriesOption,
   SankeySeriesOption,
 } from "echarts/charts";
 import {
@@ -46,7 +45,6 @@ export type ECOption = echarts.ComposeOption<
   | DatasetComponentOption
   | LegendComponentOption
   | HeatmapSeriesOption
-  | PieSeriesOption
   | SankeySeriesOption
 >;
 
diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts
index f2a9037..3b4af09 100644
--- a/src/locales/lang/en.ts
+++ b/src/locales/lang/en.ts
@@ -147,6 +147,7 @@ const msg = {
   nameTip:
     "The name only supports Chinese and English, horizontal lines and underscores. The length of the name is limited to 300 characters",
   duplicateName: "Duplicate name",
+  enableAssociate: "Enable association",
   seconds: "Seconds",
   hourTip: "Select Hour",
   minuteTip: "Select Minute",
diff --git a/src/locales/lang/es.ts b/src/locales/lang/es.ts
index 2da5283..d65c1e4 100644
--- a/src/locales/lang/es.ts
+++ b/src/locales/lang/es.ts
@@ -147,6 +147,7 @@ const msg = {
   duplicateName: "Nombre duplicado",
   nameTip:
     "El nombre sólo admite chino e inglés, líneas horizontales y subrayado, y la longitud del nombre no excederá de 300 caracteres",
+  enableAssociate: "Activar asociación",
   seconds: "Segundos",
   hourTip: "Seleccione Hora",
   minuteTip: "Seleccione Minuto",
diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts
index 7a63626..6c05e54 100644
--- a/src/locales/lang/zh.ts
+++ b/src/locales/lang/zh.ts
@@ -142,6 +142,7 @@ const msg = {
   begin: "开始",
   associateOptions: "关联选项",
   widget: "部件",
+  enableAssociate: "启用关联",
   nameTip: "该名称仅支持中文和英文、横线和下划线, 并且限制长度为300个字符",
   duplicateName: "重复的名称",
   seconds: "秒",
diff --git a/src/store/modules/event.ts b/src/store/modules/event.ts
index fed1fa7..82c8a82 100644
--- a/src/store/modules/event.ts
+++ b/src/store/modules/event.ts
@@ -104,6 +104,9 @@ export const eventStore = defineStore({
               scope = "Endpoint";
             }
             item.scope = scope;
+            if (!item.endTime || item.endTime === item.startTime) {
+              item.endTime = Number(item.startTime) + 60000;
+            }
             return item;
           }
         );
diff --git a/src/types/dashboard.d.ts b/src/types/dashboard.d.ts
index 4bb9aa3..1f3ecac 100644
--- a/src/types/dashboard.d.ts
+++ b/src/types/dashboard.d.ts
@@ -38,7 +38,16 @@ export interface LayoutConfig {
   metricConfig?: MetricConfigOpt[];
   id?: string;
   associate?: { widgetId: string }[];
-  filters?: { dataIndex: number; sourceId: string };
+  eventAssociate?: boolean;
+  filters?: {
+    dataIndex: number;
+    sourceId: string;
+    isRange?: boolean;
+    duration?: {
+      startTime: string;
+      endTime: string;
+    };
+  };
 }
 
 export type MetricConfigOpt = {
diff --git a/src/utils/echarts.ts b/src/utils/echarts.ts
index 9103f91..db786a0 100644
--- a/src/utils/echarts.ts
+++ b/src/utils/echarts.ts
@@ -16,13 +16,7 @@
  */
 import * as echarts from "echarts/core";
 
-import {
-  BarChart,
-  LineChart,
-  PieChart,
-  HeatmapChart,
-  SankeyChart,
-} from "echarts/charts";
+import { BarChart, LineChart, HeatmapChart, SankeyChart } from "echarts/charts";
 
 import {
   TitleComponent,
@@ -32,6 +26,7 @@ import {
   DataZoomComponent,
   VisualMapComponent,
   TimelineComponent,
+  MarkAreaComponent,
 } from "echarts/components";
 
 import { SVGRenderer } from "echarts/renderers";
@@ -43,13 +38,13 @@ echarts.use([
   GridComponent,
   BarChart,
   LineChart,
-  PieChart,
   HeatmapChart,
   SankeyChart,
   SVGRenderer,
   DataZoomComponent,
   VisualMapComponent,
   TimelineComponent,
+  MarkAreaComponent,
 ]);
 
 export default echarts;
diff --git a/src/views/dashboard/configuration/Event.vue b/src/views/dashboard/configuration/Event.vue
new file mode 100644
index 0000000..a5d9b66
--- /dev/null
+++ b/src/views/dashboard/configuration/Event.vue
@@ -0,0 +1,82 @@
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. -->
+<template>
+  <div>
+    <span class="label">{{ t("enableAssociate") }}</span>
+    <el-switch
+      v-model="eventAssociate"
+      active-text="Yes"
+      inactive-text="No"
+      @change="updateConfig"
+    />
+  </div>
+  <div class="footer">
+    <el-button size="small" @click="cancelConfig">
+      {{ t("cancel") }}
+    </el-button>
+    <el-button size="small" type="primary" @click="applyConfig">
+      {{ t("apply") }}
+    </el-button>
+  </div>
+</template>
+<script lang="ts" setup>
+import { useI18n } from "vue-i18n";
+import { ref } from "vue";
+import { useDashboardStore } from "@/store/modules/dashboard";
+
+const { t } = useI18n();
+const dashboardStore = useDashboardStore();
+const originConfig = dashboardStore.selectedGrid;
+const eventAssociate = ref(dashboardStore.selectedGrid.eventAssociate || false);
+
+function updateConfig() {
+  dashboardStore.selectedGrid = {
+    ...dashboardStore.selectedGrid,
+    eventAssociate,
+  };
+  dashboardStore.selectWidget(dashboardStore.selectedGrid);
+}
+
+function applyConfig() {
+  dashboardStore.setConfigPanel(false);
+  dashboardStore.setConfigs(dashboardStore.selectedGrid);
+}
+
+function cancelConfig() {
+  dashboardStore.selectWidget(originConfig);
+  dashboardStore.setConfigPanel(false);
+}
+</script>
+<style lang="scss" scoped>
+.label {
+  font-size: 13px;
+  font-weight: 500;
+  display: block;
+  margin-bottom: 5px;
+}
+
+.item {
+  margin: 10px 0;
+}
+
+.footer {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  border-top: 1px solid #eee;
+  padding: 10px;
+  text-align: right;
+  width: 100%;
+  background-color: #fff;
+}
+</style>
diff --git a/src/views/dashboard/configuration/index.ts b/src/views/dashboard/configuration/index.ts
index a1359ee..6d39f19 100644
--- a/src/views/dashboard/configuration/index.ts
+++ b/src/views/dashboard/configuration/index.ts
@@ -18,9 +18,11 @@
 import Text from "./Text.vue";
 import Widget from "./Widget.vue";
 import Topology from "./Topology.vue";
+import Event from "./Event.vue";
 
 export default {
   Text,
   Widget,
   Topology,
+  Event,
 };
diff --git a/src/views/dashboard/configuration/widget/AssociateOptions.vue b/src/views/dashboard/configuration/widget/AssociateOptions.vue
index 0e7aab9..feae770 100644
--- a/src/views/dashboard/configuration/widget/AssociateOptions.vue
+++ b/src/views/dashboard/configuration/widget/AssociateOptions.vue
@@ -41,32 +41,16 @@ const widgetIds = ref<string[]>(
   associate.map((d: { widgetId: string }) => d.widgetId)
 );
 const widgets = computed(() => {
-  const isLinear = ["Bar", "Line", "Area"].includes(
-    dashboardStore.selectedGrid.graph && dashboardStore.selectedGrid.graph.type
-  );
-  // const isRank = ["TopList"].includes(
-  //   dashboardStore.selectedGrid.graph && dashboardStore.selectedGrid.graph.type
-  // );
-  const { widgets } = getDashboard(dashboardStore.currentDashboard);
-  const items = widgets.filter(
+  const all = getDashboard(dashboardStore.currentDashboard).widgets;
+  const items = all.filter(
     (d: { value: string; label: string } & LayoutConfig) => {
-      if (dashboardStore.selectedGrid.id !== d.id) {
-        if (
-          isLinear &&
-          d.type === "Widget" &&
-          d.widget &&
-          d.widget.name &&
-          d.id
-        ) {
-          d.value = d.id;
-          d.label = d.widget.name;
-          return d;
-        }
-        // if (isRank && d.type !== "Widget" && d.widget && d.id) {
-        //   d.value = d.id;
-        //   d.label = d.widget.name || d.id;
-        //   return d;
-        // }
+      const isLinear = ["Bar", "Line", "Area"].includes(
+        (d.graph && d.graph.type) || ""
+      );
+      if (isLinear && d.id && dashboardStore.selectedGrid.id !== d.id) {
+        d.value = d.id;
+        d.label = (d.widget && d.widget.name) || d.id;
+        return d;
       }
     }
   );
@@ -84,6 +68,7 @@ function updateWidgetConfig(options: Option[]) {
     associate: opt,
   };
   dashboardStore.selectWidget({ ...widget });
+
   // remove unuse association widget option
   for (const id of widgetIds.value) {
     if (!newVal.includes(id)) {
diff --git a/src/views/dashboard/controls/Event.vue b/src/views/dashboard/controls/Event.vue
index e510a13..c4cc16a 100644
--- a/src/views/dashboard/controls/Event.vue
+++ b/src/views/dashboard/controls/Event.vue
@@ -25,6 +25,9 @@ limitations under the License. -->
           <Icon iconName="ellipsis_v" size="middle" class="operation" />
         </span>
       </template>
+      <div class="tools" @click="editConfig">
+        <span>{{ t("edit") }}</span>
+      </div>
       <div class="tools" @click="removeWidget">
         <span>{{ t("delete") }}</span>
       </div>
@@ -58,6 +61,10 @@ const dashboardStore = useDashboardStore();
 function removeWidget() {
   dashboardStore.removeControls(props.data);
 }
+function editConfig() {
+  dashboardStore.setConfigPanel(true);
+  dashboardStore.selectWidget(props.data);
+}
 </script>
 <style lang="scss" scoped>
 .event-wrapper {
diff --git a/src/views/dashboard/graphs/Area.vue b/src/views/dashboard/graphs/Area.vue
index aaf5a5c..66c1fb4 100644
--- a/src/views/dashboard/graphs/Area.vue
+++ b/src/views/dashboard/graphs/Area.vue
@@ -38,9 +38,13 @@ defineProps({
     type: Object as PropType<
       AreaConfig & {
         filters: {
-          value: number | string;
-          dataIndex: number;
           sourceId: string;
+          duration: {
+            startTime: string;
+            endTime: string;
+          };
+          isRange: boolean;
+          dataIndex?: number;
         };
       } & { id: string }
     >,
diff --git a/src/views/dashboard/graphs/Bar.vue b/src/views/dashboard/graphs/Bar.vue
index 70e2def..e6d9da1 100644
--- a/src/views/dashboard/graphs/Bar.vue
+++ b/src/views/dashboard/graphs/Bar.vue
@@ -18,7 +18,6 @@ limitations under the License. -->
 <script lang="ts" setup>
 import { computed } from "vue";
 import type { PropType } from "vue";
-import { Event } from "@/types/events";
 import { BarConfig, EventParams } from "@/types/dashboard";
 
 /*global defineProps, defineEmits */
@@ -30,14 +29,17 @@ const props = defineProps({
   },
   intervalTime: { type: Array as PropType<string[]>, default: () => [] },
   theme: { type: String, default: "light" },
-  itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
   config: {
     type: Object as PropType<
       BarConfig & {
         filters: {
-          value: number | string;
-          dataIndex: number;
           sourceId: string;
+          duration: {
+            startTime: string;
+            endTime: string;
+          };
+          isRange: boolean;
+          dataIndex?: number;
         };
       } & { id: string }
     >,
@@ -50,30 +52,7 @@ function getOption() {
   const keys = Object.keys(props.data || {}).filter(
     (i: any) => Array.isArray(props.data[i]) && props.data[i].length
   );
-  const startP = keys.length > 1 ? 50 : 15;
-  const diff = 15;
-  const markAreas = (props.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) => {
+  const temp = keys.map((i: string) => {
     if (!props.intervalTime) {
       return;
     }
@@ -94,23 +73,6 @@ function getOption() {
       backgroundStyle: {
         color: "rgba(180, 180, 180, 0.1)",
       },
-      markArea:
-        index === 0
-          ? {
-              silent: false,
-              data: markAreas,
-              label: {
-                show: false,
-                width: 60,
-              },
-              emphasis: {
-                label: {
-                  position: "bottom",
-                  show: true,
-                },
-              },
-            }
-          : undefined,
     };
   });
   let color: string[] = [];
diff --git a/src/views/dashboard/graphs/Line.vue b/src/views/dashboard/graphs/Line.vue
index 3017903..286a633 100644
--- a/src/views/dashboard/graphs/Line.vue
+++ b/src/views/dashboard/graphs/Line.vue
@@ -18,7 +18,6 @@ limitations under the License. -->
 <script lang="ts" setup>
 import { computed } from "vue";
 import type { PropType } from "vue";
-import { Event } from "@/types/events";
 import { LineConfig, EventParams } from "@/types/dashboard";
 
 /*global defineProps, defineEmits */
@@ -30,14 +29,17 @@ const props = defineProps({
   },
   intervalTime: { type: Array as PropType<string[]>, default: () => [] },
   theme: { type: String, default: "light" },
-  itemEvents: { type: Array as PropType<Event[]>, default: () => [] },
   config: {
     type: Object as PropType<
       LineConfig & {
         filters: {
-          value: number | string;
-          dataIndex: number;
           sourceId: string;
+          duration: {
+            startTime: string;
+            endTime: string;
+          };
+          isRange: boolean;
+          dataIndex?: number;
         };
       } & { id: string }
     >,
@@ -58,30 +60,7 @@ function getOption() {
   const keys = Object.keys(props.data || {}).filter(
     (i: any) => Array.isArray(props.data[i]) && props.data[i].length
   );
-  const startP = keys.length > 1 ? 50 : 15;
-  const diff = 10;
-  const markAreas = (props.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 temp = keys.map((i: any) => {
     const serie: any = {
       data: props.data[i].map((item: any, itemIndex: number) => [
         props.intervalTime[itemIndex],
@@ -98,23 +77,6 @@ function getOption() {
         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 (props.config.type === "Area") {
       serie.areaStyle = {
diff --git a/src/views/dashboard/related/event/Content.vue b/src/views/dashboard/related/event/Content.vue
index d502375..4786e7b 100644
--- a/src/views/dashboard/related/event/Content.vue
+++ b/src/views/dashboard/related/event/Content.vue
@@ -19,15 +19,23 @@ limitations under the License. -->
 import { ref, watch, onMounted } from "vue";
 import dayjs from "dayjs";
 import { useThrottleFn } from "@vueuse/core";
+import { Event } from "@/types/events";
+import { LayoutConfig } from "@/types/dashboard";
 import { useEventStore } from "@/store/modules/event";
 import { DataSet, Timeline } from "vis-timeline/standalone";
 import "vis-timeline/styles/vis-timeline-graph2d.css";
+import { useDashboardStore } from "@/store/modules/dashboard";
+import getDashboard from "@/hooks/useDashboardsSession";
+import { dateFormatTime } from "@/utils/dateFormat";
+import { useAppStoreWithOut } from "@/store/modules/app";
 
 const eventStore = useEventStore();
 /*global Nullable */
 const timeline = ref<Nullable<HTMLDivElement>>(null);
 const visGraph = ref<Nullable<any>>(null);
 const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
+const dashboardStore = useDashboardStore();
+const appStore = useAppStoreWithOut();
 const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
   new Date(dayjs(date).format(pattern));
 const visDate = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
@@ -49,12 +57,12 @@ function visTimeline() {
     visGraph.value.destroy();
   }
   const h = timeline.value.getBoundingClientRect().height;
-  const events = eventStore.events.map((d, index) => {
+  const events = eventStore.events.map((d: Event, index: number) => {
     return {
       id: index + 1,
       content: d.name,
-      start: dateFormat(d.startTime),
-      end: dateFormat(d.endTime),
+      start: dateFormat(Number(d.startTime)),
+      end: dateFormat(Number(d.endTime)),
       data: d,
       className: d.type,
     };
@@ -68,7 +76,7 @@ function visTimeline() {
     autoResize: false,
     tooltip: {
       overflowMethod: "cap",
-      template(item) {
+      template(item: Event | any) {
         const data = item.data || {};
         let tmp = `<div>ID: ${data.uuid || ""}</div>
         <div>Name: ${data.name || ""}</div>
@@ -88,6 +96,38 @@ function visTimeline() {
     },
   };
   visGraph.value = new Timeline(timeline.value, items, options);
+  visGraph.value.on("select", (properties: { items: number[] }) => {
+    if (!dashboardStore.selectedGrid.eventAssociate) {
+      return;
+    }
+    const all = getDashboard(dashboardStore.currentDashboard).widgets;
+    const widgets = all.filter(
+      (d: { value: string; label: string } & LayoutConfig) => {
+        const isLinear = ["Bar", "Line", "Area"].includes(
+          (d.graph && d.graph.type) || ""
+        );
+        if (isLinear) {
+          return d;
+        }
+      }
+    );
+    const index = properties.items[0];
+    const i = events[index - 1 || 0];
+
+    for (const widget of widgets) {
+      const startTime = dateFormatTime(i.start, appStore.duration.step);
+      const endTime = dateFormatTime(i.end, appStore.duration.step);
+      widget.filters = {
+        sourceId: dashboardStore.selectedGrid.id || "",
+        isRange: true,
+        duration: {
+          startTime,
+          endTime,
+        },
+      };
+      dashboardStore.setWidget(widget);
+    }
+  });
 }
 function resize() {
   const observer = new ResizeObserver((entries) => {