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 b710a0a5 feat: add the queryOrder to trace filters (#508)
b710a0a5 is described below

commit b710a0a589e293b0028e3b525cbad51b2b09cf6b
Author: Fine0830 <[email protected]>
AuthorDate: Tue Oct 21 10:39:15 2025 +0800

    feat: add the queryOrder to trace filters (#508)
---
 src/views/dashboard/related/trace/Content.vue      |  18 ++-
 .../trace/components/TraceQuery/TracesTable.vue    | 121 ++++++++++++---------
 2 files changed, 87 insertions(+), 52 deletions(-)

diff --git a/src/views/dashboard/related/trace/Content.vue 
b/src/views/dashboard/related/trace/Content.vue
index 072fd45e..4972db85 100644
--- a/src/views/dashboard/related/trace/Content.vue
+++ b/src/views/dashboard/related/trace/Content.vue
@@ -16,7 +16,15 @@ limitations under the License. -->
   <div class="search-bar">
     <Filter :needQuery="needQuery" :data="data" @get="getService" 
@search="popSegmentList" />
     <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" />
     </div>
   </div>
@@ -48,6 +56,7 @@ limitations under the License. -->
   import type { LayoutConfig } from "@/types/dashboard";
   import { mutationObserver } from "@/utils/mutation";
   import TraceQuery from "./components/TraceQuery/Index.vue";
+  import { QueryOrders } from "@/views/dashboard/data";
   /*global defineProps */
   const props = defineProps({
     data: {
@@ -64,6 +73,7 @@ limitations under the License. -->
   const currentWidth = ref<number>(280);
   const needQuery = ref<boolean>(true);
   const isDrag = ref<boolean>(false);
+  const queryOrder = ref<string>(QueryOrders[0].value);
   const limit = ref(PageSize);
   const defaultWidth = 280;
   const minArrowLeftWidth = 120;
@@ -77,7 +87,11 @@ limitations under the License. -->
       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.
   function triggerArrow() {
     currentWidth.value = isLeft.value ? 0 : defaultWidth;
diff --git 
a/src/views/dashboard/related/trace/components/TraceQuery/TracesTable.vue 
b/src/views/dashboard/related/trace/components/TraceQuery/TracesTable.vue
index 2d21326c..0c717f1f 100644
--- a/src/views/dashboard/related/trace/components/TraceQuery/TracesTable.vue
+++ b/src/views/dashboard/related/trace/components/TraceQuery/TracesTable.vue
@@ -16,14 +16,7 @@ limitations under the License. -->
   <div class="flex-h result-header">
     <div style="align-items: center"> {{ filteredTraces.length }} of {{ 
totalTraces }} Results </div>
     <div class="flex-h" style="align-items: center">
-      <el-switch
-        v-model="expandAll"
-        size="large"
-        active-text="Expand All"
-        inactive-text="Collapse All"
-        class="mr-20"
-        @change="toggleAllExpansion"
-      />
+      <el-switch v-model="expandAll" size="large" active-text="Expand All" 
inactive-text="Collapse All" class="mr-20" />
       <Selector
         placeholder="Service filters"
         @change="changeServiceFilters"
@@ -43,6 +36,7 @@ limitations under the License. -->
       :default-sort="{ prop: 'duration', order: 'descending' }"
       style="width: 100%"
       v-loading="traceStore.loading"
+      @sort-change="handleSortChange"
     >
       <el-table-column type="expand">
         <template #default="props">
@@ -61,7 +55,7 @@ limitations under the License. -->
         </template>
       </el-table-column>
       <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">
           {{ dateFormat(props.row.start) }}
         </template>
@@ -137,6 +131,9 @@ limitations under the License. -->
   const loading = ref<boolean>(false);
   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
   const maxDuration = computed(() => {
     if (!traceStore.traceList.length) return 1;
@@ -161,7 +158,26 @@ limitations under the License. -->
   });
 
   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);
@@ -208,6 +224,47 @@ limitations under the License. -->
     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) {
     selectedServiceNames.value = 
selectedServiceNames.value.includes(serviceName)
       ? selectedServiceNames.value.filter((name) => name !== serviceName)
@@ -224,7 +281,6 @@ limitations under the License. -->
   function changeServiceFilters(selected: Option[]) {
     selectedServiceNames.value = selected.map((o) => String(o.value));
   }
-
   // Infinite scroll handlers
   function loadMoreItems() {
     if (loading.value || !hasMoreItems.value) return;
@@ -235,17 +291,7 @@ limitations under the License. -->
     setTimeout(() => {
       const nextBatch = Math.min(PageSize, allFilteredTraces.value.length - 
loadedItemsCount.value);
       loadedItemsCount.value += nextBatch;
-
-      // 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);
-          }
-        }
-      });
-
+      applyExpandState();
       loading.value = false;
     }, 300);
   }
@@ -265,25 +311,13 @@ limitations under the License. -->
     loadedItemsCount.value = PageSize;
     // Apply expand state to newly visible items after filter change
     nextTick(() => {
-      if (tableRef.value) {
-        const visibleItems = filteredTraces.value;
-        for (const row of visibleItems) {
-          tableRef.value.toggleRowExpansion(row, expandAll.value);
-        }
-      }
+      applyExpandState();
     });
   });
 
   // Watch for expandAll state changes to apply to all visible items
   watch(expandAll, () => {
-    nextTick(() => {
-      if (tableRef.value) {
-        const visibleItems = filteredTraces.value;
-        for (const row of visibleItems) {
-          tableRef.value.toggleRowExpansion(row, expandAll.value);
-        }
-      }
-    });
+    applyExpandState();
   });
 
   // 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;
 
       // Reapply expand state to newly loaded traces
-      nextTick(() => {
-        if (tableRef.value) {
-          const visibleItems = filteredTraces.value;
-          for (const row of visibleItems) {
-            tableRef.value.toggleRowExpansion(row, expandAll.value);
-          }
-        }
-      });
+      applyExpandState();
     },
     { deep: true },
   );
@@ -320,12 +347,6 @@ limitations under the License. -->
       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>
 <style lang="scss" scoped>
   .trace-query-table {

Reply via email to