update trace table

This commit is contained in:
Fine 2025-04-10 16:16:51 +08:00
parent 20048e71c0
commit f80bd95bef
9 changed files with 93 additions and 131 deletions

View File

@ -43,7 +43,6 @@ limitations under the License. -->
<Table
:data="profileStore.segmentSpans"
:traceId="profileStore.currentSegment.traceId"
:showBtnDetail="true"
headerType="profile"
@select="selectSpan"
/>
@ -53,7 +52,7 @@ limitations under the License. -->
<script lang="ts" setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import Table from "../../trace/components/Table/Index.vue";
import Table from "../../trace/components/Table.vue";
import { useProfileStore } from "@/store/modules/profile";
import Selector from "@/components/Selector.vue";
import type { Span } from "@/types/trace";

View File

@ -14,7 +14,17 @@ limitations under the License. -->
<div class="trace-t-loading" v-show="loading">
<Icon iconName="spinner" size="sm" />
</div>
<div ref="traceGraph" class="d3-graph"></div>
<TableContainer
v-if="type === TraceGraphType.TABLE"
:tableData="segmentId"
:type="type"
:headerType="headerType"
:traceId="traceId"
@select="handleSelectSpan"
>
<div class="trace-tips" v-if="!segmentId.length">{{ $t("noData") }}</div>
</TableContainer>
<div v-else ref="traceGraph" class="d3-graph"></div>
<div id="trace-action-box">
<div @click="showDetail = true">Span details</div>
<div v-for="span in parentSpans" :key="span.segmentId" @click="viewParentSpan(span)">
@ -45,6 +55,7 @@ limitations under the License. -->
import TreeGraph from "../../utils/d3-trace-tree";
import type { Span, Ref } from "@/types/trace";
import SpanDetail from "./SpanDetail.vue";
import TableContainer from "../Table/TableContainer.vue";
import { useAppStoreWithOut } from "@/store/modules/app";
import { debounce } from "@/utils/debounce";
import { mutationObserver } from "@/utils/mutation";
@ -55,6 +66,7 @@ limitations under the License. -->
data: { type: Array as PropType<Span[]>, default: () => [] },
traceId: { type: String, default: "" },
type: { type: String, default: TraceGraphType.LIST },
headerType: { type: String, default: "" },
});
const appStore = useAppStoreWithOut();
const loading = ref<boolean>(false);
@ -76,25 +88,21 @@ limitations under the License. -->
onMounted(() => {
loading.value = true;
changeTree();
if (!traceGraph.value) {
loading.value = false;
return;
}
if (props.type !== TraceGraphType.TABLE) {
draw();
}
draw();
loading.value = false;
// monitor segment list width changes.
mutationObserver.create("trigger-resize", () => {
d3.selectAll(".d3-tip").remove();
debounceFunc();
});
window.addEventListener("resize", debounceFunc);
});
function draw() {
if (props.type === TraceGraphType.TABLE) {
segmentId.value = setLevel(segmentId.value);
return;
}
if (!traceGraph.value) {
return;
}
@ -107,6 +115,7 @@ limitations under the License. -->
fixSpansSize.value,
);
tree.value.draw();
return;
}
if (props.type === TraceGraphType.TREE) {
tree.value = new TreeGraph(traceGraph.value, handleSelectSpan);
@ -117,6 +126,7 @@ limitations under the License. -->
}
}
function handleSelectSpan(i: Recordable) {
console.log(i);
const spans = [];
const refSpans = [];
parentSpans.value = [];
@ -364,6 +374,17 @@ limitations under the License. -->
}
}
}
function setLevel(arr: Recordable[], level = 1, totalExec?: number) {
for (const item of arr) {
item.level = level;
totalExec = totalExec || item.endTime - item.startTime;
item.totalExec = totalExec;
if (item.children && item.children.length > 0) {
setLevel(item.children, level + 1, totalExec);
}
}
return arr;
}
function getRefsAllNodes(tree: Recordable) {
let nodes = [];
let stack = [tree];

View File

@ -17,7 +17,7 @@ limitations under the License. -->
<div class="trace-t-loading" v-show="loading">
<Icon iconName="spinner" size="sm" />
</div>
<TableContainer :tableData="tableData" type="statistics" :HeaderType="HeaderType">
<TableContainer :tableData="tableData" :type="TraceGraphType.STATISTICS" :HeaderType="HeaderType">
<div class="trace-tips" v-if="!tableData.length">{{ $t("noData") }}</div>
</TableContainer>
</div>
@ -28,6 +28,7 @@ limitations under the License. -->
import TableContainer from "./Table/TableContainer.vue";
import traceTable from "../utils/trace-table";
import type { StatisticsSpan, Span, StatisticsGroupRef } from "@/types/trace";
import { TraceGraphType } from "./constant";
/* global defineProps, defineEmits, Recordable*/
const props = defineProps({

View File

@ -0,0 +1,38 @@
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="charts">
<Graph :data="data" :traceId="traceId" :type="TraceGraphType.TABLE" :headerType="headerType" />
</div>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import type { Span } from "@/types/trace";
import Graph from "./D3Graph/Index.vue";
import { TraceGraphType } from "./constant";
/* global defineProps, Recordable*/
defineProps({
data: { type: Array as PropType<Span[]>, default: () => [] },
traceId: { type: String, default: "" },
headerType: { type: String, default: "" },
});
</script>
<style lang="scss" scoped>
.charts {
overflow: auto;
padding: 10px;
height: 100%;
width: 100%;
}
</style>

View File

@ -1,102 +0,0 @@
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="trace-table">
<div class="trace-t-loading" v-show="loading">
<Icon iconName="spinner" size="sm" />
</div>
<TableContainer
:tableData="tableData"
type="table"
:headerType="headerType"
:traceId="traceId"
@select="handleSelectSpan"
>
<div class="trace-tips" v-if="!tableData.length">{{ $t("noData") }}</div>
</TableContainer>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import type { PropType } from "vue";
import TableContainer from "./TableContainer.vue";
import traceTable from "../../utils/trace-table";
import type { Span } from "@/types/trace";
/* global defineProps, defineEmits, Recordable */
const props = defineProps({
data: { type: Array as PropType<Span[]>, default: () => [] },
traceId: { type: String, default: "" },
showBtnDetail: { type: Boolean, default: false },
headerType: { type: String, default: "" },
});
const emit = defineEmits(["select", "view", "load"]);
const loading = ref<boolean>(true);
const tableData = ref<Recordable[]>([]);
const showDetail = ref<boolean>(false);
const currentSpan = ref<Span | Recordable>({});
onMounted(() => {
tableData.value = formatData(traceTable.changeTree(props.data, props.traceId));
loading.value = false;
emit("load", () => {
loading.value = true;
});
});
function formatData(arr: Recordable[], level = 1, totalExec?: number) {
for (const item of arr) {
item.level = level;
totalExec = totalExec || item.endTime - item.startTime;
item.totalExec = totalExec;
if (item.children && item.children.length > 0) {
formatData(item.children, level + 1, totalExec);
}
}
return arr;
}
function handleSelectSpan(data: Span) {
currentSpan.value = data;
if (!props.showBtnDetail) {
showDetail.value = true;
}
emit("select", data);
}
watch(
() => props.data,
() => {
if (!props.data.length) {
tableData.value = [];
return;
}
tableData.value = formatData(traceTable.changeTree(props.data, props.traceId));
loading.value = false;
},
);
</script>
<style lang="scss" scoped>
.trace-tips {
width: 100%;
text-align: center;
margin-top: 10px;
}
.trace-table {
height: 100%;
width: 100%;
}
</style>

View File

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="trace">
<div class="trace-header" v-if="type === 'statistics'">
<div class="trace-table">
<div class="trace-table-header" v-if="type === TraceGraphType.STATISTICS">
<div :class="item.label" v-for="(item, index) in headerData" :key="index">
{{ item.value }}
<span
@ -28,7 +28,7 @@ limitations under the License. -->
</span>
</div>
</div>
<div class="trace-header" v-else>
<div class="trace-table-header" v-else>
<div class="method" :style="`width: ${method}px`">
<span class="cp dragger" ref="dragger">
<Icon iconName="settings_ethernet" size="sm" />
@ -44,7 +44,7 @@ limitations under the License. -->
:traceId="traceId"
v-for="(item, index) in tableData"
:data="item"
:key="'key' + index"
:key="`key${index}`"
:type="type"
:headerType="headerType"
@select="selectItem"
@ -58,6 +58,7 @@ limitations under the License. -->
import type { Span } from "@/types/trace";
import TableItem from "./TableItem.vue";
import { ProfileConstant, TraceConstant, StatisticsConstant } from "./data";
import { TraceGraphType } from "../constant";
/* global defineProps, Nullable, defineEmits, Recordable*/
const props = defineProps({
@ -76,11 +77,11 @@ limitations under the License. -->
if (props.headerType === "profile") {
headerData = ProfileConstant;
}
if (props.type === "statistics") {
if (props.type === TraceGraphType.STATISTICS) {
headerData = StatisticsConstant;
}
onMounted(() => {
if (props.type === "statistics") {
if (props.type === TraceGraphType.STATISTICS) {
return;
}
const drag: Nullable<HTMLSpanElement> = dragger.value;
@ -102,6 +103,7 @@ limitations under the License. -->
};
});
function selectItem(span: Span) {
console.log(span);
emits("select", span);
}
function sortStatistics(key: string) {
@ -152,7 +154,7 @@ limitations under the License. -->
<style lang="scss" scoped>
@import url("./table.scss");
.trace {
.trace-table {
font-size: $font-size-smaller;
height: 100%;
overflow: auto;
@ -163,7 +165,7 @@ limitations under the License. -->
float: right;
}
.trace-header {
.trace-table-header {
white-space: nowrap;
user-select: none;
border-left: 0;
@ -171,7 +173,7 @@ limitations under the License. -->
border-bottom: 1px solid var(--sw-trace-list-border);
}
.trace-header div {
.trace-table-header div {
display: inline-block;
background-color: var(--sw-table-header);
padding: 0 4px;

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div v-if="type === 'statistics'">
<div v-if="type === TraceGraphType.STATISTICS">
<div class="trace-item">
<div :class="['method']">
<el-tooltip :content="data.groupRef.endpointName" placement="bottom" :show-after="300">
@ -149,6 +149,7 @@ limitations under the License. -->
import { dateFormat } from "@/utils/dateFormat";
import { useAppStoreWithOut } from "@/store/modules/app";
import { Themes } from "@/constants/data";
import { TraceGraphType } from "../constant";
/*global Recordable*/
const props = {
@ -213,8 +214,7 @@ limitations under the License. -->
}
function selectSpan(event: Recordable) {
const dom = event.composedPath().find((d: Recordable) => d.className.includes("trace-item"));
emit("select", props.data);
selectedItem(props.data);
if (props.headerType === "profile") {
showSelectSpan(dom);
return;
@ -226,13 +226,14 @@ limitations under the License. -->
emit("select", props.data);
viewSpanDetail(dom);
}
function selectedItem(data: HTMLSpanElement) {
function selectedItem(data: Recordable) {
emit("select", data);
}
function viewSpanDetail(dom: HTMLSpanElement) {
showSelectSpan(dom);
showDetail.value = true;
if (props.type === TraceGraphType.STATISTICS) {
showDetail.value = true;
}
}
watch(
() => appStore.theme,
@ -262,6 +263,7 @@ limitations under the License. -->
viewSpan,
t,
appStore,
TraceGraphType,
};
},
});

View File

@ -19,4 +19,5 @@ export enum TraceGraphType {
TREE = "Tree",
LIST = "List",
TABLE = "Table",
STATISTICS = "Statistics",
}

View File

@ -17,7 +17,7 @@
import List from "./List.vue";
import Tree from "./Tree.vue";
import Table from "./Table/Index.vue";
import Table from "./Table.vue";
import Statistics from "./Statistics.vue";
export default {