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;