Compare commits

...

80 Commits

Author SHA1 Message Date
陈劲松
5ee7631d25 Merge branch 'cherry-pick-53b40851' into 'dev-23.02'
CN-1156 fix: 修复s3=0时无法下载和预览的问题

See merge request cyber-narrator/cn-ui!32
2023-07-05 03:32:42 +00:00
chenjinsong
28c0b3aebf CN-1156 fix: 修复s3=0时无法下载和预览的问题
(cherry picked from commit 53b4085111)
2023-07-05 11:32:36 +08:00
chenjinsong
b178941f01 fix: 修复linkmonitor底下图表名字错误的问题 2023-03-16 18:56:10 +08:00
陈劲松
ecd82b08c7 Merge branch 'cherry-pick-831b00fc' into 'dev-23.02'
fix: 写死知识库表单cancel路由

See merge request cyber-narrator/cn-ui!26
2023-03-16 10:03:26 +00:00
chenjinsong
23c88ae447 fix: 写死知识库表单cancel路由
(cherry picked from commit 831b00fc78)
2023-03-16 18:03:19 +08:00
陈劲松
9b2f2f5e21 Merge branch 'cherry-pick-5116d4f6' into 'dev-23.02'
CN-749 fix: 增加列表错误提示

See merge request cyber-narrator/cn-ui!25
2023-03-14 11:42:29 +00:00
chenjinsong
d8de1e5d69 CN-749 fix: 增加列表错误提示
(cherry picked from commit 5116d4f6d6)
2023-03-14 19:42:13 +08:00
chenjinsong
502d098413 fix: 修复npm折线图bug 2023-03-14 17:18:49 +08:00
陈劲松
a8a84b6abb Merge branch 'cherry-pick-26b54baa' into 'dev-23.02'
下钻表格问题修改:对特殊字符单引号做处理,以正确显示列表信息

See merge request cyber-narrator/cn-ui!23
2023-03-14 08:52:11 +00:00
hyx
da9eeae556 下钻表格问题修改:对特殊字符单引号做处理,以正确显示列表信息
(cherry picked from commit 26b54baa33)
2023-03-14 16:52:04 +08:00
chenjinsong
4125a88bba fix: 将默认时间范围统一为1小时 2023-03-14 11:39:30 +08:00
陈劲松
317693e2da Merge branch 'cherry-pick-5536ac94' into 'dev-23.02'
CN-906 fix: 调整步骤条样式

See merge request cyber-narrator/cn-ui!22
2023-03-14 02:11:10 +00:00
chenjinsong
be79109487 CN-906 fix: 调整步骤条样式
(cherry picked from commit 5536ac9490)
2023-03-14 10:10:58 +08:00
唐浩
b99a20725a Update .gitlab-ci.yml file 2023-03-13 10:32:13 +00:00
唐浩
c40941fa60 Update .gitlab-ci.yml file 2023-03-13 10:05:57 +00:00
唐浩
6fba2c6116 Update .gitlab-ci.yml file 2023-03-13 09:56:55 +00:00
唐浩
2609c2e721 Merge branch 'cherry-pick-c26b0525' into 'dev-23.02'
fix: 修复knowledgeBase-create在有滚动条时时间轴图标不滚动的情况

See merge request cyber-narrator/cn-ui!21
2023-03-13 09:46:44 +00:00
刘洪洪
158a143d76 fix: 修复knowledgeBase-create在有滚动条时时间轴图标不滚动的情况
(cherry picked from commit c26b05259d)
2023-03-13 17:46:19 +08:00
chenjinsong
1dee23435d CN-926 fix: 样式细节优化 2023-03-10 21:15:09 +08:00
hyx
64a6eac4a7 CN-926 列表页样式改版 2023-03-10 20:50:54 +08:00
刘洪洪
8f1adef30d fix: 修复steps展开收起点击范围过大的问题 2023-03-10 16:30:35 +08:00
chenjinsong
2b66aecf44 fix: 修复校验错误提示逻辑 2023-03-10 13:32:46 +08:00
chenjinsong
1311e53b65 fix: 修复知识库增加重复校验后无法保存的问题 2023-03-10 13:13:27 +08:00
chenjinsong
d9677e89f5 feat: 增加步骤条交互 2023-03-09 15:20:59 +08:00
刘洪洪
d89c20a75c fix: 修复knowledge模块因初始化格式不符等导致的warning提示 2023-03-09 10:39:26 +08:00
chenjinsong
476c49b08f fix: 统一命名 2023-03-09 10:14:05 +08:00
chenjinsong
32a04eec4c fix: 修复删除导入数据后页码可能不对的问题 2023-03-08 18:10:55 +08:00
chenjinsong
fb9034229e feat: 报告参数增加input类型 2023-03-08 17:57:01 +08:00
chenjinsong
43c79b94f1 CN-906 fix: 修复一系列问题 2023-03-08 17:35:22 +08:00
chenjinsong
801858c07d CN-906 fix: 上传loading、保存loading 2023-03-07 18:48:15 +08:00
chenjinsong
85d9b3d738 CN-906 fix: 上传loading、保存loading 2023-03-06 20:16:31 +08:00
chenjinsong
ac28e52ca7 CN-906 fix: 知识库列表排序字段名更正 2023-03-06 14:19:29 +08:00
chenj
e2f9eb7f59 CN-906 feat: 优化上传交互 2023-03-05 13:50:22 +08:00
chenjinsong
a9e5915113 CN-906 feat: 知识库列表、新增功能(细节体验待完善) 2023-03-02 20:37:21 +08:00
刘洪洪
fdb4ec5cf5 fix: 修复NetworkOverviewLine的eslint报警提示 2023-03-02 18:36:02 +08:00
刘洪洪
04062195e2 fix: 修复因使用弃用的方法'>>>',导致的eslint报警提示 2023-03-02 18:16:38 +08:00
刘洪洪
e29fc1244c Merge remote-tracking branch 'origin/dev' into dev 2023-03-02 17:56:05 +08:00
刘洪洪
3fc6a42d99 fix: 修复项目运行时eslint报错的提示 2023-03-02 17:55:47 +08:00
hanyuxia
1c776f4119 CN-903 人工测试--NetworkOverviewLine,修改问题:(1) 刷新未保留选中Tab的状态 2023-03-01 17:13:01 +08:00
hanyuxia
e87deec34c CN-903 人工测试--NetworkOverviewLine:(1) 登录系统后,点击其中一个Tab标签,再点击二级菜单(网络概况)或者右侧刷新按钮或者F5刷新,折线图不显示;(2)切换参考线:报错,界面数据不更新;(3)点击高亮 tab 后未取消高亮,未恢复到全不高亮的状态; 2023-03-01 16:52:33 +08:00
hanyuxia
5685e6b2c0 CN-893 单测用例--下钻table 2023-03-01 09:33:43 +08:00
hyx
9fddf7914d CN-893 单测用例--下钻table 2023-02-28 11:49:13 +08:00
刘洪洪
a55ab9fb15 fix: 修复linkMonitor下一跳网格图排序错乱问题 2023-02-27 16:14:59 +08:00
刘洪洪
35b1f31601 fix: 去除无效代码 2023-02-27 16:08:33 +08:00
刘洪洪
8627cec919 Merge remote-tracking branch 'origin/dev' into dev 2023-02-27 16:01:43 +08:00
刘洪洪
ee14b471a8 CN-894: 单测用例--用例更新 2023-02-27 16:01:25 +08:00
chenjinsong
5896931487 CN-906 fix: 面包屑功能调整 2023-02-26 23:06:57 +08:00
chenjinsong
f87714fae2 CN-906 fix: 面包屑功能调整(部分) 2023-02-24 18:51:17 +08:00
刘洪洪
def8851da0 Merge remote-tracking branch 'origin/dev' into dev 2023-02-23 18:21:13 +08:00
刘洪洪
8b3f43eb98 fix: 完善npm-event环形图单测 2023-02-23 18:20:56 +08:00
chenjinsong
3d2623f8cf CN-906 fix: 知识库列表简版;调整一些目录名 2023-02-23 18:12:20 +08:00
刘洪洪
a9fe48933c fix: 修复npm-event环形图event计数错误的问题 2023-02-23 16:29:13 +08:00
李金洋
1c2782e8e3 Update .gitlab-ci.yml 2023-02-23 03:32:58 +00:00
lijinyang
4462b5eaa3 fix: ci构建增加测试阶段 2023-02-23 11:17:08 +08:00
刘洪洪
46a0985f55 fix: 修复npm-event单测id根据时间命名可能会导致报错的问题 2023-02-23 10:54:18 +08:00
刘洪洪
4d6c444b25 fix: 修复linkMonitor下一跳网格图排序错乱问题 2023-02-23 10:53:12 +08:00
chenjinsong
e009142e7d fix: 完善NetworkOverviewLine组件测试用例;更改变量名; 2023-02-17 20:50:29 +08:00
刘洪洪
14a24b268a fix: 去除无效代码 2023-02-17 16:14:38 +08:00
刘洪洪
06de8e2bc9 feat: 优化Link monitor网格图单测的模拟数据 2023-02-17 15:43:03 +08:00
刘洪洪
d788657048 feat: 优化单测模拟数据格式,避免eslint报错提示造成卡顿 2023-02-17 15:28:43 +08:00
lijinyang
524be68781 Update .gitlab-ci.yml 2023-02-14 15:11:42 +08:00
唐浩
0139a542da Update .gitlab-ci.yml file 2023-02-09 08:05:47 +00:00
@changcode
da607274d8 CN-878 feat: 单测用例--network overview 供应商与应用 2023-02-07 18:10:56 +08:00
唐浩
48426a3955 Update .gitlab-ci.yml file 2023-02-07 09:26:20 +00:00
hyx
e95ca314a6 CN-879 单测用例--npm events 最底下的表格 2023-02-07 17:20:26 +08:00
唐浩
3d18a4b285 Update .gitlab-ci.yml file 2023-02-07 07:04:32 +00:00
刘洪洪
2b67cb4a0b fix: detection-performanceEvent去除对eventId的格式转换 2023-02-07 10:29:27 +08:00
chenjinsong
eadb1e350d CN-858 fix: 报告任务时间设置可编辑 2023-02-06 16:19:32 +08:00
陈劲松
46a23d9464 Merge branch 'cherry-pick-84ca05dc' into 'dev'
fix: 修复dns仪表盘切换时间不生效的问题

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

See merge request cyber-narrator/cn-ui!18
2023-02-03 03:09:18 +00:00
chenjinsong
8bf7b85c2e fix: 修复地图资源路径拼写错误的问题
(cherry picked from commit ec486fe930)
2023-02-03 11:09:04 +08:00
@changcode
ab146b5b15 feat: 单测 npm events 类型分类事件 2023-02-03 10:46:06 +08:00
刘洪洪
230e897146 CN-875: 单测用例--link蜂窝图 2023-02-02 18:14:20 +08:00
104 changed files with 6611 additions and 1759 deletions

View File

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

View File

@@ -2,6 +2,7 @@
stages:
- gen_git-log
- build_project
- test
- build_image
cache:
@@ -10,7 +11,7 @@ cache:
- package.json
paths:
- node_modules
- dist/
# - dist/
before_script:
- export CNUI_TAG=$(date +%Y%m%d%H%M%S)
@@ -41,21 +42,38 @@ build_project:
- cnpm install --save-dev --unsafe-perm
- echo "npm run build"
- cnpm run build
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
when: on_success
paths:
- dist/
only:
- dev
- tags
tags:
- galaxy
test:
stage: test
script:
- cnpm run test
when: on_success
only:
- dev
tags:
- galaxy
build_image:
dependencies:
- build_project
stage: build_image
script:
- echo "docker build"
- sudo docker build --no-cache -t cn-ui:$CNUI_TAG .
- sudo docker build --no-cache -t cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG .
- echo "docker tag"
- sudo docker tag cn-ui:$CNUI_TAG 192.168.40.153:9080/cyber-narrator/cn-ui:$CNUI_TAG
- sudo docker tag cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG
- echo "docker push"
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui:$CNUI_TAG
- sudo docker push 192.168.40.153:9080/cyber-narrator/cn-ui-$CI_COMMIT_REF_NAME:$CNUI_TAG
when: on_success
only:
- dev
@@ -64,6 +82,8 @@ build_image:
build_release_image:
dependencies:
- build_project
stage: build_image
script:
- echo 'tag名称是'

View File

@@ -148,7 +148,7 @@ export default {
align-items: center;
}
>>>.el-input__inner {
:deep .el-input__inner {
background-color: #0B325C !important;
border: none;
border-radius: 0px;
@@ -156,10 +156,10 @@ export default {
font-size: 14px;
line-height: 14px;
}
>>>.el-input__inner:hover {
:deep .el-input__inner:hover {
border-color: #295688;
}
>>>.el-input__inner:focus {
:deep .el-input__inner:focus {
border-color: #295688;
}
.el-button--primary:hover, .el-button--primary:focus, .el-button--primary:active {
@@ -167,10 +167,10 @@ export default {
border-color: #21B4ED;
color: #FFFFFF;
}
>>>.el-loading-mask {
:deep .el-loading-mask {
background-color: transparent;
}
>>>input {
:deep input {
-webkit-text-fill-color: rgba(231,234,237, .8) !important;
transition: background-color 500000000000000000s ease-in-out 0s !important;
caret-color: #fff ;
@@ -205,14 +205,14 @@ export default {
flex-direction: column;
align-items: center;
}
>>>.el-form-item {
:deep .el-form-item {
width: 334px;
margin-bottom: 25px;
}
>>>.el-input__prefix {
:deep .el-input__prefix {
color: #6DBBFF;
}
>>>.el-input__prefix i {
:deep .el-input__prefix i {
width: 17px;
font-size: 17px;
}

View File

@@ -14,9 +14,8 @@
z-index: 0;
}
.main-container {
padding: 10px;
padding: 0;
height: 100%;
background-color: #f6f6f6;
&>div {
background-color: white;
}
@@ -36,7 +35,7 @@
align-items : center;
position: relative;
justify-content: space-between;
padding: 14px 20px;
padding: 12px 20px;
&.top-tools--sub {
align-items: center;
@@ -50,6 +49,12 @@
.top-tool-btn {
border-left: none;
}
.top-tool-btn--search:hover {
border-left: none !important;
}
.top-tool-btn--search:focus {
border-left: none !important;
}
}
.top-tool-right {
@@ -74,17 +79,28 @@
}
.top-tool-btn {
cursor: pointer;
height: 33px;
width: 36px;
height: 28px;
width: 72px;
border: 1px solid $--border-color-primary;
outline: none;
border-radius: $--button-border-radius;
background-color: $--button-gray-background-color;
transition: background-color linear .1s;
font-size:12px;
font-weight: 500;
font-family: NotoSansHans-Medium !important;
i {
font-size: 14px;
color: $--button-gray-color;
color: #575757;
margin-right:4px;
}
}
.top-tool-btn:disabled {
cursor: not-allowed;
opacity: 0.66;
i {
}
}
@@ -94,13 +110,14 @@
color: #666;
}
.top-tool-btn:hover:not(.cn-btn-disabled) {
border: 1px solid $--border-color-primary;
background-color: $--button-gray-hover-background-color;
}
.top-tool-btn:focus:not(.cn-btn-disabled), .top-tool-btn.is-focus {
background-color: $--button-gray-hover-background-color;
border: 1px solid $--color-primary !important;
background-color: $--button-gray-active-background-color;
border: 1px solid $--border-color-primary;
i {
color: $--button-gray-active-color;
color: #575757;
}
}
.top-tool-btn--delete.top-tool-btn:focus:not(.cn-btn-disabled) {
@@ -111,6 +128,56 @@
color: #F0745A;
}
}
.top-tool-btn--create {
background-color: #38ACD2 !important;
border-color: #2E88A6 !important;
color:#FFFFFF;
i {
color: #FFFFFF;
}
}
.top-tool-btn--create:hover {
background-color: #57B8D9 !important;
border-color: #2E88A6 !important;
color:#FFFFFF;
i {
color: #FFFFFF;
}
}
.top-tool-btn--create:focus {
background-color: #31A5CD !important;
border-color: #2E88A6 !important;
color:#FFFFFF !important;
i {
color: #FFFFFF !important;
}
}
.top-tool-btn--create:disabled {
opacity: 0.66;
background-color: #38ACD2 !important;
border-color: #2E88A6 !important;
color:#FFFFFF;
i {
color: #FFFFFF;
}
}
.btn-customize {
color:$blue;
font-size: 12px;
.icon-gear{
color:#2C72C6;
width:12px;
height:12px;
margin-right:2px;
font-size:12px;
}
}
.btn-customize:hover {
cursor:pointer;
}
.top-tool-btn--dropdown {
position: relative;
width: auto;
@@ -130,6 +197,7 @@
width: calc(100% - 40px);
border: 1px solid $--right-box-border-color;
border-bottom: none;
border-radius:4px;
.caret-wrapper {
height: 23px;
@@ -152,7 +220,8 @@
th {
border-color: $--right-box-border-color;
padding: 8px 0;
background: #F9F9F9;
background: #FFF;
border-right:0px;
}
.el-table__header th:first-of-type {
border-left: none;
@@ -244,7 +313,7 @@
border-right: none !important;
}
/* 最后一列用box-shadow模拟边框 */
.el-table:not(.no-operation):not(.chart-table).el-table--border .el-table__body-wrapper td:nth-last-child(2) {
/*.el-table:not(.no-operation):not(.chart-table).el-table--border .el-table__body-wrapper td:nth-last-child(2) {
box-shadow: 1px 0 $--right-box-border-color;
}
.el-table:not(.no-operation):not(.chart-table).el-table--border .el-table__header-wrapper th:nth-last-child(3) {
@@ -263,7 +332,7 @@
th:last-of-type {
border-right: none !important;
}
}
}*/
.el-table--border:not(.chart-table)::after, .el-table--group:not(.chart-table)::after {
width: 0;
}

View File

@@ -78,12 +78,28 @@
border-radius: 2px;
background-color: #F9F9F9;
transition: background-color linear .1s;
padding-left:10px;
}
.list-page .top-tools .top-tool-btn--search{
width:28px !important;
height:28px !important;
padding:unset !important;
i {
font-size: 14px;
color: #575757;
margin-right:unset !important;
}
}
.list-page {
.el-input--small{
width: 214px !important;
line-height: 27px;
}
.el-input--small .el-input__inner {
height: 33px;
line-height: 32px;
border-radius: 0px;
height: 28px;
border-radius: 2px 0px 0px 2px;
}
}

View File

@@ -6,9 +6,9 @@
@import './components/common/pagination';
// @import './components/entities/entities';
@import './components/layout/layout';
@import 'components/rightBox/settings/chart-box';
@import 'components/rightBox/administration/chart-box';
@import 'components/setting/galaxy-proxy-debug';
@import 'components/table/settings/galaxy-proxy-table';
@import 'components/table/administration/galaxy-proxy-table';
@import './components/table/common';
@import './views/charts/chart';
@import 'views/entityExplorer/entity-explorer';
@@ -42,8 +42,8 @@
@import './views/chartHeader';
@import './views/charts/chartMap';
@import 'views/charts/chartRelationShipList';
@import './views/report/builtinReport';
@import './components/rightBox/report/builtinReportBox';
@import 'views/report/report';
@import 'components/rightBox/report/reportBox';
@import './views/charts2/panel';
@import './views/charts2/networkOverviewLine';
@@ -77,3 +77,4 @@
@import 'views/administration/AdministrationTabs';
@import 'views/setting/knowledgeBase';

View File

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

View File

@@ -1,21 +1,22 @@
.cn-builtin {
.cn-report {
background: #fff;
margin: 10px;
height: calc(100% - 20px) !important;
display: flex;
flex-direction: row;
.cn-builtin-left {
.cn-report-left {
width: 288px;
height: 100%;
border-right: 1px solid #E7EAED;
position: relative;
.cn-builtin-left-title {
overflow: auto;
.cn-report-left-title {
padding: 28px 0 26px 13px;
font-size: 16px;
color: #333333;
letter-spacing: 0;
}
.cn-builtin-left-menu {
.cn-report-left-menu {
width: 250px;
word-break: normal;
margin: auto;
@@ -25,13 +26,13 @@
padding: 14px;
cursor: pointer;
}
.cn-builtin-left-menu.cn-active {
.cn-report-left-menu.cn-active {
background: #F4FAFF;
border-radius: 2px;
color: #0091FF;
}
}
.cn-builtin-right {
.cn-report-right {
flex: 1;
.list-page .main-container {
padding: 0;
@@ -73,7 +74,7 @@
height: 100%;
width: calc(100% - 32px);
background: #fff; //盖住fixed产生的阴影
::v-deep .is-leaf {
:deep .is-leaf {
color: #1b2e3b;
background: #ebeef5;
}

View File

@@ -0,0 +1,365 @@
.knowledge-base {
&.list-page {
height: calc(100% - 52px);
}
.type-tag {
display: inline-block;
padding: 0 10px;
background-color: #EBF7FA;
color: #046ECA;
box-shadow: 0 2px 4px 0 rgba(51,51,51,0.02);
border-radius: 12px;
}
.top-title {
font-size: 24px;
color: #353636;
font-weight: 900;
line-height: 24px;
display: flex;
align-items: center;
padding-bottom: 8px;
padding-top: 20px;
margin-left:20px;
}
}
.edit-knowledge-base {
height: 100%;
.edit-knowledge-base__header {
padding: 30px 0 30px 20px;
font-size: 24px;
line-height: 24px;
font-weight: 900;
color: #353636;
}
.edit-knowledge-base__body {
display: flex;
height: calc(100% - 147px);
padding-left: 20px;
overflow: auto;
.el-steps {
margin-left: 10px;
.el-step {
transition: flex-basis ease-in-out .28s;
}
.el-step__head {
.el-step__line {
top: 26px;
bottom: 2px;
background-color: #38ACD2;
border-color: transparent;
opacity: 0.66;
}
&.is-finish, &.is-process {
.el-step__icon {
border-color: #38ACD2;
color: white;
background: #38ACD2;
}
}
&.is-process {
.el-step__line {
opacity: 1;
}
}
&.is-wait {
.el-step__icon {
border-color: #38ACD2;
color: #38ACD2;
}
}
.el-step__icon-inner {
font-size: 16px;
font-weight: normal;
}
}
}
.el-collapse {
width: 655px;
margin-left: 5px;
border: none;
.el-collapse-item.upload-collapse {
.el-collapse-item__wrap {
height: 260px;
}
}
.el-collapse-item {
min-height: 58px;
position: relative;
.el-collapse-item__header {
height: unset;
line-height: unset;
border: none;
font-size: 16px;
color: #333333;
&.focusing:focus:not(:hover) {
color: unset;
}
.form-sub-title {
padding-left: 35px;
}
}
[role|=tab] {
display: flex;
align-items: flex-start;
}
.el-collapse-item__arrow {
position: absolute;
color: #38ACD2;
font-weight: bold;
}
.el-collapse-item__wrap {
padding-left: 35px;
border: none;
}
.el-collapse-item__content {
position: relative;
padding-bottom: 20px;
}
.form-input .el-input__inner{
padding-right: 50px !important;
}
.upload-error-tip, .preview-error-tip {
color: $--color-danger;
}
.upload-error-tip {
margin-top: -11px;
}
.el-upload {
margin-top: 12px;
.upload-tip {
font-size: 12px;
color: #999999;
}
.el-upload-dragger {
width: 320px;
border-radius: 2px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
}
.el-upload--error .el-upload-dragger {
border-color: $--color-danger;
}
.el-upload-list {
.el-upload-list__item {
padding: 0 5px;
margin-top: unset;
height: 36px;
line-height: 36px;
background: #F5F8FA;
border-radius: 2px;
color: #353636;
.el-icon-close {
top: 11px;
}
.el-icon-close-tip {
top: 11px;
}
.el-progress.el-progress--line {
top: unset;
}
}
}
}
}
.el-form {
margin-top: 20px;
width: 620px;
label {
padding-bottom: 6px;
font-size: 14px;
color: #333333;
line-height: unset;
}
.el-form-item {
margin-bottom: 12px;
}
.el-form-item__content {
line-height: unset;
.el-input__inner {
padding-left: 8px;
font-size: 14px;
color: #353636;
}
.el-textarea__inner {
padding-left: 8px;
}
.form-select {
width: 100%;
.el-input__inner {
background-color: #F5F8FA;
}
}
}
}
.skeleton-border {
position: relative;
margin-top: 12px;
padding: 15px;
border: 1px solid #DCDFE6;
border-radius: 2px;
.skeleton-item-row:not(:nth-of-type(6)) {
margin-bottom: 5px;
}
.el-skeleton__item {
background: #F5F8FA;
}
.skeleton-tip {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 14px;
color: #353636;
}
}
.imported-tip {
margin-top: 8px;
margin-bottom: 4px;
font-size: 14px;
color: #333333;
.cn-icon {
font-size: 16px;
color: #38ACD2;
}
}
.imported-table-box {
position: relative;
height: 367px;
border: 1px solid #DEDEDE;
border-radius: 2px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
&.imported-table-box--error {
border-color: $--color-danger;
}
.imported-table {
padding: 0 12px;
width: 100%;
table-layout: fixed;
th {
text-align: left;
font-size: 14px;
color: #353636;;
}
td {
font-size: 14px;
color: #353636;
}
.imported-data-msg, .imported-data-item, .imported-data-value {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.imported-data-msg {
width: 140px;
}
.el-icon-close {
color: #666;
font-weight: bold;
cursor: pointer;
&:hover {
color: #111;
}
}
.el-icon-success {
color: #749F4D;
}
.el-icon-error {
color: #E26154;
}
}
.imported-pagination.pagination {
position: absolute;
width: 100%;
bottom: 0;
margin-top: 4px;
padding-top: 8px;
height: 42px;
border-top: 1px solid #eee;
.btn-prev, .btn-next, .number {
margin: 0 2px;
}
.el-pager li, .el-pagination .btn-next, .el-pagination .btn-prev {
border: none;
font-size: 12px;
}
.el-pagination .el-pager li {
color: #353636;
}
.el-pagination .el-pager li.active {
background-color: #38ACD2;
color: white;
}
}
}
}
.edit-knowledge-base__footer {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
margin-top: 3px;
box-shadow: 0 -1px 4px 0 rgba(0,0,0,0.10);
.footer__btn {
margin: 0 10px;
height: 30px;
min-width: 74px;
padding: 0 15px;
color: white;
background-color: #38ACD2;
border: none;
border-radius: 4px;
outline: none;
font-size: 14px;
cursor: pointer;
transition: background-color linear .2s, color linear .1s;
}
.footer__btn:hover:not(.footer__btn--disabled) {
background-color: lighten(#38ACD2, 10%);
}
.footer__btn--light {
background-color: #F5F6F7;
border: 1px solid $--border-color-primary;
color: #333;
}
.footer__btn.footer__btn--light:hover:not(.footer__btn--disabled) {
background-color: white;
border-color: lighten(#38ACD2, 40%);
color: #38ACD2;
}
.footer__btn--disabled {
opacity: .6;
cursor: default;
}
}
}

View File

@@ -31,12 +31,12 @@ $--button-primary-background-color: $--color-primary; // 普通按钮背景色
$--button-hover-tint-percent: 20%; // 非灰色按钮在鼠标hover时背景色变浅的幅度
$--button-active-shade-percent: 0; // 非灰色按钮在focus时背景色变深的幅度
$--button-gray-color: #666; // 灰色按钮字色
$--button-gray-color: #353636; // 灰色按钮字色
$--button-gray-hover-color: $--button-gray-color; // 灰色按钮hover字色
$--button-gray-active-color: $--color-primary; // 灰色按钮focus字色
$--button-gray-background-color: #F9F9F9; // 灰色按钮背景色
$--button-gray-hover-background-color: #FFF; // 灰色按钮hover背景色
$--button-gray-active-background-color: $--button-gray-hover-background-color; // 灰色按钮focus背景色
$--button-gray-hover-background-color: #EBF1F4; // 灰色按钮hover背景色
$--button-gray-active-background-color: #E0E7EA; // 灰色按钮focus背景色
$--button-gray-border-color: $--border-color-primary; // 灰色按钮边框色
$--button-gray-hover-border-color: $--button-gray-border-color; // 灰色按钮hover边框色
$--button-gray-active-border-color-tint-percent: 30%; // 灰色按钮在focus时边框色相对于主题色变浅的幅度

View File

@@ -2,10 +2,10 @@
/** 重写element-ui变量 **/
$--color-primary: #0091ff; // 主题色
$--color-primary-dark-10: darken(#0091ff, 10%); // 默认主题色 深10%
$--color-primary-light-10: lighten(#0091ff, 10%); // 默认主题色 浅10%
$--color-primary-light-20: lighten(#0091ff, 20%); // 默认主题色 浅20%
$--color-primary: #699DC9; // 主题色
$--color-primary-dark-10: darken(#699DC9, 10%); // 默认主题色 深10%
$--color-primary-light-10: lighten(#699DC9, 10%); // 默认主题色 浅10%
$--color-primary-light-20: lighten(#699DC9, 20%); // 默认主题色 浅20%
/* menu相关 */
$--menu-background-color: #00162B; // menu背景色
@@ -31,12 +31,12 @@ $--button-primary-background-color: $--color-primary; // 普通按钮背景色
$--button-hover-tint-percent: 20%; // 非灰色按钮在鼠标hover时背景色变浅的幅度
$--button-active-shade-percent: 0; // 非灰色按钮在focus时背景色变深的幅度
$--button-gray-color: #666; // 灰色按钮字色
$--button-gray-color: #353636; // 灰色按钮字色
$--button-gray-hover-color: $--button-gray-color; // 灰色按钮hover字色
$--button-gray-active-color: $--color-primary; // 灰色按钮focus字色
$--button-gray-background-color: #F9F9F9; // 灰色按钮背景色
$--button-gray-hover-background-color: #FFF; // 灰色按钮hover背景色
$--button-gray-active-background-color: $--button-gray-hover-background-color; // 灰色按钮focus背景色
$--button-gray-hover-background-color: #EBF1F4; // 灰色按钮hover背景色
$--button-gray-active-background-color: #E0E7EA; // 灰色按钮focus背景色
$--button-gray-border-color: $--border-color-primary; // 灰色按钮边框色
$--button-gray-hover-border-color: $--button-gray-border-color; // 灰色按钮hover边框色
$--button-gray-active-border-color-tint-percent: 30%; // 灰色按钮在focus时边框色相对于主题色变浅的幅度

View File

@@ -10,7 +10,7 @@
<template #label>
<div class="chart-tabs__label">
<i :class="tab.icon"></i>
<span>{{ $t(tab.i18n) }}</span>
<span>{{ $t(tab.i18n || tab.name) }}</span>
</div>
</template>
</el-tab-pane>

View File

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

View File

@@ -11,6 +11,7 @@
:page-size="Number(pageObj.pageSize)"
:layout="layout"
:total="pageObj.total"
v-bind="bind"
>
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
@@ -42,15 +43,23 @@ export default {
layout: {
type: String,
default: 'total, prev, pager, next, slot'
},
bind: {
type: Object,
default: () => {}
},
storePageNoOnUrl: {
type: Boolean,
default: true
}
},
/**
* 添加vue3的setup目的是添加/获取地址栏的参数
*/
setup () {
setup (props) {
const { query } = useRoute()
const pageSize = ref(defaultPageSize)
const currentPageNo = ref(query.pageNo || 1)
const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1))
return {
pageSize,

View File

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

View File

@@ -42,9 +42,9 @@
</div>
<div class="cn-header__nav">
<i class="cn-icon cn-icon-a-NetworkAnalytics"></i>
<el-breadcrumb class="header__left-breadcrumb" :separator="route.indexOf('detection') === -1 && route.indexOf('administration') === -1 ? '>' : ''">
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item}`" :title="item"
v-for="(item,index) in breadcrumb" :key="item">
<el-breadcrumb class="header__left-breadcrumb" separator=">">
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`" :title="item.value"
v-for="(item,index) in breadcrumb" :key="item.value">
<template v-if="index===3">
<div class="header__left-breadcrumb-item-select">
<el-popover placement="bottom-start"
@@ -54,7 +54,7 @@
:hide-after="0"
:show-after="0"
popper-class="breadcrumb__popper"
@show="showBreadcrumbPopover(item)"
@show="showBreadcrumbPopover(item.value)"
@hide="hideBreadcrumbPopover()"
trigger="click">
<template #reference>
@@ -62,13 +62,13 @@
:class="showBackground?'breadcrumb-button__active':''">
<span id="breadcrumbValue">
<template v-if="curTabProp === 'qtype'">
<span>{{ dnsQtypeMapData.get(item)}}</span>
<span>{{ dnsQtypeMapData.get(item.value)}}</span>
</template>
<template v-else-if="curTabProp === 'rcode'">
<span>{{ dnsRcodeMapData.get(item)}}</span>
<span>{{ dnsRcodeMapData.get(item.value)}}</span>
</template>
<template v-else>
<span>{{ item }}</span>
<span>{{ item.value }}</span>
</template>
</span><i class="cn-icon-xiala cn-icon"></i>
</div>
@@ -82,7 +82,7 @@
</div>
<ul class="select-dropdown" id="breadcrumbSelectDropdown" @scroll="scrollList()">
<li v-for="item in breadcrumbColumnValueListShow" title='' :key="item" :id="item"
class="select-dropdown__item" @click="changeValue(item)" :class="selected?'':''">
class="select-dropdown__item" @click="changeValue(item)">
<template v-if="curTabProp === 'qtype'">
<span>{{ dnsQtypeMapData.get(item) }}</span>
</template>
@@ -99,14 +99,17 @@
</div>
</template>
<template v-else-if="index===2">
<span v-if="route===wholeScreenRouterMapping.dns">{{ $t(item) }}</span>
<span v-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span>
<span v-if="route===wholeScreenRouterMapping.dns">{{ $t(item.value) }}</span>
<span v-else class="route-menu" @click="jump(route,item.value,'',3)">{{ $t(item.value) }}</span>
</template>
<!-- index=0和index=1的点击跳转由breadcrumb里的数据控制 -->
<template v-else-if="index===1">
<span class="route-menu" @click="jump(route,'','',2)">{{ item }}</span>
<span v-if="item.clickable" class="route-menu" @click="jump(route,'','',2)">{{ item.value }}</span>
<span v-else>{{ item.value }}</span>
</template>
<template v-else>
<span>{{ item }}</span>
<span v-if="item.clickable" class="route-menu" @click="jump(item.route,'','',0)">{{ item.value }}</span>
<span v-else>{{ item.value }}</span>
</template>
</el-breadcrumb-item>
</el-breadcrumb>
@@ -208,6 +211,7 @@ import {
handleSpecialValue
} from '@/utils/tools'
import { getNowTime, getSecond } from '@/utils/date-util'
import _ from 'lodash'
export default {
name: 'Header',
@@ -285,7 +289,7 @@ export default {
return this.$store.getters.menuList.find(menu => menu.code === 'networkAnalytics')
},
otherMenu () {
return this.$store.getters.menuList.filter(menu => menu.code !== 'networkAnalytics')
return this.$store.getters.menuList.filter(menu => ['networkAnalytics', 'chart', 'I18N'].indexOf(menu.code) === -1)
/* function excludeButton (menu) {
for (let i = 0; i < menu.length; i++) {
@@ -301,38 +305,28 @@ export default {
} */
},
breadcrumb () {
const breadcrumbMap = []
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
this.$store.getters.menuList.forEach(menu => {
if (this.$_.isEmpty(menu.children) && menu.route) {
breadcrumbMap.push({
name: this.$t(menu.i18n),
path: menu.route,
columnName: menu.columnName,
columnValue: menu.columnValue
})
} else if (!this.$_.isEmpty(menu.children)) {
menu.children.forEach(child => {
breadcrumbMap.push({
name: child.i18n ? this.$t(child.i18n) : child.name,
parentName: menu.i18n ? this.$t(menu.i18n) : menu.name,
path: child.route,
columnName: child.columnName,
columnValue: child.columnValue
})
})
const breadcrumb = []
this.generateBreadcrumb(breadcrumb, this.$store.getters.menuList)
// 写死一级和二级菜单是否可以点击跳转
if (breadcrumb[0]) {
if (['knowledgeBase'].indexOf(breadcrumb[0].code) > -1) {
breadcrumb[0].clickable = true
}
})
const breadcrumb = breadcrumbMap.find(b => this.route === b.path)
if (breadcrumb[1]) {
if (breadcrumb[1].route && breadcrumb[1].route.indexOf('/panel/') === 0) {
breadcrumb[1].clickable = true
}
}
}
const thirdMenu = this.getUrlParam(this.curTabState.thirdMenu, '')
const fourthMenu = this.getUrlParam(this.curTabState.fourthMenu, '')
let result = []
let result = [...breadcrumb]
if (fourthMenu) {
result = breadcrumb ? [breadcrumb.parentName, breadcrumb.name, thirdMenu, fourthMenu] : []
result = [...result, { value: thirdMenu }, { value: fourthMenu }]
} else if (thirdMenu) {
result = breadcrumb ? [breadcrumb.parentName, breadcrumb.name, thirdMenu] : []
} else {
result = breadcrumb ? [breadcrumb.parentName, breadcrumb.name] : []
result = [...result, { value: thirdMenu }]
}
return result
},
@@ -356,6 +350,7 @@ export default {
}
},
async breadcrumb (n) {
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
if (this.dnsQtypeMapData.size === 0) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
@@ -396,6 +391,32 @@ export default {
}
},
methods: {
generateBreadcrumb (breadcrumb, menus) {
const menu = menus.find(m => m.route === this.route)
if (menu) {
breadcrumb.unshift({
code: menu.code,
value: menu.i18n ? this.$t(menu.i18n) : menu.name,
route: menu.route,
type: menu.type
})
return true
} else {
for (let i = 0; i < menus.length; i++) {
if (!_.isEmpty(menus[i].children)) {
if (this.generateBreadcrumb(breadcrumb, menus[i].children)) {
breadcrumb.unshift({
code: menus[i].code,
value: menus[i].i18n ? this.$t(menus[i].i18n) : menus[i].name,
route: menus[i].route,
type: menus[i].type
})
return true
}
}
}
}
},
handleClose () {
this.showChangePin = false
},
@@ -602,9 +623,6 @@ export default {
this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true)
this.urlChangeParams[this.curTabState.tabOperationType] = opeType
if (opeType === 3) {
/* if (route !== '/panel/networkOverview') {
this.urlChangeParams.queryCondition = ''
} */
this.urlChangeParams.queryCondition = ''
}
} else {

View File

@@ -19,7 +19,6 @@
v-model="editObject.config.timeConfig.type"
class="right-box__select"
collapse-tags
:disabled="!!editObject.id"
placeholder=" "
popper-class="right-box-select-dropdown right-box-select-report "
size="small"
@@ -31,7 +30,6 @@
</el-select>
<template v-if="editObject.config.timeConfig.type === 'this'">
<el-select id="reportBoxTimeUnitSelect"
:disabled="!!editObject.id"
v-model="editObject.config.timeConfig.unit"
class="right-box__select"
collapse-tags
@@ -45,7 +43,7 @@
</el-select>
</template>
<div v-else-if="editObject.config.timeConfig.type === 'last' || editObject.config.timeConfig.type === 'previous'" style="display: flex;">
<el-input v-model.number="editObject.config.timeConfig.offset" size="small" class="el-input-single" placeholder=" " :disabled="!!editObject.id">
<el-input v-model.number="editObject.config.timeConfig.offset" size="small" class="el-input-single" placeholder=" ">
<template #prepend><i @click="timeOffsetHandle('m')" class="cn-icon cn-icon-a-"></i></template>
<template #append><i @click="timeOffsetHandle('p')" class="cn-icon cn-icon-a-1"></i></template>
</el-input>
@@ -53,7 +51,6 @@
v-model="editObject.config.timeConfig.unit"
class="right-box__select right-box__select-single"
collapse-tags
:disabled="!!editObject.id"
placeholder=" "
popper-class="right-box-select-dropdown el-select-last"
size="small"
@@ -73,7 +70,6 @@
v-model="editObject.config.startTime"
size="small"
:format="dateFormat"
:disabled="!!editObject.id"
:disabled-date="startDisabledDate"
@change="startTimeChang"
prefix-icon="cn-icon cn-icon-shijian"
@@ -92,7 +88,6 @@
v-model="editObject.config.endTime"
size="small"
:format="dateFormat"
:disabled="!!editObject.id"
:disabled-date="endDisabledDate"
@change="endTimeChange"
prefix-icon="cn-icon cn-icon-shijian"
@@ -103,41 +98,41 @@
</div>
</el-form-item >
<el-form-item class="el-height">
<el-checkbox v-model="scheduleChecked" :disabled="editObject.config.timeConfig.type === 'customize' || !!editObject.id" :label="$t('report.enableTimeSchedule')" size="large" />
<el-checkbox v-model="scheduleChecked" :disabled="editObject.config.timeConfig.type === 'customize'" :label="$t('report.enableTimeSchedule')" size="large" />
</el-form-item>
<!--Enable time schedule-->
<el-form-item prop="enableTimeSchedule" v-if="scheduleChecked">
<div class="enable-tab">
<div class="enable-tabs" @click="editObject.id ? null : (scheduleType = type.value)" v-for="type in scheduleTypeList" :key="type.value" :class="{'active': scheduleType === type.value, 'disable': editObject.id}">{{$t(type.name)}}</div>
<div class="enable-tabs" @click="scheduleTypeChange(type.value)" v-for="type in scheduleTypeList" :key="type.value" :class="{'active': scheduleType === type.value}">{{$t(type.name)}}</div>
</div>
<div class="enable-tabs-daily" v-if="scheduleType === scheduleTypeList[0].value">
<div class="enable-tabs-custom">{{$t('report.customEvery')}}</div>
<el-input v-model.number="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;" :disabled="!!editObject.id">
<el-input v-model.number="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;">
<template #append>{{$t('report.day')}}</template>
</el-input>
</div>
<div class="enable-tabs-weekly" v-else-if="scheduleType === scheduleTypeList[1].value" :disabled="!!editObject.id">
<div class="enable-tabs-weekly" v-else-if="scheduleType === scheduleTypeList[1].value">
<!-- 每隔几周暂时隐藏 -->
<!-- <div class="enable-tabs-custom">{{$t('report.customEvery')}}</div>
<el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder="Please input">
<template #append>{{$t('report.week')}}</template>
</el-input>-->
<el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" style="margin-top: 0.3125rem" :disabled="!!editObject.id">
<el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" style="margin-top: 0.3125rem">
<el-checkbox v-for="(item, index) in weekdayList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox>
</el-checkbox-group>
</div>
<!-- -->
<div class="enable-tabs-per-month" v-else-if="scheduleType === scheduleTypeList[2].value">
<div class="enable-month-tab">
<div class="enable-month-tabs" @click="editObject.id ? null : (monthScheduleType = 'daily')" :class="{'active': monthScheduleType === 'daily', 'disable': editObject.id}">{{$t('report.date')}}</div>
<div class="enable-month-tabs" @click="editObject.id ? null : (monthScheduleType = 'weekly')" :class="{'active': monthScheduleType === 'weekly', 'disable': editObject.id}">{{$t('report.week')}}</div>
<el-checkbox v-model="monthIsCycle" :label="$t('report.cycle')" size="large" :disabled="!!editObject.id"/>
<div class="enable-month-tabs" @click="monthScheduleType = 'daily'" :class="{'active': monthScheduleType === 'daily'}">{{$t('report.date')}}</div>
<div class="enable-month-tabs" @click="monthScheduleType = 'weekly'" :class="{'active': monthScheduleType === 'weekly'}">{{$t('report.week')}}</div>
<el-checkbox v-model="monthIsCycle" :label="$t('report.cycle')" size="large"/>
</div>
<div class="enable-month-data-tab">
<!-- 自定义月循环 -->
<template v-if="monthIsCycle">
<div class="enable-tabs-custom">{{$t('report.customEvery')}}</div>
<el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;" :disabled="!!editObject.id">
<el-input v-model="editObject.config.schedulerConfig.interval" size="small" placeholder=" " style="margin-top: 0.3125rem;">
<template #append>{{$t('report.month')}}</template>
</el-input>
</template>
@@ -145,8 +140,8 @@
<template v-else>
<div class="enable-month-moon-custom">{{$t('report.custom')}}</div>
<div class="enable-month-all">
<el-checkbox v-model="monthCheckedAll" class="enable-month-all-months" :indeterminate="monthIsIndeterminate" @change="monthCheckAllChange" :label="$t('report.allMonths')" :disabled="!!editObject.id"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.months" @change="monthCheckChange" :disabled="!!editObject.id">
<el-checkbox v-model="monthCheckedAll" class="enable-month-all-months" :indeterminate="monthIsIndeterminate" @change="monthCheckAllChange" :label="$t('report.allMonths')"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.months" @change="monthCheckChange">
<el-checkbox v-for="(item, index) in monthList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox>
</el-checkbox-group>
</div>
@@ -154,8 +149,8 @@
<!-- 按日期 -->
<template v-if="monthScheduleType === 'daily'">
<div class="enable-month-data-tabs">
<el-checkbox v-model="dateCheckedAll" :indeterminate="dateIsIndeterminate" @change="dateCheckAllChange" :label="$t('report.all')" size="large" :disabled="!!editObject.id"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.monthDates" @change="dateCheckChange" :disabled="!!editObject.id">
<el-checkbox v-model="dateCheckedAll" :indeterminate="dateIsIndeterminate" @change="dateCheckAllChange" :label="$t('report.all')" size="large"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.monthDates" @change="dateCheckChange">
<el-checkbox v-for="item in dateList" :key="item" :label="item"/>
</el-checkbox-group>
</div>
@@ -168,7 +163,6 @@
class="right-box__select"
multiple
placeholder=" "
:disabled="!!editObject.id"
popper-class="right-box-select-dropdown right-box-select-report"
size="small"
@change="()=>{ this.$forceUpdate() }">
@@ -177,8 +171,8 @@
</template>
</el-select>
<div class="enable-month-week">
<el-checkbox v-model="monthWeekdayCheckedAll" class="enable-month-week-all" :label="$t('report.all')" :indeterminate="monthWeekdayIsIndeterminate" @change="monthWeekdayCheckAllChange" size="large" :disabled="!!editObject.id"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" @change="monthWeekdayCheckChange" :disabled="!!editObject.id">
<el-checkbox v-model="monthWeekdayCheckedAll" class="enable-month-week-all" :label="$t('report.all')" :indeterminate="monthWeekdayIsIndeterminate" @change="monthWeekdayCheckAllChange" size="large"/>
<el-checkbox-group v-model="editObject.config.schedulerConfig.weekDates" @change="monthWeekdayCheckChange">
<el-checkbox v-for="(item, index) in weekdayList" :key="index" :label="item.value">{{$t(item.name)}}</el-checkbox>
</el-checkbox-group>
</div>
@@ -228,7 +222,7 @@
:disabled="!!editObject.id"
popper-class="right-box-select-dropdown right-box-select-report"
size="small"
@change="typeChange">
>
<template v-for="category in categoryList" :key="category.id">
<el-option :label="category.name" :value="category.id"></el-option>
</template>
@@ -239,26 +233,29 @@
<!-- <el-input v-model="param.value" placeholder=" " v-for="(param, index) in editObject.categoryParams" :key="index" size="small" style="vertical-align: unset;" :disabled="!!editObject.id">
<template #prepend>{{param.key}}</template>
</el-input>-->
<el-select v-model="param.value"
v-for="(param, index) in editObject.categoryParams"
:key="index"
class="right-box__select right-box__select--param"
placeholder=" "
filterable
:disabled="!!editObject.id"
popper-class="right-box-select-dropdown right-box-select-report "
size="small"
>
<template #prefix>
{{$t(param.i18n)}}
</template>
<template v-for="paramOption in paramsOptions">
<template v-if="paramOption.key === param.key">
<template v-for="(param, index) in editObject.categoryParams" :key="index">
<el-input v-if="param.labelType === 'input'" v-model="param.value" placeholder=" " size="small" style="vertical-align: unset;" :disabled="!!editObject.id">
<template #prepend>{{param.key}}</template>
</el-input>
<el-select v-model="param.value"
v-else
class="right-box__select right-box__select--param"
placeholder=" "
filterable
:disabled="!!editObject.id"
popper-class="right-box-select-dropdown right-box-select-report "
size="small"
>
<template #prefix>
{{$t(param.i18n)}}
</template>
<template v-for="paramOption in paramsOptions">
<template v-if="paramOption.key === param.key">
<el-option :key="p" :value="p" v-for="p in paramOption.options"></el-option>
</template>
</template>
</el-select>
</el-select>
</template>
</el-form-item>
</el-form>
</div>
@@ -413,20 +410,14 @@ export default {
watch: {
scheduleType (n, o) {
this.editObject.config.schedulerConfig.type = n
if (!this.editObject.id) {
this.cleanScheduleConfig()
}
this.cleanScheduleConfig()
},
scheduleChecked (n) {
this.editObject.config.isScheduler = n ? 1 : 0
if (!this.editObject.id) {
this.cleanScheduleConfig()
}
this.cleanScheduleConfig()
},
monthScheduleType (n) {
if (!this.editObject.id) {
this.cleanScheduleConfig()
}
this.cleanScheduleConfig()
},
monthIsCycle (n) {
if (!this.editObject.id) {
@@ -560,15 +551,13 @@ export default {
this.editObject.config.timeConfig.offset--
}
}
},
typeChange (id) {
},
cleanScheduleConfig () {
this.editObject.config.schedulerConfig.monthDates = []
this.editObject.config.schedulerConfig.weekDates = []
this.editObject.config.schedulerConfig.months = []
this.editObject.config.schedulerConfig.monthWeekDates = []
this.editObject.config.schedulerConfig.interval = 1
this.monthIsCycle = true
this.dateCheckedAll = false
this.dateIsIndeterminate = false
@@ -582,6 +571,9 @@ export default {
this.scheduleChecked = false
}
},
scheduleTypeChange (val) {
this.scheduleType = val
},
save () {
if (this.blockOperation.save) { return }
this.blockOperation.save = true

View File

@@ -2,31 +2,29 @@
<div :class="from" class="list-page">
<!-- 主页面 -->
<div class="main-list">
<!-- 顶部工具栏 -->
<div class="main-container">
<!-- 顶部工具栏 -->
<div class="top-tools">
<div class="top-tool-left" style="min-width: 300px">
<slot name="top-tool-left"></slot>
</div>
<div class="top-tool-right">
<!-- <el-input v-model="keyWord" value="keyWord"></el-input>
<el-button @click="onsearch" icon="el-icon-search" type="info" size="mini" style="margin-right: 10px"></el-button>-->
<div v-if="showLayout.indexOf('search') > -1" class="top-tool-search margin-r-20">
<div style="display: flex">
<el-input
v-model="keyWord" size="small" @keyup.enter="onsearch"></el-input>
<!-- <el-button icon="el-icon-search" @click="onsearch" size="small"></el-button>-->
<button class="top-tool-btn" style="border-radius: 0px"
type="button" @click="onsearch">
<el-input v-model="keyWord" size="small" @keyup.enter="onSearch"></el-input>
<!-- <el-button icon="el-icon-search" @click="onSearch" size="small"></el-button>-->
<button class="top-tool-btn top-tool-btn--search" style="border-radius: 0 2px 2px 0 !important;" @click="onSearch">
<i class="el-icon-search"></i>
</button>
</div>
</div>
</div>
<div class="top-tool-right">
<!-- <el-input v-model="keyWord" value="keyWord"></el-input>
<el-button @click="onSearch" icon="el-icon-search" type="info" size="mini" style="margin-right: 10px"></el-button>-->
<slot name="top-tool-right"></slot>
<button v-if="showLayout.indexOf('elementSet') > -1" class="top-tool-btn"
type="button" @click="tools.showCustomTableTitle = true">
<i class="cn-icon-gear cn-icon"></i>
</button>
<div v-if="showLayout.indexOf('elementSet') > -1" class="btn-customize" @click="tools.showCustomTableTitle = true">
<i class="cn-icon-gear cn-icon icon-gear"></i> <span> {{$t('network.customize')}}</span>
</div>
</div>
</div>
<div class="cn-table">
@@ -94,7 +92,7 @@ export default {
updateCustomTableTitle (custom) {
this.$emit('update:customTableTitle', custom)
},
onsearch () {
onSearch () {
const params = {
q: this.keyWord
}

View File

@@ -19,17 +19,17 @@
</div>
</div>
<div class="custom-bottom-btns">
<el-button size="mini" v-if="isCancel" :id="tableId+'-element-set-none'" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new is-cancel" type="button" @click="batchHandler(false)">
<el-button size="mini" v-if="isCancel" :id="tableId+'-element-set-none'" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new is-cancel" @click="batchHandler(false)">
<span class="top-tool-btn-txt">{{$t('overall.clear')}}</span>
</el-button>
<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" @click="batchHandler(true)">
<span class="top-tool-btn-txt">{{$t('overall.all')}}</span>
</el-button>
<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" @click="esc">
<span class="top-tool-btn-txt">{{$t('overall.cancel')}}</span>
</el-button>
<el-button size="mini" :id="tableId+'-element-set-save'" class="cn-btn cn-btn-size-small-new cn-btn-style-normal-new" type="button" @click="save" style="background-color: #0091ff;color:#DCDFE6">
<el-button size="mini" :id="tableId+'-element-set-save'" class="cn-btn cn-btn-size-small-new cn-btn-style-normal-new" @click="save" style="background-color: #0091ff;color:#DCDFE6">
<span class="top-tool-btn-txt top-tool-btn-save">{{$t('overall.save')}}</span>
</el-button>
</div>
@@ -86,6 +86,7 @@ export default {
// 单选
handler (val, index) {
if (!this.allowedAll && !val.allowed && (index === 0 || index === 1 || val.NotSet)) {
//
} else {
this.custom[index].show = !this.custom[index].show
}

View File

@@ -110,7 +110,6 @@
<script>
import table from '@/mixins/table'
import { panelTypeAndRouteMapping } from '@/utils/constants'
import { api } from '@/utils/api'
export default {
name: 'chartTable',

View File

@@ -92,7 +92,7 @@ export default {
show: true
},
{
label: this.$t('config.operationlog.type'),
label: this.$t('overall.type'),
prop: 'type',
show: true
},

View File

@@ -47,29 +47,6 @@
<span v-else>{{scope.row[item.prop]}}</span>
</template>
</el-table-column>
<el-table-column
:resizable="false"
:width="operationWidth"
fixed="right">
<template #header>
<div class="table-operation-title">{{$t('overall.option')}}</div>
</template>
<template #default="scope">
<div class="table-operation-items">
<button class="table-operation-item" @click="tableOperation(['edit', scope.row])"><i class="cn-icon cn-icon-edit"></i></button>
<el-dropdown size="medium" trigger="hover" @command="tableOperation">
<div class="table-operation-item table-operation-item--more">
<i class="cn-icon cn-icon-more-arrow-down"></i>
</div>
<template #dropdown>
<el-dropdown-menu >
<el-dropdown-item :command="['delete', scope.row]" :disabled="scope.row.id === 1"><i class="cn-icon cn-icon-delete"></i><span class="operation-dropdown-text">{{$t('overall.delete')}}</span></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
</template>

View File

@@ -62,29 +62,6 @@
<span v-else>{{scope.row[item.prop]}}</span>
</template>
</el-table-column>
<el-table-column
:resizable="false"
:width="operationWidth"
fixed="right">
<template #header>
<div class="table-operation-title">{{$t('overall.option')}}</div>
</template>
<template #default="scope">
<div class="table-operation-items">
<button class="table-operation-item" @click="tableOperation(['edit', scope.row])"><i class="cn-icon cn-icon-edit"></i></button>
<el-dropdown size="medium" trigger="hover" @command="tableOperation">
<div class="table-operation-item table-operation-item--more">
<i class="cn-icon cn-icon-more-arrow-down"></i>
</div>
<template #dropdown>
<el-dropdown-menu >
<el-dropdown-item :command="['delete', scope.row]" :disabled="scope.row.id === 1"><i class="cn-icon cn-icon-delete"></i><span class="operation-dropdown-text">{{$t('overall.delete')}}</span></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
</template>

View File

@@ -188,24 +188,8 @@
<span v-else>{{ scope.row[item.prop] || '-' }}</span>
</template>
</el-table-column>
<el-table-column
:resizable="false"
:width="operationWidth"
fixed="right">
<template #header>
<div class="table-operation-title">{{ $t('overall.option') }}</div>
</template>
<template #default="scope">
<div class="table-operation-items">
<button class="table-operation-item" @click="tableOperation(['edit', scope.row])"><i
class="cn-icon cn-icon-bianji"></i></button>
<button class="table-operation-item" @click="tableOperation(['delete', scope.row])"><i
class="cn-icon cn-icon-shanchu"></i></button>
</div>
</template>
</el-table-column>
</el-table>
<div class="table-operation-all">
<!-- <div class="table-operation-all">
<el-checkbox v-model="checkboxAll" :indeterminate="isIndeterminate" @change="selectAll(tableData)"></el-checkbox>
<div class="table-operation-all-span">
<span>{{ $t('overall.all') }}</span>
@@ -216,7 +200,7 @@
<span>{{ $t('report.batchDeletion') }}</span>
</div>
</div>
</div>
</div>-->
</div>
</template>
@@ -308,6 +292,7 @@ export default {
loadingTableId: '',
loadingPreviewId: '',
downDataList: [],
disableEdit:false,//
// pageObj: {
// pageNo: 1,
// pageSize: 20,
@@ -556,6 +541,12 @@ export default {
})
// this.selectIds = selectIds
}
this.batchDeleteObjs = objs
if(objs.length > 1) {
this.disableEdit = true
}else {
this.disableEdit = false
}
},
/**
* 全选按钮
@@ -574,6 +565,13 @@ export default {
this.$refs.dataTable.clearSelection()
}
this.batchDeleteObjs = objs
if(objs.length > 1) {
this.disableEdit = true
}else {
this.disableEdit = false
}
// this.selectIds = selectIds
},
/**

View File

@@ -0,0 +1,135 @@
<template>
<el-table
id="knowledgeBaseTable"
ref="dataTable"
:data="tableData"
:height="height"
border
@header-dragend="dragend"
@sort-change="tableDataSort"
@selection-change="selectionChange"
>
<el-table-column
:resizable="false"
align="center"
type="selection"
width="55">
</el-table-column>
<el-table-column
v-for="(item, index) in customTableTitles"
:key="`col-${index}`"
:fixed="item.fixed"
:label="item.label"
:min-width="`${item.minWidth}`"
:prop="item.prop"
:resizable="true"
:sort-orders="['ascending', 'descending']"
:sortable="item.sortable"
:width="`${item.width}`"
class="data-column"
>
<template #header>
<span class="data-column__span">{{item.label}}</span>
<div class="col-resize-area"></div>
</template>
<template #default="scope" :column="item">
<template v-if="item.prop === 'name'">
<template v-if="scope.row.i18n">
<span>{{$t(scope.row.i18n)}}</span>
</template>
<template v-else-if="scope.row.name">
<span>{{scope.row.name}}</span>
</template>
<template v-else>
<span>-</span>
</template>
</template>
<template v-else-if="item.prop === 'tagType'">
<span class="type-tag">{{tagTypeText(scope.row[item.prop])}}</span>
</template>
<template v-else-if="item.prop === 'utime' || item.prop === 'ctime'">
<template v-if="scope.row[item.prop]">
{{dateFormatByAppearance(scope.row[item.prop])}}
</template>
<template v-else>
<span>-</span>
</template>
</template>
<template v-else-if="item.prop === 'cuser' || item.prop === 'uuser'">
<template v-if="scope.row[item.prop]">
{{scope.row[item.prop].username || '-'}}
</template>
<template v-else>
<span>-</span>
</template>
</template>
<span v-else>{{scope.row[item.prop] || '-'}}</span>
</template>
</el-table-column>
</el-table>
</template>
<script>
import table from '@/mixins/table'
import { knowledgeBaseType } from '@/utils/constants'
export default {
name: 'knowledgeBaseTable',
mixins: [table],
data () {
return {
tableTitle: [ // 原table列
{
label: 'ID',
prop: 'id',
show: true,
width: 100,
sortable: 'custom'
}, {
label: this.$t('config.roles.name'),
prop: 'tagName',
show: true,
sortable: 'custom'
}, {
label: this.$t('overall.type'),
prop: 'tagType',
show: true
}, {
label: this.$t('overall.remark'),
prop: 'remark',
show: true
},
{
label: this.$t('overall.createdBy'),
prop: 'cuser',
show: true
},
{
label: this.$t('config.user.createTime'),
prop: 'ctime',
show: false,
sortable: 'custom'
},
{
label: this.$t('overall.updatedBy'),
prop: 'uuser',
show: false
},
{
label: this.$t('overall.updateTime'),
prop: 'utime',
show: false,
sortable: 'custom'
}
]
}
},
computed: {
tagTypeText () {
return function (type) {
const t = knowledgeBaseType.find(t => t.value === type)
return t ? t.name : ''
}
}
}
}
</script>

View File

@@ -20,7 +20,7 @@ export default {
pageObj: { // 分页对象
pageNo: 1,
pageSize: defaultPageSize,
total: ''
total: 0
},
/* 工具参数 */
tools: {
@@ -37,6 +37,7 @@ export default {
tableData: [],
scrollbarWrap: null,
delFlag: false,
disableEdit:false,//编辑按钮是否不可用,当选择多条记录的时候你,编辑按钮不可用
operationWidth: '165' // 操作列宽
}
},
@@ -74,6 +75,11 @@ export default {
},
selectionChange (objs) {
this.batchDeleteObjs = objs
if(this.batchDeleteObjs.length > 1) {
this.disableEdit = true
}else {
this.disableEdit = false
}
},
getTableData (params) {
if (params) {
@@ -95,6 +101,13 @@ export default {
this.tableData = response.data.list
this.pageObj.total = response.data.total
// TODO 回到顶部
} else {
console.error(response)
if (response.message) {
this.$message.error(response.message)
} else {
this.$message.error('Something went wrong...')
}
}
})
},
@@ -115,6 +128,39 @@ export default {
})
})
},
delBatch () {
let ids = []
if(this.batchDeleteObjs && this.batchDeleteObjs.length > 0){
this.batchDeleteObjs.forEach(item =>{
ids.push(item.id)
})
}
if(ids.length === 0){
this.$alert(this.$t('tip.pleaseSelect'),{
confirmButtonText: this.$t('tip.yes'),
type:'warning'
})
}else {
this.$confirm(this.$t('tip.confirmDelete'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
type: 'warning'
}).then(() => {
this.tools.loading = true
axios.delete(this.url + '?ids=' + ids).then(response => {
if (response.data.code === 200) {
this.delFlag = true
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
this.getTableData()
} else {
this.$message.error(response.data.message)
}
}).finally(() => {
this.tools.loading = false
})
})
}
},
newObject () {
return JSON.parse(JSON.stringify(this.blankObject))
},
@@ -153,6 +199,21 @@ export default {
}
})
},
editSelectRecord(){
if(this.batchDeleteObjs.length === 0){
this.$alert(this.$t('tip.pleaseSelectForEdit'),{
confirmButtonText: this.$t('tip.yes'),
type:'warning'
})
}else {
get(`${this.url}/${this.batchDeleteObjs[0].id}`).then(response => {
if (response.code === 200) {
this.object = response.data
this.rightBox.show = true
}
})
}
},
copy (u) {
this.object = { ...u, name: 'Copy from ' + u.name, id: '' }
this.rightBox.show = true
@@ -161,8 +222,14 @@ export default {
if (this.$refs.dataTable.loadingTableId === u.id) { // 列表单个下载
return
}
if (u.state !== 1 || u.upload !== 1) {
return
if (localStorage.getItem(storageKey.s3Enable) == 1) {
if (u.state !== 1 || u.upload !== 1) {
return
}
} else {
if (u.state !== 1) {
return
}
}
let fileName = ''
let url = ''
@@ -223,8 +290,14 @@ export default {
if (this.$refs.dataTable.loadingPreviewId === u.id) { // 列表单个下载
return
}
if (u.state !== 1 || u.upload !== 1) {
return
if (localStorage.getItem(storageKey.s3Enable) == 1) {
if (u.state !== 1 || u.upload !== 1) {
return
}
} else {
if (u.state !== 1) {
return
}
}
const params = {
id: u.id

View File

@@ -28,7 +28,7 @@ export default {
if (n) {
setTimeout(() => {
this.$refs.dataTable.doLayout()
}, 100)
}, 200)
}
}
},
@@ -55,21 +55,7 @@ export default {
})
},
tableDataSort (item) {
let orderBy = ''
if (item.order === 'ascending') {
if (item.prop === 'lastTime') {
orderBy = chartTableOrderOptionsMapping[item.prop]
} else {
orderBy = item.prop
}
}
if (item.order === 'descending') {
if (item.prop === 'lastTime') {
orderBy = '-' + chartTableOrderOptionsMapping[item.prop]
} else {
orderBy = '-' + item.prop
}
}
const orderBy = (item.order === 'descending' ? '-' : '') + (item.prop ? (chartTableOrderOptionsMapping[item.prop] || item.prop) : '')
this.$emit('orderBy', orderBy)
}
}

View File

@@ -1,6 +1,5 @@
import router from './router'
import store from './store'
import { ElMessage } from 'element-plus'
import { getPermission } from '@/utils/api'
import { loadGeoData } from '@/utils/tools'
import axios from 'axios'
@@ -18,6 +17,7 @@ router.beforeEach(async (to, from, next) => {
loadGeoData()
// 加载baseUrl
if (!axios.defaults.baseURL) {
// eslint-disable-next-line no-undef
axios.defaults.baseURL = BASE_CONFIG.baseUrl
}
if (localStorage.getItem(storageKey.token)) {
@@ -36,11 +36,12 @@ router.beforeEach(async (to, from, next) => {
store.commit('setRoleList', roleList)
}
if (to.path) {
if (hasMenu(store.getters.menuList, to.path)) {
next()
/* if (hasMenu(store.getters.menuList, to.path)) {
next()
} else {
ElMessage.error('No access') // TODO 国际化
}
} */
}
}
} else {
@@ -80,7 +81,7 @@ export function hasMenu (menuList, route) {
export function hasParam (url, param) {
let hasParam = false
let tempArr = url.split('?')
const query = {}
// const query = {}
if (tempArr[1]) {
tempArr = tempArr[1].split('&')
tempArr.forEach(t => {

View File

@@ -21,12 +21,16 @@ const routes = [
},
{
path: '/report/builtIn',
component: () => import('@/views/report/reportTest')
component: () => import('@/views/report/Report')
},
{
path: '/entityExplorer',
component: () => import('@/views/entityExplorer/EntityExplorer')
},
{
path: '/detection',
redirect: '/detection/securityEvent'
},
{
path: '/detection/:typeName',
component: () => import('@/views/detections/Index')
@@ -35,6 +39,14 @@ const routes = [
path: '/businessLog/viewer',
component: () => import('@/views/businessLog/Viewer')
},
{
path: '/knowledgeBase',
component: () => import('@/views/setting/KnowledgeBase')
},
{
path: '/knowledgeBase/form',
component: () => import('@/views/setting/KnowledgeBaseForm')
},
{
name: 'Administration',
path: '/administration',
@@ -51,27 +63,22 @@ const routes = [
path: '/administration/role',
component: () => import('@/views/administration/Roles')
},
{
name: 'I18n',
path: '/administration/i18n',
component: () => import('@/views/administration/I18n')
},
{
name: 'OperationLog',
path: '/administration/operationLog',
component: () => import('@/views/administration/OperationLog')
},
{
name: 'GalaxyProxy',
path: '/administration/galaxyProxy',
component: () => import('@/views/administration/GalaxyProxy')
},
{
name: 'Chart',
path: '/administration/chart',
component: () => import('@/views/administration/Chart')
}
]
},
{
name: 'I18n',
path: '/i18n',
component: () => import('@/views/administration/I18n')
},
{
name: 'Chart',
path: '/chart',
component: () => import('@/views/administration/Chart')
}
]
}

View File

@@ -12,7 +12,8 @@ const store = createStore({
return {
i18n: false,
showEntityTypeSelector: false, // 在entity explore页面时控制header显示实体类型选择框
from: '' // entity type
from: '', // entity type
test: 'jest' // 用于单测的demo
}
},
getters: {
@@ -24,6 +25,9 @@ const store = createStore({
},
entityName (state) {
return state.entityName
},
getTest (state) {
return state.test
}
},
mutations: {
@@ -37,6 +41,9 @@ const store = createStore({
},
showEntityTypeSelector (state, show) {
state.showEntityTypeSelector = show
},
setTest (state, test) {
state.test = test
}
}
})

View File

@@ -33,6 +33,8 @@ export const api = {
chartList: '/visual/chart/list',
// galaxyProxy
galaxyProxy: '/galaxy/setting',
// 知识库
knowledgeBase: '/knowledge',
// 报告相关
reportJob: '/report/job',
@@ -318,11 +320,12 @@ export async function getI18n () {
/* 获得原始的3611-2 json字符串数据 */
export async function getIso36112JsonData (suffix) {
const url = `${window.location.protocol}//${window.location.host}/geojson/${suffix}.json`
const request = new Promise(resolve => {
axios({
url: `${window.location.protocol}//${window.location.host}:${window.location.port}/geojson/${suffix}.json`
}).then(response => {
axios({ url }).then(response => {
resolve(response.data || response || null)
}).catch(err => {
console.error(err)
})
})
return await request

View File

@@ -122,7 +122,8 @@ export const chartTableColumnMapping = {
ip: 'IP'
}
export const chartTableOrderOptionsMapping = {
lastTime: 'last_time'
lastTime: 'last_time',
tagName: 'tag_name'
}
export const chartPieTableTopOptions = [
{ name: 'Sessions', value: 'sessions' },
@@ -277,6 +278,21 @@ export const cycle = {
pre: 1
}
export const knowledgeBaseType = [
{
name: 'IP',
value: 'ip'
},
{
name: 'Domain',
value: 'domain'
},
{
name: 'APP',
value: 'app'
}
]
export const curTabState = {
curTab: 'curTab',
tableMetric: 'tableMetric',

View File

@@ -532,14 +532,14 @@ export function getUserDrilldownTableGeo (userId) {
return data[userId]
}
function JSONParse (data) {
const firstParse = JSON.parse(data)
if (typeof firstParse === 'string') {
return JSON.parse(firstParse)
} else {
return firstParse
}
}
// function JSONParse (data) {
// const firstParse = JSON.parse(data)
// if (typeof firstParse === 'string') {
// return JSON.parse(firstParse)
// } else {
// return firstParse
// }
// }
export function copyValue (item) {
const str = item
@@ -878,9 +878,9 @@ export async function getDnsMapData (type) {
}
export function handleSpecialValue (value) {
if (value) {
value = value.replaceAll("'", "\\\\'")
.replaceAll('"', '\\"')
.replaceAll('&', '%26')
value = value.replace(/\'/g, "\\\\'")
.replace(/\"/g, '\\"')
.replace(/\&/g, '%26')
}
return value
}
@@ -894,13 +894,13 @@ export function combineTabList (tableType, list, commonTabList) {
const commonTab = commonTabList.find(item => item.name === tabName)
tab.label = commonTab ? commonTab.i18n : ''
tab.prop = commonTab ? commonTab.prop : ''
if (!tab.hasOwnProperty('checked')) {
if (!Object.prototype.hasOwnProperty.call(tab, 'checked')) {
tab.checked = tab ? tab.show : true
}
if (!tab.hasOwnProperty('disabled')) {
if (!Object.prototype.hasOwnProperty.call(tab, 'disabled')) {
tab.disabled = tab ? !tab.enable : false
}
if (!tab.hasOwnProperty('panelId')) {
if (!Object.prototype.hasOwnProperty.call(tab, 'panelId')) {
tab.panelId = tab ? tab.panelIdOfFourthMenu : null
}
// 代码里写死的
@@ -1146,3 +1146,20 @@ export function getLineIndexUnit (type, show) {
}
}
}
export function getLineIndexUnit2 (type) {
if (type.indexOf('total') > -1) {
return 0
} else if (type.indexOf('inbound') > -1) {
return 1
} else if (type.indexOf('outbound') > -1) {
return 2
} else if (type.indexOf('internal') > -1) {
return 3
} else if (type.indexOf('through') > -1) {
return 4
} else if (type.indexOf('other') > -1) {
return 5
} else {
return 0
}
}

View File

@@ -53,7 +53,7 @@
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import chartTable from '@/components/table/settings/ChartTable'
import chartTable from '@/components/table/administration/ChartTable'
import chartBox from '@/components/rightBox/settings/ChartBox'
import { api } from '@/utils/api'

View File

@@ -76,7 +76,7 @@
<script>
import cnDataList from '@/components/table/CnDataList'
import galaxyProxyBox from '@/components/rightBox/settings/GalaxyProxyBox'
import galaxyProxyTable from '@/components/table/settings/GalaxyProxyTable'
import galaxyProxyTable from '@/components/table/administration/GalaxyProxyTable'
import dataListMixin from '@/mixins/data-list'
import { api } from '@/utils/api'
import { get, put } from '@/utils/http'

View File

@@ -55,7 +55,7 @@
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import i18nTable from '@/components/table/settings/I18nTable'
import i18nTable from '@/components/table/administration/I18nTable'
import i18nBox from '@/components/rightBox/settings/I18nBox'
import { api } from '@/utils/api'

View File

@@ -16,45 +16,21 @@
<script>
import ChartTabs from '@/components/common/ChartTabs'
import { useStore } from 'vuex'
export default {
name: 'index',
components: {
ChartTabs
},
data () {
setup () {
const store = useStore()
const menu = store.getters.menuList.find(m => m.code === 'administration')
const tabsData = menu.children.map(l => ({
...l,
path: l.route
})).sort((a, b) => a.sort - b.sort)
return {
tabsData: [
{
i18n: 'overall.user',
path: '/administration/user',
icon: 'cn-icon cn-icon-user2'
},
{
i18n: 'overall.role',
path: '/administration/role',
icon: 'cn-icon cn-icon-role2'
},
{
i18n: 'overall.operationLog',
path: '/administration/operationLog',
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'
}
]
tabsData
}
}
}

View File

@@ -34,7 +34,7 @@
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import operationLogTable from '@/components/table/settings/OperationLogTable'
import operationLogTable from '@/components/table/administration/OperationLogTable'
import { api } from '@/utils/api'
export default {

View File

@@ -9,11 +9,23 @@
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template v-slot:top-tool-right>
<button id="roles-add" :title="$t('overall.createRole')" class="top-tool-btn margin-r-10"
type="button" @click="add">
<i class="cn-icon-add cn-icon"></i>
<template v-slot:top-tool-left>
<button id="roles-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
@click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{$t('overall.create')}}</span>
</button>
<button id="roles-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
@click="edit">
<i class="cn-icon-edit cn-icon"></i>
<span>{{$t('overall.edit')}}</span>
</button>
<button id="roles-delete" class="top-tool-btn margin-r-10"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{$t('overall.delete')}}</span>
</button>
<delete-button id="role-list-batch-delete" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
</template>
<template v-slot:default>
@@ -48,7 +60,7 @@
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import rolesTable from '@/components/table/settings/RoleTable'
import rolesTable from '@/components/table/administration/RoleTable'
import roleBox from '@/components/rightBox/settings/RoleBox'
import { api } from '@/utils/api'
import { get } from '@/utils/http'
@@ -78,13 +90,20 @@ export default {
}
},
methods: {
edit (u) {
get(`${this.url}`, { ids: u.id }).then(response => {
if (response.code === 200) {
this.object = response.data.list[0]
this.rightBox.show = true
}
})
edit () {
if(this.batchDeleteObjs.length === 0){
this.$alert(this.$t('tip.pleaseSelectForEdit'),{
confirmButtonText: this.$t('tip.yes'),
type:'warning'
})
}else {
get(`${this.url}`, { ids: this.batchDeleteObjs[0].id }).then(response => {
if (response.code === 200) {
this.object = response.data.list[0]
this.rightBox.show = true
}
})
}
}
}
}

View File

@@ -9,14 +9,21 @@
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template #top-tool-right>
<button
id="account-add"
class="top-tool-btn margin-r-10"
type="button"
@click="add"
>
<i class="cn-icon-add cn-icon"/>
<template #top-tool-left>
<button id="account-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
@click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{$t('overall.create')}}</span>
</button>
<button id="account-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
@click="editSelectRecord">
<i class="cn-icon-edit cn-icon"></i>
<span>{{$t('overall.edit')}}</span>
</button>
<button id="account-delete" class="top-tool-btn margin-r-10"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{$t('overall.delete')}}</span>
</button>
</template>
<template #default>
@@ -53,7 +60,7 @@
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import userTable from '@/components/table/settings/UserTable'
import userTable from '@/components/table/administration/UserTable'
import userBox from '@/components/rightBox/settings/UserBox'
import { api } from '@/utils/api'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,17 +5,17 @@
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<div class="line-value-mpackets"
<div class="line-value-tabs"
v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in mpackets"
v-for="(item, index) in tabs"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)">
<div class="line-value-mpackets-name">
<div class="line-value-tabs-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
<div class="tabs-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
@@ -126,7 +126,7 @@ export default {
label: 'Maximum'
}
],
mpackets: [
tabs: [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -202,7 +202,7 @@ export default {
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
this.tabs = [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -323,7 +323,7 @@ export default {
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
this.tabs.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
@@ -366,16 +366,16 @@ export default {
legendSelectChange (item, index, val) {
if (index === 'index') {
this.dispatchLegendSelectAction(item.name)
} else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
} else if (this.tabs[index] && this.tabs[index].name === item.name) {
this.dispatchLegendSelectAction(item.name)
this.mpackets.forEach((t) => {
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
}
if (val === 'active') {
this.mpackets.forEach(t => {
this.tabs.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
@@ -387,7 +387,7 @@ export default {
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
@@ -395,8 +395,8 @@ export default {
}
},
handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
if (document.querySelector('.network .line-value-tabs.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active')
const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
@@ -409,7 +409,7 @@ export default {
this.lineTab = ''
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
@@ -421,9 +421,9 @@ export default {
let echartsData
const chartOption = this.myChart.getOption()
if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
echartsData = this.tabs.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = this.mpackets.filter(t => t.show === true)
echartsData = this.tabs.filter(t => t.show === true)
}
if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
@@ -488,13 +488,13 @@ export default {
})
}
lineData.splice(0, 1)
const mpackets = _.cloneDeep(this.mpackets)
const tabs = _.cloneDeep(this.tabs)
if (val === 'Queries/s') {
lineData.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
mpackets.forEach((e, i) => {
tabs.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
@@ -503,11 +503,11 @@ export default {
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.mpackets = mpackets
this.echartsInit(this.mpackets, true)
this.tabs = tabs
this.echartsInit(this.tabs, true)
} else {
const unit = 'bps'
this.legendInit(lineData, active, show, unit, mpackets)
this.legendInit(lineData, active, show, unit, tabs)
}
},
legendInit (data, active, show, type, dnsData) {
@@ -535,14 +535,14 @@ export default {
}
}
})
this.mpackets = dnsData
this.tabs = dnsData
if (num === 3) {
dnsData[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(dnsData[0], 0)
this.echartsInit(this.mpackets)
this.echartsInit(this.tabs)
} else {
this.echartsInit(this.mpackets, show)
this.echartsInit(this.tabs, show)
if (!this.lineRefer) this.lineRefer = 'Average'
}
}
@@ -550,7 +550,7 @@ export default {
mounted () {
this.timer = setTimeout(() => {
if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab)
const data = this.tabs.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
} else {
this.init()

View File

@@ -1,13 +1,13 @@
<template>
<div class="link-blocks">
<div class="block-list" style="position: relative">
<div class="block-list__title" v-if="!showError">{{ $t('linkMonitor.links') }}</div>
<div class="block-list__title" v-if="!showError1">{{ $t('linkMonitor.links') }}</div>
<!--无数据noData-->
<chart-no-data v-if="isNoData"></chart-no-data>
<chart-no-data v-if="linkNoData" test-id="linkBlockNoData"></chart-no-data>
<div class="block-list__list" v-show="!isNoData">
<chart-error v-if="showError" :content="errorMsg1" />
<div class="block-list__list" v-show="!linkNoData">
<chart-error v-if="showError1" :content="errorMsg1" />
<el-popover
v-else
placement="bottom"
@@ -29,20 +29,20 @@
<div class="popper-content__link-id">Link ID: {{ item.linkId }}</div>
<div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="info__value" style="margin-left: 8px">
<div class="info__value" :test-id="`linkBlockTotal${index}`" style="margin-left: 8px">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }}
</div>
</div>
<div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.bandwidthUsage') }}</div>
<div class="info__value" style="display: flex">
<div>
<div :test-id="`linkBlockEgressUsage${index}`">
<svg class="icon item-popover-up" aria-hidden="true">
<use xlink:href="#cn-icon-egress"></use>
</svg>
{{ convertValue(item.egressUsage) }}
</div>
<div>
<div :test-id="`linkBlockIngressUsage${index}`">
<svg class="icon item-popover-down" aria-hidden="true">
<use xlink:href="#cn-icon-ingress"></use>
</svg>
@@ -57,12 +57,12 @@
</div>
<div class="block-list" >
<div class="block-list__title" v-if="!showError">{{ $t('linkMonitor.nextHopInternet') }}</div>
<div class="block-list__title" v-if="!showError2">{{ $t('linkMonitor.nextHopInternet') }}</div>
<chart-no-data v-if="isNoData"></chart-no-data>
<chart-no-data v-if="nextHopNoData" test-id="nextHpNoData"></chart-no-data>
<div class="block-list__list" v-show="!isNoData">
<chart-error v-if="showError" :content="errorMsg2" />
<div class="block-list__list" v-show="!nextHopNoData">
<chart-error v-if="showError2" :content="errorMsg2" />
<el-popover
v-else
placement="bottom"
@@ -84,20 +84,20 @@
<div class="popper-content__link-id">Next-Hop Internet: {{ item.linkDirection }}</div>
<div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="info__value" style="margin-left: 8px">
<div class="info__value" :test-id="`nextHopTotal${index}`" style="margin-left: 8px">
{{ unitConvert(item.totalBitsRate, unitTypes.bps).join(' ') }}
</div>
</div>
<div class="popper-content__link-info">
<div class="info__label">{{ $t('linkMonitor.linkBlock.bandwidthUsage') }}</div>
<div class="info__value" style="display: flex">
<div>
<div :test-id="`nextHopEgressUsage${index}`">
<svg class="icon item-popover-up" aria-hidden="true">
<use xlink:href="#cn-icon-egress"></use>
</svg>
{{ convertValue(item.egressUsage) }}
</div>
<div>
<div :test-id="`nextHopIngressUsage${index}`">
<svg class="icon item-popover-down" aria-hidden="true">
<use xlink:href="#cn-icon-ingress"></use>
</svg>
@@ -118,13 +118,13 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import { colorGradientCalculation } from '@/utils/tools'
import unitConvert from '@/utils/unit-convert'
import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/constants'
import { getSecond } from '@/utils/date-util'
import ChartError from '@/components/common/Error'
import axios from 'axios'
export default {
name: 'LinkBlock',
@@ -135,12 +135,14 @@ export default {
},
data () {
return {
isNoData: false,
linkNoData: false,
nextHopNoData: false,
unitTypes,
linkData: [],
nextHopData: [],
gradientColor: ['#FF005C', '#40537E'], // [start, end]
showError: false,
showError1: false,
showError2: false,
errorMsg1: '',
errorMsg2: ''
}
@@ -175,126 +177,140 @@ export default {
endTime: getSecond(this.timeFilter.endTime)
}
const dataRequest = get(api.linkMonitor.analysis, params)
const nextHopRequest = get(api.linkMonitor.nextHopAnalysis, params)
const dataRequest = axios.get(api.linkMonitor.analysis, { params: params })
const nextHopRequest = axios.get(api.linkMonitor.nextHopAnalysis, { params: params })
Promise.all([dataRequest, nextHopRequest]).then(res => {
if (res[0].code === 200 && res[1].code === 200) {
this.showError = false
Promise.all([dataRequest, nextHopRequest]).then(response => {
const res = []
res[0] = response[0].data
res[1] = response[1].data
if (res[0].code === 200) {
this.showError1 = false
const linkData = res[0].data.result
const nextHopData = res[1].data.result
this.isNoData = linkData.length === 0 && nextHopData.length === 0
if (this.isNoData) {
return
this.linkNoData = linkData.length === 0
if (!this.linkNoData) {
const data = []
linkData.forEach(d => {
const info = linkInfo.find(i => i.originalLinkId === d.linkId)
if (info) {
const hit = data.find(d => d.linkId === info.linkId)
if (hit) {
hit.egressBitsRate += d.egressBitsRate
hit.ingressBitsRate += d.ingressBitsRate
if (info.direction === 'egress') {
hit.egressBandwidth = info.bandwidth
hit.egressLinkId = d.linkId
} else if (info.direction === 'ingress') {
hit.ingressBandwidth = info.bandwidth
hit.ingressLinkId = d.linkId
}
} else {
const hit = {
linkId: info.linkId,
egressBitsRate: d.egressBitsRate,
ingressBitsRate: d.ingressBitsRate
}
if (info.direction === 'egress') {
hit.egressBandwidth = info.bandwidth
hit.egressLinkId = d.linkId
} else if (info.direction === 'ingress') {
hit.ingressBandwidth = info.bandwidth
hit.ingressLinkId = d.linkId
}
data.push(hit)
}
}
})
data.forEach((item) => {
item.totalBitsRate = item.egressBitsRate + item.ingressBitsRate
})
const sorted = data.sort((a, b) => b.totalBitsRate - a.totalBitsRate)
const linkColors = colorGradientCalculation(this.gradientColor[0], this.gradientColor[1], sorted.map(s => s.totalBitsRate))
sorted.forEach((s, i) => {
s.color = linkColors[i]
s.egressUsage = this.computeUsage(s.egressBitsRate, s.egressBandwidth)
s.ingressUsage = this.computeUsage(s.ingressBitsRate, s.ingressBandwidth)
s.popoverWidth = this.computePopoverWidth(s.egressUsage, s.ingressUsage)
})
this.linkData = sorted
}
const data = []
linkData.forEach(d => {
const info = linkInfo.find(i => i.originalLinkId === d.linkId)
if (info) {
const hit = data.find(d => d.linkId === info.linkId)
if (hit) {
hit.egressBitsRate += d.egressBitsRate
hit.ingressBitsRate += d.ingressBitsRate
if (info.direction === 'egress') {
hit.egressBandwidth = info.bandwidth
hit.egressLinkId = d.linkId
} else if (info.direction === 'ingress') {
hit.ingressBandwidth = info.bandwidth
hit.ingressLinkId = d.linkId
}
} else {
const hit = {
linkId: info.linkId,
egressBitsRate: d.egressBitsRate,
ingressBitsRate: d.ingressBitsRate
}
if (info.direction === 'egress') {
hit.egressBandwidth = info.bandwidth
hit.egressLinkId = d.linkId
} else if (info.direction === 'ingress') {
hit.ingressBandwidth = info.bandwidth
hit.ingressLinkId = d.linkId
}
data.push(hit)
}
}
})
data.forEach((item) => {
item.totalBitsRate = item.egressBitsRate + item.ingressBitsRate
})
const sorted = data.sort((a, b) => b.totalBitsRate - a.totalBitsRate)
const linkColors = colorGradientCalculation(this.gradientColor[0], this.gradientColor[1], sorted.map(s => s.totalBitsRate))
sorted.forEach((s, i) => {
s.color = linkColors[i]
s.egressUsage = this.computeUsage(s.egressBitsRate, s.egressBandwidth)
s.ingressUsage = this.computeUsage(s.ingressBitsRate, s.ingressBandwidth)
s.popoverWidth = this.computePopoverWidth(s.egressUsage, s.ingressUsage)
})
this.linkData = sorted
let directionArr = []
nextHopData.forEach((item) => {
if (item.egressLinkDirection !== '' && item.ingressLinkDirection !== '') {
directionArr.push(item.egressLinkDirection)
directionArr.push(item.ingressLinkDirection)
}
})
directionArr = Array.from(new Set(directionArr))
const newNextHopData = []
directionArr.forEach((item1) => {
const newObj = { egressBitsRate: 0, ingressBitsRate: 0, totalBitsRate: 0, linkDirection: item1 }
nextHopData.forEach((item2) => {
if (item1 === item2.egressLinkDirection) {
newObj.egressBitsRate += item2.egressBitsRate
newObj.totalBitsRate += item2.egressBitsRate
}
if (item1 === item2.ingressLinkDirection) {
newObj.ingressBitsRate += item2.ingressBitsRate
newObj.totalBitsRate += item2.ingressBitsRate
}
})
newNextHopData.push(newObj)
})
// 下一跳数据处理
const nextHopSorted = newNextHopData.sort((a, b) => b.totalBitsRate - a.totalBitsRate)
const nextHopColors = colorGradientCalculation(this.gradientColor[0], this.gradientColor[1], nextHopSorted.map(s => s.totalBitsRate))
nextHopSorted.forEach((s, i) => {
s.color = nextHopColors[i]
let sum = 0
linkInfo.forEach((item) => {
// todo 此处需注意不明确接口返回的方向字段名是拼音还是汉字后期可能会变动缓存中的nextHop
if (s.linkDirection === item.nextHop) {
sum += item.bandwidth
}
})
// 上行使用情况计算
const egressUsage = this.computeUsage(s.egressBitsRate, sum)
// 下行使用情况计算
const ingressUsage = this.computeUsage(s.ingressBitsRate, sum)
s.egressUsage = egressUsage
s.ingressUsage = ingressUsage
s.popoverWidth = this.computePopoverWidth(egressUsage, ingressUsage)
})
this.nextHopData = nextHopSorted
} else {
this.isNoData = false
this.showError = true
this.linkNoData = false
this.showError1 = true
this.errorMsg1 = res[0].message
}
if (res[1].code === 200) {
this.showError2 = false
const nextHopData = res[1].data.result
this.nextHopNoData = nextHopData.length === 0
if (!this.nextHopNoData) {
let directionArr = []
nextHopData.forEach((item) => {
if (item.egressLinkDirection !== '' && item.ingressLinkDirection !== '') {
directionArr.push(item.egressLinkDirection)
directionArr.push(item.ingressLinkDirection)
}
})
directionArr = Array.from(new Set(directionArr))
const newNextHopData = []
directionArr.forEach((item1) => {
const newObj = { egressBitsRate: 0, ingressBitsRate: 0, totalBitsRate: 0, linkDirection: item1 }
nextHopData.forEach((item2) => {
if (item1 === item2.egressLinkDirection) {
newObj.egressBitsRate += item2.egressBitsRate
newObj.totalBitsRate += item2.egressBitsRate
}
if (item1 === item2.ingressLinkDirection) {
newObj.ingressBitsRate += item2.ingressBitsRate
newObj.totalBitsRate += item2.ingressBitsRate
}
})
newNextHopData.push(newObj)
})
// 下一跳数据处理
const nextHopSorted = newNextHopData.sort((a, b) => b.totalBitsRate - a.totalBitsRate)
const nextHopColors = colorGradientCalculation(this.gradientColor[0], this.gradientColor[1], nextHopSorted.map(s => s.totalBitsRate))
nextHopSorted.forEach((s, i) => {
s.color = nextHopColors[i]
let sum = 0
linkInfo.forEach((item) => {
// todo 此处需注意不明确接口返回的方向字段名是拼音还是汉字后期可能会变动缓存中的nextHop
if (s.linkDirection === item.nextHop) {
sum += item.bandwidth
}
})
// 上行使用情况计算
const egressUsage = this.computeUsage(s.egressBitsRate, sum)
// 下行使用情况计算
const ingressUsage = this.computeUsage(s.ingressBitsRate, sum)
s.egressUsage = egressUsage
s.ingressUsage = ingressUsage
s.popoverWidth = this.computePopoverWidth(egressUsage, ingressUsage)
})
this.nextHopData = nextHopSorted
}
} else {
this.showError2 = true
this.nextHopNoData = false
this.errorMsg2 = res[1].message
}
}).catch(e => {
console.error(e)
this.isNoData = false
this.showError = true
this.linkNoData = false
this.nextHopNoData = false
this.showError1 = true
this.showError2 = true
// todo 此处数据还待验证
this.errorMsg1 = e.message
this.errorMsg2 = e.message

View File

@@ -1,10 +1,10 @@
<template>
<div class="link-direction-grid">
<!--左侧链路出入口-->
<popover-content :isNoData="isLinkNoData" :gridData="linkGridData" :showError="isLinkShowError" :content="linkErrorMsg" style="width: 900px;"/>
<popover-content :title="$t('linkMonitor.egressLink') + ' & ' + $t('linkMonitor.ingressLink')" :isNoData="isLinkNoData" :gridData="linkGridData" :showError="isLinkShowError" :content="linkErrorMsg" style="width: 900px;"/>
<!--右侧链路下一跳-->
<popover-content :isNoData="isNextNoData" :gridData="nextGridData" :showError="isNextShowError" :content="nextErrorMsg" />
<popover-content :title="$t('linkMonitor.nextHopInternetOfGrid')" :isNoData="isNextNoData" :gridData="nextGridData" :showError="isNextShowError" :content="nextErrorMsg" />
</div>
</template>
@@ -12,10 +12,10 @@
import chartMixin from '@/views/charts2/chart-mixin'
import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api'
import { get } from '@/utils/http'
import { storageKey } from '@/utils/constants'
import PopoverContent from './LinkDirectionGrid/PopoverContent'
import { computeScore } from '@/utils/tools'
import axios from 'axios'
export default {
name: 'LinkDirectionGrid',
@@ -56,11 +56,14 @@ export default {
endTime: getSecond(this.timeFilter.endTime)
}
const dataRequest = get(api.linkMonitor.bigramAnalysis, params)
const nextHopRequest = get(api.linkMonitor.bigramNextHopAnalysis, params)
const dataRequest = axios.get(api.linkMonitor.bigramAnalysis, { params: params })
const nextHopRequest = axios.get(api.linkMonitor.bigramNextHopAnalysis, { params: params })
this.toggleLoading(true)
Promise.all([dataRequest, nextHopRequest]).then(res => {
Promise.all([dataRequest, nextHopRequest]).then(response => {
const res = []
res[0] = response[0].data
res[1] = response[1].data
if (res[0].code === 200) {
this.isLinkShowError = false
// 链路流量数据
@@ -95,7 +98,7 @@ export default {
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3
d.scoreLow3 = d.score < 3 || d.score === '-'
if (data) {
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
@@ -140,12 +143,12 @@ export default {
// 链路下一跳信息
const nextLinkData = res[1].data.result
// 接口数据乱序,根据方向排序,再根据同个方向下的进行排序
// 接口数据乱序,根据方向排序,再根据同个方向下的出方向进行排序
nextLinkData.sort((a, b) => {
if (a.ingressLinkDirection !== b.ingressLinkDirection) {
return a.ingressLinkDirection.localeCompare(b.ingressLinkDirection)
return a.ingressLinkDirection.localeCompare(b.ingressLinkDirection, 'zh')
}
return a.egressLinkDirection.localeCompare(b.egressLinkDirection)
return a.egressLinkDirection.localeCompare(b.egressLinkDirection, 'zh')
})
this.isNextNoData = nextLinkData.length === 0
@@ -182,7 +185,7 @@ export default {
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3
d.scoreLow3 = d.score < 3 || d.score === '-'
if (data) {
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
@@ -225,13 +228,11 @@ export default {
this.nextErrorMsg = res[1].message
}
}).catch(e => {
console.error(e)
this.isLinkShowError = true
this.linkErrorMsg = e[0].message
this.linkErrorMsg = e.message
this.isNextShowError = true
this.nextErrorMsg = e[1].message
this.nextErrorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})

View File

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

View File

@@ -2,7 +2,7 @@
<div class="link-traffic-line">
<link-traffic-drill-down-list
:chart="chart"
:line-data="mpackets"
:line-data="tabs[0]"
:time-filter="timeFilter">
</link-traffic-drill-down-list>
<div class="line network link-traffic">
@@ -12,23 +12,25 @@
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<div class="line-value-mpackets"
<div class="line-value-tabs"
v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in mpackets"
v-for="(item, index) in tabs"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)">
<div class="line-value-mpackets-name">
@click="activeChange(item, index)"
:test-id="`tab-${index}`">
<div class="line-value-tabs-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
<div class="tabs-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit">
<div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span>
</span>
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
</span>
</div>
</div>
</div>
@@ -51,7 +53,7 @@
</div>
</div>
<div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData"></chart-no-data>
<chart-no-data v-if="isNoData" test-id="noData"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData" id="linkTrafficLineChart"></div>
</div>
</div>
@@ -69,12 +71,12 @@ import unitConvert from '@/utils/unit-convert'
import { chartColor3, chartColor4, unitTypes } from '@/utils/constants'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import _ from 'lodash'
import * as echarts from 'echarts'
import { linkTrafficLineChartOption } from '@/views/charts2/charts/options/echartOption'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import axios from 'axios'
export default {
name: 'LinkTrafficLine',
@@ -112,7 +114,7 @@ export default {
label: 'Packets/s'
}
],
mpackets: [
tabs: [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -176,13 +178,14 @@ export default {
}
}
this.loading = true
get(api.linkMonitor.totalTrafficAnalysis, params).then((res) => {
axios.get(api.linkMonitor.totalTrafficAnalysis, { params: params }).then((res) => {
res = res.data
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
this.tabs = [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.ingress', class: 'ingress', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'linkMonitor.egress', class: 'egress', show: true, invertTab: true, positioning: 2, data: [], unitType: '' }
@@ -198,73 +201,75 @@ export default {
console.error(e)
this.showError = true
this.errorMsg = e.message
// this.isNoData = true
this.isNoData = false
}).finally(() => {
this.loading = false
})
},
echartsInit (echartsData) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = echartsData.filter(t => t.show === true)
}
const _this = this
const dom = document.getElementById('linkTrafficLineChart')
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = linkTrafficLineChartOption
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => {
return {
...chartOption,
name: t.name,
lineStyle: {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0])
},
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartColor3[t.positioning]
},
{
offset: 1,
color: chartColor3[t.positioning]
}
])
},
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number'])
if (!this.isUnitTesting) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
} else {
echartsData = echartsData.filter(t => t.show === true)
}
})
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
const _this = this
const dom = document.getElementById('linkTrafficLineChart')
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = linkTrafficLineChartOption
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => {
return {
...chartOption,
name: t.name,
lineStyle: {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0])
},
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartColor3[t.positioning]
},
{
offset: 1,
color: chartColor3[t.positioning]
}
])
},
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number'])
}
})
// const str = stackedLineTooltipFormatter(params)
return stackedLineTooltipFormatter(params)
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.tabs.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
})
// const str = stackedLineTooltipFormatter(params)
return stackedLineTooltipFormatter(params)
}
this.showMarkLine = true
this.myChart.setOption(this.chartOption)
}
this.showMarkLine = true
this.myChart.setOption(this.chartOption)
},
activeChange (item, index) {
if (this.isNoData) return
@@ -296,16 +301,16 @@ export default {
legendSelectChange (item, index, val) {
if (index === 'index') {
this.dispatchLegendSelectAction(item.name)
} else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
} else if (this.tabs[index] && this.tabs[index].name === item.name) {
this.dispatchLegendSelectAction(item.name)
this.mpackets.forEach((t) => {
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
}
if (val === 'active') {
this.mpackets.forEach(t => {
this.tabs.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
@@ -317,7 +322,7 @@ export default {
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
@@ -325,8 +330,8 @@ export default {
}
},
handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
if (document.querySelector('.network .line-value-tabs.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-tabs.is-active')
const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
@@ -339,7 +344,7 @@ export default {
this.lineTab = ''
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
@@ -388,9 +393,9 @@ export default {
})
}
lineData.splice(0, 1)
const mpackets = _.cloneDeep(this.mpackets)
const tabs = _.cloneDeep(this.tabs)
const unit = val === 'Bits/s' ? 'bps' : 'packets/s'
this.legendInit(lineData, active, show, unit, mpackets)
this.legendInit(lineData, active, show, unit, tabs)
},
legendInit (data, active, show, type, linkData) {
data.forEach((d, i) => {
@@ -417,17 +422,17 @@ export default {
}
}
})
this.mpackets = linkData
this.tabs = linkData
if (num === 3) {
linkData[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(linkData[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
this.echartsInit(this.tabs)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
this.echartsInit(this.tabs)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
@@ -436,7 +441,7 @@ export default {
mounted () {
this.timer = setTimeout(() => {
if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab)
const data = this.tabs.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
} else {
this.init()

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
<template>
<div class="ddos-detection">
<chart-no-data v-if="isNoData"></chart-no-data>
<chart-no-data v-if="isNoData" test-id="noData"></chart-no-data>
<chart-error info v-if="showError" :content="errorMsg" />
<div class="ddos-detection-title">

View File

@@ -1,37 +1,38 @@
<template>
<div class="line network">
<chart-error v-if="showError" :content="errorMsg" />
<chart-error v-if="showError" :content="errorMsg"/>
<div class="line-header" v-if="!showError">
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<div class="line-value-mpackets"
v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in mpackets"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)"
:test-id="`tab${index}`"
>
<div class="line-value-mpackets-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
<span v-if="item.unitType">{{item.unitType}}</span>
<template v-for="(item, index) in tabs">
<div class="line-value-tabs"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-if="item.show"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index,true)"
:test-id="`tab${index}`"
>
<div class="line-value-tabs-name">
<div :class="item.class"></div>
<div class="tabs-name" :test-id="`tabTitle${index}`">{{ $t(item.name) }}</div>
</div>
<div class="line-value-unit" :test-id="`tabContent${index}`">
<span class="line-value-unit-number">{{ unitConvert(item.analysis.avg, unitTypes.number)[0] }}</span>
<span class="line-value-unit-number2">
<span>{{ unitConvert(item.analysis.avg, unitTypes.number)[1] }}</span>
<span v-if="item.unitType">{{ item.unitType }}</span>
</span>
</div>
</div>
</div>
</template>
</div>
</div>
<div class="line-select line-header-right">
<div class="line-select-reference-line">
<span>{{$t('network.referenceLine')}}:</span>
<span>{{ $t('network.referenceLine') }}:</span>
<div class="line-select__operation">
<el-select
size="mini"
@@ -50,10 +51,10 @@
<div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" ref="overviewLineChart"></div>
<!-- todo 后续改动此处为框选返回-->
<!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">-->
<!-- <el-button @click.stop="backBrushHistory">返回</el-button>-->
<!-- </div>-->
<!-- todo 后续改动此处为框选返回-->
<!-- <div id="brushBtn" style="position: absolute;left: 0;top: 0;" v-show="mouseDownFlag">-->
<!-- <el-button @click.stop="backBrushHistory">返回</el-button>-->
<!-- </div>-->
</div>
</div>
</template>
@@ -74,6 +75,7 @@ import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error'
export default {
name: 'NetworkOverviewLine',
components: {
@@ -119,14 +121,69 @@ export default {
label: 'Maximum'
}
],
mpackets: [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' },
{ analysis: {}, name: 'network.internal', class: 'internal', show: true, invertTab: true, positioning: 3, data: [], unitType: '' },
{ analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' },
{ analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' }
tabsTemplate: [
{
analysis: {},
name: 'network.total',
class: 'total',
show: true,
invertTab: true,
positioning: 0,
data: [],
unitType: ''
},
{
analysis: {},
name: 'network.inbound',
class: 'inbound',
show: true,
invertTab: true,
positioning: 1,
data: [],
unitType: ''
},
{
analysis: {},
name: 'network.outbound',
class: 'outbound',
show: true,
invertTab: true,
positioning: 2,
data: [],
unitType: ''
},
{
analysis: {},
name: 'network.internal',
class: 'internal',
show: true,
invertTab: true,
positioning: 3,
data: [],
unitType: ''
},
{
analysis: {},
name: 'network.through',
class: 'through',
show: true,
invertTab: true,
positioning: 4,
data: [],
unitType: ''
},
{
analysis: {},
name: 'network.other',
class: 'other',
show: true,
invertTab: true,
positioning: 5,
data: [],
unitType: ''
}
],
tabs: [],
unitConvert,
unitTypes,
chartDateObject: [],
@@ -172,7 +229,7 @@ export default {
metric (n) {
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
@@ -195,20 +252,14 @@ export default {
this.toggleLoading(true)
axios.get(api.netWorkOverview.totalTrafficAnalysis, { params: params }).then(response => {
const res = response.data
// const res = mockData.bytes.boundary.data
this.errorMsg = res.message
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
{ analysis: {}, name: 'network.total', class: 'total', show: true, invertTab: true, positioning: 0, data: [], unitType: '' },
{ analysis: {}, name: 'network.inbound', class: 'inbound', show: true, invertTab: true, positioning: 1, data: [], unitType: '' },
{ analysis: {}, name: 'network.outbound', class: 'outbound', show: true, invertTab: true, positioning: 2, data: [], unitType: '' },
{ analysis: {}, name: 'network.internal', class: 'internal', show: true, invertTab: true, positioning: 3, data: [], unitType: '' },
{ analysis: {}, name: 'network.through', class: 'through', show: true, invertTab: true, positioning: 4, data: [], unitType: '' },
{ analysis: {}, name: 'network.other', class: 'other', show: true, invertTab: true, positioning: 5, data: [], unitType: '' }
]
this.tabs = _.cloneDeep(this.tabsTemplate)
} else {
this.initData(res.data.result, val, active, show, n)
}
@@ -228,31 +279,31 @@ export default {
/**
* 初始化echartsdom用于右键点击返回框选
*/
domInit () {
const self = this
// 去掉默认的contextmenu事件否则会和右键事件同时出现。
document.oncontextmenu = function (e) {
e.preventDefault()
}
document.getElementById('overviewLineChart').onmousedown = function (e) {
// e.button: 0左键1滚轮2右键
if (e.button === 2) {
self.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
self.mouseDownFlag = true
document.getElementById('brushBtn').style.left = e.layerX + 'px'
document.getElementById('brushBtn').style.top = e.layerY + 74 + 'px'
}
}
},
// domInit () {
// const self = this
// // 去掉默认的contextmenu事件否则会和右键事件同时出现。
// document.oncontextmenu = function (e) {
// e.preventDefault()
// }
// document.getElementById('overviewLineChart').onmousedown = function (e) {
// // e.button: 0左键1滚轮2右键
// if (e.button === 2) {
// self.myChart.dispatchAction({
// type: 'brush',
// areas: [] // 删除选框
// })
// self.mouseDownFlag = true
// document.getElementById('brushBtn').style.left = e.layerX + 'px'
// document.getElementById('brushBtn').style.top = e.layerY + 74 + 'px'
// }
// }
// },
echartsInit (echartsData, show) {
// echarts内容在单元测试时不执行
if (!this.isUnitTesting) {
if (this.lineTab) {
this.handleActiveBar()
echartsData = echartsData.filter(t => t.show === true && t.invertTab === false)
echartsData = echartsData.filter(t => t.show === true && t.class === this.lineTab) // t.invertTab === false
} else {
echartsData = echartsData.filter(t => t.show === true)
}
@@ -265,6 +316,10 @@ export default {
return {
...chartOption,
name: t.name,
type: 'line',
showSymbol: false,
smooth: true,
symbol: 'circle',
lineStyle: {
color: chartColor3[t.positioning],
width: 1
@@ -346,7 +401,7 @@ export default {
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
this.tabs.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
@@ -406,11 +461,17 @@ export default {
})
}
},
activeChange (item, index) {
activeChange (item, index, isClick) { // isClick:代表是通过点击操作来的
if (this.isNoData) return
this.lineTab = item.class
this.legendSelectChange(item, index, 'active')
this.showMarkLine = !item.invertTab
if (isClick && this.lineTab === item.class) { // 点击高亮 tab 后取消高亮,恢复到全不高亮的状态
this.legendSelectChange(item, index, 'active', true)
this.lineTab = ''
this.showMarkLine = false
} else {
this.lineTab = item.class
this.legendSelectChange(item, index, 'active')
this.showMarkLine = !item.invertTab
}
this.init(this.metric, this.showMarkLine, 'active')
},
mouseenter (item) {
@@ -433,19 +494,25 @@ export default {
name: name
})
},
legendSelectChange (item, index, val) {
legendSelectChange (item, index, val, isActiveAll) {
if (index === 'index') {
this.dispatchLegendSelectAction(item.name)
} else if (this.mpackets[index] && this.mpackets[index].name === item.name) {
this.dispatchLegendSelectAction(item.name)
this.mpackets.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
} else if (this.tabs[index] && this.tabs[index].name === item.name) {
if (isActiveAll) {
this.tabs.forEach((t) => {
this.dispatchLegendSelectAction(t.name)
})
} else {
this.dispatchLegendSelectAction(item.name)
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchLegendUnSelectAction(t.name)
}
})
}
}
if (val === 'active') {
this.mpackets.forEach(t => {
this.tabs.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
@@ -457,7 +524,7 @@ export default {
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.tabs.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
@@ -465,39 +532,45 @@ export default {
}
},
handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
if (document.querySelector('.network .line-value-tabs.is-active')) {
const {
offsetLeft,
clientWidth,
clientLeft
} = document.querySelector('.network .line-value-tabs.is-active')
const activeBar = document.querySelector('.network .line-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
},
resize () {
this.myChart.resize()
if (this.myChart) {
this.myChart.resize()
}
},
referenceSelectChange (val) {
this.lineRefer = val
let echartsData
if (this.lineTab) {
echartsData = this.mpackets.filter(t => t.show === true && t.invertTab === false)
echartsData = this.tabs.filter(t => t.show === true && t.class === this.lineTab) // t.invertTab === false
} else {
echartsData = this.mpackets.filter(t => t.show === true)
echartsData = this.tabs.filter(t => t.show === true)
}
if (!this.isUnitTesting) {
const chartOption = this.myChart.getOption()
if (this.lineRefer === 'Average' && this.showMarkLine) {
chartOption.series.forEach((t, i) => {
chartOption.series.forEach((t) => {
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) => {
chartOption.series.forEach((t) => {
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) => {
chartOption.series.forEach((t) => {
if (t.name === echartsData[0].name) {
t.markLine.data = [{ yAxis: echartsData[0].analysis.max }]
}
@@ -550,7 +623,9 @@ export default {
otherData[2] = 5
}
}
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)
return this.sizes[sortIndex]
},
@@ -570,12 +645,12 @@ export default {
}
lineData.splice(0, 1)
if (val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
const tabs = _.cloneDeep(this.tabsTemplate)
lineData.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
mpackets.forEach((e, i) => {
tabs.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
@@ -584,9 +659,9 @@ export default {
this.lineTab = 'total'
this.legendSelectChange(e, 0)
})
this.mpackets = mpackets
this.tabs = tabs
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
this.echartsInit(this.tabs, true)
})
} else {
const unit = val === 'Bits/s' ? 'bps' : 'packets/s'
@@ -594,73 +669,75 @@ export default {
}
},
legendInit (data, active, show, type, n) {
const mpackets = _.cloneDeep(this.mpackets)
const tabs = _.cloneDeep(this.tabsTemplate)
data.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
let num = 0
mpackets.forEach(e => {
const self = this
tabs.forEach(e => {
e.unitType = type
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false
num += 1
} else {
e.show = true
if (!active && show !== this.lineRefer) {
this.legendSelectChange(e, 'index')
if (!active && show !== self.lineRefer) {
self.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (self.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
self.lineTab = ''
self.lineRefer = ''
self.init()
}
}
})
this.mpackets = mpackets
this.tabs = tabs
if (num === 5) {
mpackets[0].invertTab = false
tabs[0].invertTab = false
this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.legendSelectChange(tabs[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
this.echartsInit(this.tabs, true)
})
} else {
if (n) this.lineTab = ''
this.$nextTick(() => {
this.echartsInit(this.mpackets, show)
this.echartsInit(this.tabs, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
},
}
/**
* 鼠标右键返回框选的时间范围
*/
backBrushHistory () {
this.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
if (this.brushHistory.length > 0) {
this.$store.commit('setRangeEchartsData', _.cloneDeep(this.brushHistory[0]))
this.brushHistory.shift()
}
this.mouseDownFlag = false
}
// backBrushHistory () {
// this.myChart.dispatchAction({
// type: 'brush',
// areas: [] // 删除选框
// })
// if (this.brushHistory.length > 0) {
// this.$store.commit('setRangeEchartsData', _.cloneDeep(this.brushHistory[0]))
// this.brushHistory.shift()
// }
// this.mouseDownFlag = false
// }
},
mounted () {
// todo 初始化鼠标事件,开启右键返回
// this.domInit()
this.myChart = null
this.chartOption = null
this.timer = setTimeout(() => {
if (this.lineTab && this.metric !== 'Sessions/s') {
const data = this.mpackets.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
const self = this
self.timer = setTimeout(() => {
if (self.lineTab && self.metric !== 'Sessions/s') {
const data = self.tabsTemplate.find(t => t.class === self.lineTab)
self.activeChange(data, data.positioning)
} else {
this.init()
self.init()
}
}, 200)
window.addEventListener('resize', this.resize)

View File

@@ -24,6 +24,7 @@
border
:cell-style="tableCellStyle"
:header-cell-style="tableHeaderCellStyle"
:row-class-name="rowClassName"
:class="tableClass"
height="100%"
empty-text=" "
@@ -47,7 +48,7 @@
<template #default="scope" :column="item">
<template v-if="item.columnType === tableColumnType.chainRatio" >
<div class="data-total" >
<div class="data-value">
<div class="data-value" :test-id="`data-value-${item.prop}${scope.row.index}`">
<template v-if="showUnit && item.unit">
{{scope.row[item.prop]?((scope.row[item.prop][0]||scope.row[item.prop][0]===0)? unitConvert(scope.row[item.prop][0], item.unit).join(' ') : '-'):'' }}
</template>
@@ -55,16 +56,16 @@
{{scope.row[item.prop]?((scope.row[item.prop][0]||scope.row[item.prop][0]===0)? unitConvert(scope.row[item.prop][0], unitTypes.number).join(' ') : '-'):'' }}
</template>
</div>
<div class="data-trend">
<div class="data-trend" :test-id="`data-trend-all-${item.prop}${scope.row.index}`">
<template v-if="scope.row[item.prop]">
<div v-if="scope.row[item.prop][1] === 'up'" class="data-total-trend data-total-trend-red">
<i class="cn-icon-rise1 cn-icon"></i><span>{{scope.row[item.prop][2]}}</span>
<div v-if="scope.row[item.prop][1] === 'up'" class="data-total-trend data-total-trend-red" :test-id="`data-trend-${item.prop}${scope.row.index}`">
<i class="cn-icon-rise1 cn-icon" :test-id="`data-trend-icon-${item.prop}${scope.row.index}`"></i><span :test-id="`data-trend-value-${item.prop}${scope.row.index}`">{{scope.row[item.prop][2]}}</span>
</div>
<div v-else-if="scope.row[item.prop][1] === 'down'" class="data-total-trend data-total-trend-green">
<i class="cn-icon-decline cn-icon"></i><span>{{scope.row[item.prop][2]}}</span>
<div v-else-if="scope.row[item.prop][1] === 'down'" class="data-total-trend data-total-trend-green" :test-id="`data-trend-${item.prop}${scope.row.index}`">
<i class="cn-icon-decline cn-icon" :test-id="`data-trend-icon-${item.prop}${scope.row.index}`"></i><span :test-id="`data-trend-value-${item.prop}${scope.row.index}`">{{scope.row[item.prop][2]}}</span>
</div>
<div v-else-if="scope.row[item.prop][1] === 'noChange'" class="data-total-trend data-total-trend-black">
<i class="cn-icon-constant cn-icon"></i>
<div v-else-if="scope.row[item.prop][1] === 'noChange'" class="data-total-trend data-total-trend-black" :test-id="`data-trend-${item.prop}${scope.row.index}`">
<i class="cn-icon-constant cn-icon" :test-id="`data-trend-icon-${item.prop}${scope.row.index}`"></i>
</div>
</template>
<template v-else>
@@ -74,7 +75,7 @@
</div>
</template>
<template v-else-if="item.columnType === tableColumnType.dillDown" >
<div v-if="isOnlyRead" >
<div v-if="isOnlyRead" :test-id="`dnsMapType-${scope.row.index}`">
<template v-if="tableType === fromRoute.dnsServiceInsights && isDnsMapType">
{{dnsMapData.get(scope.row['tab'])}}
</template>
@@ -82,7 +83,7 @@
{{scope.row['tab']}}
</template>
</div>
<div v-else class="data-click" @click="handleTabValue(item.name,scope.row['tab'])">
<div v-else class="data-click" :test-id="`dnsMapType-${scope.row.index}`" @click="handleTabValue(item.name,scope.row['tab'])">
<template v-if="tableType === fromRoute.dnsServiceInsights && isDnsMapType">
{{dnsMapData.get(scope.row['tab'])}}
</template>
@@ -94,10 +95,10 @@
<template v-else-if="item.columnType === tableColumnType.percent" >
<div class="dns-in-ex">
<div class="dns-percent-pic">
<div v-if="scope.row[item.prop][0] !== false" class="div-green" id="green" :style="`width:${scope.row[item.prop][0]}`"></div>
<div v-if="scope.row[item.prop][0] !== false" class="div-yellow" id="yellow" :style="`width:${scope.row[item.prop][1]}`"></div>
<div v-if="scope.row[item.prop][0] !== false" class="div-green" id="green" :test-id="`percent-green-${scope.row.index}`" :style="`width:${scope.row[item.prop][0]}`"></div>
<div v-if="scope.row[item.prop][0] !== false" class="div-yellow" id="yellow" :test-id="`percent-yellow-${scope.row.index}`" :style="`width:${scope.row[item.prop][1]}`"></div>
</div>
<div class="dns-percent">{{scope.row[item.prop][2]}}</div>
<div class="dns-percent" :test-id="`percent-value-${scope.row.index}`">{{scope.row[item.prop][2]}}</div>
</div>
</template>
<template v-else-if="item.prop === 'score'" >
@@ -105,10 +106,10 @@
<div v-if="scope.row.score <= 2" class="data-score data-score-red" >
{{scope.row[item.prop] ? unitConvert(scope.row[item.prop], unitTypes.number).join(' ') : '0'}}
</div>
<div v-else-if="scope.row.score <= 4" class="data-score data-score-yellow" >
<div v-else-if="scope.row.score <= 4" class="data-score data-score-yellow" :test-id="`score-${scope.row.index}`">
{{scope.row[item.prop] ? unitConvert(scope.row[item.prop], unitTypes.number).join(' ') : '0'}}
</div>
<div v-else-if="scope.row.score <= 6" class="data-score data-score-green" >
<div v-else-if="scope.row.score <= 6" class="data-score data-score-green" :test-id="`score-${scope.row.index}`">
{{scope.row[item.prop] ? unitConvert(scope.row[item.prop], unitTypes.number).join(' ') : '0'}}
</div>
</div>
@@ -326,6 +327,10 @@ export default {
},
mixins: [chartMixin],
methods: {
rowClassName ({ row, rowIndex }) {
// 把每一行的索引放进row
row.index = rowIndex
},
/* 参数 extraParams 额外请求参数 */
getChartData (extraParams = {}) {
this.initState()
@@ -1053,7 +1058,6 @@ export default {
type: tabList[0].prop
}
this.changeUrlTabState()
// const condition = this.$store.getters.getQueryCondition
const condition = this.getQueryCondition()
if (condition) {
queryParams = {
@@ -1539,13 +1543,13 @@ export default {
const commonTab = this.commonTabList.find(item => item.name === tabName)
tab.label = commonTab ? commonTab.i18n : ''
tab.prop = commonTab ? commonTab.prop : ''
if (!tab.hasOwnProperty('checked') || tab.checked === undefined || tab.checked === null) {
if (!Object.prototype.hasOwnProperty.call(tab, 'checked') || tab.checked === undefined || tab.checked === null) {
tab.checked = tab ? tab.show : true
}
if (!tab.hasOwnProperty('disabled') || tab.disabled === undefined || tab.disabled === null) {
if (!Object.prototype.hasOwnProperty.call(tab, 'disabled') || tab.disabled === undefined || tab.disabled === null) {
tab.disabled = tab ? !tab.enable : false
}
if (!tab.hasOwnProperty('panelId') || tab.panelId === undefined || tab.panelId === null) {
if (!Object.prototype.hasOwnProperty.call(tab, 'panelId') || tab.panelId === undefined || tab.panelId === null) {
tab.panelId = tab ? tab.panelIdOfFourthMenu : null
}
// 代码里写死的
@@ -1562,7 +1566,7 @@ export default {
const drilldownTabFull = []
const drilldownTabList = tab.drilldownTabs
drilldownTabList.forEach(drilldownTab => {
if (!drilldownTab.hasOwnProperty('name') || drilldownTab.name === undefined || drilldownTab.name === null) {
if (!Object.prototype.hasOwnProperty.call(drilldownTab, 'name') || drilldownTab.name === undefined || drilldownTab.name === null) {
const drilldownTabName = drilldownTab ? (drilldownTab.name ? drilldownTab.name : drilldownTab) : ''
const fullTab = oldList.find(item => item.name === drilldownTabName)
const drilldownTabWithAllInfo = this.$_.cloneDeep(fullTab)
@@ -1660,7 +1664,7 @@ export default {
let isSetDrilldownTabInfo = false
if (tabList && tabList.length > 0) {
const drilldownTab = tabList[0].drilldownTabs
if (drilldownTab && drilldownTab.length > 0 && drilldownTab.hasOwnProperty('name')) {
if (drilldownTab && drilldownTab.length > 0 && Object.prototype.hasOwnProperty.call(drilldownTab, 'name')) {
isSetDrilldownTabInfo = true
}
}
@@ -1743,7 +1747,7 @@ export default {
const tabItem = this.curTable.tabs.find(tabItem => tabItem.name === tab.name)
tab.hiddenDrilldownTabs = this.getHiddenDrilldownTabNameGroup(tabItem)
if (tab && tab.hasMetricSearch === true) {
const columnsForMetric = tab.metrics.find(metric => metric.name === this.metric)
// const columnsForMetric = tab.metrics.find(metric => metric.name === this.metric)
tab.metrics.forEach(metric => {
const oldSortColumns = metric.columns
const newSortColumns = this.customTableTitles.slice(1)

View File

@@ -82,7 +82,7 @@
</div>
</template>
<template v-else-if="item.prop === 'inbound'">
<div class="data-total-value">{{unitConvert(scope.row.inboundPacketsRate, unitTypes.bps).join(' ')}}</div>
<div class="data-total-value" :test-id="`inbound-packet-${scope.row.appSubcategory}`">{{unitConvert(scope.row.inboundPacketsRate, unitTypes.bps).join(' ')}}</div>
<div class="data-trend">
<div v-if="scope.row.inboundBytesRateChainRatio > 0" class="data-total-trend data-total-trend-red">
<i class="cn-icon-rise1 cn-icon"></i>&nbsp;
@@ -197,7 +197,6 @@ export default {
const lastCycleTrafficRequest = axios.get(api.npm.overview.appTrafficAnalysis, { params: { ...params, cycle: 1 } })
this.toggleLoading(true)
Promise.all([currentTrafficRequest, lastCycleTrafficRequest]).then(res => {
// console.log('打印数据=====', res)
if (res[0].data.code === 200 && res[1].data.code === 200) {
this.showError = false
this.errorMsg = ''

View File

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

View File

@@ -5,7 +5,7 @@
<chart-error tooltip v-if="showError" :content="errorMsg"></chart-error>
</div>
<div class="npm-event-pie">
<chart-no-data v-if="isNoData"></chart-no-data>
<chart-no-data v-if="isNoData" test-id="noData"></chart-no-data>
<div class="npm-event-pies" v-show="!isNoData">
<div class="chart-drawing" id="chart"></div>
<div class="npm-event-pie-legends">
@@ -13,14 +13,14 @@
<div class="npm-event-pie-legend-title" v-if="chartData.length > 0">{{ $t('overall.type') }}</div>
<template v-for="(legend, index) in chartData" :key="index">
<div class="npm-event-pie-legend-type">
<div class="npm-event-pie-legend-type-severity">{{legend.name}}</div>
<div class="npm-event-pie-legend-type-severity" :test-id="`testNode${index}`">{{legend.name}}</div>
</div>
</template>
</div>
<div class="npm-event-pie-legend">
<div class="npm-event-pie-legend-title" v-if="chartData.length > 0">{{ $t('network.total') }}</div>
<template v-for="(legend, index) in chartData" :key="index">
<div class="npm-event-pie-legend-total">{{legend.value}}</div>
<div class="npm-event-pie-legend-total" :test-id="`total${index}`">{{legend.value}}</div>
</template>
</div>
</div>
@@ -31,7 +31,6 @@
<script>
import { shallowRef } from 'vue'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import * as echarts from 'echarts'
import { pieChartOption3 } from '@/views/charts2/charts/options/echartOption'
@@ -39,6 +38,7 @@ import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
import axios from 'axios'
export default {
name: 'NpmEventsByType',
@@ -65,7 +65,7 @@ export default {
},
watch: {
timeFilter: {
handler (n) {
handler () {
this.eventsByTypeData()
}
}
@@ -91,11 +91,11 @@ export default {
return num
}
}
this.myChart.on('mouseover', function (params) {
this.myChart.on('mouseover', function () {
_this.chartOption.series[0].label.show = false
_this.myChart.setOption(_this.chartOption)
})
this.myChart.on('mouseout', function (params) {
this.myChart.on('mouseout', function () {
_this.chartOption.series[0].label.show = true
_this.myChart.setOption(_this.chartOption)
})
@@ -108,7 +108,8 @@ export default {
type: 'severity'
}
this.toggleLoading(true)
get(api.npm.events.recentEvents, params).then(res => {
axios.get(api.npm.events.recentEvents, { params: params }).then(res => {
res = res.data
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
@@ -117,7 +118,9 @@ export default {
const hit = arrData.find(e => e.name === t.eventType)
if (hit) {
arrData.forEach(d => {
d.value++
if (hit.name === d.name) {
d.value++
}
})
} else {
arrData.push({ name: t.eventType, value: 1 })
@@ -126,7 +129,9 @@ export default {
this.chartData = arrData.sort((a, b) => { return b.value - a.value })
this.$nextTick(() => {
if (this.chartData.length > 0) {
this.init()
if (!this.isUnitTesting) {
this.init()
}
}
})
} else {

View File

@@ -17,6 +17,7 @@ import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
import axios from 'axios'
export default {
name: 'NpmEventsHeader',
components: { ChartError },

View File

@@ -29,7 +29,7 @@
<template v-else-if="item.prop === 'eventType'">
<span class="data-recent-table-eventType" :test-id="`eventType-${scope.row.eventType}-${scope.$index}`">{{scope.row[item.prop]}}</span>
</template>
<span v-else-if="scope.row[item.prop]" :test-id="`startTime-${scope.row.startTime}-${scope.$index}`">{{scope.row[item.prop]}}</span>
<span v-else-if="scope.row[item.prop]" :test-id="`startTime-${scope.$index}`">{{scope.row[item.prop]}}</span>
<span v-else>-</span>
</div>
</template>
@@ -40,7 +40,7 @@
<svg class="icon" aria-hidden="true">
<use xlink:href="#cn-icon-good"></use>
</svg>
<div class="table-no-data__title">{{ $t('npm.thereNoEvents') }}</div>
<div class="table-no-data__title" test-id="noData">{{ $t('npm.thereNoEvents') }}</div>
</div>
</template>
</el-table>
@@ -126,11 +126,12 @@ export default {
})
this.tableData = res.data.result
} else {
// this.isNoData = true
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(error => {
this.isNoData = false
this.showError = true
this.errorMsg = error.message
}).finally(() => {

View File

@@ -42,7 +42,7 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import _ from 'lodash'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { getLineType, getLineIndexUnit, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { getLineType, getLineIndexUnit, getLineIndexUnit2, overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error'
export default {
@@ -71,7 +71,7 @@ export default {
unitConvert,
unitTypes,
side: '',
mpackets: [
tabs: [
{ name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
{ name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
{ name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
@@ -199,7 +199,7 @@ export default {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.mpackets = [
this.tabs = [
{ name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
{ name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
{ name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
@@ -215,6 +215,7 @@ export default {
{ name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
]
} else {
console.info(res.data.result)
this.initData(res.data.result, val)
}
} else {
@@ -226,6 +227,7 @@ export default {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
console.error(e)
}).finally(() => {
this.toggleLoading(false)
})
@@ -249,7 +251,7 @@ export default {
this.showError = false
this.isNoData = npmLineData.length === 0
if (this.isNoData) {
this.mpackets = [
this.tabs = [
{ name: this.$t('network.total'), show: true, positioning: 0, data: [], unitType: 'number' },
{ name: this.$t('network.inbound'), show: true, positioning: 1, data: [], unitType: 'number' },
{ name: this.$t('network.outbound'), show: true, positioning: 2, data: [], unitType: 'number' },
@@ -265,6 +267,7 @@ export default {
{ name: this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
]
} else {
console.info(npmLineData)
this.initData(npmLineData, val)
}
}).catch(e => {
@@ -282,7 +285,7 @@ export default {
if (echartsData.length > 0) {
const dom = document.getElementById('chart-line')
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = trafficLineChartOption
this.chartOption = _.cloneDeep(trafficLineChartOption)
const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t) => {
this.chartOption.yAxis[0].axisLabel.formatter = (value) => {
@@ -301,7 +304,7 @@ export default {
color: chartColor3[t.positioning],
width: 1
},
stack: t.name !== 'network.total' ? 'network.total' : '',
stack: t.name !== this.$t('network.total') ? this.$t('network.total') : '',
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -320,7 +323,7 @@ export default {
})
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
this.mpackets.forEach(e => {
this.tabs.forEach(e => {
if (e.name === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
@@ -393,36 +396,50 @@ export default {
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)
}
})
if (['Bits/s', 'Packets/s', 'Sessions/s'].indexOf(val) > -1) {
if (item.type === val) {
lineData = Object.keys((item)).map(t => {
return {
...item[t],
index: getLineIndexUnit2(t, false),
key: t
}
})
}
} else {
if (item.type === val) {
lineData = Object.keys((item)).map(t => {
return {
...item[t],
index: getLineIndexUnit(item.type, false),
unit: getLineIndexUnit(item.type, true),
key: t
}
})
}
}
})
}
lineData.splice(0, 1)
const mpackets = _.cloneDeep(this.mpackets)
console.info(lineData)
const tabs = _.cloneDeep(this.tabs)
const npmQuantity = _.cloneDeep(this.npmQuantity)
if (val === 'Sessions/s') {
lineData.forEach((d, i) => {
mpackets[i].data = d.values
mpackets[i].analysis = d.analysis
tabs[i].data = d.values
tabs[i].analysis = d.analysis
})
mpackets.forEach((e, i) => {
tabs.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.tabs = tabs
this.echartsInit(this.tabs)
} else if (val !== 'Bits/s' && val !== 'Packets/s') {
this.legendInit(lineData, npmQuantity, true)
} else {
this.legendInit(lineData, mpackets, false)
this.legendInit(lineData, tabs, false)
}
},
legendInit (data, npmData, show) {
@@ -431,8 +448,8 @@ export default {
npmData[d.index].data = d.values
npmData[d.index].analysis = d.analysis
} else {
npmData[i].data = d.values
npmData[i].analysis = d.analysis
npmData[d.index].data = d.values
npmData[d.index].analysis = d.analysis
}
})
if (show) {
@@ -445,8 +462,8 @@ export default {
npmData.forEach((e) => {
e.show = true
})
this.mpackets = npmData
this.echartsInit(this.mpackets)
this.tabs = npmData
this.echartsInit(this.tabs)
}
}
},

View File

@@ -205,7 +205,7 @@ export default {
*/
initExpendTab () {
if (this.$route.query.eventId) {
if (parseFloat(this.$route.query.eventId) === this.detection.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高度

View File

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

View File

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

View File

@@ -1,18 +1,18 @@
<template>
<div class="cn-builtin">
<div class="cn-builtin-left">
<div class="cn-report">
<div class="cn-report-left">
<loading :loading="builtinLeftLoading"></loading>
<div class="cn-builtin-left-title">
<div class="cn-report-left-title">
{{$t('report.category')}}
</div>
<div class="cn-builtin-left-menu" :class="{'cn-active': !builtinId}" @click="builtinTabs(null)">
<div class="cn-report-left-menu" :class="{'cn-active': !builtinId}" @click="builtinTabs(null)">
{{$t('dns.all')}}
</div>
<div class="cn-builtin-left-menu" :class="{'cn-active': builtinId === item.id}" v-for="item in builtinReportLeftMenu" :key="item.id" @click="builtinTabs(item.id)">
<div class="cn-report-left-menu" :class="{'cn-active': builtinId === item.id}" v-for="item in builtinReportLeftMenu" :key="item.id" @click="builtinTabs(item.id)">
{{item.name}}
</div>
</div>
<div class="cn-builtin-right">
<div class="cn-report-right">
<cn-data-list
ref="dataList"
:tableId="tableId"
@@ -22,19 +22,26 @@
:layout="['search']"
@search="search"
>
<template #top-tool-right>
<button
id="account-add"
class="top-tool-btn"
type="button"
@click="add"
>
<i class="cn-icon-add cn-icon"/>
<template #top-tool-left>
<button id="account-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
@click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{$t('overall.create')}}</span>
</button>
<button id="report-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
@click="editReport">
<i class="cn-icon-edit cn-icon"></i>
<span>{{$t('overall.edit')}}</span>
</button>
<button id="report-delete" class="top-tool-btn margin-r-10"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{$t('overall.delete')}}</span>
</button>
</template>
<template #default>
<loading :loading="builtinRightLoading"></loading>
<builtin-report-table
<report-table
ref="dataTable"
:api="url"
:custom-table-title="tools.customTableTitle"
@@ -73,7 +80,7 @@
<script>
import { get } from '@/utils/http'
import builtinReportTable from '@/components/table/report/reportTestTable'
import ReportTable from '@/components/table/report/ReportTable'
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import { api } from '@/utils/api'
@@ -148,7 +155,7 @@ export default {
'report.november',
'report.december'
],
tableId: 'builtinReportTable',
tableId: 'reportTable',
builtinLeftLoading: false,
builtinRightLoading: false,
getNum: -1
@@ -171,7 +178,7 @@ export default {
components: {
Loading,
cnDataList,
builtinReportTable,
ReportTable,
ReportBox
},
mounted () {
@@ -210,7 +217,7 @@ export default {
methods: {
queryGetTempData () {
this.builtinLeftLoading = true
get(api.reportCategory).then(res => {
get(api.reportCategory, { pageSize: 999 }).then(res => {
if (res.code === 200) {
this.builtinReportLeftMenu = res.data.list.map(c => {
return {
@@ -265,7 +272,7 @@ export default {
}
})
this.pageObj.total = response.data.total
})
})
// TODO
}
})
@@ -287,6 +294,19 @@ export default {
this.object = u
this.rightBox.show = true
},
editReport () {
if(this.batchDeleteObjs.length === 0){
this.$alert(this.$t('tip.pleaseSelectForEdit'),{
confirmButtonText: this.$t('tip.yes'),
type:'warning'
})
}else {
let curRecord = this.batchDeleteObjs[0]
this.initConfig(curRecord)
this.object = curRecord
this.rightBox.show = true
}
},
initConfig (u) {
if (!u.config) {
u.config = {

View File

@@ -0,0 +1,41 @@
<template>
<div class="administration entity-explorer entity-explorer--show-list">
<!-- 顶部工具栏在列表页显示 -->
<div class="explorer-top-tools explorer-detection-top-tools">
<div class="explorer-top-tools-title">{{$t('overall.knowledgeBase')}}</div>
</div>
<div style="width: 100%;padding-bottom: 26px;">
<chart-tabs :data="tabsData" router></chart-tabs>
</div>
<!-- 内容区 -->
<div class="explorer-container administration-container">
<router-view />
</div>
</div>
</template>
<script>
import ChartTabs from '@/components/common/ChartTabs'
import { useStore } from 'vuex'
export default {
name: 'Index',
components: {
ChartTabs
},
setup () {
const store = useStore()
const menu = store.getters.menuList.find(m => m.code === 'setting')
const tabsData = menu.children.map(l => ({
...l,
path: l.route
})).sort((a, b) => a.sort - b.sort)
return {
tabsData
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,128 @@
<template>
<div style="height: 100%;" class="knowledge-base">
<div class="top-title">
{{$t('overall.knowledgeBase')}}
</div>
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
from="knowledge-base"
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template v-slot:top-tool-left>
<button id="knowledge-base-add" :title="$t('knowledgeBase.createKnowledgeBase')" class="top-tool-btn margin-r-10 top-tool-btn--create"
@click="jumpToCreatePage">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{$t('overall.create')}}</span>
</button>
<!--
<button id="knowledge-base-edit" :title="$t('knowledgeBase.editKnowledgeBase')" class="top-tool-btn margin-r-10" :disabled="disableEdit"
@click="edit">
<i class="cn-icon-edit cn-icon" ></i>
<span>{{$t('overall.edit')}}</span>
</button>
-->
<button id="knowledge-base-delete" :title="$t('knowledgeBase.deleteKnowledgeBase')" class="top-tool-btn margin-r-10"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{$t('overall.delete')}}</span>
</button>
</template>
<template v-slot:default>
<knowledge-base-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
></knowledge-base-table>
</template>
<!-- 分页组件 -->
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
</div>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import KnowledgeBaseTable from '@/components/table/setting/KnowledgeBaseTable'
import { api } from '@/utils/api'
import axios from 'axios'
export default {
name: 'knowledgeBase',
components: {
cnDataList,
KnowledgeBaseTable
},
mixins: [dataListMixin],
data () {
return {
url: api.knowledgeBase,
tableId: 'knowledgeBaseTable' // 需要分页的table的id用于记录每页数量
}
},
methods: {
edit (u) {
axios.get(`${this.url}`, { params: { ids: u.id } }).then(response => {
if (response.data.code === 200) {
this.object = response.data.data.list[0]
}
}).catch(e => {
console.error(e)
if (e.response.data && e.response.data.message) {
this.$message.error(e.response.data.message)
} else {
this.$message.error('Something went wrong...')
}
})
},
del (row) {
this.$confirm(this.$t('tip.confirmDelete'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
type: 'warning'
}).then(() => {
this.tools.loading = true
axios.delete(this.url + '?ids=' + row.id).then(response => {
if (response.data.code === 200) {
this.delFlag = true
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
this.getTableData()
} else {
this.$message.error(response.data.message)
}
}).catch(e => {
console.error(e)
if (e.response.data && e.response.data.message) {
this.$message.error(e.response.data.message)
} else {
this.$message.error('Something went wrong...')
}
}).finally(() => {
this.tools.loading = false
})
})
},
jumpToCreatePage () {
this.$router.push({
path: '/knowledgeBase/form',
query: {
t: +new Date()
}
})
}
}
}
</script>

View File

@@ -0,0 +1,510 @@
<template>
<div class="edit-knowledge-base">
<div class="edit-knowledge-base__header">{{$t('overall.create')}}</div>
<div class="edit-knowledge-base__body">
<el-steps direction="vertical" :active="activeStep">
<el-step v-for="(height, index) in stepHeights" :style="`flex-basis: ${height}px; flex-shrink: 0;`" :key="index"></el-step>
</el-steps>
<el-collapse v-model="activeCollapses">
<el-collapse-item name="0">
<template #title><div class="form-sub-title">{{$t('knowledgeBase.editInformation')}}</div></template>
<el-form :model="editObject" label-position="top" ref="form" :rules="rules">
<!--name-->
<el-form-item :label="$t('config.roles.name')" prop="tagName">
<el-input class="form-input" maxlength="64" placeholder="" :disabled="!!editObject.id" show-word-limit size="mini" type="text" v-model="editObject.tagName" @blur="tagNameBlur"></el-input>
</el-form-item>
<el-form-item :label="$t('overall.type')" prop="tagType">
<el-select v-model="editObject.tagType"
class="form-select"
placeholder=" "
popper-class="form-select-popper"
:disabled="!!editObject.id || typeSelectDisable"
size="mini"
>
<template v-for="type in knowledgeBaseType" :key="type.name">
<el-option :label="type.name" :value="type.value"></el-option>
</template>
</el-select>
</el-form-item>
<el-form-item :label="$t('overall.remark')" prop="remark">
<el-input maxlength="255" show-word-limit :rows="4" size='mini' type="textarea" v-model="editObject.remark" id="role-box-input-remark"/>
</el-form-item>
</el-form>
</el-collapse-item>
<el-collapse-item name="1" class="upload-collapse">
<template #title><div class="form-sub-title">{{$t('overall.importFromFile')}}</div></template>
<loading :loading="uploadLoading"></loading>
<el-upload :action="`${baseUrl}knowledge/import`"
:headers="uploadHeaders"
:data="uploadParams"
:multiple="false"
:file-list="fileList"
:on-change="fileChange"
:on-success="uploadSuccess"
:on-remove="onRemove"
:before-upload="beforeUpload"
:on-progress="onUpload"
:on-error="uploadError"
:class="uploadErrorTip ? 'el-upload--error' : ''"
drag
:accept="fileTypeLimit"
ref="upload"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
<div>{{$t('knowledgeBase.dropFileHereOr')}}<em>{{$t('knowledgeBase.clickToUpload')}}</em></div>
<div class="upload-tip">{{$t('knowledgeBase.supportCsv')}}</div>
</div>
</el-upload>
<transition name="el-zoom-in-top">
<div class="upload-error-tip" v-if="uploadErrorTip">{{uploadErrorTip}}</div>
</transition>
</el-collapse-item>
<el-collapse-item name="2">
<template #title><div class="form-sub-title">{{$t('overall.preview')}}</div></template>
<div class="skeleton-border" v-if="!uploaded">
<el-skeleton>
<template #template>
<div v-for="item of 6" :key="item" class="skeleton-item-row">
<el-skeleton-item variant="text" style="width: calc(33% - 25px); margin-right: 38px;"/>
<el-skeleton-item variant="text" style="width: calc(33% - 25px); margin-right: 38px;"/>
<el-skeleton-item variant="text" style="width: calc(33% - 26px);"/>
</div>
</template>
</el-skeleton>
<div class="skeleton-tip">{{$t('knowledgeBase.skeletonTip')}}</div>
</div>
<div v-else>
<div class="imported-tip"><i class="cn-icon cn-icon-baocuo"/>
&nbsp;&nbsp;{{$t('knowledgeBase.importTip', { total: originalImportInfo.total, succeeded: originalImportInfo.succeeded, failed: originalImportInfo.failed })}}
</div>
<div class="imported-table-box" :class="previewErrorTip ? 'imported-table-box--error' : ''">
<table class="imported-table" v-if="!importedDataNoData">
<tr>
<th width="230">{{importedTableFirstColumn}}</th>
<th width="180">Label</th>
<th>{{$t('overall.import')}}</th>
<th width="16"></th>
</tr>
<tr v-for="(d, i) in showImportedData" :key="importedType + d.tagItem + d.tagValue + i">
<td class="imported-data-item" :title="d.tagItem">{{d.tagItem}}</td>
<td class="imported-data-value" :title="d.tagValue">{{d.tagValue}}</td>
<td class="imported-data-msg" :title="d.msg"><i :class="d.status === 1 ? 'el-icon-success' : 'el-icon-error'"></i>&nbsp;&nbsp;{{d.msg}}</td>
<td><i class="el-icon-close" @click="removeImportedData(i)"></i></td>
</tr>
</table>
<chart-no-data v-else></chart-no-data>
<Pagination
class="imported-pagination"
:page-obj="importedPageObj"
:store-page-no-on-url="false"
layout="prev,pager,next"
@pageNo='pageNo'
@prev-click="prev"
@next-click="next"
></Pagination>
</div>
<transition name="el-zoom-in-top">
<div class="preview-error-tip" v-if="previewErrorTip">{{previewErrorTip}}</div>
</transition>
</div>
</el-collapse-item>
</el-collapse>
</div>
<div class="edit-knowledge-base__footer">
<button class="footer__btn footer__btn--light" @click="cancel">
<span>{{$t('overall.cancel')}}</span>
</button>
<button style="position: relative;" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
<loading size="small" :loading="blockOperation.save"></loading>
<span>{{$t('overall.save')}}</span>
</button>
</div>
</div>
</template>
<script>
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import _ from 'lodash'
import { knowledgeBaseType, storageKey, unitTypes } from '@/utils/constants'
import Pagination from '@/components/common/Pagination'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import axios from 'axios'
import { api } from '@/utils/api'
import unitConvert from '@/utils/unit-convert'
import Loading from '@/components/common/Loading'
export default {
name: 'CreateKnowledgeBase',
components: {
Pagination,
ChartNoData,
Loading
},
data () {
const nameValidator = (rule, value, callback) => {
let validate = true
// /^[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEFA-Za-z0-9\-\_]*$/
const reg = /^[\u4e00-\u9fa5A-Za-z0-9\-\_]*$/
validate = reg.test(value)
return validate
}
const nameAndTypeValidator = async (rule, value, callback) => {
this.$refs.form.clearValidate('tagType')
let validate = true
const response = await this.getKnowledgeBaseList()
if (response.data.code === 200) {
const find = response.data.data.list.find(d => d.tagName === value && d.tagType === this.editObject.tagType)
if (find) {
validate = false
callback(new Error())
}
}
return validate
}
const typeAndNameValidator = async (rule, value, callback) => {
this.$refs.form.clearValidate('tagName')
let validate = true
const response = await this.getKnowledgeBaseList()
if (response.data.code === 200) {
const find = response.data.data.list.find(d => d.tagName === this.editObject.tagName && d.tagType === value)
if (find) {
validate = false
callback(new Error())
}
}
return validate
}
return {
rules: {
tagName: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nameValidator, message: this.$t('validate.onlyAllowNumberLetterChinese-_'), trigger: 'blur' },
{ validator: nameAndTypeValidator, message: this.$t('validate.duplicateRecord', { columns: '(' + this.$t('config.roles.name') + '+' + this.$t('overall.type') + ')' }), trigger: 'blur' }
],
tagType: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
{ validator: typeAndNameValidator, message: this.$t('validate.duplicateRecord', { columns: '(' + this.$t('config.roles.name') + '+' + this.$t('overall.type') + ')' }), trigger: 'change' }
],
remark: [
{ validator: nameValidator, message: this.$t('validate.onlyAllowNumberLetterChinese-_'), trigger: 'blur' }
]
}
}
},
methods: {
tagNameBlur () {
if (!this.tagNameFirstBlur) {
this.$refs.form.validate(valid => {
if (valid) {
this.tagNameFirstBlur = true
}
})
}
},
fileChange (files, fileList) {
this.fileList = fileList.slice(-1)
},
uploadError () {
this.uploadLoading = false
this.$message.error(this.$t('tip.uploadFailed', { msg: 'error' }))
},
uploadSuccess (response) {
this.uploaded = response.code === 200
if (response.code === 200) {
// 上传成功后去掉upload和preview的错误提示
this.uploadErrorTip = ''
this.previewErrorTip = ''
this.importedType = this.editObject.tagType
const originalImportedData = _.cloneDeep(response.data.data)
this.importedDataNoData = originalImportedData.length === 0
this.originalImportInfo = {
total: originalImportedData.length,
succeeded: originalImportedData.filter(d => d.status === 1).length,
failed: originalImportedData.filter(d => d.status !== 1).length
}
originalImportedData.sort((a, b) => b.status - a.status)
this.importedData = originalImportedData
this.handleShowImportedData()
} else {
this.uploadLoading = false
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
}
},
onRemove (files, fileList) {
this.uploaded = false
this.typeSelectDisable = false
this.importedData = []
this.showImportedData = []
this.originalImportInfo = {
total: null,
succeeded: null,
failed: null
}
},
beforeUpload (file) {
// 判断后缀,仅支持.csv
if (!_.endsWith(file.name, '.csv')) {
this.$message.error(this.$t('validate.fileTypeLimit', { types: this.fileTypeLimit }))
this.fileList = []
return false
}
// 判断文件大小
if (file.size > this.uploadFileSizeLimit) {
this.$message.error(this.$t('validate.fileSizeLimit', { size: unitConvert(this.uploadFileSizeLimit, unitTypes.byte).join('') }))
this.fileList = []
return false
}
return true
},
onUpload (event, file) {
this.uploadLoading = true
this.typeSelectDisable = true
},
handleShowImportedData () {
const startIndex = (this.importedPageObj.pageNo - 1) * this.importedPageObj.pageSize
const endIndex = this.importedPageObj.pageNo * this.importedPageObj.pageSize
this.showImportedData = this.importedData.slice(startIndex, endIndex)
this.$nextTick(() => {
this.uploadLoading = false
})
},
pageNo (val) {
this.importedPageObj.pageNo = val
},
prev () {
this.importedPageObj.pageNo--
},
next () {
this.importedPageObj.pageNo++
},
removeImportedData (index) {
const toRemoveIndex = (this.importedPageObj.pageNo - 1) * this.importedPageObj.pageSize + index
this.importedData.splice(toRemoveIndex, 1)
this.importedPageObj.total--
this.handleShowImportedData()
// 若删除后本页无数据则页码减1或者提示无数据
if (this.showImportedData.length === 0) {
if (this.importedData.length > 0) {
this.importedPageObj.pageNo--
this.handleShowImportedData()
} else {
this.importedDataNoData = true
}
}
// 删除后若有错误提示且列表中不再有错误项,则清空错误提示
if (!this.hasErrorImportedData() && this.previewErrorTip) {
this.previewErrorTip = ''
}
},
cancel () {
this.$router.push({
path: '/knowledgeBase',
t: +new Date()
})
},
save () {
if (this.blockOperation.save) { return }
this.blockOperation.save = true
// 校验form + upload + preview
this.$refs.form.validate(valid => {
this.$refs.form.validateField('tagName')
if (!this.uploaded) {
this.uploadErrorTip = this.$t('validate.required')
} else {
this.uploadErrorTip = ''
}
if (this.importedData.length === 0) {
this.previewErrorTip = this.$t('validate.required')
} else if (this.hasErrorImportedData()) {
this.previewErrorTip = this.$t('validate.pleaseCheckForErrorItem')
} else {
this.previewErrorTip = ''
}
if (valid) {
// 校验通过后组织数据、请求接口
if (valid && !this.uploadErrorTip && !this.previewErrorTip) {
const postData = {
tagName: this.editObject.tagName,
tagType: this.editObject.tagType,
data: []
}
this.importedData.forEach(d => {
const findData = postData.data.find(d2 => d2.tagValue === d.tagValue)
if (findData) {
findData.itemList.add(d.tagItem)
} else {
const set = new Set()
set.add(d.tagItem)
postData.data.push({
tagValue: d.tagValue,
itemList: set
})
}
})
postData.data.forEach(d => {
d.itemList = [...d.itemList]
})
postData.remark = this.editObject.remark
axios.post(this.url, postData).then(response => {
if (response.data.code === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.$router.push({
path: '/knowledgeBase',
t: +new Date()
})
} else {
this.$message.error(response.data.message)
}
}).catch(e => {
console.error(e)
if (e.response.data && e.response.data.message) {
this.$message.error(e.response.data.message)
} else {
this.$message.error('Something went wrong...')
}
}).finally(() => {
this.blockOperation.save = false
})
} else {
this.blockOperation.save = false
}
} else {
this.blockOperation.save = false
}
})
},
hasErrorImportedData () {
return this.importedData.filter(d => d.status !== 1).length > 0
},
async getKnowledgeBaseList () {
return await axios.get(this.url, { params: { pageSize: 999 } })
}
},
computed: {
uploadParams () {
return {
type: this.editObject.tagType
}
},
importedTableFirstColumn () {
const t = this.knowledgeBaseType.find(t => t.value === this.importedType)
return t ? t.name : ''
},
activeStep () {
if (this.tagNameFirstBlur) {
return this.uploaded ? 2 : 1
} else {
return 0
}
}
},
watch: {
activeCollapses (n) {
const index0 = n.indexOf('0')
const index1 = n.indexOf('1')
if (index0 > -1) {
if (this.stepHeights[0] === this.stepHeightConstant.collapse) {
this.stepHeights.splice(0, 1, this.stepHeightConstant.first)
}
} else {
if (this.stepHeights[0] === this.stepHeightConstant.first) {
this.stepHeights.splice(0, 1, this.stepHeightConstant.collapse)
}
}
if (index1 > -1) {
if (this.stepHeights[1] === this.stepHeightConstant.collapse) {
this.stepHeights.splice(1, 1, this.stepHeightConstant.second)
}
} else {
if (this.stepHeights[1] === this.stepHeightConstant.second) {
this.stepHeights.splice(1, 1, this.stepHeightConstant.collapse)
}
}
},
importedData (n) {
this.importedPageObj.total = n.length
},
'importedPageObj.pageNo': {
handler (n) {
this.handleShowImportedData()
}
}
},
setup () {
const { query } = useRoute()
const knowledgeBaseId = ref(query.id || '')
const url = api.knowledgeBase
// 空白对象
const blankObject = {
tagName: '',
buildIn: '',
id: '',
tagType: 'ip',
remark: '',
updateTime: ''
}
// form绑定的对象
const editObject = ref(_.cloneDeep(blankObject))
// 折叠组件控制
const activeCollapses = ref(['0', '1', '2'])
// 步骤条控制
const stepHeightConstant = {
collapse: 58,
first: 333,
second: 284
}
const stepHeights = ref([stepHeightConstant.first, stepHeightConstant.second, stepHeightConstant.collapse])
// 所有导入的数据
const importedData = ref([])
// 导入数据的原始数量信息
const originalImportInfo = ref({
total: null,
succeeded: null,
failed: null
})
// table中显示的导入的数据
const showImportedData = ref([])
const importedPageObj = ref({
pageNo: 1,
pageSize: 10,
total: null
})
const importedType = ref('')
// 没上传过文件的提示
const uploadErrorTip = ref('')
// 预览区无内容的提示
const previewErrorTip = ref('')
return {
knowledgeBaseId,
editObject,
tagNameFirstBlur: ref(false),
blankObject,
activeCollapses,
stepHeightConstant,
stepHeights,
knowledgeBaseType,
importedData,
showImportedData,
importedPageObj,
importedType,
baseUrl: BASE_CONFIG.baseUrl,
fileList: ref([]),
uploadHeaders: {
'Cn-Authorization': localStorage.getItem(storageKey.token)
},
uploaded: ref(false),
importedDataNoData: ref(false),
url,
originalImportInfo,
uploadErrorTip,
previewErrorTip,
typeSelectDisable: ref(false),
uploadFileSizeLimit: 100 * 1024 * 1024,
uploadLoading: ref(false),
fileTypeLimit: '.csv'
}
}
}
</script>

View File

@@ -1,82 +0,0 @@
<template>
<div style="height: 100%;">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.chart"
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template #top-tool-right>
<button
id="account-add"
class="top-tool-btn margin-r-10"
type="button"
@click="add"
>
<i class="cn-icon-add cn-icon"/>
</button>
</template>
<template #default>
<chart-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
/>
</template>
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
<el-drawer
v-model="rightBox.show"
direction="rtl"
:with-header="false"
destroy-on-close>
<chart-box
:object="object"
@close="closeRightBox"
/>
</el-drawer>
</div>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import chartTable from '@/components/table/settings/ChartTable'
import chartBox from '@/components/rightBox/settings/ChartBox'
import { api } from '@/utils/api'
export default {
name: 'Chart',
mixins: [dataListMixin],
components: {
cnDataList,
chartTable,
chartBox
},
data () {
return {
url: api.chart,
listUrl: api.chartList,
blankObject: { // 空白对象
id: '',
name: '',
params: '',
i18n: ''
},
tableId: 'chartTable'
}
}
}
</script>

View File

@@ -1,144 +0,0 @@
<template>
<div style="height: 100%;">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.galaxyProxy"
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template v-slot:top-tool-right>
<button id="galaxy-proxy-clear-cache" class="top-tool-btn margin-r-10" :title="$t('overall.clearCache')"
type="button" @click="clearCache">
<i class="cn-icon cn-icon-clear-cache"></i>
</button>
<button id="galaxy-proxy-add" class="top-tool-btn margin-r-10"
type="button" @click="add">
<i class="cn-icon-add cn-icon"></i>
</button>
<button id="galaxy-proxy-debug" class="top-tool-btn margin-r-10" :title="$t('overall.debug')"
type="button" @click="debug(true,{})">
<i class="cn-icon-category cn-icon"></i>
</button>
<top-tool-more-options
ref="export"
id="model"
:params="searchLabel"
class="top-tool-export margin-l-10 margin-r-10"
export-file-name="galaxyProxy"
export-url="/galaxy/setting/export"
import-url="/galaxy/setting/import"
@afterImport="getTableData"
>
<template v-slot:before>
</template>
</top-tool-more-options>
</template>
<template v-slot:default>
<galaxy-proxy-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
@copy="copy"
@debug="debugRow"
></galaxy-proxy-table>
</template>
<!-- 分页组件 -->
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
<el-drawer
v-model="rightBox.show"
direction="rtl"
:with-header="false"
destroy-on-close>
<galaxy-proxy-box :object="object" @close="closeRightBox"></galaxy-proxy-box>
</el-drawer>
</div>
<galaxy-proxy-debug
v-model:show-debug="showDebug"
top="5vh"
:show-close="false"
:curGalaxyProxy="curGalaxyProxy"></galaxy-proxy-debug>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import galaxyProxyBox from '@/components/rightBox/settings/GalaxyProxyBox'
import galaxyProxyTable from '@/components/table/settings/GalaxyProxyTable'
import dataListMixin from '@/mixins/data-list'
import { api } from '@/utils/api'
import { get, put } from '@/utils/http'
import TopToolMoreOptions from '@/components/common/popBox/TopToolMoreOptions'
import galaxyProxyDebug from '@/components/setting/GalaxyProxyDebug'
export default {
name: 'GalaxyProxy',
components: {
cnDataList,
galaxyProxyBox,
galaxyProxyTable,
TopToolMoreOptions,
galaxyProxyDebug
},
mixins: [dataListMixin],
data () {
return {
url: api.galaxyProxy,
tableId: 'galaxySettingTable', // 需要分页的table的id用于记录每页数量
blankObject: { // 空白对象
name: ''
},
showDebug: false,
curGalaxyProxy: {}
}
},
methods: {
edit (u) {
get(`${this.url}/${u.id}`).then(response => {
if (response.code === 200) {
const editObject = response.data
editObject.targetHeader || (editObject.targetHeader = '')
editObject.preHandle || (editObject.preHandle = '')
editObject.postHandle || (editObject.postHandle = '')
editObject.targetParam || (editObject.targetParam = '')
this.object = editObject
this.rightBox.show = true
}
})
},
debug (isTopDebug, u) {
if (!isTopDebug && u) {
this.curGalaxyProxy = JSON.parse(JSON.stringify(u))
}
this.showDebug = true
},
debugRow (u) {
this.debug(false, u)
},
clearCache () {
put(`${this.url}/clearCache`).then(response => {
if (response.code === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.success') })
} else {
this.$message.error(response.msg || response.message)
}
}).catch(() => {
this.$message.error(this.$t('tip.unknownError'))
})
}
}
}
</script>

View File

@@ -1,86 +0,0 @@
<template>
<div style="height: 100%;">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.user"
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template #top-tool-right>
<button
id="account-add"
v-has="'user_add'"
class="top-tool-btn margin-r-10"
type="button"
@click="add"
>
<i class="cn-icon-add cn-icon"/>
</button>
</template>
<template #default>
<i18n-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
@showBottomBox="(targetTab, object) => { $refs.dataList.showBottomBox(targetTab, object) }"
/>
</template>
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
<el-drawer
v-model="rightBox.show"
direction="rtl"
:with-header="false"
destroy-on-close>
<i18n-box
:object="object"
@close="closeRightBox"
/>
</el-drawer>
</div>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import i18nTable from '@/components/table/settings/I18nTable'
import i18nBox from '@/components/rightBox/settings/I18nBox'
import { api } from '@/utils/api'
export default {
name: 'I18n',
mixins: [dataListMixin],
components: {
cnDataList,
i18nTable,
i18nBox
},
data () {
return {
url: api.i18nSys,
blankObject: { // 空白对象
id: '',
name: '',
code: '',
lang: '',
value: ''
},
tableId: 'i18nTable'
}
},
methods: {
}
}
</script>

View File

@@ -1,54 +0,0 @@
<template>
<div style="height: 100%;">
<cn-data-list
ref="dataList"
:api="url"
:tableId="tableId"
:layout="['search', 'elementSet','search']"
v-model:custom-table-title="tools.customTableTitle"
:from="fromRoute.operationLog"
@search="search">
<template v-slot:default>
<operation-log-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@del="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
@showBottomBox="(targetTab, object) => { $refs.dataList.showBottomBox(targetTab, object) }"></operation-log-table>
@search="search"
</template>
<!-- 分页组件 -->
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
</div>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import operationLogTable from '@/components/table/settings/OperationLogTable'
import { api } from '@/utils/api'
export default {
name: 'operationLog',
components: {
cnDataList,
operationLogTable
},
mixins: [dataListMixin],
data () {
return {
url: api.operationLog,
tableId: 'operationLogTable' // 需要分页的table的id用于记录每页数量
}
}
}
</script>

View File

@@ -1,91 +0,0 @@
<template>
<div style="height: 100%;">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.roles"
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template v-slot:top-tool-right>
<button id="roles-add" :title="$t('overall.createRole')" class="top-tool-btn margin-r-10"
type="button" @click="add">
<i class="cn-icon-add cn-icon"></i>
</button>
<delete-button id="role-list-batch-delete" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
</template>
<template v-slot:default>
<roles-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
></roles-table>
</template>
<!-- 分页组件 -->
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
<el-drawer
v-model="rightBox.show"
direction="rtl"
:with-header="false"
destroy-on-close>
<role-box :object="object" @close="closeRightBox"></role-box>
</el-drawer>
</div>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import rolesTable from '@/components/table/settings/RoleTable'
import roleBox from '@/components/rightBox/settings/RoleBox'
import { api } from '@/utils/api'
import { get } from '@/utils/http'
export default {
name: 'roles',
components: {
cnDataList,
roleBox,
rolesTable
},
mixins: [dataListMixin],
data () {
return {
url: api.role,
tableId: 'rolesTable', // 需要分页的table的id用于记录每页数量
blankObject: { // 空白对象
name: '',
buildIn: '',
i18n: '',
id: '',
menuIds: [],
remark: '',
uby: 0,
utime: ''
}
}
},
methods: {
edit (u) {
get(`${this.url}`, { ids: u.id }).then(response => {
if (response.code === 200) {
this.object = response.data.list[0]
this.rightBox.show = true
}
})
}
}
}
</script>

View File

@@ -1,86 +0,0 @@
<template>
<div style="height: 100%;">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.user"
:layout="['columnCustomize','elementSet','search']"
@search="search"
>
<template #top-tool-right>
<button
id="account-add"
class="top-tool-btn margin-r-10"
type="button"
@click="add"
>
<i class="cn-icon-add cn-icon"/>
</button>
</template>
<template #default>
<user-table
ref="dataTable"
v-loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
/>
</template>
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
</template>
</cn-data-list>
<el-drawer
v-model="rightBox.show"
direction="rtl"
:with-header="false"
destroy-on-close>
<user-box
:object="object"
@close="closeRightBox"
/>
</el-drawer>
</div>
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import userTable from '@/components/table/settings/UserTable'
import userBox from '@/components/rightBox/settings/UserBox'
import { api } from '@/utils/api'
export default {
name: 'User',
mixins: [dataListMixin],
components: {
cnDataList,
userTable,
userBox
},
data () {
return {
url: api.user,
blankObject: { // 空白对象
id: '',
name: '',
username: '',
email: '',
pin: '',
mobile: '',
status: 1,
roleIds: '',
pinChange: ''
},
tableId: 'userTable'
}
}
}
</script>

View File

@@ -0,0 +1,163 @@
import linkBlock from '@/views/charts2/charts/linkMonitor/LinkBlock'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import { mockData, linkInfoData } from './MockData/LinkBlock'
const mockGet1 = mockData.common.data1
const mockGet2 = mockData.common.data2
const linkInfo = JSON.stringify(linkInfoData)
const timeFilter = {
dateRangeValue: -1,
startTime: 1673244000000,
endTime: 1673247600000
}
var wrapper = null
/**
* 进行axios请求并挂载vue实例
*
*/
function axiosPostAndMounted (list1, list2) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const data1 = list1 || mockGet1
const data2 = list2 || mockGet2
// 模拟axios返回数据
axios.get.mockImplementation(url => {
switch (url) {
case '/interface/link/overview/analysis':
return Promise.resolve(data1)
case '/interface/link/overview/nextHopAnalysis':
return Promise.resolve(data2)
}
})
// 模拟localStorage获取数据
// eslint-disable-next-line no-proto
jest.spyOn(localStorage.__proto__, 'getItem').mockImplementation(() => linkInfo)
// 加载vue组件获得实例
wrapper = mount(linkBlock, {
propsData: {
timeFilter
}
})
}
describe('views/charts2/charts/linkMonitor/LinkBlock.vue测试', () => {
test('测试链路蜂窝图和下一跳蜂窝图从大到小排列', async () => {
axiosPostAndMounted()
// 延迟等待渲染。使用wrapper.vm.$nextTick有时不管用例如组件中使用了setTimeout的时候
await new Promise(resolve => setTimeout(() => {
const linkBlockTotal0 = wrapper.get('[test-id="linkBlockTotal0"]')
const linkBlockTotal1 = wrapper.get('[test-id="linkBlockTotal1"]')
const linkBlockTotal2 = wrapper.get('[test-id="linkBlockTotal2"]')
const linkBlockTotal3 = wrapper.get('[test-id="linkBlockTotal3"]')
const linkBlockTotal4 = wrapper.get('[test-id="linkBlockTotal4"]')
const linkBlockTotal5 = wrapper.get('[test-id="linkBlockTotal5"]')
const linkBlockTotal6 = wrapper.get('[test-id="linkBlockTotal6"]')
const linkBlockTotal7 = wrapper.get('[test-id="linkBlockTotal7"]')
const linkBlockTotal8 = wrapper.get('[test-id="linkBlockTotal8"]')
const linkBlockTotal9 = wrapper.get('[test-id="linkBlockTotal9"]')
expect(linkBlockTotal0.text()).toBe('13.88 Mbps')
expect(linkBlockTotal1.text()).toBe('11.99 Mbps')
expect(linkBlockTotal2.text()).toBe('11.86 Mbps')
expect(linkBlockTotal3.text()).toBe('10.56 Mbps')
expect(linkBlockTotal4.text()).toBe('10.49 Mbps')
expect(linkBlockTotal5.text()).toBe('10.31 Mbps')
expect(linkBlockTotal6.text()).toBe('10.19 Mbps')
expect(linkBlockTotal7.text()).toBe('10.14 Mbps')
expect(linkBlockTotal8.text()).toBe('9.85 Mbps')
expect(linkBlockTotal9.text()).toBe('9.54 Mbps')
const nextHopTotal0 = wrapper.get('[test-id="nextHopTotal0"]')
const nextHopTotal1 = wrapper.get('[test-id="nextHopTotal1"]')
const nextHopTotal2 = wrapper.get('[test-id="nextHopTotal2"]')
expect(nextHopTotal0.text()).toBe('67.97 Mbps')
expect(nextHopTotal1.text()).toBe('30.35 Mbps')
expect(nextHopTotal2.text()).toBe('10.49 Mbps')
resolve()
}, 200))
})
test('链路蜂窝图和下一跳蜂窝图均无数据', async () => {
const list1 = mockData.empty.data1
const list2 = mockData.empty.data2
axiosPostAndMounted(list1, list2)
await new Promise(resolve => setTimeout(() => {
const linkBlockNoData = wrapper.get('[test-id="linkBlockNoData"]')
const nextHpNoData = wrapper.get('[test-id="nextHpNoData"]')
expect(linkBlockNoData.text()).toBe('npm.noData')
expect(nextHpNoData.text()).toBe('npm.noData')
resolve()
}, 200))
})
test('链路蜂窝图上行流量大数值', async () => {
const list1 = mockData.boundary.data1
axiosPostAndMounted(list1)
await new Promise(resolve => setTimeout(() => {
const linkBlockTotal0 = wrapper.get('[test-id="linkBlockTotal0"]')
const linkBlockEgressUsage0 = wrapper.get('[test-id="linkBlockEgressUsage0"]')
const linkBlockEgressUsage1 = wrapper.get('[test-id="linkBlockEgressUsage1"]')
const linkBlockEgressUsage2 = wrapper.get('[test-id="linkBlockEgressUsage2"]')
const linkBlockEgressUsage3 = wrapper.get('[test-id="linkBlockEgressUsage3"]')
const linkBlockEgressUsage4 = wrapper.get('[test-id="linkBlockEgressUsage4"]')
const linkBlockEgressUsage5 = wrapper.get('[test-id="linkBlockEgressUsage5"]')
const linkBlockEgressUsage6 = wrapper.get('[test-id="linkBlockEgressUsage6"]')
const linkBlockEgressUsage7 = wrapper.get('[test-id="linkBlockEgressUsage7"]')
const linkBlockEgressUsage8 = wrapper.get('[test-id="linkBlockEgressUsage8"]')
const linkBlockEgressUsage9 = wrapper.get('[test-id="linkBlockEgressUsage9"]')
// 超大流量时total显示
expect(linkBlockTotal0.text()).toBe('100 Gbps')
// 超大上行流量,上行占比情况
expect(linkBlockEgressUsage0.text()).toBe('100.00%')
expect(linkBlockEgressUsage1.text()).toBe('100.00%')
expect(linkBlockEgressUsage2.text()).toBe('100.00%')
expect(linkBlockEgressUsage3.text()).toBe('100.00%')
expect(linkBlockEgressUsage4.text()).toBe('100.00%')
expect(linkBlockEgressUsage5.text()).toBe('100.00%')
expect(linkBlockEgressUsage6.text()).toBe('100.00%')
expect(linkBlockEgressUsage7.text()).toBe('100.00%')
expect(linkBlockEgressUsage8.text()).toBe('100.00%')
expect(linkBlockEgressUsage9.text()).toBe('100.00%')
resolve()
}, 200))
})
test('下一跳蜂窝图下行流量大数值', async () => {
const list1 = mockData.common.data1
const list2 = mockData.boundary.data2
axiosPostAndMounted(list1, list2)
await new Promise(resolve => setTimeout(() => {
const nextHopTotal0 = wrapper.get('[test-id="nextHopTotal0"]')
const nextHopIngressUsage0 = wrapper.get('[test-id="nextHopIngressUsage0"]')
const nextHopIngressUsage1 = wrapper.get('[test-id="nextHopIngressUsage1"]')
const nextHopIngressUsage2 = wrapper.get('[test-id="nextHopIngressUsage2"]')
const nextHopEgressUsage0 = wrapper.get('[test-id="nextHopEgressUsage0"]')
const nextHopEgressUsage1 = wrapper.get('[test-id="nextHopEgressUsage1"]')
const nextHopEgressUsage2 = wrapper.get('[test-id="nextHopEgressUsage2"]')
// 超大流量时total显示
expect(nextHopTotal0.text()).toBe('30 Tbps')
// 超大上行流量,上行、下行占比情况
expect(nextHopEgressUsage0.text()).toBe('0%')
expect(nextHopEgressUsage1.text()).toBe('0%')
expect(nextHopEgressUsage2.text()).toBe('0%')
expect(nextHopIngressUsage0.text()).toBe('100.00%')
expect(nextHopIngressUsage1.text()).toBe('100.00%')
expect(nextHopIngressUsage2.text()).toBe('100.00%')
resolve()
}, 200))
})
})

View File

@@ -0,0 +1,190 @@
import LinkDirectionGrid from '@/views/charts2/charts/linkMonitor/LinkDirectionGrid'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import ElementPlus from 'element-plus'
import { mockData } from './MockData/LinkDirectionGrid'
import { linkInfoData } from './MockData/LinkBlock'
const mockGet1 = mockData.common.data1
const mockGet2 = mockData.common.data2
const linkInfo = JSON.stringify(linkInfoData)
const timeFilter = {
dateRangeValue: -1,
startTime: 1673244000000,
endTime: 1673247600000
}
/**
* 进行axios请求并挂载vue实例
* @param list1
* @param list2
*/
function axiosPostAndMounted (list1, list2) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const data1 = list1 || mockGet1
const data2 = list2 || mockGet2
// 模拟axios返回数据
axios.get.mockImplementation(url => {
switch (url) {
case '/interface/link/overview/bigramAnalysis':
return Promise.resolve(data1)
case '/interface/link/overview/bigramNextHopAnalysis':
return Promise.resolve(data2)
}
})
// 模拟localStorage获取数据
// eslint-disable-next-line no-proto
jest.spyOn(localStorage.__proto__, 'getItem').mockImplementation(() => linkInfo)
}
describe('views/charts2/charts/linkMonitor/LinkDirectionGrid.vue测试', () => {
test('鼠标移动到链路网格图第5行第3列下一跳网格图第3行第1列测试链路编号和悬浮框dom内容', async () => {
axiosPostAndMounted()
// 加载vue组件获得实例
// 如需加载悬浮框dom则此处不需要挂载Element挂载Element的话需要触发hover事件获取悬浮框dom
const wrapper = mount(LinkDirectionGrid, {
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
/**
* 此为左侧链路网格图dom
*/
const fromLinkId5 = wrapper.find('[test-id="fromLinkId5"]')
const toLinkId3 = wrapper.find('[test-id="toLinkId3"]')
// 出入流量占比domegressUsage10-5-310-5-3表示10行10列的左侧网格图第5行第3列的上行流量
const egressUsage1053 = wrapper.find('[test-id="egressUsage10-5-3"]')
const ingressUsage1053 = wrapper.find('[test-id="ingressUsage10-5-3"]')
// 总流量
const totalBitsRate1053 = wrapper.find('[test-id="totalBitsRate10-5-3"]')
// 评分
const score1053 = wrapper.find('[test-id="score10-5-3"]')
// TCP连接建立延迟
const tcp1053 = wrapper.find('[test-id="tcp10-5-3"]')
// HTTP响应延迟
const http1053 = wrapper.find('[test-id="http10-5-3"]')
// SSL响应延迟
const ssl1053 = wrapper.find('[test-id="ssl10-5-3"]')
// 数据包丢失
const packetLoss1053 = wrapper.find('[test-id="packetLoss10-5-3"]')
// 重传率
const packetRetrans1053 = wrapper.find('[test-id="packetRetrans10-5-3"]')
expect(fromLinkId5.text()).toBe('Hundredgige5')
expect(toLinkId3.text()).toBe('Hundredgige3')
expect(egressUsage1053.text()).toBe('< 0.01%')
expect(ingressUsage1053.text()).toBe('< 0.01%')
expect(totalBitsRate1053.text()).toBe('576.94Kbps')
expect(score1053.text()).toBe('6')
expect(tcp1053.text()).toBe('83ms')
expect(http1053.text()).toBe('107ms')
expect(ssl1053.text()).toBe('0ms')
expect(packetLoss1053.text()).toBe('0.38%')
expect(packetRetrans1053.text()).toBe('0.91%')
/**
* 此为右侧下一跳网络网格图dom
*/
const toNextHop3 = wrapper.find('[test-id="toNextHop3"]')
const toNextHop1 = wrapper.find('[test-id="toNextHop1"]')
// 出入流量占比domegressUsage3-3-13-3-1表示3行3列的右侧网格图第3行第1列的上行流量
const egressUsage331 = wrapper.find('[test-id="egressUsage3-3-1"]')
const ingressUsage331 = wrapper.find('[test-id="ingressUsage3-3-1"]')
// 总流量
const totalBitsRate331 = wrapper.find('[test-id="totalBitsRate3-3-1"]')
// 评分
const score331 = wrapper.find('[test-id="score3-3-1"]')
// TCP连接建立延迟
const tcp331 = wrapper.find('[test-id="tcp3-3-1"]')
// HTTP响应延迟
const http331 = wrapper.find('[test-id="http3-3-1"]')
// SSL响应延迟
const ssl331 = wrapper.find('[test-id="ssl3-3-1"]')
// 数据包丢失
const packetLoss331 = wrapper.find('[test-id="packetLoss3-3-1"]')
// 重传率
const packetRetrans331 = wrapper.find('[test-id="packetRetrans3-3-1"]')
expect(toNextHop3.text()).toBe('西宁')
expect(toNextHop1.text()).toBe('太原')
expect(egressUsage331.text()).toBe('< 0.01%')
expect(ingressUsage331.text()).toBe('< 0.01%')
expect(totalBitsRate331.text()).toBe('6.50Mbps')
expect(score331.text()).toBe('5')
expect(tcp331.text()).toBe('89ms')
expect(http331.text()).toBe('151ms')
expect(ssl331.text()).toBe('0ms')
expect(packetLoss331.text()).toBe('0.53%')
expect(packetRetrans331.text()).toBe('2.24%')
resolve()
}, 200))
})
test('链路网格图第5行第3列下一跳网格图第3行第1列测试网格颜色随评分和流量占比变化', async () => {
const list1 = JSON.parse(JSON.stringify(mockGet1))
const list2 = JSON.parse(JSON.stringify(mockGet2))
// 修改链路评分为-,使得网格颜色为前绿后红
list1.data.data.result[43].establishLatencyMs = null
list1.data.data.result[43].httpResponseLatency = null
list1.data.data.result[43].sslConLatency = null
list1.data.data.result[43].tcpLostlenPercent = null
list1.data.data.result[43].pktRetransPercent = null
// 修改下一跳的流量占比为100%,使得网格颜色为前红后绿
list2.data.data.result[6].egressBitsRate = 0
list2.data.data.result[6].ingressBitsRate = 100000000000
axiosPostAndMounted(list1, list2)
const wrapper = mount(LinkDirectionGrid, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
/**
* 此为左侧链路网格图dom
*/
const scorePoint1053 = wrapper.find('[test-id="scorePoint10-5-3"]')
const usagePoint1053 = wrapper.find('[test-id="usagePoint10-5-3"]')
expect(scorePoint1053.classes()).toContain('data-item__point-red')
expect(usagePoint1053.classes()).toContain('data-item__point')
/**
* 此为右侧下一跳网络网格图dom
*/
const usagePoint331 = wrapper.find('[test-id="usagePoint3-3-1"]')
const scorePoint331 = wrapper.find('[test-id="scorePoint3-3-1"]')
expect(usagePoint331.classes()).toContain('data-item__point-red')
expect(scorePoint331.classes()).toContain('data-item__point')
resolve()
}, 200))
})
test('链路网格图、下一跳网格图无数据', async () => {
const list1 = mockData.empty.data1
const list2 = mockData.empty.data2
axiosPostAndMounted(list1, list2)
const wrapper = mount(LinkDirectionGrid, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
await new Promise(resolve => setTimeout(() => {
const noData0 = wrapper.find('[test-id="noData0"]')
expect(noData0.text()).toBe('npm.noData')
resolve()
}, 200))
})
})

View File

@@ -0,0 +1,174 @@
import LinkTrafficLine from '@/views/charts2/charts/linkMonitor/LinkTrafficLine'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import { mockData } from './MockData/LinkTrafficLine'
import { linkInfoData } from './MockData/LinkBlock'
const mockGet = mockData.common.data1
const mockGet1 = mockData.common.data2
const timeFilter = {
dateRangeValue: -1,
startTime: 1673244000,
endTime: 1673247600
}
const linkInfo = JSON.stringify(linkInfoData)
const query1 = { t: '1675395023486', thirdPanel: '15', thirdMenu: 'Link ID: Hundredgige10', panelName: 'Link ID: Hundredgige10', queryCondition: 'common_egress_link_id = 1792 or common_ingress_link_id = 1793', curTab: 'ip', startTime: 1675391498, endTime: 1675395098, range: 60 }
const query2 = { t: '1675409029937', thirdPanel: '15', thirdMenu: 'Link ID: Hundredgige10', panelName: 'Link ID: Hundredgige10', queryCondition: 'common_egress_link_id = 2816 or common_ingress_link_id = 2817', curTab: 'ip', startTime: 1675388605, endTime: 1675410205, range: 360, lineMetric: 'Bits/s' }
const query3 = { t: '1675409029937', thirdPanel: '15', thirdMenu: 'Link ID: Hundredgige10', panelName: 'Link ID: Hundredgige10', queryCondition: 'common_egress_link_id = 2816 or common_ingress_link_id = 2817', curTab: 'ip', startTime: 1675388605, endTime: 1675410205, range: 360, lineMetric: 'Packets/s' }
let wrapper = null
/**
* 进行axios请求并挂载vue实例
* 模拟localStorage获取数据
* @param query
* @param data1
* @param data2
*/
function axiosPostAndMounted (query, data1, data2) {
require('vue-router').useRoute.mockReturnValue({ query: query })
const _data1 = data1 || mockGet
const _data2 = data2 || mockGet1
// 模拟 axios 数据
axios.get.mockImplementation(url => {
switch (url) {
case '/interface/link/overview/drilldown/totalTrafficAnalysis':
return Promise.resolve(_data1)
case '/interface/link/overview/drilldown/networkAnalysis':
return Promise.resolve(_data2)
}
})
// 模拟localStorage获取数据
// eslint-disable-next-line no-proto
jest.spyOn(localStorage.__proto__, 'getItem').mockImplementation(() => linkInfo)
// 加载vue组件获得实例
wrapper = mount(LinkTrafficLine, {
propsData: {
timeFilter
}
})
}
describe('views/charts2/charts/linkMonitor/LinkTrafficLine.vue测试', () => {
test('左侧列表信息link下钻折线图 ', async () => {
axiosPostAndMounted(query1)
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.get('[test-id="line-tabContent"]')
const textNode1 = wrapper.get('[test-id="line-percent"]')
const textNode2 = wrapper.get('[test-id="line-score"]')
const textNode3 = wrapper.get('[test-id="line-tcp"]')
const textNode4 = wrapper.get('[test-id="line-http"]')
const textNode5 = wrapper.get('[test-id="line-ssl"]')
const textNode6 = wrapper.get('[test-id="line-packetLoss"]')
const textNode7 = wrapper.get('[test-id="line-packetRetrans"]')
expect(textNode0.text()).toEqual('756.87Kbps')
expect(textNode1.text()).toEqual('0%')
expect(textNode2.text()).toEqual('5')
expect(textNode3.text()).toEqual('92ms')
expect(textNode4.text()).toEqual('154ms')
expect(textNode5.text()).toEqual('0ms')
expect(textNode6.text()).toEqual('0.69%')
expect(textNode7.text()).toEqual('1.65%')
resolve()
}, 200))
})
test('Metric=Bits/s, tab点击第二个高亮link下钻折线图 ', async () => {
axiosPostAndMounted(query2)
const textNode3 = await wrapper.get('[test-id="tab-1"]')
await textNode3.trigger('click')
expect(textNode3.classes()).toContain('is-active')
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('756.87Kbps')
expect(textNode1.text()).toEqual('10.58Mbps')
expect(textNode2.text()).toEqual('9.82Mbps')
resolve()
}, 200))
})
test('Metric=Packets/slink下钻折线图 ', async () => {
axiosPostAndMounted(query3)
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('72.72packets/s')
expect(textNode1.text()).toEqual('904.03packets/s')
expect(textNode2.text()).toEqual('976.75packets/s')
resolve()
}, 200))
})
test('左侧列表信息link下钻折线图 大数值 ', async () => {
const data1 = mockData.boundary.large.data1
const data2 = mockData.boundary.large.data2
axiosPostAndMounted(query1, data1, data2)
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.get('[test-id="line-tabContent"]')
const textNode1 = wrapper.get('[test-id="line-percent"]')
const textNode2 = wrapper.get('[test-id="line-score"]')
const textNode3 = wrapper.get('[test-id="line-tcp"]')
const textNode4 = wrapper.get('[test-id="line-http"]')
const textNode5 = wrapper.get('[test-id="line-ssl"]')
const textNode6 = wrapper.get('[test-id="line-packetLoss"]')
const textNode7 = wrapper.get('[test-id="line-packetRetrans"]')
expect(textNode0.text()).toEqual('75.69Tbps')
expect(textNode1.text()).toEqual('37843.52%')
expect(textNode2.text()).toEqual('1')
expect(textNode3.text()).toEqual('9.29s')
expect(textNode4.text()).toEqual('154ms')
expect(textNode5.text()).toEqual('90ms')
expect(textNode6.text()).toEqual('69.00%')
expect(textNode7.text()).toEqual('16.50%')
resolve()
}, 200))
})
test('Metric=Packets/s无数据', async () => {
const data1 = mockData.empty.data1
const data2 = mockData.empty.data2
axiosPostAndMounted(query3, data1, data2)
await new Promise(resolve => setTimeout(() => {
const noData = wrapper.get('[test-id="noData"]')
expect(noData.text()).toBe('npm.noData')
resolve()
}, 200))
})
test('Metric=Packets/s大数值', async () => {
const data = mockData.boundary.large.data1
axiosPostAndMounted(query3, data)
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('727.20Mpackets/s')
expect(textNode1.text()).toEqual('904.03Gpackets/s')
expect(textNode2.text()).toEqual('976.75Gpackets/s')
resolve()
}, 200))
})
test('Metric=Bits/s数值为0', async () => {
const data = mockData.boundary.zero.data1
axiosPostAndMounted(query2, data)
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('0bps')
expect(textNode1.text()).toEqual('0bps')
expect(textNode2.text()).toEqual('0bps')
resolve()
}, 200))
})
})

View File

@@ -0,0 +1,240 @@
export const mockData = {
common: {
data1: {
data: { status: 200, code: 200, queryKey: '549b4c3bcabf0feee193b834671879de', success: true, message: null, statistics: { elapsed: 3, rows_read: 11480, bytes_read: 459200, result_size: 1214, result_rows: 21 }, job: null, formatType: 'json', meta: [{ name: 'link_id', type: 'string', category: 'Dimension' }, { name: 'egress_bytes', type: 'long', category: 'Metric' }, { name: 'ingress_bytes', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ linkId: '257', egressBytes: '0', egressBitsRate: 0, ingressBytes: '493739879', ingressBitsRate: 1097199.76 }, { linkId: '256', egressBytes: '4147998874', egressBitsRate: 9217775.28, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1024', egressBytes: '4229808296', egressBitsRate: 9399574, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1793', egressBytes: '0', egressBitsRate: 0, ingressBytes: '604840505', ingressBitsRate: 1344090 }, { linkId: '2817', egressBytes: '0', egressBitsRate: 0, ingressBytes: '430811638', ingressBitsRate: 957359.2 }, { linkId: '0', egressBytes: '819878366014', egressBitsRate: 1821951924.48, ingressBytes: '140774357362', ingressBitsRate: 312831905.28 }, { linkId: '1281', egressBytes: '0', egressBitsRate: 0, ingressBytes: '544675122', ingressBitsRate: 1210389.12 }, { linkId: '2049', egressBytes: '0', egressBitsRate: 0, ingressBytes: '711346274', ingressBitsRate: 1580769.52 }, { linkId: '1536', egressBytes: '4195896971', egressBitsRate: 9324215.52, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '2305', egressBytes: '0', egressBitsRate: 0, ingressBytes: '524865003', ingressBitsRate: 1166366.64 }, { linkId: '1792', egressBytes: '4145790227', egressBitsRate: 9212867.2, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1025', egressBytes: '0', egressBitsRate: 0, ingressBytes: '492227445', ingressBitsRate: 1093838.8 }, { linkId: '2816', egressBytes: '4000074817', egressBitsRate: 8889055.12, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '513', egressBytes: '0', egressBitsRate: 0, ingressBytes: '1444814826', ingressBitsRate: 3210699.6 }, { linkId: '768', egressBytes: '4370006099', egressBitsRate: 9711124.64, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '512', egressBytes: '3894190397', egressBitsRate: 8653756.4, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '769', egressBytes: '0', egressBitsRate: 0, ingressBytes: '1877580759', ingressBitsRate: 4172401.68 }, { linkId: '2304', egressBytes: '3767761711', egressBitsRate: 8372803.84, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '1537', egressBytes: '0', egressBitsRate: 0, ingressBytes: '367986916', ingressBitsRate: 817748.72 }, { linkId: '1280', egressBytes: '4040444894', egressBitsRate: 8978766.4, ingressBytes: '0', ingressBitsRate: 0 }, { linkId: '2048', egressBytes: '4682050724', egressBitsRate: 10404557.2, ingressBytes: '0', ingressBitsRate: 0 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20arrayJoin%28splitByChar%28%27_%27%2C%20concat%28toString%28common_egress_link_id%29%2C%20%27_%27%2C%20toString%28common_ingress_link_id%29%29%29%29%20AS%20link_id%2CSUM%28IF%28toString%28common_egress_link_id%29%20%3D%20link_id%2C%20traffic_outbound_byte%2C%200%29%29%20AS%20egress_bytes%2CSUM%28IF%28toString%28common_ingress_link_id%29%20%3D%20link_id%2C%20traffic_inbound_byte%2C%200%29%29%20AS%20ingress_bytes%20FROM%20metric_link%20WHERE%20stat_time%20%3E%3D%201675303793%20AND%20stat_time%20%3C%201675307393%20GROUP%20BY%20link_id&format=json&option=real-time', msg: 'OK' }
},
data2: {
data: { status: 200, code: 200, queryKey: 'ee2e820b1275748167cdee82b2b4ea40', success: true, message: null, statistics: { elapsed: 3, rows_read: 11480, bytes_read: 618564, result_size: 1053, result_rows: 10 }, job: null, formatType: 'json', meta: [{ name: 'egress_link_direction', type: 'string', category: 'Dimension' }, { name: 'ingress_link_direction', type: 'string', category: 'Dimension' }, { name: 'egress_bytes', type: 'long', category: 'Metric' }, { name: 'ingress_bytes', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ egressLinkDirection: '太原', ingressLinkDirection: '西宁', egressBytes: '2407509269', egressBitsRate: 5350020.56, ingressBytes: '193368911', ingressBitsRate: 429708.72 }, { egressLinkDirection: '西安', ingressLinkDirection: '西安', egressBytes: '3609358392', egressBitsRate: 8020796.4, ingressBytes: '345009143', ingressBitsRate: 766686.96 }, { egressLinkDirection: '西宁', ingressLinkDirection: '西安', egressBytes: '1273508499', egressBitsRate: 2830018.88, ingressBytes: '122646511', ingressBitsRate: 272547.84 }, { egressLinkDirection: '太原', ingressLinkDirection: '太原', egressBytes: '14840136119', egressBitsRate: 32978080.24, ingressBytes: '3386427658', ingressBitsRate: 7525394.8 }, { egressLinkDirection: '西安', ingressLinkDirection: '太原', egressBytes: '7070361369', egressBitsRate: 15711914.16, ingressBytes: '1853445429', ingressBitsRate: 4118767.6 }, { egressLinkDirection: '西宁', ingressLinkDirection: '太原', egressBytes: '2619231460', egressBitsRate: 5820514.32, ingressBytes: '291561196', ingressBitsRate: 647913.76 }, { egressLinkDirection: '', ingressLinkDirection: '', egressBytes: '409939183007', egressBitsRate: 910975962.24, ingressBytes: '70387178681', ingressBitsRate: 156415952.64 }, { egressLinkDirection: '太原', ingressLinkDirection: '西安', egressBytes: '7808050741', egressBitsRate: 17351223.84, ingressBytes: '1001570985', ingressBitsRate: 2225713.28 }, { egressLinkDirection: '西宁', ingressLinkDirection: '西宁', egressBytes: '337068337', egressBitsRate: 749040.72, ingressBytes: '165230290', ingressBitsRate: 367178.4 }, { egressLinkDirection: '西安', ingressLinkDirection: '西宁', egressBytes: '1508798824', egressBitsRate: 3352886.24, ingressBytes: '133628244', ingressBitsRate: 296951.68 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20egress_link_direction%20AS%20egress_link_direction%2C%20ingress_link_direction%20AS%20ingress_link_direction%2C%20SUM%28traffic_outbound_byte%29%20AS%20egress_bytes%2C%20SUM%28traffic_inbound_byte%29%20AS%20ingress_bytes%20FROM%20metric_link%20WHERE%20stat_time%20%3E%3D%201675303793%20AND%20stat_time%20%3C%201675307393%20GROUP%20BY%20egress_link_direction%2C%20ingress_link_direction&format=json&option=real-time', msg: 'OK' }
}
},
empty: {
data1: {
data: { status: 200, code: 200, data: { resultType: 'table', result: [] }, msg: 'OK' }
},
data2: {
data: { status: 200, code: 200, data: { resultType: 'table', result: [] }, msg: 'OK' }
}
},
boundary: {
data1: {
data: {
status: 200,
code: 200,
queryKey: '549b4c3bcabf0feee193b834671879de',
data: {
result: [
{
linkId: '257',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '493739879',
ingressBitsRate: 0
}, {
linkId: '256',
egressBytes: '4147998874',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '1024',
egressBytes: '4229808296',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '1793',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '604840505',
ingressBitsRate: 0
}, {
linkId: '2817',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '430811638',
ingressBitsRate: 0
}, {
linkId: '0',
egressBytes: '819878366014',
egressBitsRate: 1821951924.48,
ingressBytes: '140774357362',
ingressBitsRate: 312831905.28
}, {
linkId: '1281',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '544675122',
ingressBitsRate: 0
}, {
linkId: '2049',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '711346274',
ingressBitsRate: 0
}, {
linkId: '1536',
egressBytes: '4195896971',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '2305',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '524865003',
ingressBitsRate: 0
}, {
linkId: '1792',
egressBytes: '4145790227',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '1025',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '492227445',
ingressBitsRate: 0
}, {
linkId: '2816',
egressBytes: '4000074817',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '513',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '1444814826',
ingressBitsRate: 0
}, {
linkId: '768',
egressBytes: '4370006099',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '512',
egressBytes: '3894190397',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '769',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '1877580759',
ingressBitsRate: 0
}, {
linkId: '2304',
egressBytes: '3767761711',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '1537',
egressBytes: '0',
egressBitsRate: 0,
ingressBytes: '367986916',
ingressBitsRate: 0
}, {
linkId: '1280',
egressBytes: '4040444894',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}, {
linkId: '2048',
egressBytes: '4682050724',
egressBitsRate: 100000000000.00,
ingressBytes: '0',
ingressBitsRate: 0
}]
}
}
},
data2: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: [{
egressLinkDirection: '太原',
ingressLinkDirection: '西宁',
egressBytes: '2407509269',
egressBitsRate: 0,
ingressBytes: '193368911',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '西安',
ingressLinkDirection: '西安',
egressBytes: '3609358392',
egressBitsRate: 0,
ingressBytes: '345009143',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '西宁',
ingressLinkDirection: '西安',
egressBytes: '1273508499',
egressBitsRate: 0,
ingressBytes: '122646511',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '太原',
ingressLinkDirection: '太原',
egressBytes: '14840136119',
egressBitsRate: 0,
ingressBytes: '3386427658',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '西安',
ingressLinkDirection: '太原',
egressBytes: '7070361369',
egressBitsRate: 0,
ingressBytes: '1853445429',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '西宁',
ingressLinkDirection: '太原',
egressBytes: '2619231460',
egressBitsRate: 0,
ingressBytes: '291561196',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '',
ingressLinkDirection: '',
egressBytes: '409939183007',
egressBitsRate: 0,
ingressBytes: '70387178681',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '太原',
ingressLinkDirection: '西安',
egressBytes: '7808050741',
egressBitsRate: 0,
ingressBytes: '1001570985',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '西宁',
ingressLinkDirection: '西宁',
egressBytes: '337068337',
egressBitsRate: 0,
ingressBytes: '165230290',
ingressBitsRate: 10000000000000.00
}, {
egressLinkDirection: '西安',
ingressLinkDirection: '西宁',
egressBytes: '1508798824',
egressBitsRate: 0,
ingressBytes: '133628244',
ingressBitsRate: 10000000000000.00
}]
},
msg: 'OK'
}
}
}
}
export const linkInfoData = [{ originalLinkId: '256', linkId: 'Hundredgige1', direction: 'egress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '257', linkId: 'Hundredgige1', direction: 'ingress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '512', linkId: 'Hundredgige2', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '513', linkId: 'Hundredgige2', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '768', linkId: 'Hundredgige3', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '769', linkId: 'Hundredgige3', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1024', linkId: 'Hundredgige4', direction: 'egress', nextHop: '西宁', bandwidth: 100000000000 }, { originalLinkId: '1025', linkId: 'Hundredgige4', direction: 'ingress', nextHop: '西宁', bandwidth: 100000000000 }, { originalLinkId: '1280', linkId: 'Hundredgige5', direction: 'egress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '1281', linkId: 'Hundredgige5', direction: 'ingress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '1536', linkId: 'Hundredgige6', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1537', linkId: 'Hundredgige6', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1792', linkId: 'Hundredgige7', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '1793', linkId: 'Hundredgige7', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2048', linkId: 'Hundredgige8', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2049', linkId: 'Hundredgige8', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2304', linkId: 'Hundredgige9', direction: 'egress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2305', linkId: 'Hundredgige9', direction: 'ingress', nextHop: '太原', bandwidth: 100000000000 }, { originalLinkId: '2816', linkId: 'Hundredgige10', direction: 'egress', nextHop: '西安', bandwidth: 100000000000 }, { originalLinkId: '2817', linkId: 'Hundredgige10', direction: 'ingress', nextHop: '西安', bandwidth: 100000000000 }]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,389 @@
export const mockData = {
common: {
data1: {
data: {
status: 200,
code: 200,
queryKey: '87649896d5b7547a0128eb47c2d40853',
success: true,
message: null,
statistics: {
elapsed: 4,
rows_read: 1033063,
bytes_read: 8618120,
result_size: 13807,
result_rows: 101
},
job: null,
formatType: 'json',
meta: [
{
name: 'stat_time',
type: 'long',
category: 'Dimension'
}, {
name: 'egress_bytes',
type: 'long',
category: 'Metric'
}, {
name: 'ingress_bytes',
type: 'long',
category: 'Metric'
}, {
name: 'egress_pkt',
type: 'long',
category: 'Metric'
}, {
name: 'ingress_pkt',
type: 'long',
category: 'Metric'
}, {
name: 'bytes_total',
type: 'long',
category: 'Metric'
}, {
name: 'packets_total',
type: 'long',
category: 'Metric'
}],
data: {
resultType: 'object',
result: [
{
type: 'bytes',
ingressBitsRate: {
values: [['1675388124', '0'], ['1675388160', '4253144'], ['1675388196', '470709.36'], ['1675388232', '0'], ['1675388268', '148192.24'], ['1675388304', '0'], ['1675388340', '577072.48'], ['1675388376', '160431.12'], ['1675388412', '0'], ['1675388448', '1639602.64'], ['1675388484', '0'], ['1675388520', '2074017.36'], ['1675388556', '798924.64'], ['1675388592', '0'], ['1675388628', '884606'], ['1675388664', '0'], ['1675388700', '1373220.48'], ['1675388736', '21305.36'], ['1675388772', '0'], ['1675388808', '1398940.24'], ['1675388844', '0'], ['1675388880', '4474519.52'], ['1675388916', '383473.12'], ['1675388952', '0'], ['1675388988', '582813.36'], ['1675389024', '0'], ['1675389060', '3545078.24'], ['1675389096', '766672'], ['1675389132', '0'], ['1675389168', '2877694'], ['1675389204', '0'], ['1675389240', '2195562.48'], ['1675389276', '2268681.12'], ['1675389312', '0'], ['1675389348', '1438248.88'], ['1675389384', '0'], ['1675389420', '88914.24'], ['1675389456', '363776.24'], ['1675389492', '0'], ['1675389528', '103931.36'], ['1675389564', '0'], ['1675389600', '930861.12'], ['1675389636', '473982.48'], ['1675389672', '0'], ['1675389708', '210004.88'], ['1675389744', '0'], ['1675389780', '764367.52'], ['1675389816', '236745.52'], ['1675389852', '0'], ['1675389888', '199598'], ['1675389924', '0'], ['1675389960', '385563.36'], ['1675389996', '353556.64'], ['1675390032', '0'], ['1675390068', '380246.48'], ['1675390104', '0'], ['1675390140', '456284'], ['1675390176', '2560143.76'], ['1675390212', '0'], ['1675390248', '3806779.36'], ['1675390284', '0'], ['1675390320', '382829.52'], ['1675390356', '225432.48'], ['1675390392', '0'], ['1675390428', '1024283.52'], ['1675390464', '0'], ['1675390500', '494194.88'], ['1675390536', '718094.88'], ['1675390572', '0'], ['1675390608', '373146.64'], ['1675390644', '0'], ['1675390680', '101628.64'], ['1675390716', '977218'], ['1675390752', '0'], ['1675390788', '7263452.48'], ['1675390824', '0'], ['1675390860', '2613115.76'], ['1675390896', '2987862'], ['1675390932', '0'], ['1675390968', '1376989.36'], ['1675391004', '0'], ['1675391040', '2245663.12'], ['1675391076', '602240.64'], ['1675391112', '0'], ['1675391148', '437569.12'], ['1675391184', '0'], ['1675391220', '115680.88'], ['1675391256', '42582.24'], ['1675391292', '0'], ['1675391328', '42002'], ['1675391364', '0'], ['1675391400', '720716'], ['1675391436', '5382.88'], ['1675391472', '0'], ['1675391508', '169074.64'], ['1675391544', '0'], ['1675391580', '1214277.76'], ['1675391616', '1523679.76'], ['1675391652', '0'], ['1675391688', '6382256'], ['1675391724', '0']],
analysis: {
avg: '756870.4',
max: '7263452.48',
min: '0',
p95: '3545078.24'
}
},
totalBitsRate: {
values: [['1675388124', '0'], ['1675388160', '17581092.48'], ['1675388196', '9918148.64'], ['1675388232', '0'], ['1675388268', '13213336.64'], ['1675388304', '0'], ['1675388340', '41123655.76'], ['1675388376', '14569548.64'], ['1675388412', '0'], ['1675388448', '39157977.52'], ['1675388484', '0'], ['1675388520', '15875735.76'], ['1675388556', '10885102.24'], ['1675388592', '0'], ['1675388628', '8943487.12'], ['1675388664', '0'], ['1675388700', '18985121.76'], ['1675388736', '7659627.36'], ['1675388772', '0'], ['1675388808', '9288266.88'], ['1675388844', '0'], ['1675388880', '15999674.24'], ['1675388916', '16802700.48'], ['1675388952', '0'], ['1675388988', '17836499.52'], ['1675389024', '0'], ['1675389060', '22368492'], ['1675389096', '12570182.88'], ['1675389132', '0'], ['1675389168', '19977345.36'], ['1675389204', '0'], ['1675389240', '7747381.76'], ['1675389276', '29050230.64'], ['1675389312', '0'], ['1675389348', '5834855.36'], ['1675389384', '0'], ['1675389420', '22798420.24'], ['1675389456', '9061622.64'], ['1675389492', '0'], ['1675389528', '2391149.76'], ['1675389564', '0'], ['1675389600', '10752915.76'], ['1675389636', '72671214'], ['1675389672', '0'], ['1675389708', '23536865.76'], ['1675389744', '0'], ['1675389780', '6080925.76'], ['1675389816', '27096624.64'], ['1675389852', '0'], ['1675389888', '47355125.36'], ['1675389924', '0'], ['1675389960', '7525635.52'], ['1675389996', '12312211.76'], ['1675390032', '0'], ['1675390068', '9587065.36'], ['1675390104', '0'], ['1675390140', '7763073.36'], ['1675390176', '12692375.36'], ['1675390212', '0'], ['1675390248', '13007133.76'], ['1675390284', '0'], ['1675390320', '3241344.88'], ['1675390356', '13811041.12'], ['1675390392', '0'], ['1675390428', '38206391.36'], ['1675390464', '0'], ['1675390500', '5489114'], ['1675390536', '17855136.88'], ['1675390572', '0'], ['1675390608', '18409624.48'], ['1675390644', '0'], ['1675390680', '15132633.12'], ['1675390716', '19189583.52'], ['1675390752', '0'], ['1675390788', '16803327.36'], ['1675390824', '0'], ['1675390860', '19972804.88'], ['1675390896', '22541580.48'], ['1675390932', '0'], ['1675390968', '21671364.88'], ['1675391004', '0'], ['1675391040', '25293518.24'], ['1675391076', '34056467.52'], ['1675391112', '0'], ['1675391148', '13181229.76'], ['1675391184', '0'], ['1675391220', '12337126.88'], ['1675391256', '5989032.64'], ['1675391292', '0'], ['1675391328', '7393281.36'], ['1675391364', '0'], ['1675391400', '29175007.36'], ['1675391436', '17228925.76'], ['1675391472', '0'], ['1675391508', '7375468'], ['1675391544', '0'], ['1675391580', '16675685.76'], ['1675391616', '22908316'], ['1675391652', '0'], ['1675391688', '13867890.88'], ['1675391724', '0']],
analysis: {
avg: '10578267.2',
max: '72671214',
min: '0',
p95: '34056467.52'
}
},
egressBitsRate: {
values: [['1675388124', '0'], ['1675388160', '13327948.48'], ['1675388196', '9447439.36'], ['1675388232', '0'], ['1675388268', '13065144.48'], ['1675388304', '0'], ['1675388340', '40546583.36'], ['1675388376', '14409117.52'], ['1675388412', '0'], ['1675388448', '37518374.88'], ['1675388484', '0'], ['1675388520', '13801718.48'], ['1675388556', '10086177.52'], ['1675388592', '0'], ['1675388628', '8058881.12'], ['1675388664', '0'], ['1675388700', '17611901.36'], ['1675388736', '7638322'], ['1675388772', '0'], ['1675388808', '7889326.64'], ['1675388844', '0'], ['1675388880', '11525154.64'], ['1675388916', '16419227.36'], ['1675388952', '0'], ['1675388988', '17253686.24'], ['1675389024', '0'], ['1675389060', '18823413.76'], ['1675389096', '11803510.88'], ['1675389132', '0'], ['1675389168', '17099651.36'], ['1675389204', '0'], ['1675389240', '5551819.36'], ['1675389276', '26781549.52'], ['1675389312', '0'], ['1675389348', '4396606.48'], ['1675389384', '0'], ['1675389420', '22709506'], ['1675389456', '8697846.48'], ['1675389492', '0'], ['1675389528', '2287218.48'], ['1675389564', '0'], ['1675389600', '9822054.64'], ['1675389636', '72197231.52'], ['1675389672', '0'], ['1675389708', '23326860.88'], ['1675389744', '0'], ['1675389780', '5316558.24'], ['1675389816', '26859879.12'], ['1675389852', '0'], ['1675389888', '47155527.36'], ['1675389924', '0'], ['1675389960', '7140072.24'], ['1675389996', '11958655.12'], ['1675390032', '0'], ['1675390068', '9206818.88'], ['1675390104', '0'], ['1675390140', '7306789.36'], ['1675390176', '10132231.52'], ['1675390212', '0'], ['1675390248', '9200354.48'], ['1675390284', '0'], ['1675390320', '2858515.36'], ['1675390356', '13585608.64'], ['1675390392', '0'], ['1675390428', '37182107.76'], ['1675390464', '0'], ['1675390500', '4994919.12'], ['1675390536', '17137042'], ['1675390572', '0'], ['1675390608', '18036477.76'], ['1675390644', '0'], ['1675390680', '15031004.48'], ['1675390716', '18212365.52'], ['1675390752', '0'], ['1675390788', '9539874.88'], ['1675390824', '0'], ['1675390860', '17359689.12'], ['1675390896', '19553718.48'], ['1675390932', '0'], ['1675390968', '20294375.52'], ['1675391004', '0'], ['1675391040', '23047855.12'], ['1675391076', '33454226.88'], ['1675391112', '0'], ['1675391148', '12743660.64'], ['1675391184', '0'], ['1675391220', '12221446'], ['1675391256', '5946450.48'], ['1675391292', '0'], ['1675391328', '7351279.36'], ['1675391364', '0'], ['1675391400', '28454291.36'], ['1675391436', '17223542.88'], ['1675391472', '0'], ['1675391508', '7206393.36'], ['1675391544', '0'], ['1675391580', '15461408'], ['1675391616', '21384636.24'], ['1675391652', '0'], ['1675391688', '7485634.88'], ['1675391724', '0']],
analysis: {
avg: '9821396.8',
max: '72197231.52',
min: '0',
p95: '33454226.88'
}
}
},
{
type: 'packets',
ingressPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '382.61'], ['1675388196', '44.67'], ['1675388232', '0.00'], ['1675388268', '23.64'], ['1675388304', '0.00'], ['1675388340', '53.75'], ['1675388376', '34.89'], ['1675388412', '0.00'], ['1675388448', '155.25'], ['1675388484', '0.00'], ['1675388520', '186.17'], ['1675388556', '77.81'], ['1675388592', '0.00'], ['1675388628', '84.75'], ['1675388664', '0.00'], ['1675388700', '140.39'], ['1675388736', '9.69'], ['1675388772', '0.00'], ['1675388808', '126.83'], ['1675388844', '0.00'], ['1675388880', '398.50'], ['1675388916', '94.92'], ['1675388952', '0.00'], ['1675388988', '57.33'], ['1675389024', '0.00'], ['1675389060', '326.89'], ['1675389096', '69.06'], ['1675389132', '0.00'], ['1675389168', '253.61'], ['1675389204', '0.00'], ['1675389240', '201.11'], ['1675389276', '203.14'], ['1675389312', '0.00'], ['1675389348', '126.33'], ['1675389384', '0.00'], ['1675389420', '13.86'], ['1675389456', '34.81'], ['1675389492', '0.00'], ['1675389528', '12.47'], ['1675389564', '0.00'], ['1675389600', '107.08'], ['1675389636', '54.39'], ['1675389672', '0.00'], ['1675389708', '24.42'], ['1675389744', '0.00'], ['1675389780', '67.47'], ['1675389816', '35.94'], ['1675389852', '0.00'], ['1675389888', '22.44'], ['1675389924', '0.00'], ['1675389960', '45.64'], ['1675389996', '32.81'], ['1675390032', '0.00'], ['1675390068', '37.72'], ['1675390104', '0.00'], ['1675390140', '43.69'], ['1675390176', '228.97'], ['1675390212', '0.00'], ['1675390248', '392.69'], ['1675390284', '0.00'], ['1675390320', '55.33'], ['1675390356', '47.44'], ['1675390392', '0.00'], ['1675390428', '106.06'], ['1675390464', '0.00'], ['1675390500', '47.58'], ['1675390536', '64.56'], ['1675390572', '0.00'], ['1675390608', '35.94'], ['1675390644', '0.00'], ['1675390680', '17.31'], ['1675390716', '95.47'], ['1675390752', '0.00'], ['1675390788', '638.00'], ['1675390824', '0.00'], ['1675390860', '239.11'], ['1675390896', '290.89'], ['1675390932', '0.00'], ['1675390968', '129.31'], ['1675391004', '0.00'], ['1675391040', '208.83'], ['1675391076', '65.31'], ['1675391112', '0.00'], ['1675391148', '42.17'], ['1675391184', '0.00'], ['1675391220', '17.94'], ['1675391256', '14.33'], ['1675391292', '0.00'], ['1675391328', '6.31'], ['1675391364', '0.00'], ['1675391400', '62.83'], ['1675391436', '2.53'], ['1675391472', '0.00'], ['1675391508', '37.06'], ['1675391544', '0.00'], ['1675391580', '110.00'], ['1675391616', '148.53'], ['1675391652', '0.00'], ['1675391688', '583.64'], ['1675391724', '0.00']],
analysis: {
avg: '72.72',
max: '638.00',
min: '0.00',
p95: '326.89'
}
},
egressPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '1835.75'], ['1675388196', '865.61'], ['1675388232', '0.00'], ['1675388268', '1202.69'], ['1675388304', '0.00'], ['1675388340', '3864.69'], ['1675388376', '1428.56'], ['1675388412', '0.00'], ['1675388448', '3433.61'], ['1675388484', '0.00'], ['1675388520', '1228.06'], ['1675388556', '896.42'], ['1675388592', '0.00'], ['1675388628', '740.83'], ['1675388664', '0.00'], ['1675388700', '1576.75'], ['1675388736', '721.92'], ['1675388772', '0.00'], ['1675388808', '700.17'], ['1675388844', '0.00'], ['1675388880', '1058.28'], ['1675388916', '1487.19'], ['1675388952', '0.00'], ['1675388988', '1536.19'], ['1675389024', '0.00'], ['1675389060', '1671.50'], ['1675389096', '1083.58'], ['1675389132', '0.00'], ['1675389168', '1561.64'], ['1675389204', '0.00'], ['1675389240', '549.50'], ['1675389276', '2454.47'], ['1675389312', '0.00'], ['1675389348', '404.53'], ['1675389384', '0.00'], ['1675389420', '2011.22'], ['1675389456', '810.75'], ['1675389492', '0.00'], ['1675389528', '228.50'], ['1675389564', '0.00'], ['1675389600', '910.58'], ['1675389636', '6422.11'], ['1675389672', '0.00'], ['1675389708', '2123.44'], ['1675389744', '0.00'], ['1675389780', '481.97'], ['1675389816', '2462.33'], ['1675389852', '0.00'], ['1675389888', '4183.94'], ['1675389924', '0.00'], ['1675389960', '677.81'], ['1675389996', '1103.31'], ['1675390032', '0.00'], ['1675390068', '851.08'], ['1675390104', '0.00'], ['1675390140', '659.33'], ['1675390176', '936.56'], ['1675390212', '0.00'], ['1675390248', '835.78'], ['1675390284', '0.00'], ['1675390320', '296.28'], ['1675390356', '1205.39'], ['1675390392', '0.00'], ['1675390428', '3288.14'], ['1675390464', '0.00'], ['1675390500', '471.14'], ['1675390536', '1577.00'], ['1675390572', '0.00'], ['1675390608', '1645.36'], ['1675390644', '0.00'], ['1675390680', '1390.75'], ['1675390716', '1612.75'], ['1675390752', '0.00'], ['1675390788', '880.92'], ['1675390824', '0.00'], ['1675390860', '1573.47'], ['1675390896', '1809.56'], ['1675390932', '0.00'], ['1675390968', '1847.53'], ['1675391004', '0.00'], ['1675391040', '2160.33'], ['1675391076', '3126.36'], ['1675391112', '0.00'], ['1675391148', '1166.14'], ['1675391184', '0.00'], ['1675391220', '1166.81'], ['1675391256', '538.75'], ['1675391292', '0.00'], ['1675391328', '681.00'], ['1675391364', '0.00'], ['1675391400', '2580.83'], ['1675391436', '1693.17'], ['1675391472', '0.00'], ['1675391508', '695.89'], ['1675391544', '0.00'], ['1675391580', '1395.19'], ['1675391616', '1920.39'], ['1675391652', '0.00'], ['1675391688', '679.08'], ['1675391724', '0.00']],
analysis: {
avg: '904.03',
max: '6422.11',
min: '0.00',
p95: '3126.36'
}
},
totalPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '2218.36'], ['1675388196', '910.28'], ['1675388232', '0.00'], ['1675388268', '1226.33'], ['1675388304', '0.00'], ['1675388340', '3918.44'], ['1675388376', '1463.44'], ['1675388412', '0.00'], ['1675388448', '3588.86'], ['1675388484', '0.00'], ['1675388520', '1414.22'], ['1675388556', '974.22'], ['1675388592', '0.00'], ['1675388628', '825.58'], ['1675388664', '0.00'], ['1675388700', '1717.14'], ['1675388736', '731.61'], ['1675388772', '0.00'], ['1675388808', '827.00'], ['1675388844', '0.00'], ['1675388880', '1456.78'], ['1675388916', '1582.11'], ['1675388952', '0.00'], ['1675388988', '1593.53'], ['1675389024', '0.00'], ['1675389060', '1998.39'], ['1675389096', '1152.64'], ['1675389132', '0.00'], ['1675389168', '1815.25'], ['1675389204', '0.00'], ['1675389240', '750.61'], ['1675389276', '2657.61'], ['1675389312', '0.00'], ['1675389348', '530.86'], ['1675389384', '0.00'], ['1675389420', '2025.08'], ['1675389456', '845.56'], ['1675389492', '0.00'], ['1675389528', '240.97'], ['1675389564', '0.00'], ['1675389600', '1017.67'], ['1675389636', '6476.50'], ['1675389672', '0.00'], ['1675389708', '2147.86'], ['1675389744', '0.00'], ['1675389780', '549.44'], ['1675389816', '2498.28'], ['1675389852', '0.00'], ['1675389888', '4206.39'], ['1675389924', '0.00'], ['1675389960', '723.44'], ['1675389996', '1136.11'], ['1675390032', '0.00'], ['1675390068', '888.81'], ['1675390104', '0.00'], ['1675390140', '703.03'], ['1675390176', '1165.53'], ['1675390212', '0.00'], ['1675390248', '1228.47'], ['1675390284', '0.00'], ['1675390320', '351.61'], ['1675390356', '1252.83'], ['1675390392', '0.00'], ['1675390428', '3394.19'], ['1675390464', '0.00'], ['1675390500', '518.72'], ['1675390536', '1641.56'], ['1675390572', '0.00'], ['1675390608', '1681.31'], ['1675390644', '0.00'], ['1675390680', '1408.06'], ['1675390716', '1708.22'], ['1675390752', '0.00'], ['1675390788', '1518.92'], ['1675390824', '0.00'], ['1675390860', '1812.58'], ['1675390896', '2100.44'], ['1675390932', '0.00'], ['1675390968', '1976.83'], ['1675391004', '0.00'], ['1675391040', '2369.17'], ['1675391076', '3191.67'], ['1675391112', '0.00'], ['1675391148', '1208.31'], ['1675391184', '0.00'], ['1675391220', '1184.75'], ['1675391256', '553.08'], ['1675391292', '0.00'], ['1675391328', '687.31'], ['1675391364', '0.00'], ['1675391400', '2643.67'], ['1675391436', '1695.69'], ['1675391472', '0.00'], ['1675391508', '732.94'], ['1675391544', '0.00'], ['1675391580', '1505.19'], ['1675391616', '2068.92'], ['1675391652', '0.00'], ['1675391688', '1262.72'], ['1675391724', '0.00']],
analysis: {
avg: '976.75',
max: '6476.50',
min: '0.00',
p95: '3191.67'
}
}
}]
},
originalUrl: 'http://192.168.44.55:9999?query=SELECT%20TIME_FLOOR_WITH_FILL%28stat_time%2C%27PT36S%27%2C%27zero%27%29%20AS%20stat_time%2CSUM%28IF%28%20common_egress_link_id%20%3D%202048%2C%20traffic_outbound_byte%2C%200%29%29%20AS%20egress_bytes%2CSUM%28IF%28%20common_ingress_link_id%20%3D%202049%2C%20traffic_inbound_byte%2C%200%29%29%20AS%20ingress_bytes%2CSUM%28IF%28%20common_egress_link_id%20%3D%202048%2C%20traffic_outbound_pkt%2C%200%29%29%20AS%20egress_pkt%2CSUM%28IF%28%20common_ingress_link_id%20%3D%202049%2C%20traffic_inbound_pkt%2C%200%29%29%20AS%20ingress_pkt%2C%28egress_bytes%20%2B%20ingress_bytes%29%20AS%20bytes_total%2C%28egress_pkt%20%2B%20ingress_pkt%29%20AS%20packets_total%20FROM%20metric_link%20WHERE%20stat_time%20%3E%3D%201675388125%20AND%20stat_time%20%3C%201675391725%20%20GROUP%20BY%20stat_time%20ORDER%20BY%20bytes_total%20DESC%20&format=json&option=real-time',
msg: 'OK'
}
},
data2: {
data: {
status: 200,
code: 200,
queryKey: 'a54916e7f363fd065dfaa44ad23d31b5',
success: true,
message: null,
statistics: {
elapsed: 4,
rows_read: 10518,
bytes_read: 1062318,
result_size: 186,
result_rows: 1
},
job: null,
formatType: 'json',
meta: [
{
name: 'total_bytes',
type: 'long',
category: 'Metric'
}, {
name: 'total_packets',
type: 'long',
category: 'Metric'
}, {
name: 'establish_latency_ms',
type: 'double',
category: 'Metric'
}, {
name: 'http_response_latency',
type: 'double',
category: 'Metric'
}, {
name: 'ssl_con_latency',
type: 'double',
category: 'Metric'
}, {
name: 'tcp_lostlen_percent',
type: 'double',
category: 'Metric'
}, {
name: 'pkt_retrans_percent',
type: 'double',
category: 'Metric'
}],
data: {
resultType: 'table',
result: [{
totalBytes: '4426409159',
totalBitsRate: 9836464.8,
totalPackets: '3324302',
establishLatencyMs: 92.9489,
httpResponseLatency: 154.2884,
sslConLatency: 0,
tcpLostlenPercent: 0.0069,
pktRetransPercent: 0.0165
}]
},
originalUrl: 'http://192.168.44.55:9999?query=SELECT%20SUM%28IF%28%20common_egress_link_id%20%3D%201792%2C%20traffic_outbound_byte%2C%200%29%20%2B%20IF%28%20common_ingress_link_id%20%3D%201793%2C%20traffic_inbound_byte%2C%200%29%29%20as%20total_bytes%2CSUM%28IF%28%20common_egress_link_id%20%3D%201792%2C%20traffic_outbound_pkt%2C%200%29%20%2B%20IF%28%20common_ingress_link_id%20%3D%201793%2C%20traffic_inbound_pkt%2C%200%29%29%20AS%20total_packets%2CROUND%28AVG%28avg_establish_latency_ms%29%2C%204%29%20AS%20establish_latency_ms%2CROUND%28AVG%28avg_http_response_latency_ms%29%2C%204%29%20AS%20http_response_latency%2CROUND%28AVG%28avg_ssl_con_latency_ms%29%2C%204%29%20AS%20ssl_con_latency%2CROUND%28AVG%28tcp_lostlen_ratio%29%2C%204%29%20AS%20tcp_lostlen_percent%2CROUND%28AVG%28pkt_retrans_ratio%29%2C%204%29%20AS%20pkt_retrans_percent%20FROM%20metric_link%20WHERE%20stat_time%20%3E%3D%201675403646%20AND%20stat_time%20%3C%201675407246%20&format=json&option=real-time',
msg: 'OK'
}
}
},
empty: {
data1: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: []
},
msg: 'OK'
}
},
data2: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: []
},
msg: 'OK'
}
}
},
boundary: {
large: {
data1: {
data: {
status: 200,
code: 200,
data: {
resultType: 'object',
result: [
{
type: 'bytes',
ingressBitsRate: {
values: [['1675388124', '0'], ['1675388160', '4253144'], ['1675388196', '470709.36'], ['1675388232', '0'], ['1675388268', '148192.24'], ['1675388304', '0'], ['1675388340', '577072.48'], ['1675388376', '160431.12'], ['1675388412', '0'], ['1675388448', '1639602.64'], ['1675388484', '0'], ['1675388520', '2074017.36'], ['1675388556', '798924.64'], ['1675388592', '0'], ['1675388628', '884606'], ['1675388664', '0'], ['1675388700', '1373220.48'], ['1675388736', '21305.36'], ['1675388772', '0'], ['1675388808', '1398940.24'], ['1675388844', '0'], ['1675388880', '4474519.52'], ['1675388916', '383473.12'], ['1675388952', '0'], ['1675388988', '582813.36'], ['1675389024', '0'], ['1675389060', '3545078.24'], ['1675389096', '766672'], ['1675389132', '0'], ['1675389168', '2877694'], ['1675389204', '0'], ['1675389240', '2195562.48'], ['1675389276', '2268681.12'], ['1675389312', '0'], ['1675389348', '1438248.88'], ['1675389384', '0'], ['1675389420', '88914.24'], ['1675389456', '363776.24'], ['1675389492', '0'], ['1675389528', '103931.36'], ['1675389564', '0'], ['1675389600', '930861.12'], ['1675389636', '473982.48'], ['1675389672', '0'], ['1675389708', '210004.88'], ['1675389744', '0'], ['1675389780', '764367.52'], ['1675389816', '236745.52'], ['1675389852', '0'], ['1675389888', '199598'], ['1675389924', '0'], ['1675389960', '385563.36'], ['1675389996', '353556.64'], ['1675390032', '0'], ['1675390068', '380246.48'], ['1675390104', '0'], ['1675390140', '456284'], ['1675390176', '2560143.76'], ['1675390212', '0'], ['1675390248', '3806779.36'], ['1675390284', '0'], ['1675390320', '382829.52'], ['1675390356', '225432.48'], ['1675390392', '0'], ['1675390428', '1024283.52'], ['1675390464', '0'], ['1675390500', '494194.88'], ['1675390536', '718094.88'], ['1675390572', '0'], ['1675390608', '373146.64'], ['1675390644', '0'], ['1675390680', '101628.64'], ['1675390716', '977218'], ['1675390752', '0'], ['1675390788', '7263452.48'], ['1675390824', '0'], ['1675390860', '2613115.76'], ['1675390896', '2987862'], ['1675390932', '0'], ['1675390968', '1376989.36'], ['1675391004', '0'], ['1675391040', '2245663.12'], ['1675391076', '602240.64'], ['1675391112', '0'], ['1675391148', '437569.12'], ['1675391184', '0'], ['1675391220', '115680.88'], ['1675391256', '42582.24'], ['1675391292', '0'], ['1675391328', '42002'], ['1675391364', '0'], ['1675391400', '720716'], ['1675391436', '5382.88'], ['1675391472', '0'], ['1675391508', '169074.64'], ['1675391544', '0'], ['1675391580', '1214277.76'], ['1675391616', '1523679.76'], ['1675391652', '0'], ['1675391688', '6382256'], ['1675391724', '0']],
analysis: {
avg: '75687040000000',
max: '7263452.48',
min: '0',
p95: '3545078.24'
}
},
totalBitsRate: {
values: [['1675388124', '0'], ['1675388160', '17581092.48'], ['1675388196', '9918148.64'], ['1675388232', '0'], ['1675388268', '13213336.64'], ['1675388304', '0'], ['1675388340', '41123655.76'], ['1675388376', '14569548.64'], ['1675388412', '0'], ['1675388448', '39157977.52'], ['1675388484', '0'], ['1675388520', '15875735.76'], ['1675388556', '10885102.24'], ['1675388592', '0'], ['1675388628', '8943487.12'], ['1675388664', '0'], ['1675388700', '18985121.76'], ['1675388736', '7659627.36'], ['1675388772', '0'], ['1675388808', '9288266.88'], ['1675388844', '0'], ['1675388880', '15999674.24'], ['1675388916', '16802700.48'], ['1675388952', '0'], ['1675388988', '17836499.52'], ['1675389024', '0'], ['1675389060', '22368492'], ['1675389096', '12570182.88'], ['1675389132', '0'], ['1675389168', '19977345.36'], ['1675389204', '0'], ['1675389240', '7747381.76'], ['1675389276', '29050230.64'], ['1675389312', '0'], ['1675389348', '5834855.36'], ['1675389384', '0'], ['1675389420', '22798420.24'], ['1675389456', '9061622.64'], ['1675389492', '0'], ['1675389528', '2391149.76'], ['1675389564', '0'], ['1675389600', '10752915.76'], ['1675389636', '72671214'], ['1675389672', '0'], ['1675389708', '23536865.76'], ['1675389744', '0'], ['1675389780', '6080925.76'], ['1675389816', '27096624.64'], ['1675389852', '0'], ['1675389888', '47355125.36'], ['1675389924', '0'], ['1675389960', '7525635.52'], ['1675389996', '12312211.76'], ['1675390032', '0'], ['1675390068', '9587065.36'], ['1675390104', '0'], ['1675390140', '7763073.36'], ['1675390176', '12692375.36'], ['1675390212', '0'], ['1675390248', '13007133.76'], ['1675390284', '0'], ['1675390320', '3241344.88'], ['1675390356', '13811041.12'], ['1675390392', '0'], ['1675390428', '38206391.36'], ['1675390464', '0'], ['1675390500', '5489114'], ['1675390536', '17855136.88'], ['1675390572', '0'], ['1675390608', '18409624.48'], ['1675390644', '0'], ['1675390680', '15132633.12'], ['1675390716', '19189583.52'], ['1675390752', '0'], ['1675390788', '16803327.36'], ['1675390824', '0'], ['1675390860', '19972804.88'], ['1675390896', '22541580.48'], ['1675390932', '0'], ['1675390968', '21671364.88'], ['1675391004', '0'], ['1675391040', '25293518.24'], ['1675391076', '34056467.52'], ['1675391112', '0'], ['1675391148', '13181229.76'], ['1675391184', '0'], ['1675391220', '12337126.88'], ['1675391256', '5989032.64'], ['1675391292', '0'], ['1675391328', '7393281.36'], ['1675391364', '0'], ['1675391400', '29175007.36'], ['1675391436', '17228925.76'], ['1675391472', '0'], ['1675391508', '7375468'], ['1675391544', '0'], ['1675391580', '16675685.76'], ['1675391616', '22908316'], ['1675391652', '0'], ['1675391688', '13867890.88'], ['1675391724', '0']],
analysis: {
avg: '10578267.2',
max: '72671214',
min: '0',
p95: '34056467.52'
}
},
egressBitsRate: {
values: [['1675388124', '0'], ['1675388160', '13327948.48'], ['1675388196', '9447439.36'], ['1675388232', '0'], ['1675388268', '13065144.48'], ['1675388304', '0'], ['1675388340', '40546583.36'], ['1675388376', '14409117.52'], ['1675388412', '0'], ['1675388448', '37518374.88'], ['1675388484', '0'], ['1675388520', '13801718.48'], ['1675388556', '10086177.52'], ['1675388592', '0'], ['1675388628', '8058881.12'], ['1675388664', '0'], ['1675388700', '17611901.36'], ['1675388736', '7638322'], ['1675388772', '0'], ['1675388808', '7889326.64'], ['1675388844', '0'], ['1675388880', '11525154.64'], ['1675388916', '16419227.36'], ['1675388952', '0'], ['1675388988', '17253686.24'], ['1675389024', '0'], ['1675389060', '18823413.76'], ['1675389096', '11803510.88'], ['1675389132', '0'], ['1675389168', '17099651.36'], ['1675389204', '0'], ['1675389240', '5551819.36'], ['1675389276', '26781549.52'], ['1675389312', '0'], ['1675389348', '4396606.48'], ['1675389384', '0'], ['1675389420', '22709506'], ['1675389456', '8697846.48'], ['1675389492', '0'], ['1675389528', '2287218.48'], ['1675389564', '0'], ['1675389600', '9822054.64'], ['1675389636', '72197231.52'], ['1675389672', '0'], ['1675389708', '23326860.88'], ['1675389744', '0'], ['1675389780', '5316558.24'], ['1675389816', '26859879.12'], ['1675389852', '0'], ['1675389888', '47155527.36'], ['1675389924', '0'], ['1675389960', '7140072.24'], ['1675389996', '11958655.12'], ['1675390032', '0'], ['1675390068', '9206818.88'], ['1675390104', '0'], ['1675390140', '7306789.36'], ['1675390176', '10132231.52'], ['1675390212', '0'], ['1675390248', '9200354.48'], ['1675390284', '0'], ['1675390320', '2858515.36'], ['1675390356', '13585608.64'], ['1675390392', '0'], ['1675390428', '37182107.76'], ['1675390464', '0'], ['1675390500', '4994919.12'], ['1675390536', '17137042'], ['1675390572', '0'], ['1675390608', '18036477.76'], ['1675390644', '0'], ['1675390680', '15031004.48'], ['1675390716', '18212365.52'], ['1675390752', '0'], ['1675390788', '9539874.88'], ['1675390824', '0'], ['1675390860', '17359689.12'], ['1675390896', '19553718.48'], ['1675390932', '0'], ['1675390968', '20294375.52'], ['1675391004', '0'], ['1675391040', '23047855.12'], ['1675391076', '33454226.88'], ['1675391112', '0'], ['1675391148', '12743660.64'], ['1675391184', '0'], ['1675391220', '12221446'], ['1675391256', '5946450.48'], ['1675391292', '0'], ['1675391328', '7351279.36'], ['1675391364', '0'], ['1675391400', '28454291.36'], ['1675391436', '17223542.88'], ['1675391472', '0'], ['1675391508', '7206393.36'], ['1675391544', '0'], ['1675391580', '15461408'], ['1675391616', '21384636.24'], ['1675391652', '0'], ['1675391688', '7485634.88'], ['1675391724', '0']],
analysis: {
avg: '9821396.8',
max: '72197231.52',
min: '0',
p95: '33454226.88'
}
}
},
{
type: 'packets',
ingressPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '382.61'], ['1675388196', '44.67'], ['1675388232', '0.00'], ['1675388268', '23.64'], ['1675388304', '0.00'], ['1675388340', '53.75'], ['1675388376', '34.89'], ['1675388412', '0.00'], ['1675388448', '155.25'], ['1675388484', '0.00'], ['1675388520', '186.17'], ['1675388556', '77.81'], ['1675388592', '0.00'], ['1675388628', '84.75'], ['1675388664', '0.00'], ['1675388700', '140.39'], ['1675388736', '9.69'], ['1675388772', '0.00'], ['1675388808', '126.83'], ['1675388844', '0.00'], ['1675388880', '398.50'], ['1675388916', '94.92'], ['1675388952', '0.00'], ['1675388988', '57.33'], ['1675389024', '0.00'], ['1675389060', '326.89'], ['1675389096', '69.06'], ['1675389132', '0.00'], ['1675389168', '253.61'], ['1675389204', '0.00'], ['1675389240', '201.11'], ['1675389276', '203.14'], ['1675389312', '0.00'], ['1675389348', '126.33'], ['1675389384', '0.00'], ['1675389420', '13.86'], ['1675389456', '34.81'], ['1675389492', '0.00'], ['1675389528', '12.47'], ['1675389564', '0.00'], ['1675389600', '107.08'], ['1675389636', '54.39'], ['1675389672', '0.00'], ['1675389708', '24.42'], ['1675389744', '0.00'], ['1675389780', '67.47'], ['1675389816', '35.94'], ['1675389852', '0.00'], ['1675389888', '22.44'], ['1675389924', '0.00'], ['1675389960', '45.64'], ['1675389996', '32.81'], ['1675390032', '0.00'], ['1675390068', '37.72'], ['1675390104', '0.00'], ['1675390140', '43.69'], ['1675390176', '228.97'], ['1675390212', '0.00'], ['1675390248', '392.69'], ['1675390284', '0.00'], ['1675390320', '55.33'], ['1675390356', '47.44'], ['1675390392', '0.00'], ['1675390428', '106.06'], ['1675390464', '0.00'], ['1675390500', '47.58'], ['1675390536', '64.56'], ['1675390572', '0.00'], ['1675390608', '35.94'], ['1675390644', '0.00'], ['1675390680', '17.31'], ['1675390716', '95.47'], ['1675390752', '0.00'], ['1675390788', '638.00'], ['1675390824', '0.00'], ['1675390860', '239.11'], ['1675390896', '290.89'], ['1675390932', '0.00'], ['1675390968', '129.31'], ['1675391004', '0.00'], ['1675391040', '208.83'], ['1675391076', '65.31'], ['1675391112', '0.00'], ['1675391148', '42.17'], ['1675391184', '0.00'], ['1675391220', '17.94'], ['1675391256', '14.33'], ['1675391292', '0.00'], ['1675391328', '6.31'], ['1675391364', '0.00'], ['1675391400', '62.83'], ['1675391436', '2.53'], ['1675391472', '0.00'], ['1675391508', '37.06'], ['1675391544', '0.00'], ['1675391580', '110.00'], ['1675391616', '148.53'], ['1675391652', '0.00'], ['1675391688', '583.64'], ['1675391724', '0.00']],
analysis: {
avg: '727200000',
max: '638.00',
min: '0.00',
p95: '326.89'
}
},
egressPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '1835.75'], ['1675388196', '865.61'], ['1675388232', '0.00'], ['1675388268', '1202.69'], ['1675388304', '0.00'], ['1675388340', '3864.69'], ['1675388376', '1428.56'], ['1675388412', '0.00'], ['1675388448', '3433.61'], ['1675388484', '0.00'], ['1675388520', '1228.06'], ['1675388556', '896.42'], ['1675388592', '0.00'], ['1675388628', '740.83'], ['1675388664', '0.00'], ['1675388700', '1576.75'], ['1675388736', '721.92'], ['1675388772', '0.00'], ['1675388808', '700.17'], ['1675388844', '0.00'], ['1675388880', '1058.28'], ['1675388916', '1487.19'], ['1675388952', '0.00'], ['1675388988', '1536.19'], ['1675389024', '0.00'], ['1675389060', '1671.50'], ['1675389096', '1083.58'], ['1675389132', '0.00'], ['1675389168', '1561.64'], ['1675389204', '0.00'], ['1675389240', '549.50'], ['1675389276', '2454.47'], ['1675389312', '0.00'], ['1675389348', '404.53'], ['1675389384', '0.00'], ['1675389420', '2011.22'], ['1675389456', '810.75'], ['1675389492', '0.00'], ['1675389528', '228.50'], ['1675389564', '0.00'], ['1675389600', '910.58'], ['1675389636', '6422.11'], ['1675389672', '0.00'], ['1675389708', '2123.44'], ['1675389744', '0.00'], ['1675389780', '481.97'], ['1675389816', '2462.33'], ['1675389852', '0.00'], ['1675389888', '4183.94'], ['1675389924', '0.00'], ['1675389960', '677.81'], ['1675389996', '1103.31'], ['1675390032', '0.00'], ['1675390068', '851.08'], ['1675390104', '0.00'], ['1675390140', '659.33'], ['1675390176', '936.56'], ['1675390212', '0.00'], ['1675390248', '835.78'], ['1675390284', '0.00'], ['1675390320', '296.28'], ['1675390356', '1205.39'], ['1675390392', '0.00'], ['1675390428', '3288.14'], ['1675390464', '0.00'], ['1675390500', '471.14'], ['1675390536', '1577.00'], ['1675390572', '0.00'], ['1675390608', '1645.36'], ['1675390644', '0.00'], ['1675390680', '1390.75'], ['1675390716', '1612.75'], ['1675390752', '0.00'], ['1675390788', '880.92'], ['1675390824', '0.00'], ['1675390860', '1573.47'], ['1675390896', '1809.56'], ['1675390932', '0.00'], ['1675390968', '1847.53'], ['1675391004', '0.00'], ['1675391040', '2160.33'], ['1675391076', '3126.36'], ['1675391112', '0.00'], ['1675391148', '1166.14'], ['1675391184', '0.00'], ['1675391220', '1166.81'], ['1675391256', '538.75'], ['1675391292', '0.00'], ['1675391328', '681.00'], ['1675391364', '0.00'], ['1675391400', '2580.83'], ['1675391436', '1693.17'], ['1675391472', '0.00'], ['1675391508', '695.89'], ['1675391544', '0.00'], ['1675391580', '1395.19'], ['1675391616', '1920.39'], ['1675391652', '0.00'], ['1675391688', '679.08'], ['1675391724', '0.00']],
analysis: {
avg: '904030000000',
max: '6422.11',
min: '0.00',
p95: '3126.36'
}
},
totalPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '2218.36'], ['1675388196', '910.28'], ['1675388232', '0.00'], ['1675388268', '1226.33'], ['1675388304', '0.00'], ['1675388340', '3918.44'], ['1675388376', '1463.44'], ['1675388412', '0.00'], ['1675388448', '3588.86'], ['1675388484', '0.00'], ['1675388520', '1414.22'], ['1675388556', '974.22'], ['1675388592', '0.00'], ['1675388628', '825.58'], ['1675388664', '0.00'], ['1675388700', '1717.14'], ['1675388736', '731.61'], ['1675388772', '0.00'], ['1675388808', '827.00'], ['1675388844', '0.00'], ['1675388880', '1456.78'], ['1675388916', '1582.11'], ['1675388952', '0.00'], ['1675388988', '1593.53'], ['1675389024', '0.00'], ['1675389060', '1998.39'], ['1675389096', '1152.64'], ['1675389132', '0.00'], ['1675389168', '1815.25'], ['1675389204', '0.00'], ['1675389240', '750.61'], ['1675389276', '2657.61'], ['1675389312', '0.00'], ['1675389348', '530.86'], ['1675389384', '0.00'], ['1675389420', '2025.08'], ['1675389456', '845.56'], ['1675389492', '0.00'], ['1675389528', '240.97'], ['1675389564', '0.00'], ['1675389600', '1017.67'], ['1675389636', '6476.50'], ['1675389672', '0.00'], ['1675389708', '2147.86'], ['1675389744', '0.00'], ['1675389780', '549.44'], ['1675389816', '2498.28'], ['1675389852', '0.00'], ['1675389888', '4206.39'], ['1675389924', '0.00'], ['1675389960', '723.44'], ['1675389996', '1136.11'], ['1675390032', '0.00'], ['1675390068', '888.81'], ['1675390104', '0.00'], ['1675390140', '703.03'], ['1675390176', '1165.53'], ['1675390212', '0.00'], ['1675390248', '1228.47'], ['1675390284', '0.00'], ['1675390320', '351.61'], ['1675390356', '1252.83'], ['1675390392', '0.00'], ['1675390428', '3394.19'], ['1675390464', '0.00'], ['1675390500', '518.72'], ['1675390536', '1641.56'], ['1675390572', '0.00'], ['1675390608', '1681.31'], ['1675390644', '0.00'], ['1675390680', '1408.06'], ['1675390716', '1708.22'], ['1675390752', '0.00'], ['1675390788', '1518.92'], ['1675390824', '0.00'], ['1675390860', '1812.58'], ['1675390896', '2100.44'], ['1675390932', '0.00'], ['1675390968', '1976.83'], ['1675391004', '0.00'], ['1675391040', '2369.17'], ['1675391076', '3191.67'], ['1675391112', '0.00'], ['1675391148', '1208.31'], ['1675391184', '0.00'], ['1675391220', '1184.75'], ['1675391256', '553.08'], ['1675391292', '0.00'], ['1675391328', '687.31'], ['1675391364', '0.00'], ['1675391400', '2643.67'], ['1675391436', '1695.69'], ['1675391472', '0.00'], ['1675391508', '732.94'], ['1675391544', '0.00'], ['1675391580', '1505.19'], ['1675391616', '2068.92'], ['1675391652', '0.00'], ['1675391688', '1262.72'], ['1675391724', '0.00']],
analysis: {
avg: '976750000000',
max: '6476.50',
min: '0.00',
p95: '3191.67'
}
}
}]
}
}
},
data2: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: [{
totalBytes: '4426409159',
totalBitsRate: 983646480,
totalPackets: '3324302',
establishLatencyMs: 9294.89,
httpResponseLatency: 154.2884,
sslConLatency: 90,
tcpLostlenPercent: 0.69,
pktRetransPercent: 0.165
}]
}
}
}
},
zero: {
data1: {
data: {
status: 200,
code: 200,
data: {
resultType: 'object',
result: [
{
type: 'bytes',
ingressBitsRate: {
values: [['1675388124', '0'], ['1675388160', '4253144'], ['1675388196', '470709.36'], ['1675388232', '0'], ['1675388268', '148192.24'], ['1675388304', '0'], ['1675388340', '577072.48'], ['1675388376', '160431.12'], ['1675388412', '0'], ['1675388448', '1639602.64'], ['1675388484', '0'], ['1675388520', '2074017.36'], ['1675388556', '798924.64'], ['1675388592', '0'], ['1675388628', '884606'], ['1675388664', '0'], ['1675388700', '1373220.48'], ['1675388736', '21305.36'], ['1675388772', '0'], ['1675388808', '1398940.24'], ['1675388844', '0'], ['1675388880', '4474519.52'], ['1675388916', '383473.12'], ['1675388952', '0'], ['1675388988', '582813.36'], ['1675389024', '0'], ['1675389060', '3545078.24'], ['1675389096', '766672'], ['1675389132', '0'], ['1675389168', '2877694'], ['1675389204', '0'], ['1675389240', '2195562.48'], ['1675389276', '2268681.12'], ['1675389312', '0'], ['1675389348', '1438248.88'], ['1675389384', '0'], ['1675389420', '88914.24'], ['1675389456', '363776.24'], ['1675389492', '0'], ['1675389528', '103931.36'], ['1675389564', '0'], ['1675389600', '930861.12'], ['1675389636', '473982.48'], ['1675389672', '0'], ['1675389708', '210004.88'], ['1675389744', '0'], ['1675389780', '764367.52'], ['1675389816', '236745.52'], ['1675389852', '0'], ['1675389888', '199598'], ['1675389924', '0'], ['1675389960', '385563.36'], ['1675389996', '353556.64'], ['1675390032', '0'], ['1675390068', '380246.48'], ['1675390104', '0'], ['1675390140', '456284'], ['1675390176', '2560143.76'], ['1675390212', '0'], ['1675390248', '3806779.36'], ['1675390284', '0'], ['1675390320', '382829.52'], ['1675390356', '225432.48'], ['1675390392', '0'], ['1675390428', '1024283.52'], ['1675390464', '0'], ['1675390500', '494194.88'], ['1675390536', '718094.88'], ['1675390572', '0'], ['1675390608', '373146.64'], ['1675390644', '0'], ['1675390680', '101628.64'], ['1675390716', '977218'], ['1675390752', '0'], ['1675390788', '7263452.48'], ['1675390824', '0'], ['1675390860', '2613115.76'], ['1675390896', '2987862'], ['1675390932', '0'], ['1675390968', '1376989.36'], ['1675391004', '0'], ['1675391040', '2245663.12'], ['1675391076', '602240.64'], ['1675391112', '0'], ['1675391148', '437569.12'], ['1675391184', '0'], ['1675391220', '115680.88'], ['1675391256', '42582.24'], ['1675391292', '0'], ['1675391328', '42002'], ['1675391364', '0'], ['1675391400', '720716'], ['1675391436', '5382.88'], ['1675391472', '0'], ['1675391508', '169074.64'], ['1675391544', '0'], ['1675391580', '1214277.76'], ['1675391616', '1523679.76'], ['1675391652', '0'], ['1675391688', '6382256'], ['1675391724', '0']],
analysis: {
avg: '0.00',
max: '0.00',
min: '0',
p95: '3545078.24'
}
},
totalBitsRate: {
values: [['1675388124', '0'], ['1675388160', '17581092.48'], ['1675388196', '9918148.64'], ['1675388232', '0'], ['1675388268', '13213336.64'], ['1675388304', '0'], ['1675388340', '41123655.76'], ['1675388376', '14569548.64'], ['1675388412', '0'], ['1675388448', '39157977.52'], ['1675388484', '0'], ['1675388520', '15875735.76'], ['1675388556', '10885102.24'], ['1675388592', '0'], ['1675388628', '8943487.12'], ['1675388664', '0'], ['1675388700', '18985121.76'], ['1675388736', '7659627.36'], ['1675388772', '0'], ['1675388808', '9288266.88'], ['1675388844', '0'], ['1675388880', '15999674.24'], ['1675388916', '16802700.48'], ['1675388952', '0'], ['1675388988', '17836499.52'], ['1675389024', '0'], ['1675389060', '22368492'], ['1675389096', '12570182.88'], ['1675389132', '0'], ['1675389168', '19977345.36'], ['1675389204', '0'], ['1675389240', '7747381.76'], ['1675389276', '29050230.64'], ['1675389312', '0'], ['1675389348', '5834855.36'], ['1675389384', '0'], ['1675389420', '22798420.24'], ['1675389456', '9061622.64'], ['1675389492', '0'], ['1675389528', '2391149.76'], ['1675389564', '0'], ['1675389600', '10752915.76'], ['1675389636', '72671214'], ['1675389672', '0'], ['1675389708', '23536865.76'], ['1675389744', '0'], ['1675389780', '6080925.76'], ['1675389816', '27096624.64'], ['1675389852', '0'], ['1675389888', '47355125.36'], ['1675389924', '0'], ['1675389960', '7525635.52'], ['1675389996', '12312211.76'], ['1675390032', '0'], ['1675390068', '9587065.36'], ['1675390104', '0'], ['1675390140', '7763073.36'], ['1675390176', '12692375.36'], ['1675390212', '0'], ['1675390248', '13007133.76'], ['1675390284', '0'], ['1675390320', '3241344.88'], ['1675390356', '13811041.12'], ['1675390392', '0'], ['1675390428', '38206391.36'], ['1675390464', '0'], ['1675390500', '5489114'], ['1675390536', '17855136.88'], ['1675390572', '0'], ['1675390608', '18409624.48'], ['1675390644', '0'], ['1675390680', '15132633.12'], ['1675390716', '19189583.52'], ['1675390752', '0'], ['1675390788', '16803327.36'], ['1675390824', '0'], ['1675390860', '19972804.88'], ['1675390896', '22541580.48'], ['1675390932', '0'], ['1675390968', '21671364.88'], ['1675391004', '0'], ['1675391040', '25293518.24'], ['1675391076', '34056467.52'], ['1675391112', '0'], ['1675391148', '13181229.76'], ['1675391184', '0'], ['1675391220', '12337126.88'], ['1675391256', '5989032.64'], ['1675391292', '0'], ['1675391328', '7393281.36'], ['1675391364', '0'], ['1675391400', '29175007.36'], ['1675391436', '17228925.76'], ['1675391472', '0'], ['1675391508', '7375468'], ['1675391544', '0'], ['1675391580', '16675685.76'], ['1675391616', '22908316'], ['1675391652', '0'], ['1675391688', '13867890.88'], ['1675391724', '0']],
analysis: {
avg: '0.00',
max: '0.00',
min: '0',
p95: '34056467.52'
}
},
egressBitsRate: {
values: [['1675388124', '0'], ['1675388160', '13327948.48'], ['1675388196', '9447439.36'], ['1675388232', '0'], ['1675388268', '13065144.48'], ['1675388304', '0'], ['1675388340', '40546583.36'], ['1675388376', '14409117.52'], ['1675388412', '0'], ['1675388448', '37518374.88'], ['1675388484', '0'], ['1675388520', '13801718.48'], ['1675388556', '10086177.52'], ['1675388592', '0'], ['1675388628', '8058881.12'], ['1675388664', '0'], ['1675388700', '17611901.36'], ['1675388736', '7638322'], ['1675388772', '0'], ['1675388808', '7889326.64'], ['1675388844', '0'], ['1675388880', '11525154.64'], ['1675388916', '16419227.36'], ['1675388952', '0'], ['1675388988', '17253686.24'], ['1675389024', '0'], ['1675389060', '18823413.76'], ['1675389096', '11803510.88'], ['1675389132', '0'], ['1675389168', '17099651.36'], ['1675389204', '0'], ['1675389240', '5551819.36'], ['1675389276', '26781549.52'], ['1675389312', '0'], ['1675389348', '4396606.48'], ['1675389384', '0'], ['1675389420', '22709506'], ['1675389456', '8697846.48'], ['1675389492', '0'], ['1675389528', '2287218.48'], ['1675389564', '0'], ['1675389600', '9822054.64'], ['1675389636', '72197231.52'], ['1675389672', '0'], ['1675389708', '23326860.88'], ['1675389744', '0'], ['1675389780', '5316558.24'], ['1675389816', '26859879.12'], ['1675389852', '0'], ['1675389888', '47155527.36'], ['1675389924', '0'], ['1675389960', '7140072.24'], ['1675389996', '11958655.12'], ['1675390032', '0'], ['1675390068', '9206818.88'], ['1675390104', '0'], ['1675390140', '7306789.36'], ['1675390176', '10132231.52'], ['1675390212', '0'], ['1675390248', '9200354.48'], ['1675390284', '0'], ['1675390320', '2858515.36'], ['1675390356', '13585608.64'], ['1675390392', '0'], ['1675390428', '37182107.76'], ['1675390464', '0'], ['1675390500', '4994919.12'], ['1675390536', '17137042'], ['1675390572', '0'], ['1675390608', '18036477.76'], ['1675390644', '0'], ['1675390680', '15031004.48'], ['1675390716', '18212365.52'], ['1675390752', '0'], ['1675390788', '9539874.88'], ['1675390824', '0'], ['1675390860', '17359689.12'], ['1675390896', '19553718.48'], ['1675390932', '0'], ['1675390968', '20294375.52'], ['1675391004', '0'], ['1675391040', '23047855.12'], ['1675391076', '33454226.88'], ['1675391112', '0'], ['1675391148', '12743660.64'], ['1675391184', '0'], ['1675391220', '12221446'], ['1675391256', '5946450.48'], ['1675391292', '0'], ['1675391328', '7351279.36'], ['1675391364', '0'], ['1675391400', '28454291.36'], ['1675391436', '17223542.88'], ['1675391472', '0'], ['1675391508', '7206393.36'], ['1675391544', '0'], ['1675391580', '15461408'], ['1675391616', '21384636.24'], ['1675391652', '0'], ['1675391688', '7485634.88'], ['1675391724', '0']],
analysis: {
avg: '0.00',
max: '0.00',
min: '0',
p95: '33454226.88'
}
}
},
{
type: 'packets',
ingressPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '382.61'], ['1675388196', '44.67'], ['1675388232', '0.00'], ['1675388268', '23.64'], ['1675388304', '0.00'], ['1675388340', '53.75'], ['1675388376', '34.89'], ['1675388412', '0.00'], ['1675388448', '155.25'], ['1675388484', '0.00'], ['1675388520', '186.17'], ['1675388556', '77.81'], ['1675388592', '0.00'], ['1675388628', '84.75'], ['1675388664', '0.00'], ['1675388700', '140.39'], ['1675388736', '9.69'], ['1675388772', '0.00'], ['1675388808', '126.83'], ['1675388844', '0.00'], ['1675388880', '398.50'], ['1675388916', '94.92'], ['1675388952', '0.00'], ['1675388988', '57.33'], ['1675389024', '0.00'], ['1675389060', '326.89'], ['1675389096', '69.06'], ['1675389132', '0.00'], ['1675389168', '253.61'], ['1675389204', '0.00'], ['1675389240', '201.11'], ['1675389276', '203.14'], ['1675389312', '0.00'], ['1675389348', '126.33'], ['1675389384', '0.00'], ['1675389420', '13.86'], ['1675389456', '34.81'], ['1675389492', '0.00'], ['1675389528', '12.47'], ['1675389564', '0.00'], ['1675389600', '107.08'], ['1675389636', '54.39'], ['1675389672', '0.00'], ['1675389708', '24.42'], ['1675389744', '0.00'], ['1675389780', '67.47'], ['1675389816', '35.94'], ['1675389852', '0.00'], ['1675389888', '22.44'], ['1675389924', '0.00'], ['1675389960', '45.64'], ['1675389996', '32.81'], ['1675390032', '0.00'], ['1675390068', '37.72'], ['1675390104', '0.00'], ['1675390140', '43.69'], ['1675390176', '228.97'], ['1675390212', '0.00'], ['1675390248', '392.69'], ['1675390284', '0.00'], ['1675390320', '55.33'], ['1675390356', '47.44'], ['1675390392', '0.00'], ['1675390428', '106.06'], ['1675390464', '0.00'], ['1675390500', '47.58'], ['1675390536', '64.56'], ['1675390572', '0.00'], ['1675390608', '35.94'], ['1675390644', '0.00'], ['1675390680', '17.31'], ['1675390716', '95.47'], ['1675390752', '0.00'], ['1675390788', '638.00'], ['1675390824', '0.00'], ['1675390860', '239.11'], ['1675390896', '290.89'], ['1675390932', '0.00'], ['1675390968', '129.31'], ['1675391004', '0.00'], ['1675391040', '208.83'], ['1675391076', '65.31'], ['1675391112', '0.00'], ['1675391148', '42.17'], ['1675391184', '0.00'], ['1675391220', '17.94'], ['1675391256', '14.33'], ['1675391292', '0.00'], ['1675391328', '6.31'], ['1675391364', '0.00'], ['1675391400', '62.83'], ['1675391436', '2.53'], ['1675391472', '0.00'], ['1675391508', '37.06'], ['1675391544', '0.00'], ['1675391580', '110.00'], ['1675391616', '148.53'], ['1675391652', '0.00'], ['1675391688', '583.64'], ['1675391724', '0.00']],
analysis: {
avg: '0.00',
max: '0.00',
min: '0.00',
p95: '326.89'
}
},
egressPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '1835.75'], ['1675388196', '865.61'], ['1675388232', '0.00'], ['1675388268', '1202.69'], ['1675388304', '0.00'], ['1675388340', '3864.69'], ['1675388376', '1428.56'], ['1675388412', '0.00'], ['1675388448', '3433.61'], ['1675388484', '0.00'], ['1675388520', '1228.06'], ['1675388556', '896.42'], ['1675388592', '0.00'], ['1675388628', '740.83'], ['1675388664', '0.00'], ['1675388700', '1576.75'], ['1675388736', '721.92'], ['1675388772', '0.00'], ['1675388808', '700.17'], ['1675388844', '0.00'], ['1675388880', '1058.28'], ['1675388916', '1487.19'], ['1675388952', '0.00'], ['1675388988', '1536.19'], ['1675389024', '0.00'], ['1675389060', '1671.50'], ['1675389096', '1083.58'], ['1675389132', '0.00'], ['1675389168', '1561.64'], ['1675389204', '0.00'], ['1675389240', '549.50'], ['1675389276', '2454.47'], ['1675389312', '0.00'], ['1675389348', '404.53'], ['1675389384', '0.00'], ['1675389420', '2011.22'], ['1675389456', '810.75'], ['1675389492', '0.00'], ['1675389528', '228.50'], ['1675389564', '0.00'], ['1675389600', '910.58'], ['1675389636', '6422.11'], ['1675389672', '0.00'], ['1675389708', '2123.44'], ['1675389744', '0.00'], ['1675389780', '481.97'], ['1675389816', '2462.33'], ['1675389852', '0.00'], ['1675389888', '4183.94'], ['1675389924', '0.00'], ['1675389960', '677.81'], ['1675389996', '1103.31'], ['1675390032', '0.00'], ['1675390068', '851.08'], ['1675390104', '0.00'], ['1675390140', '659.33'], ['1675390176', '936.56'], ['1675390212', '0.00'], ['1675390248', '835.78'], ['1675390284', '0.00'], ['1675390320', '296.28'], ['1675390356', '1205.39'], ['1675390392', '0.00'], ['1675390428', '3288.14'], ['1675390464', '0.00'], ['1675390500', '471.14'], ['1675390536', '1577.00'], ['1675390572', '0.00'], ['1675390608', '1645.36'], ['1675390644', '0.00'], ['1675390680', '1390.75'], ['1675390716', '1612.75'], ['1675390752', '0.00'], ['1675390788', '880.92'], ['1675390824', '0.00'], ['1675390860', '1573.47'], ['1675390896', '1809.56'], ['1675390932', '0.00'], ['1675390968', '1847.53'], ['1675391004', '0.00'], ['1675391040', '2160.33'], ['1675391076', '3126.36'], ['1675391112', '0.00'], ['1675391148', '1166.14'], ['1675391184', '0.00'], ['1675391220', '1166.81'], ['1675391256', '538.75'], ['1675391292', '0.00'], ['1675391328', '681.00'], ['1675391364', '0.00'], ['1675391400', '2580.83'], ['1675391436', '1693.17'], ['1675391472', '0.00'], ['1675391508', '695.89'], ['1675391544', '0.00'], ['1675391580', '1395.19'], ['1675391616', '1920.39'], ['1675391652', '0.00'], ['1675391688', '679.08'], ['1675391724', '0.00']],
analysis: {
avg: '0.00',
max: '0.00',
min: '0.00',
p95: '3126.36'
}
},
totalPacketsRate: {
values: [['1675388124', '0.00'], ['1675388160', '2218.36'], ['1675388196', '910.28'], ['1675388232', '0.00'], ['1675388268', '1226.33'], ['1675388304', '0.00'], ['1675388340', '3918.44'], ['1675388376', '1463.44'], ['1675388412', '0.00'], ['1675388448', '3588.86'], ['1675388484', '0.00'], ['1675388520', '1414.22'], ['1675388556', '974.22'], ['1675388592', '0.00'], ['1675388628', '825.58'], ['1675388664', '0.00'], ['1675388700', '1717.14'], ['1675388736', '731.61'], ['1675388772', '0.00'], ['1675388808', '827.00'], ['1675388844', '0.00'], ['1675388880', '1456.78'], ['1675388916', '1582.11'], ['1675388952', '0.00'], ['1675388988', '1593.53'], ['1675389024', '0.00'], ['1675389060', '1998.39'], ['1675389096', '1152.64'], ['1675389132', '0.00'], ['1675389168', '1815.25'], ['1675389204', '0.00'], ['1675389240', '750.61'], ['1675389276', '2657.61'], ['1675389312', '0.00'], ['1675389348', '530.86'], ['1675389384', '0.00'], ['1675389420', '2025.08'], ['1675389456', '845.56'], ['1675389492', '0.00'], ['1675389528', '240.97'], ['1675389564', '0.00'], ['1675389600', '1017.67'], ['1675389636', '6476.50'], ['1675389672', '0.00'], ['1675389708', '2147.86'], ['1675389744', '0.00'], ['1675389780', '549.44'], ['1675389816', '2498.28'], ['1675389852', '0.00'], ['1675389888', '4206.39'], ['1675389924', '0.00'], ['1675389960', '723.44'], ['1675389996', '1136.11'], ['1675390032', '0.00'], ['1675390068', '888.81'], ['1675390104', '0.00'], ['1675390140', '703.03'], ['1675390176', '1165.53'], ['1675390212', '0.00'], ['1675390248', '1228.47'], ['1675390284', '0.00'], ['1675390320', '351.61'], ['1675390356', '1252.83'], ['1675390392', '0.00'], ['1675390428', '3394.19'], ['1675390464', '0.00'], ['1675390500', '518.72'], ['1675390536', '1641.56'], ['1675390572', '0.00'], ['1675390608', '1681.31'], ['1675390644', '0.00'], ['1675390680', '1408.06'], ['1675390716', '1708.22'], ['1675390752', '0.00'], ['1675390788', '1518.92'], ['1675390824', '0.00'], ['1675390860', '1812.58'], ['1675390896', '2100.44'], ['1675390932', '0.00'], ['1675390968', '1976.83'], ['1675391004', '0.00'], ['1675391040', '2369.17'], ['1675391076', '3191.67'], ['1675391112', '0.00'], ['1675391148', '1208.31'], ['1675391184', '0.00'], ['1675391220', '1184.75'], ['1675391256', '553.08'], ['1675391292', '0.00'], ['1675391328', '687.31'], ['1675391364', '0.00'], ['1675391400', '2643.67'], ['1675391436', '1695.69'], ['1675391472', '0.00'], ['1675391508', '732.94'], ['1675391544', '0.00'], ['1675391580', '1505.19'], ['1675391616', '2068.92'], ['1675391652', '0.00'], ['1675391688', '1262.72'], ['1675391724', '0.00']],
analysis: {
avg: '0.00',
max: '0.00',
min: '0.00',
p95: '3191.67'
}
}
}]
}
}
},
data2: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: [{
totalBytes: '4426409159',
totalBitsRate: 983646480,
totalPackets: '3324302',
establishLatencyMs: 9294.89,
httpResponseLatency: 154.2884,
sslConLatency: 90,
tcpLostlenPercent: 0.69,
pktRetransPercent: 0.165
}]
}
}
}
}
}
}

View File

@@ -0,0 +1,450 @@
import NetworkOverviewApps from '@/views/charts2/charts/networkOverview/NetworkOverviewApps'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import ElementPlus from 'element-plus'
import { mockData } from './mockData/NetworkOverviewApps'
const chart = mockData.common.chart
const linkInfo = 5
const timeFilter = {
dateRangeValue: -1,
startTime: 1675180800,
endTime: 1675353599
}
let wrapper = null
/**
* 进行axios请求并挂载vue实例
* 模拟localStorage获取数据
* ElementPlus
* @param Metric
*/
function axiosPostAndMounted (Metric, flag, data) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// eslint-disable-next-line no-proto
jest.spyOn(localStorage.__proto__, 'getItem').mockImplementation(() => linkInfo)
if (flag === 'empty') {
for (let i = 0; i < 8; i++) {
axios.get.mockResolvedValueOnce(data)
}
} else if (flag === 'boundary') {
data.forEach(item => {
axios.get.mockResolvedValueOnce(item)
})
} else {
mockData.common.mockGet.forEach(item => {
axios.get.mockResolvedValueOnce(item)
})
}
wrapper = mount(NetworkOverviewApps, {
propsData: {
timeFilter,
chart,
metric: Metric
},
global: {
plugins: [ElementPlus]
}
})
}
describe('views/charts2/charts/networkOverview/NetworkOverviewApps.vue测试', () => {
test('Providers and Applications 列表信息 Metric:Bits/s', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(async () => {
const testName0 = wrapper.get('[test-id="name0"]')
const testName1 = wrapper.get('[test-id="name1"]')
const testName2 = wrapper.get('[test-id="name6"]')
const testName3 = wrapper.get('[test-id="name7"]')
expect(testName0.text()).toEqual('douyin')
expect(testName1.text()).toEqual('kuaishou')
expect(testName2.text()).toEqual('Alibaba')
expect(testName3.text()).toEqual('Baidu')
//
const testIcon0 = wrapper.get('[test-id="icon0"]')
const testIcon1 = wrapper.get('[test-id="icon1"]')
const testIcon2 = wrapper.get('[test-id="icon6"]')
const testIcon3 = wrapper.get('[test-id="icon7"]')
expect(testIcon0.classes()).toContain('cn-icon-app2')
expect(testIcon1.classes()).toContain('cn-icon-app2')
expect(testIcon2.classes()).toContain('cn-icon-entity')
expect(testIcon3.classes()).toContain('cn-icon-entity')
//
const testrate0 = wrapper.get('[test-id="rate0"]')
const testrate1 = wrapper.get('[test-id="rate1"]')
const testrate2 = wrapper.get('[test-id="rate6"]')
const testrate3 = wrapper.get('[test-id="rate7"]')
expect(testrate0.text()).toEqual('34.15 M')
expect(testrate1.text()).toEqual('9.18 M')
expect(testrate2.text()).toEqual('4.74 M')
expect(testrate3.text()).toEqual('1.27 M')
const testPercent0 = wrapper.get('[test-id="percent0"]')
const testPercent1 = wrapper.get('[test-id="percent3"]')
const testPercent2 = wrapper.get('[test-id="percent6"]')
const testPercent3 = wrapper.get('[test-id="percent7"]')
expect(testPercent0.text()).toEqual('>500.00%')
expect(testPercent1.text()).toEqual('-9.82%')
expect(testPercent2.text()).toEqual('+23.87%')
expect(testPercent3.text()).toEqual('+16.63%')
const testTotal0 = wrapper.get('[test-id="total0"]')
const testTotal1 = wrapper.get('[test-id="total1"]')
const testTotal2 = wrapper.get('[test-id="total6"]')
const testTotal3 = wrapper.get('[test-id="total7"]')
expect(testTotal0.text()).toEqual('686.94 GB')
expect(testTotal1.text()).toEqual('184.70 GB')
expect(testTotal2.text()).toEqual('95.26 GB')
expect(testTotal3.text()).toEqual('25.53 GB')
// 模拟点击 add 新增
const testAdd0 = wrapper.find('[test-id="add"]')
await testAdd0.trigger('click')
expect(wrapper.vm.showAddApp).toBe(true)
await wrapper.vm.$nextTick()
// element drawer
// add provide 列表 图标
// const testProvideIcon0 = wrapper.get('[test-id="provide-icon0"]')
// const testAPPIcon0 = wrapper.get('[test-id="app-icon0"]')
const testProvide0 = wrapper.find('[test-id="provide0"]')
await testProvide0.trigger('click')
expect(testProvide0.classes()).toContain('provide-show')
const testProvideTitle0 = wrapper.get('[test-id="provide-title0"]')
expect(testProvideTitle0.text()).toEqual('Jingdong')
const testProvideRemark0 = wrapper.get('[test-id="provide-remark0"]')
expect(testProvideRemark0.text()).toEqual('JD.com, Inc., also known as Jingdong and formerly called 360buy, is a Chinese e-commerce company headquartered in Beijing. It is one of the two massive B2C online retailers in China by transaction volume and revenue, a member of the Fortune Global 500 and a major competitor to Alibaba-run Tmall.')
const testAppTypeTab1 = wrapper.find('[id="tab-1"]')
await testAppTypeTab1.trigger('click')
expect(parseFloat(wrapper.vm.appTypeTab)).toBe(1)
await wrapper.vm.$nextTick()
const testApp0 = wrapper.find('[test-id="app0"]')
await testApp0.trigger('click')
expect(testApp0.classes()).toContain('app-show')
const testAppTitle0 = wrapper.get('[test-id="app-title0"]')
expect(testAppTitle0.text()).toEqual('avoidr')
const testAppRemark0 = wrapper.get('[test-id="app-remark0"]')
expect(testAppRemark0.text()).toEqual('Avoidr is a free web proxy')
resolve()
}, 200))
})
test('Providers and Applications 列表信息 Metric:Packets/s', async () => {
axiosPostAndMounted('Packets/s')
await new Promise(resolve => setTimeout(async () => {
const testName0 = wrapper.get('[test-id="name0"]')
const testName1 = wrapper.get('[test-id="name1"]')
const testName2 = wrapper.get('[test-id="name6"]')
const testName3 = wrapper.get('[test-id="name7"]')
expect(testName0.text()).toEqual('douyin')
expect(testName1.text()).toEqual('kuaishou')
expect(testName2.text()).toEqual('Alibaba')
expect(testName3.text()).toEqual('Baidu')
//
const testIcon0 = wrapper.get('[test-id="icon0"]')
const testIcon1 = wrapper.get('[test-id="icon1"]')
const testIcon2 = wrapper.get('[test-id="icon6"]')
const testIcon3 = wrapper.get('[test-id="icon7"]')
const testIcon4 = wrapper.get('[test-id="icon2"]')
expect(testIcon0.classes()).toContain('cn-icon-app2')
expect(testIcon1.classes()).toContain('cn-icon-app2')
expect(testIcon4.classes()).toContain('cn-icon-app2')
expect(testIcon2.classes()).toContain('cn-icon-entity')
expect(testIcon3.classes()).toContain('cn-icon-entity')
//
const testrate0 = wrapper.get('[test-id="rate0"]')
const testrate1 = wrapper.get('[test-id="rate1"]')
const testrate2 = wrapper.get('[test-id="rate6"]')
const testrate3 = wrapper.get('[test-id="rate7"]')
const testrate4 = wrapper.get('[test-id="rate2"]')
expect(testrate0.text()).toEqual('31.33 K')
expect(testrate1.text()).toEqual('8.46 K')
expect(testrate2.text()).toEqual('5.01 K')
expect(testrate3.text()).toEqual('1.33 K')
expect(testrate4.text()).toEqual('-')
const testPercent0 = wrapper.get('[test-id="percent0"]')
const testPercent1 = wrapper.get('[test-id="percent3"]')
const testPercent2 = wrapper.get('[test-id="percent6"]')
const testPercent3 = wrapper.get('[test-id="percent7"]')
expect(testPercent0.text()).toEqual('+25.20%')
expect(testPercent1.text()).toEqual('+12.12%')
expect(testPercent2.text()).toEqual('+23.61%')
expect(testPercent3.text()).toEqual('+17.90%')
const testTotal0 = wrapper.get('[test-id="total0"]')
const testTotal1 = wrapper.get('[test-id="total1"]')
const testTotal2 = wrapper.get('[test-id="total6"]')
const testTotal3 = wrapper.get('[test-id="total7"]')
const testTotal4 = wrapper.get('[test-id="total2"]')
expect(testTotal0.text()).toEqual('676.80 M')
expect(testTotal1.text()).toEqual('182.71 M')
expect(testTotal2.text()).toEqual('108.16 M')
expect(testTotal3.text()).toEqual('28.83 M')
expect(testTotal4.text()).toEqual('-')
resolve()
}, 200))
})
test('Providers and Applications 列表信息 Metric:Sessions/s', async () => {
axiosPostAndMounted('Sessions/s')
await new Promise(resolve => setTimeout(async () => {
const testName0 = wrapper.get('[test-id="name0"]')
const testName1 = wrapper.get('[test-id="name1"]')
const testName2 = wrapper.get('[test-id="name6"]')
const testName3 = wrapper.get('[test-id="name7"]')
expect(testName0.text()).toEqual('douyin')
expect(testName1.text()).toEqual('kuaishou')
expect(testName2.text()).toEqual('Alibaba')
expect(testName3.text()).toEqual('Baidu')
//
const testIcon0 = wrapper.get('[test-id="icon0"]')
const testIcon1 = wrapper.get('[test-id="icon1"]')
const testIcon2 = wrapper.get('[test-id="icon6"]')
const testIcon3 = wrapper.get('[test-id="icon7"]')
expect(testIcon0.classes()).toContain('cn-icon-app2')
expect(testIcon1.classes()).toContain('cn-icon-app2')
expect(testIcon2.classes()).toContain('cn-icon-entity')
expect(testIcon3.classes()).toContain('cn-icon-entity')
//
const testrate0 = wrapper.get('[test-id="rate0"]')
const testrate1 = wrapper.get('[test-id="rate1"]')
const testrate2 = wrapper.get('[test-id="rate6"]')
const testrate3 = wrapper.get('[test-id="rate7"]')
expect(testrate0.text()).toEqual('30.96')
expect(testrate1.text()).toEqual('6.24')
expect(testrate2.text()).toEqual('-')
expect(testrate3.text()).toEqual('9.20')
const testPercent0 = wrapper.get('[test-id="percent0"]')
const testPercent1 = wrapper.get('[test-id="percent3"]')
const testPercent2 = wrapper.get('[test-id="percent6"]')
const testPercent3 = wrapper.get('[test-id="percent7"]')
expect(testPercent0.text()).toEqual('+23.64%')
expect(testPercent1.text()).toEqual('+100.00%')
expect(testPercent2.text()).toEqual('-100.00%')
expect(testPercent3.text()).toEqual('+25.00%')
const testTotal0 = wrapper.get('[test-id="total0"]')
const testTotal1 = wrapper.get('[test-id="total1"]')
const testTotal2 = wrapper.get('[test-id="total6"]')
const testTotal3 = wrapper.get('[test-id="total7"]')
expect(testTotal0.text()).toEqual('668.23 K')
expect(testTotal1.text()).toEqual('134.46 K')
expect(testTotal2.text()).toEqual('-')
expect(testTotal3.text()).toEqual('198.26 K')
resolve()
}, 200))
})
test('搜索功能:模糊匹配', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(async () => {
// 模拟点击 add 新增
const testAdd0 = wrapper.find('[test-id="add"]')
await testAdd0.trigger('click')
expect(wrapper.vm.showAddApp).toBe(true)
await wrapper.vm.$nextTick()
const testSearchInput = wrapper.get('[test-id="search-input"]')
await wrapper.get('input[type="text"]').setValue('sina')
await testSearchInput.trigger('input')
await testAdd0.trigger('click')
expect(wrapper.vm.searcherApp).toEqual('sina')
const testAppTypeTab0 = wrapper.find('[id="tab-0"]')
await testAppTypeTab0.trigger('click')
expect(parseFloat(wrapper.vm.appTypeTab)).toBe(0)
const testProvideTitle0 = wrapper.get('[test-id="provide-title0"]')
expect(testProvideTitle0.text()).toEqual('Sina')
const testProvideRemark0 = wrapper.get('[test-id="provide-remark0"]')
expect(testProvideRemark0.text()).toEqual('Sina Corporation is a Chinese technology company. Sina operates four major business lines: Sina Weibo, Sina Mobile, Sina Online, and Sinanet. Sina has over 100 million registered users worldwide.')
const testAppTypeTab1 = wrapper.find('[id="tab-1"]')
await testAppTypeTab1.trigger('click')
expect(parseFloat(wrapper.vm.appTypeTab)).toBe(1)
const testAppTitle0 = wrapper.get('[test-id="app-title0"]')
expect(testAppTitle0.text()).toEqual('iapp')
const testAppRemark0 = wrapper.get('[test-id="app-remark0"]')
expect(testAppRemark0.text()).toEqual('News site about mobile applications')
resolve()
}, 300))
})
test('鼠标移入、移出、点击事件', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(async () => {
// 模拟鼠标 移入 移出 显示隐藏更多 icon 图标
const testAppDataCard = wrapper.find('[test-id="app-data-card"]')
await testAppDataCard.trigger('mouseenter')
expect(wrapper.vm.appData[0].showMore).toBe(true)
await testAppDataCard.trigger('mouseleave')
expect(wrapper.vm.appData[0].showMore).toBe(false)
expect(wrapper.vm.appData[0].moreOptions).toBe(false)
// 模拟鼠标移入 移出 显示隐藏下拉内容
const testDark = wrapper.find('[test-id="mouseenter-dark"]')
await testDark.trigger('mouseenter')
expect(wrapper.vm.appData[0].moreOptions).toBe(true)
const testMouseleaveMore = wrapper.find('[test-id="mouseleave-more"]')
await testMouseleaveMore.trigger('mouseleave')
expect(wrapper.vm.appData[0].moreOptions).toBe(false)
// 模拟点击 add 新增
const testAdd0 = wrapper.find('[test-id="add"]')
await testAdd0.trigger('click')
expect(wrapper.vm.showAddApp).toBe(true)
await wrapper.vm.$nextTick()
// 模拟 cancel 取消
const testCancelAdd = wrapper.find('[test-id="cancel-app"]')
await testCancelAdd.trigger('click')
expect(wrapper.vm.showAddApp).toBe(false)
resolve()
}, 500))
})
test('Providers and Applications 列表信息 Metric:Bits/s无数据', async () => {
axiosPostAndMounted('Bits/s', 'empty', mockData.empty)
await new Promise(resolve => setTimeout(async () => {
const testName0 = wrapper.get('[test-id="name0"]')
const testName1 = wrapper.get('[test-id="name1"]')
const testName2 = wrapper.get('[test-id="name6"]')
const testName3 = wrapper.get('[test-id="name7"]')
expect(testName0.text()).toEqual('douyin')
expect(testName1.text()).toEqual('kuaishou')
expect(testName2.text()).toEqual('Alibaba')
expect(testName3.text()).toEqual('Baidu')
const testIcon0 = wrapper.get('[test-id="icon0"]')
const testIcon1 = wrapper.get('[test-id="icon1"]')
const testIcon2 = wrapper.get('[test-id="icon6"]')
const testIcon3 = wrapper.get('[test-id="icon7"]')
expect(testIcon0.classes()).toContain('cn-icon-app2')
expect(testIcon1.classes()).toContain('cn-icon-app2')
expect(testIcon2.classes()).toContain('cn-icon-entity')
expect(testIcon3.classes()).toContain('cn-icon-entity')
//
const testrate0 = wrapper.get('[test-id="rate0"]')
const testrate1 = wrapper.get('[test-id="rate1"]')
const testrate2 = wrapper.get('[test-id="rate6"]')
const testrate3 = wrapper.get('[test-id="rate7"]')
expect(testrate0.text()).toEqual('-')
expect(testrate1.text()).toEqual('-')
expect(testrate2.text()).toEqual('-')
expect(testrate3.text()).toEqual('-')
const testTotal0 = wrapper.get('[test-id="total0"]')
const testTotal1 = wrapper.get('[test-id="total1"]')
const testTotal2 = wrapper.get('[test-id="total6"]')
const testTotal3 = wrapper.get('[test-id="total7"]')
expect(testTotal0.text()).toEqual('-')
expect(testTotal1.text()).toEqual('-')
expect(testTotal2.text()).toEqual('-')
expect(testTotal3.text()).toEqual('-')
// 模拟点击 add 新增
const testAdd0 = wrapper.find('[test-id="add"]')
await testAdd0.trigger('click')
expect(wrapper.vm.showAddApp).toBe(true)
await wrapper.vm.$nextTick()
const noData = wrapper.get('[test-id="noData"]')
expect(noData.text()).toBe('npm.noData')
resolve()
}, 200))
})
test('Providers and Applications 列表信息 Metric:Packets/s 0或大数值', async () => {
axiosPostAndMounted('Packets/s', 'boundary', mockData.boundary.data)
await new Promise(resolve => setTimeout(async () => {
const testName0 = wrapper.get('[test-id="name0"]')
const testName1 = wrapper.get('[test-id="name1"]')
const testName2 = wrapper.get('[test-id="name6"]')
const testName3 = wrapper.get('[test-id="name7"]')
expect(testName0.text()).toEqual('douyin')
expect(testName1.text()).toEqual('kuaishou')
expect(testName2.text()).toEqual('Alibaba')
expect(testName3.text()).toEqual('Baidu')
const testIcon0 = wrapper.get('[test-id="icon0"]')
const testIcon1 = wrapper.get('[test-id="icon1"]')
const testIcon2 = wrapper.get('[test-id="icon6"]')
const testIcon3 = wrapper.get('[test-id="icon7"]')
const testIcon4 = wrapper.get('[test-id="icon2"]')
expect(testIcon0.classes()).toContain('cn-icon-app2')
expect(testIcon1.classes()).toContain('cn-icon-app2')
expect(testIcon4.classes()).toContain('cn-icon-app2')
expect(testIcon2.classes()).toContain('cn-icon-entity')
expect(testIcon3.classes()).toContain('cn-icon-entity')
const testrate0 = wrapper.get('[test-id="rate0"]')
const testrate1 = wrapper.get('[test-id="rate1"]')
const testrate2 = wrapper.get('[test-id="rate6"]')
const testrate3 = wrapper.get('[test-id="rate7"]')
const testrate4 = wrapper.get('[test-id="rate2"]')
expect(testrate0.text()).toEqual('31.33 G')
expect(testrate1.text()).toEqual('84.59 T')
expect(testrate2.text()).toEqual('500.75 K')
expect(testrate3.text()).toEqual('0')
expect(testrate4.text()).toEqual('-')
const testPercent0 = wrapper.get('[test-id="percent0"]')
const testPercent1 = wrapper.get('[test-id="percent1"]')
const testPercent2 = wrapper.get('[test-id="percent6"]')
const testPercent3 = wrapper.get('[test-id="percent7"]')
expect(testPercent0.text()).toEqual('-87.48%')
expect(testPercent1.text()).toEqual('>500.00%')
expect(testPercent2.text()).toEqual('>500.00%')
expect(testPercent3.text()).toEqual('-100.00%')
const testTotal0 = wrapper.get('[test-id="total0"]')
const testTotal1 = wrapper.get('[test-id="total1"]')
const testTotal2 = wrapper.get('[test-id="total6"]')
const testTotal3 = wrapper.get('[test-id="total7"]')
const testTotal4 = wrapper.get('[test-id="total2"]')
expect(testTotal0.text()).toEqual('67.68 P')
expect(testTotal1.text()).toEqual('18.27 P')
expect(testTotal2.text()).toEqual('10.82 P')
expect(testTotal3.text()).toEqual('288.34 T')
expect(testTotal4.text()).toEqual('-')
resolve()
}, 300))
})
})

View File

@@ -1,28 +1,38 @@
import NetworkOverviewDdosDetection from '@/views/charts2/charts/networkOverview/NetworkOverviewDdosDetection'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import { mockData } from './mockData/NetworkOverviewDdosDetection'
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 mockGet = mockData.common
const timeFilter = {
dateRangeValue: -1,
startTime: 1675043912,
endTime: 1675047512
}
let wrapper = null
/**
* 进行axios请求并挂载vue实例
* @param data
*/
function axiosPostAndMounted (data) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const _data = data || mockGet
axios.get.mockResolvedValue(_data)
// 加载vue组件获得实例
wrapper = mount(NetworkOverviewDdosDetection, {
propsData: {
timeFilter
}
})
}
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
}
})
axiosPostAndMounted()
const attackEventCount = wrapper.get('[test-id="attackEventCount"]')
const attackerCount = wrapper.get('[test-id="attackerCount"]')
const victimCount = wrapper.get('[test-id="victimCount"]')
@@ -34,4 +44,27 @@ describe('views/charts2/charts/networkOverview/NetworkOverviewDdosDetection.vue
resolve()
}, 200))
})
test('攻击、受害、攻击数事件ddos检测图无数据', async () => {
axiosPostAndMounted(mockData.empty)
await new Promise(resolve => setTimeout(() => {
const noData = wrapper.get('[test-id="noData"]')
expect(noData.text()).toBe('npm.noData')
resolve()
}, 200))
})
test('攻击、受害、攻击数事件ddos检测图数据为0或极大值', async () => {
axiosPostAndMounted(mockData.boundary)
await new Promise(resolve => setTimeout(() => {
const attackEventCount = wrapper.get('[test-id="attackEventCount"]')
const attackerCount = wrapper.get('[test-id="attackerCount"]')
const victimCount = wrapper.get('[test-id="victimCount"]')
expect(attackEventCount.text()).toEqual('120.00 G')
expect(attackerCount.text()).toEqual('0')
expect(victimCount.text()).toEqual('3.67 T')
resolve()
}, 200))
})
})

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
export const mockData = {
common: {
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' }
},
empty: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: []
},
msg: 'OK'
}
},
boundary: {
data: {
status: 200,
code: 200,
data: {
resultType: 'object',
result: [
{
attackEventCount: 120000077777,
attackerCount: 0,
victimCount: 3667676700000
}
]
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,235 @@
import NpmAppEventTable from '@/views/charts2/charts/npm/NpmAppEventTable'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import ElementPlus from 'element-plus'
import { mockData } from './mockData/NpmAppEventByTable'
let wrapper = null
const mockGet = mockData.common.data
const timeFilter = {
dateRangeValue: -1,
startTime: 1675558657,
endTime: 1675731457
}
/**
* 进行axios请求并挂载vue实例
* @param data
*/
function axiosPostAndMounted (data) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const _data = data || mockGet
// 模拟 axios 数据
axios.get.mockResolvedValue(_data)
// 加载vue组件获得实例
wrapper = mount(NpmAppEventTable, {
global: {
plugins: [ElementPlus]
},
propsData: {
timeFilter
}
})
}
describe('views/charts2/charts/npm/NpmAppEventTable.vue测试', () => {
test('Npm 事件APP事件信息表格 严重程度色块验证', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(() => {
// critical
const criticalArray = [1, 8]
for (let index = 0; index < criticalArray.length; index++) {
const rowIndex = criticalArray[index]
for (let i = 1; i <= 5; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
}
// high
const highArray = [6]
for (let index = 0; index < highArray.length; index++) {
const rowIndex = highArray[index]
for (let i = 1; i <= 4; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 5; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
// medium
const mediumArray = [5, 7]
for (let index = 0; index < mediumArray.length; index++) {
const rowIndex = mediumArray[index]
for (let i = 1; i <= 3; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 4; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
// low
const lowArray = [2, 4]
for (let index = 0; index < lowArray.length; index++) {
const rowIndex = lowArray[index]
for (let i = 1; i <= 2; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 3; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
// info
const infoArray = [0, 3, 9]
for (let index = 0; index < infoArray.length; index++) {
const rowIndex = infoArray[index]
for (let i = 1; i <= 1; i++) {
const eventSeverityRedValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityRedValue.classes()).toContain('red-dot')
}
for (let i = 2; i <= 5; i++) {
const eventSeverityGreyValue = wrapper.find('[test-id="eventSeverityValue' + rowIndex + i + '"]')
expect(eventSeverityGreyValue.classes()).toContain('grey-dot')
}
}
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(应用数据列)', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="appName0"]')
const textNode1 = wrapper.find('[test-id="appName1"]')
const textNode2 = wrapper.find('[test-id="appName2"]')
const textNode3 = wrapper.find('[test-id="appName3"]')
const textNode4 = wrapper.find('[test-id="appName4"]')
const textNode5 = wrapper.find('[test-id="appName5"]')
const textNode6 = wrapper.find('[test-id="appName6"]')
const textNode7 = wrapper.find('[test-id="appName7"]')
const textNode8 = wrapper.find('[test-id="appName8"]')
const textNode9 = wrapper.find('[test-id="appName9"]')
expect(textNode0.text()).toEqual('youku')
expect(textNode1.text()).toEqual('uplive')
expect(textNode2.text()).toEqual('youku')
expect(textNode3.text()).toEqual('apple_hls')
expect(textNode4.text()).toEqual('apple_hls')
expect(textNode5.text()).toEqual('apple_hls')
expect(textNode6.text()).toEqual('uplive')
expect(textNode7.text()).toEqual('windows_update')
expect(textNode8.text()).toEqual('apple_hls')
expect(textNode9.text()).toEqual('cloudflare')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(严重程度数据列)', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="eventSeverity0"]')
const textNode1 = wrapper.find('[test-id="eventSeverity1"]')
const textNode2 = wrapper.find('[test-id="eventSeverity2"]')
const textNode3 = wrapper.find('[test-id="eventSeverity3"]')
const textNode4 = wrapper.find('[test-id="eventSeverity4"]')
const textNode5 = wrapper.find('[test-id="eventSeverity5"]')
const textNode6 = wrapper.find('[test-id="eventSeverity6"]')
const textNode7 = wrapper.find('[test-id="eventSeverity7"]')
const textNode8 = wrapper.find('[test-id="eventSeverity8"]')
const textNode9 = wrapper.find('[test-id="eventSeverity9"]')
expect(textNode0.text()).toEqual('info')
expect(textNode1.text()).toEqual('critical')
expect(textNode2.text()).toEqual('low')
expect(textNode3.text()).toEqual('info')
expect(textNode4.text()).toEqual('low')
expect(textNode5.text()).toEqual('medium')
expect(textNode6.text()).toEqual('high')
expect(textNode7.text()).toEqual('medium')
expect(textNode8.text()).toEqual('critical')
expect(textNode9.text()).toEqual('info')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(事件类型数据列)', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="eventType0"]')
const textNode1 = wrapper.find('[test-id="eventType1"]')
const textNode2 = wrapper.find('[test-id="eventType2"]')
const textNode3 = wrapper.find('[test-id="eventType3"]')
const textNode4 = wrapper.find('[test-id="eventType4"]')
const textNode5 = wrapper.find('[test-id="eventType5"]')
const textNode6 = wrapper.find('[test-id="eventType6"]')
const textNode7 = wrapper.find('[test-id="eventType7"]')
const textNode8 = wrapper.find('[test-id="eventType8"]')
const textNode9 = wrapper.find('[test-id="eventType9"]')
expect(textNode0.text()).toEqual('http error')
expect(textNode1.text()).toEqual('http error')
expect(textNode2.text()).toEqual('http error')
expect(textNode3.text()).toEqual('http error')
expect(textNode4.text()).toEqual('http error')
expect(textNode5.text()).toEqual('http error')
expect(textNode6.text()).toEqual('http error')
expect(textNode7.text()).toEqual('http error')
expect(textNode8.text()).toEqual('http error')
expect(textNode9.text()).toEqual('http error')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 数据验证(事件数量数据列)', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(() => {
const textNode0 = wrapper.find('[test-id="count0"]')
const textNode1 = wrapper.find('[test-id="count1"]')
const textNode2 = wrapper.find('[test-id="count2"]')
const textNode3 = wrapper.find('[test-id="count3"]')
const textNode4 = wrapper.find('[test-id="count4"]')
const textNode5 = wrapper.find('[test-id="count5"]')
const textNode6 = wrapper.find('[test-id="count6"]')
const textNode7 = wrapper.find('[test-id="count7"]')
const textNode8 = wrapper.find('[test-id="count8"]')
const textNode9 = wrapper.find('[test-id="count9"]')
expect(textNode0.text()).toEqual('6')
expect(textNode1.text()).toEqual('5')
expect(textNode2.text()).toEqual('4')
expect(textNode3.text()).toEqual('3')
expect(textNode4.text()).toEqual('3')
expect(textNode5.text()).toEqual('2')
expect(textNode6.text()).toEqual('2')
expect(textNode7.text()).toEqual('2')
expect(textNode8.text()).toEqual('1')
expect(textNode9.text()).toEqual('1')
resolve()
}, 200))
})
test('Npm 事件APP事件信息表格 无数据', async () => {
axiosPostAndMounted(mockData.empty.data)
await new Promise(resolve => setTimeout(() => {
const noData = wrapper.get('[test-id="noData"]')
expect(noData.text()).toBe('npm.noData')
resolve()
}, 200))
})
})

View File

@@ -0,0 +1,77 @@
import NpmEventsByType from '@/views/charts2/charts/npm/NpmEventsByType'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import { mockData } from './mockData/NpmEventsByType'
const mockGet = mockData.common.data
let wrapper = null
const timeFilter = {
dateRangeValue: -1,
startTime: 1673244000,
endTime: 1673247600
}
/**
* 进行axios请求并挂载vue实例
* @param data
*/
function axiosPostAndMounted (data) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const _data = data || mockGet
// 模拟 axios 数据
axios.get.mockResolvedValue(_data)
// 加载vue组件获得实例
wrapper = mount(NpmEventsByType, {
propsData: {
timeFilter
}
})
}
describe('views/charts2/charts/npm/NpmEventsByType.vue测试', () => {
test('类型分类事件统计信息: npm events 类型分类事件图 ', async () => {
axiosPostAndMounted()
await new Promise(resolve => setTimeout(() => {
// type
const testNode0 = wrapper.get('[test-id="testNode0"]')
const testNode1 = wrapper.get('[test-id="testNode1"]')
expect(testNode0.text()).toEqual('dns error')
expect(testNode1.text()).toEqual('http error')
// total
const total0 = wrapper.get('[test-id="total0"]')
const total1 = wrapper.get('[test-id="total1"]')
expect(total0.text()).toEqual('151')
expect(total1.text()).toEqual('32')
resolve()
}, 200))
})
test('类型分类事件统计信息: npm events 类型分类事件图无数据 ', async () => {
axiosPostAndMounted(mockData.empty.data)
await new Promise(resolve => setTimeout(() => {
const noData = wrapper.get('[test-id="noData"]')
expect(noData.text()).toBe('npm.noData')
resolve()
}, 200))
})
test('类型分类事件统计信息: npm events 类型分类事件图无Http Error类型 ', async () => {
const data = JSON.parse(JSON.stringify(mockData.common.data))
data.data.data.result = data.data.data.result.filter(item => item.eventType !== 'http error')
axiosPostAndMounted(data)
await new Promise(resolve => setTimeout(() => {
const testNode0 = wrapper.get('[test-id="testNode0"]')
expect(testNode0.text()).toEqual('dns error')
// total
const total0 = wrapper.get('[test-id="total0"]')
expect(total0.text()).toEqual('151')
resolve()
}, 200))
})
})

View File

@@ -1,25 +1,35 @@
import NpmEventsHeader from '@/views/charts2/charts/npm/NpmEventsHeader'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import { mockData } from './mockData/NpmEventsHeader'
// 模拟数据
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"}
}
const chartData = mockData.common.data
// type
const type = 'severity'
let wrapper = null
/**
* 进行axios请求并挂载vue实例
* @param data
*/
function axiosPostAndMounted (data) {
require('vue-router').useRoute.mockReturnValue({ query: {} })
const _data = data || chartData
// 模拟 axios 数据
axios.get.mockResolvedValue(_data)
// 加载vue组件获得实例
wrapper = mount(NpmEventsHeader, {
propsData: {
type
}
})
}
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
}
})
axiosPostAndMounted()
// 严重等级
const severity0 = wrapper.get('[test-id="severity0"]')
const severity1 = wrapper.get('[test-id="severity1"]')
@@ -51,4 +61,40 @@ describe('views/charts2/charts/npm/NpmEventsHeader.vue测试', () => {
resolve()
}, 200))
})
test('请求无数据', async () => {
axiosPostAndMounted(mockData.empty.data)
// 各等级个数
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"]')
await new Promise(resolve => setTimeout(() => {
expect(total0.text()).toEqual('-')
expect(total1.text()).toEqual('-')
expect(total2.text()).toEqual('-')
expect(total3.text()).toEqual('-')
expect(total4.text()).toEqual('-')
resolve()
}, 200))
})
test('数据为0或极大', async () => {
axiosPostAndMounted(mockData.boundary.data)
// 各等级个数
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"]')
await new Promise(resolve => setTimeout(() => {
expect(total0.text()).toEqual('0')
expect(total1.text()).toEqual('0')
expect(total2.text()).toEqual('0')
expect(total3.text()).toEqual('14,456,678')
expect(total4.text()).toEqual('1,222,225,432,456,789')
resolve()
}, 200))
})
})

File diff suppressed because one or more lines are too long

View File

@@ -2,52 +2,45 @@ import NpmRecentEvents from '@/views/charts2/charts/npm/NpmRecentEvents'
import { mount } from '@vue/test-utils'
import axios from 'axios'
import ElementPlus from 'element-plus'
import { mockData } from './mockData/NpmRecentEvents'
// 未下钻
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 mockGet = mockData.common.data1
// 下钻
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 mockGet1 = mockData.common.data2
const query = mockData.common.query
const timeFilter = {
dateRangeValue: -1,
startTime: 1675043912,
endTime: 1675047512
}
let wrapper = null
/**
* 进行axios请求并挂载vue实例
* @param data
* @param query
*/
function axiosPostAndMounted (data, query) {
const _query = query || {}
require('vue-router').useRoute.mockReturnValue({ query: _query })
const _data = data || mockGet
axios.get.mockResolvedValue(_data)
wrapper = mount(NpmRecentEvents, {
propsData: {
timeFilter
},
global: {
plugins: [ElementPlus]
}
})
}
describe('views/charts2/charts/npm/NpmRecentEvents.vue测试', () => {
test('npm events 近期事件表格 未下钻', async () => {
require('vue-router').useRoute.mockReturnValue({ query: {} })
// 模拟 axios 返回数据
axios.get.mockResolvedValue(mockGet)
axiosPostAndMounted()
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"]')
@@ -98,18 +91,8 @@ describe('views/charts2/charts/npm/NpmRecentEvents.vue测试', () => {
}, 200))
})
test('npm events 近期事件表格 下钻', async () => {
require('vue-router').useRoute.mockReturnValue({ query: query })
// 模拟 axios 返回数据
axios.get.mockResolvedValue(mockGet1)
axiosPostAndMounted(mockGet1, query)
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"]')
@@ -144,11 +127,11 @@ describe('views/charts2/charts/npm/NpmRecentEvents.vue测试', () => {
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"]')
const startTime0 = wrapper.get('[test-id="startTime-0"]')
const startTime1 = wrapper.get('[test-id="startTime-1"]')
const startTime2 = wrapper.get('[test-id="startTime-2"]')
const startTime3 = wrapper.get('[test-id="startTime-3"]')
const startTime4 = wrapper.get('[test-id="startTime-4"]')
expect(startTime0.text()).toEqual('2023-01-04T11:25:00+08:00')
expect(startTime1.text()).toEqual('2023-01-04T00:30:00+08:00')
@@ -159,4 +142,13 @@ describe('views/charts2/charts/npm/NpmRecentEvents.vue测试', () => {
resolve()
}, 300))
})
test('npm events 近期事件表格 未下钻、未下钻无数据', async () => {
axiosPostAndMounted(mockData.empty.data)
await new Promise(resolve => setTimeout(() => {
const noData = wrapper.get('[test-id="noData"]')
expect(noData.text()).toBe('npm.thereNoEvents')
resolve()
}, 200))
})
})

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
export const mockData = {
common: {
data: {
data: { status: 200, code: 200, queryKey: '88eeb92e0ddb40c0327db494cfe5c74c', success: true, message: null, statistics: { elapsed: 0, rows_read: 2, result_size: 752, result_rows: 10 }, job: null, formatType: 'json', meta: [{ name: 'app_name', type: 'string', category: 'Dimension' }, { name: 'event_severity', type: 'string', category: 'Dimension' }, { name: 'event_type', type: 'string', category: 'Dimension' }, { name: 'count', type: 'long', category: 'Metric' }], data: { resultType: 'table', result: [{ appName: 'youku', eventSeverity: 'info', eventType: 'http error', count: 6 }, { appName: 'uplive', eventSeverity: 'critical', eventType: 'http error', count: 5 }, { appName: 'youku', eventSeverity: 'low', eventType: 'http error', count: 4 }, { appName: 'apple_hls', eventSeverity: 'info', eventType: 'http error', count: 3 }, { appName: 'apple_hls', eventSeverity: 'low', eventType: 'http error', count: 3 }, { appName: 'apple_hls', eventSeverity: 'medium', eventType: 'http error', count: 2 }, { appName: 'uplive', eventSeverity: 'high', eventType: 'http error', count: 2 }, { appName: 'windows_update', eventSeverity: 'medium', eventType: 'http error', count: 2 }, { appName: 'apple_hls', eventSeverity: 'critical', eventType: 'http error', count: 1 }, { appName: 'cloudflare', eventSeverity: 'info', eventType: 'http error', count: 1 }] }, originalUrl: 'http://192.168.44.55:9999?query=SELECT%20app_name%20AS%20app_name%2C%20event_severity%20AS%20event_severity%2C%20event_type%20AS%20event_type%2C%20COUNT%28*%29%20AS%20count%20FROM%20performance_event%20WHERE%20start_time%20%3E%3D%201675580110%20AND%20end_time%20%3C%201675752910%20AND%20entity_type%20%3D%20%27app%27%20GROUP%20BY%20app_name%2Cevent_severity%2Cevent_type%20ORDER%20BY%20count%20DESC%20%20LIMIT%2010%20&format=json&option=real-time', msg: 'OK' }
}
},
empty: {
data: {
data: {
status: 200,
code: 200,
data: {
resultType: 'table',
result: []
},
msg: 'OK'
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More