mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-05-14 17:05:10 +00:00
Merge pull request #8 from pemeraldy/mod/filters-layout
Mod/filters layout
This commit is contained in:
commit
44f3763a54
@ -37,7 +37,7 @@ limitations under the License. -->
|
||||
</el-select>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import type { PropType } from "vue";
|
||||
|
||||
interface Option {
|
||||
@ -95,4 +95,7 @@ watch(
|
||||
.el-input__inner {
|
||||
border-radius: unset !important;
|
||||
}
|
||||
.el-input.el-input--small.el-input--suffix {
|
||||
height: 18px !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -27,7 +27,8 @@ async function query(param: {
|
||||
{
|
||||
cancelToken: cancelToken(),
|
||||
headers: {
|
||||
Authorization: "Basic c2t5d2Fsa2luZzpza3l3YWxraW5n",
|
||||
Authorization:
|
||||
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkZXZlbG9wZXJfaWQiOiJzeXN0ZW0iLCJjcmVhdGVkX2F0IjoxNjIyNDIxMzY0ODY4LCJleHBpcmVzX2F0IjoxNjUzOTU3MzY0ODY4LCJpYXQiOjE2MjI0MjEzNjR9.ZVHtxQkfCF7KM_dyDOgawbwpEAsmnCWB4c8I52svPvVc-SlzkEe0SYrNufNPniYZeM3IF0Gbojl_DSk2KleAz9CLRO3zfegciXKeEEvGjsNOqfQjgU5yZtBWmTimVXq5QoZMEGuAojACaf-m4J0H7o4LQNGwrDVA-noXVE0Eu84A5HxkjrRuFlQWv3fzqSRC_-lI0zRKuFGD-JkIfJ9b_wP_OjBWT6nmqkZn_JmK7UwniTUJjocszSA2Ma3XLx2xVPzBcz00QWyjhIyiftxNQzgqLl1XDVkRtzXUIrHnFCR8BcgR_PsqTBn5nH7aCp16zgmkkbOpmJXlNpDSVz9zUY4NOrB1jTzDB190COrfCXddb7JO6fmpet9_Zd3kInJx4XsT3x7JfBSWr9FBqFoUmNkgIWjkbN1TpwMyizXASp1nOmwJ64FDIbSpfpgUAqfSWXKZYhSisfnBLEyHCjMSPzVmDh949w-W1wU9q5nGFtrx6PTOxK_WKOiWU8_oeTjL0pD8pKXqJMaLW-OIzfrl3kzQNuF80YT-nxmNtp5PrcxehprlPmqSB_dyTHccsO3l63d8y9hiIzfRUgUjTJbktFn5t41ADARMs_0WMpIGZJyxcVssstt4J1Gj8WUFOdqPsIKigJZMn3yshC5S-KY-7S0dVd0VXgvpPqmpb9Q9Uho",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@ -56,7 +56,8 @@ class Graphql {
|
||||
{
|
||||
cancelToken: cancelToken(),
|
||||
headers: {
|
||||
Authorization: "Basic c2t5d2Fsa2luZzpza3l3YWxraW5n",
|
||||
Authorization:
|
||||
"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJkZXZlbG9wZXJfaWQiOiJzeXN0ZW0iLCJjcmVhdGVkX2F0IjoxNjIyNDIxMzY0ODY4LCJleHBpcmVzX2F0IjoxNjUzOTU3MzY0ODY4LCJpYXQiOjE2MjI0MjEzNjR9.ZVHtxQkfCF7KM_dyDOgawbwpEAsmnCWB4c8I52svPvVc-SlzkEe0SYrNufNPniYZeM3IF0Gbojl_DSk2KleAz9CLRO3zfegciXKeEEvGjsNOqfQjgU5yZtBWmTimVXq5QoZMEGuAojACaf-m4J0H7o4LQNGwrDVA-noXVE0Eu84A5HxkjrRuFlQWv3fzqSRC_-lI0zRKuFGD-JkIfJ9b_wP_OjBWT6nmqkZn_JmK7UwniTUJjocszSA2Ma3XLx2xVPzBcz00QWyjhIyiftxNQzgqLl1XDVkRtzXUIrHnFCR8BcgR_PsqTBn5nH7aCp16zgmkkbOpmJXlNpDSVz9zUY4NOrB1jTzDB190COrfCXddb7JO6fmpet9_Zd3kInJx4XsT3x7JfBSWr9FBqFoUmNkgIWjkbN1TpwMyizXASp1nOmwJ64FDIbSpfpgUAqfSWXKZYhSisfnBLEyHCjMSPzVmDh949w-W1wU9q5nGFtrx6PTOxK_WKOiWU8_oeTjL0pD8pKXqJMaLW-OIzfrl3kzQNuF80YT-nxmNtp5PrcxehprlPmqSB_dyTHccsO3l63d8y9hiIzfRUgUjTJbktFn5t41ADARMs_0WMpIGZJyxcVssstt4J1Gj8WUFOdqPsIKigJZMn3yshC5S-KY-7S0dVd0VXgvpPqmpb9Q9Uho",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -269,6 +269,7 @@ const msg = {
|
||||
contentType: "Content Type",
|
||||
content: "Content",
|
||||
viewLogs: "View Logs",
|
||||
back: "Back",
|
||||
logsTagsTip: `Only tags defined in the core/default/searchableLogsTags are searchable.
|
||||
Check more details on the Configuration Vocabulary page`,
|
||||
keywordsOfContentLogTips:
|
||||
|
@ -40,6 +40,8 @@ interface DashboardState {
|
||||
showTopology: boolean;
|
||||
fullView: boolean;
|
||||
currentTabItems: LayoutConfig[];
|
||||
showTraceTools: boolean;
|
||||
showLogTools: boolean;
|
||||
dashboards: DashboardItem[];
|
||||
currentDashboard: Nullable<DashboardItem>;
|
||||
editMode: boolean;
|
||||
@ -57,6 +59,8 @@ export const dashboardStore = defineStore({
|
||||
durationTime: useAppStoreWithOut().durationTime,
|
||||
selectorStore: useSelectorStore(),
|
||||
showTopology: false,
|
||||
showLogTools: false,
|
||||
showTraceTools: false,
|
||||
fullView: false,
|
||||
currentTabItems: [],
|
||||
dashboards: [],
|
||||
@ -260,6 +264,12 @@ export const dashboardStore = defineStore({
|
||||
setEntity(type: string) {
|
||||
this.entity = type;
|
||||
},
|
||||
setTraceTools(show: boolean) {
|
||||
this.showTraceTools = show;
|
||||
},
|
||||
setLogTools(show: boolean) {
|
||||
this.showLogTools = show;
|
||||
},
|
||||
setTopology(show: boolean) {
|
||||
this.showTopology = show;
|
||||
},
|
||||
|
@ -29,6 +29,9 @@ interface TraceState {
|
||||
instances: Instance[];
|
||||
endpoints: Endpoint[];
|
||||
traceList: Trace[];
|
||||
activeFilter: string;
|
||||
displayMode: string;
|
||||
currentView: string;
|
||||
traceTotal: number;
|
||||
traceSpans: Span[];
|
||||
currentTrace: Trace | any;
|
||||
@ -48,6 +51,9 @@ export const traceStore = defineStore({
|
||||
services: [{ value: "0", label: "All" }],
|
||||
instances: [{ value: "0", label: "All" }],
|
||||
endpoints: [{ value: "0", label: "All" }],
|
||||
displayMode: "List",
|
||||
currentView: "traceList",
|
||||
activeFilter: "",
|
||||
traceList: [],
|
||||
traceSpans: [],
|
||||
traceTotal: 0,
|
||||
@ -67,6 +73,16 @@ export const traceStore = defineStore({
|
||||
setTraceCondition(data: any) {
|
||||
this.condition = { ...this.condition, ...data };
|
||||
},
|
||||
setDisplayMode(data: string) {
|
||||
this.displayMode = data;
|
||||
},
|
||||
setCurrentView(data: string) {
|
||||
this.currentView = data;
|
||||
},
|
||||
setActiveFilter(data: string) {
|
||||
if (!data) this.activeFilter = "";
|
||||
this.activeFilter = data;
|
||||
},
|
||||
setCurrentTrace(trace: Trace) {
|
||||
this.currentTrace = trace;
|
||||
},
|
||||
|
3
src/types/components.d.ts
vendored
3
src/types/components.d.ts
vendored
@ -1,6 +1,7 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/vue-next/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
@ -45,4 +46,4 @@ declare module '@vue/runtime-core' {
|
||||
}
|
||||
}
|
||||
|
||||
export { }
|
||||
export {}
|
||||
|
@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="flex-h" :class="{ light: theme === 'light' }">
|
||||
<div class="mr-5">
|
||||
<div class="flex-h items-center mr-5">
|
||||
<span class="sm grey" v-show="theme === 'dark'">{{ t("tags") }}: </span>
|
||||
<span
|
||||
class="trace-tags"
|
||||
:style="type === 'LOG' ? `min-width: 122px;` : ''"
|
||||
>
|
||||
<span v-if="tagsList.length" class="trace-tags">
|
||||
<!-- :style="type === 'LOG' ? `min-width: 122px;` : ''" -->
|
||||
<span class="selected" v-for="(item, index) in tagsList" :key="index">
|
||||
<span>{{ item }}</span>
|
||||
<span class="remove-icon" @click="removeTags(index)">×</span>
|
||||
@ -34,6 +32,7 @@ limitations under the License. -->
|
||||
/>
|
||||
<span class="tags-tip">
|
||||
<a
|
||||
v-if="false"
|
||||
target="blank"
|
||||
href="https://github.com/apache/skywalking/blob/master/docs/en/setup/backend/configuration-vocabulary.md"
|
||||
>
|
||||
@ -54,17 +53,18 @@ limitations under the License. -->
|
||||
<Icon class="icon-help mr-5" iconName="help" size="middle" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<b v-if="type !== 'LOG'">{{ t("noticeTag") }}</b>
|
||||
<!-- <b v-if="type !== 'LOG'">{{ t("noticeTag") }}</b> -->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { ref, defineExpose } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
/*global defineEmits, defineProps */
|
||||
const emit = defineEmits(["update"]);
|
||||
|
||||
defineProps({
|
||||
type: { type: String, default: "TRACE" },
|
||||
});
|
||||
@ -73,6 +73,13 @@ const theme = ref<string>("dark");
|
||||
const tags = ref<string>("");
|
||||
const tagsList = ref<string[]>([]);
|
||||
|
||||
defineExpose({
|
||||
tagsList,
|
||||
emptyTags
|
||||
})
|
||||
function emptyTags (){
|
||||
tagsList.value = []
|
||||
}
|
||||
function removeTags(index: number) {
|
||||
tagsList.value.splice(index, 1);
|
||||
updateTags();
|
||||
@ -97,6 +104,9 @@ function updateTags() {
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.trace-tags {
|
||||
padding: 1px 5px 0 0;
|
||||
border-radius: 3px;
|
||||
|
@ -29,18 +29,17 @@ limitations under the License. -->
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="header">
|
||||
<Header />
|
||||
</div>
|
||||
<!-- <div class="header">
|
||||
</div> -->
|
||||
<div class="log">
|
||||
<List />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { onBeforeUnmount, onMounted } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import Header from "../related/log/Header.vue";
|
||||
import List from "../related/log/List.vue";
|
||||
|
||||
/*global defineProps */
|
||||
@ -57,6 +56,12 @@ const dashboardStore = useDashboardStore();
|
||||
function removeWidget() {
|
||||
dashboardStore.removeControls(props.data);
|
||||
}
|
||||
onMounted(() => {
|
||||
dashboardStore.setLogTools(true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
dashboardStore.setLogTools(false);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.log-wrapper {
|
||||
|
@ -147,14 +147,7 @@ limitations under the License. -->
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
defineComponent,
|
||||
toRefs,
|
||||
} from "vue";
|
||||
import { ref, watch, onMounted, onBeforeUnmount, defineComponent, toRefs } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { PropType } from "vue";
|
||||
import { LayoutConfig } from "@/types/dashboard";
|
||||
@ -194,9 +187,7 @@ export default defineComponent({
|
||||
const currentItem = ref<number>(0);
|
||||
const isScrolling = ref(false);
|
||||
|
||||
const l = dashboardStore.layout.findIndex(
|
||||
(d: LayoutConfig) => d.i === props.data.i
|
||||
);
|
||||
const l = dashboardStore.layout.findIndex((d: LayoutConfig) => d.i === props.data.i);
|
||||
if (dashboardStore.layout[l].children.length) {
|
||||
dashboardStore.setCurrentTabItems(
|
||||
dashboardStore.layout[l].children[activeTabIndex.value].children
|
||||
@ -314,9 +305,7 @@ export default defineComponent({
|
||||
function clickTabGrid(e: Event, item: LayoutConfig) {
|
||||
e.stopPropagation();
|
||||
activeTabWidget.value = item.i;
|
||||
dashboardStore.activeGridItem(
|
||||
`${props.data.i}-${activeTabIndex.value}-${item.i}`
|
||||
);
|
||||
dashboardStore.activeGridItem(`${props.data.i}-${activeTabIndex.value}-${item.i}`);
|
||||
handleClick(e);
|
||||
}
|
||||
function layoutUpdatedEvent() {
|
||||
@ -484,7 +473,9 @@ export default defineComponent({
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-header .tabs .span.active {
|
||||
color: red !important;
|
||||
}
|
||||
.operations {
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
|
@ -29,22 +29,19 @@ limitations under the License. -->
|
||||
<span>{{ t("delete") }}</span>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="header">
|
||||
<Filter />
|
||||
</div>
|
||||
<div class="trace flex-h">
|
||||
<TraceList />
|
||||
<TraceDetail />
|
||||
<TraceList @show:trace="showTraceDetails" v-if="traceListActive" />
|
||||
<TraceDetail @show:list="showTraceList" v-if="!traceListActive" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import Filter from "../related/trace/Filter.vue";
|
||||
import type { PropType, computed, onMounted, onBeforeUnmount } from "vue";
|
||||
import TraceList from "../related/trace/TraceList.vue";
|
||||
import TraceDetail from "../related/trace/Detail.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
|
||||
/*global defineProps */
|
||||
const props = defineProps({
|
||||
@ -56,9 +53,26 @@ const props = defineProps({
|
||||
});
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const traceStore = useTraceStore();
|
||||
const traceListActive = computed(() => {
|
||||
return traceStore.currentView === "traceList";
|
||||
});
|
||||
function removeWidget() {
|
||||
dashboardStore.removeControls(props.data);
|
||||
}
|
||||
function showTraceDetails() {
|
||||
traceStore.currentView === "traceDetails";
|
||||
}
|
||||
function showTraceList() {
|
||||
traceStore.currentView === "traceList";
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
dashboardStore.setTraceTools(true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
dashboardStore.setTraceTools(false);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.trace-wrapper {
|
||||
@ -95,6 +109,7 @@ function removeWidget() {
|
||||
|
||||
.trace {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
@ -15,115 +15,182 @@ limitations under the License. -->
|
||||
<template>
|
||||
<div class="dashboard-tool flex-h">
|
||||
<div class="flex-h">
|
||||
<div class="selectors-item" v-if="key !== 10">
|
||||
<span class="label">$Service</span>
|
||||
<Selector
|
||||
v-model="states.currentService"
|
||||
:options="selectorStore.services"
|
||||
size="small"
|
||||
placeholder="Select a service"
|
||||
@change="changeService"
|
||||
class="selectors"
|
||||
/>
|
||||
<div class="flex-h">
|
||||
<div
|
||||
class="selectors-item"
|
||||
v-if="key !== 10 && currentTraceView === 'traceList'"
|
||||
>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="Services"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button
|
||||
v-if="!selectedSelector.length || selectedSelector === '$service'"
|
||||
@click="setSelectedSelector('$service')"
|
||||
class="tool-btn"
|
||||
size="small"
|
||||
>
|
||||
<Icon size="sm" iconName="playlist_add" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<Selector
|
||||
v-if="selectedSelector === '$service'"
|
||||
style="margin-left: 20px"
|
||||
v-model="states.currentService"
|
||||
:options="selectorStore.services"
|
||||
size="small"
|
||||
placeholder="Select a service"
|
||||
@change="changeService"
|
||||
class="selectors"
|
||||
/>
|
||||
<el-button
|
||||
style="margin-left: 4px"
|
||||
v-if="selectedSelector === '$service'"
|
||||
class="search-btn tool-btn"
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="closeSelector"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="cancel" />
|
||||
</el-button>
|
||||
</div>
|
||||
<div
|
||||
class="selectors-item"
|
||||
v-if="(key === 3 || key === 4) && currentTraceView === 'traceList'"
|
||||
>
|
||||
<el-tooltip
|
||||
v-if="!selectedSelector.length || selectedSelector === '$endpoint'"
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="Endpoint"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button
|
||||
style="margin-left: 4px"
|
||||
@click="setSelectedSelector('$endpoint')"
|
||||
class="tool-btn"
|
||||
>
|
||||
<Icon size="sm" iconName="view" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<Selector
|
||||
v-if="selectedSelector === '$endpoint'"
|
||||
style="margin-left: 20px"
|
||||
v-model="states.currentPod"
|
||||
:options="selectorStore.pods"
|
||||
size="small"
|
||||
placeholder="Select a data"
|
||||
@change="changePods"
|
||||
@query="searchPods"
|
||||
class="selectorPod"
|
||||
:isRemote="
|
||||
['EndpointRelation', 'Endpoint'].includes(dashboardStore.entity)
|
||||
"
|
||||
/>
|
||||
<el-button
|
||||
style="margin-left: 4px"
|
||||
v-if="selectedSelector === '$endpoint'"
|
||||
class="search-btn"
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="closeSelector"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="cancel" />
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 2 || key === 4">
|
||||
<span class="label">$DestinationService</span>
|
||||
<Selector
|
||||
v-model="states.currentDestService"
|
||||
:options="selectorStore.destServices"
|
||||
size="small"
|
||||
placeholder="Select a service"
|
||||
@change="changeDestService"
|
||||
class="selectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 4">
|
||||
<span class="label">
|
||||
{{
|
||||
dashboardStore.entity === "EndpointRelation"
|
||||
? "$DestinationEndpoint"
|
||||
: "$DestinationServiceInstance"
|
||||
}}
|
||||
</span>
|
||||
<Selector
|
||||
v-model="states.currentDestPod"
|
||||
:options="selectorStore.destPods"
|
||||
size="small"
|
||||
placeholder="Select a data"
|
||||
@change="changeDestPods"
|
||||
class="selectorPod"
|
||||
@query="searchDestPods"
|
||||
:isRemote="dashboardStore.entity === 'EndpointRelation'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 3 || key === 4">
|
||||
<span class="label">
|
||||
{{
|
||||
["EndpointRelation", "Endpoint"].includes(dashboardStore.entity)
|
||||
? "$Endpoint"
|
||||
: "$ServiceInstance"
|
||||
}}
|
||||
</span>
|
||||
<Selector
|
||||
v-model="states.currentPod"
|
||||
:options="selectorStore.pods"
|
||||
size="small"
|
||||
placeholder="Select a data"
|
||||
@change="changePods"
|
||||
@query="searchPods"
|
||||
class="selectorPod"
|
||||
:isRemote="
|
||||
['EndpointRelation', 'Endpoint'].includes(dashboardStore.entity)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 2 || key === 4">
|
||||
<span class="label">$DestinationService</span>
|
||||
<Selector
|
||||
v-model="states.currentDestService"
|
||||
:options="selectorStore.destServices"
|
||||
size="small"
|
||||
placeholder="Select a service"
|
||||
@change="changeDestService"
|
||||
class="selectors"
|
||||
/>
|
||||
</div>
|
||||
<div class="selectors-item" v-if="key === 4">
|
||||
<span class="label">
|
||||
{{
|
||||
dashboardStore.entity === "EndpointRelation"
|
||||
? "$DestinationEndpoint"
|
||||
: "$DestinationServiceInstance"
|
||||
}}
|
||||
</span>
|
||||
<Selector
|
||||
v-model="states.currentDestPod"
|
||||
:options="selectorStore.destPods"
|
||||
size="small"
|
||||
placeholder="Select a data"
|
||||
@change="changeDestPods"
|
||||
class="selectorPod"
|
||||
@query="searchDestPods"
|
||||
:isRemote="dashboardStore.entity === 'EndpointRelation'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h tools" v-loading="loading" v-if="$route.query['portal'] !== 'true'">
|
||||
<div class="tool-icons flex-h" v-if="dashboardStore.editMode">
|
||||
<el-dropdown content="Controls" placement="bottom">
|
||||
<i>
|
||||
<Icon class="icon-btn" size="sm" iconName="control" />
|
||||
</i>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="clickIcons(t)"
|
||||
v-for="(t, index) in toolIcons"
|
||||
:key="index"
|
||||
:title="t.content"
|
||||
>
|
||||
<Icon class="mr-5" size="middle" :iconName="t.name" />
|
||||
<span>{{ t.content }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-tooltip content="Apply" placement="bottom" effect="light">
|
||||
<i @click="applyDashboard">
|
||||
<Icon class="icon-btn" size="sm" iconName="save" />
|
||||
</i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="switch">
|
||||
<el-switch
|
||||
v-model="dashboardStore.editMode"
|
||||
active-text="Edit"
|
||||
inactive-text="View"
|
||||
size="small"
|
||||
inline-prompt
|
||||
active-color="#409eff"
|
||||
inactive-color="#999"
|
||||
@change="changeMode"
|
||||
/>
|
||||
<div
|
||||
class="flex-h tools"
|
||||
v-loading="loading"
|
||||
v-if="$route.query['portal'] !== 'true'"
|
||||
>
|
||||
<div class="tool-icons flex-h" v-if="dashboardStore.editMode">
|
||||
<el-dropdown content="Controls" placement="bottom">
|
||||
<i>
|
||||
<Icon class="icon-btn" size="sm" iconName="control" />
|
||||
</i>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="clickIcons(t)"
|
||||
v-for="(t, index) in toolIcons"
|
||||
:key="index"
|
||||
:title="t.content"
|
||||
>
|
||||
<Icon class="mr-5" size="middle" :iconName="t.name" />
|
||||
<span>{{ t.content }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-tooltip content="Apply" placement="bottom" effect="light">
|
||||
<i @click="applyDashboard">
|
||||
<Icon class="icon-btn" size="sm" iconName="save" />
|
||||
</i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="switch">
|
||||
<el-switch
|
||||
v-model="dashboardStore.editMode"
|
||||
active-text="Edit"
|
||||
inactive-text="View"
|
||||
size="small"
|
||||
inline-prompt
|
||||
active-color="#409eff"
|
||||
inactive-color="#999"
|
||||
@change="changeMode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Header v-if="showLogHeader" />
|
||||
<TraceDetailsTools
|
||||
v-if="showTraceHeader && currentTraceView === 'traceDetails'"
|
||||
/>
|
||||
<Filter v-if="showTraceHeader && currentTraceView === 'traceList'" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import Filter from "../related/trace/Filter.vue";
|
||||
import Header from "../related/log/Header.vue";
|
||||
|
||||
import { reactive, ref, computed, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||
import { useAppStoreWithOut } from "@/store/modules/app";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
import {
|
||||
EntityType,
|
||||
AllTools,
|
||||
@ -138,13 +205,17 @@ import { useSelectorStore } from "@/store/modules/selectors";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { Option } from "@/types/app";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
import TraceDetailsTools from "./component/TraceDetailsTools";
|
||||
const { t } = useI18n();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const selectorStore = useSelectorStore();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const traceStore = useTraceStore();
|
||||
const params = useRoute().params;
|
||||
|
||||
const selectedSelector = ref<string>("");
|
||||
const showTraceHeader = computed(() => dashboardStore.showTraceTools);
|
||||
const showLogHeader = computed(() => dashboardStore.showLogTools);
|
||||
const currentTraceView = computed(() => traceStore.currentView);
|
||||
const { query } = useRoute();
|
||||
dashboardStore.setViewMode(query["fullview"] === "true");
|
||||
|
||||
@ -176,6 +247,12 @@ const key = computed(() => {
|
||||
return (type && type.key) || 0;
|
||||
});
|
||||
|
||||
function setSelectedSelector(selector: string) {
|
||||
selectedSelector.value = selector;
|
||||
}
|
||||
function closeSelector() {
|
||||
selectedSelector.value = "";
|
||||
}
|
||||
setCurrentDashboard();
|
||||
appStore.setEventStack([initSelector]);
|
||||
initSelector();
|
||||
@ -362,6 +439,7 @@ async function getServices() {
|
||||
}
|
||||
|
||||
async function changeService(service: any) {
|
||||
selectedSelector.value = "";
|
||||
if (service[0]) {
|
||||
states.currentService = service[0].value;
|
||||
selectorStore.setCurrentService(service[0]);
|
||||
@ -381,6 +459,7 @@ function changeDestService(service: any) {
|
||||
}
|
||||
|
||||
function changePods(pod: any) {
|
||||
selectedSelector.value = "";
|
||||
if (pod[0]) {
|
||||
selectorStore.setCurrentPod(pod[0]);
|
||||
} else {
|
||||
@ -636,6 +715,7 @@ watch(
|
||||
background: rgb(240, 242, 245);
|
||||
border-bottom: 1px solid #dfe4e8;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.switch {
|
||||
@ -682,4 +762,11 @@ watch(
|
||||
.selectorPod {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.tool-btn {
|
||||
height: 18px;
|
||||
}
|
||||
.el-input__wrapper {
|
||||
height: 18px !important;
|
||||
}
|
||||
</style>
|
||||
|
339
src/views/dashboard/panel/component/TraceDetailsTools.vue
Normal file
339
src/views/dashboard/panel/component/TraceDetailsTools.vue
Normal file
@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<div
|
||||
class="trace-detail-wrapper flex-h"
|
||||
v-if="traceStore.currentTrace.endpointNames"
|
||||
>
|
||||
<div class="mb-0 mt-0">
|
||||
<Icon
|
||||
icon="clear"
|
||||
v-if="traceStore.currentTrace.isError"
|
||||
class="red mr-5 sm"
|
||||
/>
|
||||
<div class="trace-log-btn">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="Back"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
class="mr-10 filter-btn"
|
||||
type="primary"
|
||||
@click="showTraceList"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="chevron-left" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('viewLogs')"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
class="mr-10 filter-btn"
|
||||
type="primary"
|
||||
@click="searchTraceLogs"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="folder_open" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="showTraceLogs"
|
||||
:destroy-on-close="true"
|
||||
fullscreen
|
||||
@closed="showTraceLogs = false"
|
||||
>
|
||||
<div>
|
||||
<el-pagination
|
||||
v-model:currentPage="pageNum"
|
||||
v-model:page-size="pageSize"
|
||||
:small="true"
|
||||
:total="traceStore.traceSpanLogsTotal"
|
||||
@current-change="turnLogsPage"
|
||||
/>
|
||||
<LogTable
|
||||
:tableData="traceStore.traceSpanLogs || []"
|
||||
:type="`service`"
|
||||
:noLink="true"
|
||||
>
|
||||
<div class="log-tips" v-if="!traceStore.traceSpanLogs.length">
|
||||
{{ t("noData") }}
|
||||
</div>
|
||||
</LogTable>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div class="mb- blue sm">
|
||||
<span class="vm">{{ traceStore.currentTrace.endpointNames[0] }}</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="
|
||||
traceStore.currentTrace.traceIds &&
|
||||
traceStore.currentTrace.traceIds[0] &&
|
||||
traceStore.currentTrace.traceIds[0].value
|
||||
"
|
||||
:options="traceStore.currentTrace.traceIds"
|
||||
@change="changeTraceId"
|
||||
class="trace-detail-ids"
|
||||
/>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="Copy Ids"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
class="mr-10 copy-btn"
|
||||
type="primary"
|
||||
@click="handleClick(traceStore.currentTrace.traceIds)"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="review-list" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="flex-h item">
|
||||
<div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('list')"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
class="filter-btn"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode === 'List' }"
|
||||
@click="changeDisplayMode('List')"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="list-bulleted" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('tree')"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
class="filter-btn"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode === 'Tree' }"
|
||||
@click="changeDisplayMode('Tree')"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="issue-child" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('table')"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
class="filter-btn"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode === 'Table' }"
|
||||
@click="changeDisplayMode('Table')"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="table" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('statistics')"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
class="filter-btn"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode === 'Statistics' }"
|
||||
@click="changeDisplayMode('Statistics')"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="statistics-bulleted" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-data" v-else>t("noData")</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import dayjs from "dayjs";
|
||||
import { ref, computed, defineComponent } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
import { Option } from "@/types/app";
|
||||
import copy from "@/utils/copy";
|
||||
import LogTable from "@/views/dashboard/related/components/LogTable/Index.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
export default defineComponent({
|
||||
name: "TraceDetailsTools",
|
||||
components: {
|
||||
LogTable,
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const { t } = useI18n();
|
||||
const traceStore = useTraceStore();
|
||||
const loading = ref<boolean>(false);
|
||||
const traceId = ref<string>("");
|
||||
const displayMode = computed(() => {
|
||||
return traceStore.displayMode;
|
||||
});
|
||||
const pageNum = ref<number>(1);
|
||||
const pageSize = 10;
|
||||
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
dayjs(date).format(pattern);
|
||||
const showTraceLogs = ref<boolean>(false);
|
||||
|
||||
function showTraceList() {
|
||||
traceStore.setCurrentView("traceList");
|
||||
}
|
||||
function handleClick(ids: string[] | any) {
|
||||
let copyValue = null;
|
||||
if (ids.length === 1) {
|
||||
copyValue = ids[0].value;
|
||||
} else {
|
||||
copyValue = ids.map((trace: any) => trace.value).join(",");
|
||||
}
|
||||
copy(copyValue);
|
||||
}
|
||||
function changeDisplayMode(mode: string) {
|
||||
traceStore.displayMode = mode;
|
||||
}
|
||||
async function changeTraceId(opt: Option[] | any) {
|
||||
traceId.value = opt[0].value;
|
||||
loading.value = true;
|
||||
const res = await traceStore.getTraceSpans({ traceId: opt[0].value });
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
async function searchTraceLogs() {
|
||||
showTraceLogs.value = true;
|
||||
const res = await traceStore.getSpanLogs({
|
||||
condition: {
|
||||
relatedTrace: {
|
||||
traceId: traceId.value || traceStore.currentTrace.traceIds[0].value,
|
||||
},
|
||||
paging: { pageNum: pageNum.value, pageSize, needTotal: true },
|
||||
},
|
||||
});
|
||||
if (res.errors) {
|
||||
ElMessage.error(res.errors);
|
||||
}
|
||||
}
|
||||
|
||||
function turnLogsPage(page: number) {
|
||||
pageNum.value = page;
|
||||
searchTraceLogs();
|
||||
}
|
||||
return {
|
||||
showTraceList,
|
||||
changeDisplayMode,
|
||||
traceStore,
|
||||
displayMode,
|
||||
dateFormat,
|
||||
changeTraceId,
|
||||
handleClick,
|
||||
t,
|
||||
searchTraceLogs,
|
||||
showTraceLogs,
|
||||
turnLogsPage,
|
||||
pageSize,
|
||||
pageNum,
|
||||
loading,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.trace-detail {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.trace-chart {
|
||||
height: calc(100% - 100px);
|
||||
overflow: auto;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.trace-chart.full-view {
|
||||
height: calc(100% - 1px) !important;
|
||||
}
|
||||
|
||||
.trace-detail-wrapper {
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.grey {
|
||||
color: #fff;
|
||||
background-color: #448dfe;
|
||||
}
|
||||
|
||||
.ghost {
|
||||
cursor: pointer;
|
||||
background: rgba(4, 147, 114, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.trace-detail-ids {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
outline: 0;
|
||||
border-style: unset;
|
||||
color: inherit;
|
||||
border: 1px solid;
|
||||
border-radius: 4px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.trace-log-btn {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
padding: 0px 7px;
|
||||
background-color: #40454e;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.no-data {
|
||||
padding-top: 50px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.vm {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.filter-btn {
|
||||
height: 18px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.copy-btn {
|
||||
height: 18px;
|
||||
width: 10px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
</style>
|
@ -13,114 +13,192 @@ 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="flex-h row">
|
||||
<div class="mr-5" v-if="dashboardStore.entity === EntityType[1].value">
|
||||
<span class="grey mr-5">{{ t("service") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.service.value"
|
||||
:options="logStore.services"
|
||||
placeholder="Select a service"
|
||||
@change="changeField('service', $event)"
|
||||
/>
|
||||
<div class="flex-h log-wrapper">
|
||||
<div v-if="!currentSearchTerm.length" class="flex-h items-center">
|
||||
<div v-for="(item, index) in arrayOfFilters" :key="index">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="item.description"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
type="success"
|
||||
:class="[activeTerms.includes(item.name) ? 'active-toggle' : '']"
|
||||
class="toggle-btn mx-3"
|
||||
v-show="item.isVisible"
|
||||
@click="setSearchTerm(item.name)"
|
||||
>
|
||||
<Icon iconSize="sm" :iconName="item.iconName" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mr-5" v-if="dashboardStore.entity !== EntityType[3].value">
|
||||
<span class="grey mr-5">
|
||||
{{ isBrowser ? t("version") : t("instance") }}:
|
||||
</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.instance.value"
|
||||
:options="logStore.instances"
|
||||
placeholder="Select a instance"
|
||||
@change="changeField('instance', $event)"
|
||||
/>
|
||||
<div class="flex-h items-center">
|
||||
<div class="flex-h items-center" v-if="currentSearchTerm === 'service'">
|
||||
<div
|
||||
class="mr-5 flex-h items-center"
|
||||
v-if="dashboardStore.entity === EntityType[1].value"
|
||||
>
|
||||
<span class="grey mr-5">{{ t("service") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.service.value"
|
||||
:options="logStore.services"
|
||||
placeholder="Select a service"
|
||||
@change="changeField('service', $event)"
|
||||
/>
|
||||
</div>
|
||||
<b v-else>{{ t("service") }} data not available</b>
|
||||
</div>
|
||||
|
||||
<div class="flex-h items-center" v-if="currentSearchTerm === 'instance'">
|
||||
<div
|
||||
class="mr-5 items-center flex-h"
|
||||
v-if="
|
||||
dashboardStore.entity !== EntityType[3].value &&
|
||||
currentSearchTerm === 'instance'
|
||||
"
|
||||
>
|
||||
<span class="grey mr-5">
|
||||
{{ isBrowser ? t("version") : t("instance") }}:
|
||||
</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.instance.value"
|
||||
:options="logStore.instances"
|
||||
placeholder="Select a instance"
|
||||
@change="changeField('instance', $event)"
|
||||
/>
|
||||
</div>
|
||||
<b v-else>{{ t("instance") }} data not available</b>
|
||||
</div>
|
||||
|
||||
<div class="flex-h items-center" v-if="currentSearchTerm === 'endpoints'">
|
||||
<div
|
||||
class="mr-5 flex-h items-center"
|
||||
v-if="
|
||||
dashboardStore.entity !== EntityType[2].value &&
|
||||
currentSearchTerm === 'endpoints'
|
||||
"
|
||||
>
|
||||
<span class="grey mr-5"
|
||||
>{{ isBrowser ? t("page") : t("endpoint") }}:</span
|
||||
>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.endpoint.value"
|
||||
:options="logStore.endpoints"
|
||||
placeholder="Select a endpoint"
|
||||
@change="changeField('endpoint', $event)"
|
||||
:isRemote="true"
|
||||
@query="searchEndpoints"
|
||||
/>
|
||||
</div>
|
||||
<b v-else>{{ t("endpoint") }} data not available</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mr-5" v-if="dashboardStore.entity !== EntityType[2].value">
|
||||
<span class="grey mr-5"
|
||||
>{{ isBrowser ? t("page") : t("endpoint") }}:</span
|
||||
<!-- <div class="row tips">
|
||||
<b>{{ t("conditionNotice") }}</b>
|
||||
</div> -->
|
||||
<div class="flex-h items-center">
|
||||
<div class="mr-5 flex-h items-center traceId" v-show="!isBrowser">
|
||||
<div class="flex-h items-center" v-if="currentSearchTerm === 'traceId'">
|
||||
<span class="grey mr-5">{{ t("traceID") }}:</span>
|
||||
<el-input v-model="traceId" class="inputs-max" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<keep-alive>
|
||||
<ConditionTags
|
||||
ref="logTagsComponent"
|
||||
v-if="currentSearchTerm === 'tags'"
|
||||
:type="'LOG'"
|
||||
@update="updateTags"
|
||||
/>
|
||||
</keep-alive>
|
||||
</div>
|
||||
|
||||
<div class="flex-h items-center" v-show="!isBrowser">
|
||||
<div
|
||||
class="mr-5 flex-h items-center"
|
||||
v-show="supportQueryLogsByKeywords && currentSearchTerm === 'keywords'"
|
||||
>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.endpoint.value"
|
||||
:options="logStore.endpoints"
|
||||
placeholder="Select a endpoint"
|
||||
@change="changeField('endpoint', $event)"
|
||||
:isRemote="true"
|
||||
@query="searchEndpoints"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row tips">
|
||||
<b>{{ t("conditionNotice") }}</b>
|
||||
</div>
|
||||
<div class="flex-h row">
|
||||
<div class="mr-5 traceId" v-show="!isBrowser">
|
||||
<span class="grey mr-5">{{ t("traceID") }}:</span>
|
||||
<el-input v-model="traceId" class="inputs-max" size="small" />
|
||||
</div>
|
||||
<ConditionTags :type="'LOG'" @update="updateTags" />
|
||||
</div>
|
||||
<div class="flex-h" v-show="!isBrowser">
|
||||
<div class="mr-5" v-show="logStore.supportQueryLogsByKeywords">
|
||||
<span class="mr-5 grey">{{ t("keywordsOfContent") }}:</span>
|
||||
<span class="log-tags">
|
||||
<span
|
||||
class="selected"
|
||||
v-for="(item, index) in keywordsOfContent"
|
||||
:key="`keywordsOfContent${index}`"
|
||||
>
|
||||
<span>{{ item }}</span>
|
||||
<span class="remove-icon" @click="removeContent(index)">×</span>
|
||||
</span>
|
||||
</span>
|
||||
<el-input
|
||||
size="small"
|
||||
class="inputs-max"
|
||||
:placeholder="t('addKeywordsOfContent')"
|
||||
v-model="contentStr"
|
||||
@change="addLabels('keywordsOfContent')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mr-5" v-show="logStore.supportQueryLogsByKeywords">
|
||||
<span class="grey mr-5"> {{ t("excludingKeywordsOfContent") }}: </span>
|
||||
<span class="log-tags">
|
||||
<span
|
||||
class="selected"
|
||||
v-for="(item, index) in excludingKeywordsOfContent"
|
||||
:key="`excludingKeywordsOfContent${index}`"
|
||||
>
|
||||
<span>{{ item }}</span>
|
||||
<span class="remove-icon" @click="removeExcludeContent(index)">
|
||||
×
|
||||
<span class="mr-5 grey">{{ t("keywordsOfContent") }}:</span>
|
||||
<span class="log-tags">
|
||||
<span
|
||||
class="selected"
|
||||
v-for="(item, index) in keywordsOfContent"
|
||||
:key="`keywordsOfContent${index}`"
|
||||
>
|
||||
<span>{{ item }}</span>
|
||||
<span class="remove-icon" @click="removeContent(index)">×</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<el-input
|
||||
class="inputs-max"
|
||||
size="small"
|
||||
:placeholder="t('addExcludingKeywordsOfContent')"
|
||||
v-model="excludingContentStr"
|
||||
@change="addLabels('excludingKeywordsOfContent')"
|
||||
/>
|
||||
<el-tooltip :content="t('keywordsOfContentLogTips')">
|
||||
<span class="log-tips" v-show="!logStore.supportQueryLogsByKeywords">
|
||||
<Icon icon="help" class="mr-5" />
|
||||
<el-input
|
||||
size="small"
|
||||
class="inputs-max"
|
||||
:placeholder="t('addKeywordsOfContent')"
|
||||
v-model="contentStr"
|
||||
@change="addLabels('keywordsOfContent')"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mr-5 flex-h items-center"
|
||||
v-show="
|
||||
supportExcludeQueryLogsByKeywords && currentSearchTerm === 'exclude'
|
||||
"
|
||||
>
|
||||
<span class="grey mr-5"> {{ t("excludingKeywordsOfContent") }}: </span>
|
||||
<span class="log-tags">
|
||||
<span
|
||||
class="selected"
|
||||
v-for="(item, index) in excludingKeywordsOfContent"
|
||||
:key="`excludingKeywordsOfContent${index}`"
|
||||
>
|
||||
<span>{{ item }}</span>
|
||||
<span class="remove-icon" @click="removeExcludeContent(index)">
|
||||
×
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<el-input
|
||||
class="inputs-max"
|
||||
size="small"
|
||||
:placeholder="t('addExcludingKeywordsOfContent')"
|
||||
v-model="excludingContentStr"
|
||||
@change="addLabels('excludingKeywordsOfContent')"
|
||||
/>
|
||||
<el-tooltip :content="t('keywordsOfContentLogTips')">
|
||||
<span class="log-tips" v-show="!logStore.supportQueryLogsByKeywords">
|
||||
<Icon icon="help" class="mr-5" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<!-- Search&cancel buttons -->
|
||||
<div v-if="currentSearchTerm.length" class="flex-h items-center">
|
||||
<el-button
|
||||
class="search-btn toggle-btn"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="searchLogs"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="search" />
|
||||
</el-button>
|
||||
<el-button
|
||||
class="search-btn toggle-btn"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="cancelSearchTerm"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="cancel" />
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-button
|
||||
class="search-btn"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="searchLogs"
|
||||
>
|
||||
{{ t("search") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ref, reactive, watch, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Option } from "@/types/app";
|
||||
import { useLogStore } from "@/store/modules/log";
|
||||
@ -139,6 +217,15 @@ const logStore = useLogStore();
|
||||
const traceId = ref<string>("");
|
||||
const keywordsOfContent = ref<string[]>([]);
|
||||
const excludingKeywordsOfContent = ref<string[]>([]);
|
||||
const supportQueryLogsByKeywords = computed<boolean>(() => {
|
||||
return logStore.supportQueryLogsByKeywords
|
||||
})
|
||||
const supportExcludeQueryLogsByKeywords = computed<boolean>(() => {
|
||||
return logStore.supportQueryLogsByKeywords
|
||||
})
|
||||
|
||||
const currentSearchTerm = ref<string>("");
|
||||
const activeTerms = ref<string[]>([]);
|
||||
const tagsList = ref<string[]>([]);
|
||||
const tagsMap = ref<Option[]>([]);
|
||||
const contentStr = ref<string>("");
|
||||
@ -149,7 +236,57 @@ const state = reactive<any>({
|
||||
endpoint: { value: "0", label: "All" },
|
||||
service: { value: "", label: "" },
|
||||
});
|
||||
|
||||
const logTagsComponent = ref<InstanceType<typeof ConditionTags> | null>(null);
|
||||
interface filtersObject {
|
||||
name: string;
|
||||
iconName: string;
|
||||
description: string;
|
||||
isVisible?: boolean | unknown; // one of the situations is dependent on an api call
|
||||
}
|
||||
const arrayOfFilters = ref<filtersObject[]>([
|
||||
{
|
||||
name: "traceId",
|
||||
iconName: "timeline",
|
||||
description: "Trace ID",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: "tags",
|
||||
iconName: "epic",
|
||||
description: "Tags",
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: "keywords",
|
||||
iconName: "library_books",
|
||||
description: "Keywords",
|
||||
isVisible: supportQueryLogsByKeywords,
|
||||
},
|
||||
{
|
||||
name: "exclude",
|
||||
iconName: "issue-child",
|
||||
description: "Exclude keywords",
|
||||
isVisible: supportExcludeQueryLogsByKeywords,
|
||||
},
|
||||
{
|
||||
name: "instance",
|
||||
iconName: "epic",
|
||||
description: "Instance",
|
||||
isVisible: dashboardStore.entity !== EntityType[3].value,
|
||||
},
|
||||
{
|
||||
name: "service",
|
||||
iconName: "settings",
|
||||
description: "Service",
|
||||
isVisible: dashboardStore.entity === EntityType[1].value,
|
||||
},
|
||||
{
|
||||
name: "endpoints",
|
||||
iconName: "timeline",
|
||||
description: "Endpoints",
|
||||
isVisible: dashboardStore.entity !== EntityType[2].value,
|
||||
},
|
||||
]);
|
||||
init();
|
||||
async function init() {
|
||||
const resp = await logStore.getLogsByKeywords();
|
||||
@ -210,7 +347,46 @@ async function getInstances(id?: string) {
|
||||
}
|
||||
state.instance = logStore.instances[0];
|
||||
}
|
||||
function addToActiveTerms() {
|
||||
activeTerms.value.push(currentSearchTerm.value);
|
||||
}
|
||||
function removeFromActiveTerms() {
|
||||
activeTerms.value = activeTerms.value.filter(
|
||||
(term) => term !== currentSearchTerm.value
|
||||
);
|
||||
}
|
||||
function handleActiveSearchTerms() {
|
||||
switch (currentSearchTerm.value) {
|
||||
case "traceId":
|
||||
if (!traceId.value.length) return;
|
||||
addToActiveTerms();
|
||||
break;
|
||||
case "tags":
|
||||
if (!tagsList.value.length) return;
|
||||
addToActiveTerms();
|
||||
break;
|
||||
case "keywords":
|
||||
if (!keywordsOfContent.value.length) return;
|
||||
addToActiveTerms();
|
||||
break;
|
||||
case "exclude":
|
||||
if (!excludingKeywordsOfContent.value.length) return;
|
||||
addToActiveTerms();
|
||||
break;
|
||||
case "instance":
|
||||
addToActiveTerms();
|
||||
break;
|
||||
case "service":
|
||||
addToActiveTerms();
|
||||
break;
|
||||
case "endpoints":
|
||||
addToActiveTerms();
|
||||
break;
|
||||
}
|
||||
}
|
||||
function searchLogs() {
|
||||
handleActiveSearchTerms();
|
||||
currentSearchTerm.value = "";
|
||||
let endpoint = "",
|
||||
instance = "";
|
||||
if (dashboardStore.entity === EntityType[2].value) {
|
||||
@ -293,6 +469,40 @@ function removeExcludeContent(index: number) {
|
||||
});
|
||||
excludingContentStr.value = "";
|
||||
}
|
||||
function setSearchTerm(term: string) {
|
||||
currentSearchTerm.value = term;
|
||||
}
|
||||
function cancelSearchTerm() {
|
||||
switch (currentSearchTerm.value) {
|
||||
case "traceId":
|
||||
traceId.value = "";
|
||||
break;
|
||||
case "tags":
|
||||
tagsList.value = [];
|
||||
tagsMap.value = [];
|
||||
logTagsComponent.value?.emptyTags();
|
||||
break;
|
||||
case "keywords":
|
||||
keywordsOfContent.value = [];
|
||||
break;
|
||||
case "exclude":
|
||||
excludingKeywordsOfContent.value = [];
|
||||
break;
|
||||
case "instance":
|
||||
state.instance = { value: "0", label: "All" };
|
||||
break;
|
||||
case "endpoints":
|
||||
state.endpoint = { value: "0", label: "All" };
|
||||
getEndpoints();
|
||||
break;
|
||||
case "service":
|
||||
state.service = { value: "", label: "" };
|
||||
break;
|
||||
}
|
||||
removeFromActiveTerms();
|
||||
currentSearchTerm.value = "";
|
||||
searchLogs()
|
||||
}
|
||||
watch(
|
||||
() => selectorStore.currentService,
|
||||
() => {
|
||||
@ -320,6 +530,12 @@ watch(
|
||||
);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// .log-wrapper {
|
||||
// width: 600px;
|
||||
// padding-left: 40px;
|
||||
// overflow-x: scroll;
|
||||
// align-items: center;
|
||||
// }
|
||||
.inputs {
|
||||
width: 120px;
|
||||
}
|
||||
@ -378,4 +594,22 @@ watch(
|
||||
margin-left: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* buttons*/
|
||||
.el-button span {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
.toggle-btn {
|
||||
height: 18px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.active-toggle.toggle-btn {
|
||||
background: rgba(4, 147, 114, 1) !important;
|
||||
span {
|
||||
color: #275410 !important;
|
||||
}
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
@ -12,130 +12,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License. -->
|
||||
<template>
|
||||
<div class="trace-detail" v-loading="loading">
|
||||
<div
|
||||
class="trace-detail-wrapper clear"
|
||||
v-if="traceStore.currentTrace.endpointNames"
|
||||
>
|
||||
<h5 class="mb-5 mt-0">
|
||||
<Icon
|
||||
icon="clear"
|
||||
v-if="traceStore.currentTrace.isError"
|
||||
class="red mr-5 sm"
|
||||
/>
|
||||
<span class="vm">{{ traceStore.currentTrace.endpointNames[0] }}</span>
|
||||
<div class="trace-log-btn">
|
||||
<el-button
|
||||
size="small"
|
||||
class="mr-10"
|
||||
type="primary"
|
||||
@click="searchTraceLogs"
|
||||
>
|
||||
{{ t("viewLogs") }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="showTraceLogs"
|
||||
:destroy-on-close="true"
|
||||
fullscreen
|
||||
@closed="showTraceLogs = false"
|
||||
>
|
||||
<div>
|
||||
<el-pagination
|
||||
v-model:currentPage="pageNum"
|
||||
v-model:page-size="pageSize"
|
||||
:small="true"
|
||||
:total="traceStore.traceSpanLogsTotal"
|
||||
@current-change="turnLogsPage"
|
||||
/>
|
||||
<LogTable
|
||||
:tableData="traceStore.traceSpanLogs || []"
|
||||
:type="`service`"
|
||||
:noLink="true"
|
||||
>
|
||||
<div class="log-tips" v-if="!traceStore.traceSpanLogs.length">
|
||||
{{ t("noData") }}
|
||||
</div>
|
||||
</LogTable>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</h5>
|
||||
<div class="mb-5 blue sm">
|
||||
<Selector
|
||||
size="small"
|
||||
:value="
|
||||
traceStore.currentTrace.traceIds &&
|
||||
traceStore.currentTrace.traceIds[0] &&
|
||||
traceStore.currentTrace.traceIds[0].value
|
||||
"
|
||||
:options="traceStore.currentTrace.traceIds"
|
||||
@change="changeTraceId"
|
||||
class="trace-detail-ids"
|
||||
/>
|
||||
<Icon
|
||||
size="sm"
|
||||
class="icon grey link-hover cp ml-5"
|
||||
iconName="review-list"
|
||||
@click="handleClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-h item">
|
||||
<div>
|
||||
<div class="tag mr-5">{{ t("start") }}</div>
|
||||
<span class="mr-15 sm">
|
||||
{{ dateFormat(parseInt(traceStore.currentTrace.start)) }}
|
||||
</span>
|
||||
<div class="tag mr-5">{{ t("duration") }}</div>
|
||||
<span class="mr-15 sm"
|
||||
>{{ traceStore.currentTrace.duration }} ms</span
|
||||
>
|
||||
<div class="tag mr-5">{{ t("spans") }}</div>
|
||||
<span class="sm">{{ traceStore.traceSpans.length }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<el-button
|
||||
class="grey"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode !== 'List' }"
|
||||
@click="displayMode = 'List'"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="list-bulleted" />
|
||||
{{ t("list") }}
|
||||
</el-button>
|
||||
<el-button
|
||||
class="grey"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode !== 'Tree' }"
|
||||
@click="displayMode = 'Tree'"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="issue-child" />
|
||||
{{ t("tree") }}
|
||||
</el-button>
|
||||
<el-button
|
||||
class="grey"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode !== 'Table' }"
|
||||
@click="displayMode = 'Table'"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="table" />
|
||||
{{ t("table") }}
|
||||
</el-button>
|
||||
<el-button
|
||||
class="grey"
|
||||
size="small"
|
||||
:class="{ ghost: displayMode !== 'Statistics' }"
|
||||
@click="displayMode = 'Statistics'"
|
||||
>
|
||||
<Icon class="mr-5" size="sm" iconName="statistics-bulleted" />
|
||||
{{ t("statistics") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="no-data" v-else>{{ t("noData") }}</div>
|
||||
<div class="trace-chart">
|
||||
<div :class="{ 'full-view': isFullView }" class="trace-chart">
|
||||
<component
|
||||
v-if="traceStore.currentTrace.endpointNames"
|
||||
:is="displayMode"
|
||||
:is="traceStore.displayMode"
|
||||
:data="traceStore.traceSpans"
|
||||
:traceId="traceStore.currentTrace.traceIds[0].value"
|
||||
:showBtnDetail="false"
|
||||
@ -146,7 +26,8 @@ limitations under the License. -->
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import dayjs from "dayjs";
|
||||
import { ref, defineComponent } from "vue";
|
||||
import { ref, computed, defineComponent } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useTraceStore } from "@/store/modules/trace";
|
||||
import { Option } from "@/types/app";
|
||||
@ -155,7 +36,7 @@ import List from "./components/List.vue";
|
||||
import graphs from "./components/index";
|
||||
import LogTable from "@/views/dashboard/related/components/LogTable/Index.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
// import TraceDetailsTools from '@/views/dashboard'
|
||||
export default defineComponent({
|
||||
name: "TraceDetail",
|
||||
components: {
|
||||
@ -163,18 +44,27 @@ export default defineComponent({
|
||||
List,
|
||||
LogTable,
|
||||
},
|
||||
setup() {
|
||||
setup(props, ctx) {
|
||||
const { t } = useI18n();
|
||||
const traceStore = useTraceStore();
|
||||
const loading = ref<boolean>(false);
|
||||
const traceId = ref<string>("");
|
||||
const displayMode = ref<string>("List");
|
||||
const queries = useRoute().query;
|
||||
const isFullView = computed(() => {
|
||||
return queries?.fullview === "true" && queries?.portal === "true";
|
||||
});
|
||||
const displayMode = computed(() => {
|
||||
return traceStore.displayMode;
|
||||
});
|
||||
const pageNum = ref<number>(1);
|
||||
const pageSize = 10;
|
||||
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
|
||||
dayjs(date).format(pattern);
|
||||
const showTraceLogs = ref<boolean>(false);
|
||||
|
||||
function showTraceList() {
|
||||
ctx.emit("show:list");
|
||||
}
|
||||
function handleClick(ids: string[] | any) {
|
||||
let copyValue = null;
|
||||
if (ids.length === 1) {
|
||||
@ -215,6 +105,8 @@ export default defineComponent({
|
||||
searchTraceLogs();
|
||||
}
|
||||
return {
|
||||
isFullView,
|
||||
showTraceList,
|
||||
traceStore,
|
||||
displayMode,
|
||||
dateFormat,
|
||||
@ -233,17 +125,21 @@ export default defineComponent({
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.trace-detail {
|
||||
// min-height: 300px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.trace-chart {
|
||||
height: calc(100% - 100px);
|
||||
height: 100%;
|
||||
// height: calc(100% - 100px);
|
||||
overflow: auto;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.trace-chart.full-view {
|
||||
height: calc(100% - 1px) !important;
|
||||
}
|
||||
.trace-detail-wrapper {
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
|
@ -13,83 +13,122 @@ 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="flex-h row">
|
||||
<div class="mr-5" v-if="dashboardStore.entity === EntityType[1].value">
|
||||
<span class="grey mr-5">{{ t("service") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.service.value"
|
||||
:options="traceStore.services"
|
||||
placeholder="Select a service"
|
||||
@change="changeField('service', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="mr-5" v-if="dashboardStore.entity !== EntityType[3].value">
|
||||
<span class="grey mr-5">{{ t("instance") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.instance.value"
|
||||
:options="traceStore.instances"
|
||||
placeholder="Select a instance"
|
||||
@change="changeField('instance', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="mr-5" v-if="dashboardStore.entity !== EntityType[2].value">
|
||||
<span class="grey mr-5">{{ t("endpoint") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.endpoint.value"
|
||||
:options="traceStore.endpoints"
|
||||
placeholder="Select a endpoint"
|
||||
:isRemote="true"
|
||||
@change="changeField('endpoint', $event)"
|
||||
@query="searchEndpoints"
|
||||
/>
|
||||
</div>
|
||||
<div class="mr-5">
|
||||
<span class="grey mr-5">{{ t("status") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.status.value"
|
||||
:options="Status"
|
||||
placeholder="Select a status"
|
||||
@change="changeField('status', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="mr-5">
|
||||
<span class="grey mr-5">{{ t("traceID") }}:</span>
|
||||
<el-input size="small" v-model="traceId" class="traceId" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-h">
|
||||
<!-- <div class="mr-5">
|
||||
<span class="grey mr-5">{{ t("timeRange") }}:</span>
|
||||
<TimePicker
|
||||
:value="dateTime"
|
||||
position="bottom"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
@input="changeTimeRange"
|
||||
/>
|
||||
</div> -->
|
||||
<div class="mr-5">
|
||||
<span class="sm b grey mr-5">{{ t("duration") }}:</span>
|
||||
<el-input size="small" class="inputs mr-5" v-model="minTraceDuration" />
|
||||
<span class="grey mr-5">-</span>
|
||||
<el-input size="small" class="inputs" v-model="maxTraceDuration" />
|
||||
<div class="flex-h filter-container">
|
||||
<div v-for="(filter, index) in arrayOfFilters" :key="index">
|
||||
<el-tooltip
|
||||
v-if="!activeFilter.length || activeFilter === filter.name"
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="filter.description"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<el-button
|
||||
type="success"
|
||||
:class="[listOfActiveFilters.includes(filter.name) ? 'active-filter' : '']"
|
||||
class="filter-btn mx-3"
|
||||
@click="setFilter(filter.name)"
|
||||
>
|
||||
<Icon size="sm" :iconName="filter.iconName" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrap-filters">
|
||||
<div class="filter" v-if="activeFilter === 'service'">
|
||||
<span class="grey mr-5">{{ t("service") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.service.value"
|
||||
:options="traceStore.services"
|
||||
placeholder="Select a service"
|
||||
@change="changeField('service', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="filter"
|
||||
v-if="
|
||||
activeFilter === 'instance' && dashboardStore.entity !== EntityType[3].value
|
||||
"
|
||||
>
|
||||
<span class="grey mr-5">{{ t("instance") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.instance.value"
|
||||
:options="traceStore.instances"
|
||||
placeholder="Select a instance"
|
||||
@change="changeField('instance', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="filter"
|
||||
v-if="
|
||||
dashboardStore.entity !== EntityType[2].value && activeFilter === 'endpoints'
|
||||
"
|
||||
>
|
||||
<span class="grey mr-5">{{ t("endpoint") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.endpoint.value"
|
||||
:options="traceStore.endpoints"
|
||||
placeholder="Select a endpoint"
|
||||
:isRemote="true"
|
||||
@change="changeField('endpoint', $event)"
|
||||
@query="searchEndpoints"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="activeFilter === 'status'" class="filter">
|
||||
<span class="grey mr-5">{{ t("status") }}:</span>
|
||||
<Selector
|
||||
size="small"
|
||||
:value="state.status.value"
|
||||
:options="Status"
|
||||
placeholder="Select a status"
|
||||
@change="changeField('status', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="activeFilter === 'traceId'" class="filter">
|
||||
<span class="grey mr-5">{{ t("traceID") }}:</span>
|
||||
<el-input size="small" v-model="traceId" class="traceId" />
|
||||
</div>
|
||||
|
||||
<div v-if="activeFilter === 'duration'" class="filter">
|
||||
<span class="sm b grey mr-5">{{ t("duration") }}:</span>
|
||||
<el-input size="small" class="inputs mr-5" v-model="minTraceDuration" />
|
||||
<span class="grey mr-5">-</span>
|
||||
<el-input size="small" class="inputs" v-model="maxTraceDuration" />
|
||||
</div>
|
||||
<keep-alive>
|
||||
<ConditionTags
|
||||
v-if="activeFilter === 'tags'"
|
||||
ref="traceTagsComponent"
|
||||
:type="'TRACE'"
|
||||
@update="updateTags"
|
||||
/>
|
||||
</keep-alive>
|
||||
<el-button
|
||||
v-if="activeFilter"
|
||||
class="search-btn filter-btn"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="searchTraces"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="search" />
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="activeFilter"
|
||||
class="search-btn filter-btn"
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="cancelSearch"
|
||||
>
|
||||
<Icon iconSize="sm" iconName="cancel" />
|
||||
</el-button>
|
||||
</div>
|
||||
<ConditionTags :type="'TRACE'" @update="updateTags" />
|
||||
<el-button
|
||||
class="search-btn"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="searchTraces"
|
||||
>
|
||||
{{ t("search") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ref, reactive, watch, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Option } from "@/types/app";
|
||||
import { Status } from "../../data";
|
||||
@ -101,11 +140,60 @@ import ConditionTags from "@/views/components/ConditionTags.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { EntityType } from "../../data";
|
||||
|
||||
interface filtersObject {
|
||||
name: string;
|
||||
iconName: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStoreWithOut();
|
||||
const selectorStore = useSelectorStore();
|
||||
const dashboardStore = useDashboardStore();
|
||||
const traceStore = useTraceStore();
|
||||
const listOfActiveFilters = ref<string[]>([]);
|
||||
const arrayOfFilters = ref<filtersObject[]>([
|
||||
{
|
||||
name: "service",
|
||||
iconName: "cloud_queue",
|
||||
description: "Service",
|
||||
},
|
||||
{
|
||||
name: "instance",
|
||||
iconName: "storage",
|
||||
description: "Instance",
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
iconName: "device_hub",
|
||||
description: "Status",
|
||||
},
|
||||
{
|
||||
name: "duration",
|
||||
iconName: "av_timer",
|
||||
description: "Duration",
|
||||
},
|
||||
{
|
||||
name: "traceId",
|
||||
iconName: "timeline",
|
||||
description: "Trace ID",
|
||||
},
|
||||
{
|
||||
name: "tags",
|
||||
iconName: "epic",
|
||||
description: "Tags",
|
||||
},
|
||||
{
|
||||
name: "endpoints",
|
||||
iconName: "device_hub",
|
||||
description: "Endpoints",
|
||||
},
|
||||
]);
|
||||
const activeFilter = ref<string>("");
|
||||
function setFilter(filter: string) {
|
||||
activeFilter.value = filter;
|
||||
}
|
||||
|
||||
const traceId = ref<string>("");
|
||||
const minTraceDuration = ref<string>("");
|
||||
const maxTraceDuration = ref<string>("");
|
||||
@ -118,6 +206,8 @@ const state = reactive<any>({
|
||||
service: { value: "", label: "" },
|
||||
});
|
||||
|
||||
const traceTagsComponent = ref<InstanceType<typeof ConditionTags> | null>(null);
|
||||
|
||||
// const dateTime = computed(() => [
|
||||
// appStore.durationRow.start,
|
||||
// appStore.durationRow.end,
|
||||
@ -167,7 +257,92 @@ async function getInstances(id?: string) {
|
||||
}
|
||||
state.instance = traceStore.instances[0];
|
||||
}
|
||||
function addToActiveFilterList() {
|
||||
listOfActiveFilters.value.push(activeFilter.value);
|
||||
}
|
||||
function removeFromActiveFilters() {
|
||||
listOfActiveFilters.value = listOfActiveFilters.value.filter(
|
||||
(filter) => filter !== activeFilter.value
|
||||
);
|
||||
}
|
||||
function cancelSearch() {
|
||||
switch (activeFilter.value) {
|
||||
case "status":
|
||||
state.status = { label: "All", value: "ALL" };
|
||||
break;
|
||||
case "instance":
|
||||
state.instance = { value: "0", label: "All" };
|
||||
break;
|
||||
case "endpoints":
|
||||
state.endpoint = { value: "0", label: "All" };
|
||||
break;
|
||||
case "service":
|
||||
state.service = { value: "", label: "" };
|
||||
break;
|
||||
case "duration":
|
||||
minTraceDuration.value = "";
|
||||
maxTraceDuration.value = "";
|
||||
break;
|
||||
case "tags":
|
||||
tagsList.value = [];
|
||||
tagsMap.value = [];
|
||||
updateTags({ tagsMap: [], tagsList: [] });
|
||||
traceTagsComponent.value?.emptyTags();
|
||||
break;
|
||||
case "traceId":
|
||||
traceId.value = "";
|
||||
break;
|
||||
}
|
||||
removeFromActiveFilters();
|
||||
activeFilter.value = "";
|
||||
traceStore.activeFilter = "";
|
||||
searchTraces();
|
||||
}
|
||||
function handleActiveFilterState() {
|
||||
switch (activeFilter.value) {
|
||||
case "traceId":
|
||||
if (!traceId.value.length) return;
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
case "tags":
|
||||
if (!tagsList.value.length) return;
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
case "duration":
|
||||
if (!minTraceDuration.value.length || !maxTraceDuration.value.length) return;
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
case "service":
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
case "instance":
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
case "status":
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
case "endpoints":
|
||||
traceStore.setActiveFilter(activeFilter.value);
|
||||
addToActiveFilterList();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
function searchTraces() {
|
||||
handleActiveFilterState();
|
||||
activeFilter.value = "";
|
||||
let endpoint = "",
|
||||
instance = "";
|
||||
if (dashboardStore.entity === EntityType[2].value) {
|
||||
@ -185,8 +360,8 @@ function searchTraces() {
|
||||
serviceInstanceId: instance || state.instance.id || undefined,
|
||||
traceState: state.status.value || "ALL",
|
||||
queryDuration: appStore.durationTime,
|
||||
minTraceDuration: appStore.minTraceDuration || undefined,
|
||||
maxTraceDuration: appStore.maxTraceDuration || undefined,
|
||||
minTraceDuration: minTraceDuration.value || undefined,
|
||||
maxTraceDuration: maxTraceDuration.value || undefined,
|
||||
queryOrder: "BY_DURATION",
|
||||
tags: tagsMap.value.length ? tagsMap.value : undefined,
|
||||
paging: { pageNum: 1, pageSize: 15, needTotal: true },
|
||||
@ -260,4 +435,28 @@ watch(
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.filter-container {
|
||||
align-items: center;
|
||||
}
|
||||
.wrap-filters {
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
.filter {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.filter-btn {
|
||||
height: 18px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.active-filter.filter-btn {
|
||||
background: rgba(4, 147, 114, 1) !important;
|
||||
span {
|
||||
color: #275410 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -110,6 +110,7 @@ function changeSort(opt: Option[] | any) {
|
||||
}
|
||||
|
||||
async function selectTrace(i: Trace) {
|
||||
traceStore.setCurrentView("traceDetails");
|
||||
traceStore.setCurrentTrace(i);
|
||||
selectedKey.value = i.key;
|
||||
if (i.traceIds.length) {
|
||||
@ -136,6 +137,7 @@ async function queryTraces() {
|
||||
border-bottom: 1px solid #c1c5ca41;
|
||||
border-right: 1px solid #c1c5ca41;
|
||||
height: 35px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.selectors {
|
||||
@ -163,11 +165,11 @@ async function queryTraces() {
|
||||
}
|
||||
|
||||
.trace-t {
|
||||
width: 420px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list {
|
||||
width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.trace-tr {
|
||||
|
@ -26,7 +26,7 @@ module.exports = {
|
||||
proxy: {
|
||||
"/graphql": {
|
||||
target: `${
|
||||
process.env.SW_PROXY_TARGET || "http://demo.skywalking.apache.org"
|
||||
process.env.SW_PROXY_TARGET || "https://demo.sourceplus.plus:12800"
|
||||
}`,
|
||||
changeOrigin: true,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user