diff --git a/web/server/vue-cli/src/views/Reports.vue b/web/server/vue-cli/src/views/Reports.vue
index c1af76a516..444775b45c 100644
--- a/web/server/vue-cli/src/views/Reports.vue
+++ b/web/server/vue-cli/src/views/Reports.vue
@@ -199,38 +199,140 @@
({ ...node }));
+ _sorted.forEach(node => {
+ if (node.children && node.children.length > 0) {
+ node.children = sortTreeNodes(node.children);
+ }
+ });
+
+ if (treeSortKey.value && treeSortOrder.value) {
+ _sorted.sort((a, b) => {
+ const _aVal = getTreeStatValue(a, treeSortKey.value);
+ const _bVal = getTreeStatValue(b, treeSortKey.value);
+ return treeSortOrder.value === "desc"
+ ? _bVal - _aVal
+ : _aVal - _bVal;
+ });
+ }
+
+ return _sorted;
+}
+
+const sortedTreeItems = computed(function() {
+ return sortTreeNodes(treeItems.value);
+});
+
watch(
[ page, itemsPerPage, sortBy ],
() => {
@@ -540,6 +675,18 @@ function onTreeItemClick(item) {
viewMode.value = "table";
}
+function toggleTreeSort(statKey) {
+ if (treeSortKey.value !== statKey) {
+ treeSortKey.value = statKey;
+ treeSortOrder.value = "desc";
+ } else if (treeSortOrder.value === "desc") {
+ treeSortOrder.value = "asc";
+ } else {
+ treeSortKey.value = null;
+ treeSortOrder.value = null;
+ }
+}
+
function i64ToNum(val) {
if (val == null) return 0;
if (typeof val === "number") return val;
@@ -867,6 +1014,19 @@ body {
width: 50px;
text-align: center;
flex-shrink: 0;
+
+ &.sortable {
+ cursor: pointer;
+ user-select: none;
+
+ &:hover {
+ color: rgb(var(--v-theme-primary));
+ }
+ }
+
+ .sort-icon {
+ vertical-align: middle;
+ }
}
.tree-row {