This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
cyber-narrator-cn-ui/src/views/charts2/Panel.vue
2024-06-18 06:28:25 +00:00

560 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="panel-box2" :class="{'panel-box2--entity-detail': entity && entity.entityType}">
<div class="panel__header" v-if="!entity">
<div class="panel__title">{{panelName ? panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}}
<div v-if="showScore" class="score">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
<div v-if="showEntityDetail" class="panel-show-detail">
<el-tooltip
effect="light"
trigger="hover"
:content="$t('entity.jumpToEntityDetails')"
placement="right"
popper-class="panel-tooltip"
>
<i class="cn-icon cn-icon-jump-to" @click="jumpEntityDetail"></i>
</el-tooltip>
</div>
</div>
<div class="panel__tools">
<el-select
v-model="metric"
placeholder=" "
v-if="showMetric"
:teleported="false"
@change="metricChange"
>
<template #prefix>
<span class="select-prefix">{{$t('detections.metric')}}:</span>
</template>
<el-option v-for="item in metricOptions" :key="item.value" :label="$t(item.label)" :value="item.value"></el-option>
</el-select>
<div class="panel__time">
<date-time-range
class="date-time-range"
:start-time="timeFilter.startTime"
:end-time="timeFilter.endTime"
:date-range="timeFilter.dateRangeValue"
ref="dateTimeRange"
@change="reload"
/>
<time-refresh
class="date-time-range"
@change="timeRefreshChange"
:end-time="timeFilter.endTime"
/>
</div>
</div>
</div>
<chart-list
ref="chartList"
:time-filter="timeFilter"
:metric="metric"
:chart-list="chartList"
:panel-type="panelType"
:panel-lock="panelLock"
:extra-params="extraParams"
:entity="entity"
></chart-list>
</div>
</template>
<script>
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import {
panelTypeAndRouteMapping,
curTabState,
drillDownPanelTypeMapping,
metricOptions,
fromRoute
} from '@/utils/constants'
import { getPanelList, getChartList, api } from '@/utils/api'
import { getNowTime, getSecond } from '@/utils/date-util'
import { getTypeCategory } from '@/views/charts/charts/tools'
import { urlParamsHandler, overwriteUrl, getDnsMapData, computeScore } from '@/utils/tools'
import ChartList from '@/views/charts2/ChartList'
import { useStore } from 'vuex'
import axios from 'axios'
import _ from 'lodash'
export default {
name: 'Panel',
props: {
entity: Object,
typeName: String
},
components: {
ChartList
},
data () {
return {
panelTypeAndRouteMapping,
metricOptions,
chartList: [], // 普通panel的chart
panelLock: true,
extraParams: {},
panelName: '',
dnsRcodeMapData: [],
dnsQtypeMapData: [],
score: null,
curTabState: curTabState,
performanceData: {},
scoreDataState: false // 评分数据是否加载完成
}
},
computed: {
// npmThirdLevelMenuScore () {
// return this.$store.getters.getNpmThirdLevelMenuScore
// }
// 显示顶部的Metric单位选项标识
showMetric () {
return this.panelType === panelTypeAndRouteMapping.networkOverview || this.panelType === panelTypeAndRouteMapping.networkOverviewDrillDown
},
scoreBaseState () {
return this.$store.getters.scoreBaseReady
}
},
watch: {
timeFilter: {
handler () {
if (this.$route.path === '/panel/networkAppPerformance') {
this.$store.commit('resetScoreBase')
this.queryScoreBase()
if (this.lineQueryCondition || this.networkOverviewBeforeTab) {
this.scoreCalculation()
}
}
}
},
scoreBaseState (n) {
if (n && this.scoreDataState) {
this.handleScoreData()
}
},
scoreDataState (n) {
if (n && this.scoreBaseState) {
this.handleScoreData()
}
}
},
async mounted () {
// this.panelName = this.$store.getters.getPanelName
const pName = this.$route.query.panelName ? this.$t(this.$route.query.panelName) : ''
const curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
this.$store.commit('setDnsQtypeMapData', this.dnsQtypeMapData)
this.$store.commit('setDnsRcodeMapData', this.dnsRcodeMapData)
}
if (curTabProp === 'qtype') {
this.panelName = this.dnsQtypeMapData.get(pName)
} else if (curTabProp === 'rcode') {
this.panelName = this.dnsRcodeMapData.get(pName)
} else {
this.panelName = pName
}
// const curOperationType = this.$store.getters.getTabOperationType
/* const curOperationType = this.getUrlParam(this.curTabState.tabOperationType, '', true)
if (this.panelName && this.$route.path === '/panel/networkAppPerformance' && curOperationType !== operationType.thirdMenu) {
// const columnValue = this.$store.getters.getBreadcrumbColumnValue
const columnValue = this.getUrlParam(this.curTabState.fourthMenu, '')
const queryParams = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
// type: this.$store.getters.getDimensionType,
type: this.$route.query.dimensionType ? this.$route.query.dimensionType : '',
params: columnValue || ''
}
const requestGroup = []
scoreUrl.forEach(url => {
if (url) {
const request = get(url, queryParams, true)
requestGroup.push(request)
}
})
const scoreGroup = []
let score = 0
Promise.all(requestGroup).then(res => {
res.forEach(t => {
if (t.code === 200) {
const data = t.data.result ? t.data.result[0] : null
if (data) {
customTableTitlesForAppPerformance.forEach(item => {
if (data[bytesColumnNameGroupForNpm[item.prop]]) {
score = computeScore(data, item.scoreType)
scoreGroup.push(score)
} else {
scoreGroup.push(0)
}
})
}
}
})
}).finally(() => {
scoreGroup.forEach(i => {
score = Number(score) + Number(i)
})
score = Math.ceil(score * 6)
if (score > 6) {
score = 6
}
this.score = score || 0
})
} else if (this.$route.path === '/panel/networkAppPerformance' && curOperationType === operationType.thirdMenu) {
this.score = this.$store.getters.getNpmThirdLevelMenuScore
} */
await this.init()
const vm = this
this.emitter.on('reloadChartList', async function () {
vm.chartList = []
vm.chartList = (await getChartList({ panelId: vm.panel.id, pageSize: -1 })).map(chart => {
chart.i = chart.id
// 递归初始化各属性
vm.initChartAttr(chart)
return chart
})
})
if (this.$route.path === '/panel/networkAppPerformance') {
if (this.lineQueryCondition || this.networkOverviewBeforeTab) {
this.scoreCalculation()
}
}
if (this.$route.path === '/panel/networkAppPerformance') {
this.$store.commit('resetScoreBase')
this.queryScoreBase()
}
},
setup (props) {
// router引入store会报错故在panel里调用
const store = useStore()
const cancelList = store.state.panel.httpCancel
// 进入页面时,发现有未结束的请求,终止请求
if (cancelList.length > 0) {
cancelList.forEach((cancel, index) => {
cancel()
delete cancelList[index]
})
}
const panel = ref({})
let panelType = 1 // 取得panel的type
let { query, path } = useRoute()
// 获取路由跳转过的历史状态,赋值给当前界面,起到保留状态的作用,如浏览器的回退前进等
const routerObj = store.getters.getRouterHistoryList.find(item => item.t === query.t)
if (routerObj) {
query = routerObj.query
path = routerObj.path
// 如果当前界面之前载入过,获取状态后更新地址栏,以便后续的赋值操作
const newUrl = urlParamsHandler(window.location.href, useRoute().query, query)
overwriteUrl(newUrl)
}
const thirdPanel = query.thirdPanel
const fourthPanel = query.fourthPanel
const entityType = ref('')
const entityValue = ref('')
const showEntityDetail = ref(false)
if (fourthPanel) {
// networkOverviewBeforeTab是dashboard点击下钻标识dimensionType是详情页到下钻页标识
const tab = query.networkOverviewBeforeTab || query.dimensionType
const value = query.fourthMenu
const ipList = ['ip', 'clientIp', 'serverIp', 'a', 'aaaa']
const appList = ['appLabel']
const domainList = ['sslSni', 'domain']
if (ipList.indexOf(tab) > -1) {
entityType.value = 'ip'
showEntityDetail.value = true
} else if (appList.indexOf(tab) > -1) {
entityType.value = 'app'
showEntityDetail.value = true
} else if (domainList.indexOf(tab) > -1) {
entityType.value = 'domain'
showEntityDetail.value = true
}
entityValue.value = value
panelType = Number(fourthPanel)
} else if (thirdPanel) {
panelType = Number(thirdPanel)
} else {
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[path.replace('/panel/', '')]
}
// 获取url携带的range、startTime、endTime
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
// 优先级url > config.js > 默认值。
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.dashboard || 60)
const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = getSecond(startTime)
timeFilter.value.endTime = getSecond(endTime)
// 如果没有时间参数就将参数写入url
const newUrl = urlParamsHandler(window.location.href, useRoute().query, { startTime: timeFilter.value.startTime, endTime: timeFilter.value.endTime, range: dateRangeValue })
overwriteUrl(newUrl)
} else {
timeFilter.value.startTime = parseInt(startTimeParam)
timeFilter.value.endTime = parseInt(endTimeParam)
}
// npm是否展示分数
const showScorePanel = [drillDownPanelTypeMapping.npmOverviewIp, drillDownPanelTypeMapping.npmOverviewDomain, drillDownPanelTypeMapping.npmOverviewApp, drillDownPanelTypeMapping.npmOverviewCommon, drillDownPanelTypeMapping.npmThirdMenu]
const showScore = showScorePanel.indexOf(panelType) > -1
const metric = ref(query.metric || 'Bits/s')
const lineQueryCondition = ref(query.lineQueryCondition || '')
const dimensionType = ref(query.dimensionType || '')
// 三级菜单判断
const tabOperationType = ref(query.tabOperationType)
const networkOverviewBeforeTab = ref(query.networkOverviewBeforeTab || '')
return {
panelType,
panel,
timeFilter,
showScore,
metric,
path,
lineQueryCondition,
dimensionType,
tabOperationType,
networkOverviewBeforeTab,
showEntityDetail,
entityType,
entityValue
}
},
methods: {
async init () {
const panels = await getPanelList({ type: this.panelType })
if (panels && panels.length > 0) {
this.panel = panels[0]
}
if (this.panel.id) {
if (this.panel.params) {
this.panel.params = JSON.parse(this.panel.params)
} else {
this.panel.params = {}
}
this.chartList = (await getChartList({ panelId: this.panel.id, pageSize: -1 })).map(chart => {
chart.i = chart.id
// 递归初始化各属性
this.initChartAttr(chart)
return chart
})
}
},
initChartAttr (chart) {
chart.i = chart.id
chart.category = getTypeCategory(chart.type)
// 初始化params
chart.params = chart.params ? JSON.parse(chart.params) : {}
chart.firstShow = false
if (!this.$_.isEmpty(chart.children)) {
chart.children.forEach(c => {
this.initChartAttr(c)
})
}
},
reload (startTime, endTime, dateRangeValue) {
this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime), dateRangeValue: dateRangeValue }
const { query } = this.$route
this.$store.commit('setTimeRangeArray', [this.timeFilter.startTime, this.timeFilter.endTime])
this.$store.commit('setTimeRangeFlag', dateRangeValue.value)
const newUrl = urlParamsHandler(window.location.href, query, {
startTime: this.timeFilter.startTime,
endTime: this.timeFilter.endTime,
range: dateRangeValue.value
})
overwriteUrl(newUrl)
},
timeRefreshChange () {
// 不是自选时间
if (this.$refs.dateTimeRange) {
if (!this.$refs.dateTimeRange.isCustom) {
const value = this.timeFilter.dateRangeValue
this.$refs.dateTimeRange.quickChange(value)
} else {
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
}
} else {
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
}
},
getUrlParam (param, defaultValue, isNumber) {
if (isNumber) {
return this.$route.query[param] ? Number(this.$route.query[param]) : defaultValue
} else {
return this.$route.query[param] ? this.$route.query[param] : defaultValue
}
},
metricChange (value) {
const { query } = this.$route
const rangeParam = query.range
let params = { metric: value }
// 优先级url > config.js > 默认值。
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.dashboard || 60)
if (dateRangeValue !== -1) {
const { startTime, endTime } = getNowTime(dateRangeValue)
// this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime), dateRangeValue: dateRangeValue }
this.$store.commit('setTimeRangeArray', [this.timeFilter.startTime, this.timeFilter.endTime])
this.$store.commit('setTimeRangeFlag', dateRangeValue.value)
params = { metric: value, startTime: getSecond(startTime), endTime: getSecond(endTime), range: dateRangeValue }
}
const newUrl = urlParamsHandler(window.location.href, query, params)
overwriteUrl(newUrl)
},
// 动态查询评分基准
queryScoreBase () {
const params = {
startTime: this.timeFilter.startTime,
endTime: this.timeFilter.endTime
}
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params })
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params })
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params })
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params })
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params })
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
const scoreBase = {}
res.forEach((t, i) => {
if (t.status === 200) {
if (i === 0) {
scoreBase.establishLatencyMsP10 = _.get(t.data, 'data.result.establishLatencyMsP10', null)
scoreBase.establishLatencyMsP90 = _.get(t.data, 'data.result.establishLatencyMsP90', null)
} else if (i === 1) {
scoreBase.httpResponseLatencyP10 = _.get(t.data, 'data.result.httpResponseLatencyP10', null)
scoreBase.httpResponseLatencyP90 = _.get(t.data, 'data.result.httpResponseLatencyP90', null)
} else if (i === 2) {
scoreBase.sslConLatencyP10 = _.get(t.data, 'data.result.sslConLatencyP10', null)
scoreBase.sslConLatencyP90 = _.get(t.data, 'data.result.sslConLatencyP90', null)
} else if (i === 3) {
scoreBase.tcpLostlenPercentP10 = _.get(t.data, 'data.result.tcpLostlenPercentP10', null)
scoreBase.tcpLostlenPercentP90 = _.get(t.data, 'data.result.tcpLostlenPercentP90', null)
} else if (i === 4) {
scoreBase.pktRetransPercentP10 = _.get(t.data, 'data.result.pktRetransPercentP10', null)
scoreBase.pktRetransPercentP90 = _.get(t.data, 'data.result.pktRetransPercentP90', null)
}
}
})
this.$store.commit('setScoreBase', scoreBase)
}).catch((e) => {
}).finally(() => {
})
},
scoreCalculation () {
let condition = ''
let url = ''
if (this.lineQueryCondition.indexOf(' OR ') > -1) {
condition = this.lineQueryCondition.split(/["|'](.*?)["|']/)
} else if (this.lineQueryCondition.indexOf('+OR+') > -1) {
condition = this.lineQueryCondition.replace(/\+/g, ' ').split(/["|'](.*?)["|']/)
} else {
condition = this.lineQueryCondition
}
const type = this.dimensionType || this.networkOverviewBeforeTab
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
cycle: 0
}
if (condition && (typeof condition !== 'object') && type) {
if (type === 'clientIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='client'`
} else if (type === 'serverIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='server'`
} else if (type === 'clientCity') {
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'`
} else if (type === 'serverCity') {
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'`
} else {
params.q = condition
}
params.type = type
} else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}'`
params.type = type
} else if (condition.length > 1 && type && type !== 'ip') {
if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') {
params.q = `${type}='${condition[1]}'`
params.type = type
} else {
params.q = `${condition[0]}'${condition[1]}'`
params.type = type
}
}
if (parseFloat(this.tabOperationType) === 3) {
url = api.npm.overview.allNetworkAnalysis
} else {
url = api.npm.overview.networkAnalysis
}
if ((type && condition) || type) {
this.scoreDataState = false
this.performanceData = {}
params.type = params.type || type
axios.get(url, { params }).then(res => {
if (res.status === 200) {
this.performanceData = {
establishLatencyMs: _.get(res, 'data.data.result.establishLatencyMsAvg', null),
httpResponseLatency: _.get(res, 'data.data.result.httpResponseLatencyAvg', null),
sslConLatency: _.get(res, 'data.data.result.sslConLatencyAvg', null),
tcpLostlenPercent: _.get(res, 'data.data.result.tcpLostlenPercentAvg', null),
pktRetransPercent: _.get(res, 'data.data.result.pktRetransPercentAvg', null)
}
}
}).finally(() => {
this.scoreDataState = true
})
}
},
jumpEntityDetail () {
const { href } = this.$router.resolve({
path: '/entity/detail',
query: {
entityType: this.entityType,
entityName: this.entityValue
}
})
window.open(href, '_blank')
},
handleScoreData () {
this.score = computeScore(this.performanceData, this.$store.getters.getScoreBase)
}
},
/**
* 页面销毁前,更新历史中已保存的状态
* 之所以会在下钻时、销毁前保存状态是因为panel第一次下钻时beforeUnmount获取不到下钻前参数
*/
beforeUnmount () {
const query = this.$_.cloneDeep(this.$route.query)
const routerObj = this.$store.getters.getRouterHistoryList.find(item => item.t === query.t)
// const routerObj = window.localRouterHistoryList.find(item => item.t === query.t)
if (routerObj !== undefined) {
if (Object.getOwnPropertyNames(query).length >= Object.getOwnPropertyNames(routerObj.query).length) {
routerObj.query = query
}
}
this.emitter.off('reloadChartList')
this.$store = null
this.emitter = null
}
}
</script>