This is an automated email from the ASF dual-hosted git repository. qiuxiafan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/skywalking-rocketbot-ui.git
The following commit(s) were added to refs/heads/master by this push: new 774b69d Support clicking the service name in the chart to link to the tracking page. (#530) 774b69d is described below commit 774b69dd84e305be975e4c5ffc0d433aa8cbda32 Author: CharliePu <heihaozi2...@163.com> AuthorDate: Thu Aug 26 21:12:51 2021 +0800 Support clicking the service name in the chart to link to the tracking page. (#530) * Support service metrics link to trace query * Support service metrics link to trace query * Modify the hard code. * Support service metrics link to log or trace * Clean up useless code and modify styles * Clean up useless code and modify styles * Clean up useless code * Modify style and copywriting * fix undefined bug * fix undefined bug Co-authored-by: 吴晟 Wu Sheng <wu.sh...@foxmail.com> --- src/assets/lang/en.ts | 2 + src/assets/lang/zh.ts | 2 + src/components/index.ts | 2 + src/components/rk-modal.vue | 164 +++++++++++++++++++++ .../components/dashboard/charts/chart-slow.vue | 70 ++++++++- src/views/components/dashboard/charts/constant.ts | 8 + src/views/components/log/log-bar.vue | 9 ++ src/views/components/trace/trace-search.vue | 27 +++- 8 files changed, 276 insertions(+), 8 deletions(-) diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts index 8529304..0e0ced9 100644 --- a/src/assets/lang/en.ts +++ b/src/assets/lang/en.ts @@ -230,6 +230,8 @@ const m = { destService: 'Destination Service', destServiceInstance: 'Destination Service Instance', eventSource: 'Event Source', + modalTitle: 'Inspection', + selectRedirectPage: 'Do you want to inspect Traces or Logs of %s service?', }; export default m; diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts index 84ec613..5e13722 100644 --- a/src/assets/lang/zh.ts +++ b/src/assets/lang/zh.ts @@ -228,6 +228,8 @@ const m = { destService: '终点服务', destServiceInstance: '终点实例', eventSource: '事件资源', + modalTitle: '查看', + selectRedirectPage: '查看 %s 服务的追踪或日志?', }; export default m; diff --git a/src/components/index.ts b/src/components/index.ts index 67d83ec..a5f02e8 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -24,6 +24,7 @@ import RkPage from './rk-page.vue'; import RkDate from './rk-date.vue'; import RkPanel from './rk-panel.vue'; import RkSidebox from './rk-sidebox.vue'; +import RkModal from './rk-modal.vue'; import RkEcharts from './rk-echarts.vue'; import RkSelect from './rk-select.vue'; import RkPopper from './rk-popper.vue'; @@ -42,6 +43,7 @@ const components: any = { RkEcharts, RkPage, RkSidebox, + RkModal, RkFooterTime, RkSelect, RkPopper, diff --git a/src/components/rk-modal.vue b/src/components/rk-modal.vue new file mode 100644 index 0000000..93357f2 --- /dev/null +++ b/src/components/rk-modal.vue @@ -0,0 +1,164 @@ +<!-- 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="rk-modal-bg" v-show="show" @mousemove="modalMove" @mouseup="cancelMove"> + <div class="rk-modal-container"> + <div class="rk-modal-header" @mousedown="setStartingPoint"> + <span class="title">{{ this.title }}</span> + <div class="r rk-modal-close" @click="cancel"> + <svg class="icon"> + <use xlink:href="#close"></use> + </svg> + </div> + </div> + <div class="rk-modal-main"> + <slot></slot> + </div> + <div v-if="showButton" class="rk-modal-footer"> + <rk-button class="cancel" @click="cancel">{{ $t('cancel') }}</rk-button> + <rk-button @click="confirm">{{ $t('confirm') }}</rk-button> + </div> + </div> + </div> +</template> +<script lang="js"> + import RkButton from '@/components/rk-button'; + export default { + name: 'RkModal', + components: {RkButton}, + props: { + show: { + type: Boolean, + default: false, + }, + showButton: { + type: Boolean, + default: false, + }, + title: { + type: String, + default: '', + }, + }, + data() { + return { + x: 0, + y: 0, + node: null, + isCanMove: false, + }; + }, + mounted() { + this.node = document.querySelector('.rk-modal-container'); + }, + methods: { + cancel() { + this.$emit('update:show', false); + this.$emit('cancelModalCallback'); + }, + + confirm() { + this.$emit('update:show', false); + this.$emit('confirmModalCallback'); + }, + + setStartingPoint(e) { + this.x = e.clientX - this.node.offsetLeft; + this.y = e.clientY - this.node.offsetTop; + this.isCanMove = true; + }, + + modalMove(e) { + if (this.isCanMove) { + this.node.style.left = e.clientX - this.x + 'px'; + this.node.style.top = e.clientY - this.y + 'px'; + } + }, + + cancelMove() { + this.isCanMove = false; + }, + }, + }; +</script> + +<style lang="scss"> + .rk-modal-bg { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, .5); + z-index: 10; + } + + .rk-modal-container { + background: #fff; + border-radius: 10px; + overflow: hidden; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + .rk-modal-header { + height: 35px; + background: #333840; + color: #efefef; + display: flex; + align-items: center; + justify-content: center; + cursor: move; + } + + .rk-modal-close { + position: absolute; + right: 10px; + top: 5px; + cursor: pointer; + color: #d8d8d8; + transition: color 0.3s; + + .icon { + width: 18px; + height: 20px; + } + + &:hover { + color: #3d92ff; + } + } + + .rk-modal-main { + padding: 15px 40px; + } + + .rk-modal-footer { + display: flex; + align-items: center; + justify-content: right; + height: 35px; + border-top: 1px solid #ddd; + padding-right: 10px; + } + .rk-modal-footer .cancel{ + margin-right: 10px; + border: 1px solid #ddd; + color: #333; + background-color: #fff; + } +</style> diff --git a/src/views/components/dashboard/charts/chart-slow.vue b/src/views/components/dashboard/charts/chart-slow.vue index 9f905e5..23bf084 100644 --- a/src/views/components/dashboard/charts/chart-slow.vue +++ b/src/views/components/dashboard/charts/chart-slow.vue @@ -22,11 +22,20 @@ limitations under the License. --> </svg> <div class="mb-5 ell" v-tooltip:top.ellipsis="i.name || ''"> <span class="calls sm mr-10">{{ i.value }}</span> - <span class="cp link-hover">{{ i.name + getTraceId(i) }}</span> + <span class="cp link-hover" @click="handleLink(i)">{{ i.name + getTraceId(i) }}</span> </div> <RkProgress :precent="(i.value / maxValue) * 100" color="#bf99f8" /> </div> </div> + <rk-modal :show.sync="showModal" :title="$t('modalTitle')"> + <div> + {{ $t('selectRedirectPage').replace('%s', redirectData.name) }} + </div> + <div class="mt-15"> + <router-link :to="redirectData.log" class="rk-chart-slow-link mr-20">{{ $t('log') }}</router-link> + <router-link :to="redirectData.trace" class="rk-chart-slow-link mr-20">{{ $t('trace') }}</router-link> + </div> + </rk-modal> </div> </template> @@ -34,6 +43,7 @@ limitations under the License. --> import Vue from 'vue'; import { Component, Prop } from 'vue-property-decorator'; import copy from '@/utils/copy'; + import {MetricsName} from '@/views/components/dashboard/charts/constant'; @Component({}) export default class ChartSlow extends Vue { @@ -41,6 +51,37 @@ limitations under the License. --> @Prop() private item!: any; @Prop() private type!: any; @Prop() private intervalTime!: any; + private showModal: boolean = false; + private redirectData: any = { + name: '', + log: { + path: '', + query: { + service: '', + }, + }, + trace: { + path: '', + query: { + service: '', + }, + }, + }; + private isServiceChart: boolean = false; + + private created() { + const serviceMetricNames = [ + MetricsName.SERVICE_RESP_TIME, + MetricsName.SERVICE_SLA, + MetricsName.SERVICE_CPM, + MetricsName.SERVICE_PERCENTILE, + MetricsName.SERVICE_APDEX, + ]; + this.isServiceChart = 'Service' === this.item.entityType + && 'sortMetrics' === this.item.queryMetricType + && serviceMetricNames.includes(this.item.metricName); + } + get maxValue() { if (!this.data.length) { return null; @@ -54,6 +95,24 @@ limitations under the License. --> private handleClick(i: any) { copy(i); } + private handleLink(i: any) { + if (this.isServiceChart && i.name) { + this.redirectData.name = i.name; + this.redirectData.log = { + path: 'log', + query: { + service: encodeURIComponent(i.name), + }, + }; + this.redirectData.trace = { + path: 'trace', + query: { + service: encodeURIComponent(i.name), + }, + }; + this.showModal = true; + } + } get datas() { if (!this.data.length) { return []; @@ -88,4 +147,13 @@ limitations under the License. --> .rk-chart-slow-i { padding: 6px 0; } + .rk-chart-slow-link { + padding: 4px 10px 7px 10px; + border-radius: 4px; + border: 1px solid #ddd; + color: #333; + background-color: #fff; + will-change: opacity, background-color; + transition: opacity 0.3s, background-color 0.3s; + } </style> diff --git a/src/views/components/dashboard/charts/constant.ts b/src/views/components/dashboard/charts/constant.ts index 5aa812f..ef2f80a 100644 --- a/src/views/components/dashboard/charts/constant.ts +++ b/src/views/components/dashboard/charts/constant.ts @@ -86,3 +86,11 @@ export const ReadValueChartType = [ ]; export const MaxItemNum = 10; + +export enum MetricsName { + SERVICE_RESP_TIME = 'service_resp_time', + SERVICE_SLA = 'service_sla', + SERVICE_CPM = 'service_cpm', + SERVICE_PERCENTILE = 'service_percentile', + SERVICE_APDEX = 'service_apdex', +} diff --git a/src/views/components/log/log-bar.vue b/src/views/components/log/log-bar.vue index 8216294..fe369e5 100644 --- a/src/views/components/log/log-bar.vue +++ b/src/views/components/log/log-bar.vue @@ -127,6 +127,15 @@ limitations under the License. --> this.QUERY_LOGS_BYKEYWORDS(); }) .then(() => { + const serviceName = this.$route.query.service ? this.$route.query.service.toString() : undefined; + if (serviceName) { + for (const s of this.rocketOption.services) { + if (s.label === serviceName) { + this.selectService(s); + break; + } + } + } this.queryLogs(); }); } diff --git a/src/views/components/trace/trace-search.vue b/src/views/components/trace/trace-search.vue index 92b0c4f..1882460 100644 --- a/src/views/components/trace/trace-search.vue +++ b/src/views/components/trace/trace-search.vue @@ -132,19 +132,32 @@ limitations under the License. --> private tagsMap: Array<{ key: string; value: string }> = []; private tagsList: string[] = []; private clearTags: boolean = false; + private serviceName: string = ''; private created() { this.traceId = this.$route.query.traceid ? this.$route.query.traceid.toString() : this.traceId; + this.serviceName = this.$route.query.service ? this.$route.query.service.toString() : this.serviceName; this.time = [this.rocketbotGlobal.durationRow.start, this.rocketbotGlobal.durationRow.end]; } private mounted() { - this.getTraceList(); - if (this.service && this.service.key) { - this.GET_INSTANCES({ - duration: this.durationTime, - serviceId: this.service.key, + this.GET_SERVICES({ duration: this.durationTime }) + .then(() => { + if (this.serviceName) { + for (const s of this.rocketTrace.services) { + if (s.label === this.serviceName) { + this.service = s; + break; + } + } + } + this.getTraceList(); + if (this.service && this.service.key) { + this.GET_INSTANCES({ + duration: this.durationTime, + serviceId: this.service.key, + }); + } }); - } } private globalTimeFormat(time: Date[]) { const step = 'SECOND'; @@ -195,7 +208,6 @@ limitations under the License. --> this.tagsMap = data.tagsMap; } private getTraceList() { - this.GET_SERVICES({ duration: this.durationTime }); const temp: any = { queryDuration: this.globalTimeFormat([ new Date( @@ -259,6 +271,7 @@ limitations under the License. --> localStorage.removeItem('traceId'); this.traceState = { label: 'All', key: 'ALL' }; this.SET_TRACE_FORM_ITEM({ type: 'queryOrder', data: '' }); + this.GET_SERVICES({ duration: this.durationTime }); this.getTraceList(); }