Compare commits

...

77 Commits

Author SHA1 Message Date
刘洪洪
12895e3589 fix: 修复npm-event单测id根据时间命名可能会导致报错的问题 2023-02-23 10:51:56 +08:00
刘洪洪
c2f318391b fix: 修复linkMonitor下一跳网格图排序错乱问题 2023-02-23 10:23:24 +08:00
李金洋
5ccb91ac24 Update .gitlab-ci.yml 2023-02-21 08:07:38 +00:00
李金洋
d4ce688a5f Update .gitlab-ci.yml 2023-02-21 07:59:43 +00:00
李金洋
88836a1932 Update .gitlab-ci.yml 2023-02-21 07:55:38 +00:00
李金洋
fec08141e5 Update .gitlab-ci.yml 2023-02-21 07:35:45 +00:00
李金洋
28092cc9ab Update .gitlab-ci.yml 2023-02-21 07:14:28 +00:00
chenjinsong
e009142e7d fix: 完善NetworkOverviewLine组件测试用例;更改变量名; 2023-02-17 20:50:29 +08:00
刘洪洪
14a24b268a fix: 去除无效代码 2023-02-17 16:14:38 +08:00
刘洪洪
06de8e2bc9 feat: 优化Link monitor网格图单测的模拟数据 2023-02-17 15:43:03 +08:00
刘洪洪
d788657048 feat: 优化单测模拟数据格式,避免eslint报错提示造成卡顿 2023-02-17 15:28:43 +08:00
lijinyang
524be68781 Update .gitlab-ci.yml 2023-02-14 15:11:42 +08:00
唐浩
0139a542da Update .gitlab-ci.yml file 2023-02-09 08:05:47 +00:00
@changcode
da607274d8 CN-878 feat: 单测用例--network overview 供应商与应用 2023-02-07 18:10:56 +08:00
唐浩
48426a3955 Update .gitlab-ci.yml file 2023-02-07 09:26:20 +00:00
hyx
e95ca314a6 CN-879 单测用例--npm events 最底下的表格 2023-02-07 17:20:26 +08:00
唐浩
3d18a4b285 Update .gitlab-ci.yml file 2023-02-07 07:04:32 +00:00
刘洪洪
2b67cb4a0b fix: detection-performanceEvent去除对eventId的格式转换 2023-02-07 10:29:27 +08:00
chenjinsong
eadb1e350d CN-858 fix: 报告任务时间设置可编辑 2023-02-06 16:19:32 +08:00
陈劲松
46a23d9464 Merge branch 'cherry-pick-84ca05dc' into 'dev'
fix: 修复dns仪表盘切换时间不生效的问题

See merge request cyber-narrator/cn-ui!19
2023-02-03 10:00:35 +00:00
chenjinsong
a7e06c6ffa fix: 修复dns仪表盘切换时间不生效的问题
(cherry picked from commit 84ca05dccc)
2023-02-03 18:00:30 +08:00
刘洪洪
822e7bd9aa fix: 去除测试代码 2023-02-03 17:47:55 +08:00
刘洪洪
73952e6811 fix: 完善link流量流向网格图单测用例 2023-02-03 17:41:50 +08:00
@changcode
251e0a2018 CN-877 feat: 单测用例--link下钻折线图 2023-02-03 16:09:32 +08:00
刘洪洪
d04bb5a87e fix: 去除多余代码 2023-02-03 16:01:18 +08:00
刘洪洪
f6a6ac82bd CN-876: 单测用例--link流量流向网格图 2023-02-03 16:00:29 +08:00
刘洪洪
bca147a7db CN-876: 单测用例--link流量流向网格图 2023-02-03 15:57:43 +08:00
陈劲松
afab449f3a Merge branch 'cherry-pick-ec486fe9' into 'dev'
fix: 修复地图资源路径拼写错误的问题

See merge request cyber-narrator/cn-ui!18
2023-02-03 03:09:18 +00:00
chenjinsong
8bf7b85c2e fix: 修复地图资源路径拼写错误的问题
(cherry picked from commit ec486fe930)
2023-02-03 11:09:04 +08:00
@changcode
ab146b5b15 feat: 单测 npm events 类型分类事件 2023-02-03 10:46:06 +08:00
刘洪洪
230e897146 CN-875: 单测用例--link蜂窝图 2023-02-02 18:14:20 +08:00
chenjinsong
949a8e9d86 CN-873 feat: 域名实体下拉预览增加备案信息相关字段 2023-02-02 17:15:17 +08:00
@changcode
3604783570 CN-869 feat: 单测用例--npm events 近期事件表格 2023-02-02 09:51:11 +08:00
刘洪洪
f60a6bd778 CN-870: 时间选择器组件时间交互优化 2023-02-01 16:51:45 +08:00
chenjinsong
5ece1b6c8e CN-868 fix: 修复report customize类别下endTime选取逻辑不对的问题 2023-02-01 15:41:20 +08:00
刘洪洪
86c2c8364d fix: 修改npm app类别评分图单测用例时间 2023-02-01 11:52:05 +08:00
@changcode
1f130a6ac8 feat: 单测 Npm - Overview 概况信息统计 2023-02-01 11:32:47 +08:00
刘洪洪
c8af5fd5a1 CN-865: 单测用例--npm app类别评分图 2023-01-31 18:52:07 +08:00
@changcode
48b7493b2e fix: 修复network overview部分维度二级菜单判断逻辑调整 2023-01-31 17:38:14 +08:00
chenjinsong
98ba09b586 CN-868 fix: 修复network overview部分维度三级菜单图表错误问题 2023-01-31 15:41:04 +08:00
刘洪洪
7d85d332df fix: 完善单测demo(同一url不同入参的axios请求内包含多个不同url的axios请求) 2023-01-31 15:06:30 +08:00
chenjinsong
6ee5ea6f6e fix: 完善单测demo(第三方库) 2023-01-30 17:54:24 +08:00
chenjinsong
6756812c34 fix: 完善单测demo(多个同url不同参数的axios请求) 2023-01-30 17:07:58 +08:00
@changcode
d2aa5c9b7a fix: 完善单测,去除无用代码 2023-01-30 16:24:44 +08:00
@changcode
049d5f92b4 CN-866 fix: 单测用例--network overview ddos检测图 2023-01-30 14:02:28 +08:00
chenjinsong
7ce190f3c7 fix: 完善单测demo(第三方ui库) 2023-01-30 11:50:22 +08:00
@changcode
e9edd9cf05 CN-866 feat: 单测用例--network overview ddos检测图 编写单测用例 2023-01-29 16:04:01 +08:00
@changcode
4655eed55f CN-866 fix: 单测用例--network overview ddos检测图 数值使用单位转换 2023-01-29 09:52:02 +08:00
刘洪洪
cf625c196e fix: 修改linkMonitor网格图名称 2023-01-28 15:40:35 +08:00
hyx
55fdd3f0e4 CN-863 单测用例--npm服务质量图(5个单值) 2023-01-20 11:13:07 +08:00
刘洪洪
8aa96da577 fix: 修复从npm-events点击跳转至detections-performance events展开tab位置不正确的问题 2023-01-18 14:42:12 +08:00
@changcode
22bf16a01d fix: 去除无效代码 2023-01-18 11:53:21 +08:00
chenjinsong
04e186e7d8 fix: 完善单测demo(indexedDB相关) 2023-01-17 17:56:44 +08:00
@changcode
38006bd964 fix: 部分接口请求 noData 判断调整 2023-01-17 14:00:41 +08:00
@changcode
05677d5fb6 style: Administration tab列表更多Select下拉样式调整 2023-01-16 17:19:30 +08:00
chenjinsong
156979e79e fix: 完善单测demo;升级单测版本 2023-01-13 17:42:25 +08:00
刘洪洪
0f2fcbe9e6 feat: chartTabs组件的状态存储由window改为store 2023-01-13 15:03:45 +08:00
@changcode
089887f05b fix: 修复npm overview 折线图数据为空时 No data 占位错误问题 2023-01-13 14:34:52 +08:00
@changcode
c83f64706f Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev 2023-01-13 14:09:28 +08:00
@changcode
c82d33fa39 fix: npm overview折线图代码优化 2023-01-13 14:09:10 +08:00
刘洪洪
eed1d398d8 fix: 修复chartTabs初始化时有滑动痕迹的问题 2023-01-13 13:59:42 +08:00
@changcode
a8643b8543 fix: 优化部分折线图表代码和逻辑 2023-01-12 18:00:19 +08:00
刘洪洪
bd1eeec770 fix: 优化页面销毁时对echarts实例的销毁 2023-01-11 18:11:43 +08:00
@changcode
8cb3f00aa4 fix: 优化linkTraffic请求判断逻辑 2023-01-11 17:54:14 +08:00
chenjinsong
4fed5a9b8c fix: 完善单测demo 2023-01-11 15:50:04 +08:00
刘洪洪
d54054510b fix: 修复id改成ref导致echarts获取不到dom报错的问题 2023-01-11 15:33:59 +08:00
@changcode
27bd8260d2 fix: 修复detections - Security events 下拉内容不足5条时中括号未能去除问题 2023-01-11 14:13:33 +08:00
刘洪洪
29916f8517 fix: 修复从npm-events点击跳转至detections-performance events回退不保留状态的问题 2023-01-10 15:14:46 +08:00
刘洪洪
2bac72eb5d fix: 修复chartTabs组件由其他界面进入时还保留上次状态 2023-01-10 15:01:55 +08:00
chenjinsong
dfcc03f11a fix: 单测的一些注释和配置顺序更改 2023-01-10 14:08:00 +08:00
@changcode
a608ac72f6 Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev
# Conflicts:
#	src/views/charts2/charts/networkOverview/NetworkOverviewLine.vue
2023-01-10 12:27:15 +08:00
@changcode
b5d897608c fix: tab折线图逻辑优化 2023-01-10 12:25:49 +08:00
chenjinsong
431821154e feat: 解决vue组件单元测试复杂的环境依赖,以及提交NetworkOverviewLine单元测试用例 2023-01-10 10:09:32 +08:00
@changcode
9f488adcb9 fix: 折线图初始化逻辑优化 2023-01-10 09:20:26 +08:00
@changcode
a31c408327 CN-856 fix: 修复Dashboard - network overview Metric下拉默认不是第一个时,折线图无法加载 2023-01-09 18:40:43 +08:00
刘洪洪
d84483c0dc fix: 修复detection下拉内容中domain和app的展示调整 2023-01-09 16:36:44 +08:00
刘洪洪
4804748564 CN-855: Dashboard自定义时间后,刷新按钮点击无反应 2023-01-09 15:49:03 +08:00
75 changed files with 3751 additions and 1604 deletions

View File

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

View File

@@ -2,6 +2,7 @@
stages:
- gen_git-log
- build_project
- test
- build_image
cache:
@@ -10,7 +11,7 @@ cache:
- package.json
paths:
- node_modules
- dist/
# - dist/
before_script:
- export CNUI_TAG=$(date +%Y%m%d%H%M%S)
@@ -29,7 +30,7 @@ generate_git-log:
- public/index.html
- public/git-log.html
only:
- dev
- dev-test
tags:
- galaxy
@@ -41,29 +42,48 @@ build_project:
- cnpm install --save-dev --unsafe-perm
- echo "npm run build"
- cnpm run build
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
when: on_success
paths:
- dist/
only:
- dev
- dev-test
- tags
tags:
- galaxy
test:
stage: test
script:
- cnpm run test
when: on_success
only:
- dev-test
tags:
- galaxy
build_image:
dependencies:
- build_project
stage: build_image
script:
- echo "docker build"
- sudo docker build --no-cache -t cn-ui:$CNUI_TAG .
- sudo docker build --no-cache -t cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG .
- echo "docker tag"
- sudo docker tag cn-ui:$CNUI_TAG 192.168.40.153:9080/cyber-narrator/cn-ui:$CNUI_TAG
- sudo docker tag cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG
- echo "docker push"
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui:$CNUI_TAG
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG
when: on_success
only:
- dev
- dev-test
tags:
- galaxy
build_release_image:
dependencies:
- build_project
stage: build_image
script:
- echo 'tag名称是'
@@ -71,11 +91,11 @@ build_release_image:
- echo '提交的版本是'
- echo $CI_COMMIT_REF_NAME
- echo "docker build"
- sudo docker build --no-cache -t cn-ui:$CI_COMMIT_TAG .
- sudo docker build --no-cache -t cn-ui-$CI_COMMIT_REF_NAME:$CI_COMMIT_TAG .
- echo "docker tag"
- sudo docker tag cn-ui:$CI_COMMIT_TAG 192.168.40.153:9080/cyber-narrator/cn-ui:$CI_COMMIT_TAG
- sudo docker tag cn-ui-$CI_COMMIT_REF_NAME:$CI_COMMIT_TAG 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CI_COMMIT_TAG
- echo "docker push"
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui:$CI_COMMIT_TAG
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CI_COMMIT_TAG
only:
- tags
tags:

View File

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

0
npm
View File

View File

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

View File

@@ -1,27 +1,120 @@
<template>
<span data-test="count">{{count}}</span>
<button data-test="button" @click="click">click</button>
<span test-id="count">{{count}}</span>
<span test-id="id">{{obj.id}}</span>
<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>
<script>
/* vue-jest的测试示例 */
import VueRouter from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import axios from 'axios'
import { ref } from 'vue'
import indexedDBUtils from '@/indexedDB'
export default {
name: 'Test',
data () {
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: {
click () {
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)
}
},
created () {
const { currentRoute } = VueRouter.useRouter()
setup () {
const { query } = useRoute()
const { currentRoute } = useRouter()
const localstorageValue = localStorage.getItem('key')
const lineTab = ref(query.lineTab || '')
const path = currentRoute.value.path
return {
currentRoute
lineTab,
path,
localstorageValue
}
}
}

View File

@@ -11,7 +11,9 @@
z-index: 999999;
box-shadow: 0 0 10px #CCC;
box-sizing: border-box;
.pop-title {
margin: 10px 0;
}
.el-button--mini{
padding: 5px 7px;
}
@@ -23,7 +25,6 @@
top: 33px;
}
.custom-labels {
margin-top: 12px;
width: 100%;
height: 300px;
}
@@ -41,8 +42,7 @@
font-size: 14px;
}
.custom-label:hover{
color: #cccccc;
background-color: #DCDFE6;
background-color: rgba(220, 223, 230, .5)
}
.custom-title{
padding: 2px 0 2px 2px;
@@ -57,6 +57,14 @@
display: flex;
justify-content: space-between;
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 {
display: none;

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,6 +25,7 @@
class="date_style"
style="position: absolute;top: -53px;left: -536px;"
:clearable="false"
:default-time="defaultTime"
type="datetimerange"
@change="timeArrChange"
/>
@@ -127,6 +128,11 @@ export default {
{ value: 2880, name: 'last 2 days' }
]
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
const utcStr = computed(() => {
@@ -274,6 +280,7 @@ export default {
rangeEchartsData,
address,
dateRangeArr,
defaultTime,
dateRangeValue,
isCustom,
newDateValue,

View File

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

View File

@@ -103,32 +103,7 @@
<span v-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span>
</template>
<template v-else-if="index===1">
<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>-->
<span class="route-menu" @click="jump(route,'','',2)">{{ item }}</span>
</template>
<template v-else>
<span>{{ item }}</span>
@@ -552,7 +527,7 @@ export default {
queryCondition.push('common_l7_protocol=\'' + valueGroup[0] + '\'')
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 ')
} else {
searchProps.forEach(item => {
@@ -627,9 +602,10 @@ export default {
this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true)
this.urlChangeParams[this.curTabState.tabOperationType] = opeType
if (opeType === 3) {
if (route !== '/panel/networkOverview') {
/* if (route !== '/panel/networkOverview') {
this.urlChangeParams.queryCondition = ''
}
} */
this.urlChangeParams.queryCondition = ''
}
} else {
this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu

View File

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

View File

@@ -1,8 +1,19 @@
import { dbName, dbGeoDataTableName, dbDrilldownTableConfig } from '@/utils/constants'
import Dexie from 'dexie'
/* https://dexie.org/ */
export const db = new Dexie(dbName)
db.version(2).stores({
const db = new Dexie(dbName)
db.version(3).stores({
[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,6 +1,5 @@
import { hasButton } from '@/permission'
import { dateFormatByAppearance } from '@/utils/date-util'
import { storageKey } from '@/utils/constants'
export default {
data () {
return {
@@ -21,7 +20,9 @@ export default {
query: false
},
timeout: null,
debounceFunc: null
debounceFunc: null,
// 是否正在单元测试
isUnitTesting: false
}
},
methods: {

View File

@@ -59,7 +59,8 @@ const panel = {
rangeEchartsData: {}, // 框选echarts图表
routerHistoryList: [], // 路由跳转记录列表
dnsQtypeMapData: [],
dnsRcodeMapData: []
dnsRcodeMapData: [],
chartTabList: null // chartTabs组件的tab状态点击列表初始化为null方便原有逻辑计算
},
mutations: {
setShowRightBox (state, flag) {
@@ -151,6 +152,9 @@ const panel = {
},
setRouterHistoryList (state, list) {
state.routerHistoryList = list
},
setChartTabList (state, list) {
state.chartTabList = list
}
},
getters: {
@@ -225,6 +229,9 @@ const panel = {
},
getRouterHistoryList (state) {
return state.routerHistoryList
},
getChartTabList (state) {
return state.chartTabList
}
},
actions: {
@@ -253,6 +260,9 @@ const panel = {
},
clearPanel (store) {
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 { getConfigVersion } from '@/utils/tools'
import { api } from '@/utils/api'
import { db } from '@/indexedDB'
import indexedDBUtils from '@/indexedDB'
const user = {
state () {
@@ -92,7 +92,7 @@ const user = {
if (res.code === 200 && res.page.list && res.page.list.length > 0) {
// 从接口返回整体配置,再读取用户缓存,将对应条目覆盖,作为使用的配置
const defaultConfigs = JSON.parse(res.page.list[0].cvalue)
await db[dbDrilldownTableConfig].put({
await indexedDBUtils.selectTable(dbDrilldownTableConfig).put({
id: 'default',
version: defaultConfigs.version,
config: defaultConfigs.config
@@ -100,7 +100,7 @@ const user = {
const userId = localStorage.getItem(storageKey.userId)
const oldVersion = await getConfigVersion(userId)
if (oldVersion !== defaultConfigs.version) {
db[dbDrilldownTableConfig].delete(userId)
indexedDBUtils.selectTable(dbDrilldownTableConfig).delete(userId)
}
}
})

View File

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

View File

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

View File

@@ -5,8 +5,7 @@ import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, n
import { getIso36112JsonData, getDictList } from '@/utils/api'
import { format } from 'echarts'
import router from '@/router'
import { db } from '@/indexedDB'
import { useRoute } from 'vue-router'
import indexedDBUtils from '@/indexedDB'
export const tableSort = {
// 是否需要排序
@@ -488,11 +487,11 @@ export function loadGeoData () {
keys.push(storageKey.iso36112Capital)
keys.push(storageKey.iso36112WorldLow)
keys.forEach(async k => {
const queryData = await db[dbGeoDataTableName].get({ name: k })
const queryData = await indexedDBUtils.selectTable(dbGeoDataTableName).get({ name: k })
if (!queryData) {
const data = await getIso36112JsonData(iso36112[k])
if (data) {
db[dbGeoDataTableName].add({
indexedDBUtils.selectTable(dbGeoDataTableName).add({
name: k,
geo: data
})
@@ -505,14 +504,14 @@ export function loadGeoData () {
* 使用indexedDB缓存地图数据
* */
export async function getGeoData (key) {
const data = await db[dbGeoDataTableName].get({ name: key })
const data = await indexedDBUtils.selectTable(dbGeoDataTableName).get({ name: key })
if (data) {
return data.geo
} else {
if (iso36112[key]) {
const d = await getIso36112JsonData(iso36112[key])
if (d) {
db[dbGeoDataTableName].add({
indexedDBUtils.selectTable(dbGeoDataTableName).add({
name: key,
geo: d
})
@@ -937,7 +936,7 @@ export async function getDefaultCurTab (tableType, metric, columnName) {
export async function readDrilldownTableConfigByUser () {
// 获取用户定制的自定义配置
const userId = localStorage.getItem(storageKey.userId)
const userLocalConfig = await db[dbDrilldownTableConfig].get({ id: userId })
const userLocalConfig = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: userId })
let defaultDrillDownTableConfigs = []
if (userLocalConfig) {
defaultDrillDownTableConfigs = userLocalConfig.config
@@ -946,15 +945,15 @@ export async function readDrilldownTableConfigByUser () {
}
export async function getConfigVersion (id) {
let defaultConfigInDb = await db[dbDrilldownTableConfig].get({ id: id })
let defaultConfigInDb = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: id })
if (!defaultConfigInDb) {
defaultConfigInDb = await db[dbDrilldownTableConfig].get({ id: 'default' })
defaultConfigInDb = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: 'default' })
}
return defaultConfigInDb.version || ''
}
export async function combineDrilldownTableWithUserConfig () {
const defaultConfigInDb = await db[dbDrilldownTableConfig].get({ id: 'default' })
const defaultConfigInDb = await indexedDBUtils.selectTable(dbDrilldownTableConfig).get({ id: 'default' })
const defaultConfigGroup = defaultConfigInDb ? defaultConfigInDb.config : []
const currentUserConfigGroup = await readDrilldownTableConfigByUser()
if (defaultConfigGroup && currentUserConfigGroup && currentUserConfigGroup.length > 0) {
@@ -1086,3 +1085,64 @@ export function colorGradientCalculation (startColor, endColor, values) {
export function colorHexToRgbArr (hex) {
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,16 +43,6 @@ export default {
i18n: 'I18n',
path: '/administration/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

@@ -150,14 +150,12 @@ export default {
if (panels && panels.length > 0) {
this.panel = panels[0]
}
// console.log(this.panel)
if (this.panel.id) {
if (this.panel.params) {
this.panel.params = JSON.parse(this.panel.params)
} else {
this.panel.params = {}
}
// console.log(this.panel)
const allCharts = (await getChartList({ panelId: this.panel.id, pageSize: -1 })).map(chart => {
chart.i = chart.id
this.recursionParamsConvert(chart)

View File

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

View File

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

View File

@@ -334,6 +334,8 @@ export default {
if (!this.$refs.dateTimeRange.isCustom) {
const value = this.timeFilter.dateRangeValue
this.$refs.dateTimeRange.quickChange(value)
} else {
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
}
} else {
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,17 +5,17 @@
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<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}"
v-for="(item, index) in mpackets"
v-for="(item, index) in tabs"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)">
<div class="line-value-mpackets-name">
<div class="line-value-tabs-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
<div class="tabs-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit">
<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 chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
export default {
name: 'DnsTrafficLine',
components: {
@@ -126,7 +126,7 @@ export default {
label: 'Maximum'
}
],
mpackets: [
tabs: [
{ 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.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -202,68 +202,14 @@ export default {
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
this.tabs = [
{ 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.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 {
this.isNoData = false
this.showError = true
@@ -377,7 +323,7 @@ export default {
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
this.tabs.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
@@ -420,16 +366,16 @@ export default {
legendSelectChange (item, index, val) {
if (index === 'index') {
this.dispatchLegendSelectAction(item.name)
} else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
} else if (this.tabs[index] && this.tabs[index].name === item.name) {
this.dispatchLegendSelectAction(item.name)
this.mpackets.forEach((t) => {
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
}
if (val === 'active') {
this.mpackets.forEach(t => {
this.tabs.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
@@ -441,7 +387,7 @@ export default {
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
@@ -449,8 +395,8 @@ export default {
}
},
handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
if (document.querySelector('.network .line-value-tabs.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active')
const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
@@ -463,7 +409,7 @@ export default {
this.lineTab = ''
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
@@ -475,9 +421,9 @@ export default {
let echartsData
const chartOption = this.myChart.getOption()
if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
echartsData = this.tabs.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = this.mpackets.filter(t => t.show === true)
echartsData = this.tabs.filter(t => t.show === true)
}
if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
@@ -526,12 +472,85 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
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 () {
this.timer = setTimeout(() => {
if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab)
const data = this.tabs.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
} else {
this.init()
@@ -542,7 +561,9 @@ export default {
beforeUnmount () {
clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
this.myChart = null
if (this.myChart) {
echarts.dispose(this.myChart)
}
this.chartOption = 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-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="info__value" style="margin-left: 8px">
<div class="info__value" :test-id="`linkBlockTotal${index}`" style="margin-left: 8px">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }}
</div>
</div>
<div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.bandwidthUsage') }}</div>
<div class="info__value" style="display: flex">
<div>
<div :test-id="`linkBlockEgressUsage${index}`">
<svg class="icon item-popover-up" aria-hidden="true">
<use xlink:href="#cn-icon-egress"></use>
</svg>
{{ convertValue(item.egressUsage) }}
</div>
<div>
<div :test-id="`linkBlockIngressUsage${index}`">
<svg class="icon item-popover-down" aria-hidden="true">
<use xlink:href="#cn-icon-ingress"></use>
</svg>
@@ -84,7 +84,7 @@
<div class="popper-content__link-id">Next-Hop Internet: {{ item.linkDirection }}</div>
<div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="info__value" style="margin-left: 8px">
<div class="info__value" :test-id="`nextHopTotal${index}`" style="margin-left: 8px">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }}
</div>
</div>
@@ -118,13 +118,13 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import { colorGradientCalculation } from '@/utils/tools'
import unitConvert from '@/utils/unit-convert'
import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/constants'
import { getSecond } from '@/utils/date-util'
import ChartError from '@/components/common/Error'
import axios from 'axios'
export default {
name: 'LinkBlock',
@@ -175,10 +175,13 @@ export default {
endTime: getSecond(this.timeFilter.endTime)
}
const dataRequest = get(api.linkMonitor.analysis, params)
const nextHopRequest = get(api.linkMonitor.nextHopAnalysis, params)
const dataRequest = axios.get(api.linkMonitor.analysis, { params: params })
const nextHopRequest = axios.get(api.linkMonitor.nextHopAnalysis, { params: params })
Promise.all([dataRequest, nextHopRequest]).then(res => {
Promise.all([dataRequest, nextHopRequest]).then(response => {
const res = []
res[0] = response[0].data
res[1] = response[1].data
if (res[0].code === 200 && res[1].code === 200) {
this.showError = false

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
<div class="link-traffic-line">
<link-traffic-drill-down-list
:chart="chart"
:line-data="mpackets"
:line-data="tabs[0]"
:time-filter="timeFilter">
</link-traffic-drill-down-list>
<div class="line network link-traffic">
@@ -12,23 +12,25 @@
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<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}"
v-for="(item, index) in mpackets"
v-for="(item, index) in tabs"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)">
<div class="line-value-mpackets-name">
@click="activeChange(item, index)"
:test-id="`tab-${index}`">
<div class="line-value-tabs-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
<div class="tabs-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit">
<div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span>
</span>
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span>
</div>
</div>
</div>
@@ -67,14 +69,14 @@ import { useRoute } from 'vue-router'
import { ref, shallowRef } from 'vue'
import unitConvert from '@/utils/unit-convert'
import { chartColor3, chartColor4, unitTypes } from '@/utils/constants'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import _ from 'lodash'
import * as echarts from 'echarts'
import { linkTrafficLineChartOption } from '@/views/charts2/charts/options/echartOption'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import axios from 'axios'
export default {
name: 'LinkTrafficLine',
@@ -112,7 +114,7 @@ export default {
label: 'Packets/s'
}
],
mpackets: [
tabs: [
{ 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.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -176,105 +178,21 @@ export default {
}
}
this.loading = true
get(api.linkMonitor.totalTrafficAnalysis, params).then((res) => {
axios.get(api.linkMonitor.totalTrafficAnalysis, { params: params }).then((res) => {
res = res.data
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
this.tabs = [
{ 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.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 {
this.showError = true
this.errorMsg = res.message
@@ -289,67 +207,69 @@ export default {
})
},
echartsInit (echartsData) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = echartsData.filter(t => t.show === true)
}
const _this = this
const dom = document.getElementById('linkTrafficLineChart')
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = linkTrafficLineChartOption
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => {
return {
...chartOption,
name: t.name,
lineStyle: {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0])
},
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartColor3[t.positioning]
},
{
offset: 1,
color: chartColor3[t.positioning]
}
])
},
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number'])
if (!this.isUnitTesting) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = echartsData.filter(t => t.show === true)
}
})
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
const _this = this
const dom = document.getElementById('linkTrafficLineChart')
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = linkTrafficLineChartOption
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => {
return {
...chartOption,
name: t.name,
lineStyle: {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0])
},
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartColor3[t.positioning]
},
{
offset: 1,
color: chartColor3[t.positioning]
}
])
},
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number'])
}
})
// const str = stackedLineTooltipFormatter(params)
return stackedLineTooltipFormatter(params)
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.tabs.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
})
// const str = stackedLineTooltipFormatter(params)
return stackedLineTooltipFormatter(params)
}
this.showMarkLine = true
this.myChart.setOption(this.chartOption)
}
this.showMarkLine = true
this.myChart.setOption(this.chartOption)
},
activeChange (item, index) {
if (this.isNoData) return
@@ -381,16 +301,16 @@ export default {
legendSelectChange (item, index, val) {
if (index === 'index') {
this.dispatchLegendSelectAction(item.name)
} else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
} else if (this.tabs[index] && this.tabs[index].name === item.name) {
this.dispatchLegendSelectAction(item.name)
this.mpackets.forEach((t) => {
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
}
if (val === 'active') {
this.mpackets.forEach(t => {
this.tabs.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
@@ -402,7 +322,7 @@ export default {
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
@@ -410,8 +330,8 @@ export default {
}
},
handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
if (document.querySelector('.network .line-value-tabs.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active')
const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
@@ -424,7 +344,7 @@ export default {
this.lineTab = ''
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
@@ -457,12 +377,71 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
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 () {
this.timer = setTimeout(() => {
if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab)
const data = this.tabs.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
} else {
this.init()
@@ -473,7 +452,9 @@ export default {
beforeUnmount () {
clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
this.myChart = null
if (this.myChart) {
echarts.dispose(this.myChart)
}
this.chartOption = null
this.unitConvert = null
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,25 +5,29 @@
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<div class="line-value-mpackets"
v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in mpackets"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)">
<div class="line-value-mpackets-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span>
<template v-for="(item, index) in tabs">
<div class="line-value-tabs"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-if="item.show"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)"
:test-id="`tab${index}`"
>
<div class="line-value-tabs-name">
<div :class="item.class"></div>
<div class="tabs-name" :test-id="`tabTitle${index}`">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span>
</div>
</div>
</div>
</template>
</div>
</div>
<div class="line-select line-header-right">
@@ -46,7 +50,7 @@
</div>
<div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" id="overviewLineChart"></div>
<div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" ref="overviewLineChart"></div>
<!-- todo 后续改动此处为框选返回-->
<!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">-->
<!-- <el-button @click.stop="backBrushHistory">返回</el-button>-->
@@ -63,14 +67,16 @@ import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
import { ref, shallowRef } from 'vue'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import _ from 'lodash'
import { get } from '@/utils/http'
import axios from 'axios'
import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error'
import mockData from '../../../../../test/views/charts2/charts/networkOverview/NetworkOverviewLineMockData'
export default {
name: 'NetworkOverviewLine',
components: {
@@ -116,7 +122,7 @@ export default {
label: 'Maximum'
}
],
mpackets: [
tabsTemplate: [
{ 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.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' },
@@ -124,6 +130,7 @@ export default {
{ 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: '' }
],
tabs: [],
unitConvert,
unitTypes,
chartDateObject: [],
@@ -169,7 +176,7 @@ export default {
metric (n) {
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
@@ -186,161 +193,23 @@ export default {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
let condition = ''
if (this.queryCondition && this.tabOperationType !== '3') {
if (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)
get(api.netWorkOverview.totalTrafficAnalysis, params).then((res) => {
axios.get(api.netWorkOverview.totalTrafficAnalysis, { params: params }).then(response => {
const res = response.data
// const res = mockData.bytes.boundary.data
this.errorMsg = res.message
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
{ 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.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: '' }
]
this.tabs = _.cloneDeep(this.tabsTemplate)
} else {
this.initData(res.data.result, val, active, show, n)
}
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 {
this.showError = true
this.errorMsg = res.message
@@ -377,163 +246,163 @@ export default {
}
},
echartsInit (echartsData, show) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = echartsData.filter(t => t.show === true)
}
const _this = this
// !this.myChart && (this.myChart = echarts.init(dom))
// 此处为验证是否因dom未销毁导致图表出错后续可能会改
let dom = null
dom = document.getElementById('overviewLineChart')
this.chartOption = stackedLineChartOption
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => {
return {
...chartOption,
name: t.name,
lineStyle: {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0])
},
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartColor3[t.positioning]
},
{
offset: 1,
color: chartColor3[t.positioning]
}
])
},
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number']),
markLine: {
silent: true,
lineStyle: {
color: '#B4B1A8'
},
symbol: 'none',
label: {
formatter (params) {
const arr = unitConvert(params.value, unitTypes.number).join('')
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
},
position: 'insideStartTop',
color: '#717171',
fontFamily: 'NotoSansSChineseRegular'
}
}
// echarts内容在单元测试时不执行
if (!this.isUnitTesting) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = echartsData.filter(t => t.show === true)
}
})
if (!show) {
this.chartOption.series.forEach((t) => {
t.markLine.label.show = false
t.markLine = []
})
}
if (this.lineRefer === 'Average' && show) {
this.chartOption.series.forEach((t, i) => {
t.markLine.label.show = true
t.markLine.data = [
{
yAxis: echartsData[i].analysis.avg
}
]
})
} else if (this.lineRefer === '95th Percentile' && show) {
this.chartOption.series.forEach((t, i) => {
t.markLine.label.show = true
t.markLine.data = [
{ yAxis: echartsData[i].analysis.p95 }
]
})
} else if (this.lineRefer === 'Maximum' && show) {
this.chartOption.series.forEach((t, i) => {
t.markLine.label.show = true
t.markLine.data = [
{ yAxis: echartsData[i].analysis.max }
]
})
}
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
})
return stackedLineTooltipFormatter(params)
}
this.showMarkLine = true
this.$nextTick(() => {
this.myChart = echarts.init(dom)
this.myChart.setOption(this.chartOption)
// 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush
this.myChart.dispatchAction({
// 刷选模式的开关。使用此 action 可将当前鼠标变为可刷选状态。事实上,点击 toolbox 中的 brush 按钮时,就是通过这个 action将当前普通鼠标变为刷选器的。
type: 'takeGlobalCursor',
// 如果想变为“可刷选状态”,必须设置。不设置则会关闭“可刷选状态”。
key: 'brush',
brushOption: {
// 参见 brush 组件的 brushType。如果设置为 false 则关闭“可刷选状态”。
brushType: 'lineX',
xAxisIndex: 'all',
// 单击清除选框
brushMode: 'single',
// 选择完毕再返回所选数据
throttleType: 'debounce'
}
})
const self = this
this.myChart.on('brushEnd', function (params) {
self.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
if (!self.mouseDownFlag) {
// 避免点击空白区域报错
if (params.areas !== undefined && params.areas.length > 0) {
self.brushHistory.unshift({
startTime: _.cloneDeep(self.timeFilter.startTime) * 1000,
endTime: _.cloneDeep(self.timeFilter.endTime) * 1000
})
const rangeObj = {
startTime: Math.ceil(params.areas[0].coordRange[0]),
endTime: Math.ceil(params.areas[0].coordRange[1])
const _this = this
// !this.myChart && (this.myChart = echarts.init(dom))
// 此处为验证是否因dom未销毁导致图表出错后续可能会改
this.chartOption = stackedLineChartOption
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => {
return {
...chartOption,
name: t.name,
lineStyle: {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0])
},
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
// todo 目前暂定框选最小范围为5分钟后续可能会变动
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) {
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartColor3[t.positioning]
},
{
offset: 1,
color: chartColor3[t.positioning]
}
])
},
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number']),
markLine: {
silent: true,
lineStyle: {
color: '#B4B1A8'
},
symbol: 'none',
label: {
formatter (params) {
const arr = unitConvert(params.value, unitTypes.number).join('')
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
},
position: 'insideStartTop',
color: '#717171',
fontFamily: 'NotoSansSChineseRegular'
}
_this.$store.commit('setRangeEchartsData', rangeObj)
}
}
})
})
if (!show) {
this.chartOption.series.forEach((t) => {
t.markLine.label.show = false
t.markLine = []
})
}
if (this.lineRefer === 'Average' && show) {
this.chartOption.series.forEach((t, i) => {
t.markLine.label.show = true
t.markLine.data = [
{
yAxis: echartsData[i].analysis.avg
}
]
})
} else if (this.lineRefer === '95th Percentile' && show) {
this.chartOption.series.forEach((t, i) => {
t.markLine.label.show = true
t.markLine.data = [
{ yAxis: echartsData[i].analysis.p95 }
]
})
} else if (this.lineRefer === 'Maximum' && show) {
this.chartOption.series.forEach((t, i) => {
t.markLine.label.show = true
t.markLine.data = [
{ yAxis: echartsData[i].analysis.max }
]
})
}
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.tabs.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
})
return stackedLineTooltipFormatter(params)
}
this.showMarkLine = true
this.$nextTick(() => {
this.myChart = echarts.init(this.$refs.overviewLineChart)
this.myChart.setOption(this.chartOption)
// 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush
this.myChart.dispatchAction({
// 刷选模式的开关。使用此 action 可将当前鼠标变为可刷选状态。事实上,点击 toolbox 中的 brush 按钮时,就是通过这个 action将当前普通鼠标变为刷选器的。
type: 'takeGlobalCursor',
// 如果想变为“可刷选状态”,必须设置。不设置则会关闭“可刷选状态”。
key: 'brush',
brushOption: {
// 参见 brush 组件的 brushType。如果设置为 false 则关闭“可刷选状态”。
brushType: 'lineX',
xAxisIndex: 'all',
// 单击清除选框
brushMode: 'single',
// 选择完毕再返回所选数据
throttleType: 'debounce'
}
})
const self = this
this.myChart.on('brushEnd', function (params) {
self.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
if (!self.mouseDownFlag) {
// 避免点击空白区域报错
if (params.areas !== undefined && params.areas.length > 0) {
self.brushHistory.unshift({
startTime: _.cloneDeep(self.timeFilter.startTime) * 1000,
endTime: _.cloneDeep(self.timeFilter.endTime) * 1000
})
const rangeObj = {
startTime: Math.ceil(params.areas[0].coordRange[0]),
endTime: Math.ceil(params.areas[0].coordRange[1])
}
// todo 目前暂定框选最小范围为5分钟后续可能会变动
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) {
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000
}
_this.$store.commit('setRangeEchartsData', rangeObj)
}
}
})
})
}
},
activeChange (item, index) {
if (this.isNoData) return
@@ -565,16 +434,16 @@ export default {
legendSelectChange (item, index, val) {
if (index === 'index') {
this.dispatchLegendSelectAction(item.name)
} else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
} else if (this.tabs[index] && this.tabs[index].name === item.name) {
this.dispatchLegendSelectAction(item.name)
this.mpackets.forEach((t) => {
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
}
if (val === 'active') {
this.mpackets.forEach(t => {
this.tabs.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
@@ -586,7 +455,7 @@ export default {
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
@@ -594,8 +463,8 @@ export default {
}
},
handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
if (document.querySelector('.network .line-value-tabs.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active')
const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
@@ -606,32 +475,34 @@ export default {
referenceSelectChange (val) {
this.lineRefer = val
let echartsData
const chartOption = this.myChart.getOption()
if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
echartsData = this.tabs.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = this.mpackets.filter(t => t.show === true)
echartsData = this.tabs.filter(t => t.show === true)
}
if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.avg }]
}
})
} else if (this.lineRefer === '95th Percentile' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.p95 }]
}
})
} else if (this.lineRefer === 'Maximum' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.max }]
}
})
if (!this.isUnitTesting) {
const chartOption = this.myChart.getOption()
if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.avg }]
}
})
} else if (this.lineRefer === '95th Percentile' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.p95 }]
}
})
} else if (this.lineRefer === 'Maximum' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.max }]
}
})
}
this.myChart.setOption(chartOption)
}
this.myChart.setOption(chartOption)
},
symbolSizeSortChange (index, time) {
const dataIntegrationArray = []
@@ -681,6 +552,87 @@ export default {
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
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'
})
}
},
/**
* 鼠标右键返回框选的时间范围
*/
@@ -702,11 +654,9 @@ export default {
this.myChart = null
this.chartOption = null
this.timer = setTimeout(() => {
if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab)
if (data && data.positioning) {
this.activeChange(data, data.positioning)
}
if (this.lineTab && this.metric !== 'Sessions/s') {
const data = this.tabsTemplate.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
} else {
this.init()
}
@@ -717,11 +667,13 @@ export default {
clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
let myChart = echarts.getInstanceByDom(document.getElementById('overviewLineChart'))
let myChart = echarts.getInstanceByDom(this.$refs.overviewLineChart)
if (myChart) {
echarts.dispose(myChart)
}
this.myChart = null
if (this.myChart) {
echarts.dispose(this.myChart)
}
// 检测时发现该方法占用较大内存,且未被释放
this.unitConvert = null
myChart = null

View File

@@ -61,7 +61,6 @@ export default {
methods: {
init () {
const params = {
// startTime: true,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
@@ -193,6 +192,14 @@ export default {
beforeUnmount () {
clearTimeout(this.timer)
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.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 { getSecond } from '@/utils/date-util'
import chartMixin from '@/views/charts2/chart-mixin'
import { db } from '@/indexedDB'
import _ from 'lodash'
import indexedDBUtils from '@/indexedDB'
export default {
name: 'NetworkOverviewTabs',
@@ -1729,7 +1729,7 @@ export default {
version = await getConfigVersion('default')
}
// 更新缓存中的配置
await db[dbDrilldownTableConfig].put({
await indexedDBUtils.selectTable(dbDrilldownTableConfig).put({
id: this.userId,
version: version,
config: this.$_.cloneDeep(curUserConfigGroup)

View File

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

View File

@@ -1,29 +1,11 @@
<template>
<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
id="tabTable"
ref="dataTable"
:data="tableData"
class="npm-app-event-table"
:row-class-name="rowClassName"
height="100%"
empty-text=""
>
@@ -33,34 +15,33 @@
<span class="data-column__span">{{ $t(item.label) }}</span>
</template>
<template #default="scope" :column="item">
<div class="data-app-event-table">
<div class="data-app-event-table" :test-id="`${item.prop}${scope.row.index}`">
<template v-if="item.prop === 'domain' ||item.prop === 'appName' ||item.prop === 'serverIp' ">
<span class="data-applications">{{ scope.row[item.prop] }}</span>
</template>
<template v-else-if="item.prop === 'eventSeverity'">
<template v-else-if="item.prop === 'eventSeverity'" >
<template v-if="scope.row[item.prop]==='critical'">
<div v-for="item in 5" class="red-dot" :key="item"></div>
<div v-for="item in 5" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div>
</template>
<template v-else-if="scope.row[item.prop]==='high'">
<div v-for="item in 4" class="red-dot" :key="item"></div>
<div class="grey-dot"></div>
<div v-for="item in 4" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div>
<div v-for="item in 1" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${4+item}`"></div>
</template>
<template v-else-if="scope.row[item.prop]==='medium'">
<div v-for="item in 3" class="red-dot" :key="item"></div>
<div v-for="item in 2" class="grey-dot" :key="item"></div>
<div v-for="item in 3" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div>
<div v-for="item in 2" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${3+item}`"></div>
</template>
<template v-else-if="scope.row[item.prop]==='low'">
<div v-for="item in 2" class="red-dot" :key="item"></div>
<div v-for="item in 3" class="grey-dot" :key="item"></div>
<div v-for="item in 2" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div>
<div v-for="item in 3" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${2+item}`"></div>
</template>
<template v-else-if="scope.row[item.prop]==='info'">
<div v-for="item in 1" class="red-dot" :key="item"></div>
<div v-for="item in 4" class="grey-dot" :key="item"></div>
<div v-for="item in 1" class="red-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${item}`"></div>
<div v-for="item in 4" class="grey-dot" :key="item" :test-id="`eventSeverityValue${scope.row.index}${1+item}`"></div>
</template>
<span class="data-severity">{{ scope.row[item.prop] }}</span>
</template>
<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>
</template>
<template v-else-if="item.prop === 'count'">
@@ -153,6 +134,10 @@ export default {
this.init()
},
methods: {
rowClassName ({ row, rowIndex }) {
// 把每一行的索引放进row
row.index = rowIndex
},
init () {
this.toggleLoading(true)
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>
<template v-for="(legend, index) in chartData" :key="index">
<div class="npm-event-pie-legend-type">
<div class="npm-event-pie-legend-type-severity">{{legend.name}}</div>
<div class="npm-event-pie-legend-type-severity" :test-id="`testNode${index}`">{{legend.name}}</div>
</div>
</template>
</div>
<div class="npm-event-pie-legend">
<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">
<div class="npm-event-pie-legend-total">{{legend.value}}</div>
<div class="npm-event-pie-legend-total" :test-id="`total${index}`">{{legend.value}}</div>
</template>
</div>
</div>
@@ -31,7 +31,6 @@
<script>
import { shallowRef } from 'vue'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import * as echarts from 'echarts'
import { pieChartOption3 } from '@/views/charts2/charts/options/echartOption'
@@ -39,6 +38,7 @@ import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
import axios from 'axios'
export default {
name: 'NpmEventsByType',
@@ -108,7 +108,8 @@ export default {
type: 'severity'
}
this.toggleLoading(true)
get(api.npm.events.recentEvents, params).then(res => {
axios.get(api.npm.events.recentEvents, { params: params }).then(res => {
res = res.data
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
@@ -126,7 +127,9 @@ export default {
this.chartData = arrData.sort((a, b) => { return b.value - a.value })
this.$nextTick(() => {
if (this.chartData.length > 0) {
this.init()
if (!this.isUnitTesting) {
this.init()
}
}
})
} else {
@@ -152,7 +155,9 @@ export default {
},
beforeUnmount () {
window.removeEventListener('resize', this.resize)
this.myChart = null
if (this.myChart) {
echarts.dispose(this.myChart)
}
this.chartOption = null
}
}

View File

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

View File

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

View File

@@ -1,16 +1,60 @@
<template>
<div class="npm-network-quantity">
<single-value
v-if="npmNetworkData.length>0"
:npm-network-name="npmNetworkName"
:npm-network-data="npmNetworkData"
></single-value>
<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" :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>
</template>
<script>
import SingleValue from '@/views/charts2/charts/npm/localComponents/SingleValue'
import { get } from '@/utils/http'
import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin'
@@ -18,9 +62,11 @@ import _ from 'lodash'
import { getChainRatio } from '@/utils/tools'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from 'axios'
export default {
name: 'NpmNetworkQuantity',
components: { SingleValue },
mixins: [chartMixin],
setup () {
const { query } = useRoute()
@@ -39,6 +85,8 @@ export default {
},
data () {
return {
unitTypes,
unitConvert,
npmNetworkName: [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency' },
{ name: 'networkAppPerformance.httpResponse' },
@@ -48,6 +96,7 @@ export default {
],
npmNetworkCycleData: [],
npmNetworkLastCycleData: [],
newNpmNetworkData: [],
npmNetworkData: [],
side: '',
chartData: {},
@@ -68,7 +117,7 @@ export default {
npmNetworkCycleQuery () {
let condition = ''
let url = ''
if (this.queryCondition.indexOf(' OR ') > -1) {
if (this.queryCondition && this.queryCondition.indexOf(' OR ') > -1) {
condition = this.queryCondition.split(/["|'](.*?)["|']/)
} else {
condition = this.queryCondition
@@ -84,37 +133,38 @@ export default {
} else if (parseFloat(this.tabIndex) === 1) {
this.side = 'server'
}
if (condition && (typeof condition !== 'object') && type) {
if (type === 'clientIp' || type === 'serverIp') {
if (parseFloat(this.tabIndex) === 0) {
if (condition && (typeof condition !== 'object') && type) { // 判断 condition 不为空并且不为对象 type 不为空
if (type === 'clientIp' || type === 'serverIp') { // type = clientIp || serverIp
if (parseFloat(this.tabIndex) === 0) { // npm 下钻 tabIndex 为 0
type = 'clientIp'
} else if (parseFloat(this.tabIndex) === 1) {
} else if (parseFloat(this.tabIndex) === 1) { // npm 下钻 tabIndex 为 1
type = 'serverIp'
}
params.q = `ip='${condition.split(/'(.*?)'/)[1]}'`
params.q = `ip='${condition.split(/'(.*?)'/)[1]}'` // 拼接字段作为参数
} else if (type === 'clientCity') {
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'`
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'` // 拼接字段作为参数
} else if (type === 'serverCity') {
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'`
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'` // 拼接字段作为参数
} else {
params.q = condition
params.q = condition // 默认参数
}
params.type = type
} else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}' and side='${this.side}'`
} else if (condition.length > 1 && type && type === 'ip') { // condition 为数组时数组长度不为 0 | type 不为空 | type为ip
params.q = `${type}='${condition[1]}' and side='${this.side}'` // 拼接字段作为参数
params.type = type
} else if (condition.length > 1 && type && type !== 'ip') {
if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') {
params.q = `${type}='${condition[1]}'`
} else if (condition.length > 1 && type && type !== 'ip') { // condition 为数组时数组长度不为 0 | type 不为空 | type不为ip
if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') { // 根据接口所需,调整参数
params.q = `${type}='${condition[1]}'` // 拼接字段作为参数
params.type = type
} else if (type === 'idcRenter') {
params.q = `idc_renter='${condition[1]}'`
params.q = `idc_renter='${condition[1]}'` // 拼接字段作为参数
params.type = type
} else {
params.q = `${condition[0]}'${condition[1]}'`
params.q = `${condition[0]}'${condition[1]}'` // 拼接字段作为参数
params.type = type
}
}
// 区分 3 级菜单和 2 级菜单使用不同的 url
if (parseFloat(this.tabOperationType) === 3) {
url = api.npm.overview.allNetworkAnalysis
} else {
@@ -123,7 +173,8 @@ export default {
if ((type && condition) || type) {
params.type = params.type || type
this.toggleLoading(true)
get(url, params).then(res => {
axios.get(url, { params: params }).then(res => {
res = res.data
if (res.code === 200) {
this.npmNetworkCycleData = res.data.result
}
@@ -133,21 +184,21 @@ export default {
this.toggleLoading(false)
})
} else {
const tcp = get(api.npm.overview.tcpSessionDelay, params)
const http = get(api.npm.overview.httpResponseDelay, params)
const ssl = get(api.npm.overview.sslConDelay, params)
const tcpPercent = get(api.npm.overview.tcpLostlenPercent, params)
const packetPercent = get(api.npm.overview.packetRetransPercent, params)
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params })
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params })
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params })
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params })
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params })
this.toggleLoading(true)
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
// 状态为200的赋值接口数据不为200的传入报错提示message
// 传给子组件SingleValue再进行error处理error处理不在此处处理
this.npmNetworkCycleData = []
res.forEach(t => {
if (t.code === 200) {
this.npmNetworkCycleData.push(t.data.result)
if (t.data.code === 200) {
this.npmNetworkCycleData.push(t.data.data.result)
} else {
this.npmNetworkCycleData.push(t)
this.npmNetworkCycleData.push(t.data)
}
})
this.npmNetworkLastCycleQuery()
@@ -177,7 +228,8 @@ export default {
if ((params.type && params.q) || (param && param.type)) {
params.type = params.type || param.type
this.toggleLoading(true)
get(url, params).then(res => {
axios.get(url, { params: params }).then(res => {
res = res.data
if (res.code === 200) {
this.npmNetworkLastCycleData = res.data.result
} else {
@@ -195,20 +247,20 @@ export default {
this.toggleLoading(false)
})
} else {
const tcp = get(api.npm.overview.tcpSessionDelay, params)
const http = get(api.npm.overview.httpResponseDelay, params)
const ssl = get(api.npm.overview.sslConDelay, params)
const tcpPercent = get(api.npm.overview.tcpLostlenPercent, params)
const packetPercent = get(api.npm.overview.packetRetransPercent, params)
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params })
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params })
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params })
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params })
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params })
this.toggleLoading(true)
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
// 状态为200的赋值接口数据不为200的保留报错提示message
// 传给子组件SingleValue再进行error处理error处理不在此处处理
res.forEach(t => {
if (t.code === 200) {
this.npmNetworkLastCycleData.push(t.data.result)
if (t.data.code === 200) {
this.npmNetworkLastCycleData.push(t.data.data.result)
} else {
this.npmNetworkLastCycleData.push(t)
this.npmNetworkLastCycleData.push(t.data)
}
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 1)
})
@@ -271,6 +323,39 @@ export default {
})
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 () {
@@ -282,6 +367,7 @@ export default {
beforeUnmount () {
clearTimeout(this.timer1)
clearTimeout(this.timer2)
this.unitConvert = null
}
}
</script>

View File

@@ -21,15 +21,15 @@
<template #default="scope" :column="item">
<div class="data-recent-table">
<template v-if="item.prop === 'eventSeverity'">
<span class="data-recent-table-severity" :class="scope.row[item.prop]">{{scope.row[item.prop]}}</span>
<span class="data-recent-table-severity" :class="scope.row[item.prop]" :test-id="`eventSeverity-${scope.row.eventSeverity}-${scope.$index}`">{{scope.row[item.prop]}}</span>
</template>
<template v-else-if="item.prop === 'eventKey'">
<span class="data-recent-table-entity click-active" @click="jumpPage(scope.row)">{{splitEventKey(scope.row[item.prop])}}</span>
<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>
</template>
<template v-else-if="item.prop === 'eventType'">
<span class="data-recent-table-eventType">{{scope.row[item.prop]}}</span>
<span class="data-recent-table-eventType" :test-id="`eventType-${scope.row.eventType}-${scope.$index}`">{{scope.row[item.prop]}}</span>
</template>
<span v-else-if="scope.row[item.prop]">{{scope.row[item.prop]}}</span>
<span v-else-if="scope.row[item.prop]" :test-id="`startTime-${scope.$index}`">{{scope.row[item.prop]}}</span>
<span v-else>-</span>
</div>
</template>
@@ -48,13 +48,13 @@
</template>
<script>
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import { getSecond, dateFormatByAppearance } from '@/utils/date-util'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import ChartError from '@/components/common/Error'
import axios from 'axios'
export default {
name: 'NpmRecentEvents',
@@ -99,7 +99,7 @@ export default {
endTime: getSecond(this.timeFilter.endTime),
limit: 8
}
if (condition.length > 1 && this.dimensionType) {
if (condition && condition.length > 1 && this.dimensionType) {
params.param = condition[1]
params.type = this.dimensionType
if (params.type === 'serverIp' || params.type === 'clientIp') params.type = 'ip'
@@ -114,7 +114,8 @@ export default {
url = api.npm.events.recentEvents
}
this.toggleLoading(true)
get(url, params).then(res => {
axios.get(url, { params: params }).then(res => {
res = res.data
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
@@ -151,6 +152,7 @@ export default {
return name
},
jumpPage (item) {
this.beforeRouterPush()
this.$router.push({
path: '/detection/performanceEvent',
query: {
@@ -158,6 +160,36 @@ export default {
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 () {

View File

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

View File

@@ -42,7 +42,7 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import _ from 'lodash'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getLineType, getLineIndexUnit, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error'
export default {
@@ -71,7 +71,7 @@ export default {
unitConvert,
unitTypes,
side: '',
mpackets: [
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' },
@@ -80,11 +80,11 @@ export default {
{ name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
],
npmQuantity: [
{ 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 },
{ 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 },
{ name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent }
{ 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 }
],
chartData: {},
metricOptions: [
@@ -144,7 +144,6 @@ export default {
if (!val) {
val = this.metricFilter
}
// const conditionStr = this.$route.query.queryCondition ? this.$route.query.queryCondition : ''
let condition = ''
let type = this.dimensionType
if (this.queryCondition.indexOf(' OR ') > -1) {
@@ -200,7 +199,7 @@ export default {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.mpackets = [
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' },
@@ -209,92 +208,15 @@ export default {
{ 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 },
{ 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 },
{ 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 }
{ 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(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 {
this.isNoData = false
this.showError = true
@@ -308,204 +230,50 @@ export default {
this.toggleLoading(false)
})
} else {
if (val === 'Bits/s' || val === 'Packets/s' || val === 'Sessions/s') {
this.toggleLoading(true)
get(api.npm.overview.totalTrafficAnalysis, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.mpackets = [
{ name: 'network.total', show: true, positioning: 0, data: [], unitType: 'number' },
{ name: 'network.inbound', show: true, positioning: 1, data: [], unitType: 'number' },
{ 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)
}
})
this.toggleLoading(true)
const totalTrafficAnalysis = get(api.npm.overview.totalTrafficAnalysis, params)
const totalNetworkAnalysis = get(api.npm.overview.totalNetworkAnalysis, params)
const totalHttpResponseDelay = get(api.npm.overview.totalHttpResponseDelay, params)
const totalSslConDelay = get(api.npm.overview.totalSslConDelay, params)
const npmLineData = []
Promise.all([totalNetworkAnalysis, totalTrafficAnalysis, totalHttpResponseDelay, totalSslConDelay]).then(res => {
res.forEach(item => {
if (item.code === 200) {
npmLineData.push(...item.data.result)
} 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 === '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)
})
}
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 => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
}
},
echartsInit (echartsData, legendUnit) {
@@ -552,7 +320,7 @@ export default {
})
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
this.mpackets.forEach(e => {
this.tabs.forEach(e => {
if (e.name === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
@@ -619,6 +387,67 @@ export default {
},
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 () {
@@ -633,8 +462,9 @@ export default {
beforeUnmount () {
clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
this.myChart = null
this.myChart = null
if (this.myChart) {
echarts.dispose(this.myChart)
}
this.unitConvert = null
}
}

View File

@@ -1,125 +0,0 @@
<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,15 +134,7 @@ export default {
}
},
mounted () {
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)
}
}
this.initExpendTab()
},
computed: {
iconClass () {
@@ -186,7 +178,7 @@ export default {
this.$emit('switchCollapse', this.isCollapse, this.index)
if (this.isCollapse) {
const newQuery = this.$route.query // 深拷贝路由数据
const newQuery = this.$route.query
delete newQuery.eventId
this.reloadUrl(newQuery, 'cleanOldParams')
} else {
@@ -207,6 +199,33 @@ export default {
newUrl = urlParamsHandler(window.location.href, query, newParam, clean)
}
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

@@ -61,7 +61,7 @@
<div class="metric__column">
<div class="overview__title">{{$t('detections.metric')}}</div>
<div class="overview__row">
<div class="row__content--metric ">{{detection.eventType || '-'}}</div>
<div class="row__content--metric ">{{getNameByEventType(detection.eventType) || '-'}}</div>
</div>
</div>
<div class="metric__column">
@@ -95,7 +95,7 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts'
import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy } from '@/utils/tools'
import { sortBy, reverseSortBy, getNameByEventType } from '@/utils/tools'
import _ from 'lodash'
export default {
name: 'DetectionPerformanceEventAppOverview',
@@ -134,6 +134,7 @@ export default {
}
},
methods: {
getNameByEventType,
query () {
this.queryBasic().then(responses => {
responses && (this.basicInfo = responses)
@@ -180,6 +181,10 @@ 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.forEach(item => {
item.name = this.getNameByEventType(this.detection.eventType)
})
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
},

View File

@@ -63,7 +63,7 @@
<div class="metric__column">
<div class="overview__title">{{$t('detections.metric')}}</div>
<div class="overview__row">
<div class="row__content--metric ">{{detection.eventType || '-'}}</div>
<div class="row__content--metric ">{{getNameByEventType(detection.eventType) || '-'}}</div>
</div>
</div>
<div class="metric__column">
@@ -98,7 +98,7 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts'
import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy } from '@/utils/tools'
import { sortBy, reverseSortBy, getNameByEventType } from '@/utils/tools'
import _ from 'lodash'
export default {
name: 'DetectionPerformanceEventDomainOverview',
@@ -165,6 +165,7 @@ export default {
}
},
methods: {
getNameByEventType,
query () {
this.queryBasic().then(responses => {
responses && (this.basicInfo = responses)
@@ -212,6 +213,10 @@ 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.forEach(item => {
item.name = this.getNameByEventType(this.detection.eventType)
})
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
},

View File

@@ -54,7 +54,7 @@
<div class="metric__column">
<div class="overview__title">{{$t('detections.metric')}}</div>
<div class="overview__row">
<div class="row__content--metric ">{{getNameByType(detection.eventType) || '-'}}</div>
<div class="row__content--metric ">{{getNameByEventType(detection.eventType) || '-'}}</div>
</div>
</div>
<div class="metric__column">
@@ -88,7 +88,7 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts'
import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy } from '@/utils/tools'
import { sortBy, reverseSortBy, getNameByEventType } from '@/utils/tools'
import _ from 'lodash'
export default {
@@ -129,6 +129,7 @@ export default {
}
},
methods: {
getNameByEventType,
query () {
this.queryBasic().then(responses => {
responses && (this.basicInfo = responses)
@@ -173,9 +174,11 @@ 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[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.getNameByType(this.detection.eventType)
item.name = this.getNameByEventType(this.detection.eventType)
})
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
},
queryMetric () {
@@ -236,29 +239,6 @@ export default {
}
})
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 () {

View File

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

View File

@@ -22,6 +22,14 @@
<div class="row__label row__label--width130">{{$t('entities.org')}}</div>
<div class="row__content">{{entityData.domainWhoisOrg || '-'}}</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="row__label row__label&#45;&#45;width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{entityData.domainDescription || '-'}}</div>
@@ -352,7 +360,9 @@ export default {
domainDescription: response.data.result.domainDescription,
domainReputationScore: response.data.result.domainReputationScore,
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,34 +1,215 @@
/**
* jest: https://jestjs.io/docs/getting-started
* vue-jest: https://test-utils.vuejs.org/guide/
* */
import Test from '../src/Test'
import { mount } from '@vue/test-utils'
import { getNameByEventType } from '@/utils/tools'
import axios from 'axios'
import indexedDBUtils from '@/indexedDB'
import ElementPlus from 'element-plus'
// 模拟vue-router库否则组件中引用vue-router的代码报错
jest.mock('vue-router', () => {
return {
useRouter: function () { return { currentRoute: '/' } }
}
})
test('点击按钮后count的值+1', async () => {
// 加载vue组件获得实例
const wrapper = mount(Test)
// 取到文本和按钮的dom
const textNode = await wrapper.get('[data-test="count"]')
const button = await wrapper.get('[data-test="button"]')
// 断言文本dom的内容是否是'0'
expect(textNode.text()).toContain('0')
// 模拟按钮dom点击执行click()方法
await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'1'
expect(textNode.text()).toContain('1')
// 再次点击
await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'2'
expect(textNode.text()).toContain('2')
// 通过wrapper.vm.xxx直接执行click方法、获取data的数据
expect(wrapper.vm.count).toEqual(2)
await wrapper.vm.click()
expect(wrapper.vm.count).toEqual(3)
// 更多断言类型https://jestjs.io/zh-Hans/docs/expect
// 模拟的axios返回数据
const mockId = { data: 2 }
const mockTitle = { data: 'title2' }
const mockCount = { data: 999 }
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]
}
})
// 取到文本和按钮的dom
const textNode = await wrapper.get('[test-id="count"]')
const button = await wrapper.get('[test-id="button"]')
// 断言文本dom的内容是否是'0'
expect(textNode.text()).toBe('0')
// 模拟按钮dom点击执行click()方法
await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'1'
expect(textNode.text()).toBe('1')
// 再次点击
await button.trigger('click')
// 断言点击按钮后文本dom的内容是否是'2'
expect(textNode.text()).toBe('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的数据
expect(wrapper.vm.count).toBe(0)
await wrapper.vm.click()
expect(wrapper.vm.count).toBe(1)
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 => {
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')
})
})

41
test/init.js Normal file
View File

@@ -0,0 +1,41 @@
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

@@ -0,0 +1,130 @@
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

@@ -0,0 +1,37 @@
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

@@ -0,0 +1,141 @@
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

@@ -0,0 +1,73 @@
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

@@ -0,0 +1,258 @@
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

@@ -0,0 +1,54 @@
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

@@ -0,0 +1,162 @@
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))
})
})