Compare commits

...

63 Commits

Author SHA1 Message Date
lijinyang
53c24fe51b Update .gitlab-ci.yml 2023-02-14 15:02:20 +08:00
chenjinsong
2aa7f6edfb fix: 修复ip实体详情右上角饼图请求参数太长导致请求失败的问题 2023-02-10 15:23:19 +08:00
chenjinsong
6ed9c4f5fe fix: 修复detection顶部柱状图数据没有按时间排序的问题,修复app图逻辑问题 2023-02-10 14:20:07 +08:00
chenjinsong
6f8631f947 fix: whois字段问题 2023-02-09 17:15:21 +08:00
chenjinsong
63ab36f4d2 fix: 单位转换方法参数为0时不返回单位 2023-02-08 15:54:32 +08:00
chenjinsong
cccc1f0e6a CN-885 feat: whois字段调整 2023-02-08 15:24:18 +08:00
chenjinsong
4651709529 fix: logo变更、国际化切换按钮隐藏 2023-02-08 11:30:23 +08:00
chenjinsong
6ab98be4c8 fix: 修复加密DNS协议支持情况统计图表数据颠倒的问题 2023-02-08 10:55:11 +08:00
陈劲松
7b96f979a4 Merge branch 'cherry-pick-2b67cb4a' into '6suo'
fix: detection-performanceEvent去除对eventId的格式转换

See merge request cyber-narrator/cn-ui!20
2023-02-07 09:18:19 +00:00
刘洪洪
a59bff2cb7 fix: detection-performanceEvent去除对eventId的格式转换
(cherry picked from commit 2b67cb4a0b)
2023-02-07 17:18:13 +08:00
chenjinsong
04651581fe fix: 更新log外链链接 2023-02-03 18:46:08 +08:00
chenjinsong
84ca05dccc fix: 修复dns仪表盘切换时间不生效的问题 2023-02-03 16:38:24 +08:00
chenjinsong
780de66ddf fix: 隐藏代理和chart入口 2023-02-03 16:28:02 +08:00
chenjinsong
9205d7645d fix: 优化折线图效果 2023-02-03 16:26:46 +08:00
chenjinsong
ec486fe930 fix: 修复地图资源路径拼写错误的问题 2023-02-03 11:08:35 +08:00
chenjinsong
12c31b5bf6 feat: 6所相关内容,增加折线图左侧边距 2023-02-02 18:38:33 +08:00
chenjinsong
96591cb9ef feat: 6所相关内容 2023-02-02 17:55:03 +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 2292 additions and 1317 deletions

View File

@@ -29,7 +29,7 @@ generate_git-log:
- public/index.html - public/index.html
- public/git-log.html - public/git-log.html
only: only:
- dev - 6suo
tags: tags:
- galaxy - galaxy
@@ -42,7 +42,7 @@ build_project:
- echo "npm run build" - echo "npm run build"
- cnpm run build - cnpm run build
only: only:
- dev - 6suo
- tags - tags
tags: tags:
- galaxy - galaxy
@@ -51,14 +51,14 @@ build_image:
stage: build_image stage: build_image
script: script:
- echo "docker build" - 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" - 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" - 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 when: on_success
only: only:
- dev - 6suo
tags: tags:
- galaxy - galaxy
@@ -71,11 +71,11 @@ build_release_image:
- echo '提交的版本是' - echo '提交的版本是'
- echo $CI_COMMIT_REF_NAME - echo $CI_COMMIT_REF_NAME
- echo "docker build" - 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" - 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" - 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: only:
- tags - tags
tags: tags:

View File

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

0
npm
View File

View File

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

View File

@@ -1,4 +1,5 @@
var BASE_CONFIG = { var BASE_CONFIG = {
baseUrl: 'http://192.168.44.54:8090/', baseUrl: 'http://192.168.44.54:8093/',
version: '2.0.2021.05.11.19.43' version: '23.02.08',
logUrl: 'http://192.168.44.114:8088/superset/explore/?r=12&standalone=1'
} }

View File

@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>images/logo.svg"> <link rel="icon" href="<%= BASE_URL %>images/logo.svg">
<script src="config.js"></script> <script src="config.js"></script>
<title>Cyber Narrator</title> <title>域名解析服务监测系统</title>
</head> </head>
<body class="theme-light"> <body class="theme-light">
<noscript> <noscript>

View File

@@ -1,9 +1,7 @@
<template> <template>
<div class="logins"> <div class="logins">
<div class="inside"> <div class="inside">
<div class="title"> <div class="title">域名解析服务监测系统</div>
<img src="../public/images/logo-title.svg" />
</div>
<el-form class="login__box"> <el-form class="login__box">
<el-form-item> <el-form-item>
<el-input <el-input
@@ -193,12 +191,12 @@ export default {
} }
.title { .title {
margin-top: 65px; margin-top: 90px;
padding: 0 70px;
color: white;
font-size: 40px;
text-align: center; text-align: center;
} }
.title > img {
height: 135px;
}
.login__box { .login__box {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

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

View File

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

View File

@@ -98,6 +98,8 @@
.domain-detail-list { .domain-detail-list {
display: table; display: table;
width: 100%; width: 100%;
height: 100%;
overflow: auto;
.domain-detail-list__row { .domain-detail-list__row {
display: table-row; display: table-row;

View File

@@ -1,4 +1,5 @@
.cn-chart__whois { .cn-chart__whois {
overflow: auto; overflow: auto;
height: 100%;
} }

View File

@@ -7,7 +7,7 @@
display: flex; display: flex;
&.panel__time--scrolled-out { &.panel__time--scrolled-out {
position: fixed; position: fixed;
top: 70px; top: 120px;
padding-right: 10px; padding-right: 10px;
} }
&>div { &>div {
@@ -18,7 +18,7 @@
.chart-list { .chart-list {
&.chart-list--screen { &.chart-list--screen {
display: grid; display: grid;
height: calc(100vh - 90px); height: calc(100vh - 130px);
width: 100%; width: 100%;
grid-template-columns: repeat(30,1fr); grid-template-columns: repeat(30,1fr);
grid-template-rows: repeat(19,1fr); grid-template-rows: repeat(19,1fr);

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,12 @@
<div class="banner__left"> <div class="banner__left">
<span @click="shrink" class="shrink-button" :class="{'shrink-button--collapse': showMenu}"><i <span @click="shrink" class="shrink-button" :class="{'shrink-button--collapse': showMenu}"><i
class="cn-icon cn-icon-navigation"></i></span> class="cn-icon cn-icon-navigation"></i></span>
<img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo-header.svg')" @click="jump('/panel/networkOverview', '', '', 0)" style="cursor: pointer"/> <span style="color: white; font-size: 20px;">域名解析服务监测系统</span>
</div> </div>
<!--个人操作--> <!--个人操作-->
<div class="personal"> <div class="personal">
<el-dropdown> <!-- <el-dropdown>
<div class="header-menu--item"><i class="cn-icon cn-icon-language"></i></div> <div class="header-menu&#45;&#45;item"><i class="cn-icon cn-icon-language"></i></div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item> <el-dropdown-item>
@@ -24,7 +24,7 @@
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>-->
<el-dropdown> <el-dropdown>
<div class='login-user header-menu--item'>{{ username }}&nbsp;<i class="cn-icon cn-icon-arrow-down"></i></div> <div class='login-user header-menu--item'>{{ username }}&nbsp;<i class="cn-icon cn-icon-arrow-down"></i></div>
<template #dropdown> <template #dropdown>
@@ -103,32 +103,7 @@
<span v-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span> <span v-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span>
</template> </template>
<template v-else-if="index===1"> <template v-else-if="index===1">
<span class="route-menu" @click="jump(route,'','',2)" <span class="route-menu" @click="jump(route,'','',2)">{{ item }}</span>
v-if="route.indexOf('detection') === -1 && route.indexOf('administration') === -1">{{ item }}</span>
<!-- <div class="header__left-breadcrumb-item-select" v-if="route.indexOf('detection') > -1">-->
<!-- <el-popover placement="bottom-start"-->
<!-- v-if="route.indexOf('detection') > -1"-->
<!-- ref="breadcrumbPopover"-->
<!-- :show-arrow="false"-->
<!-- :append-to-body="false"-->
<!-- :hide-after="0"-->
<!-- :show-after="0"-->
<!-- popper-class="breadcrumb__popper"-->
<!-- trigger="click">-->
<!-- <template #reference>-->
<!-- <div class="breadcrumb-button" id="breadcrumbButton2" :class="showBackground?'breadcrumb-button__active':''" v-if="route.indexOf('detection') > -1">-->
<!-- <span id="breadcrumbValue2"> {{item}}</span><i class="cn-icon-xiala cn-icon"></i>-->
<!-- </div>-->
<!-- </template>-->
<!-- <el-row type="flex" justify="center" style="width: fit-content;flex-direction: column;">-->
<!-- <ul class="select-dropdown" id="breadcrumbSelectDropdown2">-->
<!-- <li v-for="item in detectionMenuList" title='' :key="item.name" :id="item.name" class="select-dropdown__item" @click="jump(item.path,'','',2)">-->
<!-- <span>{{$t(item.i18n)}}</span>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </el-row>-->
<!-- </el-popover>-->
<!-- </div>-->
</template> </template>
<template v-else> <template v-else>
<span>{{ item }}</span> <span>{{ item }}</span>
@@ -552,7 +527,7 @@ export default {
queryCondition.push('common_l7_protocol=\'' + valueGroup[0] + '\'') queryCondition.push('common_l7_protocol=\'' + valueGroup[0] + '\'')
queryCondition.push('common_server_port=' + valueGroup[1]) queryCondition.push('common_server_port=' + valueGroup[1])
} }
console.log(queryCondition.join(' AND ')) // console.log(queryCondition.join(' AND '))
this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' AND ') this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' AND ')
} else { } else {
searchProps.forEach(item => { searchProps.forEach(item => {
@@ -627,9 +602,10 @@ export default {
this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true) this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true)
this.urlChangeParams[this.curTabState.tabOperationType] = opeType this.urlChangeParams[this.curTabState.tabOperationType] = opeType
if (opeType === 3) { if (opeType === 3) {
if (route !== '/panel/networkOverview') { /* if (route !== '/panel/networkOverview') {
this.urlChangeParams.queryCondition = '' this.urlChangeParams.queryCondition = ''
} } */
this.urlChangeParams.queryCondition = ''
} }
} else { } else {
this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu

View File

@@ -8,7 +8,7 @@
</div> </div>
<div class="right-box__container"> <div class="right-box__container">
<div class="container__form"> <div class="container__form">
<el-form ref="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--> <!--name-->
<el-form-item :label="$t('report.name')" prop="name"> <el-form-item :label="$t('report.name')" prop="name">
<el-input id="account-input-name" v-model="editObject.name" maxlength="64" placeholder=" " show-word-limit size="small" type="text"></el-input> <el-input id="account-input-name" v-model="editObject.name" maxlength="64" placeholder=" " show-word-limit size="small" type="text"></el-input>
@@ -76,7 +76,6 @@
:disabled="!!editObject.id" :disabled="!!editObject.id"
:disabled-date="startDisabledDate" :disabled-date="startDisabledDate"
@change="startTimeChang" @change="startTimeChang"
@focus="startFocus"
prefix-icon="cn-icon cn-icon-shijian" prefix-icon="cn-icon cn-icon-shijian"
type="datetime" type="datetime"
placeholder=" " placeholder=" "
@@ -96,7 +95,6 @@
:disabled="!!editObject.id" :disabled="!!editObject.id"
:disabled-date="endDisabledDate" :disabled-date="endDisabledDate"
@change="endTimeChange" @change="endTimeChange"
@focus="endFocus"
prefix-icon="cn-icon cn-icon-shijian" prefix-icon="cn-icon cn-icon-shijian"
type="datetime" type="datetime"
placeholder=" " placeholder=" "
@@ -283,28 +281,8 @@ import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
import { get, post, put } from '@/utils/http' import { get, post, put } from '@/utils/http'
import { dateFormat, getMillisecond } from '@/utils/date-util' import { dateFormat, getMillisecond } from '@/utils/date-util'
import { ref } from 'vue' import { ref, getCurrentInstance } from 'vue'
const paramValidator = (rule, value, callback) => { import i18n from '@/i18n'
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
}
export default { export default {
name: 'ReportBox', name: 'ReportBox',
mixins: [rightBoxMixin], mixins: [rightBoxMixin],
@@ -313,32 +291,21 @@ export default {
currentCategoryId: Number currentCategoryId: Number
}, },
setup () { setup () {
const { proxy } = getCurrentInstance()
const startTime = ref('') const startTime = ref('')
const endTime = ref('') const endTime = ref('')
const focus = ref('')
const focusDate = ref('')
function endTimeChange (val) { function endTimeChange (val) {
endTime.value = val endTime.value = val
} }
function startTimeChang (val) { function startTimeChang (val) {
startTime.value = val startTime.value = val
} }
function startFocus (val) {
focus.value = val.target.value
}
function endFocus (val) {
focusDate.value = val.target.value
}
const endDisabledDate = (time) => { const endDisabledDate = (time) => {
if (time.getTime() > new Date()) { if (time.getTime() > new Date()) {
return true return true
} }
if (startTime.value != '' && startTime.value > time) { if (startTime.value !== '' && startTime.value > time) {
return true
}
if (focusDate.value != '' && endTime.value > time) {
return false
} else if (endTime.value != '' && endTime.value < time) {
return true return true
} }
} }
@@ -346,22 +313,71 @@ export default {
if (time.getTime() > new Date()) { if (time.getTime() > new Date()) {
return true return true
} }
if (focus.value != '' && startTime.value > time) { if (endTime.value !== '' && endTime.value < time) {
return false
} else if (startTime.value != '' && startTime.value > time) {
return true 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 { return {
endDisabledDate, endDisabledDate,
startDisabledDate, startDisabledDate,
startTimeChang, startTimeChang,
endTimeChange, endTimeChange,
startFocus, rules
endFocus
} }
}, },
data () { data () {
@@ -391,28 +407,6 @@ export default {
monthWeekdayCheckedAll: false, monthWeekdayCheckedAll: false,
monthWeekdayIsIndeterminate: false, monthWeekdayIsIndeterminate: false,
rules: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nameValidator, message: this.$t('validate.onlyAllowNumberLetterChinese-_'), trigger: 'blur' }
],
categoryId: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
schedulerStart: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
'config.startTime': [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
'config.endTime': [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
],
categoryParams: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: paramValidator, message: this.$t('validate.required'), trigger: 'blur' }
]
},
paramsOptions: [] paramsOptions: []
} }
}, },
@@ -592,7 +586,7 @@ export default {
if (this.blockOperation.save) { return } if (this.blockOperation.save) { return }
this.blockOperation.save = true this.blockOperation.save = true
this.$refs.userForm.validate((valid) => { this.$refs.reportForm.validate((valid) => {
if (valid) { if (valid) {
let startTime = '' let startTime = ''
let endTime = '' let endTime = ''

View File

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

View File

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

View File

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

View File

@@ -19,6 +19,11 @@ const routes = [
path: '/panel/:typeName', path: '/panel/:typeName',
component: () => import('@/views/charts2/Panel') component: () => import('@/views/charts2/Panel')
}, },
{
name: 'dns6',
path: '/dns6/:typeName',
component: () => import('@/views/charts/Panel')
},
{ {
path: '/report/builtIn', path: '/report/builtIn',
component: () => import('@/views/report/reportTest') component: () => import('@/views/report/reportTest')

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
export const defaultPageSize = 20 export const defaultPageSize = 20
// indexedDB库名
export const dbName = 'cn-db' export const dbName = 'cn-db'
// indexedDB表名
export const dbGeoDataTableName = 'geodata' export const dbGeoDataTableName = 'geodata'
export const dbDrilldownTableConfig = 'cn-drilldown-table-config' export const dbDrilldownTableConfig = 'cn-drilldown-table-config'
export const storageKey = { export const storageKey = {
@@ -61,7 +63,8 @@ export const panelTypeAndRouteMapping = {
cryptocurrency: 7, cryptocurrency: 7,
ipDrillDownTest: 8, ipDrillDownTest: 8,
linkMonitor: 14, linkMonitor: 14,
linkMonitorDrillDown: 15 linkMonitorDrillDown: 15,
dns6: 18
} }
/* operationLog state 执行状态属性 值与名称之间的映射 */ /* operationLog state 执行状态属性 值与名称之间的映射 */
@@ -1552,7 +1555,7 @@ export const npmCategoryInfoMapping = [
// 整屏滚动的路径映射 // 整屏滚动的路径映射
export const wholeScreenRouterMapping = { export const wholeScreenRouterMapping = {
dns: '/panel/dnsServiceInsights' dns: '/dns6/dns6'
} }
export const themeData = [ export const themeData = [

View File

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

View File

@@ -17,6 +17,9 @@ function asciiCompute (num, ascii = 1000, units, dot = 2) {
return ['', ''] return ['', '']
} }
num = Number(num) num = Number(num)
if (num === 0) {
return [0, '']
}
let carry = 0 let carry = 0
if (num > 1) { if (num > 1) {
const log = Math.log(num) / Math.log(ascii) const log = Math.log(num) / Math.log(ascii)

View File

@@ -38,21 +38,6 @@ export default {
i18n: 'overall.operationLog', i18n: 'overall.operationLog',
path: '/administration/operationLog', path: '/administration/operationLog',
icon: 'cn-icon cn-icon-operation-log' icon: 'cn-icon cn-icon-operation-log'
},
{
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

@@ -23,7 +23,7 @@ export default {
const boxDom = document.getElementById('frame-box') const boxDom = document.getElementById('frame-box')
const dom = document.getElementById('frame') const dom = document.getElementById('frame')
const height = boxDom.offsetHeight const height = boxDom.offsetHeight
dom.src = `http://192.168.44.114:8088/superset/explore/?r=15&standalone=1&height=${height || 800}` dom.src = BASE_CONFIG.logUrl + '&height=' + (height + 100)
} }
} }
</script> </script>

View File

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

View File

@@ -11,20 +11,40 @@
<div class="domain-detail-list__content">{{ $_.get(chartData, "org") || '-'}}</div> <div class="domain-detail-list__content">{{ $_.get(chartData, "org") || '-'}}</div>
</div> </div>
<div class="domain-detail-list__row"> <div class="domain-detail-list__row">
<div class="domain-detail-list__label">Email</div> <div class="domain-detail-list__label">注册机构邮箱</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "email") || '-'}}</div> <div class="domain-detail-list__content">{{ $_.get(chartData, "email") || '-'}}</div>
</div> </div>
<div class="domain-detail-list__row"> <div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('overall.country')}}</div> <div class="domain-detail-list__label">注册机构电话</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "domainWhoisPhone") || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">注册国家</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "country") || '-'}}</div> <div class="domain-detail-list__content">{{ $_.get(chartData, "country") || '-'}}</div>
</div> </div>
<div class="domain-detail-list__row"> <div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.creationDate')}}</div> <div class="domain-detail-list__label">注册地邮编</div>
<div class="domain-detail-list__content">{{ dateFormatByAppearance($_.get(chartData, "createTime") * 1) || '-'}}</div> <div class="domain-detail-list__content">{{ $_.get(chartData, "domainWhoisPostcode") || '-'}}</div>
</div> </div>
<div class="domain-detail-list__row"> <div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.expirationDate')}}</div> <div class="domain-detail-list__label">{{$t('entities.creationDate')}}</div>
<div class="domain-detail-list__content">{{ dateFormatByAppearance($_.get(chartData, "expirationTime") * 1) || '-'}}</div> <div class="domain-detail-list__content">{{ $_.get(chartData, "createTime") ? dateFormatByAppearance($_.get(chartData, "createTime") * 1) : '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">过期时间</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "expirationTime") ? dateFormatByAppearance($_.get(chartData, "expirationTime") * 1) : '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">主办单位名称</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "domainIcpCompanyName") || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">主办单位性质</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "domainIcpCompanyType") || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">网站备案号</div>
<div class="domain-detail-list__content">{{ $_.get(chartData, "domainIcpSiteLicense") || '-'}}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -126,7 +126,7 @@ export default {
? legendMapping[ ? legendMapping[
`${this.entity && this.entity.ip ? 'ip_' : ''}${r.legend}` `${this.entity && this.entity.ip ? 'ip_' : ''}${r.legend}`
] ]
: (legendMapping[r.legend] ? legendMapping[r.legend] : humpToSpace(r.legend)), : (legendMapping[r.legend] ? legendMapping[r.legend] : r.legend),
data: r.values.map((v) => [ data: r.values.map((v) => [
Number(v[0]) * 1000, Number(v[0]) * 1000,
Number(v[1]), Number(v[1]),

View File

@@ -35,10 +35,12 @@ export default {
initEcharts (id) { initEcharts (id) {
this.initDom(id, 2) this.initDom(id, 2)
const chartParams = this.chartInfo.params const chartParams = this.chartInfo.params
const domains = this.chartData.map(function (item, i) { let domains = this.chartData.map(function (item, i) {
return item.domain return item.domain
}).join(',') }).join(',')
// 参数字符串限制长度在4300以内。经测试超过4600左右会报错
domains = domains.substring(0, 4300)
domains = domains.substring(0, domains.lastIndexOf(','))
const byType = new Promise(resolve => { const byType = new Promise(resolve => {
get(replaceUrlPlaceholder(chartParams.byCategoryUrl, { domains: domains })).then(response => { get(replaceUrlPlaceholder(chartParams.byCategoryUrl, { domains: domains })).then(response => {
if (response.code === 200) { if (response.code === 200) {
@@ -47,7 +49,7 @@ export default {
} else { } else {
// this.noData0 = false // this.noData0 = false
// chartOption = this.$_.cloneDeep(this.chartOption) // chartOption = this.$_.cloneDeep(this.chartOption)
const data = response.data.result.sort(reverseSortBy('uniqDomains')).map(d => { const originalData = response.data.result.sort(reverseSortBy('uniqDomains')).map(d => {
return { return {
data: d, data: d,
name: d.categoryName, name: d.categoryName,
@@ -55,6 +57,22 @@ export default {
unitType: chartParams.unitType unitType: chartParams.unitType
} }
}) })
const data = originalData.filter((d, i) => i < 5)
let otherValue = 0
originalData.forEach((d, i) => {
if (i > 4) {
otherValue += parseInt(d.uniqDomains)
}
})
data.push({
data: {
uniqDomains: otherValue,
categoryName: 'other'
},
name: 'other',
value: otherValue,
unitType: chartParams.unitType
})
this.chartOption.series[0].data = data this.chartOption.series[0].data = data
} }
} }

View File

@@ -202,10 +202,10 @@
</div> </div>
</div> </div>
<div class="content__data-protocol-value"> <div class="content__data-protocol-value">
<div class="content__data-protocol-value-title">{{$t('protocol.requestVolume')}}</div> <div class="content__data-protocol-value-title">{{$t('protocol.totalFlow')}}</div>
<div class="content__data-protocol-value-num">{{unitConvert($_.get(chartData, 'bytes'), chartInfo.params.unitType).join('')}}</div> <div class="content__data-protocol-value-num">{{unitConvert($_.get(chartData, 'count'), chartInfo.params.unitType).join('')}}</div>
</div> </div>
<div class="content__data-protocol-percent"><span>{{$t('protocol.proportion')}}</span>&nbsp;<span>{{unitConvert($_.get(chartData, 'bytesPercent'), unitTypes.percent).join('')}}</span></div> <div class="content__data-protocol-percent"><span>{{$t('protocol.proportion')}}</span>&nbsp;<span>{{unitConvert($_.get(chartData, 'countPercent'), unitTypes.percent).join('')}}</span></div>
</div> </div>
<div class="content__data-protocol"> <div class="content__data-protocol">
<div class="content__data-protocol-all"> <div class="content__data-protocol-all">
@@ -214,10 +214,10 @@
</div> </div>
</div> </div>
<div class="content__data-protocol-value"> <div class="content__data-protocol-value">
<div class="content__data-protocol-value-title">{{$t('protocol.totalFlow')}}</div> <div class="content__data-protocol-value-title">{{$t('protocol.requestVolume')}}</div>
<div class="content__data-protocol-value-num">{{unitConvert($_.get(chartData, 'count'), chartInfo.params.unitType).join('')}}</div> <div class="content__data-protocol-value-num">{{unitConvert($_.get(chartData, 'bytes'), unitTypes.byte).join('')}}</div>
</div> </div>
<div class="content__data-protocol-percent"><span>{{$t('protocol.proportion')}}</span>&nbsp;<span>{{unitConvert($_.get(chartData, 'countPercent'), unitTypes.percent).join('')}}</span></div> <div class="content__data-protocol-percent"><span>{{$t('protocol.proportion')}}</span>&nbsp;<span>{{unitConvert($_.get(chartData, 'bytesPercent'), unitTypes.percent).join('')}}</span></div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -64,11 +64,11 @@ export const legendMapping = {
ip_packets_sent_rate: i18n.global.t('trafficSummary.packetsPerSecondS2c'), ip_packets_sent_rate: i18n.global.t('trafficSummary.packetsPerSecondS2c'),
dnsLatency: i18n.global.t('dns.latency'), dnsLatency: i18n.global.t('dns.latency'),
queryRate: i18n.global.t('dns.query.rate'), queryRate: i18n.global.t('dns.query.rate'),
formatErrorRate: i18n.global.t('dns.formatErrorRate'), formatErrorRate: 'FormErr',
serverFailureRate: i18n.global.t('dns.serverFailureRate'), serverFailureRate: 'ServFail',
nonExistentDomainRate: i18n.global.t('dns.nonExistentDomainRate'), nonExistentDomainRate: 'NXDomain',
notImplementedRate: i18n.global.t('dns.notImplementedRate'), notImplementedRate: 'NotImp',
queryRefusedRate: i18n.global.t('dns.queryRefusedRate'), queryRefusedRate: 'Refused',
sequenceGapLossPercent: i18n.global.t('entity.ip.sequenceGapLossPercent'), sequenceGapLossPercent: i18n.global.t('entity.ip.sequenceGapLossPercent'),
establishLatency: i18n.global.t('entity.ip.establishLatency'), establishLatency: i18n.global.t('entity.ip.establishLatency'),
httpResponseLatency: i18n.global.t('entity.ip.httpResponseLatency'), httpResponseLatency: i18n.global.t('entity.ip.httpResponseLatency'),

View File

@@ -22,6 +22,9 @@ export const line = {
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
splitLine: {
show: false
},
axisLabel: { axisLabel: {
formatter: function (value) { formatter: function (value) {
return unitConvert(value, unitTypes.number, null, null, 0).join(' ') return unitConvert(value, unitTypes.number, null, null, 0).join(' ')
@@ -30,7 +33,7 @@ export const line = {
}, },
animation: false, animation: false,
grid: { grid: {
left: 55, left: 60,
bottom: 30, bottom: 30,
top: 20, top: 20,
right: 25 right: 25
@@ -62,7 +65,7 @@ export const line = {
{ {
name: '', name: '',
type: 'line', type: 'line',
smooth: false, smooth: true,
symbol: 'none', symbol: 'none',
data: [], data: [],
markLine: {} markLine: {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -79,7 +79,7 @@ import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
export default { export default {
name: 'DnsTrafficLine', name: 'DnsTrafficLine',
components: { components: {
@@ -207,63 +207,9 @@ export default {
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' } { analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
] ]
} else {
this.initData(res.data.result, val, active, show)
} }
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis
mpackets[1].analysis = t.inboundBitsRate.analysis
mpackets[2].analysis = t.outboundBitsRate.analysis
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'bps'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 3) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.echartsInit(this.mpackets)
} else {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
}
} else if (t.type === 'queries' && val === 'Queries/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalQueryRate.analysis
mpackets[0].data = t.totalQueryRate.values ? t.totalQueryRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
e.unitType = 'queries/s'
e.invertTab = false
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.mpackets = mpackets
this.echartsInit(this.mpackets, true)
}
})
} else { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
@@ -526,6 +472,79 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] }) dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
},
initData (data, val, active, show) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys(item).map(t => {
return {
...item[t]
}
})
}
})
}
lineData.splice(0, 1)
const mpackets = _.cloneDeep(this.mpackets)
if (val === 'Queries/s') {
lineData.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
})
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 {
const unit = 'bps'
this.legendInit(lineData, active, show, unit, mpackets)
}
},
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.mpackets = dnsData
if (num === 3) {
dnsData[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(dnsData[0], 0)
this.echartsInit(this.mpackets)
} else {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
}
} }
}, },
mounted () { mounted () {
@@ -542,7 +561,9 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null if (this.myChart) {
echarts.dispose(this.myChart)
}
this.chartOption = null this.chartOption = null
this.unitConvert = null this.unitConvert = null
} }

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="link-statistical-dimension" style="position: relative"> <div class="link-statistical-dimension" style="position: relative">
<div class="dimension-title">{{ $t('linkMonitor.egressLink') }}&nbsp;&&nbsp;{{ $t('linkMonitor.ingressLink') }} <div class="dimension-title" v-if="gridData.length>3">{{ $t('linkMonitor.egressLink') }}&nbsp;&&nbsp;{{ $t('linkMonitor.ingressLink') }}</div>
</div> <div class="dimension-title" v-else>{{ $t('linkMonitor.nextHopInternetOfGrid') }}</div>
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>

View File

@@ -67,7 +67,7 @@ import { useRoute } from 'vue-router'
import { ref, shallowRef } from 'vue' import { ref, shallowRef } from 'vue'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { chartColor3, chartColor4, unitTypes } from '@/utils/constants' import { chartColor3, chartColor4, unitTypes } from '@/utils/constants'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http' import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
@@ -187,94 +187,9 @@ export default {
{ analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' }, { analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' } { analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
] ]
} else {
this.initData(res.data.result, val, active, show)
} }
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis
mpackets[1].analysis = t.ingressBitsRate.analysis
mpackets[2].analysis = t.egressBitsRate.analysis
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.ingressBitsRate.values ? t.ingressBitsRate.values : []
mpackets[2].data = t.egressBitsRate.values ? t.egressBitsRate.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'bps'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 3) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalPacketsRate.analysis
mpackets[1].analysis = t.ingressPacketsRate.analysis
mpackets[2].analysis = t.egressPacketsRate.analysis
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.ingressPacketsRate.values ? t.ingressPacketsRate.values : []
mpackets[2].data = t.egressPacketsRate.values ? t.egressPacketsRate.values : []
let num = 0
mpackets.forEach(e => {
e.unitType = 'packets/s'
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
}
}
})
this.mpackets = mpackets
if (num === 3) {
mpackets[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
}
})
} else { } else {
this.showError = true this.showError = true
this.errorMsg = res.message this.errorMsg = res.message
@@ -457,6 +372,65 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] }) dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
},
initData (data, val, active, show) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys(item).map(t => {
return {
...item[t]
}
})
}
})
}
lineData.splice(0, 1)
const mpackets = _.cloneDeep(this.mpackets)
const unit = val === 'Bits/s' ? 'bps' : 'packets/s'
this.legendInit(lineData, active, show, unit, mpackets)
},
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.mpackets = linkData
if (num === 3) {
linkData[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(linkData[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} }
}, },
mounted () { mounted () {
@@ -473,7 +447,9 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null if (this.myChart) {
echarts.dispose(this.myChart)
}
this.chartOption = null this.chartOption = null
this.unitConvert = null this.unitConvert = null
} }

View File

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

View File

@@ -152,7 +152,6 @@ export default {
searcherApp: '', searcherApp: '',
// 选中的app不区分app和provider // 选中的app不区分app和provider
toSaveApp: [], toSaveApp: [],
appShowType: 'bytes',
pageObj: { // 分页对象 pageObj: { // 分页对象
pageNo: 1, pageNo: 1,
pageSize: 24, pageSize: 24,
@@ -248,7 +247,7 @@ export default {
handleData (prevRequest, request, _t) { handleData (prevRequest, request, _t) {
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([prevRequest, request]).then(res => { Promise.all([prevRequest, request]).then(res => {
this.isNoData = (res[0].data.result.length && res[1].data.result.length) === 0 this.isNoData = res[0].data.result.length === 0 && res[1].data.result.length === 0
if (this.isNoData) { if (this.isNoData) {
this.appData = this.appData.map(t => { this.appData = this.appData.map(t => {
return { return {
@@ -298,16 +297,6 @@ export default {
this.toggleLoading(false) this.toggleLoading(false)
}) })
}, },
metricChange (value) {
if (value === 'Bits/s') {
this.appShowType = 'bytes'
} else if (value === 'Packets/s') {
this.appShowType = 'packets'
} else if (value === 'Sessions/s') {
this.appShowType = 'sessions'
}
this.init()
},
getUrlParam (param, defaultValue, isNumber) { getUrlParam (param, defaultValue, isNumber) {
if (isNumber) { if (isNumber) {
return this.$route.query[param] ? Number(this.$route.query[param]) : defaultValue return this.$route.query[param] ? Number(this.$route.query[param]) : defaultValue
@@ -675,13 +664,6 @@ export default {
}) })
} }
}, },
// moreChange (app) {
// this.appData.forEach(t => {
// if (t.name === app.name && t.type === app.type) {
// t.moreOptions = !t.moreOptions
// }
// })
// },
resize () { resize () {
this.myChart.forEach(t => { this.myChart.forEach(t => {
t.resize() t.resize()
@@ -734,7 +716,9 @@ export default {
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
clearTimeout(this.timerScroll) clearTimeout(this.timerScroll)
clearTimeout(this.timerSearch) clearTimeout(this.timerSearch)
this.myChart = null if (this.myChart) {
echarts.dispose(this.myChart)
}
this.unitConvert = null this.unitConvert = null
} }
} }

View File

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

View File

@@ -12,15 +12,18 @@
:key="index" :key="index"
@mouseenter="mouseenter(item)" @mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)" @mouseleave="mouseleave(item)"
@click="activeChange(item, index)"> @click="activeChange(item, index)"
:test-id="`tab${index}`"
>
<div class="line-value-mpackets-name"> <div class="line-value-mpackets-name">
<div :class="item.class"></div> <div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div> <div class="mpackets-name">{{$t(item.name)}}</div>
</div> </div>
<div class="line-value-unit"> <div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span> <span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2"> <span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span> <span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span> </span>
</div> </div>
</div> </div>
@@ -46,7 +49,7 @@
</div> </div>
<div style="height: calc(100% - 74px); position: relative"> <div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData && !showError"></chart-no-data> <chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" id="overviewLineChart"></div> <div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" ref="overviewLineChart"></div>
<!-- todo 后续改动此处为框选返回--> <!-- todo 后续改动此处为框选返回-->
<!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">--> <!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">-->
<!-- <el-button @click.stop="backBrushHistory">返回</el-button>--> <!-- <el-button @click.stop="backBrushHistory">返回</el-button>-->
@@ -63,13 +66,13 @@ import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
import { ref, shallowRef } from 'vue' import { ref, shallowRef } from 'vue'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools' import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import _ from 'lodash' import _ from 'lodash'
import { get } from '@/utils/http' import axios from 'axios'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
export default { export default {
name: 'NetworkOverviewLine', name: 'NetworkOverviewLine',
@@ -186,32 +189,16 @@ export default {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
let condition = '' if (this.queryCondition) {
if (this.queryCondition && this.tabOperationType !== '3') {
params.q = this.queryCondition params.q = this.queryCondition
} else if (this.tabOperationType == '3' && this.queryCondition) {
if (this.queryCondition.indexOf(' OR ') > -1) {
if (this.networkOverviewBeforeTab === 'isp') {
condition = this.queryCondition.split(/["|'= ](.*?)["|'= ]/)
params.q = `notEmpty(${condition[0]}) OR notEmpty(${condition[9]})`
} else {
condition = this.queryCondition.split(/["|'= ](.*?)["|'= ]/)
params.q = `notEmpty(${condition[0]}) OR notEmpty(${condition[5]})`
}
} else {
condition = this.queryCondition.split(/['=](.*?)['=]/)
params.q = `notEmpty(${condition[0]})`
}
} }
this.toggleLoading(true) this.toggleLoading(true)
axios.get(api.netWorkOverview.totalTrafficAnalysis, { params: params }).then(response => {
get(api.netWorkOverview.totalTrafficAnalysis, params).then((res) => { const res = response.data
this.errorMsg = res.message this.errorMsg = res.message
if (res.code === 200) { if (res.code === 200) {
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
this.showError = false this.showError = false
if (this.isNoData) { if (this.isNoData) {
this.lineTab = '' this.lineTab = ''
this.mpackets = [ this.mpackets = [
@@ -222,125 +209,9 @@ export default {
{ analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' }, { analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' },
{ analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' } { analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' }
] ]
} 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 { } else {
this.showError = true this.showError = true
this.errorMsg = res.message this.errorMsg = res.message
@@ -377,163 +248,163 @@ export default {
} }
}, },
echartsInit (echartsData, show) { echartsInit (echartsData, show) {
if (this.lineTab) { // echarts内容在单元测试时不执行
this.handleActiveBar() if (!this.isUnitTesting) {
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false) if (this.lineTab) {
} else { this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true) echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} } else {
const _this = this echartsData = echartsData.filter(t => t.show === true)
// !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'
}
}
} }
}) const _this = this
if (!show) { // !this.myChart && (this.myChart = echarts.init(dom))
this.chartOption.series.forEach((t) => { // 此处为验证是否因dom未销毁导致图表出错后续可能会改
t.markLine.label.show = false this.chartOption = stackedLineChartOption
t.markLine = [] const chartOption = this.chartOption.series[0]
}) this.chartOption.series = echartsData.map((t, i) => {
} return {
if (this.lineRefer === 'Average' && show) { ...chartOption,
this.chartOption.series.forEach((t, i) => { name: t.name,
t.markLine.label.show = true lineStyle: {
t.markLine.data = [ color: chartColor3[t.positioning],
{ width: 1
yAxis: echartsData[i].analysis.avg },
} stack: t.name !== 'network.total' ? 'network.total' : '',
] symbolSize: function (value) {
}) return _this.symbolSizeSortChange(i, value[0])
} else if (this.lineRefer === '95th Percentile' && show) { },
this.chartOption.series.forEach((t, i) => { emphasis: {
t.markLine.label.show = true itemStyle: {
t.markLine.data = [ borderColor: chartColor4[t.positioning],
{ yAxis: echartsData[i].analysis.p95 } borderWidth: 2,
] shadowColor: chartColor4[t.positioning],
}) shadowBlur: this.sizes[t.positioning] + 2
} 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])
} }
},
// todo 目前暂定框选最小范围为5分钟后续可能会变动 areaStyle: {
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) { opacity: 0.1,
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000 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.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(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) { activeChange (item, index) {
if (this.isNoData) return if (this.isNoData) return
@@ -606,32 +477,34 @@ export default {
referenceSelectChange (val) { referenceSelectChange (val) {
this.lineRefer = val this.lineRefer = val
let echartsData let echartsData
const chartOption = this.myChart.getOption()
if (this.lineTab) { if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false) echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
} else { } else {
echartsData = this.mpackets.filter(t => t.show === true) echartsData = this.mpackets.filter(t => t.show === true)
} }
if (this.lineRefer === 'Average' && this.showMarkLine) { if (!this.isUnitTesting) {
chartOption.series.forEach((t, i) => { const chartOption = this.myChart.getOption()
if (t.name === echartsData[0].name) { if (this.lineRefer === 'Average' && this.showMarkLine) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.avg }] 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) { } else if (this.lineRefer === '95th Percentile' && this.showMarkLine) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.p95 }] 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) { } else if (this.lineRefer === 'Maximum' && this.showMarkLine) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.max }] 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) { symbolSizeSortChange (index, time) {
const dataIntegrationArray = [] const dataIntegrationArray = []
@@ -681,6 +554,87 @@ export default {
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
}, },
initData (data, val, active, show, n) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys(item).map(t => {
return {
...item[t]
}
})
}
})
}
lineData.splice(0, 1)
if (val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
lineData.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
})
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 {
const unit = val === 'Bits/s' ? 'bps' : 'packets/s'
this.legendInit(lineData, active, show, unit, n)
}
},
legendInit (data, active, show, type, n) {
const mpackets = _.cloneDeep(this.mpackets)
data.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
})
let num = 0
mpackets.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.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'
})
}
},
/** /**
* 鼠标右键返回框选的时间范围 * 鼠标右键返回框选的时间范围
*/ */
@@ -702,11 +656,9 @@ export default {
this.myChart = null this.myChart = null
this.chartOption = null this.chartOption = null
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
if (this.lineTab) { if (this.lineTab && this.metric !== 'Sessions/s') {
const data = this.mpackets.find(t => t.class === this.lineTab) const data = this.mpackets.find(t => t.class === this.lineTab)
if (data && data.positioning) { this.activeChange(data, data.positioning)
this.activeChange(data, data.positioning)
}
} else { } else {
this.init() this.init()
} }
@@ -717,11 +669,13 @@ export default {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
let myChart = echarts.getInstanceByDom(document.getElementById('overviewLineChart')) let myChart = echarts.getInstanceByDom(this.$refs.overviewLineChart)
if (myChart) { if (myChart) {
echarts.dispose(myChart) echarts.dispose(myChart)
} }
this.myChart = null if (this.myChart) {
echarts.dispose(this.myChart)
}
// 检测时发现该方法占用较大内存,且未被释放 // 检测时发现该方法占用较大内存,且未被释放
this.unitConvert = null this.unitConvert = null
myChart = null myChart = null

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import _ from 'lodash' import _ from 'lodash'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { getLineType, getLineIndexUnit, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
export default { export default {
@@ -80,11 +80,11 @@ export default {
{ name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' } { name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
], ],
npmQuantity: [ npmQuantity: [
{ name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time }, { 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 }, { 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 }, { 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 }, { 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 } { name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
], ],
chartData: {}, chartData: {},
metricOptions: [ metricOptions: [
@@ -144,7 +144,6 @@ export default {
if (!val) { if (!val) {
val = this.metricFilter val = this.metricFilter
} }
// const conditionStr = this.$route.query.queryCondition ? this.$route.query.queryCondition : ''
let condition = '' let condition = ''
let type = this.dimensionType let type = this.dimensionType
if (this.queryCondition.indexOf(' OR ') > -1) { if (this.queryCondition.indexOf(' OR ') > -1) {
@@ -209,92 +208,15 @@ export default {
{ name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' } { name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
] ]
this.npmQuantity = [ this.npmQuantity = [
{ name: this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time }, { 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 }, { 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 }, { 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 }, { 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 } { 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 { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
@@ -308,204 +230,50 @@ export default {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} else { } else {
if (val === 'Bits/s' || val === 'Packets/s' || val === 'Sessions/s') { this.toggleLoading(true)
this.toggleLoading(true) const totalTrafficAnalysis = get(api.npm.overview.totalTrafficAnalysis, params)
get(api.npm.overview.totalTrafficAnalysis, params).then(res => { const totalNetworkAnalysis = get(api.npm.overview.totalNetworkAnalysis, params)
if (res.code === 200) { const totalHttpResponseDelay = get(api.npm.overview.totalHttpResponseDelay, params)
this.showError = false const totalSslConDelay = get(api.npm.overview.totalSslConDelay, params)
this.isNoData = res.data.result.length === 0 const npmLineData = []
if (this.isNoData) { Promise.all([totalNetworkAnalysis, totalTrafficAnalysis, totalHttpResponseDelay, totalSslConDelay]).then(res => {
this.mpackets = [ res.forEach(item => {
{ name: 'network.total', show: true, positioning: 0, data: [], unitType: 'number' }, if (item.code === 200) {
{ name: 'network.inbound', show: true, positioning: 1, data: [], unitType: 'number' }, npmLineData.push(...item.data.result)
{ name: 'network.outbound', show: true, positioning: 2, data: [], unitType: 'number' },
{ name: 'network.internal', show: true, positioning: 3, data: [], unitType: 'number' },
{ name: 'network.through', show: true, positioning: 4, data: [], unitType: 'number' },
{ name: 'network.other', show: true, positioning: 5, data: [], unitType: 'number' }
]
}
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : []
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalSessionsRate.values ? t.totalSessionsRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
}
})
} else { } else {
this.isNoData = false this.isNoData = false
this.showError = true this.showError = true
this.errorMsg = res.message this.errorMsg = res.message
} }
}).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.showError = false
this.toggleLoading(true) this.isNoData = npmLineData.length === 0
get(api.npm.overview.totalNetworkAnalysis, params).then(res => { if (this.isNoData) {
if (res.code === 200) { this.mpackets = [
this.showError = false { name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
this.isNoData = res.data.result.length === 0 { name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
if (this.isNoData) { { name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
this.npmQuantity = [ { name: this.$t('network.internal'), show: true, positioning: 3, data: [], unitType: 'number' },
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time }, { name: this.$t('network.through'), show: true, positioning: 4, data: [], unitType: 'number' },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time }, { name: this.$t('network.other'), show: true, positioning: 5, data: [], unitType: 'number' }
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time }, ]
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent }, this.npmQuantity = [
{ name: '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 },
res.data.result.forEach((t) => { { name: this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 },
if (t.type === 'establishLatencyMs' && val === 'establishLatencyMs') { { name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
const npmQuantity = _.cloneDeep(this.npmQuantity) ]
npmQuantity[0].data = t.establishLatencyMs.values ? t.establishLatencyMs.values : [] } else {
npmQuantity.forEach((e, i) => { this.initData(npmLineData, val)
e.show = i === 0 }
}) }).catch(e => {
this.npmQuantity = npmQuantity this.isNoData = false
this.echartsInit(this.npmQuantity, '(ms)') this.showError = true
} else if (t.type === 'tcpLostlenPercent' && val === 'tcpLostlenPercent') { this.errorMsg = e.message
const npmQuantity = _.cloneDeep(this.npmQuantity) }).finally(() => {
npmQuantity[3].data = t.tcpLostlenPercent.values ? t.tcpLostlenPercent.values : [] this.toggleLoading(false)
npmQuantity.forEach((e, i) => { })
e.show = i === 3
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
} else if (t.type === 'pktRetransPercent' && val === 'pktRetransPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[4].data = t.pktRetransPercent.values ? t.pktRetransPercent.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 4
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'httpResponseLatency') {
this.toggleLoading(true)
get(api.npm.overview.totalHttpResponseDelay, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach(t => {
if (t.type === 'httpResponseLatency' && val === 'httpResponseLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[1].data = t.httpResponseLatency.values ? t.httpResponseLatency.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 1
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'sslConLatency') {
this.toggleLoading(true)
get(api.npm.overview.totalSslConDelay, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach(t => {
if (t.type === 'sslConLatency' && val === 'sslConLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[2].data = t.sslConLatency.values ? t.sslConLatency.values : []
npmQuantity.forEach((e, i) => {
e.show = i === 2
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
}
} }
}, },
echartsInit (echartsData, legendUnit) { echartsInit (echartsData, legendUnit) {
@@ -619,6 +387,67 @@ export default {
}, },
resize () { resize () {
this.myChart.resize() this.myChart.resize()
},
initData (data, val) {
let lineData = []
if (data !== undefined && data.length > 0) {
data.forEach(item => {
item.type = getLineType(item.type)
if (item.type === val) {
lineData = Object.keys((item)).map(t => {
return {
...item[t],
index: getLineIndexUnit(item.type, false),
unit: getLineIndexUnit(item.type, true)
}
})
}
})
}
lineData.splice(0, 1)
const mpackets = _.cloneDeep(this.mpackets)
const npmQuantity = _.cloneDeep(this.npmQuantity)
if (val === 'Sessions/s') {
lineData.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
})
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (val !== 'Bits/s' && val !== 'Packets/s' && val !== 'Sessions/s') {
this.legendInit(lineData, npmQuantity, true)
} else {
this.legendInit(lineData, mpackets, 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.mpackets = npmData
this.echartsInit(this.mpackets)
}
} }
}, },
mounted () { mounted () {
@@ -633,8 +462,9 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null if (this.myChart) {
this.myChart = null echarts.dispose(this.myChart)
}
this.unitConvert = null 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 () { mounted () {
if (this.$route.query.eventId) { this.initExpendTab()
if (parseFloat(this.$route.query.eventId) === this.detection.eventId) {
const container = document.getElementById('cnContainer')
container.scrollTop = 555 + this.index * 97
this.isCollapse = false
this.$emit('switchCollapse', this.isCollapse, this.index)
}
}
}, },
computed: { computed: {
iconClass () { iconClass () {
@@ -186,7 +178,7 @@ export default {
this.$emit('switchCollapse', this.isCollapse, this.index) this.$emit('switchCollapse', this.isCollapse, this.index)
if (this.isCollapse) { if (this.isCollapse) {
const newQuery = this.$route.query // 深拷贝路由数据 const newQuery = this.$route.query
delete newQuery.eventId delete newQuery.eventId
this.reloadUrl(newQuery, 'cleanOldParams') this.reloadUrl(newQuery, 'cleanOldParams')
} else { } else {
@@ -207,6 +199,33 @@ export default {
newUrl = urlParamsHandler(window.location.href, query, newParam, clean) newUrl = urlParamsHandler(window.location.href, query, newParam, clean)
} }
overwriteUrl(newUrl) overwriteUrl(newUrl)
},
/**
* 初始化从npm跳转过来的id并展开tab
*/
initExpendTab () {
if (this.$route.query.eventId) {
if (this.$route.query.eventId === this.detection.eventId) {
const container = document.getElementById('cnContainer')
const dom = document.getElementsByClassName('cn-detection__case')
// 未展开的item折叠块高度67+下边距10+底部线高度1兼容不同分辨率下的tab高度
let itemHeight = 78
if (dom && this.index > 0) {
itemHeight = dom[0].clientHeight + 11
}
let topHeight = 554 + this.index * itemHeight
// 经过测试对比第7个以后的tab会往上移动但是手动展开和自动展开tab的滚动条高度一致为解决该问题自动加上误差值
if (this.index > 6) {
topHeight = topHeight + 6
}
container.scrollTop = topHeight
this.isCollapse = false
this.$emit('switchCollapse', this.isCollapse, this.index)
}
}
} }
} }
} }

View File

@@ -294,7 +294,7 @@ export default {
const chartDom = document.getElementById(`eventSeverityTrendBar${this.pageType}`) const chartDom = document.getElementById(`eventSeverityTrendBar${this.pageType}`)
const eventSeverityTrendOption = this.$_.cloneDeep(multipleBarOption) const eventSeverityTrendOption = this.$_.cloneDeep(multipleBarOption)
const xData = [] let xData = []
dataMap.forEach(function (value) { dataMap.forEach(function (value) {
// eventSeverityTrendOption.series[Number(getSeriesIndex(key))].data = value.map(v => Number(v[1])) // eventSeverityTrendOption.series[Number(getSeriesIndex(key))].data = value.map(v => Number(v[1]))
value.forEach(item => { value.forEach(item => {
@@ -303,6 +303,9 @@ export default {
} }
}) })
}) })
xData = xData.sort((a, b) => {
return new Date(a) - new Date(b)
})
eventSeverityTrendOption.series.forEach(serie => { eventSeverityTrendOption.series.forEach(serie => {
const seriesData = [] const seriesData = []
xData.forEach(item => { xData.forEach(item => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,34 +1,215 @@
/**
* jest: https://jestjs.io/docs/getting-started
* vue-jest: https://test-utils.vuejs.org/guide/
* */
import Test from '../src/Test' import Test from '../src/Test'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { getNameByEventType } from '@/utils/tools'
import axios from 'axios'
import indexedDBUtils from '@/indexedDB'
import ElementPlus from 'element-plus'
// 模拟vue-router库否则组件中引用vue-router的代码报错 // 模拟的axios返回数据
jest.mock('vue-router', () => { const mockId = { data: 2 }
return { const mockTitle = { data: 'title2' }
useRouter: function () { return { currentRoute: '/' } } const mockCount = { data: 999 }
} const mockDifferentParam0 = { data: 0 }
}) const mockDifferentParam1 = { data: 1 }
test('点击按钮后count的值+1', async () => { const mergeRequestParam0 = { data: 0 }
// 加载vue组件获得实例 const mergeRequestParam1 = { data: 1 }
const wrapper = mount(Test) const mergeRequestChildParam0 = { data: 0 }
const mergeRequestChildParam1 = { data: 1 }
// 取到文本和按钮的dom
const textNode = await wrapper.get('[data-test="count"]') describe('单元测试demo', () => {
const button = await wrapper.get('[data-test="button"]') test('Vue组件--点击按钮后count的值+1', async () => {
// 断言文本dom的内容是否是'0' // 若组件中使用了vue-router需要细化模拟相关内容
expect(textNode.text()).toContain('0') require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟按钮dom点击执行click()方法 require('vue-router').useRouter.mockReturnValue({ currentRoute: { value: { path: '/' } } })
await button.trigger('click') // 加载vue组件获得实例
// 断言点击按钮后文本dom的内容是否是'1' const wrapper = mount(Test, {
expect(textNode.text()).toContain('1') global: {
// 再次点击 plugins: [ElementPlus]
await button.trigger('click') }
// 断言点击按钮后文本dom的内容是否是'2' })
expect(textNode.text()).toContain('2') // 取到文本和按钮的dom
const textNode = await wrapper.get('[test-id="count"]')
// 通过wrapper.vm.xxx直接执行click方法、获取data的数据 const button = await wrapper.get('[test-id="button"]')
expect(wrapper.vm.count).toEqual(2) // 断言文本dom的内容是否是'0'
await wrapper.vm.click() expect(textNode.text()).toBe('0')
expect(wrapper.vm.count).toEqual(3) // 模拟按钮dom点击执行click()方法
await button.trigger('click')
// 更多断言类型https://jestjs.io/zh-Hans/docs/expect // 断言点击按钮后文本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,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,83 @@
import NetworkOverviewLine from '@/views/charts2/charts/networkOverview/NetworkOverviewLine'
import { mount } from '@vue/test-utils'
import axios from 'axios'
const mockGet = {
data: {"status":200,"code":200,"success":true,"message":null,"formatType":"json","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"}
}
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,"params":{},"cby":1,"ctime":"2022-07-06 16:59:22","uby":1,"utime":"2022-07-06 16:59:22","remark":"","state":1,"system":0,"buildIn":0,"uuser":{"id":1,"name":null,"username":"admin","salt":null,"lang":null,"theme":null,"lastLoginIp":null,"lastLoginTime":null,"ctime":null,"cby":null,"email":null,"mobile":null,"status":null,"source":null,"buildIn":null,"roleIds":null,"usergroupIds":null,"roles":null,"apiKeyId":null},"cuser":{"id":1,"name":null,"username":"admin","salt":null,"lang":null,"theme":null,"lastLoginIp":null,"lastLoginTime":null,"ctime":null,"cby":null,"email":null,"mobile":null,"status":null,"source":null,"buildIn":null,"roleIds":null,"usergroupIds":null,"roles":null,"apiKeyId":null},"children":[],"parent":null,"panel":{"id":1,"name":"Network Overview","i18n":null,"type":null,"params":null,"cby":null,"ctime":null,"uby":null,"utime":null,"remark":null,"state":null,"buildIn":null,"uuser":null,"cuser":null},"i":1,"category":"echarts","firstShow":false,"moved":false}
describe('views/charts2/charts/networkOverview/NetworkOverviewLine.vue测试', () => {
test('Metric=Bits/s点击第三个tab', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟axios返回数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart
}
})
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(() => {
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 () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟axios返回数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart,
metric: 'Packets/s'
}
})
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
const textNode1 = await wrapper.get('[test-id="tabContent1"]')
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
await new Promise(resolve => setTimeout(() => {
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 () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟axios返回数据
axios.get.mockResolvedValue(mockGet)
// 加载vue组件获得实例
const wrapper = mount(NetworkOverviewLine, {
propsData: {
timeFilter,
chart,
metric: 'Sessions/s'
}
})
const textNode0 = await wrapper.get('[test-id="tabContent0"]')
await new Promise(resolve => setTimeout(() => {
expect(textNode0.text()).toEqual('29.89sessions/s')
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":1173511643475208192,"eventSeverity":"critical","eventKey":"114.114.114.114 dns error","eventType":"dns error"},{"eventId":1173504415263352832,"eventSeverity":"high","eventKey":"116.178.78.241 http error","eventType":"http error"},{"eventId":1173492761289025537,"eventSeverity":"medium","eventKey":"223.6.6.6 dns error","eventType":"dns error"},{"eventId":1173489002890651648,"eventSeverity":"low","eventKey":"114.114.114.114 dns error","eventType":"dns error"},{"eventId":1173482380537620480,"eventSeverity":"info","eventKey":"1.1.1.2 dns error","eventType":"http error"},{"eventId":1173482380537620481,"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":1132790825086844928,"eventSeverity":"critical","eventType":"http error","startTime":1672802700},{"eventId":1132132403379142657,"eventSeverity":"high","eventType":"dns error","startTime":1672763400},{"eventId":1131441760155688960,"eventSeverity":"low","eventType":"dns error","startTime":1672722300},{"eventId":1131411523384598528,"eventSeverity":"medium","eventType":"http error","startTime":1672720500},{"eventId":1131390214323789824,"eventSeverity":"info","eventType":"dns error","startTime":1672719300},{"eventId":1131306200132968450,"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-2023-01-04T11:25:00+08:00-0"]')
const startTime1 = wrapper.get('[test-id="startTime-2023-01-04T00:30:00+08:00-1"]')
const startTime2 = wrapper.get('[test-id="startTime-2023-01-03T13:05:00+08:00-2"]')
const startTime3 = wrapper.get('[test-id="startTime-2023-01-03T12:35:00+08:00-3"]')
const startTime4 = wrapper.get('[test-id="startTime-2023-01-03T12:15:00+08:00-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))
})
})