Compare commits

..

8 Commits

Author SHA1 Message Date
chenjinsong
823c761adf fix: 修复npm折线图堆叠bug 2023-03-14 17:58:27 +08:00
chenjinsong
9e2f1a02d6 fix: 将entity-detail默认时间查询范围改为1小时 2023-03-11 20:40:35 +08:00
chenjinsong
44e7bbf87d fix: 将entity默认时间查询范围改为1小时 2023-03-11 15:10:27 +08:00
chenjinsong
c6e21a0967 fix: 将detection默认时间查询范围改为1小时 2023-03-11 11:15:41 +08:00
lijinyang
cc847102c4 Merge remote-tracking branch 'origin/dev-22.12' into dev-22.12 2023-02-14 16:35:14 +08:00
lijinyang
56f010e991 Update .gitlab-ci.yml 2023-02-14 16:35:05 +08:00
李金洋
4642003193 Update .gitlab-ci.yml file 2023-02-14 08:24:54 +00:00
lijinyang
89dcd7b253 Update .gitlab-ci.yml 2023-02-14 16:14:25 +08:00
79 changed files with 1620 additions and 3761 deletions

View File

@@ -1,8 +1,7 @@
module.exports = { module.exports = {
env: { env: {
browser: true, browser: true,
es2021: true, es2021: true
jest: true
}, },
extends: [ extends: [
'plugin:vue/vue3-essential', 'plugin:vue/vue3-essential',

View File

@@ -2,7 +2,6 @@
stages: stages:
- gen_git-log - gen_git-log
- build_project - build_project
- test
- build_image - build_image
cache: cache:
@@ -30,7 +29,7 @@ generate_git-log:
- public/index.html - public/index.html
- public/git-log.html - public/git-log.html
only: only:
- dev-test - dev-22.12
tags: tags:
- galaxy - galaxy
@@ -48,21 +47,11 @@ build_project:
paths: paths:
- dist/ - dist/
only: only:
- dev-test - dev-22.12
- tags - tags
tags: tags:
- galaxy - galaxy
test:
stage: test
script:
- cnpm run test
when: on_success
only:
- dev-test
tags:
- galaxy
build_image: build_image:
dependencies: dependencies:
- build_project - build_project
@@ -76,7 +65,7 @@ build_image:
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG - sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG
when: on_success when: on_success
only: only:
- dev-test - dev-22.12
tags: tags:
- galaxy - galaxy

View File

@@ -6,8 +6,6 @@ module.exports = {
'<rootDir>/test/**/__tests__/**/*.{vue,js,jsx,ts,tsx}', '<rootDir>/test/**/__tests__/**/*.{vue,js,jsx,ts,tsx}',
'<rootDir>/test/**/*.{spec,test}.{vue,js,jsx,ts,tsx}' '<rootDir>/test/**/*.{spec,test}.{vue,js,jsx,ts,tsx}'
], ],
setupFilesAfterEnv: ['<rootDir>/test/init.js'],
verbose: true,
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
transform: { transform: {
'^.+\\.(vue)$': '<rootDir>/node_modules/vue-jest', '^.+\\.(vue)$': '<rootDir>/node_modules/vue-jest',

0
npm Normal file
View File

View File

@@ -63,7 +63,7 @@
"@vue/cli-service": "~4.5.0", "@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0", "@vue/compiler-sfc": "^3.0.0",
"@vue/component-compiler-utils": "^3.2.0", "@vue/component-compiler-utils": "^3.2.0",
"@vue/test-utils": "^2.2.7", "@vue/test-utils": "^2.0.0-rc.18",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^26.0.0", "babel-jest": "^26.0.0",
"compression-webpack-plugin": "^8.0.1", "compression-webpack-plugin": "^8.0.1",

View File

@@ -1,120 +1,27 @@
<template> <template>
<span test-id="count">{{count}}</span> <span data-test="count">{{count}}</span>
<span test-id="id">{{obj.id}}</span> <button data-test="button" @click="click">click</button>
<span test-id="title">{{obj.title}}</span>
<button test-id="button" @click="click">click</button>
<span test-id="tab">{{lineTab}}</span>
<el-table
:data="tableData"
class="test-table"
height="100%"
empty-text=" "
>
<template v-for="(item, index) in tableTitles" :key="index">
<el-table-column>
<template #default="scope" :column="item">
<span :test-id="`${item.prop}${scope.$index}`">{{scope.row[item.prop]}}</span>
</template>
</el-table-column>
</template>
</el-table>
</template> </template>
<script> <script>
/* vue-jest的测试示例 */ /* vue-jest的测试示例 */
import { useRoute, useRouter } from 'vue-router' import VueRouter from 'vue-router'
import axios from 'axios'
import { ref } from 'vue'
import indexedDBUtils from '@/indexedDB'
export default { export default {
name: 'Test', name: 'Test',
data () { data () {
return { return {
count: 0, count: 0
obj: { id: 1, title: 'title' },
differentParamData0: null,
differentParamData1: null,
indexedDBValue: null,
tableData: [
{
name: 'a',
age: 10
},
{
name: 'b',
age: 11
}
],
tableTitles: [
{ label: 'Name', prop: 'name' },
{ label: 'Age', prop: 'age' }
],
mergeRequestData0: null,
mergeRequestData1: null,
mergeRequestChildData0: null,
mergeRequestChildData1: null
} }
}, },
methods: { methods: {
click () { click () {
this.count++ this.count++
},
async getObj () {
axios.get('/api/getObjId').then(response => {
this.obj.id = response.data
})
axios.get('/api/getObjTitle').then(response => {
this.obj.title = response.data
})
},
async getCount () {
axios.get('/api/getCount').then(response => {
this.count = response.data
})
},
async differentRequestParam () {
axios.get('/api/differentParam', { params: { name: 0 } }).then(response => {
this.differentParamData0 = response.data
})
axios.get('/api/differentParam', { params: { name: 1 } }).then(response => {
this.differentParamData1 = response.data
})
},
/**
* 同一url不同入参的axios请求内包含多个不同url请求的demo
* @returns {Promise<void>}
*/
async mergeRequest () {
axios.get('/api/differentParam', { params: { name: 0 } }).then(response => {
this.mergeRequestData0 = response.data
})
axios.get('/api/differentParam', { params: { name: 1 } }).then(response => {
this.mergeRequestData1 = response.data
axios.get('/api/getChildId').then(response1 => {
this.mergeRequestChildData0 = response1.data
})
axios.get('/api/getChildTitle').then(response2 => {
this.mergeRequestChildData1 = response2.data
})
})
},
async setIndexedDBValue () {
await indexedDBUtils.selectTable('test').put({ id: 1, name: 'test' })
},
async getIndexedDBValue () {
this.indexedDBValue = await indexedDBUtils.selectTable('test').get(1)
} }
}, },
setup () { created () {
const { query } = useRoute() const { currentRoute } = VueRouter.useRouter()
const { currentRoute } = useRouter()
const localstorageValue = localStorage.getItem('key')
const lineTab = ref(query.lineTab || '')
const path = currentRoute.value.path
return { return {
lineTab, currentRoute
path,
localstorageValue
} }
} }
} }

View File

@@ -11,9 +11,7 @@
z-index: 999999; z-index: 999999;
box-shadow: 0 0 10px #CCC; box-shadow: 0 0 10px #CCC;
box-sizing: border-box; box-sizing: border-box;
.pop-title {
margin: 10px 0;
}
.el-button--mini{ .el-button--mini{
padding: 5px 7px; padding: 5px 7px;
} }
@@ -25,6 +23,7 @@
top: 33px; top: 33px;
} }
.custom-labels { .custom-labels {
margin-top: 12px;
width: 100%; width: 100%;
height: 300px; height: 300px;
} }
@@ -42,7 +41,8 @@
font-size: 14px; font-size: 14px;
} }
.custom-label:hover{ .custom-label:hover{
background-color: rgba(220, 223, 230, .5) color: #cccccc;
background-color: #DCDFE6;
} }
.custom-title{ .custom-title{
padding: 2px 0 2px 2px; padding: 2px 0 2px 2px;
@@ -57,14 +57,6 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.custom-bottom-btns-right {
.el-button:nth-of-type(1) {
margin-right: 3px;
}
.el-button .top-tool-btn-save {
color: #fff;
}
}
} }
.unshow { .unshow {
display: none; display: none;

View File

@@ -98,11 +98,11 @@
//top: 0; //top: 0;
//left: 0; //left: 0;
display: flex; display: flex;
.line-value-tabs.mousemove-cursor { .line-value-mpackets.mousemove-cursor {
border-top: 4px solid #D3D0D8; border-top: 4px solid #D3D0D8;
z-index: 0; z-index: 0;
} }
.line-value-tabs { .line-value-mpackets {
cursor: pointer; cursor: pointer;
padding: 16px 0 0 20px; padding: 16px 0 0 20px;
border-top: 4px solid transparent; border-top: 4px solid transparent;
@@ -122,10 +122,10 @@
} }
} }
} }
.line-value-tabs-name { .line-value-mpackets-name {
position: relative; position: relative;
display: flex; display: flex;
.tabs-name { .mpackets-name {
flex: 1; flex: 1;
padding-left: 19px; padding-left: 19px;
} }

View File

@@ -87,7 +87,7 @@
padding-right: 20px; padding-right: 20px;
&.row__label--width130 { &.row__label--width130 {
flex-basis: 140px; flex-basis: 130px;
padding-right: unset; padding-right: unset;
} }
&.row__label--width160 { &.row__label--width160 {

View File

@@ -45,7 +45,6 @@
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { ref } from 'vue' import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'
export default { export default {
name: 'ChartTabs', name: 'ChartTabs',
@@ -70,9 +69,8 @@ export default {
setup (props) { setup (props) {
const tabsData = ref([]) const tabsData = ref([])
const router = useRouter() const router = useRouter()
const store = useStore()
const routerPath = router.currentRoute.value.path const routerPath = router.currentRoute.value.path
const tabList = store.getters.getChartTabList const tabList = window.currentChartTabList
let currentTab = '0' let currentTab = '0'
if (props.data) { if (props.data) {
@@ -96,7 +94,7 @@ export default {
return item.path === routerPath return item.path === routerPath
}) })
currentTab = JSON.stringify(currentTab) currentTab = JSON.stringify(currentTab)
store.dispatch('dispatchChartTabList', [{ path: routerPath, index: currentTab }]) window.currentChartTabList = [{ path: routerPath, index: currentTab }]
} else { } else {
// 此处为切换界面如果window里保存的路径和tabsData里的路径一致选择window数据并使用 // 此处为切换界面如果window里保存的路径和tabsData里的路径一致选择window数据并使用
// 两个不一致的话则默认选择tabsData里的第一条 // 两个不一致的话则默认选择tabsData里的第一条
@@ -110,15 +108,10 @@ export default {
if (obj0 && obj1) { if (obj0 && obj1) {
currentTab = tabList[1].index currentTab = tabList[1].index
// 场景从遮罩面板进入界面时重置状态默认选中第一个tab
if (routerPath === tabList[0].path) {
currentTab = tabList[0].index
store.dispatch('dispatchChartTabList', [{ path: tabsData.value[0].path, index: '0' }])
}
} else if (obj0) { } else if (obj0) {
currentTab = tabList[0].index currentTab = tabList[0].index
} else { } else {
store.dispatch('dispatchChartTabList', [{ path: tabsData.value[0].path, index: '0' }]) window.currentChartTabList = [{ path: tabsData.value[0].path, index: '0' }]
currentTab = '0' currentTab = '0'
} }
} }
@@ -163,10 +156,9 @@ export default {
} }
} }
}) })
const tabList = this.$store.getters.getChartTabList
if (tabList && this.router !== 'noRouter') { if (window.currentChartTabList && this.router !== 'noRouter') {
tabList.forEach((item) => { window.currentChartTabList.forEach((item) => {
this.$nextTick(() => { this.$nextTick(() => {
this.handleActiveBar(parseFloat(item.index)) this.handleActiveBar(parseFloat(item.index))
}) })
@@ -175,7 +167,7 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
this.handleActiveBar(this.currentTab) this.handleActiveBar(this.currentTab)
}) })
this.$store.dispatch('dispatchChartTabList', null) window.currentChartTabList = null
} }
}, },
handleActiveBar (index) { handleActiveBar (index) {
@@ -194,7 +186,7 @@ export default {
} else { } else {
// 数组长度为1即代表刚刷新界面获取active的dom添加样式避免原模式错位问题 // 数组长度为1即代表刚刷新界面获取active的dom添加样式避免原模式错位问题
// 可添加css样式也可添加class类名两个操作选一即可 // 可添加css样式也可添加class类名两个操作选一即可
if (this.$store.getters.getChartTabList.length === 1) { if (window.currentChartTabList.length === 1) {
// 此处操作是因为初始化时给active加border导致tab下移故需要将整体往上移动对应高度 // 此处操作是因为初始化时给active加border导致tab下移故需要将整体往上移动对应高度
const topDom = document.getElementsByClassName('el-tabs__header is-top') const topDom = document.getElementsByClassName('el-tabs__header is-top')
topDom[0].style.cssText += 'top: -3px' topDom[0].style.cssText += 'top: -3px'
@@ -209,7 +201,6 @@ export default {
}, },
handleClick (item) { handleClick (item) {
this.$emit('click', item) this.$emit('click', item)
const tabList = this.$store.getters.getChartTabList
if (this.router === 'noRouter') { if (this.router === 'noRouter') {
const { query } = this.$route const { query } = this.$route
@@ -219,15 +210,14 @@ export default {
}) })
overwriteUrl(newUrl) overwriteUrl(newUrl)
} else { } else {
tabList.push({ window.currentChartTabList.push({
path: this.tabsData[item.index].path, path: this.tabsData[item.index].path,
index: item.index index: item.index
}) })
if (tabList.length > 2) { if (window.currentChartTabList.length > 2) {
tabList.splice(0, 1) window.currentChartTabList.splice(0, 1)
} }
this.$store.dispatch('dispatchChartTabList', tabList)
this.$router.push({ this.$router.push({
path: this.tabsData[item.index].path, path: this.tabsData[item.index].path,
@@ -240,19 +230,6 @@ export default {
}, },
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
const path = this.$router.currentRoute.value.path
const list = this.$store.getters.getChartTabList
if (list && this.router !== 'noRouter') {
if (list[1]) {
// 去其他界面,清除状态
if (path !== list[0].path && path !== list[1].path) {
this.$store.dispatch('dispatchChartTabList', null)
}
} else if (path !== list[0].path) {
// 避免刷新页面之后又点击菜单栏进入该界面,还保留上次点击状态
this.$store.dispatch('dispatchChartTabList', null)
}
}
} }
} }
</script> </script>

View File

@@ -141,6 +141,7 @@ export default {
hoverError (e) { hoverError (e) {
// const dom = document.getElementById('error-content') // const dom = document.getElementById('error-content')
// if (dom) { // if (dom) {
// console.log('---', dom.clientHeight)
// } // }
} }
} }

View File

@@ -25,7 +25,6 @@
class="date_style" class="date_style"
style="position: absolute;top: -53px;left: -536px;" style="position: absolute;top: -53px;left: -536px;"
:clearable="false" :clearable="false"
:default-time="defaultTime"
type="datetimerange" type="datetimerange"
@change="timeArrChange" @change="timeArrChange"
/> />
@@ -128,11 +127,6 @@ export default {
{ value: 2880, name: 'last 2 days' } { value: 2880, name: 'last 2 days' }
] ]
const dropdownFlag = ref(false) const dropdownFlag = ref(false)
// 默认日历选择时间即开始时间YYYY-MM-DD 00:00:00,结束时间YYYY-MM-DD 59:59:59
const defaultTime = ref([
new Date(2023, 1, 1, 0, 0, 0),
new Date(2023, 1, 2, 23, 59, 59)
])
// computed // computed
const utcStr = computed(() => { const utcStr = computed(() => {
@@ -280,7 +274,6 @@ export default {
rangeEchartsData, rangeEchartsData,
address, address,
dateRangeArr, dateRangeArr,
defaultTime,
dateRangeValue, dateRangeValue,
isCustom, isCustom,
newDateValue, newDateValue,

View File

@@ -98,6 +98,7 @@ export default {
if (val && val.value !== -1) { if (val && val.value !== -1) {
// 切换轮询请求时间频率时,发现有未结束的请求,终止请求 // 切换轮询请求时间频率时,发现有未结束的请求,终止请求
const cancelList = this.$store.state.panel.httpCancel const cancelList = this.$store.state.panel.httpCancel
// console.log('timeRefresh.vue------setRefresh------查看终止数量', cancelList, cancelList.length)
if (cancelList.length > 0) { if (cancelList.length > 0) {
cancelList.forEach((cancel, index) => { cancelList.forEach((cancel, index) => {
cancel() cancel()

View File

@@ -103,7 +103,32 @@
<span v-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span> <span v-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span>
</template> </template>
<template v-else-if="index===1"> <template v-else-if="index===1">
<span class="route-menu" @click="jump(route,'','',2)">{{ item }}</span> <span class="route-menu" @click="jump(route,'','',2)"
v-if="route.indexOf('detection') === -1 && route.indexOf('administration') === -1">{{ item }}</span>
<!-- <div class="header__left-breadcrumb-item-select" v-if="route.indexOf('detection') > -1">-->
<!-- <el-popover placement="bottom-start"-->
<!-- v-if="route.indexOf('detection') > -1"-->
<!-- ref="breadcrumbPopover"-->
<!-- :show-arrow="false"-->
<!-- :append-to-body="false"-->
<!-- :hide-after="0"-->
<!-- :show-after="0"-->
<!-- popper-class="breadcrumb__popper"-->
<!-- trigger="click">-->
<!-- <template #reference>-->
<!-- <div class="breadcrumb-button" id="breadcrumbButton2" :class="showBackground?'breadcrumb-button__active':''" v-if="route.indexOf('detection') > -1">-->
<!-- <span id="breadcrumbValue2"> {{item}}</span><i class="cn-icon-xiala cn-icon"></i>-->
<!-- </div>-->
<!-- </template>-->
<!-- <el-row type="flex" justify="center" style="width: fit-content;flex-direction: column;">-->
<!-- <ul class="select-dropdown" id="breadcrumbSelectDropdown2">-->
<!-- <li v-for="item in detectionMenuList" title='' :key="item.name" :id="item.name" class="select-dropdown__item" @click="jump(item.path,'','',2)">-->
<!-- <span>{{$t(item.i18n)}}</span>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </el-row>-->
<!-- </el-popover>-->
<!-- </div>-->
</template> </template>
<template v-else> <template v-else>
<span>{{ item }}</span> <span>{{ item }}</span>
@@ -527,7 +552,7 @@ export default {
queryCondition.push('common_l7_protocol=\'' + valueGroup[0] + '\'') queryCondition.push('common_l7_protocol=\'' + valueGroup[0] + '\'')
queryCondition.push('common_server_port=' + valueGroup[1]) queryCondition.push('common_server_port=' + valueGroup[1])
} }
// console.log(queryCondition.join(' AND ')) console.log(queryCondition.join(' AND '))
this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' AND ') this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' AND ')
} else { } else {
searchProps.forEach(item => { searchProps.forEach(item => {
@@ -602,11 +627,10 @@ export default {
this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true) this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true)
this.urlChangeParams[this.curTabState.tabOperationType] = opeType this.urlChangeParams[this.curTabState.tabOperationType] = opeType
if (opeType === 3) { if (opeType === 3) {
/* if (route !== '/panel/networkOverview') { if (route !== '/panel/networkOverview') {
this.urlChangeParams.queryCondition = ''
} */
this.urlChangeParams.queryCondition = '' this.urlChangeParams.queryCondition = ''
} }
}
} else { } else {
this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu
} }

View File

@@ -8,7 +8,7 @@
</div> </div>
<div class="right-box__container"> <div class="right-box__container">
<div class="container__form"> <div class="container__form">
<el-form ref="reportForm" :model="editObject" :rules="rules" label-position="top" label-width="120px"> <el-form ref="userForm" :model="editObject" :rules="rules" label-position="top" label-width="120px">
<!--name--> <!--name-->
<el-form-item :label="$t('report.name')" prop="name"> <el-form-item :label="$t('report.name')" prop="name">
<el-input id="account-input-name" v-model="editObject.name" maxlength="64" placeholder=" " show-word-limit size="small" type="text"></el-input> <el-input id="account-input-name" v-model="editObject.name" maxlength="64" placeholder=" " show-word-limit size="small" type="text"></el-input>
@@ -19,6 +19,7 @@
v-model="editObject.config.timeConfig.type" v-model="editObject.config.timeConfig.type"
class="right-box__select" class="right-box__select"
collapse-tags collapse-tags
:disabled="!!editObject.id"
placeholder=" " placeholder=" "
popper-class="right-box-select-dropdown right-box-select-report " popper-class="right-box-select-dropdown right-box-select-report "
size="small" size="small"
@@ -30,6 +31,7 @@
</el-select> </el-select>
<template v-if="editObject.config.timeConfig.type === 'this'"> <template v-if="editObject.config.timeConfig.type === 'this'">
<el-select id="reportBoxTimeUnitSelect" <el-select id="reportBoxTimeUnitSelect"
:disabled="!!editObject.id"
v-model="editObject.config.timeConfig.unit" v-model="editObject.config.timeConfig.unit"
class="right-box__select" class="right-box__select"
collapse-tags collapse-tags
@@ -43,7 +45,7 @@
</el-select> </el-select>
</template> </template>
<div v-else-if="editObject.config.timeConfig.type === 'last' || editObject.config.timeConfig.type === 'previous'" style="display: flex;"> <div v-else-if="editObject.config.timeConfig.type === 'last' || editObject.config.timeConfig.type === 'previous'" style="display: flex;">
<el-input v-model.number="editObject.config.timeConfig.offset" size="small" class="el-input-single" placeholder=" "> <el-input v-model.number="editObject.config.timeConfig.offset" size="small" class="el-input-single" placeholder=" " :disabled="!!editObject.id">
<template #prepend><i @click="timeOffsetHandle('m')" class="cn-icon cn-icon-a-"></i></template> <template #prepend><i @click="timeOffsetHandle('m')" class="cn-icon cn-icon-a-"></i></template>
<template #append><i @click="timeOffsetHandle('p')" class="cn-icon cn-icon-a-1"></i></template> <template #append><i @click="timeOffsetHandle('p')" class="cn-icon cn-icon-a-1"></i></template>
</el-input> </el-input>
@@ -51,6 +53,7 @@
v-model="editObject.config.timeConfig.unit" v-model="editObject.config.timeConfig.unit"
class="right-box__select right-box__select-single" class="right-box__select right-box__select-single"
collapse-tags collapse-tags
:disabled="!!editObject.id"
placeholder=" " placeholder=" "
popper-class="right-box-select-dropdown el-select-last" popper-class="right-box-select-dropdown el-select-last"
size="small" size="small"
@@ -70,8 +73,10 @@
v-model="editObject.config.startTime" v-model="editObject.config.startTime"
size="small" size="small"
:format="dateFormat" :format="dateFormat"
:disabled="!!editObject.id"
:disabled-date="startDisabledDate" :disabled-date="startDisabledDate"
@change="startTimeChang" @change="startTimeChang"
@focus="startFocus"
prefix-icon="cn-icon cn-icon-shijian" prefix-icon="cn-icon cn-icon-shijian"
type="datetime" type="datetime"
placeholder=" " placeholder=" "
@@ -88,8 +93,10 @@
v-model="editObject.config.endTime" v-model="editObject.config.endTime"
size="small" size="small"
:format="dateFormat" :format="dateFormat"
:disabled="!!editObject.id"
:disabled-date="endDisabledDate" :disabled-date="endDisabledDate"
@change="endTimeChange" @change="endTimeChange"
@focus="endFocus"
prefix-icon="cn-icon cn-icon-shijian" prefix-icon="cn-icon cn-icon-shijian"
type="datetime" type="datetime"
placeholder=" " placeholder=" "
@@ -98,41 +105,41 @@
</div> </div>
</el-form-item > </el-form-item >
<el-form-item class="el-height"> <el-form-item class="el-height">
<el-checkbox v-model="scheduleChecked" :disabled="editObject.config.timeConfig.type === 'customize'" :label="$t('report.enableTimeSchedule')" size="large" /> <el-checkbox v-model="scheduleChecked" :disabled="editObject.config.timeConfig.type === 'customize' || !!editObject.id" :label="$t('report.enableTimeSchedule')" size="large" />
</el-form-item> </el-form-item>
<!--Enable time schedule--> <!--Enable time schedule-->
<el-form-item prop="enableTimeSchedule" v-if="scheduleChecked"> <el-form-item prop="enableTimeSchedule" v-if="scheduleChecked">
<div class="enable-tab"> <div class="enable-tab">
<div class="enable-tabs" @click="scheduleTypeChange(type.value)" v-for="type in scheduleTypeList" :key="type.value" :class="{'active': scheduleType === type.value}">{{$t(type.name)}}</div> <div class="enable-tabs" @click="editObject.id ? null : (scheduleType = type.value)" v-for="type in scheduleTypeList" :key="type.value" :class="{'active': scheduleType === type.value, 'disable': editObject.id}">{{$t(type.name)}}</div>
</div> </div>
<div class="enable-tabs-daily" v-if="scheduleType === scheduleTypeList[0].value"> <div class="enable-tabs-daily" v-if="scheduleType === scheduleTypeList[0].value">
<div class="enable-tabs-custom">{{$t('report.customEvery')}}</div> <div class="enable-tabs-custom">{{$t('report.customEvery')}}</div>
<el-input v-model.number="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;"> <el-input v-model.number="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;" :disabled="!!editObject.id">
<template #append>{{$t('report.day')}}</template> <template #append>{{$t('report.day')}}</template>
</el-input> </el-input>
</div> </div>
<div class="enable-tabs-weekly" v-else-if="scheduleType === scheduleTypeList[1].value"> <div class="enable-tabs-weekly" v-else-if="scheduleType === scheduleTypeList[1].value" :disabled="!!editObject.id">
<!-- 每隔几周暂时隐藏 --> <!-- 每隔几周暂时隐藏 -->
<!-- <div class="enable-tabs-custom">{{$t('report.customEvery')}}</div> <!-- <div class="enable-tabs-custom">{{$t('report.customEvery')}}</div>
<el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder="Please input"> <el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder="Please input">
<template #append>{{$t('report.week')}}</template> <template #append>{{$t('report.week')}}</template>
</el-input>--> </el-input>-->
<el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" style="margin-top: 0.3125rem"> <el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" style="margin-top: 0.3125rem" :disabled="!!editObject.id">
<el-checkbox v-for="(item, index) in weekdayList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox> <el-checkbox v-for="(item, index) in weekdayList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</div> </div>
<!-- --> <!-- -->
<div class="enable-tabs-per-month" v-else-if="scheduleType === scheduleTypeList[2].value"> <div class="enable-tabs-per-month" v-else-if="scheduleType === scheduleTypeList[2].value">
<div class="enable-month-tab"> <div class="enable-month-tab">
<div class="enable-month-tabs" @click="monthScheduleType = 'daily'" :class="{'active': monthScheduleType === 'daily'}">{{$t('report.date')}}</div> <div class="enable-month-tabs" @click="editObject.id ? null : (monthScheduleType = 'daily')" :class="{'active': monthScheduleType === 'daily', 'disable': editObject.id}">{{$t('report.date')}}</div>
<div class="enable-month-tabs" @click="monthScheduleType = 'weekly'" :class="{'active': monthScheduleType === 'weekly'}">{{$t('report.week')}}</div> <div class="enable-month-tabs" @click="editObject.id ? null : (monthScheduleType = 'weekly')" :class="{'active': monthScheduleType === 'weekly', 'disable': editObject.id}">{{$t('report.week')}}</div>
<el-checkbox v-model="monthIsCycle" :label="$t('report.cycle')" size="large"/> <el-checkbox v-model="monthIsCycle" :label="$t('report.cycle')" size="large" :disabled="!!editObject.id"/>
</div> </div>
<div class="enable-month-data-tab"> <div class="enable-month-data-tab">
<!-- 自定义月循环 --> <!-- 自定义月循环 -->
<template v-if="monthIsCycle"> <template v-if="monthIsCycle">
<div class="enable-tabs-custom">{{$t('report.customEvery')}}</div> <div class="enable-tabs-custom">{{$t('report.customEvery')}}</div>
<el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;"> <el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;" :disabled="!!editObject.id">
<template #append>{{$t('report.month')}}</template> <template #append>{{$t('report.month')}}</template>
</el-input> </el-input>
</template> </template>
@@ -140,8 +147,8 @@
<template v-else> <template v-else>
<div class="enable-month-moon-custom">{{$t('report.custom')}}</div> <div class="enable-month-moon-custom">{{$t('report.custom')}}</div>
<div class="enable-month-all"> <div class="enable-month-all">
<el-checkbox v-model="monthCheckedAll" class="enable-month-all-months" :indeterminate="monthIsIndeterminate" @change="monthCheckAllChange" :label="$t('report.allMonths')"/> <el-checkbox v-model="monthCheckedAll" class="enable-month-all-months" :indeterminate="monthIsIndeterminate" @change="monthCheckAllChange" :label="$t('report.allMonths')" :disabled="!!editObject.id"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.months" @change="monthCheckChange"> <el-checkbox-group v-model="editObject.config.schedulerConfig.months" @change="monthCheckChange" :disabled="!!editObject.id">
<el-checkbox v-for="(item, index) in monthList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox> <el-checkbox v-for="(item, index) in monthList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</div> </div>
@@ -149,8 +156,8 @@
<!-- 按日期 --> <!-- 按日期 -->
<template v-if="monthScheduleType === 'daily'"> <template v-if="monthScheduleType === 'daily'">
<div class="enable-month-data-tabs"> <div class="enable-month-data-tabs">
<el-checkbox v-model="dateCheckedAll" :indeterminate="dateIsIndeterminate" @change="dateCheckAllChange" :label="$t('report.all')" size="large"/> <el-checkbox v-model="dateCheckedAll" :indeterminate="dateIsIndeterminate" @change="dateCheckAllChange" :label="$t('report.all')" size="large" :disabled="!!editObject.id"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.monthDates" @change="dateCheckChange"> <el-checkbox-group v-model="editObject.config.schedulerConfig.monthDates" @change="dateCheckChange" :disabled="!!editObject.id">
<el-checkbox v-for="item in dateList" :key="item" :label="item"/> <el-checkbox v-for="item in dateList" :key="item" :label="item"/>
</el-checkbox-group> </el-checkbox-group>
</div> </div>
@@ -163,6 +170,7 @@
class="right-box__select" class="right-box__select"
multiple multiple
placeholder=" " placeholder=" "
:disabled="!!editObject.id"
popper-class="right-box-select-dropdown right-box-select-report" popper-class="right-box-select-dropdown right-box-select-report"
size="small" size="small"
@change="()=>{ this.$forceUpdate() }"> @change="()=>{ this.$forceUpdate() }">
@@ -171,8 +179,8 @@
</template> </template>
</el-select> </el-select>
<div class="enable-month-week"> <div class="enable-month-week">
<el-checkbox v-model="monthWeekdayCheckedAll" class="enable-month-week-all" :label="$t('report.all')" :indeterminate="monthWeekdayIsIndeterminate" @change="monthWeekdayCheckAllChange" size="large"/> <el-checkbox v-model="monthWeekdayCheckedAll" class="enable-month-week-all" :label="$t('report.all')" :indeterminate="monthWeekdayIsIndeterminate" @change="monthWeekdayCheckAllChange" size="large" :disabled="!!editObject.id"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" @change="monthWeekdayCheckChange"> <el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" @change="monthWeekdayCheckChange" :disabled="!!editObject.id">
<el-checkbox v-for="(item, index) in weekdayList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox> <el-checkbox v-for="(item, index) in weekdayList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</div> </div>
@@ -222,7 +230,7 @@
:disabled="!!editObject.id" :disabled="!!editObject.id"
popper-class="right-box-select-dropdown right-box-select-report" popper-class="right-box-select-dropdown right-box-select-report"
size="small" size="small"
> @change="typeChange">
<template v-for="category in categoryList" :key="category.id"> <template v-for="category in categoryList" :key="category.id">
<el-option :label="category.name" :value="category.id"></el-option> <el-option :label="category.name" :value="category.id"></el-option>
</template> </template>
@@ -275,44 +283,8 @@ import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
import { get, post, put } from '@/utils/http' import { get, post, put } from '@/utils/http'
import { dateFormat, getMillisecond } from '@/utils/date-util' import { dateFormat, getMillisecond } from '@/utils/date-util'
import { ref, getCurrentInstance } from 'vue' import { ref } from 'vue'
import i18n from '@/i18n' const paramValidator = (rule, value, callback) => {
export default {
name: 'ReportBox',
mixins: [rightBoxMixin],
props: {
categoryList: Array,
currentCategoryId: Number
},
setup () {
const { proxy } = getCurrentInstance()
const startTime = ref('')
const endTime = ref('')
function endTimeChange (val) {
endTime.value = val
}
function startTimeChang (val) {
startTime.value = val
}
const endDisabledDate = (time) => {
if (time.getTime() > new Date()) {
return true
}
if (startTime.value !== '' && startTime.value > time) {
return true
}
}
const startDisabledDate = (time) => {
if (time.getTime() > new Date()) {
return true
}
if (endTime.value !== '' && endTime.value < time) {
return true
}
}
const paramValidator = (rule, value, callback) => {
let validate = true let validate = true
if (value && value.length > 0) { if (value && value.length > 0) {
const hasEmpty = value.some(v => { const hasEmpty = value.some(v => {
@@ -321,57 +293,75 @@ export default {
validate = !hasEmpty validate = !hasEmpty
} }
return validate return validate
} }
const nameValidator = (rule, value, callback) => { const nameValidator = (rule, value, callback) => {
let validate = true let validate = true
const reg = /^[\u4e00-\u9fa5A-Za-z0-9\-\_]*$/ const reg = /^[\u4e00-\u9fa5A-Za-z0-9\-\_]*$/
validate = reg.test(value) if (reg.test(value)) {
return validate validate = true
} } else {
const startTimeValidator = (rule, value, callback) => {
const form = proxy.$refs.reportForm
if (form.model.config.endTime) {
form.validateField('config.endTime', () => null)
}
callback()
}
const endTimeValidator = (rule, value, callback) => {
let validate = true
if (startTime.value !== '' && value <= startTime.value) {
validate = false validate = false
} }
return validate return validate
}
export default {
name: 'ReportBox',
mixins: [rightBoxMixin],
props: {
categoryList: Array,
currentCategoryId: Number
},
setup () {
const startTime = ref('')
const endTime = ref('')
const focus = ref('')
const focusDate = ref('')
function endTimeChange (val) {
endTime.value = val
}
function startTimeChang (val) {
startTime.value = val
}
function startFocus (val) {
focus.value = val.target.value
}
function endFocus (val) {
focusDate.value = val.target.value
}
const endDisabledDate = (time) => {
if (time.getTime() > new Date()) {
return true
}
if (startTime.value != '' && startTime.value > time) {
return true
}
if (focusDate.value != '' && endTime.value > time) {
return false
} else if (endTime.value != '' && endTime.value < time) {
return true
}
}
const startDisabledDate = (time) => {
if (time.getTime() > new Date()) {
return true
}
if (focus.value != '' && startTime.value > time) {
return false
} else if (startTime.value != '' && startTime.value > time) {
return true
}
if (endTime.value != '' && endTime.value < time) {
return true
} }
const rules = { // 表单校验规则
name: [
{ required: true, message: i18n.global.t('validate.required'), trigger: 'blur' },
{ validator: nameValidator, message: i18n.global.t('validate.onlyAllowNumberLetterChinese-_'), trigger: 'blur' }
],
categoryId: [
{ required: true, message: i18n.global.t('validate.required'), trigger: 'change' }
],
schedulerStart: [
{ required: true, message: i18n.global.t('validate.required'), trigger: 'change' }
],
'config.startTime': [
{ required: true, message: i18n.global.t('validate.required'), trigger: 'change' },
{ validator: startTimeValidator, trigger: 'change' }
],
'config.endTime': [
{ required: true, message: i18n.global.t('validate.required'), trigger: 'change' },
{ validator: endTimeValidator, message: i18n.global.t('validate.endTimeGreaterThanStart'), trigger: 'change' }
],
categoryParams: [
{ required: true, message: i18n.global.t('validate.required'), trigger: 'blur' },
{ validator: paramValidator, message: i18n.global.t('validate.required'), trigger: 'blur' }
]
} }
return { return {
endDisabledDate, endDisabledDate,
startDisabledDate, startDisabledDate,
startTimeChang, startTimeChang,
endTimeChange, endTimeChange,
rules startFocus,
endFocus
} }
}, },
data () { data () {
@@ -401,20 +391,48 @@ export default {
monthWeekdayCheckedAll: false, monthWeekdayCheckedAll: false,
monthWeekdayIsIndeterminate: false, monthWeekdayIsIndeterminate: false,
rules: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nameValidator, message: this.$t('validate.onlyAllowNumberLetterChinese-_'), trigger: 'blur' }
],
categoryId: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
schedulerStart: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
'config.startTime': [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
'config.endTime': [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
categoryParams: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: paramValidator, message: this.$t('validate.required'), trigger: 'blur' }
]
},
paramsOptions: [] paramsOptions: []
} }
}, },
watch: { watch: {
scheduleType (n, o) { scheduleType (n, o) {
this.editObject.config.schedulerConfig.type = n this.editObject.config.schedulerConfig.type = n
if (!this.editObject.id) {
this.cleanScheduleConfig() this.cleanScheduleConfig()
}
}, },
scheduleChecked (n) { scheduleChecked (n) {
this.editObject.config.isScheduler = n ? 1 : 0 this.editObject.config.isScheduler = n ? 1 : 0
if (!this.editObject.id) {
this.cleanScheduleConfig() this.cleanScheduleConfig()
}
}, },
monthScheduleType (n) { monthScheduleType (n) {
if (!this.editObject.id) {
this.cleanScheduleConfig() this.cleanScheduleConfig()
}
}, },
monthIsCycle (n) { monthIsCycle (n) {
if (!this.editObject.id) { if (!this.editObject.id) {
@@ -548,13 +566,15 @@ export default {
this.editObject.config.timeConfig.offset-- this.editObject.config.timeConfig.offset--
} }
} }
},
typeChange (id) {
}, },
cleanScheduleConfig () { cleanScheduleConfig () {
this.editObject.config.schedulerConfig.monthDates = [] this.editObject.config.schedulerConfig.monthDates = []
this.editObject.config.schedulerConfig.weekDates = [] this.editObject.config.schedulerConfig.weekDates = []
this.editObject.config.schedulerConfig.months = [] this.editObject.config.schedulerConfig.months = []
this.editObject.config.schedulerConfig.monthWeekDates = [] this.editObject.config.schedulerConfig.monthWeekDates = []
this.editObject.config.schedulerConfig.interval = 1
this.monthIsCycle = true this.monthIsCycle = true
this.dateCheckedAll = false this.dateCheckedAll = false
this.dateIsIndeterminate = false this.dateIsIndeterminate = false
@@ -568,14 +588,11 @@ export default {
this.scheduleChecked = false this.scheduleChecked = false
} }
}, },
scheduleTypeChange (val) {
this.scheduleType = val
},
save () { save () {
if (this.blockOperation.save) { return } if (this.blockOperation.save) { return }
this.blockOperation.save = true this.blockOperation.save = true
this.$refs.reportForm.validate((valid) => { this.$refs.userForm.validate((valid) => {
if (valid) { if (valid) {
let startTime = '' let startTime = ''
let endTime = '' let endTime = ''

View File

@@ -25,12 +25,12 @@
<el-button size="mini" v-if="!isCancel" :id="tableId+'-element-set-all'" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new" type="button" @click="batchHandler(true)"> <el-button size="mini" v-if="!isCancel" :id="tableId+'-element-set-all'" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new" type="button" @click="batchHandler(true)">
<span class="top-tool-btn-txt">{{$t('overall.all')}}</span> <span class="top-tool-btn-txt">{{$t('overall.all')}}</span>
</el-button> </el-button>
<div class="custom-bottom-btns-right"> <div>
<el-button size="mini" :id="tableId+'-element-set-esc'" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new" type="button" @click="esc"> <el-button size="mini" :id="tableId+'-element-set-esc'" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new" type="button" @click="esc">
<span class="top-tool-btn-txt">{{$t('overall.cancel')}}</span> <span class="top-tool-btn-txt">{{$t('overall.cancel')}}</span>
</el-button> </el-button>
<el-button size="mini" :id="tableId+'-element-set-save'" class="cn-btn cn-btn-size-small-new cn-btn-style-normal-new" type="button" @click="save" style="background-color: #0091ff;color:#DCDFE6"> <el-button size="mini" :id="tableId+'-element-set-save'" class="cn-btn cn-btn-size-small-new cn-btn-style-normal-new" type="button" @click="save" style="background-color: #0091ff;color:#DCDFE6">
<span class="top-tool-btn-txt top-tool-btn-save">{{$t('overall.save')}}</span> <span class="top-tool-btn-txt">{{$t('overall.save')}}</span>
</el-button> </el-button>
</div> </div>
</div> </div>

View File

@@ -1,19 +1,8 @@
import { dbName, dbGeoDataTableName, dbDrilldownTableConfig } from '@/utils/constants' import { dbName, dbGeoDataTableName, dbDrilldownTableConfig } from '@/utils/constants'
import Dexie from 'dexie' import Dexie from 'dexie'
/* https://dexie.org/ */
const db = new Dexie(dbName) export const db = new Dexie(dbName)
db.version(3).stores({ db.version(2).stores({
[dbGeoDataTableName]: '++name, geo', [dbGeoDataTableName]: '++name, geo',
[dbDrilldownTableConfig]: '++id, config', [dbDrilldownTableConfig]: '++id, config'
test: '++id, name'
}) })
function selectTable (tableName) {
return db[tableName]
}
const indexedDBUtils = {
db,
selectTable
}
export default indexedDBUtils

View File

@@ -1,5 +1,6 @@
import { hasButton } from '@/permission' import { hasButton } from '@/permission'
import { dateFormatByAppearance } from '@/utils/date-util' import { dateFormatByAppearance } from '@/utils/date-util'
import { storageKey } from '@/utils/constants'
export default { export default {
data () { data () {
return { return {
@@ -20,9 +21,7 @@ export default {
query: false query: false
}, },
timeout: null, timeout: null,
debounceFunc: null, debounceFunc: null
// 是否正在单元测试
isUnitTesting: false
} }
}, },
methods: { methods: {

View File

@@ -59,8 +59,7 @@ const panel = {
rangeEchartsData: {}, // 框选echarts图表 rangeEchartsData: {}, // 框选echarts图表
routerHistoryList: [], // 路由跳转记录列表 routerHistoryList: [], // 路由跳转记录列表
dnsQtypeMapData: [], dnsQtypeMapData: [],
dnsRcodeMapData: [], dnsRcodeMapData: []
chartTabList: null // chartTabs组件的tab状态点击列表初始化为null方便原有逻辑计算
}, },
mutations: { mutations: {
setShowRightBox (state, flag) { setShowRightBox (state, flag) {
@@ -152,9 +151,6 @@ const panel = {
}, },
setRouterHistoryList (state, list) { setRouterHistoryList (state, list) {
state.routerHistoryList = list state.routerHistoryList = list
},
setChartTabList (state, list) {
state.chartTabList = list
} }
}, },
getters: { getters: {
@@ -229,9 +225,6 @@ const panel = {
}, },
getRouterHistoryList (state) { getRouterHistoryList (state) {
return state.routerHistoryList return state.routerHistoryList
},
getChartTabList (state) {
return state.chartTabList
} }
}, },
actions: { actions: {
@@ -260,9 +253,6 @@ const panel = {
}, },
clearPanel (store) { clearPanel (store) {
store.commit('cleanPanel') store.commit('cleanPanel')
},
dispatchChartTabList (store, list) {
store.commit('setChartTabList', list)
} }
} }
} }

View File

@@ -5,7 +5,7 @@ import { ElMessage } from 'element-plus' // dependent on utc plugin
import { storageKey, dbDrilldownTableConfig } from '@/utils/constants' import { storageKey, dbDrilldownTableConfig } from '@/utils/constants'
import { getConfigVersion } from '@/utils/tools' import { getConfigVersion } from '@/utils/tools'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import indexedDBUtils from '@/indexedDB' import { db } from '@/indexedDB'
const user = { const user = {
state () { state () {
@@ -92,7 +92,7 @@ const user = {
if (res.code === 200 && res.page.list && res.page.list.length > 0) { if (res.code === 200 && res.page.list && res.page.list.length > 0) {
// 从接口返回整体配置,再读取用户缓存,将对应条目覆盖,作为使用的配置 // 从接口返回整体配置,再读取用户缓存,将对应条目覆盖,作为使用的配置
const defaultConfigs = JSON.parse(res.page.list[0].cvalue) const defaultConfigs = JSON.parse(res.page.list[0].cvalue)
await indexedDBUtils.selectTable(dbDrilldownTableConfig).put({ await db[dbDrilldownTableConfig].put({
id: 'default', id: 'default',
version: defaultConfigs.version, version: defaultConfigs.version,
config: defaultConfigs.config config: defaultConfigs.config
@@ -100,7 +100,7 @@ const user = {
const userId = localStorage.getItem(storageKey.userId) const userId = localStorage.getItem(storageKey.userId)
const oldVersion = await getConfigVersion(userId) const oldVersion = await getConfigVersion(userId)
if (oldVersion !== defaultConfigs.version) { if (oldVersion !== defaultConfigs.version) {
indexedDBUtils.selectTable(dbDrilldownTableConfig).delete(userId) db[dbDrilldownTableConfig].delete(userId)
} }
} }
}) })

View File

@@ -318,12 +318,11 @@ export async function getI18n () {
/* 获得原始的3611-2 json字符串数据 */ /* 获得原始的3611-2 json字符串数据 */
export async function getIso36112JsonData (suffix) { export async function getIso36112JsonData (suffix) {
const url = `${window.location.protocol}//${window.location.host}/geojson/${suffix}.json`
const request = new Promise(resolve => { const request = new Promise(resolve => {
axios({ url }).then(response => { axios({
url: `${window.location.protocol}//${window.location.host}:${window.location.port}/geojson/${suffix}.json`
}).then(response => {
resolve(response.data || response || null) resolve(response.data || response || null)
}).catch(err => {
console.error(err)
}) })
}) })
return await request return await request

View File

@@ -1,8 +1,6 @@
export const defaultPageSize = 20 export const defaultPageSize = 20
// indexedDB库名
export const dbName = 'cn-db' export const dbName = 'cn-db'
// indexedDB表名
export const dbGeoDataTableName = 'geodata' export const dbGeoDataTableName = 'geodata'
export const dbDrilldownTableConfig = 'cn-drilldown-table-config' export const dbDrilldownTableConfig = 'cn-drilldown-table-config'
export const storageKey = { export const storageKey = {

View File

@@ -5,7 +5,8 @@ import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, n
import { getIso36112JsonData, getDictList } from '@/utils/api' import { getIso36112JsonData, getDictList } from '@/utils/api'
import { format } from 'echarts' import { format } from 'echarts'
import router from '@/router' import router from '@/router'
import indexedDBUtils from '@/indexedDB' import { db } from '@/indexedDB'
import { useRoute } from 'vue-router'
export const tableSort = { export const tableSort = {
// 是否需要排序 // 是否需要排序
@@ -487,11 +488,11 @@ export function loadGeoData () {
keys.push(storageKey.iso36112Capital) keys.push(storageKey.iso36112Capital)
keys.push(storageKey.iso36112WorldLow) keys.push(storageKey.iso36112WorldLow)
keys.forEach(async k => { keys.forEach(async k => {
const queryData = await indexedDBUtils.selectTable(dbGeoDataTableName).get({ name: k }) const queryData = await db[dbGeoDataTableName].get({ name: k })
if (!queryData) { if (!queryData) {
const data = await getIso36112JsonData(iso36112[k]) const data = await getIso36112JsonData(iso36112[k])
if (data) { if (data) {
indexedDBUtils.selectTable(dbGeoDataTableName).add({ db[dbGeoDataTableName].add({
name: k, name: k,
geo: data geo: data
}) })
@@ -504,14 +505,14 @@ export function loadGeoData () {
* 使用indexedDB缓存地图数据 * 使用indexedDB缓存地图数据
* */ * */
export async function getGeoData (key) { export async function getGeoData (key) {
const data = await indexedDBUtils.selectTable(dbGeoDataTableName).get({ name: key }) const data = await db[dbGeoDataTableName].get({ name: key })
if (data) { if (data) {
return data.geo return data.geo
} else { } else {
if (iso36112[key]) { if (iso36112[key]) {
const d = await getIso36112JsonData(iso36112[key]) const d = await getIso36112JsonData(iso36112[key])
if (d) { if (d) {
indexedDBUtils.selectTable(dbGeoDataTableName).add({ db[dbGeoDataTableName].add({
name: key, name: key,
geo: d geo: d
}) })
@@ -936,7 +937,7 @@ export async function getDefaultCurTab (tableType, metric, columnName) {
export async function readDrilldownTableConfigByUser () { export async function readDrilldownTableConfigByUser () {
// 获取用户定制的自定义配置 // 获取用户定制的自定义配置
const userId = localStorage.getItem(storageKey.userId) const userId = localStorage.getItem(storageKey.userId)
const userLocalConfig = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: userId }) const userLocalConfig = await db[dbDrilldownTableConfig].get({ id: userId })
let defaultDrillDownTableConfigs = [] let defaultDrillDownTableConfigs = []
if (userLocalConfig) { if (userLocalConfig) {
defaultDrillDownTableConfigs = userLocalConfig.config defaultDrillDownTableConfigs = userLocalConfig.config
@@ -945,15 +946,15 @@ export async function readDrilldownTableConfigByUser () {
} }
export async function getConfigVersion (id) { export async function getConfigVersion (id) {
let defaultConfigInDb = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: id }) let defaultConfigInDb = await db[dbDrilldownTableConfig].get({ id: id })
if (!defaultConfigInDb) { if (!defaultConfigInDb) {
defaultConfigInDb = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: 'default' }) defaultConfigInDb = await db[dbDrilldownTableConfig].get({ id: 'default' })
} }
return defaultConfigInDb.version || '' return defaultConfigInDb.version || ''
} }
export async function combineDrilldownTableWithUserConfig () { export async function combineDrilldownTableWithUserConfig () {
const defaultConfigInDb = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: 'default' }) const defaultConfigInDb = await db[dbDrilldownTableConfig].get({ id: 'default' })
const defaultConfigGroup = defaultConfigInDb ? defaultConfigInDb.config : [] const defaultConfigGroup = defaultConfigInDb ? defaultConfigInDb.config : []
const currentUserConfigGroup = await readDrilldownTableConfigByUser() const currentUserConfigGroup = await readDrilldownTableConfigByUser()
if (defaultConfigGroup && currentUserConfigGroup && currentUserConfigGroup.length > 0) { if (defaultConfigGroup && currentUserConfigGroup && currentUserConfigGroup.length > 0) {
@@ -1085,64 +1086,3 @@ export function colorGradientCalculation (startColor, endColor, values) {
export function colorHexToRgbArr (hex) { export function colorHexToRgbArr (hex) {
return [1, 3, 5].map((h) => parseInt(hex.substring(h, h + 2), 16)) return [1, 3, 5].map((h) => parseInt(hex.substring(h, h + 2), 16))
} }
/**
* 通过事件类型eventType转换对应名称
* @param type
* @returns {string}
*/
export function getNameByEventType (type) {
switch (type) {
case 'http error': {
return 'http error ratio'
}
case 'dns error': {
return 'dns error ratio'
}
case 'high dns response time': {
return 'dns response time'
}
}
}
/**
折线图通过事件类型 type 转换对应名称
*/
export function getLineType (type) {
switch (type) {
case 'bytes': {
return 'Bits/s'
}
case 'packets': {
return 'Packets/s'
}
case 'sessions': {
return 'Sessions/s'
}
case 'queries': {
return 'Queries/s'
}
default: return type
}
}
/**
npm折线图通过事件类型 type 转换对应 index 以及 unit
*/
export function getLineIndexUnit (type, show) {
switch (type) {
case 'establishLatencyMs': {
return show ? '(ms)' : 0
}
case 'tcpLostlenPercent': {
return show ? '(%)' : 3
}
case 'pktRetransPercent': {
return show ? '(%)' : 4
}
case 'httpResponseLatency': {
return show ? '(ms)' : 1
}
case 'sslConLatency': {
return show ? '(ms)' : 2
}
}
}

View File

@@ -43,6 +43,16 @@ export default {
i18n: 'I18n', i18n: 'I18n',
path: '/administration/i18n', path: '/administration/i18n',
icon: 'cn-icon cn-icon-i18n' icon: 'cn-icon cn-icon-i18n'
},
{
i18n: 'galaxyProxy.galaxyProxy',
path: '/administration/galaxyProxy',
icon: 'cn-icon cn-icon-proxy'
},
{
i18n: 'overall.chart',
path: '/administration/chart',
icon: 'cn-icon cn-icon-chart'
} }
] ]
} }

View File

@@ -247,9 +247,8 @@ import {
chartActiveIpTableOrderOptions, chartActiveIpTableOrderOptions,
chartPieTableTopOptions, chartPieTableTopOptions,
eventSeverity, eventSeverity,
chartTableColumnMapping, panelTypeAndRouteMapping chartTableColumnMapping
} from '@/utils/constants' } from '@/utils/constants'
import { useRouter } from 'vue-router'
export default { export default {
name: 'ChartHeader', name: 'ChartHeader',
@@ -370,17 +369,12 @@ export default {
} }
}, },
setup (props) { setup (props) {
const { currentRoute } = useRouter() const dateRangeValue = 60
function isEntityDetail (r) {
return r.indexOf('entityDetail') > -1
}
const dateRangeValue = isEntityDetail(currentRoute.value.path) ? 60 * 24 : 60
const { startTime, endTime } = getNowTime(dateRangeValue) const { startTime, endTime } = getNowTime(dateRangeValue)
// entity详情内的chart时间工具不是公共的需要单独定义 // entity详情内的chart时间工具不是公共的需要单独定义
const chartTimeFilter = ref({ startTime, endTime, dateRangeValue }) const chartTimeFilter = ref({ startTime, endTime, dateRangeValue })
// 复制一份prop中需要被组件v-model的内容避免报错 // 复制一份prop中需要被组件v-model的内容避免报错
const copyOrderPieTable = props.orderPieTable const copyOrderPieTable = ref(props.orderPieTable)
return { return {
chartTimeFilter, chartTimeFilter,
chartTableTopOptions, chartTableTopOptions,

View File

@@ -107,11 +107,8 @@ export default {
const { params } = useRoute() const { params } = useRoute()
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName] panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName]
function isEntityDetail (t) {
return [4, 5, 6].indexOf(t) > -1
}
// date // date
const dateRangeValue = isEntityDetail(panelType) ? 60 * 24 : 60 const dateRangeValue = 60
const { startTime, endTime } = getNowTime(dateRangeValue) const { startTime, endTime } = getNowTime(dateRangeValue)
const timeFilter = ref({ startTime, endTime, dateRangeValue }) const timeFilter = ref({ startTime, endTime, dateRangeValue })
@@ -150,12 +147,14 @@ export default {
if (panels && panels.length > 0) { if (panels && panels.length > 0) {
this.panel = panels[0] this.panel = panels[0]
} }
// console.log(this.panel)
if (this.panel.id) { if (this.panel.id) {
if (this.panel.params) { if (this.panel.params) {
this.panel.params = JSON.parse(this.panel.params) this.panel.params = JSON.parse(this.panel.params)
} else { } else {
this.panel.params = {} this.panel.params = {}
} }
// console.log(this.panel)
const allCharts = (await getChartList({ panelId: this.panel.id, pageSize: -1 })).map(chart => { const allCharts = (await getChartList({ panelId: this.panel.id, pageSize: -1 })).map(chart => {
chart.i = chart.id chart.i = chart.id
this.recursionParamsConvert(chart) this.recursionParamsConvert(chart)

View File

@@ -114,8 +114,8 @@ export default {
}, },
reload () { reload () {
this.copyDataList.forEach(item => { this.copyDataList.forEach(item => {
if (this.$refs['chart' + item.id] && this.$refs['chart' + item.id][0]) { if (this.$refs['chart' + item.id]) {
this.$refs['chart' + item.id][0].reload() this.$refs['chart' + item.id].reload()
} }
}) })
}, },

View File

@@ -32,8 +32,8 @@ export default {
methods: { methods: {
reload () { reload () {
this.dnsScreenDataList.forEach(item => { this.dnsScreenDataList.forEach(item => {
if (this.$refs['chart' + item.id] && this.$refs['chart' + item.id][0]) { if (this.$refs['chart' + item.id]) {
this.$refs['chart' + item.id][0].getChartData() this.$refs['chart' + item.id].getChartData()
} }
}) })
} }

View File

@@ -238,20 +238,16 @@ export default {
} else { } else {
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName] panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName]
} }
function isEntityDetail (t) {
return [4, 5, 6].indexOf(t) > -1
}
// 获取url携带的range、startTime、endTime // 获取url携带的range、startTime、endTime
const rangeParam = query.range const rangeParam = query.range
const startTimeParam = query.startTime const startTimeParam = query.startTime
const endTimeParam = query.endTime const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。 // 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : (isEntityDetail(panelType) ? 60 * 24 : 60) const dateRangeValue = rangeParam ? parseInt(query.range) : 60
const timeFilter = ref({ dateRangeValue }) const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) { if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(isEntityDetail(panelType) ? 60 * 24 : 60) const { startTime, endTime } = getNowTime(60)
timeFilter.value.startTime = startTime timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime timeFilter.value.endTime = endTime
} else { } else {
@@ -334,8 +330,6 @@ export default {
if (!this.$refs.dateTimeRange.isCustom) { if (!this.$refs.dateTimeRange.isCustom) {
const value = this.timeFilter.dateRangeValue const value = this.timeFilter.dateRangeValue
this.$refs.dateTimeRange.quickChange(value) this.$refs.dateTimeRange.quickChange(value)
} else {
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
} }
} else { } else {
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter)) this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))

View File

@@ -123,8 +123,11 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(api.dnsInsight.activeMaliciousDomain, params).then(res => { get(api.dnsInsight.activeMaliciousDomain, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.isNoData = res.data.result.length === 0 const data = res.data.result
this.tableData = res.data.result if (!data || data.length === 0) {
this.isNoData = true
}
this.tableData = data
} else { } else {
this.isNoData = true this.isNoData = true
} }

View File

@@ -95,9 +95,6 @@ export default {
}, },
beforeUnmount () { beforeUnmount () {
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) {
echarts.dispose(this.myChart)
}
} }
} }
</script> </script>

View File

@@ -147,9 +147,6 @@ export default {
}, },
beforeUnmount () { beforeUnmount () {
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) {
echarts.dispose(this.myChart)
}
} }
} }
</script> </script>

View File

@@ -208,7 +208,9 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(api.dnsInsight.recentEvents, params).then(res => { get(api.dnsInsight.recentEvents, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.isNoData = res.data.result.length === 0 if (!res.data.result || res.data.result.length === 0) {
this.isNoData = true
}
this.tableData = res.data.result this.tableData = res.data.result
this.tableData.forEach((t, index) => { this.tableData.forEach((t, index) => {
if (index > 5) { if (index > 5) {

View File

@@ -5,17 +5,17 @@
<div class="line-header-left"> <div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div> <div class="line-value-active" v-if="lineTab"></div>
<div class="line-value"> <div class="line-value">
<div class="line-value-tabs" <div class="line-value-mpackets"
v-show="item.show" v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}" :class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in tabs" v-for="(item, index) in mpackets"
:key="index" :key="index"
@mouseenter="mouseenter(item)" @mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)" @mouseleave="mouseleave(item)"
@click="activeChange(item, index)"> @click="activeChange(item, index)">
<div class="line-value-tabs-name"> <div class="line-value-mpackets-name">
<div :class="item.class"></div> <div :class="item.class"></div>
<div class="tabs-name">{{$t(item.name)}}</div> <div class="mpackets-name">{{$t(item.name)}}</div>
</div> </div>
<div class="line-value-unit"> <div class="line-value-unit">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span> <span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
@@ -79,7 +79,7 @@ import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
export default { export default {
name: 'DnsTrafficLine', name: 'DnsTrafficLine',
components: { components: {
@@ -126,7 +126,7 @@ export default {
label: 'Maximum' label: 'Maximum'
} }
], ],
tabs: [ mpackets: [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' }, { analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' } { analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -202,14 +202,68 @@ export default {
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
if (this.isNoData) { if (this.isNoData) {
this.lineTab = '' this.lineTab = ''
this.tabs = [ this.mpackets = [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' }, { analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' } { analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
] ]
} else {
this.initData(res.data.result, val, active, show)
} }
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis
mpackets[1].analysis = t.inboundBitsRate.analysis
mpackets[2].analysis = t.outboundBitsRate.analysis
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'bps'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 3) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.echartsInit(this.mpackets)
} else {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
}
} else if (t.type === 'queries' && val === 'Queries/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalQueryRate.analysis
mpackets[0].data = t.totalQueryRate.values ? t.totalQueryRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
e.unitType = 'queries/s'
e.invertTab = false
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.mpackets = mpackets
this.echartsInit(this.mpackets, true)
}
})
} else { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
@@ -323,7 +377,7 @@ export default {
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.tabs.forEach(e => { this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) { if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning] t.borderColor = chartColor3[e.positioning]
} }
@@ -366,16 +420,16 @@ export default {
legendSelectChange (item, index, val) { legendSelectChange (item, index, val) {
if (index === 'index') { if (index === 'index') {
this.dispatchLegendSelectAction(item.name) this.dispatchLegendSelectAction(item.name)
} else if (this.tabs[index] && this.tabs[index].name === item.name) { } else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
this.dispatchLegendSelectAction(item.name) this.dispatchLegendSelectAction(item.name)
this.tabs.forEach((t) => { this.mpackets.forEach((t) => {
if (t.name !== item.name) { if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name) this.dispatchLegendUnSelectAction(t.name)
} }
}) })
} }
if (val === 'active') { if (val === 'active') {
this.tabs.forEach(t => { this.mpackets.forEach(t => {
if (item.name === t.name) { if (item.name === t.name) {
t.invertTab = !t.invertTab t.invertTab = !t.invertTab
} else { } else {
@@ -387,7 +441,7 @@ export default {
} else { } else {
this.lineTab = t.class this.lineTab = t.class
} }
this.tabs.forEach((e) => { this.mpackets.forEach((e) => {
this.dispatchLegendSelectAction(e.name) this.dispatchLegendSelectAction(e.name)
}) })
} }
@@ -395,8 +449,8 @@ export default {
} }
}, },
handleActiveBar () { handleActiveBar () {
if (document.querySelector('.network .line-value-tabs.is-active')) { if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active') const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
const activeBar = document.querySelector('.network .line-value-active') const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;` activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
} }
@@ -409,7 +463,7 @@ export default {
this.lineTab = '' this.lineTab = ''
this.handleActiveBar() this.handleActiveBar()
this.showMarkLine = !this.showMarkLine this.showMarkLine = !this.showMarkLine
this.tabs.forEach((e) => { this.mpackets.forEach((e) => {
if (!e.invertTab) { if (!e.invertTab) {
e.invertTab = true e.invertTab = true
} }
@@ -421,9 +475,9 @@ export default {
let echartsData let echartsData
const chartOption = this.myChart.getOption() const chartOption = this.myChart.getOption()
if (this.lineTab) { if (this.lineTab) {
echartsData = this.tabs.filter(t => t.show === true && t.invertTab === false) echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
} else { } else {
echartsData = this.tabs.filter(t => t.show === true) echartsData = this.mpackets.filter(t => t.show === true)
} }
if (this.lineRefer === 'Average' && this.showMarkLine) { if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => { chartOption.series.forEach((t, i) => {
@@ -472,85 +526,12 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] }) dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
},
initData (data, val, active, show) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys(item).map(t => {
return {
...item[t]
}
})
}
})
}
lineData.splice(0, 1)
const tabs = _.cloneDeep(this.tabs)
if (val === 'Queries/s') {
lineData.forEach((d, i) => {
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
tabs.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
e.unitType = 'queries/s'
e.invertTab = false
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.tabs = tabs
this.echartsInit(this.tabs, true)
} else {
const unit = 'bps'
this.legendInit(lineData, active, show, unit, tabs)
}
},
legendInit (data, active, show, type, dnsData) {
data.forEach((d, i) => {
dnsData[i].data = d.values
dnsData[i].analysis = d.analysis
})
let num = 0
dnsData.forEach(e => {
e.unitType = type
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.tabs = dnsData
if (num === 3) {
dnsData[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(dnsData[0], 0)
this.echartsInit(this.tabs)
} else {
this.echartsInit(this.tabs, show)
if (!this.lineRefer) this.lineRefer = 'Average'
}
} }
}, },
mounted () { mounted () {
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
if (this.lineTab) { if (this.lineTab) {
const data = this.tabs.find(t => t.class === this.lineTab) const data = this.mpackets.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning) this.activeChange(data, data.positioning)
} else { } else {
this.init() this.init()
@@ -561,9 +542,7 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart)
}
this.chartOption = null this.chartOption = null
this.unitConvert = null this.unitConvert = null
} }

View File

@@ -29,20 +29,20 @@
<div class="popper-content__link-id">Link ID: {{ item.linkId }}</div> <div class="popper-content__link-id">Link ID: {{ item.linkId }}</div>
<div class="popper-content__link-info"> <div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div> <div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="info__value" :test-id="`linkBlockTotal${index}`" style="margin-left: 8px"> <div class="info__value" style="margin-left: 8px">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }} {{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }}
</div> </div>
</div> </div>
<div class="popper-content__link-info"> <div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.bandwidthUsage') }}</div> <div class="info__label">{{ $t('linkMonitor.linkBlock.bandwidthUsage') }}</div>
<div class="info__value" style="display: flex"> <div class="info__value" style="display: flex">
<div :test-id="`linkBlockEgressUsage${index}`"> <div>
<svg class="icon item-popover-up" aria-hidden="true"> <svg class="icon item-popover-up" aria-hidden="true">
<use xlink:href="#cn-icon-egress"></use> <use xlink:href="#cn-icon-egress"></use>
</svg> </svg>
{{ convertValue(item.egressUsage) }} {{ convertValue(item.egressUsage) }}
</div> </div>
<div :test-id="`linkBlockIngressUsage${index}`"> <div>
<svg class="icon item-popover-down" aria-hidden="true"> <svg class="icon item-popover-down" aria-hidden="true">
<use xlink:href="#cn-icon-ingress"></use> <use xlink:href="#cn-icon-ingress"></use>
</svg> </svg>
@@ -84,7 +84,7 @@
<div class="popper-content__link-id">Next-Hop Internet: {{ item.linkDirection }}</div> <div class="popper-content__link-id">Next-Hop Internet: {{ item.linkDirection }}</div>
<div class="popper-content__link-info"> <div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div> <div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="info__value" :test-id="`nextHopTotal${index}`" style="margin-left: 8px"> <div class="info__value" style="margin-left: 8px">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }} {{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }}
</div> </div>
</div> </div>
@@ -118,13 +118,13 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { colorGradientCalculation } from '@/utils/tools' import { colorGradientCalculation } from '@/utils/tools'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/constants' import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/constants'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios'
export default { export default {
name: 'LinkBlock', name: 'LinkBlock',
@@ -175,13 +175,10 @@ export default {
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
const dataRequest = axios.get(api.linkMonitor.analysis, { params: params }) const dataRequest = get(api.linkMonitor.analysis, params)
const nextHopRequest = axios.get(api.linkMonitor.nextHopAnalysis, { params: params }) const nextHopRequest = get(api.linkMonitor.nextHopAnalysis, params)
Promise.all([dataRequest, nextHopRequest]).then(response => { Promise.all([dataRequest, nextHopRequest]).then(res => {
const res = []
res[0] = response[0].data
res[1] = response[1].data
if (res[0].code === 200 && res[1].code === 200) { if (res[0].code === 200 && res[1].code === 200) {
this.showError = false this.showError = false

View File

@@ -12,10 +12,10 @@
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { get } from '@/utils/http'
import { storageKey } from '@/utils/constants' import { storageKey } from '@/utils/constants'
import PopoverContent from './LinkDirectionGrid/PopoverContent' import PopoverContent from './LinkDirectionGrid/PopoverContent'
import { computeScore } from '@/utils/tools' import { computeScore } from '@/utils/tools'
import axios from 'axios'
export default { export default {
name: 'LinkDirectionGrid', name: 'LinkDirectionGrid',
@@ -56,14 +56,11 @@ export default {
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
const dataRequest = axios.get(api.linkMonitor.bigramAnalysis, { params: params }) const dataRequest = get(api.linkMonitor.bigramAnalysis, params)
const nextHopRequest = axios.get(api.linkMonitor.bigramNextHopAnalysis, { params: params }) const nextHopRequest = get(api.linkMonitor.bigramNextHopAnalysis, params)
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([dataRequest, nextHopRequest]).then(response => { Promise.all([dataRequest, nextHopRequest]).then(res => {
const res = []
res[0] = response[0].data
res[1] = response[1].data
if (res[0].code === 200) { if (res[0].code === 200) {
this.isLinkShowError = false this.isLinkShowError = false
// 链路流量数据 // 链路流量数据
@@ -98,7 +95,7 @@ export default {
// 分数低于3分赋红点 // 分数低于3分赋红点
d.score = this.localComputeScore(d) d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3 || d.score === '-' d.scoreLow3 = d.score < 3
if (data) { if (data) {
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId) const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
@@ -146,9 +143,9 @@ export default {
// 接口数据乱序,根据出方向排序,再根据同个出方向下的入进行排序 // 接口数据乱序,根据出方向排序,再根据同个出方向下的入进行排序
nextLinkData.sort((a, b) => { nextLinkData.sort((a, b) => {
if (a.ingressLinkDirection !== b.ingressLinkDirection) { if (a.ingressLinkDirection !== b.ingressLinkDirection) {
return a.ingressLinkDirection.localeCompare(b.ingressLinkDirection, 'zh') return a.ingressLinkDirection.localeCompare(b.ingressLinkDirection)
} }
return a.egressLinkDirection.localeCompare(b.egressLinkDirection, 'zh') return a.egressLinkDirection.localeCompare(b.egressLinkDirection)
}) })
this.isNextNoData = nextLinkData.length === 0 this.isNextNoData = nextLinkData.length === 0
@@ -185,7 +182,7 @@ export default {
// 分数低于3分赋红点 // 分数低于3分赋红点
d.score = this.localComputeScore(d) d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3 || d.score === '-' d.scoreLow3 = d.score < 3
if (data) { if (data) {
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId) const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
@@ -228,11 +225,13 @@ export default {
this.nextErrorMsg = res[1].message this.nextErrorMsg = res[1].message
} }
}).catch(e => { }).catch(e => {
console.error(e)
this.isLinkShowError = true this.isLinkShowError = true
this.linkErrorMsg = e.message this.linkErrorMsg = e[0].message
this.isNextShowError = true this.isNextShowError = true
this.nextErrorMsg = e.message this.nextErrorMsg = e[1].message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="link-statistical-dimension" style="position: relative"> <div class="link-statistical-dimension" style="position: relative">
<div class="dimension-title" v-if="gridData.length>3">{{ $t('linkMonitor.egressLink') }}&nbsp;&&nbsp;{{ $t('linkMonitor.ingressLink') }}</div> <div class="dimension-title">{{ $t('linkMonitor.egressLink') }}&nbsp;&&nbsp;{{ $t('linkMonitor.ingressLink') }}
<div class="dimension-title" v-else>{{ $t('linkMonitor.nextHopInternetOfGrid') }}</div> </div>
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
@@ -21,13 +21,13 @@
<span v-if="row.nextHop">{{ row.nextHop }}</span> <span v-if="row.nextHop">{{ row.nextHop }}</span>
<span v-else>{{ row.linkId }}</span> <span v-else>{{ row.linkId }}</span>
</div> </div>
<div v-for="(item, index3) in row.egress" :key="index3"> <div v-for="(item, index2) in row.egress" :key="index2">
<el-popover :width="item.popoverWidth" placement="right" trigger="hover"> <el-popover :width="item.popoverWidth" placement="right" trigger="hover">
<template #reference> <template #reference>
<div class="data-item data-item__hover"> <div class="data-item data-item__hover">
<div :test-id="`usagePoint${gridData.length}-${index2+1}-${index3+1}`" :class="item.usageMore90 ? 'data-item__point-red':'data-item__point'"></div> <div :class="row.egress[index2].usageMore90 ? 'data-item__point-red':'data-item__point'"></div>
<div :test-id="`scorePoint${gridData.length}-${index2+1}-${index3+1}`" :class="item.scoreLow3 ? 'data-item__point-red':'data-item__point'"></div> <div :class="row.egress[index2].scoreLow3 ? 'data-item__point-red':'data-item__point'"></div>
</div> </div>
</template> </template>
@@ -35,13 +35,13 @@
<template #default> <template #default>
<div class="item-popover-header"> <div class="item-popover-header">
<!--兼容下一跳情况--> <!--兼容下一跳情况-->
<span v-if="row.nextHop" :test-id="`toNextHop${index2+1}`">{{ row.nextHop }}</span> <span v-if="row.nextHop">{{ row.nextHop }}</span>
<span v-else :test-id="`fromLinkId${index2+1}`">{{ row.linkId }}</span> <span v-else>{{ row.linkId }}</span>
<svg class="icon item-popover-header-icon" aria-hidden="true"> <svg class="icon item-popover-header-icon" aria-hidden="true">
<use xlink:href="#cn-icon-arrow-right2"></use> <use xlink:href="#cn-icon-arrow-right2"></use>
</svg> </svg>
<span v-if="row.nextHop" :test-id="`toNextHop${index3+1}`">{{ item.egressLinkDirection }}</span> <span v-if="row.nextHop">{{ row.egress[index2].egressLinkDirection }}</span>
<span v-else :test-id="`toLinkId${index3+1}`">{{ item.linkId }}</span> <span v-else>{{ row.egress[index2].linkId }}</span>
</div> </div>
<div class="item-popover-block"> <div class="item-popover-block">
@@ -49,24 +49,24 @@
<div style="display: flex"> <div style="display: flex">
<div class="row-dot"> <div class="row-dot">
<div :class="item.usageMore90 ? 'red-dot':'green-dot'"></div> <div :class="row.egress[index2].usageMore90 ? 'red-dot':'green-dot'"></div>
</div> </div>
<div class="item-popover-block-content"> <div class="item-popover-block-content">
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t('linkMonitor.bandwidthUsage') }}</div> <div class="block-content-item-name">{{ $t('linkMonitor.bandwidthUsage') }}</div>
<div class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
<div :test-id="`egressUsage${gridData.length}-${index2+1}-${index3+1}`" style="margin-left: -10px"> <div style="margin-left: -10px">
<svg class="icon item-popover-up" aria-hidden="true"> <svg class="icon item-popover-up" aria-hidden="true">
<use xlink:href="#cn-icon-egress"></use> <use xlink:href="#cn-icon-egress"></use>
</svg> </svg>
{{ convertValue(item.egressUsage) }} {{ convertValue(row.egress[index2].egressUsage) }}
</div> </div>
<div :test-id="`ingressUsage${gridData.length}-${index2+1}-${index3+1}`"> <div>
<svg class="icon item-popover-down" aria-hidden="true"> <svg class="icon item-popover-down" aria-hidden="true">
<use xlink:href="#cn-icon-ingress"></use> <use xlink:href="#cn-icon-ingress"></use>
</svg> </svg>
{{ convertValue(item.ingressUsage) }} {{ convertValue(row.egress[index2].ingressUsage) }}
</div> </div>
</div> </div>
</div> </div>
@@ -74,8 +74,8 @@
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t('linkMonitor.linkBlock.total') }}</div> <div class="block-content-item-name">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div :test-id="`totalBitsRate${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join('') }} {{ unitConvert(row.egress[index2].totalBitsRate, unitTypes.bps).join('') }}
</div> </div>
</div> </div>
</div> </div>
@@ -87,48 +87,48 @@
<div style="display: flex"> <div style="display: flex">
<div class="row-dot"> <div class="row-dot">
<div :class="item.scoreLow3 ? 'red-dot':'green-dot'"></div> <div :class="row.egress[index2].scoreLow3 ? 'red-dot':'green-dot'"></div>
</div> </div>
<div class="item-popover-block-content"> <div class="item-popover-block-content">
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t('linkMonitor.npmScore1') }}</div> <div class="block-content-item-name">{{ $t('linkMonitor.npmScore1') }}</div>
<div :test-id="`score${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{ item.score }} {{ row.egress[index2].score }}
</div> </div>
</div> </div>
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t(npmNetworkName[0].name) }}</div> <div class="block-content-item-name">{{ $t(npmNetworkName[0].name) }}</div>
<div :test-id="`tcp${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{ unitConvert(item.establishLatencyMs, unitTypes.time).join('') }} {{ unitConvert(row.egress[index2].establishLatencyMs, unitTypes.time).join('') }}
</div> </div>
</div> </div>
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t(npmNetworkName[1].name) }}</div> <div class="block-content-item-name">{{ $t(npmNetworkName[1].name) }}</div>
<div :test-id="`http${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{'width': item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{'width': row.egress[index2].valueWidth + 'px'}">
{{ unitConvert(item.httpResponseLatency, unitTypes.time).join('') }} {{ unitConvert(row.egress[index2].httpResponseLatency, unitTypes.time).join('') }}
</div> </div>
</div> </div>
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t(npmNetworkName[2].name) }}</div> <div class="block-content-item-name">{{ $t(npmNetworkName[2].name) }}</div>
<div :test-id="`ssl${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{ unitConvert(item.sslConLatency, unitTypes.time).join('') }} {{ unitConvert(row.egress[index2].sslConLatency, unitTypes.time).join('') }}
</div> </div>
</div> </div>
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t(npmNetworkName[3].name) }}</div> <div class="block-content-item-name">{{ $t(npmNetworkName[3].name) }}</div>
<div :test-id="`packetLoss${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{ unitConvert(item.tcpLostlenPercent, unitTypes.percent).join('') }} {{ unitConvert(row.egress[index2].tcpLostlenPercent, unitTypes.percent).join('') }}
</div> </div>
</div> </div>
<div class="block-content-item"> <div class="block-content-item">
<div class="block-content-item-name">{{ $t(npmNetworkName[4].name) }}</div> <div class="block-content-item-name">{{ $t(npmNetworkName[4].name) }}</div>
<div :test-id="`packetRetrans${gridData.length}-${index2+1}-${index3+1}`" class="block-content-item-value" :style="{width: item.valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{ unitConvert(item.pktRetransPercent, unitTypes.percent).join('') }} {{ unitConvert(row.egress[index2].pktRetransPercent, unitTypes.percent).join('') }}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,7 +2,7 @@
<div class="link-traffic-line"> <div class="link-traffic-line">
<link-traffic-drill-down-list <link-traffic-drill-down-list
:chart="chart" :chart="chart"
:line-data="tabs[0]" :line-data="mpackets"
:time-filter="timeFilter"> :time-filter="timeFilter">
</link-traffic-drill-down-list> </link-traffic-drill-down-list>
<div class="line network link-traffic"> <div class="line network link-traffic">
@@ -12,24 +12,22 @@
<div class="line-header-left"> <div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div> <div class="line-value-active" v-if="lineTab"></div>
<div class="line-value"> <div class="line-value">
<div class="line-value-tabs" <div class="line-value-mpackets"
v-show="item.show" v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}" :class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in tabs" v-for="(item, index) in mpackets"
:key="index" :key="index"
@mouseenter="mouseenter(item)" @mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)" @mouseleave="mouseleave(item)"
@click="activeChange(item, index)" @click="activeChange(item, index)">
:test-id="`tab-${index}`"> <div class="line-value-mpackets-name">
<div class="line-value-tabs-name">
<div :class="item.class"></div> <div :class="item.class"></div>
<div class="tabs-name">{{$t(item.name)}}</div> <div class="mpackets-name">{{$t(item.name)}}</div>
</div> </div>
<div class="line-value-unit" :test-id="`tabContent${index}`"> <div class="line-value-unit">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span> <span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2"> <span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span> <span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span> </span>
</div> </div>
</div> </div>
@@ -69,14 +67,14 @@ import { useRoute } from 'vue-router'
import { ref, shallowRef } from 'vue' import { ref, shallowRef } from 'vue'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { chartColor3, chartColor4, unitTypes } from '@/utils/constants' import { chartColor3, chartColor4, unitTypes } from '@/utils/constants'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { linkTrafficLineChartOption } from '@/views/charts2/charts/options/echartOption' import { linkTrafficLineChartOption } from '@/views/charts2/charts/options/echartOption'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools' import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import axios from 'axios'
export default { export default {
name: 'LinkTrafficLine', name: 'LinkTrafficLine',
@@ -114,7 +112,7 @@ export default {
label: 'Packets/s' label: 'Packets/s'
} }
], ],
tabs: [ mpackets: [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' }, { analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' } { analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -178,21 +176,105 @@ export default {
} }
} }
this.loading = true this.loading = true
axios.get(api.linkMonitor.totalTrafficAnalysis, { params: params }).then((res) => { get(api.linkMonitor.totalTrafficAnalysis, params).then((res) => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.showError = false this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
if (this.isNoData) { if (this.isNoData) {
this.lineTab = '' this.lineTab = ''
this.tabs = [ this.mpackets = [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' }, { analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' } { analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
] ]
} else {
this.initData(res.data.result, val, active, show)
} }
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis
mpackets[1].analysis = t.ingressBitsRate.analysis
mpackets[2].analysis = t.egressBitsRate.analysis
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.ingressBitsRate.values ? t.ingressBitsRate.values : []
mpackets[2].data = t.egressBitsRate.values ? t.egressBitsRate.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'bps'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 3) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalPacketsRate.analysis
mpackets[1].analysis = t.ingressPacketsRate.analysis
mpackets[2].analysis = t.egressPacketsRate.analysis
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.ingressPacketsRate.values ? t.ingressPacketsRate.values : []
mpackets[2].data = t.egressPacketsRate.values ? t.egressPacketsRate.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'packets/s'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 3) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
}
})
} else { } else {
this.showError = true this.showError = true
this.errorMsg = res.message this.errorMsg = res.message
@@ -207,7 +289,6 @@ export default {
}) })
}, },
echartsInit (echartsData) { echartsInit (echartsData) {
if (!this.isUnitTesting) {
if (this.lineTab) { if (this.lineTab) {
this.handleActiveBar() this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false) echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
@@ -258,7 +339,7 @@ export default {
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.tabs.forEach(e => { this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) { if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning] t.borderColor = chartColor3[e.positioning]
} }
@@ -269,7 +350,6 @@ export default {
} }
this.showMarkLine = true this.showMarkLine = true
this.myChart.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
}
}, },
activeChange (item, index) { activeChange (item, index) {
if (this.isNoData) return if (this.isNoData) return
@@ -301,16 +381,16 @@ export default {
legendSelectChange (item, index, val) { legendSelectChange (item, index, val) {
if (index === 'index') { if (index === 'index') {
this.dispatchLegendSelectAction(item.name) this.dispatchLegendSelectAction(item.name)
} else if (this.tabs[index] && this.tabs[index].name === item.name) { } else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
this.dispatchLegendSelectAction(item.name) this.dispatchLegendSelectAction(item.name)
this.tabs.forEach((t) => { this.mpackets.forEach((t) => {
if (t.name !== item.name) { if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name) this.dispatchLegendUnSelectAction(t.name)
} }
}) })
} }
if (val === 'active') { if (val === 'active') {
this.tabs.forEach(t => { this.mpackets.forEach(t => {
if (item.name === t.name) { if (item.name === t.name) {
t.invertTab = !t.invertTab t.invertTab = !t.invertTab
} else { } else {
@@ -322,7 +402,7 @@ export default {
} else { } else {
this.lineTab = t.class this.lineTab = t.class
} }
this.tabs.forEach((e) => { this.mpackets.forEach((e) => {
this.dispatchLegendSelectAction(e.name) this.dispatchLegendSelectAction(e.name)
}) })
} }
@@ -330,8 +410,8 @@ export default {
} }
}, },
handleActiveBar () { handleActiveBar () {
if (document.querySelector('.network .line-value-tabs.is-active')) { if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active') const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
const activeBar = document.querySelector('.network .line-value-active') const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;` activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
} }
@@ -344,7 +424,7 @@ export default {
this.lineTab = '' this.lineTab = ''
this.handleActiveBar() this.handleActiveBar()
this.showMarkLine = !this.showMarkLine this.showMarkLine = !this.showMarkLine
this.tabs.forEach((e) => { this.mpackets.forEach((e) => {
if (!e.invertTab) { if (!e.invertTab) {
e.invertTab = true e.invertTab = true
} }
@@ -377,71 +457,12 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] }) dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
},
initData (data, val, active, show) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys(item).map(t => {
return {
...item[t]
}
})
}
})
}
lineData.splice(0, 1)
const tabs = _.cloneDeep(this.tabs)
const unit = val === 'Bits/s' ? 'bps' : 'packets/s'
this.legendInit(lineData, active, show, unit, tabs)
},
legendInit (data, active, show, type, linkData) {
data.forEach((d, i) => {
linkData[i].data = d.values
linkData[i].analysis = d.analysis
})
let num = 0
linkData.forEach(e => {
e.unitType = type
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.tabs = linkData
if (num === 3) {
linkData[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(linkData[0], 0)
this.$nextTick(() => {
this.echartsInit(this.tabs)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.tabs)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} }
}, },
mounted () { mounted () {
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
if (this.lineTab) { if (this.lineTab) {
const data = this.tabs.find(t => t.class === this.lineTab) const data = this.mpackets.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning) this.activeChange(data, data.positioning)
} else { } else {
this.init() this.init()
@@ -452,9 +473,7 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart)
}
this.chartOption = null this.chartOption = null
this.unitConvert = null this.unitConvert = null
} }

View File

@@ -102,12 +102,15 @@ export default {
if (condition.length > 1) { if (condition.length > 1) {
if (n === 0) { if (n === 0) {
params.q = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1) params.q = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1)
url = api.linkMonitor.drilldownQuadrupleIngressAnalysis // 入口
} else { } else {
params.q = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1) params.q = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1)
url = api.linkMonitor.drilldownQquadrupleEgressAnalysis // 出口
} }
} }
if (n === 0) {
url = api.linkMonitor.drilldownQuadrupleIngressAnalysis // 入口
} else {
url = api.linkMonitor.drilldownQquadrupleEgressAnalysis // 出口
}
} else { } else {
if (n === 0) { if (n === 0) {
url = api.linkMonitor.quadrupleIngressAnalysis // 入口 url = api.linkMonitor.quadrupleIngressAnalysis // 入口

View File

@@ -6,42 +6,36 @@
<div v-else> <div v-else>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('network.total')}}</div> <div class="link-traffic-list-center-label">{{$t('network.total')}}</div>
<div class="link-traffic-list-center-value" v-if="lineData && lineData.analysis" test-id="line-tabContent"> <div class="link-traffic-list-center-value" v-if="lineData[0] && lineData[0].analysis">{{unitConvert(lineData[0].analysis.avg, unitTypes.bps).join('')}}</div>
<span>{{unitConvert(lineData.analysis.avg, unitTypes.number)[0]}}</span>
<span>
<span>{{unitConvert(lineData.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="lineData.unitType">{{lineData.unitType}}</span>
</span>
</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('linkMonitor.bandwidthUsage')}}</div> <div class="link-traffic-list-center-label">{{$t('linkMonitor.bandwidthUsage')}}</div>
<div class="link-traffic-list-center-value" v-if="lineData && lineData.analysis && bandWidth" test-id="line-percent">{{unitConvert(lineData.analysis.avg / bandWidth, unitTypes.percent).join('')}}</div> <div class="link-traffic-list-center-value" v-if="bandWidth && lineData[0] && lineData[0].analysis">{{unitConvert(lineData[0].analysis.avg / bandWidth, unitTypes.percent).join('')}}</div>
<div class="link-traffic-list-center-value" v-else>-</div> <div class="link-traffic-list-center-value" v-else>-</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('linkMonitor.npmScore')}}</div> <div class="link-traffic-list-center-label">{{$t('linkMonitor.npmScore')}}</div>
<div class="link-traffic-list-center-value" test-id="line-score">{{linkTrafficListData.npmScore || '-'}}</div> <div class="link-traffic-list-center-value">{{linkTrafficListData.npmScore || '-'}}</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.tcpConnectionEstablishLatency')}}</div> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.tcpConnectionEstablishLatency')}}</div>
<div class="link-traffic-list-center-value" test-id="line-tcp">{{unitConvert(Math.floor(linkTrafficListData.establishLatencyMs), unitTypes.time).join('')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.establishLatencyMs), unitTypes.time).join('')}}</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.httpResponse')}}</div> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.httpResponse')}}</div>
<div class="link-traffic-list-center-value" test-id="line-http">{{unitConvert(Math.floor(linkTrafficListData.httpResponseLatency), unitTypes.time).join('')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.httpResponseLatency), unitTypes.time).join('')}}</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.sslResponseLatency')}}</div> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.sslResponseLatency')}}</div>
<div class="link-traffic-list-center-value" test-id="line-ssl">{{unitConvert(Math.floor(linkTrafficListData.sslConLatency), unitTypes.time).join('')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.sslConLatency), unitTypes.time).join('')}}</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.packetLoss')}}</div> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.packetLoss')}}</div>
<div class="link-traffic-list-center-value" test-id="line-packetLoss">{{unitConvert(linkTrafficListData.tcpLostlenPercent, unitTypes.percent).join('')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(linkTrafficListData.tcpLostlenPercent, unitTypes.percent).join('')}}</div>
</div> </div>
<div class="link-traffic-list-center"> <div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('overall.packetRetrans')}}</div> <div class="link-traffic-list-center-label">{{$t('overall.packetRetrans')}}</div>
<div class="link-traffic-list-center-value" test-id="line-packetRetrans">{{unitConvert(linkTrafficListData.pktRetransPercent, unitTypes.percent).join('')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(linkTrafficListData.pktRetransPercent, unitTypes.percent).join('')}}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -51,6 +45,7 @@
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { storageKey, unitTypes } from '@/utils/constants' import { storageKey, unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { ref } from 'vue' import { ref } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
@@ -58,7 +53,6 @@ import { getSecond } from '@/utils/date-util'
import { computeScore } from '@/utils/tools' import { computeScore } from '@/utils/tools'
import Loading from '@/components/common/Loading' import Loading from '@/components/common/Loading'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios'
export default { export default {
name: 'linkTrafficList', name: 'linkTrafficList',
mixins: [chartMixin], mixins: [chartMixin],
@@ -142,8 +136,7 @@ export default {
} }
} }
this.loading = true this.loading = true
axios.get(api.linkMonitor.networkAnalysis, { params: params }).then(res => { get(api.linkMonitor.networkAnalysis, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.showError = false this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
@@ -172,9 +165,6 @@ export default {
}, },
mounted () { mounted () {
this.linkTrafficData() this.linkTrafficData()
},
beforeUnmount () {
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -6,31 +6,31 @@
</div> </div>
<div class="app-cards"> <div class="app-cards">
<div class="app-card" @mouseenter="mouseenter(app)" @mouseleave="mouseleave(app)" v-for="(app, index) in appData" :key="app.type + app.name" test-id="app-data-card"> <div class="app-card" @mouseenter="mouseenter(app)" @mouseleave="mouseleave(app)" v-for="(app, index) in appData" :key="app.type + app.name">
<div class="app-card-title"> <div class="app-card-title">
<div class="app-card-title-name"> <div class="app-card-title-name">
<i class="cn-icon" :class="app.type === 'provider' ? 'cn-icon-entity' : 'cn-icon-app2'" :test-id="`icon${index}`"></i> <i class="cn-icon" :class="app.type === 'provider' ? 'cn-icon-entity' : 'cn-icon-app2'"></i>
<span @click="drillDownData(app.type, app.name)" :test-id="`name${index}`">{{app.name}}</span> <span @click="drillDownData(app.type, app.name)">{{app.name}}</span>
</div> </div>
<div class="app-card-title-more"> <div class="app-card-title-more">
<span v-show="app.showMore"><i class="cn-icon cn-icon-more-dark" @mouseenter="mouseenterMore(app)" test-id="mouseenter-dark"></i></span> <span v-show="app.showMore"><i class="cn-icon cn-icon-more-dark" @mouseenter="mouseenterMore(app)"></i></span>
<span class="app-card-title-more-delete" @click="del(app, index)" v-show="app.moreOptions" @mouseleave="mouseleaveMore(app)" test-id="mouseleave-more"><i class="cn-icon cn-icon-delete"></i>{{$t('overall.delete')}}</span> <span class="app-card-title-more-delete" @click="del(app, index)" v-show="app.moreOptions" @mouseleave="mouseleaveMore(app)"><i class="cn-icon cn-icon-delete"></i>{{$t('overall.delete')}}</span>
</div> </div>
</div> </div>
<div class="app-card__bodys"> <div class="app-card__bodys">
<div class="app-card__body"> <div class="app-card__body">
<div class="app-card__body-content"> <div class="app-card__body-content">
<div class="app-card__body-content-value"> <div class="app-card__body-content-value">
<div class="app-card__body-content-number" :test-id="`rate${index}`">{{unitConvert(app.rate, unitTypes.number).join(' ')}}</div> <div class="app-card__body-content-number">{{unitConvert(app.rate, unitTypes.number).join(' ')}}</div>
<div class="app-card__body-content-percent red" v-if="app.value > 0" :test-id="`percent${index}`"> <div class="app-card__body-content-percent red" v-if="app.value > 0">
<span v-if="app.value <= 5"> <span v-if="app.value <= 5">
+{{unitConvert(app.value, unitTypes.percent).join('')}} +{{unitConvert(app.value, unitTypes.percent).join('')}}
</span> </span>
<span v-else>>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
<div class="app-card__body-content-percent green" v-else-if="app.value < 0" :test-id="`percent${index}`"> <div class="app-card__body-content-percent green" v-else-if="app.value < 0">
<span v-if="app.value >= -5"> <span v-if="app.value >= -5">
-{{unitConvert(app.value, unitTypes.percent).join('').replace(/-/g, '')}} -{{unitConvert(app.value, unitTypes.percent).join('').replaceAll('-', '')}}
</span> </span>
<span v-else>>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
@@ -39,8 +39,8 @@
</div> </div>
<div class="app-card__body-previous"> <div class="app-card__body-previous">
<div>Total</div> <div>Total</div>
<div v-if="metric === 'Bits/s'" :test-id="`total${index}`">{{unitConvert(app.total, unitTypes.byte).join(' ')}}</div> <div v-if="metric === 'Bits/s'">{{unitConvert(app.total, unitTypes.byte).join(' ')}}</div>
<div v-else :test-id="`total${index}`">{{unitConvert(app.total, unitTypes.number).join(' ')}}</div> <div v-else>{{unitConvert(app.total, unitTypes.number).join(' ')}}</div>
</div> </div>
</div> </div>
<div class="chart__drawing" v-show="!isNoData" :id="`chart-${app.name}-${app.type}`"></div> <div class="chart__drawing" v-show="!isNoData" :id="`chart-${app.name}-${app.type}`"></div>
@@ -48,7 +48,7 @@
</div> </div>
<div class="app-card app-card--create"> <div class="app-card app-card--create">
<i class="cn-icon cn-icon-add1" @click="addApp"></i> <i class="cn-icon cn-icon-add1" @click="addApp"></i>
<span @click="addApp" test-id="add">{{$t('overall.add')}}</span> <span @click="addApp">{{$t('overall.add')}}</span>
</div> </div>
</div> </div>
<el-drawer <el-drawer
@@ -62,7 +62,7 @@
<div class="add-app__header"> <div class="add-app__header">
<div class="header__title">{{$t('overall.add')}}</div> <div class="header__title">{{$t('overall.add')}}</div>
<div class="header__operations"> <div class="header__operations">
<div class="header__operation header__operation--cancel" @click="cancelApp" test-id="cancel-app">{{$t('overall.cancel')}}</div> <div class="header__operation header__operation--cancel" @click="cancelApp">{{$t('overall.cancel')}}</div>
<div class="header__operation header__operation--save" @click="save">{{$t('overall.save')}}</div> <div class="header__operation header__operation--save" @click="save">{{$t('overall.save')}}</div>
</div> </div>
</div> </div>
@@ -72,15 +72,15 @@
<div class="body__apps" :class="{'body__apps-no-grid': providerOptions.length === 0}"> <div class="body__apps" :class="{'body__apps-no-grid': providerOptions.length === 0}">
<loading :loading="loadingBody"></loading> <loading :loading="loadingBody"></loading>
<chart-no-data v-if="providerOptions.length === 0 && !loadingBody"></chart-no-data> <chart-no-data v-if="providerOptions.length === 0 && !loadingBody"></chart-no-data>
<div class="body__app" v-else :class="{'provide-show': app.provideShow}" v-for="(app, index) in providerOptions" :key="index" @click="appCheckedChange(app, 0)" :test-id="`provide${index}`"> <div class="body__app" v-else :class="{'provide-show': app.provideShow}" v-for="(app, index) in providerOptions" :key="index" @click="appCheckedChange(app, 0)">
<div class="body__app-content"> <div class="body__app-content">
<div class="body__app-left"> <div class="body__app-left">
<span><i class="cn-icon" :class="app.icon" :test-id="`provide-icon${index}`"></i></span> <span><i class="cn-icon" :class="app.icon"></i></span>
<span class="body__app-left-title" :test-id="`provide-title${index}`">{{app.value}}</span> <span class="body__app-left-title">{{app.value}}</span>
</div> </div>
<div class="body__app-content-right" v-if="app.provideShow"><span><i class="cn-icon cn-icon-a-allclear"></i></span></div> <div class="body__app-content-right" v-if="app.provideShow"><span><i class="cn-icon cn-icon-a-allclear"></i></span></div>
</div> </div>
<div class="body__app-value" v-if="app.remark" :title="app.remark" :test-id="`provide-remark${index}`">{{app.remark}}</div> <div class="body__app-value" v-if="app.remark" :title="app.remark">{{app.remark}}</div>
<div v-else>-</div> <div v-else>-</div>
</div> </div>
</div> </div>
@@ -89,22 +89,22 @@
<div class="body__apps" :class="{'body__apps-no-grid': appOptions.length === 0}"> <div class="body__apps" :class="{'body__apps-no-grid': appOptions.length === 0}">
<loading :loading="loadingBody"></loading> <loading :loading="loadingBody"></loading>
<chart-no-data v-if="appOptions.length === 0 && !loadingBody"></chart-no-data> <chart-no-data v-if="appOptions.length === 0 && !loadingBody"></chart-no-data>
<div class="body__app" v-else :class="{'app-show': app.appShow}" v-for="(app, index) in appOptions" :key="index" @click="appCheckedChange(app, 1)" :test-id="`app${index}`"> <div class="body__app" v-else :class="{'app-show': app.appShow}" v-for="(app, index) in appOptions" :key="index" @click="appCheckedChange(app, 1)">
<div class="body__app-content"> <div class="body__app-content">
<div class="body__app-left"> <div class="body__app-left">
<span><i class="cn-icon" :class="app.icon" :test-id="`app-icon${index}`"></i></span> <span><i class="cn-icon" :class="app.icon"></i></span>
<span class="body__app-left-title" :test-id="`app-title${index}`">{{app.value}}</span> <span class="body__app-left-title">{{app.value}}</span>
</div> </div>
<div class="body__app-content-right" v-if="app.appShow"><span><i class="cn-icon cn-icon-a-allclear"></i></span></div> <div class="body__app-content-right" v-if="app.appShow"><span><i class="cn-icon cn-icon-a-allclear"></i></span></div>
</div> </div>
<div class="body__app-value" v-if="app.remark" :title="app.remark" :test-id="`app-remark${index}`">{{app.remark}}</div> <div class="body__app-value" v-if="app.remark" :title="app.remark">{{app.remark}}</div>
<div v-else>-</div> <div v-else>-</div>
</div> </div>
</div> </div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<div class="body__searcher"> <div class="body__searcher">
<el-input v-model="searcherApp" @input="searcherAppChange" size="mini" :placeholder="$t('networkOverview.search')" prefix-icon="el-icon-search" test-id="search-input"></el-input> <el-input v-model="searcherApp" @input="searcherAppChange" size="mini" :placeholder="$t('networkOverview.search')" prefix-icon="el-icon-search"></el-input>
</div> </div>
<div class="body__loading"><loading :loading="loading"></loading></div> <div class="body__loading"><loading :loading="loading"></loading></div>
</div> </div>
@@ -119,7 +119,7 @@ import { storageKey, unitTypes, networkTable, operationType, curTabState } from
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { appListChartOption } from '@/views/charts2/charts/options/echartOption' import { appListChartOption } from '@/views/charts2/charts/options/echartOption'
import { shallowRef } from 'vue' import { shallowRef } from 'vue'
import { put } from '@/utils/http' import { get, put } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
@@ -128,7 +128,6 @@ import loading from '@/components/common/Loading'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import { appStackedLineTooltipFormatter } from '@/views/charts/charts/tools' import { appStackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import axios from 'axios'
export default { export default {
name: 'NetworkOverviewApps', name: 'NetworkOverviewApps',
@@ -153,6 +152,7 @@ export default {
searcherApp: '', searcherApp: '',
// 选中的app不区分app和provider // 选中的app不区分app和provider
toSaveApp: [], toSaveApp: [],
appShowType: 'bytes',
pageObj: { // 分页对象 pageObj: { // 分页对象
pageNo: 1, pageNo: 1,
pageSize: 24, pageSize: 24,
@@ -228,8 +228,8 @@ export default {
return `'${item.name}'` return `'${item.name}'`
}).join(',') }).join(',')
} }
prevRequest = axios.get(api.netWorkOverview.applicationCycleTrafficTotal, { params: params }) prevRequest = get(api.netWorkOverview.applicationCycleTrafficTotal, params)
request = axios.get(api.netWorkOverview.applicationTrafficAnalysis, { params: params }) request = get(api.netWorkOverview.applicationTrafficAnalysis, params)
this.handleData(prevRequest, request, 'app') this.handleData(prevRequest, request, 'app')
} }
if (providerCards.length > 0) { if (providerCards.length > 0) {
@@ -240,15 +240,15 @@ export default {
return `'${item.name}'` return `'${item.name}'`
}).join(',') }).join(',')
} }
prevRequest = axios.get(api.netWorkOverview.appCompanyCycleTrafficTotal, { params: params }) prevRequest = get(api.netWorkOverview.appCompanyCycleTrafficTotal, params)
request = axios.get(api.netWorkOverview.appCompanyTrafficAnalysis, { params: params }) request = get(api.netWorkOverview.appCompanyTrafficAnalysis, params)
this.handleData(prevRequest, request, 'provider') this.handleData(prevRequest, request, 'provider')
} }
}, },
handleData (prevRequest, request, _t) { handleData (prevRequest, request, _t) {
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([prevRequest, request]).then(res => { Promise.all([prevRequest, request]).then(res => {
this.isNoData = (res[0].data.data.result.length && res[1].data.data.result.length) === 0 this.isNoData = (res[0].data.result.length && res[1].data.result.length) === 0
if (this.isNoData) { if (this.isNoData) {
this.appData = this.appData.map(t => { this.appData = this.appData.map(t => {
return { return {
@@ -257,10 +257,10 @@ export default {
} }
}) })
} }
if (res[0].data.code === 200 && res[1].data.code === 200) { if (res[0].code === 200 && res[1].code === 200) {
this.showError = false this.showError = false
const prevData = res[0].data.data.result const prevData = res[0].data.result
const data = res[1].data.data.result const data = res[1].data.result
let toCompareType = 'bytes' let toCompareType = 'bytes'
if (this.metric === 'Sessions/s') { if (this.metric === 'Sessions/s') {
toCompareType = 'sessions' toCompareType = 'sessions'
@@ -298,6 +298,16 @@ export default {
this.toggleLoading(false) this.toggleLoading(false)
}) })
}, },
metricChange (value) {
if (value === 'Bits/s') {
this.appShowType = 'bytes'
} else if (value === 'Packets/s') {
this.appShowType = 'packets'
} else if (value === 'Sessions/s') {
this.appShowType = 'sessions'
}
this.init()
},
getUrlParam (param, defaultValue, isNumber) { getUrlParam (param, defaultValue, isNumber) {
if (isNumber) { if (isNumber) {
return this.$route.query[param] ? Number(this.$route.query[param]) : defaultValue return this.$route.query[param] ? Number(this.$route.query[param]) : defaultValue
@@ -391,7 +401,6 @@ export default {
this.urlChangeParams = {} this.urlChangeParams = {}
}, },
initChart (obj) { initChart (obj) {
if (!this.isUnitTesting) {
let chart = this.myChart.find(m => m.name === obj.name && m.type === obj.type) let chart = this.myChart.find(m => m.name === obj.name && m.type === obj.type)
if (!chart) { if (!chart) {
chart = echarts.init(document.getElementById(`chart-${obj.name}-${obj.type}`)) chart = echarts.init(document.getElementById(`chart-${obj.name}-${obj.type}`))
@@ -425,7 +434,6 @@ export default {
chart.resize() chart.resize()
}) })
} }
}
}, },
handleScroll (e) { handleScroll (e) {
const clientHeight = e.target.clientHeight const clientHeight = e.target.clientHeight
@@ -461,8 +469,7 @@ export default {
} }
if (parseFloat(this.appTypeTab) === 0) { if (parseFloat(this.appTypeTab) === 0) {
params.type = 'overviewProvide' params.type = 'overviewProvide'
axios.get(api.dict, { params: params }).then(res => { get(api.dict, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
res.data.list = res.data.list.filter(l => !this.appData.some(pd => pd.type === 'provider' && pd.name === l.value)) res.data.list = res.data.list.filter(l => !this.appData.some(pd => pd.type === 'provider' && pd.name === l.value))
this.pageObj.pages = res.data.pages this.pageObj.pages = res.data.pages
@@ -487,10 +494,9 @@ export default {
}) })
} else if (parseFloat(this.appTypeTab) === 1) { } else if (parseFloat(this.appTypeTab) === 1) {
params.type = 'overviewApp' params.type = 'overviewApp'
axios.get(api.dict, { params: params }).then(res => { get(api.dict, params).then(res => {
res = res.data
if (res.code === 200) {
res.data.list = res.data.list.filter(l => !this.appData.some(pd => pd.type === 'app' && pd.name === l.value)) res.data.list = res.data.list.filter(l => !this.appData.some(pd => pd.type === 'app' && pd.name === l.value))
if (res.code === 200) {
this.pageObj.pages = res.data.pages this.pageObj.pages = res.data.pages
res.data.list.forEach(t => { res.data.list.forEach(t => {
this.toSaveApp.forEach(e => { this.toSaveApp.forEach(e => {
@@ -669,6 +675,13 @@ export default {
}) })
} }
}, },
// moreChange (app) {
// this.appData.forEach(t => {
// if (t.name === app.name && t.type === app.type) {
// t.moreOptions = !t.moreOptions
// }
// })
// },
resize () { resize () {
this.myChart.forEach(t => { this.myChart.forEach(t => {
t.resize() t.resize()
@@ -705,7 +718,7 @@ export default {
} }
}, },
mounted () { mounted () {
if (this.chart && this.chart.params && this.chart.params.app) { if (this.chart.params && this.chart.params.app) {
const userId = parseInt(localStorage.getItem(storageKey.userId)) const userId = parseInt(localStorage.getItem(storageKey.userId))
const apps = _.cloneDeep(this.chart.params.app) const apps = _.cloneDeep(this.chart.params.app)
let app = apps.find(p => p.user === userId) let app = apps.find(p => p.user === userId)
@@ -721,9 +734,7 @@ export default {
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
clearTimeout(this.timerScroll) clearTimeout(this.timerScroll)
clearTimeout(this.timerSearch) clearTimeout(this.timerSearch)
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart)
}
this.unitConvert = null this.unitConvert = null
} }
} }

View File

@@ -11,15 +11,15 @@
<div class="ddos-detection-type"> <div class="ddos-detection-type">
<div class="ddos-detection-type-value"> <div class="ddos-detection-type-value">
<div class="ddos-detection-type-value-name">{{$t('network.numberOfAttacks')}}</div> <div class="ddos-detection-type-value-name">{{$t('network.numberOfAttacks')}}</div>
<div class="ddos-detection-type-value-number" test-id="attackerCount">{{unitConvert($_.get(ddosData, 'attackerCount'), unitTypes.number).join(' ')}}</div> <div class="ddos-detection-type-value-number">{{$_.get(ddosData, 'attackerCount') || 0}}</div>
</div> </div>
<div class="ddos-detection-type-value"> <div class="ddos-detection-type-value">
<div class="ddos-detection-type-value-name">{{$t('network.number0fVictims')}}</div> <div class="ddos-detection-type-value-name">{{$t('network.number0fVictims')}}</div>
<div class="ddos-detection-type-value-number" test-id="victimCount">{{unitConvert($_.get(ddosData, 'victimCount'), unitTypes.number).join(' ')}}</div> <div class="ddos-detection-type-value-number">{{$_.get(ddosData, 'victimCount') || 0}}</div>
</div> </div>
<div class="ddos-detection-type-value"> <div class="ddos-detection-type-value">
<div class="ddos-detection-type-value-name">{{$t('network.number0fDetectedAttackEvents')}}</div> <div class="ddos-detection-type-value-name">{{$t('network.number0fDetectedAttackEvents')}}</div>
<div class="ddos-detection-type-value-number ddos-event" test-id="attackEventCount">{{unitConvert($_.get(ddosData, 'attackEventCount'), unitTypes.number).join(' ')}}</div> <div class="ddos-detection-type-value-number ddos-event">{{$_.get(ddosData, 'attackEventCount') || 0}}</div>
</div> </div>
</div> </div>
<el-button size="small">{{$t('network.ddosDetection')}}<i class="cn-icon cn-icon-arrow-right"></i></el-button> <el-button size="small">{{$t('network.ddosDetection')}}<i class="cn-icon cn-icon-arrow-right"></i></el-button>
@@ -29,13 +29,11 @@
<script> <script>
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { get } from '@/utils/http'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import unitConvert from '@/utils/unit-convert'
import { unitTypes } from '@/utils/constants'
import axios from 'axios'
export default { export default {
name: 'NetworkOverviewDdosDetection', name: 'NetworkOverviewDdosDetection',
components: { components: {
@@ -46,8 +44,6 @@ export default {
data () { data () {
return { return {
ddosData: {}, ddosData: {},
unitConvert,
unitTypes,
isNoData: false, isNoData: false,
showError: false, showError: false,
errorMsg: '' errorMsg: ''
@@ -63,16 +59,19 @@ export default {
methods: { methods: {
ddosDetectDataRequests () { ddosDetectDataRequests () {
const params = { const params = {
startTime: this.timeFilter && this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : '', startTime: getSecond(this.timeFilter.startTime),
endTime: this.timeFilter && this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : '' endTime: getSecond(this.timeFilter.endTime)
} }
this.toggleLoading(true) this.toggleLoading(true)
axios.get(api.netWorkOverview.ddosEventAnalysis, { params: params }).then(res => { get(api.netWorkOverview.ddosEventAnalysis, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.showError = false this.showError = false
this.isNoData = res.data.result.length === 0 if (res.data.result.length === 0) {
this.isNoData = true
} else {
this.ddosData = res.data.result[0] this.ddosData = res.data.result[0]
this.isNoData = false
}
} else { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
@@ -88,12 +87,7 @@ export default {
} }
}, },
mounted () { mounted () {
this.$nextTick(() => {
this.ddosDetectDataRequests() this.ddosDetectDataRequests()
})
},
beforeUnmount () {
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -5,29 +5,25 @@
<div class="line-header-left"> <div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div> <div class="line-value-active" v-if="lineTab"></div>
<div class="line-value"> <div class="line-value">
<template v-for="(item, index) in tabs"> <div class="line-value-mpackets"
<div class="line-value-tabs" v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}" :class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-if="item.show" v-for="(item, index) in mpackets"
:key="index" :key="index"
@mouseenter="mouseenter(item)" @mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)" @mouseleave="mouseleave(item)"
@click="activeChange(item, index)" @click="activeChange(item, index)">
:test-id="`tab${index}`" <div class="line-value-mpackets-name">
>
<div class="line-value-tabs-name">
<div :class="item.class"></div> <div :class="item.class"></div>
<div class="tabs-name" :test-id="`tabTitle${index}`">{{$t(item.name)}}</div> <div class="mpackets-name">{{$t(item.name)}}</div>
</div> </div>
<div class="line-value-unit" :test-id="`tabContent${index}`"> <div class="line-value-unit">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span> <span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2"> <span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span> <span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span> </span>
</div> </div>
</div> </div>
</template>
</div> </div>
</div> </div>
<div class="line-select line-header-right"> <div class="line-select line-header-right">
@@ -50,7 +46,7 @@
</div> </div>
<div style="height: calc(100% - 74px); position: relative"> <div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData && !showError"></chart-no-data> <chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" ref="overviewLineChart"></div> <div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" id="overviewLineChart"></div>
<!-- todo 后续改动此处为框选返回--> <!-- todo 后续改动此处为框选返回-->
<!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">--> <!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">-->
<!-- <el-button @click.stop="backBrushHistory">返回</el-button>--> <!-- <el-button @click.stop="backBrushHistory">返回</el-button>-->
@@ -67,16 +63,14 @@ import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
import { ref, shallowRef } from 'vue' import { ref, shallowRef } from 'vue'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools' import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import _ from 'lodash' import _ from 'lodash'
import axios from 'axios' import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import mockData from '../../../../../test/views/charts2/charts/networkOverview/NetworkOverviewLineMockData'
export default { export default {
name: 'NetworkOverviewLine', name: 'NetworkOverviewLine',
components: { components: {
@@ -122,7 +116,7 @@ export default {
label: 'Maximum' label: 'Maximum'
} }
], ],
tabsTemplate: [ mpackets: [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' }, { analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }, { analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' },
@@ -130,7 +124,6 @@ export default {
{ analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' }, { analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' },
{ analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' } { analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' }
], ],
tabs: [],
unitConvert, unitConvert,
unitTypes, unitTypes,
chartDateObject: [], chartDateObject: [],
@@ -176,7 +169,7 @@ export default {
metric (n) { metric (n) {
this.handleActiveBar() this.handleActiveBar()
this.showMarkLine = !this.showMarkLine this.showMarkLine = !this.showMarkLine
this.tabs.forEach((e) => { this.mpackets.forEach((e) => {
if (!e.invertTab) { if (!e.invertTab) {
e.invertTab = true e.invertTab = true
} }
@@ -193,23 +186,161 @@ export default {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
if (this.queryCondition) { let condition = ''
if (this.queryCondition && this.tabOperationType !== '3') {
params.q = this.queryCondition params.q = this.queryCondition
} else if (this.tabOperationType == '3' && this.queryCondition) {
if (this.queryCondition.indexOf(' OR ') > -1) {
if (this.networkOverviewBeforeTab === 'isp') {
condition = this.queryCondition.split(/["|'= ](.*?)["|'= ]/)
params.q = `notEmpty(${condition[0]}) OR notEmpty(${condition[9]})`
} else {
condition = this.queryCondition.split(/["|'= ](.*?)["|'= ]/)
params.q = `notEmpty(${condition[0]}) OR notEmpty(${condition[5]})`
}
} else {
condition = this.queryCondition.split(/['=](.*?)['=]/)
params.q = `notEmpty(${condition[0]})`
}
} }
this.toggleLoading(true) this.toggleLoading(true)
axios.get(api.netWorkOverview.totalTrafficAnalysis, { params: params }).then(response => {
const res = response.data get(api.netWorkOverview.totalTrafficAnalysis, params).then((res) => {
// const res = mockData.bytes.boundary.data
this.errorMsg = res.message this.errorMsg = res.message
if (res.code === 200) { if (res.code === 200) {
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
this.showError = false this.showError = false
if (this.isNoData) { if (this.isNoData) {
this.lineTab = '' this.lineTab = ''
this.tabs = _.cloneDeep(this.tabsTemplate) this.mpackets = [
} else { { analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
this.initData(res.data.result, val, active, show, n) { analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' },
{ analysis: {}, name: 'network.internal', class: 'internal', show: true, invertTab: true, positioning: 3, data: [], unitType: '' },
{ analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' },
{ analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' }
]
} }
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis
mpackets[1].analysis = t.inboundBitsRate.analysis
mpackets[2].analysis = t.outboundBitsRate.analysis
mpackets[3].analysis = t.internalBitsRate.analysis
mpackets[4].analysis = t.throughBitsRate.analysis
mpackets[5].analysis = t.other.analysis
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'bps'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 5) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
})
} else {
if (n) this.lineTab = ''
this.$nextTick(() => {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalPacketsRate.analysis
mpackets[1].analysis = t.inboundPacketsRate.analysis
mpackets[2].analysis = t.outboundPacketsRate.analysis
mpackets[3].analysis = t.internalPacketsRate.analysis
mpackets[4].analysis = t.throughPacketsRate.analysis
mpackets[5].analysis = t.other.analysis
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : []
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'packets/s'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 5) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
})
} else {
if (n) this.lineTab = ''
this.$nextTick(() => {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalSessionsRate.analysis
mpackets[0].data = t.totalSessionsRate.values ? t.totalSessionsRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
e.unitType = 'sessions/s'
e.invertTab = false
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.mpackets = mpackets
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
})
}
})
} else { } else {
this.showError = true this.showError = true
this.errorMsg = res.message this.errorMsg = res.message
@@ -246,8 +377,6 @@ export default {
} }
}, },
echartsInit (echartsData, show) { echartsInit (echartsData, show) {
// echarts内容在单元测试时不执行
if (!this.isUnitTesting) {
if (this.lineTab) { if (this.lineTab) {
this.handleActiveBar() this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false) echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
@@ -257,6 +386,9 @@ export default {
const _this = this const _this = this
// !this.myChart && (this.myChart = echarts.init(dom)) // !this.myChart && (this.myChart = echarts.init(dom))
// 此处为验证是否因dom未销毁导致图表出错后续可能会改 // 此处为验证是否因dom未销毁导致图表出错后续可能会改
let dom = null
dom = document.getElementById('overviewLineChart')
this.chartOption = stackedLineChartOption this.chartOption = stackedLineChartOption
const chartOption = this.chartOption.series[0] const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => { this.chartOption.series = echartsData.map((t, i) => {
@@ -344,7 +476,7 @@ export default {
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.tabs.forEach(e => { this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) { if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning] t.borderColor = chartColor3[e.positioning]
} }
@@ -354,7 +486,7 @@ export default {
} }
this.showMarkLine = true this.showMarkLine = true
this.$nextTick(() => { this.$nextTick(() => {
this.myChart = echarts.init(this.$refs.overviewLineChart) this.myChart = echarts.init(dom)
this.myChart.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
// 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush // 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush
this.myChart.dispatchAction({ this.myChart.dispatchAction({
@@ -402,7 +534,6 @@ export default {
} }
}) })
}) })
}
}, },
activeChange (item, index) { activeChange (item, index) {
if (this.isNoData) return if (this.isNoData) return
@@ -434,16 +565,16 @@ export default {
legendSelectChange (item, index, val) { legendSelectChange (item, index, val) {
if (index === 'index') { if (index === 'index') {
this.dispatchLegendSelectAction(item.name) this.dispatchLegendSelectAction(item.name)
} else if (this.tabs[index] && this.tabs[index].name === item.name) { } else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
this.dispatchLegendSelectAction(item.name) this.dispatchLegendSelectAction(item.name)
this.tabs.forEach((t) => { this.mpackets.forEach((t) => {
if (t.name !== item.name) { if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name) this.dispatchLegendUnSelectAction(t.name)
} }
}) })
} }
if (val === 'active') { if (val === 'active') {
this.tabs.forEach(t => { this.mpackets.forEach(t => {
if (item.name === t.name) { if (item.name === t.name) {
t.invertTab = !t.invertTab t.invertTab = !t.invertTab
} else { } else {
@@ -455,7 +586,7 @@ export default {
} else { } else {
this.lineTab = t.class this.lineTab = t.class
} }
this.tabs.forEach((e) => { this.mpackets.forEach((e) => {
this.dispatchLegendSelectAction(e.name) this.dispatchLegendSelectAction(e.name)
}) })
} }
@@ -463,8 +594,8 @@ export default {
} }
}, },
handleActiveBar () { handleActiveBar () {
if (document.querySelector('.network .line-value-tabs.is-active')) { if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active') const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
const activeBar = document.querySelector('.network .line-value-active') const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;` activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
} }
@@ -475,13 +606,12 @@ export default {
referenceSelectChange (val) { referenceSelectChange (val) {
this.lineRefer = val this.lineRefer = val
let echartsData let echartsData
if (this.lineTab) {
echartsData = this.tabs.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = this.tabs.filter(t => t.show === true)
}
if (!this.isUnitTesting) {
const chartOption = this.myChart.getOption() const chartOption = this.myChart.getOption()
if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = this.mpackets.filter(t => t.show === true)
}
if (this.lineRefer === 'Average' && this.showMarkLine) { if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => { chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) { if (t.name === echartsData[0].name) {
@@ -502,7 +632,6 @@ export default {
}) })
} }
this.myChart.setOption(chartOption) this.myChart.setOption(chartOption)
}
}, },
symbolSizeSortChange (index, time) { symbolSizeSortChange (index, time) {
const dataIntegrationArray = [] const dataIntegrationArray = []
@@ -552,87 +681,6 @@ export default {
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
}, },
initData (data, val, active, show, n) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys(item).map(t => {
return {
...item[t]
}
})
}
})
}
lineData.splice(0, 1)
if (val === 'Sessions/s') {
const tabs = _.cloneDeep(this.tabsTemplate)
lineData.forEach((d, i) => {
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
tabs.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
e.unitType = 'sessions/s'
e.invertTab = false
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.tabs = tabs
this.$nextTick(() => {
this.echartsInit(this.tabs, true)
})
} else {
const unit = val === 'Bits/s' ? 'bps' : 'packets/s'
this.legendInit(lineData, active, show, unit, n)
}
},
legendInit (data, active, show, type, n) {
const tabs = _.cloneDeep(this.tabsTemplate)
data.forEach((d, i) => {
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
let num = 0
tabs.forEach(e => {
e.unitType = type
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) == 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.tabs = tabs
if (num === 5) {
tabs[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(tabs[0], 0)
this.$nextTick(() => {
this.echartsInit(this.tabs, true)
})
} else {
if (n) this.lineTab = ''
this.$nextTick(() => {
this.echartsInit(this.tabs, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
},
/** /**
* 鼠标右键返回框选的时间范围 * 鼠标右键返回框选的时间范围
*/ */
@@ -654,9 +702,11 @@ export default {
this.myChart = null this.myChart = null
this.chartOption = null this.chartOption = null
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
if (this.lineTab && this.metric !== 'Sessions/s') { if (this.lineTab) {
const data = this.tabsTemplate.find(t => t.class === this.lineTab) const data = this.mpackets.find(t => t.class === this.lineTab)
if (data && data.positioning) {
this.activeChange(data, data.positioning) this.activeChange(data, data.positioning)
}
} else { } else {
this.init() this.init()
} }
@@ -667,13 +717,11 @@ export default {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
let myChart = echarts.getInstanceByDom(this.$refs.overviewLineChart) let myChart = echarts.getInstanceByDom(document.getElementById('overviewLineChart'))
if (myChart) { if (myChart) {
echarts.dispose(myChart) echarts.dispose(myChart)
} }
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart)
}
// 检测时发现该方法占用较大内存,且未被释放 // 检测时发现该方法占用较大内存,且未被释放
this.unitConvert = null this.unitConvert = null
myChart = null myChart = null

View File

@@ -61,6 +61,7 @@ export default {
methods: { methods: {
init () { init () {
const params = { const params = {
// startTime: true,
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
@@ -192,14 +193,6 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
const dom = document.getElementById('chart1')
const dom2 = document.getElementById('chart2')
if (dom) {
echarts.dispose(dom)
}
if (dom2) {
echarts.dispose(dom2)
}
this.myChart = null this.myChart = null
this.myChart2 = null this.myChart2 = null
} }

View File

@@ -215,8 +215,8 @@ import unitConvert from '@/utils/unit-convert'
import { getChainRatio, computeScore, urlParamsHandler, overwriteUrl, readDrilldownTableConfigByUser, combineDrilldownTableWithUserConfig, getDnsMapData, handleSpecialValue, getConfigVersion } from '@/utils/tools' import { getChainRatio, computeScore, urlParamsHandler, overwriteUrl, readDrilldownTableConfigByUser, combineDrilldownTableWithUserConfig, getDnsMapData, handleSpecialValue, getConfigVersion } from '@/utils/tools'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { db } from '@/indexedDB'
import _ from 'lodash' import _ from 'lodash'
import indexedDBUtils from '@/indexedDB'
export default { export default {
name: 'NetworkOverviewTabs', name: 'NetworkOverviewTabs',
@@ -1729,7 +1729,7 @@ export default {
version = await getConfigVersion('default') version = await getConfigVersion('default')
} }
// 更新缓存中的配置 // 更新缓存中的配置
await indexedDBUtils.selectTable(dbDrilldownTableConfig).put({ await db[dbDrilldownTableConfig].put({
id: this.userId, id: this.userId,
version: version, version: version,
config: this.$_.cloneDeep(curUserConfigGroup) config: this.$_.cloneDeep(curUserConfigGroup)

View File

@@ -11,8 +11,8 @@
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<div class="npm-app-body" v-else> <div class="npm-app-body" v-else>
<div class="npm-app-body-patch" v-for="(data, index) in tableData" :key="index"> <div class="npm-app-body-patch" v-for="(data, index) in tableData" :key="index">
<div class="npm-app-body-icon"><span><i :class="data.icon" :test-id="`iconContent${index}`"></i></span></div> <div class="npm-app-body-icon"><span><i :class="data.icon"></i></span></div>
<div class="npm-app-body-color" v-for="index2 in 6" :key="index2" :class="{'score-color': data.score >= index2}"></div> <div class="npm-app-body-color" v-for="(item, index) in 6" :key="index" :class="{'score-color': data.score >= index + 1}"></div>
</div> </div>
</div> </div>
</div> </div>
@@ -48,7 +48,7 @@
<div v-else-if="scope.row.bytesRateChainRatio < 0" class="data-total-trend data-total-trend-green"> <div v-else-if="scope.row.bytesRateChainRatio < 0" class="data-total-trend data-total-trend-green">
<i class="cn-icon-decline cn-icon"></i>&nbsp; <i class="cn-icon-decline cn-icon"></i>&nbsp;
<span v-if="scope.row.bytesRateChainRatio >= -5"> <span v-if="scope.row.bytesRateChainRatio >= -5">
{{unitConvert(scope.row.bytesRateChainRatio, unitTypes.percent).join('').replace(/-/g, '')}} {{unitConvert(scope.row.bytesRateChainRatio, unitTypes.percent).join('').replaceAll('-', '')}}
</span> </span>
<span v-else>>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
@@ -71,7 +71,7 @@
<div v-else-if="scope.row.outboundBytesRateChainRatio < 0" class="data-total-trend data-total-trend-green"> <div v-else-if="scope.row.outboundBytesRateChainRatio < 0" class="data-total-trend data-total-trend-green">
<i class="cn-icon-decline cn-icon"></i>&nbsp; <i class="cn-icon-decline cn-icon"></i>&nbsp;
<span v-if="scope.row.outboundBytesRateChainRatio >= -5"> <span v-if="scope.row.outboundBytesRateChainRatio >= -5">
{{unitConvert(scope.row.outboundBytesRateChainRatio, unitTypes.percent).join('').replace(/-/g, '')}} {{unitConvert(scope.row.outboundBytesRateChainRatio, unitTypes.percent).join('').replaceAll('-', '')}}
</span> </span>
<span v-else>>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
@@ -86,35 +86,35 @@
<div class="data-trend"> <div class="data-trend">
<div v-if="scope.row.inboundBytesRateChainRatio > 0" class="data-total-trend data-total-trend-red"> <div v-if="scope.row.inboundBytesRateChainRatio > 0" class="data-total-trend data-total-trend-red">
<i class="cn-icon-rise1 cn-icon"></i>&nbsp; <i class="cn-icon-rise1 cn-icon"></i>&nbsp;
<span v-if="scope.row.inboundBytesRateChainRatio <= 5" :test-id="`inbound-${scope.row.appSubcategory}`"> <span v-if="scope.row.inboundBytesRateChainRatio <= 5">
{{unitConvert(scope.row.inboundBytesRateChainRatio, unitTypes.percent).join('')}} {{unitConvert(scope.row.inboundBytesRateChainRatio, unitTypes.percent).join('')}}
</span> </span>
<span v-else :test-id="`inbound-${scope.row.appSubcategory}`">>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
<div v-else-if="scope.row.inboundBytesRateChainRatio < 0" class="data-total-trend data-total-trend-green"> <div v-else-if="scope.row.inboundBytesRateChainRatio < 0" class="data-total-trend data-total-trend-green">
<i class="cn-icon-decline cn-icon"></i>&nbsp; <i class="cn-icon-decline cn-icon"></i>&nbsp;
<span v-if="scope.row.inboundBytesRateChainRatio >= -5" :test-id="`inbound-${scope.row.appSubcategory}`"> <span v-if="scope.row.inboundBytesRateChainRatio >= -5">
{{unitConvert(scope.row.inboundBytesRateChainRatio, unitTypes.percent).join('').replace(/-/g, '')}} {{unitConvert(scope.row.inboundBytesRateChainRatio, unitTypes.percent).join('').replaceAll('-', '')}}
</span> </span>
<span v-else :test-id="`inbound-${scope.row.appSubcategory}`">>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
<div v-else-if="scope.row.inboundBytesRateChainRatio === 0" :test-id="`inbound-${scope.row.appSubcategory}`" class="data-total-trend data-total-trend-black"> <div v-else-if="scope.row.inboundBytesRateChainRatio === 0" class="data-total-trend data-total-trend-black">
<i class="cn-icon-constant cn-icon"></i> <i class="cn-icon-constant cn-icon"></i>
</div> </div>
<div v-else></div> <div v-else></div>
</div> </div>
</template> </template>
<template v-else-if="item.prop === 'score'"> <template v-else-if="item.prop === 'score'">
<div v-if="scope.row.score <= 2" :test-id="`score-${scope.row.appSubcategory}`" class="data-score data-score-red"> <div v-if="scope.row.score <= 2" :class="{'data-score-red': scope.row.score <= 2}" class="data-score">
{{scope.row.score}} {{scope.row.score}}
</div> </div>
<div v-else-if="scope.row.score <= 4" :test-id="`score-${scope.row.appSubcategory}`" class="data-score data-score-yellow"> <div v-else-if="scope.row.score <= 4" :class="{'data-score-yellow': scope.row.score <= 4}" class="data-score">
{{scope.row.score}} {{scope.row.score}}
</div> </div>
<div v-else-if="scope.row.score <= 6" :test-id="`score-${scope.row.appSubcategory}`" class="data-score data-score-green"> <div v-else-if="scope.row.score <= 6" :class="{'data-score-green': scope.row.score <= 6}" class="data-score">
{{scope.row.score}} {{scope.row.score}}
</div> </div>
<div v-else-if="scope.row.score === '-'" :test-id="`score-${scope.row.appSubcategory}`" class="data-score-no-data"> <div v-else-if="scope.row.score === '-'" class="data-score-no-data">
- -
</div> </div>
</template> </template>
@@ -138,12 +138,17 @@ import { unitTypes, npmCategoryInfoMapping, networkTable, operationType, npmCate
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { computeScore, getChainRatio, getUserDrilldownTableConfig, overwriteUrl, urlParamsHandler } from '@/utils/tools' import { get } from '@/utils/http'
import {
getChainRatio,
computeScore,
urlParamsHandler,
overwriteUrl,
getUserDrilldownTableConfig
} from '@/utils/tools'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios'
export default { export default {
name: 'NpmAppCategoryScore', name: 'NpmAppCategoryScore',
data () { data () {
@@ -193,15 +198,15 @@ export default {
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
// 获取table后三列内容 // 获取table后三列内容
const currentTrafficRequest = axios.get(api.npm.overview.appTrafficAnalysis, { params: { ...params, cycle: 0 } }) const currentTrafficRequest = get(api.npm.overview.appTrafficAnalysis, { ...params, cycle: 0 })
const lastCycleTrafficRequest = axios.get(api.npm.overview.appTrafficAnalysis, { params: { ...params, cycle: 1 } }) const lastCycleTrafficRequest = get(api.npm.overview.appTrafficAnalysis, { ...params, cycle: 1 })
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([currentTrafficRequest, lastCycleTrafficRequest]).then(res => { Promise.all([currentTrafficRequest, lastCycleTrafficRequest]).then(res => {
if (res[0].data.code === 200 && res[1].data.code === 200) { if (res[0].code === 200 && res[1].code === 200) {
this.showError = false this.showError = false
this.errorMsg = '' this.errorMsg = ''
const prevData = res[1].data.data.result const prevData = res[1].data.result
const data = res[0].data.data.result const data = res[0].data.result
if (data && data.length > 0) { if (data && data.length > 0) {
this.isNoData = false this.isNoData = false
const tableData = data.map(d => { const tableData = data.map(d => {
@@ -223,23 +228,24 @@ export default {
return result return result
}) })
// 计算分数 // 计算分数
const tcpRequest = axios.get(api.npm.overview.appTcpSessionDelay, { params: params }) const tcpRequest = get(api.npm.overview.appTcpSessionDelay, params)
const httpRequest = axios.get(api.npm.overview.appHttpResponseDelay, { params: params }) const httpRequest = get(api.npm.overview.appHttpResponseDelay, params)
const sslRequest = axios.get(api.npm.overview.appSslConDelay, { params: params }) const sslRequest = get(api.npm.overview.appSslConDelay, params)
const tcpLostRequest = axios.get(api.npm.overview.appTcpLostlenPercent, { params: params }) const tcpLostRequest = get(api.npm.overview.appTcpLostlenPercent, params)
const packetRetransRequest = axios.get(api.npm.overview.appPacketRetransPercent, { params: params }) const packetRetransRequest = get(api.npm.overview.appPacketRetransPercent, params)
Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res => { Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res => {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans'] const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
let msg = '' let msg = ''
res.forEach((r, i) => { res.forEach((r, i) => {
if (r.data.code === 200) { if (r.code === 200) {
tableData.forEach(t => { tableData.forEach(t => {
t[keyPre[i] + 'Score'] = r.data.data.result.find(d => d.appSubcategory === t.appSubcategory) const find = r.data.result.find(d => d.appSubcategory === t.appSubcategory)
t[keyPre[i] + 'Score'] = find
}) })
} else { } else {
this.showError = true this.showError = true
msg = msg + ',' + r.data.data.message msg = msg + ',' + r.message
if (msg.indexOf(',') === 0) { if (msg.indexOf(',') === 0) {
msg = msg.substring(1, msg.length) msg = msg.substring(1, msg.length)
} }
@@ -342,7 +348,7 @@ export default {
} }
}) })
let toPanel = null let toPanel = null
list.forEach(item => { list.forEach((item, index) => {
if (item.label === tabType) { if (item.label === tabType) {
item.checked = false item.checked = false
toPanel = item.panelId toPanel = item.panelId
@@ -354,7 +360,8 @@ export default {
this.$store.commit('setNetworkOverviewTabList', list) this.$store.commit('setNetworkOverviewTabList', list)
const tabObjGroup = list.filter(item => item.checked) const tabObjGroup = list.filter(item => item.checked)
if (tabObjGroup && tabObjGroup.length > 0) { if (tabObjGroup && tabObjGroup.length > 0) {
this.urlChangeParams[this.curTabState.curTab] = tabObjGroup[0] const curTab = tabObjGroup[0]
this.urlChangeParams[this.curTabState.curTab] = curTab
} }
this.changeUrlTabState() this.changeUrlTabState()
this.$router.push({ this.$router.push({
@@ -377,9 +384,6 @@ export default {
}, },
mounted () { mounted () {
this.init() this.init()
},
beforeUnmount () {
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -1,11 +1,29 @@
<template> <template>
<div class="npm-app-event"> <div class="npm-app-event">
<!-- <div class="metric-select">
<el-select
v-model="metric"
class="option__select select-column"
popper-class="option-popper common-select"
:popper-append-to-body="false"
key="tabMetric"
@change="changeMetric"
size="mini"
width="100">
<el-option
v-for="item in options"
:key="item.label"
:label="item.label"
:value="item.value"
/>
</el-select>
<span>{{ $t('network.metric') }}</span>
</div>-->
<el-table <el-table
id="tabTable" id="tabTable"
ref="dataTable" ref="dataTable"
:data="tableData" :data="tableData"
class="npm-app-event-table" class="npm-app-event-table"
:row-class-name="rowClassName"
height="100%" height="100%"
empty-text="" empty-text=""
> >
@@ -15,33 +33,34 @@
<span class="data-column__span">{{ $t(item.label) }}</span> <span class="data-column__span">{{ $t(item.label) }}</span>
</template> </template>
<template #default="scope" :column="item"> <template #default="scope" :column="item">
<div class="data-app-event-table" :test-id="`${item.prop}${scope.row.index}`"> <div class="data-app-event-table">
<template v-if="item.prop === 'domain' ||item.prop === 'appName' ||item.prop === 'serverIp' "> <template v-if="item.prop === 'domain' ||item.prop === 'appName' ||item.prop === 'serverIp' ">
<span class="data-applications">{{ scope.row[item.prop] }}</span> <span class="data-applications">{{ scope.row[item.prop] }}</span>
</template> </template>
<template v-else-if="item.prop === 'eventSeverity'" > <template v-else-if="item.prop === 'eventSeverity'">
<template v-if="scope.row[item.prop]==='critical'"> <template v-if="scope.row[item.prop]==='critical'">
<div v-for="item in 5" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div> <div v-for="item in 5" class="red-dot" :key="item"></div>
</template> </template>
<template v-else-if="scope.row[item.prop]==='high'"> <template v-else-if="scope.row[item.prop]==='high'">
<div v-for="item in 4" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div> <div v-for="item in 4" class="red-dot" :key="item"></div>
<div v-for="item in 1" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${4+item}`"></div> <div class="grey-dot"></div>
</template> </template>
<template v-else-if="scope.row[item.prop]==='medium'"> <template v-else-if="scope.row[item.prop]==='medium'">
<div v-for="item in 3" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div> <div v-for="item in 3" class="red-dot" :key="item"></div>
<div v-for="item in 2" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${3+item}`"></div> <div v-for="item in 2" class="grey-dot" :key="item"></div>
</template> </template>
<template v-else-if="scope.row[item.prop]==='low'"> <template v-else-if="scope.row[item.prop]==='low'">
<div v-for="item in 2" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div> <div v-for="item in 2" class="red-dot" :key="item"></div>
<div v-for="item in 3" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${2+item}`"></div> <div v-for="item in 3" class="grey-dot" :key="item"></div>
</template> </template>
<template v-else-if="scope.row[item.prop]==='info'"> <template v-else-if="scope.row[item.prop]==='info'">
<div v-for="item in 1" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div> <div v-for="item in 1" class="red-dot" :key="item"></div>
<div v-for="item in 4" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${1+item}`"></div> <div v-for="item in 4" class="grey-dot" :key="item"></div>
</template> </template>
<span class="data-severity">{{ scope.row[item.prop] }}</span> <span class="data-severity">{{ scope.row[item.prop] }}</span>
</template> </template>
<template v-else-if="item.prop === 'eventType'"> <template v-else-if="item.prop === 'eventType'">
<!-- <span class="data-eventType" v-for="type in scope.row[item.prop]">{{type}}</span>-->
<span class="data-eventType">{{ scope.row[item.prop] }}</span> <span class="data-eventType">{{ scope.row[item.prop] }}</span>
</template> </template>
<template v-else-if="item.prop === 'count'"> <template v-else-if="item.prop === 'count'">
@@ -134,10 +153,6 @@ export default {
this.init() this.init()
}, },
methods: { methods: {
rowClassName ({ row, rowIndex }) {
// 把每一行的索引放进row
row.index = rowIndex
},
init () { init () {
this.toggleLoading(true) this.toggleLoading(true)
this.isNoData = false this.isNoData = false

View File

@@ -13,14 +13,14 @@
<div class="npm-event-pie-legend-title" v-if="chartData.length > 0">{{ $t('overall.type') }}</div> <div class="npm-event-pie-legend-title" v-if="chartData.length > 0">{{ $t('overall.type') }}</div>
<template v-for="(legend, index) in chartData" :key="index"> <template v-for="(legend, index) in chartData" :key="index">
<div class="npm-event-pie-legend-type"> <div class="npm-event-pie-legend-type">
<div class="npm-event-pie-legend-type-severity" :test-id="`testNode${index}`">{{legend.name}}</div> <div class="npm-event-pie-legend-type-severity">{{legend.name}}</div>
</div> </div>
</template> </template>
</div> </div>
<div class="npm-event-pie-legend"> <div class="npm-event-pie-legend">
<div class="npm-event-pie-legend-title" v-if="chartData.length > 0">{{ $t('network.total') }}</div> <div class="npm-event-pie-legend-title" v-if="chartData.length > 0">{{ $t('network.total') }}</div>
<template v-for="(legend, index) in chartData" :key="index"> <template v-for="(legend, index) in chartData" :key="index">
<div class="npm-event-pie-legend-total" :test-id="`total${index}`">{{legend.value}}</div> <div class="npm-event-pie-legend-total">{{legend.value}}</div>
</template> </template>
</div> </div>
</div> </div>
@@ -31,6 +31,7 @@
<script> <script>
import { shallowRef } from 'vue' import { shallowRef } from 'vue'
import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { pieChartOption3 } from '@/views/charts2/charts/options/echartOption' import { pieChartOption3 } from '@/views/charts2/charts/options/echartOption'
@@ -38,7 +39,6 @@ import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios'
export default { export default {
name: 'NpmEventsByType', name: 'NpmEventsByType',
@@ -108,8 +108,7 @@ export default {
type: 'severity' type: 'severity'
} }
this.toggleLoading(true) this.toggleLoading(true)
axios.get(api.npm.events.recentEvents, { params: params }).then(res => { get(api.npm.events.recentEvents, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.showError = false this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
@@ -127,10 +126,8 @@ export default {
this.chartData = arrData.sort((a, b) => { return b.value - a.value }) this.chartData = arrData.sort((a, b) => { return b.value - a.value })
this.$nextTick(() => { this.$nextTick(() => {
if (this.chartData.length > 0) { if (this.chartData.length > 0) {
if (!this.isUnitTesting) {
this.init() this.init()
} }
}
}) })
} else { } else {
this.isNoData = false this.isNoData = false
@@ -155,9 +152,7 @@ export default {
}, },
beforeUnmount () { beforeUnmount () {
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart)
}
this.chartOption = null this.chartOption = null
} }
} }

View File

@@ -2,21 +2,21 @@
<div class="npm-header"> <div class="npm-header">
<div class="npm-header-body" v-for="(item, index) in chartData" :key=index> <div class="npm-header-body" v-for="(item, index) in chartData" :key=index>
<div class="npm-header-body-severity"> <div class="npm-header-body-severity">
<div class="npm-header-body-severity-icon" :class="item.eventSeverity" :test-id="`icon${index}`"></div> <div class="npm-header-body-severity-icon" :class="item.eventSeverity"></div>
<div class="npm-header-body-severity-value" :test-id="`severity${index}`">{{item.eventSeverity}}</div> <div class="npm-header-body-severity-value">{{item.eventSeverity}}</div>
</div> </div>
<chart-error v-if="showError" tooltip :content="errorMsg" /> <chart-error v-if="showError" tooltip :content="errorMsg" />
<div v-else class="npm-header-body-total" :test-id="`total${index}`">{{item.count}}</div> <div v-else class="npm-header-body-total">{{item.count}}</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios'
export default { export default {
name: 'NpmEventsHeader', name: 'NpmEventsHeader',
components: { ChartError }, components: { ChartError },
@@ -65,13 +65,12 @@ export default {
methods: { methods: {
recentEventsListData () { recentEventsListData () {
const params = { const params = {
startTime: this.timeFilter && this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : '', startTime: getSecond(this.timeFilter.startTime),
endTime: this.timeFilter && this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : '', endTime: getSecond(this.timeFilter.endTime),
type: this.type type: this.type
} }
this.toggleLoading(true) this.toggleLoading(true)
axios.get(api.npm.events.list, { params: params }).then(res => { get(api.npm.events.list, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.showError = false this.showError = false
if (res.data.result.length === 0) { if (res.data.result.length === 0) {
@@ -97,9 +96,7 @@ export default {
} }
}, },
mounted () { mounted () {
this.$nextTick(() => {
this.recentEventsListData() this.recentEventsListData()
})
} }
} }
</script> </script>

View File

@@ -336,9 +336,7 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart)
}
this.chartOption = null this.chartOption = null
} }
} }

View File

@@ -1,60 +1,16 @@
<template> <template>
<div class="npm-network-quantity"> <div class="npm-network-quantity">
<div class="single-value" v-for="(npm, index) in newNpmNetworkData" :key="index"> <single-value
<div class="single-value__title" style="display: flex"> v-if="npmNetworkData.length>0"
{{ $t(npmNetworkName[index].name) }} :npm-network-name="npmNetworkName"
<chart-error v-if="npm.message" tooltip :content="npm.message"></chart-error> :npm-network-data="npmNetworkData"
</div> ></single-value>
<div class="single-value__content" >
<div class="single-value__content-number" v-if="index ===0 || index ===1 || index ===2" :test-id="`singleValueContent${index}`">
{{ unitConvert(npm.Avg, unitTypes.time).join(' ') }}
</div>
<div class="single-value__content-number" v-else :test-id="`singleValueContent${index}`">
{{unitConvert(npm.Avg, unitTypes.percent).join(' ')}}
</div>
<div v-if="npm.value > 0" class="single-value__content-trend single-value__content-trend-red" >
<i class="cn-icon-rise1 cn-icon" :test-id="`singleValueTrendIcon${index}`"></i>&nbsp;
<span v-if="npm.value <= 5" :test-id="`singleValueTrendValue${index}`">
{{ unitConvert(npm.value, unitTypes.percent).join('') }}
</span>
<span v-else :test-id="`singleValueTrendValue${index}`">>500.00%</span>
</div>
<div v-else-if="npm.value < 0" class="single-value__content-trend single-value__content-trend-green" >
<i class="cn-icon-decline cn-icon" :test-id="`singleValueTrendIcon${index}`"></i>&nbsp;
<span v-if="npm.value >= -5" :test-id="`singleValueTrendValue${index}`">
{{ unitConvert(npm.value, unitTypes.percent).join('') }}
</span>
<span v-else :test-id="`singleValueTrendValue${index}`">>500.00%</span>
</div>
<div v-else-if="npm.value === 0" class="single-value__content-trend single-value__content-trend-black">
<i class="cn-icon-constant cn-icon" :test-id="`singleValueTrendIcon${index}`"></i>
</div>
<div v-else></div>
</div>
<div class="single-value__circle">
<div class="single-value__circle-p95" :test-id="`singleValueP95${index}`">
<span v-if="index ===0 || index ===1 || index ===2">
P95:{{ unitConvert(npm.P95, unitTypes.time).join(' ') }}</span>
<span v-else>
P95:{{ unitConvert(npm.P95, unitTypes.percent).join(' ') }}
</span>
</div>
<div class="single-value__circle-p99" :test-id="`singleValueP99${index}`">
<span v-if="index ===0 || index ===1 || index ===2">
P99:{{ unitConvert(npm.P99, unitTypes.time).join(' ') }}
</span>
<span v-else>
P99:{{ unitConvert(npm.P99, unitTypes.percent).join(' ') }}
</span>
</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import SingleValue from '@/views/charts2/charts/npm/localComponents/SingleValue'
import { get } from '@/utils/http'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
@@ -62,11 +18,9 @@ import _ from 'lodash'
import { getChainRatio } from '@/utils/tools' import { getChainRatio } from '@/utils/tools'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from 'axios'
export default { export default {
name: 'NpmNetworkQuantity', name: 'NpmNetworkQuantity',
components: { SingleValue },
mixins: [chartMixin], mixins: [chartMixin],
setup () { setup () {
const { query } = useRoute() const { query } = useRoute()
@@ -85,8 +39,6 @@ export default {
}, },
data () { data () {
return { return {
unitTypes,
unitConvert,
npmNetworkName: [ npmNetworkName: [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency' }, { name: 'networkAppPerformance.tcpConnectionEstablishLatency' },
{ name: 'networkAppPerformance.httpResponse' }, { name: 'networkAppPerformance.httpResponse' },
@@ -96,7 +48,6 @@ export default {
], ],
npmNetworkCycleData: [], npmNetworkCycleData: [],
npmNetworkLastCycleData: [], npmNetworkLastCycleData: [],
newNpmNetworkData: [],
npmNetworkData: [], npmNetworkData: [],
side: '', side: '',
chartData: {}, chartData: {},
@@ -117,7 +68,7 @@ export default {
npmNetworkCycleQuery () { npmNetworkCycleQuery () {
let condition = '' let condition = ''
let url = '' let url = ''
if (this.queryCondition && this.queryCondition.indexOf(' OR ') > -1) { if (this.queryCondition.indexOf(' OR ') > -1) {
condition = this.queryCondition.split(/["|'](.*?)["|']/) condition = this.queryCondition.split(/["|'](.*?)["|']/)
} else { } else {
condition = this.queryCondition condition = this.queryCondition
@@ -133,38 +84,37 @@ export default {
} else if (parseFloat(this.tabIndex) === 1) { } else if (parseFloat(this.tabIndex) === 1) {
this.side = 'server' this.side = 'server'
} }
if (condition && (typeof condition !== 'object') && type) { // 判断 condition 不为空并且不为对象 type 不为空 if (condition && (typeof condition !== 'object') && type) {
if (type === 'clientIp' || type === 'serverIp') { // type = clientIp || serverIp if (type === 'clientIp' || type === 'serverIp') {
if (parseFloat(this.tabIndex) === 0) { // npm 下钻 tabIndex 为 0 if (parseFloat(this.tabIndex) === 0) {
type = 'clientIp' type = 'clientIp'
} else if (parseFloat(this.tabIndex) === 1) { // npm 下钻 tabIndex 为 1 } else if (parseFloat(this.tabIndex) === 1) {
type = 'serverIp' type = 'serverIp'
} }
params.q = `ip='${condition.split(/'(.*?)'/)[1]}'` // 拼接字段作为参数 params.q = `ip='${condition.split(/'(.*?)'/)[1]}'`
} else if (type === 'clientCity') { } else if (type === 'clientCity') {
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'` // 拼接字段作为参数 params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'`
} else if (type === 'serverCity') { } else if (type === 'serverCity') {
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'` // 拼接字段作为参数 params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'`
} else { } else {
params.q = condition // 默认参数 params.q = condition
} }
params.type = type params.type = type
} else if (condition.length > 1 && type && type === 'ip') { // condition 为数组时数组长度不为 0 | type 不为空 | type为ip } else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}' and side='${this.side}'` // 拼接字段作为参数 params.q = `${type}='${condition[1]}' and side='${this.side}'`
params.type = type params.type = type
} else if (condition.length > 1 && type && type !== 'ip') { // condition 为数组时数组长度不为 0 | type 不为空 | type不为ip } else if (condition.length > 1 && type && type !== 'ip') {
if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') { // 根据接口所需,调整参数 if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') {
params.q = `${type}='${condition[1]}'` // 拼接字段作为参数 params.q = `${type}='${condition[1]}'`
params.type = type params.type = type
} else if (type === 'idcRenter') { } else if (type === 'idcRenter') {
params.q = `idc_renter='${condition[1]}'` // 拼接字段作为参数 params.q = `idc_renter='${condition[1]}'`
params.type = type params.type = type
} else { } else {
params.q = `${condition[0]}'${condition[1]}'` // 拼接字段作为参数 params.q = `${condition[0]}'${condition[1]}'`
params.type = type params.type = type
} }
} }
// 区分 3 级菜单和 2 级菜单使用不同的 url
if (parseFloat(this.tabOperationType) === 3) { if (parseFloat(this.tabOperationType) === 3) {
url = api.npm.overview.allNetworkAnalysis url = api.npm.overview.allNetworkAnalysis
} else { } else {
@@ -173,8 +123,7 @@ export default {
if ((type && condition) || type) { if ((type && condition) || type) {
params.type = params.type || type params.type = params.type || type
this.toggleLoading(true) this.toggleLoading(true)
axios.get(url, { params: params }).then(res => { get(url, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.npmNetworkCycleData = res.data.result this.npmNetworkCycleData = res.data.result
} }
@@ -184,21 +133,21 @@ export default {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} else { } else {
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params }) const tcp = get(api.npm.overview.tcpSessionDelay, params)
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params }) const http = get(api.npm.overview.httpResponseDelay, params)
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params }) const ssl = get(api.npm.overview.sslConDelay, params)
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params }) const tcpPercent = get(api.npm.overview.tcpLostlenPercent, params)
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params }) const packetPercent = get(api.npm.overview.packetRetransPercent, params)
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => { Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
// 状态为200的赋值接口数据不为200的传入报错提示message // 状态为200的赋值接口数据不为200的传入报错提示message
// 传给子组件SingleValue再进行error处理error处理不在此处处理 // 传给子组件SingleValue再进行error处理error处理不在此处处理
this.npmNetworkCycleData = [] this.npmNetworkCycleData = []
res.forEach(t => { res.forEach(t => {
if (t.data.code === 200) { if (t.code === 200) {
this.npmNetworkCycleData.push(t.data.data.result) this.npmNetworkCycleData.push(t.data.result)
} else { } else {
this.npmNetworkCycleData.push(t.data) this.npmNetworkCycleData.push(t)
} }
}) })
this.npmNetworkLastCycleQuery() this.npmNetworkLastCycleQuery()
@@ -228,8 +177,7 @@ export default {
if ((params.type && params.q) || (param && param.type)) { if ((params.type && params.q) || (param && param.type)) {
params.type = params.type || param.type params.type = params.type || param.type
this.toggleLoading(true) this.toggleLoading(true)
axios.get(url, { params: params }).then(res => { get(url, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.npmNetworkLastCycleData = res.data.result this.npmNetworkLastCycleData = res.data.result
} else { } else {
@@ -247,20 +195,20 @@ export default {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} else { } else {
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params }) const tcp = get(api.npm.overview.tcpSessionDelay, params)
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params }) const http = get(api.npm.overview.httpResponseDelay, params)
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params }) const ssl = get(api.npm.overview.sslConDelay, params)
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params }) const tcpPercent = get(api.npm.overview.tcpLostlenPercent, params)
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params }) const packetPercent = get(api.npm.overview.packetRetransPercent, params)
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => { Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
// 状态为200的赋值接口数据不为200的保留报错提示message // 状态为200的赋值接口数据不为200的保留报错提示message
// 传给子组件SingleValue再进行error处理error处理不在此处处理 // 传给子组件SingleValue再进行error处理error处理不在此处处理
res.forEach(t => { res.forEach(t => {
if (t.data.code === 200) { if (t.code === 200) {
this.npmNetworkLastCycleData.push(t.data.data.result) this.npmNetworkLastCycleData.push(t.data.result)
} else { } else {
this.npmNetworkLastCycleData.push(t.data) this.npmNetworkLastCycleData.push(t)
} }
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 1) this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 1)
}) })
@@ -323,39 +271,6 @@ export default {
}) })
this.npmNetworkData = cycle this.npmNetworkData = cycle
} }
this.initData(this.npmNetworkData)
},
initData (data) {
// 处理数据后的数组
const dealList = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
const tempObj = {}
for (const i in item) {
if (item.msg || item.message) {
// 为了兼容字段为msg的情况
tempObj.message = item.msg ? item.msg : item.message
} else {
// 将含有avg、p90等关键字使用avg、p90来代替形成统一属性
if (i.indexOf('Avg') > -1) {
tempObj.Avg = item[i]
} else if (i.indexOf('P50') > -1) {
tempObj.P50 = item[i]
} else if (i.indexOf('P90') > -1) {
tempObj.P90 = item[i]
} else if (i.indexOf('P95') > -1) {
tempObj.P95 = item[i]
} else if (i.indexOf('P99') > -1) {
tempObj.P99 = item[i]
}
tempObj.value = item.value
}
}
dealList.push(tempObj)
})
this.newNpmNetworkData = dealList
}
} }
}, },
mounted () { mounted () {
@@ -367,7 +282,6 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer1) clearTimeout(this.timer1)
clearTimeout(this.timer2) clearTimeout(this.timer2)
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -21,15 +21,15 @@
<template #default="scope" :column="item"> <template #default="scope" :column="item">
<div class="data-recent-table"> <div class="data-recent-table">
<template v-if="item.prop === 'eventSeverity'"> <template v-if="item.prop === 'eventSeverity'">
<span class="data-recent-table-severity" :class="scope.row[item.prop]" :test-id="`eventSeverity-${scope.row.eventSeverity}-${scope.$index}`">{{scope.row[item.prop]}}</span> <span class="data-recent-table-severity" :class="scope.row[item.prop]">{{scope.row[item.prop]}}</span>
</template> </template>
<template v-else-if="item.prop === 'eventKey'"> <template v-else-if="item.prop === 'eventKey'">
<span class="data-recent-table-entity click-active" @click="jumpPage(scope.row)" :test-id="`eventKey-${splitEventKey(scope.row.eventKey)}-${scope.$index}`">{{splitEventKey(scope.row[item.prop])}}</span> <span class="data-recent-table-entity click-active" @click="jumpPage(scope.row)">{{splitEventKey(scope.row[item.prop])}}</span>
</template> </template>
<template v-else-if="item.prop === 'eventType'"> <template v-else-if="item.prop === 'eventType'">
<span class="data-recent-table-eventType" :test-id="`eventType-${scope.row.eventType}-${scope.$index}`">{{scope.row[item.prop]}}</span> <span class="data-recent-table-eventType">{{scope.row[item.prop]}}</span>
</template> </template>
<span v-else-if="scope.row[item.prop]" :test-id="`startTime-${scope.$index}`">{{scope.row[item.prop]}}</span> <span v-else-if="scope.row[item.prop]">{{scope.row[item.prop]}}</span>
<span v-else>-</span> <span v-else>-</span>
</div> </div>
</template> </template>
@@ -48,13 +48,13 @@
</template> </template>
<script> <script>
import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond, dateFormatByAppearance } from '@/utils/date-util' import { getSecond, dateFormatByAppearance } from '@/utils/date-util'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios'
export default { export default {
name: 'NpmRecentEvents', name: 'NpmRecentEvents',
@@ -99,7 +99,7 @@ export default {
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
limit: 8 limit: 8
} }
if (condition && condition.length > 1 && this.dimensionType) { if (condition.length > 1 && this.dimensionType) {
params.param = condition[1] params.param = condition[1]
params.type = this.dimensionType params.type = this.dimensionType
if (params.type === 'serverIp' || params.type === 'clientIp') params.type = 'ip' if (params.type === 'serverIp' || params.type === 'clientIp') params.type = 'ip'
@@ -114,8 +114,7 @@ export default {
url = api.npm.events.recentEvents url = api.npm.events.recentEvents
} }
this.toggleLoading(true) this.toggleLoading(true)
axios.get(url, { params: params }).then(res => { get(url, params).then(res => {
res = res.data
if (res.code === 200) { if (res.code === 200) {
this.showError = false this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
@@ -152,7 +151,6 @@ export default {
return name return name
}, },
jumpPage (item) { jumpPage (item) {
this.beforeRouterPush()
this.$router.push({ this.$router.push({
path: '/detection/performanceEvent', path: '/detection/performanceEvent',
query: { query: {
@@ -160,36 +158,6 @@ export default {
eventId: item.eventId eventId: item.eventId
} }
}) })
},
/**
* 在路由跳转前,即下钻前将路由数据保存起来,确保回退和前进保留当时状态
*/
beforeRouterPush () {
const currentRouter = this.$_.cloneDeep(this.$route.query)
const historyList = this.$_.cloneDeep(this.$store.getters.getRouterHistoryList)
const tempObj = {
t: currentRouter.t,
query: currentRouter,
path: this.$_.cloneDeep(this.$route.path),
params: this.$_.cloneDeep(this.$route.params)
}
if (historyList.length > 0) {
let flag = true
historyList.forEach((item, index) => {
if (item.t === currentRouter.t) {
historyList[index] = tempObj
flag = false
}
if (!flag) {
return true
}
})
if (flag) historyList.push(tempObj)
} else {
historyList.push(tempObj)
}
this.$store.commit('setRouterHistoryList', historyList)
} }
}, },
mounted () { mounted () {

View File

@@ -22,6 +22,7 @@ import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { drillDownPanelTypeMapping } from '@/utils/constants' import { drillDownPanelTypeMapping } from '@/utils/constants'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
export default { export default {
name: 'NpmTabs', name: 'NpmTabs',

View File

@@ -42,7 +42,7 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import _ from 'lodash' import _ from 'lodash'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { getLineType, getLineIndexUnit, overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
export default { export default {
@@ -71,7 +71,7 @@ export default {
unitConvert, unitConvert,
unitTypes, unitTypes,
side: '', side: '',
tabs: [ mpackets: [
{ name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' }, { name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
{ name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' }, { name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
{ name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' }, { name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
@@ -80,11 +80,11 @@ export default {
{ name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' } { name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
], ],
npmQuantity: [ npmQuantity: [
{ name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 }, { name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 }, { name: this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 }, { name: this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 }, { name: this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 } { name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent }
], ],
chartData: {}, chartData: {},
metricOptions: [ metricOptions: [
@@ -144,6 +144,7 @@ export default {
if (!val) { if (!val) {
val = this.metricFilter val = this.metricFilter
} }
// const conditionStr = this.$route.query.queryCondition ? this.$route.query.queryCondition : ''
let condition = '' let condition = ''
let type = this.dimensionType let type = this.dimensionType
if (this.queryCondition.indexOf(' OR ') > -1) { if (this.queryCondition.indexOf(' OR ') > -1) {
@@ -199,7 +200,7 @@ export default {
this.showError = false this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
if (this.isNoData) { if (this.isNoData) {
this.tabs = [ this.mpackets = [
{ name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' }, { name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
{ name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' }, { name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
{ name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' }, { name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
@@ -208,15 +209,92 @@ export default {
{ name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' } { name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
] ]
this.npmQuantity = [ this.npmQuantity = [
{ name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 }, { name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 }, { name: this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 }, { name: this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 }, { name: this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 } { name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent }
] ]
} else {
this.initData(res.data.result, val)
} }
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : []
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalSessionsRate.values ? t.totalSessionsRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'establishLatencyMs' && val === 'establishLatencyMs') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[0].data = t.establishLatencyMsAvg.values ? t.establishLatencyMsAvg.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 0
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'httpResponseLatency' && val === 'httpResponseLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[1].data = t.httpResponseLatencyAvg.values ? t.httpResponseLatencyAvg.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 1
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'sslConLatency' && val === 'sslConLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[2].data = t.sslConLatencyAvg.values ? t.sslConLatencyAvg.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 2
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'tcpLostlenPercent' && val === 'tcpLostlenPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[3].data = t.tcpLostlenPercentAvg.values ? t.tcpLostlenPercentAvg.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 3
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
} else if (t.type === 'pktRetransPercent' && val === 'pktRetransPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[4].data = t.pktRetransPercentAvg.values ? t.pktRetransPercentAvg.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 4
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
}
})
} else { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
@@ -230,43 +308,66 @@ export default {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} else { } else {
if (val === 'Bits/s' || val === 'Packets/s' || val === 'Sessions/s') {
this.toggleLoading(true) this.toggleLoading(true)
const totalTrafficAnalysis = get(api.npm.overview.totalTrafficAnalysis, params) get(api.npm.overview.totalTrafficAnalysis, params).then(res => {
const totalNetworkAnalysis = get(api.npm.overview.totalNetworkAnalysis, params) if (res.code === 200) {
const totalHttpResponseDelay = get(api.npm.overview.totalHttpResponseDelay, params) this.showError = false
const totalSslConDelay = get(api.npm.overview.totalSslConDelay, params) this.isNoData = res.data.result.length === 0
const npmLineData = [] if (this.isNoData) {
Promise.all([totalNetworkAnalysis, totalTrafficAnalysis, totalHttpResponseDelay, totalSslConDelay]).then(res => { this.mpackets = [
res.forEach(item => { { name: 'network.total', show: true, positioning: 0, data: [], unitType: 'number' },
if (item.code === 200) { { name: 'network.inbound', show: true, positioning: 1, data: [], unitType: 'number' },
npmLineData.push(...item.data.result) { name: 'network.outbound', show: true, positioning: 2, data: [], unitType: 'number' },
{ name: 'network.internal', show: true, positioning: 3, data: [], unitType: 'number' },
{ name: 'network.through', show: true, positioning: 4, data: [], unitType: 'number' },
{ name: 'network.other', show: true, positioning: 5, data: [], unitType: 'number' }
]
}
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : []
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalSessionsRate.values ? t.totalSessionsRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
}
})
} else { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
this.errorMsg = res.message this.errorMsg = res.message
} }
})
this.showError = false
this.isNoData = npmLineData.length === 0
if (this.isNoData) {
this.tabs = [
{ name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
{ name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
{ name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
{ name: this.$t('network.internal'), show: true, positioning: 3, data: [], unitType: 'number' },
{ name: this.$t('network.through'), show: true, positioning: 4, data: [], unitType: 'number' },
{ name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
]
this.npmQuantity = [
{ name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 },
{ name: this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 },
{ name: this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 },
{ name: this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 },
{ name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
]
} else {
this.initData(npmLineData, val)
}
}).catch(e => { }).catch(e => {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
@@ -274,6 +375,137 @@ export default {
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} else if (val === 'establishLatencyMs' || val === 'tcpLostlenPercent' || val === 'pktRetransPercent') {
this.toggleLoading(true)
get(api.npm.overview.totalNetworkAnalysis, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach((t) => {
if (t.type === 'establishLatencyMs' && val === 'establishLatencyMs') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[0].data = t.establishLatencyMs.values ? t.establishLatencyMs.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 0
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'tcpLostlenPercent' && val === 'tcpLostlenPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[3].data = t.tcpLostlenPercent.values ? t.tcpLostlenPercent.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 3
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
} else if (t.type === 'pktRetransPercent' && val === 'pktRetransPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[4].data = t.pktRetransPercent.values ? t.pktRetransPercent.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 4
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'httpResponseLatency') {
this.toggleLoading(true)
get(api.npm.overview.totalHttpResponseDelay, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach(t => {
if (t.type === 'httpResponseLatency' && val === 'httpResponseLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[1].data = t.httpResponseLatency.values ? t.httpResponseLatency.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 1
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'sslConLatency') {
this.toggleLoading(true)
get(api.npm.overview.totalSslConDelay, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach(t => {
if (t.type === 'sslConLatency' && val === 'sslConLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[2].data = t.sslConLatency.values ? t.sslConLatency.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 2
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
}
} }
}, },
echartsInit (echartsData, legendUnit) { echartsInit (echartsData, legendUnit) {
@@ -301,7 +533,7 @@ export default {
color: chartColor3[t.positioning], color: chartColor3[t.positioning],
width: 1 width: 1
}, },
stack: t.name !== 'network.total' ? 'network.total' : '', stack: t.name !== this.$t('network.total') ? this.$t('network.total') : '',
areaStyle: { areaStyle: {
opacity: 0.1, opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -320,7 +552,7 @@ export default {
}) })
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
this.tabs.forEach(e => { this.mpackets.forEach(e => {
if (e.name === t.seriesName) { if (e.name === t.seriesName) {
t.borderColor = chartColor3[e.positioning] t.borderColor = chartColor3[e.positioning]
} }
@@ -387,67 +619,6 @@ export default {
}, },
resize () { resize () {
this.myChart.resize() this.myChart.resize()
},
initData (data, val) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach(item => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys((item)).map(t => {
return {
...item[t],
index: getLineIndexUnit(item.type, false),
unit: getLineIndexUnit(item.type, true)
}
})
}
})
}
lineData.splice(0, 1)
const tabs = _.cloneDeep(this.tabs)
const npmQuantity = _.cloneDeep(this.npmQuantity)
if (val === 'Sessions/s') {
lineData.forEach((d, i) => {
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
tabs.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.tabs = tabs
this.echartsInit(this.tabs)
} else if (val !== 'Bits/s' && val !== 'Packets/s' && val !== 'Sessions/s') {
this.legendInit(lineData, npmQuantity, true)
} else {
this.legendInit(lineData, tabs, false)
}
},
legendInit (data, npmData, show) {
data.forEach((d, i) => {
if (show) {
npmData[d.index].data = d.values
npmData[d.index].analysis = d.analysis
} else {
npmData[i].data = d.values
npmData[i].analysis = d.analysis
}
})
if (show) {
npmData.forEach((e, i) => {
e.show = i === data[0].index
})
this.npmQuantity = npmData
this.echartsInit(this.npmQuantity, data[0].unit)
} else {
npmData.forEach((e) => {
e.show = true
})
this.tabs = npmData
this.echartsInit(this.tabs)
}
} }
}, },
mounted () { mounted () {
@@ -462,9 +633,8 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) { this.myChart = null
echarts.dispose(this.myChart) this.myChart = null
}
this.unitConvert = null this.unitConvert = null
} }
} }

View File

@@ -0,0 +1,125 @@
<template>
<div class="single-value" v-for="(npm, index) in newNpmNetworkData" :key="index">
<div class="single-value__title" style="display: flex">
{{ $t(npmNetworkName[index].name) }}
<chart-error v-if="npm.message" tooltip :content="npm.message"></chart-error>
</div>
<div class="single-value__content">
<div class="single-value__content-number" v-if="index ===0 || index ===1 || index ===2">
{{ unitConvert(npm.Avg, unitTypes.time).join(' ') }}
</div>
<div class="single-value__content-number" v-else>
{{unitConvert(npm.Avg, unitTypes.percent).join(' ')}}
</div>
<div v-if="npm.value > 0" class="single-value__content-trend single-value__content-trend-red">
<i class="cn-icon-rise1 cn-icon"></i>&nbsp;
<span v-if="npm.value <= 5">
{{ unitConvert(npm.value, unitTypes.percent).join('') }}
</span>
<span v-else>>500.00%</span>
</div>
<div v-else-if="npm.value < 0" class="single-value__content-trend single-value__content-trend-green">
<i class="cn-icon-decline cn-icon"></i>&nbsp;
<span v-if="npm.value >= -5">
{{ unitConvert(npm.value, unitTypes.percent).join('').replaceAll('-', '') }}
</span>
<span v-else>>500.00%</span>
</div>
<div v-else-if="npm.value === 0" class="single-value__content-trend single-value__content-trend-black">
<i class="cn-icon-constant cn-icon"></i>
</div>
<div v-else></div>
</div>
<div class="single-value__circle">
<div class="single-value__circle-p95">
<span v-if="index ===0 || index ===1 || index ===2">
P95:{{ unitConvert(npm.P95, unitTypes.time).join(' ') }}</span>
<span v-else>
P95:{{ unitConvert(npm.P95, unitTypes.percent).join(' ') }}
</span>
</div>
<div class="single-value__circle-p99">
<span v-if="index ===0 || index ===1 || index ===2">
P99:{{ unitConvert(npm.P99, unitTypes.time).join(' ') }}
</span>
<span v-else>
P99:{{ unitConvert(npm.P99, unitTypes.percent).join(' ') }}
</span>
</div>
</div>
</div>
</template>
<script>
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import ChartError from '@/components/common/Error'
export default {
name: 'SingleValue',
components: { ChartError },
props: {
npmNetworkName: Array,
npmNetworkData: Array
},
data () {
return {
unitTypes,
unitConvert,
newNpmNetworkData: [] // 整合处理传过来的数据列表
}
},
watch: {
npmNetworkData: {
deep: true,
handler () {
this.initData()
}
}
},
mounted () {
this.initData()
},
methods: {
/**
* 初始化数据
*/
initData () {
// 传过来的数据
const npmNetworkData = this.npmNetworkData
// 处理数据后的数组
const dealList = []
if (npmNetworkData !== undefined && npmNetworkData.length > 0) {
npmNetworkData.forEach((item) => {
const tempObj = {}
for (const i in item) {
if (item.msg || item.message) {
// 为了兼容字段为msg的情况
tempObj.message = item.msg ? item.msg : item.message
} else {
// 将含有avg、p90等关键字使用avg、p90来代替形成统一属性
if (i.indexOf('Avg') > -1) {
tempObj.Avg = item[i]
} else if (i.indexOf('P50') > -1) {
tempObj.P50 = item[i]
} else if (i.indexOf('P90') > -1) {
tempObj.P90 = item[i]
} else if (i.indexOf('P95') > -1) {
tempObj.P95 = item[i]
} else if (i.indexOf('P99') > -1) {
tempObj.P99 = item[i]
}
tempObj.value = item.value
}
}
dealList.push(tempObj)
})
this.newNpmNetworkData = dealList
}
}
}
}
</script>

View File

@@ -134,7 +134,15 @@ export default {
} }
}, },
mounted () { mounted () {
this.initExpendTab() if (this.$route.query.eventId) {
if (parseFloat(this.$route.query.eventId) === this.detection.eventId) {
const container = document.getElementById('cnContainer')
container.scrollTop = 555 + this.index * 97
this.isCollapse = false
this.$emit('switchCollapse', this.isCollapse, this.index)
}
}
}, },
computed: { computed: {
iconClass () { iconClass () {
@@ -178,7 +186,7 @@ export default {
this.$emit('switchCollapse', this.isCollapse, this.index) this.$emit('switchCollapse', this.isCollapse, this.index)
if (this.isCollapse) { if (this.isCollapse) {
const newQuery = this.$route.query const newQuery = this.$route.query // 深拷贝路由数据
delete newQuery.eventId delete newQuery.eventId
this.reloadUrl(newQuery, 'cleanOldParams') this.reloadUrl(newQuery, 'cleanOldParams')
} else { } else {
@@ -199,33 +207,6 @@ export default {
newUrl = urlParamsHandler(window.location.href, query, newParam, clean) newUrl = urlParamsHandler(window.location.href, query, newParam, clean)
} }
overwriteUrl(newUrl) overwriteUrl(newUrl)
},
/**
* 初始化从npm跳转过来的id并展开tab
*/
initExpendTab () {
if (this.$route.query.eventId) {
if (this.$route.query.eventId === this.detection.eventId) {
const container = document.getElementById('cnContainer')
const dom = document.getElementsByClassName('cn-detection__case')
// 未展开的item折叠块高度67+下边距10+底部线高度1兼容不同分辨率下的tab高度
let itemHeight = 78
if (dom && this.index > 0) {
itemHeight = dom[0].clientHeight + 11
}
let topHeight = 554 + this.index * itemHeight
// 经过测试对比第7个以后的tab会往上移动但是手动展开和自动展开tab的滚动条高度一致为解决该问题自动加上误差值
if (this.index > 6) {
topHeight = topHeight + 6
}
container.scrollTop = topHeight
this.isCollapse = false
this.$emit('switchCollapse', this.isCollapse, this.index)
}
}
} }
} }
} }

View File

@@ -842,7 +842,7 @@ export default {
setup () { setup () {
const { params } = useRoute() const { params } = useRoute()
const pageType = params.typeName const pageType = params.typeName
const dateRangeValue = 60 * 24 const dateRangeValue = 60
const { startTime, endTime } = getNowTime(dateRangeValue) const { startTime, endTime } = getNowTime(dateRangeValue)
const timeFilter = ref({ startTime, endTime, dateRangeValue }) const timeFilter = ref({ startTime, endTime, dateRangeValue })

View File

@@ -61,7 +61,7 @@
<div class="metric__column"> <div class="metric__column">
<div class="overview__title">{{$t('detections.metric')}}</div> <div class="overview__title">{{$t('detections.metric')}}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content--metric ">{{getNameByEventType(detection.eventType) || '-'}}</div> <div class="row__content--metric ">{{detection.eventType || '-'}}</div>
</div> </div>
</div> </div>
<div class="metric__column"> <div class="metric__column">
@@ -95,7 +95,7 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { markRaw } from 'vue' import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions' import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy, getNameByEventType } from '@/utils/tools' import { sortBy, reverseSortBy } from '@/utils/tools'
import _ from 'lodash' import _ from 'lodash'
export default { export default {
name: 'DetectionPerformanceEventAppOverview', name: 'DetectionPerformanceEventAppOverview',
@@ -134,7 +134,6 @@ export default {
} }
}, },
methods: { methods: {
getNameByEventType,
query () { query () {
this.queryBasic().then(responses => { this.queryBasic().then(responses => {
responses && (this.basicInfo = responses) responses && (this.basicInfo = responses)
@@ -181,10 +180,6 @@ export default {
this.chartOptionMetric.series[2].data = this.metricList.slice(endIndex - 1, this.metricList.length).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number]) this.chartOptionMetric.series[2].data = this.metricList.slice(endIndex - 1, this.metricList.length).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
} }
this.chartOptionMetric.series.forEach(item => {
item.name = this.getNameByEventType(this.detection.eventType)
})
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric) this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
}, },

View File

@@ -63,7 +63,7 @@
<div class="metric__column"> <div class="metric__column">
<div class="overview__title">{{$t('detections.metric')}}</div> <div class="overview__title">{{$t('detections.metric')}}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content--metric ">{{getNameByEventType(detection.eventType) || '-'}}</div> <div class="row__content--metric ">{{detection.eventType || '-'}}</div>
</div> </div>
</div> </div>
<div class="metric__column"> <div class="metric__column">
@@ -98,7 +98,7 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { markRaw } from 'vue' import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions' import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy, getNameByEventType } from '@/utils/tools' import { sortBy, reverseSortBy } from '@/utils/tools'
import _ from 'lodash' import _ from 'lodash'
export default { export default {
name: 'DetectionPerformanceEventDomainOverview', name: 'DetectionPerformanceEventDomainOverview',
@@ -165,7 +165,6 @@ export default {
} }
}, },
methods: { methods: {
getNameByEventType,
query () { query () {
this.queryBasic().then(responses => { this.queryBasic().then(responses => {
responses && (this.basicInfo = responses) responses && (this.basicInfo = responses)
@@ -213,10 +212,6 @@ export default {
this.chartOptionMetric.series[2].data = this.metricList.slice(endIndex - 1, this.metricList.length).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number]) this.chartOptionMetric.series[2].data = this.metricList.slice(endIndex - 1, this.metricList.length).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
} }
this.chartOptionMetric.series.forEach(item => {
item.name = this.getNameByEventType(this.detection.eventType)
})
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric) this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
}, },

View File

@@ -54,7 +54,7 @@
<div class="metric__column"> <div class="metric__column">
<div class="overview__title">{{$t('detections.metric')}}</div> <div class="overview__title">{{$t('detections.metric')}}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content--metric ">{{getNameByEventType(detection.eventType) || '-'}}</div> <div class="row__content--metric ">{{getNameByType(detection.eventType) || '-'}}</div>
</div> </div>
</div> </div>
<div class="metric__column"> <div class="metric__column">
@@ -88,7 +88,7 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { markRaw } from 'vue' import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions' import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy, getNameByEventType } from '@/utils/tools' import { sortBy, reverseSortBy } from '@/utils/tools'
import _ from 'lodash' import _ from 'lodash'
export default { export default {
@@ -129,7 +129,6 @@ export default {
} }
}, },
methods: { methods: {
getNameByEventType,
query () { query () {
this.queryBasic().then(responses => { this.queryBasic().then(responses => {
responses && (this.basicInfo = responses) responses && (this.basicInfo = responses)
@@ -174,11 +173,9 @@ export default {
this.chartOptionMetric.series[1].data = this.metricList.slice(startIndex - 1, endIndex).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number]) this.chartOptionMetric.series[1].data = this.metricList.slice(startIndex - 1, endIndex).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
this.chartOptionMetric.series[2].data = this.metricList.slice(endIndex - 1, this.metricList.length).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number]) this.chartOptionMetric.series[2].data = this.metricList.slice(endIndex - 1, this.metricList.length).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
} }
this.chartOptionMetric.series.forEach(item => { this.chartOptionMetric.series.forEach(item => {
item.name = this.getNameByEventType(this.detection.eventType) item.name = this.getNameByType(this.detection.eventType)
}) })
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric) this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
}, },
queryMetric () { queryMetric () {
@@ -239,6 +236,29 @@ export default {
} }
}) })
window.open(href, '_blank') window.open(href, '_blank')
},
/**
* 通过事件类型转换对应名称
* @param type
* @returns {string}
*/
getNameByType (type) {
switch (type) {
case 'http error': {
return 'http error ratio'
}
case 'dns error': {
return 'dns error ratio'
}
case 'high dns response time': {
return 'dns response time'
}
// 目前ui并未找到此类型添加以防不测
case 'high http resopnse time':
default: {
return 'http response time'
}
}
} }
}, },
mounted () { mounted () {

View File

@@ -324,8 +324,8 @@ export default {
getMillisecond, getMillisecond,
query () { query () {
Promise.all([this.queryBasic(), this.queryEvent()]).then((responses) => { Promise.all([this.queryBasic(), this.queryEvent()]).then((responses) => {
responses[0].malwareTechniques = responses[0].malwareTechniques.length > 2 ? responses[0].malwareTechniques.replace('[', '').replace(']', '').split(',', 5).join(', ') : '' responses[0].malwareTechniques = responses[0].malwareTechniques.length > 2 ? responses[0].malwareTechniques.replace('[', '').split(',', 5).join(', ') : ''
responses[0].malwareGroups = responses[0].malwareGroups.length > 2 ? responses[0].malwareGroups.replace('[', '').replace(']', '').split(',', 5).join(', ') : '' responses[0].malwareGroups = responses[0].malwareGroups.length > 2 ? responses[0].malwareGroups.replace('[', '').split(',', 5).join(', ') : ''
responses[0].malwarePlatforms = responses[0].malwarePlatforms.length > 1 ? responses[0].malwarePlatforms : '' responses[0].malwarePlatforms = responses[0].malwarePlatforms.length > 1 ? responses[0].malwarePlatforms : ''
responses[0].malwareDescription = responses[0].malwareDescription.length > 1 ? responses[0].malwareDescription : '' responses[0].malwareDescription = responses[0].malwareDescription.length > 1 ? responses[0].malwareDescription : ''
responses[0] && (this.basicInfo = responses[0]) responses[0] && (this.basicInfo = responses[0])

View File

@@ -129,6 +129,16 @@ export default {
beforeUnmount () { beforeUnmount () {
window.removeEventListener('resize', this.debounceFunc) window.removeEventListener('resize', this.debounceFunc)
}, },
watch: {
top (n) {
const findIndex = this.anchorPoints.findLastIndex(a => a.top < n + 100)
if (findIndex > -1) {
this.anchorPoints.forEach((a, i) => {
a.isActive = i === findIndex
})
}
}
},
computed: { computed: {
iconClass () { iconClass () {
let className let className

View File

@@ -717,7 +717,7 @@ export default {
} }
}, },
setup () { setup () {
const dateRangeValue = 60 * 24 const dateRangeValue = 60
const { startTime, endTime } = getNowTime(dateRangeValue) const { startTime, endTime } = getNowTime(dateRangeValue)
const timeFilter = ref({ startTime, endTime, dateRangeValue }) const timeFilter = ref({ startTime, endTime, dateRangeValue })
return { return {

View File

@@ -22,14 +22,6 @@
<div class="row__label row__label--width130">{{$t('entities.org')}}</div> <div class="row__label row__label--width130">{{$t('entities.org')}}</div>
<div class="row__content">{{entityData.domainWhoisOrg || '-'}}</div> <div class="row__content">{{entityData.domainWhoisOrg || '-'}}</div>
</div> </div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpCompanyName')}}</div>
<div class="row__content">{{entityData.domainIcpCompanyName || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpLicense')}}</div>
<div class="row__content">{{entityData.domainIcpSiteLicense || '-'}}</div>
</div>
<!-- <div class="overview__row"> <!-- <div class="overview__row">
<div class="row__label row__label&#45;&#45;width130">{{$t('overall.remark')}}</div> <div class="row__label row__label&#45;&#45;width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{entityData.domainDescription || '-'}}</div> <div class="row__content">{{entityData.domainDescription || '-'}}</div>
@@ -360,9 +352,7 @@ export default {
domainDescription: response.data.result.domainDescription, domainDescription: response.data.result.domainDescription,
domainReputationScore: response.data.result.domainReputationScore, domainReputationScore: response.data.result.domainReputationScore,
domainWhoisAddress: response.data.result.domainWhoisAddress, domainWhoisAddress: response.data.result.domainWhoisAddress,
domainWhoisOrg: response.data.result.domainWhoisOrg, domainWhoisOrg: response.data.result.domainWhoisOrg
domainIcpCompanyName: response.data.result.domainIcpCompanyName,
domainIcpSiteLicense: response.data.result.domainIcpSiteLicense
} }
} }
}) })

View File

@@ -1,215 +1,34 @@
/**
* jest: https://jestjs.io/docs/getting-started
* vue-jest: https://test-utils.vuejs.org/guide/
* */
import Test from '../src/Test' import Test from '../src/Test'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { getNameByEventType } from '@/utils/tools'
import axios from 'axios'
import indexedDBUtils from '@/indexedDB'
import ElementPlus from 'element-plus'
// 模拟的axios返回数据 // 模拟vue-router库否则组件中引用vue-router的代码报错
const mockId = { data: 2 } jest.mock('vue-router', () => {
const mockTitle = { data: 'title2' } return {
const mockCount = { data: 999 } useRouter: function () { return { currentRoute: '/' } }
const mockDifferentParam0 = { data: 0 }
const mockDifferentParam1 = { data: 1 }
const mergeRequestParam0 = { data: 0 }
const mergeRequestParam1 = { data: 1 }
const mergeRequestChildParam0 = { data: 0 }
const mergeRequestChildParam1 = { data: 1 }
describe('单元测试demo', () => {
test('Vue组件--点击按钮后count的值+1', async () => {
// 若组件中使用了vue-router需要细化模拟相关内容
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
} }
}) })
test('点击按钮后count的值+1', async () => {
// 加载vue组件获得实例
const wrapper = mount(Test)
// 取到文本和按钮的dom // 取到文本和按钮的dom
const textNode = await wrapper.get('[test-id="count"]') const textNode = await wrapper.get('[data-test="count"]')
const button = await wrapper.get('[test-id="button"]') const button = await wrapper.get('[data-test="button"]')
// 断言文本dom的内容是否是'0' // 断言文本dom的内容是否是'0'
expect(textNode.text()).toBe('0') expect(textNode.text()).toContain('0')
// 模拟按钮dom点击执行click()方法 // 模拟按钮dom点击执行click()方法
await button.trigger('click') await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'1' // 断言点击按钮后文本dom的内容是否是'1'
expect(textNode.text()).toBe('1') expect(textNode.text()).toContain('1')
// 再次点击 // 再次点击
await button.trigger('click') await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'2' // 断言点击按钮后文本dom的内容是否是'2'
expect(textNode.text()).toBe('2') expect(textNode.text()).toContain('2')
/* 更多断言类型https://jestjs.io/docs/expect */
})
test('Vue组件--获取路由中的参数', async () => {
// query中带上lineTab参数
require('vue-router').useRoute.mockReturnValue({ query: { lineTab: 'total' } })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
// 取到文本和按钮的dom
const textNode = await wrapper.get('[test-id="tab"]')
expect(textNode.text()).toBe('total')
})
test('Vue组件--模拟获取localstorage/sessionStorage的内容', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 模拟localStorage的getItem
jest.spyOn(localStorage.__proto__, 'getItem').mockImplementation(key => key)
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
expect(wrapper.vm.localstorageValue).toBe('key')
})
test('Vue组件--直接获取vue实例中的data和method', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 模拟axios返回数据
// 测试内容只有一个axios请求的时候
axios.get.mockResolvedValue(mockCount)
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
mocks: {
$t: key => key
},
plugins: [ElementPlus]
}
})
const textNode = await wrapper.get('[test-id="count"]')
// 通过wrapper.vm.xxx直接执行click方法、获取data的数据 // 通过wrapper.vm.xxx直接执行click方法、获取data的数据
expect(wrapper.vm.count).toBe(0) expect(wrapper.vm.count).toEqual(2)
await wrapper.vm.click() await wrapper.vm.click()
expect(wrapper.vm.count).toBe(1) expect(wrapper.vm.count).toEqual(3)
await wrapper.vm.getCount()
await wrapper.vm.$nextTick(() => {
expect(textNode.text()).toBe('999')
})
})
test('Vue组件--多个axios请求', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 模拟axios返回数据
// 测试内容有多个url不同的axios请求的话需分开写
axios.get.mockImplementation(url => {
switch (url) {
case '/api/getObjId':
return Promise.resolve(mockId)
case '/api/getObjTitle':
return Promise.resolve(mockTitle)
}
})
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
const textNode = await wrapper.get('[test-id="id"]')
const textNode2 = await wrapper.get('[test-id="title"]')
expect(textNode2.text()).toBe('title')
await wrapper.vm.getObj()
await wrapper.vm.$nextTick(() => {
expect(textNode.text()).toBe('2')
expect(textNode2.text()).toBe('title2')
// 匹配整个对象
expect(wrapper.vm.obj).toEqual({ id: 2, title: 'title2' })
})
})
test('Vue组件--多个同url不同参数的axios请求', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 模拟axios返回数据
axios.get.mockResolvedValueOnce(mockDifferentParam0)
axios.get.mockResolvedValueOnce(mockDifferentParam1)
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
await wrapper.vm.differentRequestParam()
expect(wrapper.vm.differentParamData0).toBe(0)
expect(wrapper.vm.differentParamData1).toBe(1)
})
test('Vue组件--axios请求内包含多个不同url的组合请求', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 模拟axios返回数据
axios.get.mockResolvedValueOnce(mergeRequestParam0)
axios.get.mockResolvedValueOnce(mergeRequestParam1)
axios.get.mockImplementation(url => { // 更多断言类型https://jestjs.io/zh-Hans/docs/expect
switch (url) {
case '/api/getChildId':
return Promise.resolve(mergeRequestChildParam0)
case '/api/getChildTitle':
return Promise.resolve(mergeRequestChildParam1)
}
})
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
await wrapper.vm.mergeRequest()
await wrapper.vm.$nextTick(() => {
expect(wrapper.vm.mergeRequestData0).toBe(0)
expect(wrapper.vm.mergeRequestData1).toBe(1)
expect(wrapper.vm.mergeRequestChildData0).toBe(0)
expect(wrapper.vm.mergeRequestChildData1).toBe(1)
})
})
test('Vue组件--模拟indexedDB操作', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
// 模拟indexedDB的内容
indexedDBUtils.selectTable.mockReturnValue({
put: jest.fn(),
get: jest.fn().mockResolvedValue({ a: 1 })
})
await wrapper.vm.setIndexedDBValue()
await wrapper.vm.getIndexedDBValue()
expect(wrapper.vm.indexedDBValue).toEqual({ a: 1 })
})
test('Vue组件--使用第三方组件的情况以el-table为例', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
// 加载vue组件获得实例
const wrapper = mount(Test, {
global: {
plugins: [ElementPlus]
}
})
// 执行nextTick等待el-table渲染完成
await wrapper.vm.$nextTick()
const textNode = await wrapper.get('[test-id="name0"]')
const textNode2 = await wrapper.get('[test-id="age1"]')
expect(textNode.text()).toBe('a')
expect(textNode2.text()).toBe('11')
})
test('js方法--getNameByEventType', async () => {
expect(getNameByEventType('http error')).toBe('http error ratio')
})
}) })

View File

@@ -1,41 +0,0 @@
import { config } from '@vue/test-utils'
/* 开启测试 */
config.global.mocks.isUnitTesting = true
/* 初始化dayjs */
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
const advancedFormat = require('dayjs/plugin/advancedFormat')
const weekday = require('dayjs/plugin/weekday')
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(advancedFormat)
dayjs.extend(weekday)
window.$dayJs = dayjs
// 引入 lodash 工具 模拟 lodash
const _ = require('lodash') // lodash工具
/* 模拟vue-router库否则组件中引用vue-router的代码报错 */
jest.mock('vue-router', () => {
return {
useRouter: jest.fn(),
useRoute: jest.fn(),
createWebHashHistory: jest.fn(),
createRouter: jest.fn().mockReturnValue({
beforeEach: jest.fn()
})
}
})
/* 模拟axios */
jest.mock('axios')
/* 模拟indexedDB工具 */
jest.mock('@/indexedDB')
/* 模拟$t */
config.global.mocks.$t = key => key
/* 模拟$route具体用例中需要不同值时重写覆盖即可 */
config.global.mocks.$route = { query: '' }
/* 模拟 lodash */
config.global.mocks.$_ = _
/* 消除warn */
jest.spyOn(console, 'warn').mockImplementation(() => {})

View File

@@ -1,130 +0,0 @@
import linkBlock from '@/views/charts2/charts/linkMonitor/LinkBlock'
import { mount } from '@vue/test-utils'
import axios from 'axios'
const mockGet1 = {
data: { status: 200, code: 200, queryKey: '549b4c3bcabf0feee193b834671879de', success: true, message: null, statistics: { elapsed: 3, rows_read: 11480, bytes_read: 459200, result_size: 1214, result_rows: 21 }, job: null, formatType: 'json', meta: [{ name: 'link_id', type: 'string', category: 'Dimension' }, { name: 'egress_bytes', type: 'long', category: 'Metric' }, { name: 'ingress_bytes', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ linkId: '257', egressBytes: '0', egressBitsRate: 0, ingressBytes: '493739879', ingressBitsRate: 1097199.76 }, { linkId: '256', egressBytes: '4147998874', egressBitsRate: 9217775.28, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1024', egressBytes: '4229808296', egressBitsRate: 9399574, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1793', egressBytes: '0', egressBitsRate: 0, ingressBytes: '604840505', ingressBitsRate: 1344090 }, { linkId: '2817', egressBytes: '0', egressBitsRate: 0, ingressBytes: '430811638', ingressBitsRate: 957359.2 }, { linkId: '0', egressBytes: '819878366014', egressBitsRate: 1821951924.48, ingressBytes: '140774357362', ingressBitsRate: 312831905.28 }, { linkId: '1281', egressBytes: '0', egressBitsRate: 0, ingressBytes: '544675122', ingressBitsRate: 1210389.12 }, { linkId: '2049', egressBytes: '0', egressBitsRate: 0, ingressBytes: '711346274', ingressBitsRate: 1580769.52 }, { linkId: '1536', egressBytes: '4195896971', egressBitsRate: 9324215.52, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '2305', egressBytes: '0', egressBitsRate: 0, ingressBytes: '524865003', ingressBitsRate: 1166366.64 }, { linkId: '1792', egressBytes: '4145790227', egressBitsRate: 9212867.2, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1025', egressBytes: '0', egressBitsRate: 0, ingressBytes: '492227445', ingressBitsRate: 1093838.8 }, { linkId: '2816', egressBytes: '4000074817', egressBitsRate: 8889055.12, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '513', egressBytes: '0', egressBitsRate: 0, ingressBytes: '1444814826', ingressBitsRate: 3210699.6 }, { linkId: '768', egressBytes: '4370006099', egressBitsRate: 9711124.64, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '512', egressBytes: '3894190397', egressBitsRate: 8653756.4, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '769', egressBytes: '0', egressBitsRate: 0, ingressBytes: '1877580759', ingressBitsRate: 4172401.68 }, { linkId: '2304', egressBytes: '3767761711', egressBitsRate: 8372803.84, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1537', egressBytes: '0', egressBitsRate: 0, ingressBytes: '367986916', ingressBitsRate: 817748.72 }, { linkId: '1280', egressBytes: '4040444894', egressBitsRate: 8978766.4, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '2048', egressBytes: '4682050724', egressBitsRate: 10404557.2, ingressBytes: '0', ingressBitsRate: 0 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20arrayJoin%28splitByChar%28%27_%27%2C%20concat%28toString%28common_egress_link_id%29%2C%20%27_%27%2C%20toString%28common_ingress_link_id%29%29%29%29%20AS%20link_id%2CSUM%28IF%28toString%28common_egress_link_id%29%20%3D%20link_id%2C%20traffic_outbound_byte%2C%200%29%29%20AS%20egress_bytes%2CSUM%28IF%28toString%28common_ingress_link_id%29%20%3D%20link_id%2C%20traffic_inbound_byte%2C%200%29%29%20AS%20ingress_bytes%20FROM%20metric_link%20WHERE%20stat_time%20%3E%3D%201675303793%20AND%20stat_time%20%3C%201675307393%20GROUP%20BY%20link_id&format=json&option=real-time', msg: 'OK' }
}
const mockGet2 = {
data: { status: 200, code: 200, queryKey: 'ee2e820b1275748167cdee82b2b4ea40', success: true, message: null, statistics: { elapsed: 3, rows_read: 11480, bytes_read: 618564, result_size: 1053, result_rows: 10 }, job: null, formatType: 'json', meta: [{ name: 'egress_link_direction', type: 'string', category: 'Dimension' }, { name: 'ingress_link_direction', type: 'string', category: 'Dimension' }, { name: 'egress_bytes', type: 'long', category: 'Metric' }, { name: 'ingress_bytes', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ egressLinkDirection: '太原', ingressLinkDirection: '西宁', egressBytes: '2407509269', egressBitsRate: 5350020.56, ingressBytes: '193368911', ingressBitsRate: 429708.72 }, { egressLinkDirection: '西安', ingressLinkDirection: '西安', egressBytes: '3609358392', egressBitsRate: 8020796.4, ingressBytes: '345009143', ingressBitsRate: 766686.96 }, { egressLinkDirection: '西宁', ingressLinkDirection: '西安', egressBytes: '1273508499', egressBitsRate: 2830018.88, ingressBytes: '122646511', ingressBitsRate: 272547.84 }, { egressLinkDirection: '太原', ingressLinkDirection: '太原', egressBytes: '14840136119', egressBitsRate: 32978080.24, ingressBytes: '3386427658', ingressBitsRate: 7525394.8 }, { egressLinkDirection: '西安', ingressLinkDirection: '太原', egressBytes: '7070361369', egressBitsRate: 15711914.16, ingressBytes: '1853445429', ingressBitsRate: 4118767.6 }, { egressLinkDirection: '西宁', ingressLinkDirection: '太原', egressBytes: '2619231460', egressBitsRate: 5820514.32, ingressBytes: '291561196', ingressBitsRate: 647913.76 }, { egressLinkDirection: '', ingressLinkDirection: '', egressBytes: '409939183007', egressBitsRate: 910975962.24, ingressBytes: '70387178681', ingressBitsRate: 156415952.64 }, { egressLinkDirection: '太原', ingressLinkDirection: '西安', egressBytes: '7808050741', egressBitsRate: 17351223.84, ingressBytes: '1001570985', ingressBitsRate: 2225713.28 }, { egressLinkDirection: '西宁', ingressLinkDirection: '西宁', egressBytes: '337068337', egressBitsRate: 749040.72, ingressBytes: '165230290', ingressBitsRate: 367178.4 }, { egressLinkDirection: '西安', ingressLinkDirection: '西宁', egressBytes: '1508798824', egressBitsRate: 3352886.24, ingressBytes: '133628244', ingressBitsRate: 296951.68 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20egress_link_direction%20AS%20egress_link_direction%2C%20ingress_link_direction%20AS%20ingress_link_direction%2C%20SUM%28traffic_outbound_byte%29%20AS%20egress_bytes%2C%20SUM%28traffic_inbound_byte%29%20AS%20ingress_bytes%20FROM%20metric_link%20WHERE%20stat_time%20%3E%3D%201675303793%20AND%20stat_time%20%3C%201675307393%20GROUP%20BY%20egress_link_direction%2C%20ingress_link_direction&format=json&option=real-time', msg: 'OK' }
}
const linkInfoData = [{ originalLinkId: '256', linkId: 'Hundredgige1', direction: 'egress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '257', linkId: 'Hundredgige1', direction: 'ingress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '512', linkId: 'Hundredgige2', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '513', linkId: 'Hundredgige2', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '768', linkId: 'Hundredgige3', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '769', linkId: 'Hundredgige3', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1024', linkId: 'Hundredgige4', direction: 'egress', nextHop: '西宁', bandwidth: 100000000000 }, { originalLinkId: '1025', linkId: 'Hundredgige4', direction: 'ingress', nextHop: '西宁', bandwidth: 100000000000 }, { originalLinkId: '1280', linkId: 'Hundredgige5', direction: 'egress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '1281', linkId: 'Hundredgige5', direction: 'ingress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '1536', linkId: 'Hundredgige6', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1537', linkId: 'Hundredgige6', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1792', linkId: 'Hundredgige7', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1793', linkId: 'Hundredgige7', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2048', linkId: 'Hundredgige8', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2049', linkId: 'Hundredgige8', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2304', linkId: 'Hundredgige9', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2305', linkId: 'Hundredgige9', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2816', linkId: 'Hundredgige10', direction: 'egress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '2817', linkId: 'Hundredgige10', direction: 'ingress', nextHop: '西安', bandwidth: 100000000000 }]
const linkInfo = JSON.stringify(linkInfoData)
const timeFilter = {
dateRangeValue: -1,
startTime: 1673244000000,
endTime: 1673247600000
}
var wrapper = null
/**
* 进行axios请求并挂载vue实例
* @param list
*/
function axiosPostAndMounted (list) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const data = list || mockGet1
// 模拟axios返回数据
axios.get.mockImplementation(url => {
switch (url) {
case '/interface/link/overview/analysis':
return Promise.resolve(data)
case '/interface/link/overview/nextHopAnalysis':
return Promise.resolve(mockGet2)
}
})
// 模拟localStorage获取数据
// eslint-disable-next-line no-proto
jest.spyOn(localStorage.__proto__, 'getItem').mockImplementation(key => linkInfo)
// 加载vue组件获得实例
wrapper = mount(linkBlock, {
propsData: {
timeFilter
}
})
}
describe('views/charts2/charts/linkMonitor/LinkBlock.vue测试', () => {
test('测试链路蜂窝图和下一跳蜂窝图从大到小排列', async () => {
axiosPostAndMounted()
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(() => {
const linkBlockTotal0 = wrapper.get('[test-id="linkBlockTotal0"]')
const linkBlockTotal1 = wrapper.get('[test-id="linkBlockTotal1"]')
const linkBlockTotal2 = wrapper.get('[test-id="linkBlockTotal2"]')
const linkBlockTotal3 = wrapper.get('[test-id="linkBlockTotal3"]')
const linkBlockTotal4 = wrapper.get('[test-id="linkBlockTotal4"]')
const linkBlockTotal5 = wrapper.get('[test-id="linkBlockTotal5"]')
const linkBlockTotal6 = wrapper.get('[test-id="linkBlockTotal6"]')
const linkBlockTotal7 = wrapper.get('[test-id="linkBlockTotal7"]')
const linkBlockTotal8 = wrapper.get('[test-id="linkBlockTotal8"]')
const linkBlockTotal9 = wrapper.get('[test-id="linkBlockTotal9"]')
expect(linkBlockTotal0.text()).toBe('13.88 Mbps')
expect(linkBlockTotal1.text()).toBe('11.99 Mbps')
expect(linkBlockTotal2.text()).toBe('11.86 Mbps')
expect(linkBlockTotal3.text()).toBe('10.56 Mbps')
expect(linkBlockTotal4.text()).toBe('10.49 Mbps')
expect(linkBlockTotal5.text()).toBe('10.31 Mbps')
expect(linkBlockTotal6.text()).toBe('10.19 Mbps')
expect(linkBlockTotal7.text()).toBe('10.14 Mbps')
expect(linkBlockTotal8.text()).toBe('9.85 Mbps')
expect(linkBlockTotal9.text()).toBe('9.54 Mbps')
const nextHopTotal0 = wrapper.get('[test-id="nextHopTotal0"]')
const nextHopTotal1 = wrapper.get('[test-id="nextHopTotal1"]')
const nextHopTotal2 = wrapper.get('[test-id="nextHopTotal2"]')
expect(nextHopTotal0.text()).toBe('67.97 Mbps')
expect(nextHopTotal1.text()).toBe('30.35 Mbps')
expect(nextHopTotal2.text()).toBe('10.49 Mbps')
resolve()
}, 200))
})
test('鼠标移动到链路蜂窝图第一个测试total显示', async () => {
// 以"Hundredgige3"为例对应出方向linkId为768对应出方向流量为9711124.64
// 对应入方向linkId为769对应入方向流量为4172401.68修改流量测试total
const list = JSON.parse(JSON.stringify(mockGet1))
list.data.data.result[14].egressBitsRate = 9000000.00
list.data.data.result[16].ingressBitsRate = 4000000.00
axiosPostAndMounted(list)
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(() => {
const linkBlockTotal0 = wrapper.get('[test-id="linkBlockTotal0"]')
const linkBlockEgressUsage0 = wrapper.get('[test-id="linkBlockEgressUsage0"]')
const linkBlockIngressUsage0 = wrapper.get('[test-id="linkBlockIngressUsage0"]')
expect(linkBlockTotal0.text()).toBe('13 Mbps')
expect(linkBlockEgressUsage0.text()).toBe('< 0.01%')
expect(linkBlockIngressUsage0.text()).toBe('< 0.01%')
resolve()
}, 200))
})
test('鼠标移动到链路蜂窝图第一个,测试出入流量占比', async () => {
// 以"Hundredgige3"为例修改其出入流量为100000000000测试上行流量占比为100%下行占比为0%的情况
const list = JSON.parse(JSON.stringify(mockGet1))
list.data.data.result[14].egressBitsRate = 100000000000.00
list.data.data.result[16].ingressBitsRate = 0
axiosPostAndMounted(list)
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(() => {
const linkBlockTotal0 = wrapper.get('[test-id="linkBlockTotal0"]')
const linkBlockEgressUsage0 = wrapper.get('[test-id="linkBlockEgressUsage0"]')
const linkBlockIngressUsage0 = wrapper.get('[test-id="linkBlockIngressUsage0"]')
expect(linkBlockTotal0.text()).toBe('100 Gbps')
expect(linkBlockEgressUsage0.text()).toBe('100.00%')
expect(linkBlockIngressUsage0.text()).toBe('0%')
resolve()
}, 200))
})
})

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,37 +0,0 @@
import NetworkOverviewDdosDetection from '@/views/charts2/charts/networkOverview/NetworkOverviewDdosDetection'
import { mount } from '@vue/test-utils'
import axios from 'axios'
const mockGet = {
data: { status: 200, code: 200, queryKey: 'dec6723e173e8fa2b00917dc597bfb27', success: true, message: null, statistics: { elapsed: 0, rows_read: 2, result_size: 58, result_rows: 1 }, job: null, formatType: 'json', meta: [{ name: 'attack_event_count', type: 'long', category: 'Metric' }, { name: 'attacker_count', type: 'long', category: 'Metric' }, { name: 'victim_count', type: 'long', category: 'Metric' }], data: { resultType: 'object', result: [{ attackEventCount: 1200000, attackerCount: 2687878, victimCount: 36676767 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20COUNT%28*%29%20AS%20attack_event_count%2C%20COUNT%28DISTINCT%28offender_ip%29%29%20AS%20attacker_count%2C%20COUNT%28DISTINCT%28victim_ip%29%29%20AS%20victim_count%20FROM%20security_event%20WHERE%20start_time%20%3E%3D%201675043912%20AND%20start_time%20%3C%201675047512%20AND%20security_type%20%3D%20%27ddos%27&format=json&option=real-time', msg: 'OK' }
}
const timeFilter = {
dateRangeValue: -1,
startTime: 1675043912,
endTime: 1675047512
}
describe('views/charts2/charts/networkOverview/NetworkOverviewDdosDetection.vue测试', () => {
test('攻击、受害、攻击数事件ddos检测图', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 返回数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewDdosDetection, {
propsData: {
timeFilter
}
})
const attackEventCount = wrapper.get('[test-id="attackEventCount"]')
const attackerCount = wrapper.get('[test-id="attackerCount"]')
const victimCount = wrapper.get('[test-id="victimCount"]')
await new Promise(resolve => setTimeout(() => {
expect(attackEventCount.text()).toEqual('1.20 M')
expect(attackerCount.text()).toEqual('2.69 M')
expect(victimCount.text()).toEqual('36.68 M')
resolve()
}, 200))
})
})

View File

@@ -1,141 +0,0 @@
import NetworkOverviewLine from '@/views/charts2/charts/networkOverview/NetworkOverviewLine'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import mockData from './NetworkOverviewLineMockData'
const timeFilter = {
dateRangeValue: -1,
startTime: 1673244000000,
endTime: 1673247600000
}
const chart = {"id":1,"name":"network overview line","i18n":"","panelId":1,"pid":0,"type":102,"x":0,"y":0,"w":19,"h":6,"children":[],"panel":{"id":1,"name":"Network Overview"},"i":1,"category":"echarts","firstShow":false,"moved":false}
function init () {
require('vue-router').useRoute.mockReturnValue({ query: {} })
}
describe('views/charts2/charts/networkOverview/NetworkOverviewLine.vue测试', () => {
test('Metric=Bits/s无数据空数组', async () => {
init()
axios.get.mockResolvedValue(mockData.empty)
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart
}
})
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(async () => {
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
const textNode3 = await wrapper.get('[test-id="tabContent3"]')
const textNode4 = await wrapper.get('[test-id="tabContent4"]')
const textNode5 = await wrapper.get('[test-id="tabContent5"]')
expect(textNode0.text()).toEqual('-')
expect(textNode1.text()).toEqual('-')
expect(textNode2.text()).toEqual('-')
expect(textNode3.text()).toEqual('-')
expect(textNode4.text()).toEqual('-')
expect(textNode5.text()).toEqual('-')
resolve()
}, 200))
})
test('Metric=Bits/s0和大数值', async () => {
init()
axios.get.mockResolvedValue(mockData.bytes.boundary)
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart
}
})
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(async () => {
// total页签固定显示数据是0也一样
const titleNode0 = await wrapper.get('[test-id="tabTitle0"]')
const titleNode1 = await wrapper.get('[test-id="tabTitle2"]')
const titleNode2 = await wrapper.get('[test-id="tabTitle5"]')
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent2"]')
const textNode2 = await wrapper.get('[test-id="tabContent5"]')
expect(titleNode0.text()).toEqual('network.total')
expect(titleNode1.text()).toEqual('network.outbound')
expect(titleNode2.text()).toEqual('network.other')
expect(textNode0.text()).toEqual('0bps')
expect(textNode1.text()).toEqual('95.23Ebps')
expect(textNode2.text()).toEqual('0.01bps')
resolve()
}, 200))
})
test('Metric=Bits/s点击第三个tab', async () => {
init()
// 模拟axios返回数据
axios.get.mockResolvedValue(mockData.common)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart
}
})
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(async () => {
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
expect(textNode0.text()).toEqual('112.04Mbps')
expect(textNode1.text()).toEqual('18.59Mbps')
expect(textNode2.text()).toEqual('87.56Mbps')
resolve()
}, 200))
// 点击tab后是否切换高亮状态
const textNode3 = await wrapper.get('[test-id="tab2"]')
await textNode3.trigger('click')
expect(textNode3.classes()).toContain('is-active')
})
test('Metric=Packets/s', async () => {
init()
// 模拟axios返回数据
axios.get.mockResolvedValue(mockData.common)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart,
metric: 'Packets/s'
}
})
await new Promise(resolve => setTimeout(async () => {
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
expect(textNode0.text()).toEqual('14.06Kpackets/s')
expect(textNode1.text()).toEqual('4.24Kpackets/s')
expect(textNode2.text()).toEqual('9.17Kpackets/s')
resolve()
}, 200))
})
test('Metric=Sessions/s', async () => {
init()
// 模拟axios返回数据
axios.get.mockResolvedValue(mockData.common)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart,
metric: 'Sessions/s'
}
})
await new Promise(resolve => setTimeout(async () => {
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
expect(textNode0.text()).toEqual('29.89sessions/s')
resolve()
}, 200))
})
})

View File

@@ -1,73 +0,0 @@
const mockData = {
// 空
empty: {
data: {
"status": 200,
"code": 200,
"data": {
"resultType": "object",
"result" : []
}
}
},
bytes: {
// 边界
boundary: {
data: {
"status": 200,
"code": 200,
"data": {
"resultType": "object",
"result": [
{
"type": "bytes",
"totalBitsRate": {
"analysis": {
"avg": "0"
}
},
"inboundBitsRate": {
"analysis": {
"avg": "0"
}
},
"outboundBitsRate": {
"analysis": {
"avg": "95229000000000000000"
}
},
"internalBitsRate": {
"analysis": {
"avg": "0"
}
},
"throughBitsRate": {
"analysis": {
"avg": "0"
}
},
"other": {
"analysis": {
"avg": "0.01"
}
}
},
{
"type": "packets"
},
{
"type": "sessions"
}
]
},
"msg": "OK"
}
}
},
// 正常数据
common: {
data: {"status":200,"code":200,"data":{"resultType":"object","result":[{"type":"bytes","totalBitsRate":{"values":[[1673247564,"96801019.52"]],"analysis":{"avg":"112042808.24","max":"301842105.76","min":"52096324","p95":"168089003.35199997"}},"inboundBitsRate":{"values":[[1673247564,"11814508.48"]],"analysis":{"avg":"18587597.36","max":"137528138.88","min":"3181142.88","p95":"49561521.447999954"}},"outboundBitsRate":{"values":[[1673247564,"84282965.52"]],"analysis":{"avg":"87557861.44","max":"290402258","min":"45337684.48","p95":"121041718.81199999"}},"internalBitsRate":{"values":[[1673247564,"9125.12"]],"analysis":{"avg":"278114.32","max":"2215460.48","min":"0","p95":"923494.5719999998"}},"throughBitsRate":{"values":[[1673247564,"694420.48"]],"analysis":{"avg":"5619235.12","max":"42455480.24","min":"262607.76","p95":"13559588.195999999"}},"other":{"values":[[1673247564,"0.00"]],"analysis":{"avg":"0.01","max":"0.08","min":"0.00","p95":"0.08"}}},{"type":"packets","totalPacketsRate":{"values":[[1673247564,"12077.53"]],"analysis":{"avg":"14062.37","max":"32840.42","min":"6564.17","p95":"20923.167999999987"}},"inboundPacketsRate":{"values":[[1673247564,"3865.58"]],"analysis":{"avg":"4241.61","max":"15460.03","min":"1918.22","p95":"8549.799999999997"}},"outboundPacketsRate":{"values":[[1673247564,"8118.89"]],"analysis":{"avg":"9170.98","max":"27134.58","min":"4510.25","p95":"13690.540999999996"}},"internalPacketsRate":{"values":[[1673247564,"15.89"]],"analysis":{"avg":"35.95","max":"276.47","min":"0.00","p95":"122.49749999999999"}},"throughPacketsRate":{"values":[[1673247564,"77.17"]],"analysis":{"avg":"613.82","max":"3768.56","min":"42.92","p95":"1279.757499999999"}},"other":{"values":[[1673247564,"0.00"]],"analysis":{"avg":"0","max":"0.01","min":"0.00","p95":"0.01"}}},{"type":"sessions","totalSessionsRate":{"values":[[1673247564,"29.92"]],"analysis":{"avg":"29.89","max":"29.92","min":"29.67","p95":"29.92"}}}]},"msg":"OK"}
}
}
export default mockData

File diff suppressed because one or more lines are too long

View File

@@ -1,258 +0,0 @@
import NpmAppEventTable from '@/views/charts2/charts/npm/NpmAppEventTable'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import ElementPlus from 'element-plus'
const timeFilter = {
dateRangeValue: -1,
startTime: 1675558657,
endTime: 1675731457
}
const mockGet = { data: { status: 200, code: 200, queryKey: '88eeb92e0ddb40c0327db494cfe5c74c', success: true, message: null, statistics: { elapsed: 0, rows_read: 2, result_size: 752, result_rows: 10 }, job: null, formatType: 'json', meta: [{ name: 'app_name', type: 'string', category: 'Dimension' }, { name: 'event_severity', type: 'string', category: 'Dimension' }, { name: 'event_type', type: 'string', category: 'Dimension' }, { name: 'count', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ appName: 'youku', eventSeverity: 'info', eventType: 'http error', count: 6 }, { appName: 'uplive', eventSeverity: 'critical', eventType: 'http error', count: 5 }, { appName: 'youku', eventSeverity: 'low', eventType: 'http error', count: 4 }, { appName: 'apple_hls', eventSeverity: 'info', eventType: 'http error', count: 3 }, { appName: 'apple_hls', eventSeverity: 'low', eventType: 'http error', count: 3 }, { appName: 'apple_hls', eventSeverity: 'medium', eventType: 'http error', count: 2 }, { appName: 'uplive', eventSeverity: 'high', eventType: 'http error', count: 2 }, { appName: 'windows_update', eventSeverity: 'medium', eventType: 'http error', count: 2 }, { appName: 'apple_hls', eventSeverity: 'critical', eventType: 'http error', count: 1 }, { appName: 'cloudflare', eventSeverity: 'info', eventType: 'http error', count: 1 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20app_name%20AS%20app_name%2C%20event_severity%20AS%20event_severity%2C%20event_type%20AS%20event_type%2C%20COUNT%28*%29%20AS%20count%20FROM%20performance_event%20WHERE%20start_time%20%3E%3D%201675580110%20AND%20end_time%20%3C%201675752910%20AND%20entity_type%20%3D%20%27app%27%20GROUP%20BY%20app_name%2Cevent_severity%2Cevent_type%20ORDER%20BY%20count%20DESC%20%20LIMIT%2010%20&format=json&option=real-time', msg: 'OK' } }
describe('views/charts2/charts/npm/NpmAppEventTable.vue测试', () => {
test('Npm 事件APP事件信息表格 严重程度色块验证', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NpmAppEventTable, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
// critical
const criticalArray = [1, 8]
for (let index = 0; index < criticalArray.length; index++) {
const rowIndex = criticalArray[index]
for (let i = 1; i <= 5; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
}
// high
const highArray = [6]
for (let index = 0; index < highArray.length; index++) {
const rowIndex = highArray[index]
for (let i = 1; i <= 4; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 5; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
// medium
const mediumArray = [5, 7]
for (let index = 0; index < mediumArray.length; index++) {
const rowIndex = mediumArray[index]
for (let i = 1; i <= 3; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 4; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
// low
const lowArray = [2, 4]
for (let index = 0; index < lowArray.length; index++) {
const rowIndex = lowArray[index]
for (let i = 1; i <= 2; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 3; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
// info
const infoArray = [0, 3, 9]
for (let index = 0; index < infoArray.length; index++) {
const rowIndex = infoArray[index]
for (let i = 1; i <= 1; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 2; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(应用数据列)', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NpmAppEventTable, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="appName0"]')
const textNode1 = wrapper.find('[test-id="appName1"]')
const textNode2 = wrapper.find('[test-id="appName2"]')
const textNode3 = wrapper.find('[test-id="appName3"]')
const textNode4 = wrapper.find('[test-id="appName4"]')
const textNode5 = wrapper.find('[test-id="appName5"]')
const textNode6 = wrapper.find('[test-id="appName6"]')
const textNode7 = wrapper.find('[test-id="appName7"]')
const textNode8 = wrapper.find('[test-id="appName8"]')
const textNode9 = wrapper.find('[test-id="appName9"]')
expect(textNode0.text()).toEqual('youku')
expect(textNode1.text()).toEqual('uplive')
expect(textNode2.text()).toEqual('youku')
expect(textNode3.text()).toEqual('apple_hls')
expect(textNode4.text()).toEqual('apple_hls')
expect(textNode5.text()).toEqual('apple_hls')
expect(textNode6.text()).toEqual('uplive')
expect(textNode7.text()).toEqual('windows_update')
expect(textNode8.text()).toEqual('apple_hls')
expect(textNode9.text()).toEqual('cloudflare')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(严重程度数据列)', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NpmAppEventTable, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="eventSeverity0"]')
const textNode1 = wrapper.find('[test-id="eventSeverity1"]')
const textNode2 = wrapper.find('[test-id="eventSeverity2"]')
const textNode3 = wrapper.find('[test-id="eventSeverity3"]')
const textNode4 = wrapper.find('[test-id="eventSeverity4"]')
const textNode5 = wrapper.find('[test-id="eventSeverity5"]')
const textNode6 = wrapper.find('[test-id="eventSeverity6"]')
const textNode7 = wrapper.find('[test-id="eventSeverity7"]')
const textNode8 = wrapper.find('[test-id="eventSeverity8"]')
const textNode9 = wrapper.find('[test-id="eventSeverity9"]')
expect(textNode0.text()).toEqual('info')
expect(textNode1.text()).toEqual('critical')
expect(textNode2.text()).toEqual('low')
expect(textNode3.text()).toEqual('info')
expect(textNode4.text()).toEqual('low')
expect(textNode5.text()).toEqual('medium')
expect(textNode6.text()).toEqual('high')
expect(textNode7.text()).toEqual('medium')
expect(textNode8.text()).toEqual('critical')
expect(textNode9.text()).toEqual('info')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(事件类型数据列)', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NpmAppEventTable, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="eventType0"]')
const textNode1 = wrapper.find('[test-id="eventType1"]')
const textNode2 = wrapper.find('[test-id="eventType2"]')
const textNode3 = wrapper.find('[test-id="eventType3"]')
const textNode4 = wrapper.find('[test-id="eventType4"]')
const textNode5 = wrapper.find('[test-id="eventType5"]')
const textNode6 = wrapper.find('[test-id="eventType6"]')
const textNode7 = wrapper.find('[test-id="eventType7"]')
const textNode8 = wrapper.find('[test-id="eventType8"]')
const textNode9 = wrapper.find('[test-id="eventType9"]')
expect(textNode0.text()).toEqual('http error')
expect(textNode1.text()).toEqual('http error')
expect(textNode2.text()).toEqual('http error')
expect(textNode3.text()).toEqual('http error')
expect(textNode4.text()).toEqual('http error')
expect(textNode5.text()).toEqual('http error')
expect(textNode6.text()).toEqual('http error')
expect(textNode7.text()).toEqual('http error')
expect(textNode8.text()).toEqual('http error')
expect(textNode9.text()).toEqual('http error')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(事件数量数据列)', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NpmAppEventTable, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="count0"]')
const textNode1 = wrapper.find('[test-id="count1"]')
const textNode2 = wrapper.find('[test-id="count2"]')
const textNode3 = wrapper.find('[test-id="count3"]')
const textNode4 = wrapper.find('[test-id="count4"]')
const textNode5 = wrapper.find('[test-id="count5"]')
const textNode6 = wrapper.find('[test-id="count6"]')
const textNode7 = wrapper.find('[test-id="count7"]')
const textNode8 = wrapper.find('[test-id="count8"]')
const textNode9 = wrapper.find('[test-id="count9"]')
expect(textNode0.text()).toEqual('6')
expect(textNode1.text()).toEqual('5')
expect(textNode2.text()).toEqual('4')
expect(textNode3.text()).toEqual('3')
expect(textNode4.text()).toEqual('3')
expect(textNode5.text()).toEqual('2')
expect(textNode6.text()).toEqual('2')
expect(textNode7.text()).toEqual('2')
expect(textNode8.text()).toEqual('1')
expect(textNode9.text()).toEqual('1')
resolve()
}, 200))
})
})

File diff suppressed because one or more lines are too long

View File

@@ -1,54 +0,0 @@
import NpmEventsHeader from '@/views/charts2/charts/npm/NpmEventsHeader'
import { mount } from '@vue/test-utils'
import axios from 'axios'
// 模拟数据
const chartData = {
data: { status: 200, code: 200, queryKey: '6480498979f7501d822572ebeb9e9665', success: true, message: null, statistics: { elapsed: 0, rows_read: 3, result_size: 167, result_rows: 5 }, job: null, formatType: 'json', meta: [{ name: 'event_severity', type: 'string', category: 'Dimension' }, { name: 'count', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ eventSeverity: 'critical', count: 322334 }, { eventSeverity: 'high', count: 1111 }, { eventSeverity: 'info', count: 122222 }, { eventSeverity: 'low', count: 14456678 }, { eventSeverity: 'medium', count: 2000000 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20event_severity%20AS%20event_severity%2C%20COUNT%28*%29%20AS%20count%20FROM%20performance_event%20WHERE%20start_time%20%3E%3D%201675026686%20AND%20end_time%20%3C%201675048286%20GROUP%20BY%20event_severity&format=json&option=real-time', msg: 'OK' }
}
// type
const type = 'severity'
describe('views/charts2/charts/npm/NpmEventsHeader.vue测试', () => {
test('严重等级各等级个数npm event 严重等级单值', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 返回数据
axios.get.mockResolvedValue(chartData)
// 加载vue组件获得实例
const wrapper = mount(NpmEventsHeader, {
propsData: {
type
}
})
// 严重等级
const severity0 = wrapper.get('[test-id="severity0"]')
const severity1 = wrapper.get('[test-id="severity1"]')
const severity2 = wrapper.get('[test-id="severity2"]')
const severity3 = wrapper.get('[test-id="severity3"]')
const severity4 = wrapper.get('[test-id="severity4"]')
// 各等级个数
const total0 = wrapper.get('[test-id="total0"]')
const total1 = wrapper.get('[test-id="total1"]')
const total2 = wrapper.get('[test-id="total2"]')
const total3 = wrapper.get('[test-id="total3"]')
const total4 = wrapper.get('[test-id="total4"]')
// type
expect(wrapper.vm.type).toEqual('severity')
expect(severity0.text()).toEqual('critical')
expect(severity1.text()).toEqual('high')
expect(severity2.text()).toEqual('medium')
expect(severity3.text()).toEqual('low')
expect(severity4.text()).toEqual('info')
await new Promise(resolve => setTimeout(() => {
expect(total0.text()).toEqual('322,334')
expect(total1.text()).toEqual('1,111')
expect(total2.text()).toEqual('2,000,000')
expect(total3.text()).toEqual('14,456,678')
expect(total4.text()).toEqual('122,222')
resolve()
}, 200))
})
})

File diff suppressed because one or more lines are too long

View File

@@ -1,162 +0,0 @@
import NpmRecentEvents from '@/views/charts2/charts/npm/NpmRecentEvents'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import ElementPlus from 'element-plus'
// 未下钻
const mockGet = {
data: { status: 200, code: 200, queryKey: '68d8aa5867b08b926b5bd38c36add9e5', success: true, message: null, statistics: { elapsed: 0, rows_read: 2, result_size: 550, result_rows: 5 }, job: null, formatType: 'json', meta: [{ name: 'event_id', type: 'long', category: 'Metric' }, { name: 'event_severity', type: 'string', category: 'Metric' }, { name: 'event_key', type: 'string', category: 'Metric' }, { name: 'event_type', type: 'string', category: 'Metric' }], data: { resultType: 'table', result: [{ eventId: 1173511643475208200, eventSeverity: 'critical', eventKey: '114.114.114.114 dns error', eventType: 'dns error' }, { eventId: 1173504415263352800, eventSeverity: 'high', eventKey: '116.178.78.241 http error', eventType: 'http error' }, { eventId: 1173492761289025500, eventSeverity: 'medium', eventKey: '223.6.6.6 dns error', eventType: 'dns error' }, { eventId: 1173489002890651600, eventSeverity: 'low', eventKey: '114.114.114.114 dns error', eventType: 'dns error' }, { eventId: 1173482380537620500, eventSeverity: 'info', eventKey: '1.1.1.2 dns error', eventType: 'http error' }, { eventId: 1173482380537620500, eventSeverity: 'critical', eventKey: '1.1.1.2 dns error', eventType: 'dns error' }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20event_id%20AS%20event_id%2Cevent_severity%20AS%20event_severity%2C%20event_key%20AS%20event_key%2C%20event_type%20AS%20event_type%20FROM%20performance_event%20WHERE%20start_time%20%3E%3D%201675227528%20AND%20end_time%20%3C%201675231128%20ORDER%20BY%20start_time%20DESC%20%20LIMIT%208%20&format=json&option=real-time', msg: 'OK' }
}
// 下钻
const mockGet1 = {
data: { status: 200, code: 200, queryKey: 'fc0bd92bf3b48a37310d5c004d8b7a7b', success: true, message: null, statistics: { elapsed: 0, rows_read: 2, result_size: 689, result_rows: 7 }, job: null, formatType: 'json', meta: [{ name: 'event_id', type: 'long', category: 'Metric' }, { name: 'event_severity', type: 'string', category: 'Metric' }, { name: 'event_type', type: 'string', category: 'Metric' }, { name: 'start_time', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ eventId: 1132790825086844900, eventSeverity: 'critical', eventType: 'http error', startTime: 1672802700 }, { eventId: 1132132403379142700, eventSeverity: 'high', eventType: 'dns error', startTime: 1672763400 }, { eventId: 1131441760155689000, eventSeverity: 'low', eventType: 'dns error', startTime: 1672722300 }, { eventId: 1131411523384598500, eventSeverity: 'medium', eventType: 'http error', startTime: 1672720500 }, { eventId: 1131390214323789800, eventSeverity: 'info', eventType: 'dns error', startTime: 1672719300 }, { eventId: 1131306200132968400, eventSeverity: 'critical', eventType: 'http error', startTime: 1672714200 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20event_id%20AS%20event_id%2Cevent_severity%20AS%20event_severity%2C%20event_type%20AS%20event_type%2C%20start_time%20AS%20start_time%20FROM%20performance_event%20WHERE%20start_time%20%3E%3D%201672675200%20AND%20start_time%20%3C%201677513600%20AND%20server_ip%20%3D%20%27116.178.236.216%27%20ORDER%20BY%20start_time%20DESC&format=json&option=real-time', msg: 'OK' }
}
const query = {
curTab: 'country',
dimensionType: 'ip',
fourthMenu: '116.178.214.84',
fourthPanel: '8',
networkOverviewBeforeTab: 'ip',
panelName: '116.178.214.84',
queryCondition: "common_client_ip='116.178.214.84' OR common_server_ip='116.178.214.84'",
t: '1675236779453',
tabIndex: '1',
tabOperationBeforeType: '',
tabOperationType: '4',
thirdMenu: 'network.ips',
thirdPanel: '12'
}
const timeFilter = {
dateRangeValue: -1,
startTime: 1675043912,
endTime: 1675047512
}
describe('views/charts2/charts/npm/NpmRecentEvents.vue测试', () => {
test('npm events 近期事件表格 未下钻', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 返回数据
axios.get.mockResolvedValue(mockGet)
const wrapper = mount(NpmRecentEvents, {
propsData: {
timeFilter
},
global: {
plugins: [ElementPlus]
}
})
await new Promise(resolve => setTimeout(() => {
const severity0 = wrapper.get('[test-id="eventSeverity-critical-0"]')
const severity1 = wrapper.get('[test-id="eventSeverity-high-1"]')
const severity2 = wrapper.get('[test-id="eventSeverity-medium-2"]')
const severity3 = wrapper.get('[test-id="eventSeverity-low-3"]')
const severity4 = wrapper.get('[test-id="eventSeverity-info-4"]')
const severity5 = wrapper.get('[test-id="eventSeverity-critical-5"]')
expect(severity0.text()).toEqual('critical')
expect(severity1.text()).toEqual('high')
expect(severity2.text()).toEqual('medium')
expect(severity3.text()).toEqual('low')
expect(severity4.text()).toEqual('info')
expect(severity5.text()).toEqual('critical')
expect(severity0.classes()).toContain('critical')
expect(severity1.classes()).toContain('high')
expect(severity2.classes()).toContain('medium')
expect(severity3.classes()).toContain('low')
expect(severity4.classes()).toContain('info')
expect(severity5.classes()).toContain('critical')
const eventKey0 = wrapper.get('[test-id="eventKey-114.114.114.114-0"]')
const eventKey1 = wrapper.get('[test-id="eventKey-116.178.78.241-1"]')
const eventKey2 = wrapper.get('[test-id="eventKey-223.6.6.6-2"]')
const eventKey3 = wrapper.get('[test-id="eventKey-114.114.114.114-3"]')
const eventKey4 = wrapper.get('[test-id="eventKey-1.1.1.2-4"]')
expect(eventKey0.text()).toEqual('114.114.114.114')
expect(eventKey1.text()).toEqual('116.178.78.241')
expect(eventKey2.text()).toEqual('223.6.6.6')
expect(eventKey3.text()).toEqual('114.114.114.114')
expect(eventKey4.text()).toEqual('1.1.1.2')
const eventType0 = wrapper.get('[test-id="eventType-dns error-0"]')
const eventType1 = wrapper.get('[test-id="eventType-http error-1"]')
const eventType2 = wrapper.get('[test-id="eventType-dns error-2"]')
const eventType3 = wrapper.get('[test-id="eventType-dns error-3"]')
const eventType4 = wrapper.get('[test-id="eventType-http error-4"]')
expect(eventType0.text()).toEqual('dns error')
expect(eventType1.text()).toEqual('http error')
expect(eventType2.text()).toEqual('dns error')
expect(eventType3.text()).toEqual('dns error')
expect(eventType4.text()).toEqual('http error')
resolve()
}, 200))
})
test('npm events 近期事件表格 下钻', async () => {
require('vue-router').useRoute.mockReturnValue({ query: query })
// 模拟 axios 返回数据
axios.get.mockResolvedValue(mockGet1)
const wrapper = mount(NpmRecentEvents, {
propsData: {
timeFilter
},
global: {
plugins: [ElementPlus]
}
})
await new Promise(resolve => setTimeout(() => {
const severity0 = wrapper.get('[test-id="eventSeverity-critical-0"]')
const severity1 = wrapper.get('[test-id="eventSeverity-high-1"]')
const severity2 = wrapper.get('[test-id="eventSeverity-low-2"]')
const severity3 = wrapper.get('[test-id="eventSeverity-medium-3"]')
const severity4 = wrapper.get('[test-id="eventSeverity-info-4"]')
const severity5 = wrapper.get('[test-id="eventSeverity-critical-5"]')
expect(severity0.text()).toEqual('critical')
expect(severity1.text()).toEqual('high')
expect(severity2.text()).toEqual('low')
expect(severity3.text()).toEqual('medium')
expect(severity4.text()).toEqual('info')
expect(severity5.text()).toEqual('critical')
expect(severity0.classes()).toContain('critical')
expect(severity1.classes()).toContain('high')
expect(severity2.classes()).toContain('low')
expect(severity3.classes()).toContain('medium')
expect(severity4.classes()).toContain('info')
expect(severity5.classes()).toContain('critical')
const eventType0 = wrapper.get('[test-id="eventType-http error-0"]')
const eventType1 = wrapper.get('[test-id="eventType-dns error-1"]')
const eventType2 = wrapper.get('[test-id="eventType-dns error-2"]')
const eventType3 = wrapper.get('[test-id="eventType-http error-3"]')
const eventType4 = wrapper.get('[test-id="eventType-dns error-4"]')
expect(eventType0.text()).toEqual('http error')
expect(eventType1.text()).toEqual('dns error')
expect(eventType2.text()).toEqual('dns error')
expect(eventType3.text()).toEqual('http error')
expect(eventType4.text()).toEqual('dns error')
const startTime0 = wrapper.get('[test-id="startTime-0"]')
const startTime1 = wrapper.get('[test-id="startTime-1"]')
const startTime2 = wrapper.get('[test-id="startTime-2"]')
const startTime3 = wrapper.get('[test-id="startTime-3"]')
const startTime4 = wrapper.get('[test-id="startTime-4"]')
expect(startTime0.text()).toEqual('2023-01-04T11:25:00+08:00')
expect(startTime1.text()).toEqual('2023-01-04T00:30:00+08:00')
expect(startTime2.text()).toEqual('2023-01-03T13:05:00+08:00')
expect(startTime3.text()).toEqual('2023-01-03T12:35:00+08:00')
expect(startTime4.text()).toEqual('2023-01-03T12:15:00+08:00')
resolve()
}, 300))
})
})