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 4bf57ec7 feat: support collapsing and expanding for the event widget 
(#503)
4bf57ec7 is described below

commit 4bf57ec7c5f7c5e1db69adfb26f2bdfef1e9ac5b
Author: Fine0830 <[email protected]>
AuthorDate: Fri Oct 10 15:35:48 2025 +0800

    feat: support collapsing and expanding for the event widget (#503)
---
 src/locales/lang/en.ts                        |   1 +
 src/locales/lang/es.ts                        |   1 +
 src/locales/lang/zh.ts                        |   1 +
 src/store/modules/dashboard.ts                |  16 ++--
 src/store/modules/event.ts                    |   8 +-
 src/types/dashboard.ts                        |   1 +
 src/views/dashboard/configuration/Event.vue   |  15 +++-
 src/views/dashboard/controls/Event.vue        | 105 +++++++++++++++++++++-----
 src/views/dashboard/controls/Tab.vue          |   2 +
 src/views/dashboard/controls/Widget.vue       |   4 +-
 src/views/dashboard/panel/Layout.vue          |   2 +
 src/views/dashboard/related/event/Content.vue |   1 +
 src/views/dashboard/related/event/Header.vue  |  16 ++--
 13 files changed, 133 insertions(+), 40 deletions(-)

diff --git a/src/locales/lang/en.ts b/src/locales/lang/en.ts
index 792a5ba3..eba15632 100644
--- a/src/locales/lang/en.ts
+++ b/src/locales/lang/en.ts
@@ -413,5 +413,6 @@ const msg = {
   spanName: "Span name",
   parentId: "Parent ID",
   shareTrace: "Share This Trace",
+  eventDefaultCollapse: "Default Collapse",
 };
 export default msg;
diff --git a/src/locales/lang/es.ts b/src/locales/lang/es.ts
index 156bdd43..de5505eb 100644
--- a/src/locales/lang/es.ts
+++ b/src/locales/lang/es.ts
@@ -413,5 +413,6 @@ const msg = {
   spanName: "Nombre de Lapso",
   parentId: "ID Padre",
   shareTrace: "Compartir Traza",
+  eventDefaultCollapse: "Default Collapse",
 };
 export default msg;
diff --git a/src/locales/lang/zh.ts b/src/locales/lang/zh.ts
index c68a406d..22ffccd4 100644
--- a/src/locales/lang/zh.ts
+++ b/src/locales/lang/zh.ts
@@ -411,5 +411,6 @@ const msg = {
   spanName: "跨度名称",
   parentId: "父ID",
   shareTrace: "分享Trace",
+  eventDefaultCollapse: "默认折叠",
 };
 export default msg;
diff --git a/src/store/modules/dashboard.ts b/src/store/modules/dashboard.ts
index 76ae8009..d2944104 100644
--- a/src/store/modules/dashboard.ts
+++ b/src/store/modules/dashboard.ts
@@ -253,9 +253,13 @@ export const dashboardStore = defineStore({
     setTopology(show: boolean) {
       this.showTopology = show;
     },
-    setConfigs(param: LayoutConfig) {
-      const actived = this.activedGridItem.split("-");
+    setLayouts(param: LayoutConfig[]) {
+      this.layout = param;
+    },
+    setConfigs(param: LayoutConfig, gridIndex?: string) {
+      const actived = gridIndex || this.activedGridItem.split("-");
       const index = this.layout.findIndex((d: LayoutConfig) => actived[0] === 
d.i);
+
       if (actived.length === 3) {
         const tabIndex = Number(actived[1]);
         const itemIndex = (this.layout[index].children || 
[])[tabIndex].children.findIndex(
@@ -270,11 +274,13 @@ export const dashboardStore = defineStore({
         this.setCurrentTabItems((this.layout[index].children || 
[])[tabIndex].children);
         return;
       }
-      this.layout[index] = {
-        ...this.layout[index],
+      const layout = JSON.parse(JSON.stringify(this.layout));
+      layout[index] = {
+        ...layout[index],
         ...param,
       };
-      this.selectedGrid = this.layout[index];
+      this.setLayouts(layout);
+      this.selectedGrid = layout[index];
     },
     setWidget(param: LayoutConfig) {
       for (let i = 0; i < this.layout.length; i++) {
diff --git a/src/store/modules/event.ts b/src/store/modules/event.ts
index b2a59c6c..9ced69ac 100644
--- a/src/store/modules/event.ts
+++ b/src/store/modules/event.ts
@@ -36,8 +36,8 @@ export const eventStore = defineStore({
   state: (): eventState => ({
     loading: false,
     events: [],
-    instances: [{ value: "", label: "All" }],
-    endpoints: [{ value: "", label: "All" }],
+    instances: [{ value: "0", label: "All" }],
+    endpoints: [{ value: "0", label: "All" }],
     condition: null,
   }),
   actions: {
@@ -58,7 +58,7 @@ export const eventStore = defineStore({
       if (response.errors) {
         return response;
       }
-      this.instances = [{ value: "", label: "All" }, ...response.data.pods];
+      this.instances = [{ value: "0", label: "All" }, ...response.data.pods];
       return response;
     },
     async getEndpoints(keyword?: string) {
@@ -75,7 +75,7 @@ export const eventStore = defineStore({
       if (response.errors) {
         return response;
       }
-      this.endpoints = [{ value: "", label: "All" }, ...response.data.pods];
+      this.endpoints = [{ value: "0", label: "All" }, ...response.data.pods];
       return response;
     },
     async getEvents() {
diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts
index 0c966c76..ad8e68e0 100644
--- a/src/types/dashboard.ts
+++ b/src/types/dashboard.ts
@@ -48,6 +48,7 @@ export interface LayoutConfig {
   id?: string;
   associate?: { widgetId: string }[];
   eventAssociate?: boolean;
+  eventDefaultCollapse?: boolean;
   filters?: Filters;
   relatedTrace?: RelatedTrace;
   subExpressions?: string[];
diff --git a/src/views/dashboard/configuration/Event.vue 
b/src/views/dashboard/configuration/Event.vue
index a4489476..d521f0bb 100644
--- a/src/views/dashboard/configuration/Event.vue
+++ b/src/views/dashboard/configuration/Event.vue
@@ -15,6 +15,10 @@ limitations under the License. -->
     <div class="config-label flex-h mr-20">{{ t("enableAssociate") }}</div>
     <el-switch v-model="eventAssociate" active-text="Yes" inactive-text="No" 
@change="updateConfig" />
   </div>
+  <div class="config-item flex-h">
+    <div class="config-label flex-h mr-20">{{ t("eventDefaultCollapse") 
}}</div>
+    <el-switch v-model="eventDefaultCollapse" active-text="Yes" 
inactive-text="No" @change="updateConfig" />
+  </div>
   <ConfigurationFooter />
 </template>
 <script lang="ts" setup>
@@ -28,10 +32,19 @@ limitations under the License. -->
   const { t } = useI18n();
   const dashboardStore = useDashboardStore();
   const eventAssociate = ref(dashboardStore.selectedGrid?.eventAssociate || 
false);
+  const eventDefaultCollapse = ref(
+    dashboardStore.selectedGrid?.eventDefaultCollapse === undefined
+      ? true
+      : dashboardStore.selectedGrid?.eventDefaultCollapse,
+  );
 
   function updateConfig() {
     const { selectedGrid } = dashboardStore;
 
-    dashboardStore.selectWidget({ ...selectedGrid, eventAssociate: 
eventAssociate.value } as LayoutConfig);
+    dashboardStore.selectWidget({
+      ...selectedGrid,
+      eventAssociate: eventAssociate.value,
+      eventDefaultCollapse: eventDefaultCollapse.value,
+    } as LayoutConfig);
   }
 </script>
diff --git a/src/views/dashboard/controls/Event.vue 
b/src/views/dashboard/controls/Event.vue
index 618062dd..8a1dda3d 100644
--- a/src/views/dashboard/controls/Event.vue
+++ b/src/views/dashboard/controls/Event.vue
@@ -14,28 +14,37 @@ See the License for the specific language governing 
permissions and
 limitations under the License. -->
 <template>
   <div class="event-wrapper flex-v">
-    <el-popover placement="bottom" trigger="click" :width="100" 
v-if="dashboardStore.editMode">
-      <template #reference>
-        <span class="delete cp">
-          <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>
-    </el-popover>
-    <div class="header">
-      <Header :needQuery="needQuery" />
-    </div>
-    <div class="event">
-      <Content :data="data" />
+    <div class="operations">
+      <span class="cp" @click="handleCollapse">
+        <Icon iconName="sort" size="middle" />
+      </span>
+      <el-popover placement="bottom" trigger="click" :width="100" 
v-if="dashboardStore.editMode">
+        <template #reference>
+          <span class="cp">
+            <Icon iconName="ellipsis_v" size="middle" />
+          </span>
+        </template>
+        <div class="tools" @click="editConfig">
+          <span>{{ t("edit") }}</span>
+        </div>
+        <div class="tools" @click="removeWidget">
+          <span>{{ t("delete") }}</span>
+        </div>
+      </el-popover>
     </div>
+    <div class="event-inspector" v-if="collapsedState"> Event Timeline 
Inspector </div>
+    <Transition name="collapse" v-else>
+      <div class="timeline">
+        <Header :needQuery="needQuery" />
+        <div class="event mt-10">
+          <Content :data="data" />
+        </div>
+      </div>
+    </Transition>
   </div>
 </template>
 <script lang="ts" setup>
+  import { ref, watch, onMounted, nextTick } from "vue";
   import type { PropType } from "vue";
   import { useI18n } from "vue-i18n";
   import { useDashboardStore } from "@/store/modules/dashboard";
@@ -54,6 +63,17 @@ limitations under the License. -->
   });
   const { t } = useI18n();
   const dashboardStore = useDashboardStore();
+  const collapsedState = ref(true);
+  const originalState = ref({ h: props.data.h });
+
+  onMounted(() => {
+    collapsedState.value = props.data.eventDefaultCollapse === undefined ? 
true : props.data.eventDefaultCollapse;
+    if (collapsedState.value) {
+      dashboardStore.setConfigs({ ...props.data, ...{ h: 3 } }, props.data.i);
+    } else {
+      dashboardStore.setConfigs({ ...props.data, ...originalState.value }, 
props.data.i);
+    }
+  });
 
   function removeWidget() {
     dashboardStore.removeControls(props.data);
@@ -62,6 +82,24 @@ limitations under the License. -->
     dashboardStore.setConfigPanel(true);
     dashboardStore.selectWidget(props.data);
   }
+  function handleCollapse() {
+    dashboardStore.activeGridItem(props.data.i);
+    collapsedState.value = !collapsedState.value;
+    if (collapsedState.value) {
+      dashboardStore.setConfigs({ ...props.data, ...{ h: 3 } });
+    } else {
+      dashboardStore.setConfigs({ ...props.data, ...originalState.value });
+    }
+  }
+
+  watch(
+    () => props.data.h,
+    (newHeight) => {
+      if (!collapsedState.value) {
+        originalState.value = { h: newHeight };
+      }
+    },
+  );
 </script>
 <style lang="scss" scoped>
   .event-wrapper {
@@ -72,14 +110,14 @@ limitations under the License. -->
     overflow: auto;
   }
 
-  .delete {
+  .operations {
     position: absolute;
     top: 5px;
     right: 3px;
     z-index: 9999;
   }
 
-  .header {
+  .timeline {
     padding: 10px;
     font-size: $font-size-smaller;
     border-bottom: 1px solid $border-color;
@@ -103,4 +141,31 @@ limitations under the License. -->
     width: 100%;
     height: calc(100% - 80px);
   }
+
+  .collapse-enter-active,
+  .collapse-leave-active {
+    transition: all 0.8s ease;
+    overflow: hidden;
+  }
+
+  .collapse-enter-from,
+  .collapse-leave-to {
+    opacity: 0;
+    max-height: 0;
+    transform: translateY(-10px);
+  }
+
+  .collapse-enter-to,
+  .collapse-leave-from {
+    opacity: 1;
+    max-height: 1000px;
+    transform: translateY(0);
+  }
+
+  .event-inspector {
+    text-align: center;
+    line-height: 45px;
+    font-size: $font-size-normal;
+    font-weight: bold;
+  }
 </style>
diff --git a/src/views/dashboard/controls/Tab.vue 
b/src/views/dashboard/controls/Tab.vue
index 280a5524..7c1b7565 100644
--- a/src/views/dashboard/controls/Tab.vue
+++ b/src/views/dashboard/controls/Tab.vue
@@ -95,6 +95,8 @@ limitations under the License. -->
       :is-draggable="dashboardStore.editMode"
       :is-resizable="dashboardStore.editMode"
       @layout-updated="layoutUpdatedEvent"
+      :vertical-compact="true"
+      :auto-size="true"
     >
       <grid-item
         v-for="item in dashboardStore.currentTabItems"
diff --git a/src/views/dashboard/controls/Widget.vue 
b/src/views/dashboard/controls/Widget.vue
index 1ce1ec50..b8beb029 100644
--- a/src/views/dashboard/controls/Widget.vue
+++ b/src/views/dashboard/controls/Widget.vue
@@ -122,10 +122,10 @@ limitations under the License. -->
         };
         const metrics: { [key: string]: { source: { [key: string]: unknown }; 
typesOfMQE: string[] } } =
           (await useDashboardQueryProcessor([config])) || {};
-        const params = metrics[data.value.i];
+        const params = metrics[data.value.i] || {};
         loading.value = false;
         state.source = params.source || {};
-        typesOfMQE.value = params.typesOfMQE;
+        typesOfMQE.value = params.typesOfMQE || [];
       }
 
       function removeWidget() {
diff --git a/src/views/dashboard/panel/Layout.vue 
b/src/views/dashboard/panel/Layout.vue
index 883c763b..1ca3f954 100644
--- a/src/views/dashboard/panel/Layout.vue
+++ b/src/views/dashboard/panel/Layout.vue
@@ -23,6 +23,8 @@ limitations under the License. -->
     v-loading.fullscreen.lock="loading"
     element-loading-text="Loading..."
     element-loading-background="rgba(122, 122, 122, 0.8)"
+    :vertical-compact="true"
+    :auto-size="true"
   >
     <grid-item
       v-for="item in dashboardStore.layout"
diff --git a/src/views/dashboard/related/event/Content.vue 
b/src/views/dashboard/related/event/Content.vue
index 03d1645e..d96796f8 100644
--- a/src/views/dashboard/related/event/Content.vue
+++ b/src/views/dashboard/related/event/Content.vue
@@ -51,6 +51,7 @@ limitations under the License. -->
       width: 0,
       height: 0,
     };
+    visTimeline();
     useThrottleFn(resize, 500)();
   });
 
diff --git a/src/views/dashboard/related/event/Header.vue 
b/src/views/dashboard/related/event/Header.vue
index 8af195bf..0b0ef9ce 100644
--- a/src/views/dashboard/related/event/Header.vue
+++ b/src/views/dashboard/related/event/Header.vue
@@ -86,8 +86,8 @@ limitations under the License. -->
   const pageSize = 20;
   const pageNum = ref<number>(1);
   const state = reactive<any>({
-    instance: { value: "", label: "All", id: "" },
-    endpoint: { value: "", label: "All", id: "" },
+    instance: { value: "0", label: "All" },
+    endpoint: { value: "0", label: "All" },
     eventType: { value: "", label: "All" },
   });
   const total = computed(() =>
@@ -99,8 +99,8 @@ limitations under the License. -->
   async function init() {
     fetchSelectors();
     await queryEvents();
-    state.instance = { value: "", label: "All" };
-    state.endpoint = { value: "", label: "All" };
+    state.instance = { value: "0", label: "All" };
+    state.endpoint = { value: "0", label: "All" };
   }
 
   function fetchSelectors() {
@@ -138,13 +138,13 @@ limitations under the License. -->
     state.instance = eventStore.instances[0];
   }
   async function queryEvents() {
-    let endpoint = state.endpoint.value,
-      instance = state.instance.value;
+    let endpoint = state.endpoint.value === "0" ? undefined : 
state.endpoint.value,
+      instance = state.instance.value === "0" ? undefined : 
state.instance.value;
     if (dashboardStore.entity === EntityType[2].value) {
-      endpoint = selectorStore.currentPod && selectorStore.currentPod.id;
+      endpoint = selectorStore.currentPod?.id;
     }
     if (dashboardStore.entity === EntityType[3].value) {
-      instance = selectorStore.currentPod && selectorStore.currentPod.id;
+      instance = selectorStore.currentPod?.id;
     }
     if (!selectorStore.currentService) {
       return;

Reply via email to