mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-10-28 06:37:37 +00:00
feat: add the queryOrder to trace filters (#508)
This commit is contained in:
@@ -16,7 +16,15 @@ limitations under the License. -->
|
|||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<Filter :needQuery="needQuery" :data="data" @get="getService" @search="popSegmentList" />
|
<Filter :needQuery="needQuery" :data="data" @get="getService" @search="popSegmentList" />
|
||||||
<div class="filter-row flex-h mt-10" v-if="traceStore.hasQueryTracesV2Support">
|
<div class="filter-row flex-h mt-10" v-if="traceStore.hasQueryTracesV2Support">
|
||||||
<div class="grey mr-10 label">{{ t("limit") }}</div>
|
<div class="grey mr-10 label">{{ t("setOrder") }}</div>
|
||||||
|
<Selector
|
||||||
|
v-model="queryOrder"
|
||||||
|
:options="QueryOrders"
|
||||||
|
@change="changeQueryOrder"
|
||||||
|
size="small"
|
||||||
|
style="width: 120px"
|
||||||
|
/>
|
||||||
|
<div class="grey mr-10 label ml-20">{{ t("limit") }}</div>
|
||||||
<el-input-number size="small" v-model="limit" :min="10" @change="changeLimit" />
|
<el-input-number size="small" v-model="limit" :min="10" @change="changeLimit" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,6 +56,7 @@ limitations under the License. -->
|
|||||||
import type { LayoutConfig } from "@/types/dashboard";
|
import type { LayoutConfig } from "@/types/dashboard";
|
||||||
import { mutationObserver } from "@/utils/mutation";
|
import { mutationObserver } from "@/utils/mutation";
|
||||||
import TraceQuery from "./components/TraceQuery/Index.vue";
|
import TraceQuery from "./components/TraceQuery/Index.vue";
|
||||||
|
import { QueryOrders } from "@/views/dashboard/data";
|
||||||
/*global defineProps */
|
/*global defineProps */
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
@@ -64,6 +73,7 @@ limitations under the License. -->
|
|||||||
const currentWidth = ref<number>(280);
|
const currentWidth = ref<number>(280);
|
||||||
const needQuery = ref<boolean>(true);
|
const needQuery = ref<boolean>(true);
|
||||||
const isDrag = ref<boolean>(false);
|
const isDrag = ref<boolean>(false);
|
||||||
|
const queryOrder = ref<string>(QueryOrders[0].value);
|
||||||
const limit = ref(PageSize);
|
const limit = ref(PageSize);
|
||||||
const defaultWidth = 280;
|
const defaultWidth = 280;
|
||||||
const minArrowLeftWidth = 120;
|
const minArrowLeftWidth = 120;
|
||||||
@@ -77,7 +87,11 @@ limitations under the License. -->
|
|||||||
paging: { pageNum: 1, pageSize: val },
|
paging: { pageNum: 1, pageSize: val },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function changeQueryOrder() {
|
||||||
|
traceStore.setTraceCondition({
|
||||||
|
queryOrder: queryOrder.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
// When click the arrow, the width of the segment list is determined by the direction it points to.
|
// When click the arrow, the width of the segment list is determined by the direction it points to.
|
||||||
function triggerArrow() {
|
function triggerArrow() {
|
||||||
currentWidth.value = isLeft.value ? 0 : defaultWidth;
|
currentWidth.value = isLeft.value ? 0 : defaultWidth;
|
||||||
|
|||||||
@@ -16,14 +16,7 @@ limitations under the License. -->
|
|||||||
<div class="flex-h result-header">
|
<div class="flex-h result-header">
|
||||||
<div style="align-items: center"> {{ filteredTraces.length }} of {{ totalTraces }} Results </div>
|
<div style="align-items: center"> {{ filteredTraces.length }} of {{ totalTraces }} Results </div>
|
||||||
<div class="flex-h" style="align-items: center">
|
<div class="flex-h" style="align-items: center">
|
||||||
<el-switch
|
<el-switch v-model="expandAll" size="large" active-text="Expand All" inactive-text="Collapse All" class="mr-20" />
|
||||||
v-model="expandAll"
|
|
||||||
size="large"
|
|
||||||
active-text="Expand All"
|
|
||||||
inactive-text="Collapse All"
|
|
||||||
class="mr-20"
|
|
||||||
@change="toggleAllExpansion"
|
|
||||||
/>
|
|
||||||
<Selector
|
<Selector
|
||||||
placeholder="Service filters"
|
placeholder="Service filters"
|
||||||
@change="changeServiceFilters"
|
@change="changeServiceFilters"
|
||||||
@@ -43,6 +36,7 @@ limitations under the License. -->
|
|||||||
:default-sort="{ prop: 'duration', order: 'descending' }"
|
:default-sort="{ prop: 'duration', order: 'descending' }"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-loading="traceStore.loading"
|
v-loading="traceStore.loading"
|
||||||
|
@sort-change="handleSortChange"
|
||||||
>
|
>
|
||||||
<el-table-column type="expand">
|
<el-table-column type="expand">
|
||||||
<template #default="props">
|
<template #default="props">
|
||||||
@@ -61,7 +55,7 @@ limitations under the License. -->
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="Root" prop="label" />
|
<el-table-column label="Root" prop="label" />
|
||||||
<el-table-column label="Start Time" prop="timestamp" width="220">
|
<el-table-column label="Start Time" prop="start" width="220" sortable>
|
||||||
<template #default="props">
|
<template #default="props">
|
||||||
{{ dateFormat(props.row.start) }}
|
{{ dateFormat(props.row.start) }}
|
||||||
</template>
|
</template>
|
||||||
@@ -137,6 +131,9 @@ limitations under the License. -->
|
|||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const loadMoreThreshold = 100; // pixels from bottom to trigger load
|
const loadMoreThreshold = 100; // pixels from bottom to trigger load
|
||||||
|
|
||||||
|
// Sort state for infinite scroll
|
||||||
|
const currentSort = ref<{ prop: string; order: string } | null>({ prop: "duration", order: "descending" });
|
||||||
|
|
||||||
// Calculate max duration for progress bar scaling
|
// Calculate max duration for progress bar scaling
|
||||||
const maxDuration = computed(() => {
|
const maxDuration = computed(() => {
|
||||||
if (!traceStore.traceList.length) return 1;
|
if (!traceStore.traceList.length) return 1;
|
||||||
@@ -161,7 +158,26 @@ limitations under the License. -->
|
|||||||
});
|
});
|
||||||
|
|
||||||
const filteredTraces = computed<Trace[]>(() => {
|
const filteredTraces = computed<Trace[]>(() => {
|
||||||
return allFilteredTraces.value.slice(0, loadedItemsCount.value);
|
let sortedTraces = [...allFilteredTraces.value];
|
||||||
|
|
||||||
|
// Apply sorting if there's a current sort
|
||||||
|
if (currentSort.value) {
|
||||||
|
sortedTraces.sort((a, b) => {
|
||||||
|
const { prop, order } = currentSort.value!;
|
||||||
|
|
||||||
|
if (prop === "start") {
|
||||||
|
const result = sortByStartTime(a, b);
|
||||||
|
return order === "ascending" ? result : -result;
|
||||||
|
} else if (prop === "duration") {
|
||||||
|
const result = a.duration - b.duration;
|
||||||
|
return order === "ascending" ? result : -result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortedTraces.slice(0, loadedItemsCount.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalTraces = computed<number>(() => allFilteredTraces.value.length);
|
const totalTraces = computed<number>(() => allFilteredTraces.value.length);
|
||||||
@@ -208,6 +224,47 @@ limitations under the License. -->
|
|||||||
return Math.round((duration / maxDuration.value) * 100);
|
return Math.round((duration / maxDuration.value) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sortByStartTime(a: Trace, b: Trace): number {
|
||||||
|
// Convert start time strings to Date objects for comparison
|
||||||
|
const dateA = new Date(a.start);
|
||||||
|
const dateB = new Date(b.start);
|
||||||
|
|
||||||
|
// Handle invalid dates
|
||||||
|
if (isNaN(dateA.getTime()) && isNaN(dateB.getTime())) return 0;
|
||||||
|
if (isNaN(dateA.getTime())) return 1;
|
||||||
|
if (isNaN(dateB.getTime())) return -1;
|
||||||
|
|
||||||
|
return dateA.getTime() - dateB.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyExpandState() {
|
||||||
|
nextTick(() => {
|
||||||
|
if (tableRef.value) {
|
||||||
|
const visibleItems = filteredTraces.value;
|
||||||
|
for (const row of visibleItems) {
|
||||||
|
tableRef.value.toggleRowExpansion(row, expandAll.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSortChange(sortInfo: { prop: string; order: string | null }) {
|
||||||
|
if (sortInfo.order) {
|
||||||
|
currentSort.value = {
|
||||||
|
prop: sortInfo.prop,
|
||||||
|
order: sortInfo.order,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
currentSort.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset loaded items count when sort changes
|
||||||
|
loadedItemsCount.value = PageSize;
|
||||||
|
|
||||||
|
// Preserve expand state when sorting changes
|
||||||
|
applyExpandState();
|
||||||
|
}
|
||||||
|
|
||||||
function toggleServiceTags(serviceName: string, row: Trace) {
|
function toggleServiceTags(serviceName: string, row: Trace) {
|
||||||
selectedServiceNames.value = selectedServiceNames.value.includes(serviceName)
|
selectedServiceNames.value = selectedServiceNames.value.includes(serviceName)
|
||||||
? selectedServiceNames.value.filter((name) => name !== serviceName)
|
? selectedServiceNames.value.filter((name) => name !== serviceName)
|
||||||
@@ -224,7 +281,6 @@ limitations under the License. -->
|
|||||||
function changeServiceFilters(selected: Option[]) {
|
function changeServiceFilters(selected: Option[]) {
|
||||||
selectedServiceNames.value = selected.map((o) => String(o.value));
|
selectedServiceNames.value = selected.map((o) => String(o.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infinite scroll handlers
|
// Infinite scroll handlers
|
||||||
function loadMoreItems() {
|
function loadMoreItems() {
|
||||||
if (loading.value || !hasMoreItems.value) return;
|
if (loading.value || !hasMoreItems.value) return;
|
||||||
@@ -235,17 +291,7 @@ limitations under the License. -->
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const nextBatch = Math.min(PageSize, allFilteredTraces.value.length - loadedItemsCount.value);
|
const nextBatch = Math.min(PageSize, allFilteredTraces.value.length - loadedItemsCount.value);
|
||||||
loadedItemsCount.value += nextBatch;
|
loadedItemsCount.value += nextBatch;
|
||||||
|
applyExpandState();
|
||||||
// Apply current expand state to all currently visible items
|
|
||||||
nextTick(() => {
|
|
||||||
if (tableRef.value) {
|
|
||||||
const allVisibleItems = filteredTraces.value;
|
|
||||||
for (const row of allVisibleItems) {
|
|
||||||
tableRef.value.toggleRowExpansion(row, expandAll.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
@@ -265,25 +311,13 @@ limitations under the License. -->
|
|||||||
loadedItemsCount.value = PageSize;
|
loadedItemsCount.value = PageSize;
|
||||||
// Apply expand state to newly visible items after filter change
|
// Apply expand state to newly visible items after filter change
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (tableRef.value) {
|
applyExpandState();
|
||||||
const visibleItems = filteredTraces.value;
|
|
||||||
for (const row of visibleItems) {
|
|
||||||
tableRef.value.toggleRowExpansion(row, expandAll.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch for expandAll state changes to apply to all visible items
|
// Watch for expandAll state changes to apply to all visible items
|
||||||
watch(expandAll, () => {
|
watch(expandAll, () => {
|
||||||
nextTick(() => {
|
applyExpandState();
|
||||||
if (tableRef.value) {
|
|
||||||
const visibleItems = filteredTraces.value;
|
|
||||||
for (const row of visibleItems) {
|
|
||||||
tableRef.value.toggleRowExpansion(row, expandAll.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watch for trace list changes (when new query is executed) to reapply expand state
|
// Watch for trace list changes (when new query is executed) to reapply expand state
|
||||||
@@ -294,14 +328,7 @@ limitations under the License. -->
|
|||||||
loadedItemsCount.value = PageSize;
|
loadedItemsCount.value = PageSize;
|
||||||
|
|
||||||
// Reapply expand state to newly loaded traces
|
// Reapply expand state to newly loaded traces
|
||||||
nextTick(() => {
|
applyExpandState();
|
||||||
if (tableRef.value) {
|
|
||||||
const visibleItems = filteredTraces.value;
|
|
||||||
for (const row of visibleItems) {
|
|
||||||
tableRef.value.toggleRowExpansion(row, expandAll.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
);
|
);
|
||||||
@@ -320,12 +347,6 @@ limitations under the License. -->
|
|||||||
tableContainer.removeEventListener("scroll", handleScroll);
|
tableContainer.removeEventListener("scroll", handleScroll);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Toggle all table row expansions (only for loaded items)
|
|
||||||
function toggleAllExpansion() {
|
|
||||||
// The expandAll watcher will handle applying the state to all visible items
|
|
||||||
// This function just needs to trigger the change
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.trace-query-table {
|
.trace-query-table {
|
||||||
|
|||||||
Reference in New Issue
Block a user