diff --git a/src/assets/css/components/views/charts2/linkDirectionGrid.scss b/src/assets/css/components/views/charts2/linkDirectionGrid.scss index 3efc579d..c327e9f4 100644 --- a/src/assets/css/components/views/charts2/linkDirectionGrid.scss +++ b/src/assets/css/components/views/charts2/linkDirectionGrid.scss @@ -66,7 +66,83 @@ } } } + } } } } + +$blue: #046ECA; +.item-popover-header { + display: flex; + align-items: center; + line-height: 32px; + font-size: 14px; + font-weight: 600; + + .item-popover-header-icon { + font-size: 20px; + margin: 0 12px; + } +} + +.item-popover-block { + .item-popover-block-title { + line-height: 24px; + font-size: 13px; + color: $blue; + font-weight: 600; + } + + .item-popover-block-content { + width: 100%; + display: flex; + flex-direction: column; + + .block-content-item { + display: flex; + justify-content: space-between; + line-height: 20px; + } + + .block-content-item-name { + } + + .block-content-item-value { + display: flex; + min-width: 95px; + font-weight: 600; + } + } +} + +.row-dot { + margin-top: 5px; + margin-right: 5px; +} + +.green-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: #749F4D; +} + +.red-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: #E26154; +} + +.item-popover-up, .item-popover-down { + font-size: 17px; + margin-left: 3px; +} + +.data-item__hover:hover { + transition: all 0.2s; + background: #F4F9FD; + border: 2px $blue solid !important; + box-shadow: 1px 1px 5px #a1a1a1 !important; +} diff --git a/src/mock/linkMonitor.js b/src/mock/linkMonitor.js index 5b8bb922..fcc2f756 100644 --- a/src/mock/linkMonitor.js +++ b/src/mock/linkMonitor.js @@ -21,7 +21,7 @@ if (openMock) { const egressLinkIds = ['257', '513', '769', '1025', '1281', '1537', '1793', '2049', '2305', '2817'] ingressLinkIds.forEach(ingress => { egressLinkIds.forEach(egress => { - data.push({ egressLinkId: egress, ingressLinkId: ingress, egressUsage: 128000, ingressUsage: 12800, totalBitsRate: 985412, score: 6, tcpConnectionEstablishLatency: 50, httpResponseLatency: 50, sslResponseLatency: 50, packetsLoss: 0.2, packetRetrans: 0.1 }) + data.push({ egressLinkId: egress, ingressLinkId: ingress, egressBytes: 12800000000, ingressBytes: 52800000000, totalBytes: 98541200, score: 6, tcpConnectionEstablishLatency: 50, httpResponseLatency: 50, sslResponseLatency: 50, packetsLoss: 0.2, packetRetrans: 0.1 }) }) }) return { @@ -110,4 +110,40 @@ if (openMock) { } } }) + + Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'interface/linkMonitor/bigramAnalysis1.*'), 'get', function (requestObj) { + const data = [] + const ingressLinkIds = ['256', '512', '768', '1024', '1280', '1536', '1792', '2048', '2304', '2816'] + const egressLinkIds = ['257', '513', '769', '1025', '1281', '1537', '1793', '2049', '2305', '2817'] + ingressLinkIds.forEach(ingress => { + egressLinkIds.forEach(egress => { + data.push({ egressLinkId: egress, ingressLinkId: ingress, egressBytes: 12800000000, ingressBytes: 52800000000, totalBytes: 98541200, establishLatencyMs: 50, httpResponseLatency: 50, sslConLatency: 50, tcpLostlenPercent: 0.2, pktRetransPercent: 0.1 }) + }) + }) + return { + msg: 'success', + code: 200, + data: { + result: data + } + } + }) + + Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'interface/linkMonitor/bigramNextHopAnalysis1.*'), 'get', function (requestObj) { + const data = [] + const ingressLinkIds = ['西安', '太原', '西宁'] + const egressLinkIds = ['西安', '太原', '西宁'] + ingressLinkIds.forEach(ingress => { + egressLinkIds.forEach(egress => { + data.push({ egressLinkDirection: egress, ingressLinkDirection: ingress, egressBytes: 12800000000, ingressBytes: 52800000000, totalBytes: 985412000, establishLatencyMs: 50, httpResponseLatency: 50, sslConLatency: 50, tcpLostlenPercent: 0.2, pktRetransPercent: 0.1 }) + }) + }) + return { + msg: 'success', + code: 200, + data: { + result: data + } + } + }) } diff --git a/src/utils/api.js b/src/utils/api.js index f4bdeaf7..b9a2b6d2 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -203,7 +203,11 @@ export const api = { networkAnalysis: '/interface/link/overview/drilldown/networkAnalysis', linkTrafficDirection: '/interface/linkMonitor/linkTrafficDirection', quadrupleIngressAnalysis: '/interface/link/overview/quadrupleIngressAnalysis', // 入口 - quadrupleEgressAnalysis: '/interface/link/overview/quadrupleEgressAnalysis' // 出口 + quadrupleEgressAnalysis: '/interface/link/overview/quadrupleEgressAnalysis', // 出口 + bigramAnalysis: '/interface/link/overview/bigramAnalysis', + bigramNextHopAnalysis: '/interface/link/overview/bigramNextHopAnalysis', + bigramAnalysis1: 'interface/linkMonitor/bigramAnalysis1', + bigramNextHopAnalysis1: '/interface/linkMonitor/bigramNextHopAnalysis1' }, dnsInsight: { recentEvents: '/interface/dnsInsight/recentEvents', diff --git a/src/views/charts2/charts/linkMonitor/LinkDirectionGrid.vue b/src/views/charts2/charts/linkMonitor/LinkDirectionGrid.vue index 742db887..2cedd368 100644 --- a/src/views/charts2/charts/linkMonitor/LinkDirectionGrid.vue +++ b/src/views/charts2/charts/linkMonitor/LinkDirectionGrid.vue @@ -1,41 +1,10 @@ @@ -44,6 +13,9 @@ import chartMixin from '@/views/charts2/chart-mixin' import { getSecond } from '@/utils/date-util' import { api } from '@/utils/api' import { get } from '@/utils/http' +import { storageKey } from '@/utils/constants' +import PopoverContent from './LinkDirectionGrid/PopoverContent' +import { computeScore } from '@/utils/tools' export default { name: 'LinkDirectionGrid', @@ -54,46 +26,168 @@ export default { gridData2: [] } }, - methods: { - init () { - const params = { - startTime: getSecond(this.timeFilter.startTime), - endTime: getSecond(this.timeFilter.endTime) + components: { + PopoverContent + }, + watch: { + timeFilter: { + deep: true, + handler (n) { + this.init() } - const configRequest = get(api.config, { ckey: 'link_info' }) - const dataRequest = get(api.linkMonitor.linkTrafficDirection, params) - Promise.all([configRequest, dataRequest]).then(res => { - if (res[0].code === 200 && res[1].code === 200) { - if (res[0].page.list && res[0].page.list.length > 0) { - // 链路基本信息 - const linkInfo = JSON.parse(res[0].page.list[0].cvalue) - // 链路流量数据 - const linkData = res[1].data.result - const gridData = [] - linkData.forEach(d => { - const ingressLink = linkInfo.find(l => l.originalLinkId === d.ingressLinkId) - const egressLink = linkInfo.find(l => l.originalLinkId === d.egressLinkId) - if (ingressLink && egressLink) { - const data = gridData.find(g => g.linkId === ingressLink.linkId) - if (data) { - const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId) - if (!existedEgressLink) { - data.egress.push({ linkId: egressLink.linkId, totalBytes: d.totalBytes }) - } - } else { - gridData.push({ linkId: ingressLink.linkId, egress: [{ linkId: egressLink.linkId, totalBytes: d.totalBytes }] }) - } - } - }) - this.gridData = gridData - } - } - }) } }, mounted () { this.toggleLoading(false) this.init() + }, + methods: { + init () { + // 链路基本信息 + let linkInfo = localStorage.getItem(storageKey.linkInfo) + linkInfo = JSON.parse(linkInfo) + console.log('LinkDirectionGrid.vue---init--获取链路基本信息缓存', linkInfo) + + const params = { + startTime: getSecond(this.timeFilter.startTime), + endTime: getSecond(this.timeFilter.endTime) + } + + const dataRequest = get(api.linkMonitor.bigramAnalysis1, params) + const nextHopRequest = get(api.linkMonitor.bigramNextHopAnalysis1, params) + + Promise.all([dataRequest, nextHopRequest]).then(res => { + if (res[0].code === 200 && res[1].code === 200) { + // 链路流量数据 + const linkData = res[0].data.result + // 链路下一跳信息 + const nextLinkData = res[1].data.result + + // 链路流量数据 + const gridData = [] + // 链路下一跳数据 + const gridData2 = [] + linkData.forEach(d => { + const ingressLink = linkInfo.find(l => l.originalLinkId === d.ingressLinkId) + const egressLink = linkInfo.find(l => l.originalLinkId === d.egressLinkId) + if (ingressLink && egressLink) { + const data = gridData.find(g => g.linkId === ingressLink.linkId) + + // 上行使用情况计算 + const egressUsage = this.computeUsage(d.egressBytes, egressLink.bandwidth) + // 下行使用情况计算 + const ingressUsage = this.computeUsage(d.ingressBytes, egressLink.bandwidth) + // 计算npm分数 + d.score = this.localComputeScore(d) + + if (data) { + const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId) + if (!existedEgressLink) { + data.egress.push({ + linkId: egressLink.linkId, + egressUsage: egressUsage, + ingressUsage: ingressUsage, + totalBytes: d.totalBytes, + ...d + }) + } + } else { + gridData.push({ + linkId: ingressLink.linkId, + egress: [{ + linkId: egressLink.linkId, + egressUsage: egressUsage, + ingressUsage: ingressUsage, + totalBytes: d.totalBytes, + ...d + }] + }) + } + } + }) + + nextLinkData.forEach(d => { + const ingressLink = linkInfo.find(l => l.nextHop === d.ingressLinkDirection && l.direction === 'ingress') + const egressLink = linkInfo.find(l => l.nextHop === d.egressLinkDirection && l.direction === 'egress') + + if (ingressLink && egressLink) { + const data = gridData2.find(g => g.linkId === ingressLink.linkId) + + let egressBanwidth = 0 + let ingressBanwidth = 0 + linkInfo.forEach((item) => { + if (item.nextHop === d.egressLinkDirection && item.direction === 'egress') { + egressBanwidth += item.bandwidth + } + if (item.nextHop === d.ingressLinkDirection && item.direction === 'ingress') { + ingressBanwidth += item.bandwidth + } + }) + + // 上行使用情况计算 + const egressUsage = this.computeUsage(d.egressBytes, egressBanwidth) + // 下行使用情况计算 + const ingressUsage = this.computeUsage(d.ingressBytes, ingressBanwidth) + // 计算npm分数 + d.score = this.localComputeScore(d) + + if (data) { + const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId) + if (!existedEgressLink) { + data.egress.push({ + linkId: egressLink.linkId, + nextHop: egressLink.nextHop, + egressUsage: egressUsage, + ingressUsage: ingressUsage, + totalBytes: d.totalBytes, + ...d + }) + } + } else { + gridData2.push({ + linkId: ingressLink.linkId, + nextHop: ingressLink.nextHop, + egress: [{ + linkId: egressLink.linkId, + nextHop: ingressLink.nextHop, + egressUsage: egressUsage, + ingressUsage: ingressUsage, + totalBytes: d.totalBytes, + ...d + }] + }) + } + } + }) + console.log('左侧出入链路数据', gridData) + console.log('右侧下一跳数据', gridData2) + this.gridData = gridData + this.gridData2 = gridData2 + } + }) + }, + /** + * 计算上下行使用占比 + */ + computeUsage (e, bandwidth) { + return Math.ceil((e / bandwidth) * 100) + '%' + }, + /** + * 本地计算npm分数 + */ + localComputeScore (data, bandwidth) { + const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans'] + let score = 0 + keyPre.forEach((item, index) => { + score = computeScore(data, index) + data[keyPre[index] + 'Score'] = score + }) + let npmScore = Math.ceil((data.tcpScore + data.httpScore + data.sslScore + data.tcpLostScore + data.packetRetransScore) * 6) + if (npmScore > 6) { + npmScore = 6 + } + return npmScore + } } } diff --git a/src/views/charts2/charts/linkMonitor/LinkDirectionGrid/PopoverContent.vue b/src/views/charts2/charts/linkMonitor/LinkDirectionGrid/PopoverContent.vue new file mode 100644 index 00000000..5f938cd4 --- /dev/null +++ b/src/views/charts2/charts/linkMonitor/LinkDirectionGrid/PopoverContent.vue @@ -0,0 +1,149 @@ + + +