Compare commits

...

157 Commits

Author SHA1 Message Date
chenj
c7366ef745 fix: 图表重复点击tab坍缩问题 2022-12-05 20:12:04 +08:00
陈劲松
064001c4ab Merge branch 'cherry-pick-ed6e1f26' into 'dev-22.11'
fix:优化LinkBlock右侧蜂窝图数据去重

See merge request cyber-narrator/cn-ui!17
2022-12-05 11:59:17 +00:00
刘洪洪
eb92ef9ea8 fix:优化LinkBlock右侧蜂窝图数据去重
(cherry picked from commit ed6e1f26fb)
2022-12-05 19:59:10 +08:00
陈劲松
cb0546ebb2 Merge branch 'cherry-pick-aa1c16ab' into 'dev-22.11'
fix:修复LinkBlock右侧蜂窝图不显示的问题

See merge request cyber-narrator/cn-ui!16
2022-12-05 11:58:56 +00:00
刘洪洪
9ca2950d68 fix:修复LinkBlock右侧蜂窝图不显示的问题
(cherry picked from commit aa1c16aba3)
2022-12-05 19:58:50 +08:00
陈劲松
533f7d357d Merge branch 'cherry-pick-31187404' into 'dev-22.11'
fix:修复NetworkOverviewTabs报错时message字段使用

See merge request cyber-narrator/cn-ui!15
2022-12-05 11:58:36 +00:00
刘洪洪
efa899da2c fix:修复NetworkOverviewTabs报错时message字段使用
(cherry picked from commit 31187404f2)
2022-12-05 19:57:54 +08:00
陈劲松
c35965061f Merge branch 'cherry-pick-e026fb3c' into 'dev-22.11'
style: el-tab组件table列表首列标题向左顶格

See merge request cyber-narrator/cn-ui!14
2022-12-05 11:57:34 +00:00
changcode
19677ba521 style: el-tab组件table列表首列标题向左顶格
(cherry picked from commit e026fb3cfd)
2022-12-05 19:57:27 +08:00
陈劲松
7c1729ca63 Merge branch 'cherry-pick-7ec4ab62' into 'dev-22.11'
fix: CN-817 下钻table的error交互实施:错误图标显示后消失问题修正

See merge request cyber-narrator/cn-ui!13
2022-12-05 07:39:57 +00:00
hyx
1c6e28a200 fix: CN-817 下钻table的error交互实施:错误图标显示后消失问题修正
(cherry picked from commit 7ec4ab62ff)
2022-12-05 15:39:34 +08:00
陈劲松
fa746fd95b Merge branch 'cherry-pick-5818485f' into 'dev-22.11'
CN-820 npm 下钻ip维度:直接进入as a client时没数据

See merge request cyber-narrator/cn-ui!12
2022-12-05 07:04:56 +00:00
hyx
0a5fe3029c CN-820 npm 下钻ip维度:直接进入as a client时没数据
(cherry picked from commit 5818485f3f)
2022-12-05 15:04:09 +08:00
陈劲松
61357f2720 Merge branch 'cherry-pick-49e08f72' into 'dev-22.11'
CN-817 下钻table的error交互实施

See merge request cyber-narrator/cn-ui!11
2022-12-05 07:03:16 +00:00
hyx
9bc9103925 CN-817 下钻table的error交互实施
(cherry picked from commit 49e08f723a)
2022-12-05 15:03:01 +08:00
陈劲松
f0ea7e28fc Merge branch 'cherry-pick-e89fc78a' into 'dev-22.11'
CN-815: 前端内存占用分析

See merge request cyber-narrator/cn-ui!10
2022-12-05 07:02:31 +00:00
刘洪洪
cf5bfb4136 CN-815: 前端内存占用分析
(cherry picked from commit e89fc78aca)
2022-12-05 15:02:14 +08:00
陈劲松
ef6c95e536 Merge branch 'cherry-pick-2cf206de' into 'dev-22.11'
CN-816 下钻table分数列排序功能无法使用

See merge request cyber-narrator/cn-ui!9
2022-12-05 03:42:21 +00:00
hyx
29fe3bc3ef CN-816 下钻table分数列排序功能无法使用
(cherry picked from commit 2cf206de5a)
2022-12-05 11:42:14 +08:00
陈劲松
85830dc7ca Merge branch 'cherry-pick-82bd184b' into 'dev-22.11'
fix: 解决链路图切换时间后接口无数据,界面仍保留之前数据的问题

See merge request cyber-narrator/cn-ui!8
2022-12-05 03:41:38 +00:00
刘洪洪
f1423b6b62 fix: 解决链路图切换时间后接口无数据,界面仍保留之前数据的问题
(cherry picked from commit 82bd184bdd)
2022-12-05 11:41:28 +08:00
chenj
8b2e1e95db CN-820 fix: 修复地图报错问题 2022-12-01 17:00:48 +08:00
陈劲松
a1b7527496 Merge branch 'cherry-pick-0f600b44' into 'dev-22.11'
fix: npm ip下钻补充默认值

See merge request cyber-narrator/cn-ui!7
2022-12-01 08:39:59 +00:00
@changcode
e93345ea2a fix: npm ip下钻补充默认值
(cherry picked from commit 0f600b44ac)
2022-12-01 16:39:49 +08:00
changcode
2b3e5ca360 fix: 修复npm下钻 时间变化后,顶部的分数计算方法未能触发问题 2022-11-28 16:41:31 +08:00
changcode
8f045125ea fix: 修复 npm下钻和非下钻 性能统计父组件数据变化后子组件无法获取问题 2022-11-28 16:36:05 +08:00
hyx
aa06553b17 CN-813 NPM的IP下钻页,TAB在as server上时,url不能保留这个状态 2022-11-28 09:20:41 +08:00
hyx
9e2aa20303 Revert "CN-733 完成链路页面下钻后的表格"
This reverts commit 1e77c694
2022-11-28 09:18:33 +08:00
hyx
1e77c69460 CN-733 完成链路页面下钻后的表格 2022-11-28 09:09:44 +08:00
changcode
d25912b2ce CN-812 feat: npm下钻三级菜单网络性能接口,前端对应逻辑优化调整 2022-11-25 16:08:32 +08:00
changcode
c7eacdd6f6 fix: npm 下钻相关会话计算逻辑调整 2022-11-25 11:10:08 +08:00
刘洪洪
09edc8961c fix: NpmAppEventTable去除error模拟数据 2022-11-24 15:26:14 +08:00
刘洪洪
8e698f3b78 fix: 部分文件添加error处理 2022-11-24 14:10:28 +08:00
刘洪洪
e2d6707249 fix: NpmNetworkQuantity模块去除模拟数据 2022-11-24 11:03:53 +08:00
刘洪洪
a381f5a01b fix: NpmNetworkQuantity模块添加error处理 2022-11-24 11:02:06 +08:00
刘洪洪
1ac910fc54 feat: SingleValue精简代码 2022-11-24 09:48:17 +08:00
刘洪洪
7d9829ae27 fix: npm模块下文件请求做error处理 2022-11-23 17:20:37 +08:00
changcode
16a255be50 CN-811 feat: networkoverview下钻第三级菜单折线图请求参数调整 2022-11-23 16:51:38 +08:00
@changcode
ae4ce44eff fix: tab选项卡折线图,数据为空时去除移入点击效果 2022-11-22 17:58:15 +08:00
@changcode
bf008fe944 fix: 修复tab选项卡平局数为0时消毁当前tab选项卡时,参考线无默认值问题 2022-11-22 15:01:30 +08:00
刘洪洪
2cae53e83a fix: Detection模块修改icon图标名 2022-11-22 10:52:30 +08:00
刘洪洪
fa8ea9dce0 fix: Error组件修改提示框窗样式 2022-11-22 10:49:35 +08:00
hyx
d5298347d8 fix:DNS下钻表格的流入流出查询次数百分比图显示,在0%的时候仍然有显示 2022-11-21 18:34:07 +08:00
刘洪洪
248075cd81 fix: Error组件修改调用方式注释 2022-11-21 17:39:35 +08:00
刘洪洪
c3ffd01363 fix: 请求添加error处理 2022-11-21 17:31:30 +08:00
hyx
69735e438f DNS的qtype和rcode维度信息查询优化 2022-11-21 11:28:56 +08:00
刘洪洪
8e221414b8 fix: 通用Error组件添加注释 2022-11-18 16:59:37 +08:00
刘洪洪
8d461ef200 fix: Related sessions模块精简代码 2022-11-18 16:39:10 +08:00
刘洪洪
0f5bd8a7a7 fix: Related sessions模块精简代码 2022-11-18 16:37:11 +08:00
刘洪洪
ebf1228c68 fix: 修复当数据返回为-时,Related sessions灰条不显示的问题,以及切换时间,dom未做修改的问题 2022-11-18 16:25:55 +08:00
@changcode
cdd48102a5 style: 修复npm下钻 关联session图样式问题 2022-11-18 16:14:00 +08:00
@changcode
6ba21507f7 Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev 2022-11-18 15:18:41 +08:00
@changcode
90ee54c3ad fix: 1.npm tab为location折线图数据为空时ecahrts图加载报错2.npm 下钻世界地图接口参数错误和tab切换未正常禁用问题 2022-11-18 15:18:12 +08:00
刘洪洪
5e03847a42 CN-802:error交互实施 2022-11-18 15:18:05 +08:00
hyx
4c107704e7 CN-801 下钻table的配置增加版本属性 2022-11-18 14:21:23 +08:00
@changcode
4975f2425d fix: 1.npm地图分数补充占位复2.npm下钻3级菜单接口请求错误修复 2022-11-17 15:55:34 +08:00
hyx
2b34f8bc26 对数据中的双引号和&进行特殊处理;panelName显示异常; 2022-11-17 15:21:17 +08:00
刘洪洪
7c4b16d443 fix: 更新iconfont图标 2022-11-16 18:20:19 +08:00
@changcode
57b607fca5 CN-803 fix: 批量查询参数格式变更 2022-11-16 18:10:48 +08:00
hyx
c30f6e642a CN-799 部分接口param参数内容本身带逗号时查询不准确的问题 2022-11-16 16:59:52 +08:00
@changcode
8416060fc4 fix: 修复 npm下钻tab为 Client Cities和Server Cities时,接口请求参数错误问题 2022-11-16 11:55:11 +08:00
@changcode
45c318b391 fix: 修复echarts 配置误删问题 2022-11-16 11:16:46 +08:00
@changcode
3ec3873860 fix: 修复npm location 地图国家切换后,有为空的折线图报错不展示问题 2022-11-16 10:33:52 +08:00
刘洪洪
5a1f177ae1 fix: Detection模块右侧tab的图标自适应居中 2022-11-15 18:33:07 +08:00
刘洪洪
820c86a3d3 fix: Detection模块中的筛选模块名称换i18n 2022-11-15 17:09:35 +08:00
hyx
b4b7edb18f CN-798:1.npm clientip serverip下钻后应该跳到和ip一样的二级页面,默认高亮与之对应的顶部tab 2022-11-15 17:06:47 +08:00
刘洪洪
0a3cf92ce9 fix: Detection模块按UI调整部分样式 2022-11-15 17:00:12 +08:00
刘洪洪
5ecf096e40 fix:Detection模块调整部分样式 2022-11-15 11:36:40 +08:00
@changcode
aee521d5bc fix: npm下钻tab为client ip和server ip接口请求参数调整 2022-11-15 11:13:11 +08:00
@changcode
35fcbab852 fix: 修复apps tab切换判断导致的loading一直存在,apps列表删除问题 2022-11-14 17:12:13 +08:00
hyx
9f6a98779b CN-798 下钻table新增一系列维度后的问题:npm在customize里勾选client ip、client country等新增的维度后,没有被缓存;新增client asn和server asn维度; 2022-11-14 14:35:14 +08:00
@changcode
d44b406222 fix: npm顶部分数计算方法,限制为只有在npm下钻时才触发 2022-11-11 17:37:07 +08:00
@changcode
37ce944dc1 fix: npm 下钻接口请求 side 参数根据 tabIndex 判断赋值 2022-11-11 15:25:12 +08:00
liuhonghong
c322059c97 fix:回退保留状态弃用window,使用store 2022-11-11 15:17:44 +08:00
@changcode
323ccae196 fix: 折线图,tab展示判断逻辑使用 parseFloat 函数 2022-11-11 10:33:00 +08:00
hyx
d1beba7782 CN-774 维度表相关优化:除DNS Dashboard之外,其他各界面的维度表增加以下可选维度的统计(默认不显示,支持用户勾选) 2022-11-11 09:37:32 +08:00
@changcode
0fb496c349 CN-789 fix: 修复NPM ip下钻后,顶部的分数问题 2022-11-10 17:20:00 +08:00
chenjinsong
ef069e7fbc fix: 修复下钻table配置bug 2022-11-10 15:29:12 +08:00
@changcode
40e1e5da16 Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev 2022-11-09 17:44:59 +08:00
@changcode
e996963635 fix: 修复Network Overview apps 部分逻辑及折线图移入弹出框样式 2022-11-09 17:44:48 +08:00
刘洪洪
aa6d5f1598 fix: 隐藏框选工具栏 2022-11-09 17:41:19 +08:00
刘洪洪
39edceb0dd CN-773: 曲线图支持框选缩放 2022-11-09 17:23:31 +08:00
刘洪洪
e355eb31cd fix: detection模块中,调整因修改公共class导致的其他界面样式错乱,以及tab图标不居中的问题 2022-11-09 15:42:36 +08:00
hyx
3e4cc199a6 CN-774 维度表相关优化:Top选择50后,由于增加了滑动条,宽度和高度产生变化;排序功能,不论是否显示Top50,都按50条数据来排序; 2022-11-09 15:22:09 +08:00
陈劲松
faabc949c0 Update gitlab-ci.yml file 隐藏左上角的更新记录链接 2022-11-09 02:44:27 +00:00
刘洪洪
809a6b5562 fix: 将class移入公共css中,修改编辑器报红线的代码格式 2022-11-07 15:48:46 +08:00
刘洪洪
21e0f94d19 CN-772: 解决状态保留相关bug 2022-11-07 15:28:25 +08:00
刘洪洪
ec98178d45 CN-784: Detection列表样式按新版UI图修改 2022-11-07 15:25:00 +08:00
@changcode
32a2359e0d fix: 修复npm 折线图和下钻折线图,因名称判断错误导致的移入弹窗样式问题 2022-11-07 10:17:24 +08:00
chenjinsong
c425d87b00 fix: 临时性调整npm line和npm traffic line的legend名字 2022-11-04 12:52:12 +08:00
hyx
34e16fc890 CN-762 DNS service insights 显示建议:修改由dns的qtype及rcode维度转换对应名称的问题所影响的部分(panel名称,顶部四级菜单显示及下拉显示等) 2022-11-04 12:18:26 +08:00
chenjinsong
c0487a708a fix: 修复地图报错 2022-11-04 11:32:49 +08:00
chenjinsong
68596189b4 fix: 删除npm流量曲线标题 2022-11-04 11:26:46 +08:00
@changcode
29d89ee6e4 fix: 去除无用组件,修复部分折线图Reference line闪动问题 2022-11-04 10:31:10 +08:00
@changcode
14c53bd5ad fix: 修复折线图判断逻辑当字段不存在导致的折线图展示错误问题 2022-11-04 10:24:22 +08:00
@changcode
f39c4d8fc4 CN-790 fix: 修复NPM overview折线图metric、legend、内容 对应不上 2022-11-04 10:22:32 +08:00
hyx
6827d4f031 CN-762 DNS service insights 显示建议(dns qtype和rcode维度数据映射及自定义metric名称修改) 2022-11-03 17:51:53 +08:00
@changcode
1cb4c43b9e fix: 修复NetWork OVerview Providers and Applications 时间范围变化后,之前时间范围数据为清除问题 2022-11-03 16:01:32 +08:00
@changcode
e1bf796f0c fix: 修复 Network Overview 指标下拉为sessions/s时,切换其他选项lineTab一直为total问题 2022-11-03 15:36:36 +08:00
chenjinsong
94c15e0933 CN-775 fix: 修改图表标题 2022-11-03 14:49:55 +08:00
@changcode
043b785547 Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev 2022-11-03 11:21:53 +08:00
@changcode
b0b8de2712 fix: 修复折线图表tab反选错误 2022-11-03 11:21:39 +08:00
chenjinsong
93933502e4 CN-775 fix: 修改图表标题 2022-11-03 11:00:41 +08:00
@changcode
e5b636905f fix: 修复link下钻列表分数计算逻辑 2022-11-03 10:56:06 +08:00
@changcode
078ec2f69f CN-787 feat: Dashboard - npm - overview增加一个曲线图 2022-11-02 19:28:11 +08:00
@changcode
7c77c3c79a fix: 修复link下钻折线图左侧列表错误问题 2022-11-02 18:52:33 +08:00
hyx
0d89b3c23b CN-788 下钻table的分数样式、内容展示等调整 2022-11-02 16:26:31 +08:00
@changcode
a6ea1fa4e6 CN-771 feat: network overview metric统一顶部触发 2022-11-02 11:28:25 +08:00
@changcode
70c38fe2de fix: 修复npm下钻网络性能统计数据展示错误问题 2022-11-02 10:59:21 +08:00
@changcode
12b7290ecd fix: 修复折线图数据为空后,保留之前数据的问题 2022-11-01 19:23:17 +08:00
chenjinsong
60cbebe1cc CN-771 fix: metric下拉框背景色调整 2022-11-01 18:05:45 +08:00
chenjinsong
096517e34f fix: 更改app列表环比0的描述 2022-11-01 17:11:55 +08:00
chenjinsong
2020423c0a fix: metric下拉限定在networkoverview 2022-11-01 16:47:42 +08:00
chenjinsong
da03a3658c fix: networkoverview的metric提至顶部 2022-11-01 16:46:11 +08:00
@changcode
45de9c8107 fix: 分数计算方法逻辑错误修复 2022-11-01 14:31:28 +08:00
@changcode
14f661b78f Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev 2022-11-01 14:13:55 +08:00
@changcode
b5718a039f fix: 分数计算逻辑调整 2022-11-01 14:13:47 +08:00
@changcode
6845bb4ea4 fix: 修复network apps 移入效果错误 2022-11-01 14:13:03 +08:00
hyx
5f448c0ccb CN-778 下钻table取值变更: npm、dns、link的throughput列取值改为totalBytes,单位改为byte
CN-776 NPM Dashboard维度表中分数添加颜色标识
2022-11-01 10:33:23 +08:00
hyx
d568c748db CN-748 下钻table缓存功能优化(包括metric和tab结构的变化)
CN-778 总流量、总包、比特速率字段名前端统一
2022-11-01 09:12:14 +08:00
chenjinsong
07e137d5e0 fix: 调整networkoverview provider改为providers 2022-10-31 17:18:00 +08:00
@changcode
3093c3e3a8 fix: network app 更多点击事件切换为鼠标悬浮触发 2022-10-28 15:30:16 +08:00
刘洪洪
db105804b3 fix: 隐藏timeRange时间弹窗的方向标识 2022-10-28 14:46:04 +08:00
刘洪洪
49eb4d42ce fix:解决timeRange刷新后下钻返回时间被重置 2022-10-28 10:28:12 +08:00
刘洪洪
e019f8a02e fix: timeRange添加注释 2022-10-27 16:32:16 +08:00
刘洪洪
e7cf758ffd fix: 解决timeRange下钻后返回时间被重置的问题 2022-10-27 15:47:38 +08:00
@changcode
64610304c1 fix: 修复npm location 折线图,因字段改变导致的报错 2022-10-27 10:47:38 +08:00
刘洪洪
2da2172f03 fix: $dayJs改为全局引用,去除不必要组件引入 2022-10-27 09:48:31 +08:00
刘洪洪
c2c3898d5b CN-746: 更换时间选择器实现方案 2022-10-26 18:59:53 +08:00
@changcode
04e4371ac1 fix: 折线图多个 tab 无数据时,只保留total时默认选中 2022-10-26 18:20:31 +08:00
@changcode
06c86b8172 CN-768 fix: 1.app卡片Bit/s时底下的流量总数的单位需要使用byte2.折线图单个情况默认选中3.network overview 的右上角图表的按钮点击跳转到npm的events 2022-10-25 19:42:48 +08:00
chenjinsong
ced28fd3e7 fix: 调整networkoverview的图表位置,增加app-card图的resize逻辑 2022-10-25 18:41:27 +08:00
@changcode
943d64918d fix: 优化报告列表下拉 loading 位置 2022-10-25 10:31:47 +08:00
@changcode
de5040d9f4 fix: 注释页面中的打印 2022-10-24 18:40:57 +08:00
刘洪洪
64e41c3170 fix: 解决实体界面初始化时,接口会被调用三次的问题 2022-10-24 18:37:10 +08:00
hyx
8bee5301bc CN-760 score跳动问题 2022-10-24 16:51:41 +08:00
hyx
b75e975421 CN-754 下钻table的一些bug:NPM、DNS、Link的throughput列有值,但是显示横杠(我刚更新配置,这个字段的prop改为totalBitsRate,需清一下indexdb缓存) 2022-10-24 14:08:24 +08:00
@changcode
21c3ead654 fix: 修复折线图百分比数据与坐标对不上问题 2022-10-23 10:17:26 +08:00
hyx
74cbcf0d46 CN-754 下钻table的一些bug:城市单引号问题 2022-10-21 21:26:39 +08:00
chenjinsong
2e27cd5add fix: 关闭mock,解决下载pdf空白的问题 2022-10-21 18:10:12 +08:00
@changcode
e2b03c5d7f fix:修复domain 和 app, npm 下钻地图标题问题 2022-10-21 11:52:46 +08:00
hyx
a87b096c1c CN-754 下钻table的一些bug 2022-10-21 10:08:49 +08:00
@changcode
2a6958f61b fix: npm 地理位置折线图 弹框颜色错误问题 2022-10-20 20:13:09 +08:00
刘洪洪
ae8918f7f7 fix: Provider和Applications快捷图表添加标题,以及修改增加提示框名称问题 2022-10-20 16:13:05 +08:00
chenjinsong
f9e6250e2a fix: 下钻表格首列标题为单数 2022-10-20 14:53:50 +08:00
@changcode
bb81059529 CN-758 fix: 修复NPM下钻折线图bug 2022-10-20 14:41:48 +08:00
@changcode
c38b5ef000 fix: npm 下钻折线图切换数据展示错误问题 2022-10-20 13:56:39 +08:00
@changcode
75dd68f403 fix: npm 折线图移入弹框颜色补充 2022-10-20 11:23:32 +08:00
@changcode
e39e24258b fix; 修复折线图因数据问题导致移入弹框颜色错误问题 2022-10-20 11:19:58 +08:00
@changcode
5767fe896b fix: 修复报告Box侧滑Time limit 为 customize,时间限制错误问题 2022-10-19 17:19:39 +08:00
chenjinsong
53fc6475e2 feat: 更改评分规则 2022-10-19 15:55:23 +08:00
hyx
59ae26e078 CN-752 dns下钻后q查询条件有些与文档不符 2022-10-19 15:45:31 +08:00
刘洪洪
15cfd48770 CN-747: http请求支持取消 2022-10-19 15:27:26 +08:00
@changcode
111f731712 fix: 报告Box Time limit 时间限制逻辑调整 2022-10-19 11:16:38 +08:00
@changcode
c5a78887cb fix: 报告box Time limit 为 customize的时候,时间选择器增加限制 2022-10-19 09:57:51 +08:00
chenjinsong
add13f463d fix: 修正dns下钻后折线图的url 2022-10-18 19:14:08 +08:00
@changcode
c1dd39fb66 fix: 报告box time limit 为 customize 时增加时间范围限制 2022-10-18 17:38:37 +08:00
87 changed files with 4682 additions and 2480 deletions

View File

@@ -18,7 +18,7 @@ before_script:
generate_git-log: generate_git-log:
stage: gen_git-log stage: gen_git-log
script: script:
- if (( `grep git-log.html ./public/index.html | wc -l` == 0 )); then sed -i 's+</body>+<a style="position:fixed;top:0;left:0;z-index:999;font-size:12px;color:darkblue;text-decoration:none;" target="_blank" href="./git-log.html">R</a>\n</body>+g' ./public/index.html; echo "添加更新记录链接"; fi; - if (( `grep git-log.html ./public/index.html | wc -l` == 0 )); then sed -i 's+</body>+<a style="position:fixed;top:0;left:0;z-index:999;font-size:12px;color:transparent;text-decoration:none;display:none;" target="_blank" href="./git-log.html">R</a>\n</body>+g' ./public/index.html; echo "添加更新记录链接"; fi;
- echo "最近的100个提交记录" - echo "最近的100个提交记录"
- echo "<!DOCTYPE html><html><head><meta charset='utf-8'></head><body><pre>" > ./public/git-log.html - echo "<!DOCTYPE html><html><head><meta charset='utf-8'></head><body><pre>" > ./public/git-log.html
- "git log -100 --pretty=format:'%ad : %s' >> ./public/git-log.html" - "git log -100 --pretty=format:'%ad : %s' >> ./public/git-log.html"

View File

@@ -5,6 +5,17 @@
</template> </template>
<script> <script>
import { storageKey } from '@/utils/constants' import { storageKey } from '@/utils/constants'
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
const advancedFormat = require('dayjs/plugin/advancedFormat')
const weekday = require('dayjs/plugin/weekday')
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(advancedFormat)
dayjs.extend(weekday)
window.$dayJs = dayjs
export default { export default {
name: 'App', name: 'App',
setup () { setup () {

View File

@@ -4,7 +4,7 @@
display: none !important; display: none !important;
} }
* { * {
font-family: NotoSansSChineseRegular; font-family: NotoSansSChineseRegular, serif;
box-sizing: border-box; box-sizing: border-box;
} }
html, body, #app { html, body, #app {
@@ -43,3 +43,20 @@ body {
.el-form-item__error { .el-form-item__error {
padding-top: 0 !important; padding-top: 0 !important;
} }
/* 请求报错样式关于popover的修改 */
.error-popover {
//min-width: 30px !important;
display: inline-block;
background: #FFE7E6 !important;
border: 1px solid rgba(226,97,84,0.42) !important;
padding: 6.6px 12px !important;
border-radius: 4px !important;
font-size: 14px !important;
color: #F53A19 !important;
font-weight: 400 !important;
line-height: 1.2 !important;
.el-popper__arrow {
display: none !important;
}
}

View File

@@ -71,6 +71,7 @@
white-space: nowrap; white-space: nowrap;
display: inline-block; display: inline-block;
margin: 0 5px; margin: 0 5px;
color: #353636;
.cn-icon-Data { .cn-icon-Data {
color: #575757; color: #575757;
} }
@@ -122,7 +123,8 @@
} }
.date-range-history { .date-range-history {
height: 116px; height: 140px;
line-height: 24px;
overflow-y: auto; overflow-y: auto;
.date-range-history-item { .date-range-history-item {

View File

@@ -0,0 +1,80 @@
.error-component {
position: absolute;
//width: 100%;
//height: 100%;
left: 0;
top: 0;
}
.error-block {
display: inline-block;
//width: 100%;
//max-width: calc(100% - 24px);
//max-height: calc(100% - 24px);
//line-height: 24px;
background: #FFE7E6;
font-size: 14px;
color: #F53A19;
font-weight: 400;
padding: 7.6px 12px;
margin: 12px;
z-index: 3;
overflow: hidden;
text-overflow: ellipsis;
//white-space: nowrap;
word-break: break-all;
border: 1px solid rgba(226,97,84,0.42);
border-radius: 4px;
}
.error-block-info {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 20px);
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
font-size: 14px;
color: #575757;
line-height: 24px;
font-weight: 400;
font-family: NotoSansSChineseRegular, serif;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.new-error-icon {
position: relative;
display: inline-block;
}
.new-error-icon .error-content {
position: absolute;
z-index: 2;
top: -42px;
left: 0;
visibility: hidden;
max-width: 220px;
overflow: scroll;
height: auto;
background: #FFE7E6;
border: 1px solid rgba(226,97,84,0.42);
border-radius: 4px;
font-size: 14px;
color: #F53A19;
font-weight: 400;
padding: 5px 12px;
}
.error-icon-default {
font-size: 16px;
margin-left: -2px;
margin-right: 8px;
}
.error-icon-tooltip {
font-size: 16px !important;
margin-bottom: -1px;
margin-left: 6px;
}

View File

@@ -72,3 +72,4 @@
@import 'views/charts2/dnsEventChart'; @import 'views/charts2/dnsEventChart';
@import './views/charts2/dnsEventChartByPie'; @import './views/charts2/dnsEventChartByPie';
//@import '../chart'; //@import '../chart';
@import './components/common/chart-error';

View File

@@ -13,6 +13,13 @@
font-size: 14px; font-size: 14px;
color: #046ECA; color: #046ECA;
} }
.link-block-error {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 60px);
margin-left: -12px;
margin-top: 10px;
}
.data-grid { .data-grid {
height: 100px; height: 100px;
.egress-row { .egress-row {

View File

@@ -1,46 +1,58 @@
.network-overview-apps { .network-overview-apps {
height: 100%; height: 100%;
.line-select-metric { .network-overview-apps-header {
display: flex; display: flex;
justify-content: flex-end; //justify-content: space-between;
align-items: center;
&>span { .network-overview-apps-title {
font-size: 12px; font-size: 14px;
color: #575757; color: #353636;
font-weight: 400; font-weight: 600;
margin-right: 3px; display: flex;
align-items: flex-end;
} }
.line-select__operation {
height: 24px; .line-select-metric {
margin-left: 3px; display: flex;
box-shadow: none; justify-content: flex-end;
border-radius: 2px; align-items: center;
.el-input__inner {
width: 100px; &>span {
height: 24px;
padding-left: 4px;
line-height: 24px;
font-size: 12px; font-size: 12px;
color: #2C72C6; color: #575757;
font-weight: 400; font-weight: 400;
margin-right: 3px;
} }
.el-input__suffix { .line-select__operation {
display: flex; height: 24px;
.el-input__suffix-inner { margin-left: 3px;
box-shadow: none;
border-radius: 2px;
.el-input__inner {
width: 100px;
height: 24px;
padding-left: 4px;
line-height: 24px; line-height: 24px;
.el-select__caret { font-size: 12px;
color: #2C72C6;
font-weight: 400;
}
.el-input__suffix {
display: flex;
.el-input__suffix-inner {
line-height: 24px; line-height: 24px;
width: 16px; .el-select__caret {
color: #575757; line-height: 24px;
width: 16px;
color: #575757;
}
} }
} }
} }
} }
} }
.app-cards { .app-cards {
display: grid; display: grid;
grid-template-rows: repeat(auto-fill, 100px); grid-template-rows: repeat(auto-fill, 100px);
@@ -48,7 +60,6 @@
grid-gap: 20px; grid-gap: 20px;
width: 100%; width: 100%;
padding-top: 10px; padding-top: 10px;
height: calc(100% - 24px);
.app-card { .app-card {
border: 1px solid #E2E5EC; border: 1px solid #E2E5EC;

View File

@@ -4,7 +4,7 @@
height:calc(100% - 64px); height:calc(100% - 64px);
font-size:12px; font-size:12px;
.tab-hide{ .tab-hide{
margin-top:40px; margin-top:42px;
} }
.cn-chart__tabs { .cn-chart__tabs {
height:100%; height:100%;
@@ -32,13 +32,11 @@
.div-yellow { .div-yellow {
height: 12px; height: 12px;
background: #e5a219; background: #e5a219;
border: 1px solid #e5a219;
border-left: none; border-left: none;
} }
.div-green { .div-green {
height: 12px; height: 12px;
background: #749f4d; background: #749f4d;
border: 1px solid #749f4d;
border-right: none; border-right: none;
} }
} }
@@ -115,6 +113,41 @@
.el-table thead { .el-table thead {
color: $grey; color: $grey;
} }
.el-table__empty-text{
line-height:20px !important;
}
.el-table__header th .cell {
display:flex;
flex-direction:row;
justify-content: center;
align-items: center;
}
.el-table__header tr th:nth-of-type(1) .cell {
justify-content: start;
}
.score-cell {
display: flex !important;
justify-content: center;
.data-score {
border-radius: 10px;
font-size: 12px;
color: #FFFFFF;
font-weight: 500;
height: 20px;
width: 34px;
line-height: 20px;
text-align: center;
}
.data-score-red {
background: #E26154;
}
.data-score-yellow {
background: #E5A219;
}
.data-score-green {
background: #749F4D;
}
}
} }
.el-tabs__header { .el-tabs__header {
margin-bottom: 10px; margin-bottom: 10px;

View File

@@ -179,3 +179,6 @@
color: $grey; color: $grey;
} }
} }
.npm-app-border {
border: 1px solid #E2E5EC;
}

View File

@@ -110,4 +110,10 @@
.el-table--group::after,.el-table--border::after,.el-table::before { .el-table--group::after,.el-table--border::after,.el-table::before {
height: 0px; height: 0px;
} }
.table-error {
position: absolute;
width: 100%;
left: 0;
top: 68px;
}
} }

View File

@@ -3,6 +3,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
.npm-event-title { .npm-event-title {
display: flex;
font-size: 14px; font-size: 14px;
color: #353636; color: #353636;
font-weight: 600; font-weight: 600;

View File

@@ -4,6 +4,7 @@
width: 100%; width: 100%;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
.npm-line-title { .npm-line-title {
display: flex;
font-size: 14px; font-size: 14px;
color: #353636; color: #353636;
font-weight: 500; font-weight: 500;
@@ -14,6 +15,7 @@
margin: 20px 20px 0 20px; margin: 20px 20px 0 20px;
justify-content: space-between; justify-content: space-between;
.npm-line-header-title { .npm-line-header-title {
display: flex;
font-size: 14px; font-size: 14px;
color: #353636; color: #353636;
font-weight: 500; font-weight: 500;

View File

@@ -1,4 +1,5 @@
.cn-chart__map-title { .cn-chart__map-title {
display: flex;
font-size: 14px; font-size: 14px;
color: #353636; color: #353636;
font-weight: 600; font-weight: 600;

View File

@@ -2,6 +2,7 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
.npm-recent-title { .npm-recent-title {
display: flex;
font-size: 14px; font-size: 14px;
color: #353636; color: #353636;
font-weight: 600; font-weight: 600;

View File

@@ -2,6 +2,7 @@
height: 100%; height: 100%;
width: 100%; width: 100%;
.npm-sessions-title { .npm-sessions-title {
display: flex;
font-size: 14px; font-size: 14px;
color: #353636; color: #353636;
font-weight: 500; font-weight: 500;
@@ -27,6 +28,14 @@
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
border-right: none; border-right: none;
} }
.npm-sessions-div-gray {
height: 100%;
background: rgba(113,113,113,0.30);
border: 1px solid rgba(113,113,113,0.30);
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
border-right: none;
}
} }
.npm-sessions-body { .npm-sessions-body {
@@ -45,15 +54,10 @@
display: flex; display: flex;
align-items: center; align-items: center;
.npm-sessions-as-client-i18n { .npm-sessions-as-client-i18n {
ont-size: 12px; font-size: 12px;
color: #717171; color: #717171;
font-weight: 400; font-weight: 400;
} }
.npm-sessions-as-client-percent {
font-size: 18px;
color: #353636;
font-weight: 700;
}
.npm-sessions-as-client-green,.npm-sessions-as-client-red { .npm-sessions-as-client-green,.npm-sessions-as-client-red {
width: 8px; width: 8px;
height: 8px; height: 8px;
@@ -66,6 +70,11 @@
background: red; background: red;
} }
} }
.npm-sessions-as-client-percent {
font-size: 18px;
color: #353636;
font-weight: 700;
}
} }
} }
.npm-sessions-body-right { .npm-sessions-body-right {

View File

@@ -53,6 +53,9 @@
border-radius: 4px; border-radius: 4px;
height: calc(100% - 32px); height: calc(100% - 32px);
width: 100%; width: 100%;
.panel-chart__no-data {
height: calc(100% - 32px);
}
.chart-drawing { .chart-drawing {
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@@ -31,7 +31,6 @@
background: #749F4D; background: #749F4D;
} }
height:24px; height:24px;
font-family: NotoSansHans-Medium;
font-size: 12px; font-size: 12px;
color: #353636; color: #353636;
font-weight: 500; font-weight: 500;
@@ -46,8 +45,30 @@
justify-content: center; justify-content: center;
} }
} }
.panel__time { .panel__tools {
display: flex; display: flex;
&>.el-select {
width: 162px;
margin-right: 10px;
.select-prefix {
font-size: 14px;
color: #999;
padding: 0 6px 0 3px;
}
.el-input__inner {
font-size: 14px;
color: #353636;
background-color: #F5F8FA;
}
.common-select {
top: 32px !important;
}
}
.panel__time {
display: flex;
}
} }
} }
.chart-list { .chart-list {

View File

@@ -18,7 +18,7 @@
align-items: center; align-items: center;
padding-left: 10px; padding-left: 10px;
color: #666; color: #666;
background-color: #F3F7FA; //background-color: #F3F7FA;
cursor: pointer; cursor: pointer;
span { span {
@@ -33,7 +33,18 @@
i.arrow-rotate { i.arrow-rotate {
transform: rotate(90deg) translate(2px, 3px); transform: rotate(90deg) translate(2px, 3px);
} }
.new-detection-filter-header-title {
font-size: 14px;
color: #353636;
font-weight: 600;
}
.new-detection-filter-icon {
margin-left: 8px;
margin-bottom: 2px;
font-weight: bold !important;
}
} }
.filter__body { .filter__body {
padding: 5px 0 0 15px; padding: 5px 0 0 15px;
@@ -69,4 +80,17 @@
} }
} }
} }
.new-detection-filter-title {
display: flex;
flex: 0 0 32px;
align-items: center;
padding-left: 27px;
background-color: #EFF2F5;
cursor: pointer;
font-size: 14px;
color: #353636;
font-weight: 600;
margin: -10px;
margin-bottom: 10px;
}
} }

View File

@@ -40,7 +40,7 @@
height: 100%; height: 100%;
overflow: auto; overflow: auto;
.cn-detection__shadow { .cn-detection__shadow, .new-cn-detection__shadow {
position: fixed; position: fixed;
height: 100vh; height: 100vh;
width:100vw; width:100vw;
@@ -49,6 +49,10 @@
z-index: 1; z-index: 1;
background-color: rgba(0, 0, 0, .2); background-color: rgba(0, 0, 0, .2);
} }
.new-cn-detection__shadow {
z-index: 4;
}
} }
} }

View File

@@ -2,13 +2,22 @@
display: flex; display: flex;
.cn-detection__collapse { .cn-detection__collapse {
margin-bottom: 1px;
padding-top: 18px;
width: 24px; width: 24px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
background-color: #F3F7FA; margin-bottom: 1px;
//padding-top: 18px;
background-color: #EFF2F5;
.cn-detection__collapse-block {
min-height: 66px;
height: 100%;
max-height: 88px;
display: flex;
align-items: center;
cursor: pointer;
}
span { span {
transform: rotate(0); transform: rotate(0);
@@ -99,6 +108,25 @@
line-height: 14px; line-height: 14px;
margin-left: 5px; margin-left: 5px;
} }
.detection-event-severity-color-block {
width: 5px;
height: 20px;
border-radius: 2.5px;
margin-left: -16px;
margin-right: 12px;
}
.detection-event-severity-block {
font-family: NotoSansHans-Medium;
font-size: 12px;
color: #046EC9;
font-weight: 500;
padding: 2px 10px;
background: rgba(56,172,210,0.10);
border: 1px solid #ADC7DB;
box-shadow: 0 2px 4px 0 rgba(51,51,51,0.02);
border-radius: 3px;
margin-right: 10px;
}
} }
.cn-detection__body { .cn-detection__body {

View File

@@ -62,8 +62,11 @@
.row__content { .row__content {
display: flex; display: flex;
color: #3976CB; //color: #3976CB;
color: #046ECA;
font-weight: 500;
font-size: 14px;
&.row__content--link { &.row__content--link {
font-style: italic; font-style: italic;
text-decoration: underline; text-decoration: underline;
@@ -77,6 +80,10 @@
cursor: pointer; cursor: pointer;
} }
span{ span{
font-style: italic;
color: #046ECA;
}
.row__content--span {
font-style: italic; font-style: italic;
color: #1890FF; color: #1890FF;
} }
@@ -169,13 +176,16 @@
span { span {
padding-left: 5px; padding-left: 5px;
font-size: 14px; font-size: 14px;
color: #3976CB; //color: #3976CB;
color: #046ECA;
font-weight: 600 !important;
} }
} }
.timeline__security-type { .timeline__security-type {
font-size: 12px; font-size: 12px;
color: #3976CB; color: #046ECA;
margin-bottom: 10px; margin-bottom: 10px;
font-weight: 500;
} }
.timeline__start-time { .timeline__start-time {
font-size: 12px; font-size: 12px;

View File

@@ -26,7 +26,7 @@
justify-content: flex-start; justify-content: flex-start;
} }
.explorer-top-tools { .explorer-top-tools, .explorer-detection-top-tools {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
@@ -46,6 +46,38 @@
} }
} }
} }
.explorer-detection-top-tools {
display: flex;
justify-content: space-between;
}
.explorer-top-tools-title {
font-size: 24px;
line-height: 24px;
font-weight: 900;
color: #353636;
}
.explorer-top-tools-block {
font-family: NotoSansHans-Medium;
height: 28px;
line-height: 28px;
background: #F5F8FA;
font-size: 14px;
color: #353636;
font-weight: 500;
padding: 0 10px;
margin-right: 10px;
border: 1px solid #E2E5EC;
border-radius: 2px;
cursor: pointer;
}
.detection-icon-setting {
margin-right: 10px;
font-size: 14px;
}
.detection-border {
border: 1px solid #E2E5EC;
border-radius: 4px;
}
.explorer-container { .explorer-container {
display: flex; display: flex;
overflow: visible; /*overflow: hidden;*/ overflow: visible; /*overflow: hidden;*/

View File

@@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "cn-icon"; /* Project id 2614877 */ font-family: "cn-icon"; /* Project id 2614877 */
src: url('iconfont.woff2?t=1663570533591') format('woff2'), src: url('iconfont.woff2?t=1668593055875') format('woff2'),
url('iconfont.woff?t=1663570533591') format('woff'), url('iconfont.woff?t=1668593055875') format('woff'),
url('iconfont.ttf?t=1663570533591') format('truetype'); url('iconfont.ttf?t=1668593055875') format('truetype');
} }
.cn-icon { .cn-icon {
@@ -13,6 +13,46 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.cn-icon-baocuo:before {
content: "\e7b7";
}
.cn-icon-a-SecurityEvent:before {
content: "\e7ae";
}
.cn-icon-bianji1:before {
content: "\e7af";
}
.cn-icon-a-PerformanceEvent:before {
content: "\e7b0";
}
.cn-icon-xinjian:before {
content: "\e7b1";
}
.cn-icon-a-RegulatoryRiskEvent:before {
content: "\e7b2";
}
.cn-icon-Delete1:before {
content: "\e7b3";
}
.cn-icon-a-EvaluatedTarget:before {
content: "\e7b4";
}
.cn-icon-a-GeneralSettings:before {
content: "\e7b5";
}
.cn-icon-Thresholds:before {
content: "\e7b6";
}
.cn-icon-good:before { .cn-icon-good:before {
content: "\e7ad"; content: "\e7ad";
} }

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,149 @@
<template>
<div v-if="showDefault" class="error-component">
<div class="error-block" :style="{'max-width': localMaxWidth, 'width': localWidth}">
<svg class="icon error-icon-default" aria-hidden="true">
<use xlink:href="#cn-icon-baocuo"></use>
</svg>
{{ content }}
</div>
</div>
<div id="error-com">
<div v-if="tooltip !== undefined">
<el-popover
:width="localPopoverWidth"
placement="top-start"
trigger="hover"
:visible-arrow="false"
popper-class="error-popover"
:content="content">
<template #reference>
<span>
<svg class="icon error-icon-tooltip" aria-hidden="true">
<use xlink:href="#cn-icon-baocuo"></use>
</svg>
<!-- 为后续自定义icon插槽做预备-->
<!-- <i v-if="icon" :class="`icon cn-icon-${icon}`"></i>-->
</span>
</template>
</el-popover>
<!-- 不使用popover实现hover后续考虑替换为该方案-->
<!-- <span class="new-error-icon" @mouseenter="hoverError">-->
<!-- <svg class="icon item-popover-up" aria-hidden="true">-->
<!-- <use xlink:href="#cn-icon-baocuo"></use>-->
<!-- </svg>-->
<!-- <div id="error-content" class="error-content">-->
<!-- rview/appCompanyCyclrview/appCompanyCycleTrafficTotal?startTime=getSecond(this.timeFilter.startTime)&endTime=getSecond(this.timeFilter.endTime)&appCompanies=%27Tencent%27,%27Jingdong%27,%27Akamai%27,%27Bytedance%27,%27Baidu%27,%27Huawei%27,%27Beike%27,%27Aiqiyi%27,%27Ctrip%27,%27Meituan%27-->
<!-- </div>-->
<!-- </span>-->
</div>
<div class="error-block-info" v-if="info !== undefined">
<div>
<svg class="icon error-icon-default" aria-hidden="true">
<use xlink:href="#cn-icon-baocuo"></use>
</svg>
{{ content }}
</div>
</div>
</div>
</template>
<!-- start----------------调用方式----------------start -->
<!--
组件在全局注册了调用时: <chart-error :content="content"></chart-error>
-->
<!--
目前有三种形式分别是defaulttooltipinfo
默认即红框展示<chart-error :content="content" />
在标题之后显示需要鼠标移动到图标上显示弹窗<chart-error tooltip :content="content" />
文字提示<chart-error info :content="content" />
-->
<!--
自定义宽度<chart-error width="300" :content="content" />
自定义弹窗宽度<chart-error tooltip width="300" :content="content" />
注意info模式不支持宽度设置
-->
<!--
自定义icon图标<chart-error tooltip icon="baocuo" :content="content" />此时icon全称为'cn-icon-baocuo'
-->
<!-- end----------------调用方式----------------end -->
<script>
export default {
name: 'Error',
props: {
// 工具栏提示类型
tooltip: {
type: String
},
// 文字提示类型
info: {
type: String
},
// 报错信息内容,如果不传,默认为"Error"
content: {
type: String,
default: 'Error'
},
// 报错信息模块宽度如果类型选择tooltip则为弹窗宽度info模式没有宽度设置
width: {
type: String
},
// 报错信息模块最大宽度
maxWidth: {
type: String
// default: '350'
},
// 自定义icon图标
icon: {
type: String
},
// 自定义svg图标
svg: {
type: String
}
},
data () {
return {
showDefault: false, // 是否显示default分别是default、tooltip、info
showSmall: false, // 显示错误的类型true为图表模块内显示报错false为标题后显示报错
localWidth: '',
localMaxWidth: '',
localPopoverWidth: ''
}
},
mounted () {
this.initData()
},
methods: {
initData () {
if (this.tooltip !== undefined) {
this.showDefault = false
this.localPopoverWidth = this.width !== undefined
}
// 默认default模式
this.showDefault = this.tooltip === undefined && this.info === undefined
if (this.width) {
// 避免宽度出现负数的情况
this.localWidth = Math.abs(parseFloat(this.width)) + 'px'
}
if (this.maxWidth) {
// 避免宽度出现负数的情况
this.localMaxWidth = Math.abs(parseFloat(this.maxWidth)) + 'px'
}
},
/**
* 鼠标移入事件用于获取弹窗dom修改距离父元素的top
*/
hoverError (e) {
// const dom = document.getElementById('error-content')
// if (dom) {
// console.log('---', dom.clientHeight)
// }
}
}
}
</script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-ele-click-outside="changeDropdown" style="position: relative" class="date-range-box"> <div v-ele-click-outside="changeDropdown" style="position: relative;z-index: 2" class="date-range-box">
<div @click="showDropdown" class="date-range-text"> <div @click="showDropdown" class="date-range-text">
<div class="calendar-popover-text"><i class="cn-icon cn-icon-Data"></i></div> <div class="calendar-popover-text"><i class="cn-icon cn-icon-Data"></i></div>
<div class="calendar-popover-text" style="display: flex" v-if="isCustom"> <div class="calendar-popover-text" style="display: flex" v-if="isCustom">
@@ -10,25 +10,22 @@
<div class="calendar-popover-text" v-else> <div class="calendar-popover-text" v-else>
{{ showDetail }} {{ showDetail }}
</div> </div>
<div class="calendar-popover-text calendar-popover__small"><i class="cn-icon cn-icon-dropdown" <div class="calendar-popover-text calendar-popover__small">
:class="dropdownFlag ? 'cn-icon-up' : ''"></i></div> <i class="cn-icon cn-icon-dropdown" :class="dropdownFlag ? 'cn-icon-up' : ''"></i>
</div>
</div> </div>
<transition name="el-zoom-in-top"> <transition name="el-zoom-in-top">
<div v-if="dropdownFlag" class="date-range-panel"> <div v-if="dropdownFlag" class="date-range-panel">
<el-row class="date-range-panel-top" style="position: relative"> <el-row class="date-range-panel-top" style="position: relative">
<el-col :span="16" class="date-range-panel-content date-range-panel-content-left"> <el-col :span="16" class="date-range-panel-content date-range-panel-content-left">
<div class="date-range-title" style="padding-left: 0">Absolute time range</div> <div class="date-range-title" style="padding-left: 0">Absolute time range</div>
<MyDatePicker <el-date-picker
:clearable='false' v-model="newDateValue"
:editable='false' ref="newDatePicker"
v-model="timeArr" class="date_style"
style="position: absolute;top: -53px;left: -536px;"
:clearable="false"
type="datetimerange" type="datetimerange"
ref="myDatePicker"
:popper-class="'myDatePicker'"
class="panel-time-picker-hidden"
:size="'small'"
placement="left-start"
style="position: absolute"
@change="timeArrChange" @change="timeArrChange"
/> />
<div class="content-title">From</div> <div class="content-title">From</div>
@@ -39,9 +36,7 @@
<div @click="myDatePickerShow" tabindex="2" class="content-input"> <div @click="myDatePickerShow" tabindex="2" class="content-input">
{{ dateFormatByAppearance(getMillisecond(myEndTime)) }} {{ dateFormatByAppearance(getMillisecond(myEndTime)) }}
</div> </div>
<div>
<el-button @click="timeRange" type="primary" size="mini">Apply time range</el-button>
</div>
<div class="date-range-title" style="padding-left: 0">Recently used absolute ranges</div> <div class="date-range-title" style="padding-left: 0">Recently used absolute ranges</div>
<div class="date-range-history"> <div class="date-range-history">
<div v-for="(item, index) in rangeHistoryArr" :key="index" class="date-range-history-item" <div v-for="(item, index) in rangeHistoryArr" :key="index" class="date-range-history-item"
@@ -52,23 +47,26 @@
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :span="8" class="date-range-panel-content date-range-panel-content-right" <el-col
style="border-left: 1px solid rgba(0,0,0,0.09);"> :span="8"
class="date-range-panel-content date-range-panel-content-right"
style="border-left: 1px solid rgba(0,0,0,0.09);">
<div class="date-range-title">Relatime time ranges</div> <div class="date-range-title">Relatime time ranges</div>
<ul class="date-range-item"> <ul class="date-range-item">
<li v-for="item in dateRangeArr" @click="quickChange(item.value)" <li v-for="item in dateRangeArr"
:class="(item.value==dateRangeValue.value || item.value==dateRangeValue)?'active':''" @click="quickChange(item.value)"
:class="(item.value===dateRangeValue.value || item.value===dateRangeValue)?'active':''"
:key="item.value"> :key="item.value">
<span style="position: relative"> <span style="position: relative">
{{ item.name }} {{ item.name }}
<i v-if="(item.value==dateRangeValue.value || item.value==dateRangeValue)" <i v-if="(item.value===dateRangeValue.value || item.value===dateRangeValue)"
class="cn-icon cn-icon-check"></i> class="cn-icon cn-icon-check"></i>
</span> </span>
</li> </li>
</ul> </ul>
</el-col> </el-col>
</el-row> </el-row>
<el-row class="date-range-panel-bottom" style=""> <el-row class="date-range-panel-bottom">
<el-col :span="12">{{ address }}</el-col> <el-col :span="12">{{ address }}</el-col>
<el-col :span="12" class="utc-str">{{ utcStr }}</el-col> <el-col :span="12" class="utc-str">{{ utcStr }}</el-col>
</el-row> </el-row>
@@ -78,10 +76,10 @@
</template> </template>
<script> <script>
import { ref, computed } from 'vue' import { ref, computed, watch, reactive } from 'vue'
import MyDatePicker from '../MyDatePicker'
import { storageKey } from '@/utils/constants' import { storageKey } from '@/utils/constants'
import { getMillisecond } from '@/utils/date-util' import { getMillisecond, timestampToList } from '@/utils/date-util'
import { useStore } from 'vuex'
export default { export default {
name: 'DateTimeRange', name: 'DateTimeRange',
@@ -97,68 +95,39 @@ export default {
dateRange: { dateRange: {
type: Number type: Number
} }
/* useRefresh: {
type: Boolean,
default: true
},
useDateRange: {
type: Boolean,
default: true
} */
}, },
emits: ['change'], emits: ['change'],
components: {
MyDatePicker
},
setup (props, ctx) { setup (props, ctx) {
// data // data
const store = useStore()
const myStartTime = ref(props.startTime) const myStartTime = ref(props.startTime)
const myEndTime = ref(props.endTime) const myEndTime = ref(props.endTime)
const timeArr = ref([myStartTime.value, myEndTime.value]) // 时间选择器绑定的值
const newDateValue = ref([
new Date(...timestampToList(myStartTime.value)),
new Date(...timestampToList(myEndTime.value))
])
// 时区地址
const address = localStorage.getItem(storageKey.sysTimezone) const address = localStorage.getItem(storageKey.sysTimezone)
// 当前所在时区
const utc = localStorage.getItem(storageKey.timezoneOffset) const utc = localStorage.getItem(storageKey.timezoneOffset)
// 历史选择时间
const rangeHistory = ref(localStorage.getItem(storageKey.dataRangeHistory) ? JSON.parse(localStorage.getItem(storageKey.dataRangeHistory)) : []) const rangeHistory = ref(localStorage.getItem(storageKey.dataRangeHistory) ? JSON.parse(localStorage.getItem(storageKey.dataRangeHistory)) : [])
const dateRangeValue = props.dateRange ? ref(props.dateRange) : ref(60) const dateRangeValue = props.dateRange ? ref(props.dateRange) : ref(60)
const isCustom = ref(dateRangeValue.value === -1) const isCustom = ref(dateRangeValue.value === -1)
const dateRangeArr = [ const dateRangeArr = [
{ { value: 5, name: 'last 5 Min' },
value: 5, { value: 15, name: 'last 15 Min' },
name: 'last 5 Min' { value: 30, name: 'last 30 Min' },
}, { value: 60, name: 'last 1 hour' },
{ { value: 180, name: 'last 3 hour' },
value: 15, { value: 360, name: 'last 6 hour' },
name: 'last 15 Min' { value: 720, name: 'last 12 hour' },
}, { value: 1440, name: 'last 1 days' },
{ { value: 2880, name: 'last 2 days' }
value: 30,
name: 'last 30 Min'
},
{
value: 60,
name: 'last 1 hour'
},
{
value: 180,
name: 'last 3 hour'
},
{
value: 360,
name: 'last 6 hour'
},
{
value: 720,
name: 'last 12 hour'
},
{
value: 1440,
name: 'last 1 days'
},
{
value: 2880,
name: 'last 2 days'
}
] ]
const dropdownFlag = ref(false) const dropdownFlag = ref(false)
// computed // computed
const utcStr = computed(() => { const utcStr = computed(() => {
let str = 'UTC ' let str = 'UTC '
@@ -183,20 +152,40 @@ export default {
} }
return str return str
}) })
const rangeHistoryArr = computed(() => { const rangeHistoryArr = rangeHistory
return rangeHistory.value.slice(0, 4)
})
// refs // refs
const myDatePicker = ref(null) const newDatePicker = ref(null)
// echarts框选时间范围
const rangeEchartsData = reactive({
value: computed(() => store.state.panel.rangeEchartsData)
})
watch(() => rangeEchartsData.value, (newVal, oldVal) => {
if (newVal) {
myStartTime.value = getMillisecond(newVal.startTime)
myEndTime.value = getMillisecond(newVal.endTime)
isCustom.value = true
dateRangeValue.value = -1
returnValue()
}
})
// methods // methods
/**
* 打开/关闭时间面板
*/
const showDropdown = () => { const showDropdown = () => {
dropdownFlag.value = !dropdownFlag.value dropdownFlag.value = !dropdownFlag.value
if (dropdownFlag.value) { if (dropdownFlag.value) {
myStartTime.value = props.startTime myStartTime.value = props.startTime
myEndTime.value = props.endTime myEndTime.value = props.endTime
timeArr.value = [getMillisecond(myStartTime.value), getMillisecond(myEndTime.value)]
} }
} }
/**
* 点击空白处隐藏时间面板
*/
const changeDropdown = () => { const changeDropdown = () => {
if (dropdownFlag.value) { if (dropdownFlag.value) {
dropdownFlag.value = false dropdownFlag.value = false
@@ -205,19 +194,36 @@ export default {
dropdownFlag.value = false dropdownFlag.value = false
} }
} }
/**
* 打开时间选择器,从时间面板的“开始时间”、“结束时间”调用
*/
const myDatePickerShow = () => { const myDatePickerShow = () => {
myDatePicker.value.focus() newDateValue.value = [
myDatePicker.value.pickerVisible = true new Date(...timestampToList(myStartTime.value)),
new Date(...timestampToList(myEndTime.value))
]
newDatePicker.value.focus()
// todo 此处为弹窗打开的方向标识控制css的position修改其left与top属性未生效只好隐藏后续有更好的处理办法再修改
const dom = document.getElementsByClassName('el-picker__popper el-popper is-light is-pure')
const dom1 = dom[0].getElementsByClassName('el-popper__arrow')
dom1[0].style.display = 'none'
} }
/**
* 时间选择器选择时间点击OK后的回调
* @param val开始/结束时间数组
*/
const timeArrChange = (val) => { const timeArrChange = (val) => {
myStartTime.value = getMillisecond(val[0]) myStartTime.value = getMillisecond(val[0])
myEndTime.value = getMillisecond(val[1]) myEndTime.value = getMillisecond(val[1])
}
const timeRange = () => {
isCustom.value = true isCustom.value = true
dateRangeValue.value = -1 dateRangeValue.value = -1
returnValue() returnValue()
} }
/**
* 历史时间列表中点击一项,对时间进行赋值
* @param item
*/
const historyChange = (item) => { const historyChange = (item) => {
myStartTime.value = item.start myStartTime.value = item.start
myEndTime.value = item.end myEndTime.value = item.end
@@ -232,39 +238,64 @@ export default {
myStartTime.value = myEndTime.value - value * 60 * 1000 myStartTime.value = myEndTime.value - value * 60 * 1000
returnValue() returnValue()
} }
/**
* 重置时间,将时间存入缓存,并触发方法请求接口刷新界面
*/
const returnValue = () => { const returnValue = () => {
cancelHttp()
rangeHistory.value.unshift({ rangeHistory.value.unshift({
start: myStartTime.value, start: myStartTime.value,
end: myEndTime.value end: myEndTime.value
}) })
if (rangeHistory.value.length > 4) {
rangeHistory.value.splice(4, rangeHistory.value.length - 1)
}
localStorage.setItem(storageKey.dataRangeHistory, JSON.stringify(rangeHistory.value)) localStorage.setItem(storageKey.dataRangeHistory, JSON.stringify(rangeHistory.value))
ctx.emit('change', myStartTime.value, myEndTime.value, dateRangeValue) ctx.emit('change', myStartTime.value, myEndTime.value, dateRangeValue)
dropdownFlag.value = false dropdownFlag.value = false
} }
/**
* 终止http请求
*/
const cancelHttp = () => {
const cancelList = store.state.panel.httpCancel
if (cancelList.length > 0) {
cancelList.forEach((cancel, index) => {
cancel()
delete cancelList[index]
})
}
}
return { return {
myStartTime, myStartTime,
myEndTime, myEndTime,
getMillisecond,
dropdownFlag, dropdownFlag,
utcStr, utcStr,
rangeEchartsData,
address, address,
dateRangeArr, dateRangeArr,
dateRangeValue, dateRangeValue,
isCustom, isCustom,
timeArr, newDateValue,
myDatePicker, newDatePicker,
showDetail, showDetail,
rangeHistory, rangeHistory,
rangeHistoryArr, rangeHistoryArr,
getMillisecond,
myDatePickerShow, myDatePickerShow,
showDropdown, showDropdown,
changeDropdown, changeDropdown,
timeArrChange, timeArrChange,
returnValue, returnValue,
quickChange, quickChange,
timeRange,
historyChange historyChange
} }
} }
} }
</script> </script>
<style scoped>
/deep/.el-input__inner {
visibility: hidden !important;
}
</style>

View File

@@ -12,8 +12,8 @@
<transition name="el-zoom-in-top"> <transition name="el-zoom-in-top">
<div v-if="dropdownShow" class="refresh-list"> <div v-if="dropdownShow" class="refresh-list">
<div v-for="(item, index) in refreshArr" :key="index" @click="setRefresh(item)" class="refresh-list-item" :class="item.value==interval ? 'active' : ''"> <div v-for="(item, index) in refreshArr" :key="index" @click="setRefresh(item)" class="refresh-list-item" :class="item.value==interval ? 'active' : ''">
{{item.label}} {{ item.label }}
<i v-if="item.value==interval" class="cn-icon cn-icon-check"></i> <i v-if="item.value===interval" class="cn-icon cn-icon-check"></i>
</div> </div>
</div> </div>
</transition> </transition>
@@ -103,7 +103,16 @@ export default {
this.interLabel = val.value == -1 ? '' : val.label this.interLabel = val.value == -1 ? '' : val.label
this.dropdownShow = false this.dropdownShow = false
const now = window.$dayJs.tz().valueOf() const now = window.$dayJs.tz().valueOf()
if (val && val.value != -1) { 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()
delete cancelList[index]
})
}
// 向地址栏添加自动刷新频率 // 向地址栏添加自动刷新频率
const dateParam = { const dateParam = {
refreshTime: val.value refreshTime: val.value
@@ -121,7 +130,7 @@ export default {
} }
return return
} }
if (val && val.value == -1) { if (val && val.value === -1) {
// 清除定时器 // 清除定时器
clearInterval(this.intervalTimer) clearInterval(this.intervalTimer)

View File

@@ -20,7 +20,8 @@ export default {
entityDetectionStyle () { entityDetectionStyle () {
const route = this.$route.name !== undefined ? this.$route.name : this.$route const route = this.$route.name !== undefined ? this.$route.name : this.$route
if (listScrollPath.indexOf(route.path) > -1) { if (listScrollPath.indexOf(route.path) > -1) {
return 'overflow:auto;background-color: #EFF2F5;' const style = route.path === listScrollPath[0] ? 'overflow:auto;background-color: #EFF2F5;' : 'overflow:auto;'
return style
} else { } else {
return '' return ''
} }

View File

@@ -2,7 +2,8 @@
<div class="cn-header"> <div class="cn-header">
<div class="cn-header__banner"> <div class="cn-header__banner">
<div class="banner__left"> <div class="banner__left">
<span @click="shrink" class="shrink-button" :class="{'shrink-button--collapse': showMenu}"><i class="cn-icon cn-icon-navigation"></i></span> <span @click="shrink" class="shrink-button" :class="{'shrink-button--collapse': showMenu}"><i
class="cn-icon cn-icon-navigation"></i></span>
<img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo-header.svg')"/> <img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo-header.svg')"/>
</div> </div>
<!--个人操作--> <!--个人操作-->
@@ -12,23 +13,27 @@
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item> <el-dropdown-item>
<div id="header-to-english" :style="language === 'en'?'color:#0091ff':''" @click="changeLocal('en')">English</div> <div id="header-to-english" :style="language === 'en'?'color:#0091ff':''" @click="changeLocal('en')">
English
</div>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item> <el-dropdown-item>
<div id="header-to-chinese" :style="language === 'cn'?'color:#0091ff':''" @click="changeLocal('cn')">中文</div> <div id="header-to-chinese" :style="language === 'cn'?'color:#0091ff':''" @click="changeLocal('cn')">
中文
</div>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<el-dropdown> <el-dropdown>
<div class='login-user header-menu--item'>{{username}}&nbsp;<i class="cn-icon cn-icon-arrow-down"></i></div> <div class='login-user header-menu--item'>{{ username }}&nbsp;<i class="cn-icon cn-icon-arrow-down"></i></div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item> <el-dropdown-item>
<div id="header-to-changepin" @click="showPinDialog">{{$t('overall.changePassword')}}</div> <div id="header-to-changepin" @click="showPinDialog">{{ $t('overall.changePassword') }}</div>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item> <el-dropdown-item>
<div id="header-to-logout" @click="logout">{{$t('overall.logout')}}</div> <div id="header-to-logout" @click="logout">{{ $t('overall.logout') }}</div>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@@ -37,8 +42,9 @@
</div> </div>
<div class="cn-header__nav"> <div class="cn-header__nav">
<i class="cn-icon cn-icon-a-NetworkAnalytics"></i> <i class="cn-icon cn-icon-a-NetworkAnalytics"></i>
<el-breadcrumb class="header__left-breadcrumb" separator=">"> <el-breadcrumb class="header__left-breadcrumb" :separator="route.indexOf('detection') === -1 ? '>' : ''">
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item}`" :title="item" v-for="(item,index) in breadcrumb" :key="item"> <el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item}`" :title="item"
v-for="(item,index) in breadcrumb" :key="item">
<template v-if="index===3"> <template v-if="index===3">
<div class="header__left-breadcrumb-item-select"> <div class="header__left-breadcrumb-item-select">
<el-popover placement="bottom-start" <el-popover placement="bottom-start"
@@ -52,8 +58,19 @@
@hide="hideBreadcrumbPopover()" @hide="hideBreadcrumbPopover()"
trigger="click"> trigger="click">
<template #reference> <template #reference>
<div class="breadcrumb-button" id="breadcrumbButton" :class="showBackground?'breadcrumb-button__active':''" > <div class="breadcrumb-button" id="breadcrumbButton"
<span id="breadcrumbValue"> {{item}}</span><i class="cn-icon-xiala cn-icon"></i> :class="showBackground?'breadcrumb-button__active':''">
<span id="breadcrumbValue">
<template v-if="curTabProp === 'qtype'">
<span>{{ dnsQtypeMapData.get(item)}}</span>
</template>
<template v-else-if="curTabProp === 'rcode'">
<span>{{ dnsRcodeMapData.get(item)}}</span>
</template>
<template v-else>
<span>{{ item }}</span>
</template>
</span><i class="cn-icon-xiala cn-icon"></i>
</div> </div>
</template> </template>
<el-row type="flex" justify="center" style="width: fit-content;flex-direction: column;"> <el-row type="flex" justify="center" style="width: fit-content;flex-direction: column;">
@@ -63,9 +80,18 @@
v-model="dropDownValue" v-model="dropDownValue"
@input="dropDownSearch"></el-input> @input="dropDownSearch"></el-input>
</div> </div>
<ul class="select-dropdown" id="breadcrumbSelectDropdown" @scroll="scrollList()"> <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?'':''"> <li v-for="item in breadcrumbColumnValueListShow" title='' :key="item" :id="item"
<span>{{item}}</span> class="select-dropdown__item" @click="changeValue(item)" :class="selected?'':''">
<template v-if="curTabProp === 'qtype'">
<span>{{ dnsQtypeMapData.get(item) }}</span>
</template>
<template v-else-if="curTabProp === 'rcode'">
<span>{{ dnsRcodeMapData.get(item) }}</span>
</template>
<template v-else>
<span>{{ item }}</span>
</template>
</li> </li>
</ul> </ul>
</el-row> </el-row>
@@ -73,38 +99,39 @@
</div> </div>
</template> </template>
<template v-else-if="index===2"> <template v-else-if="index===2">
<span v-if="route===wholeScreenRouterMapping.dns" >{{$t(item)}}</span> <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-else class="route-menu" @click="jump(route,item,'',3)">{{ $t(item) }}</span>
</template> </template>
<template v-else-if="index===1"> <template v-else-if="index===1">
<span class="route-menu" @click="jump(route,'','',2)" v-if="route.indexOf('detection') === -1">{{item}}</span> <span class="route-menu" @click="jump(route,'','',2)"
<div class="header__left-breadcrumb-item-select" v-if="route.indexOf('detection') > -1"> v-if="route.indexOf('detection') === -1">{{ item }}</span>
<el-popover placement="bottom-start" <!-- <div class="header__left-breadcrumb-item-select" v-if="route.indexOf('detection') > -1">-->
v-if="route.indexOf('detection') > -1" <!-- <el-popover placement="bottom-start"-->
ref="breadcrumbPopover" <!-- v-if="route.indexOf('detection') > -1"-->
:show-arrow="false" <!-- ref="breadcrumbPopover"-->
:append-to-body="false" <!-- :show-arrow="false"-->
:hide-after="0" <!-- :append-to-body="false"-->
:show-after="0" <!-- :hide-after="0"-->
popper-class="breadcrumb__popper" <!-- :show-after="0"-->
trigger="click"> <!-- popper-class="breadcrumb__popper"-->
<template #reference> <!-- trigger="click">-->
<div class="breadcrumb-button" id="breadcrumbButton2" :class="showBackground?'breadcrumb-button__active':''" v-if="route.indexOf('detection') > -1"> <!-- <template #reference>-->
<span id="breadcrumbValue2"> {{item}}</span><i class="cn-icon-xiala cn-icon"></i> <!-- <div class="breadcrumb-button" id="breadcrumbButton2" :class="showBackground?'breadcrumb-button__active':''" v-if="route.indexOf('detection') > -1">-->
</div> <!-- <span id="breadcrumbValue2"> {{item}}</span><i class="cn-icon-xiala cn-icon"></i>-->
</template> <!-- </div>-->
<el-row type="flex" justify="center" style="width: fit-content;flex-direction: column;"> <!-- </template>-->
<ul class="select-dropdown" id="breadcrumbSelectDropdown2"> <!-- <el-row type="flex" justify="center" style="width: fit-content;flex-direction: column;">-->
<li v-for="item in detectionMenuList" title='' :key="item.name" :id="item.name" class="select-dropdown__item" @click="jump(item.path,'','',2)"> <!-- <ul class="select-dropdown" id="breadcrumbSelectDropdown2">-->
<span>{{$t(item.i18n)}}</span> <!-- <li v-for="item in detectionMenuList" title='' :key="item.name" :id="item.name" class="select-dropdown__item" @click="jump(item.path,'','',2)">-->
</li> <!-- <span>{{$t(item.i18n)}}</span>-->
</ul> <!-- </li>-->
</el-row> <!-- </ul>-->
</el-popover> <!-- </el-row>-->
</div> <!-- </el-popover>-->
<!-- </div>-->
</template> </template>
<template v-else> <template v-else>
<span>{{item}}</span> <span>{{ item }}</span>
</template> </template>
</el-breadcrumb-item> </el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
@@ -122,22 +149,26 @@
<div class="cn-menu__left"> <div class="cn-menu__left">
<div class="left-menu" v-for="menu in otherMenu" :key="menu.id" @click="jump(menu.route,'','',0)"> <div class="left-menu" v-for="menu in otherMenu" :key="menu.id" @click="jump(menu.route,'','',0)">
<i :class="menu.icon"></i> <i :class="menu.icon"></i>
<span>{{$t(menu.i18n || menu.name)}}</span> <span>{{ $t(menu.i18n || menu.name) }}</span>
<i class="cn-icon cn-icon-right"></i> <i class="cn-icon cn-icon-right"></i>
</div> </div>
</div> </div>
<div class="cn-menu__middle"> <div class="cn-menu__middle">
<div class="middle-menus middle-menus--network-analytics"> <div class="middle-menus middle-menus--network-analytics">
<div class="middle-menus__header">{{$t('overall.networkAnalytics')}}</div> <div class="middle-menus__header">{{ $t('overall.networkAnalytics') }}</div>
<div class="middle-menus__body"> <div class="middle-menus__body">
<div style="width: 260px;"> <div style="width: 260px;">
<template v-for="(menu, index) in networkAnalyticsMenu.children" :key="index"> <template v-for="(menu, index) in networkAnalyticsMenu.children" :key="index">
<div class="middle-menu" v-if="index < 5" @click="jump(menu.route,'','',2)">{{$t(menu.i18n || menu.name)}}</div> <div class="middle-menu" v-if="index < 5" @click="jump(menu.route,'','',2)">
{{ $t(menu.i18n || menu.name) }}
</div>
</template> </template>
</div> </div>
<div> <div>
<template v-for="(menu, index) in networkAnalyticsMenu.children" :key="index"> <template v-for="(menu, index) in networkAnalyticsMenu.children" :key="index">
<div class="middle-menu" v-if="index >= 5 && index < 10" @click="jump(menu.route,'','',2)">{{$t(menu.i18n || menu.name)}}</div> <div class="middle-menu" v-if="index >= 5 && index < 10" @click="jump(menu.route,'','',2)">
{{ $t(menu.i18n || menu.name) }}
</div>
</template> </template>
</div> </div>
</div> </div>
@@ -149,12 +180,12 @@
<el-dialog v-model="showChangePin" <el-dialog v-model="showChangePin"
width="30%" width="30%"
:before-close="handleClose"> :before-close="handleClose">
<el-form :rules="changePassFormRules" :model="changePassForm" ref="changePassForm"> <el-form :rules="changePassFormRules" :model="changePassForm" ref="changePassForm">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="24"> <el-col :span="24">
<el-form-item :label="$t('overall.currentPassword')" prop="oldPwd"> <el-form-item :label="$t('overall.currentPassword')" prop="oldPwd">
<el-input v-model="changePassForm.oldPwd" type="password"></el-input> <el-input v-model="changePassForm.oldPwd" type="password"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item :label="$t('overall.newPassword')" prop="newPwd"> <el-form-item :label="$t('overall.newPassword')" prop="newPwd">
@@ -170,8 +201,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="showChangePin = false">{{$t('overall.cancel')}}</el-button> <el-button @click="showChangePin = false">{{ $t('overall.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{$t('overall.update')}}</el-button> <el-button type="primary" @click="submit">{{ $t('overall.update') }}</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
@@ -179,24 +210,29 @@
</template> </template>
<script> <script>
import { useRoute } from 'vue-router'
import { get, put } from '@/utils/http' import { get, put } from '@/utils/http'
import { import {
curTabState, curTabState,
dbDrilldownTableConfig,
entityType, entityType,
networkOverviewSearchUrl,
networkOverviewTabList,
networkTable, networkTable,
operationType, operationType,
storageKey, storageKey,
wholeScreenRouterMapping wholeScreenRouterMapping,
fromRoute
} from '@/utils/constants' } from '@/utils/constants'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { ref } from 'vue' import { ref } from 'vue'
import { combineTabList, getDefaultCurTab, getTabList, overwriteUrl, urlParamsHandler } from '@/utils/tools' import {
combineTabList,
getDefaultCurTab,
getTabList,
overwriteUrl,
urlParamsHandler,
combinDrilldownTableWithUserConfig,
getDnsMapData,
handleSpecialValue
} from '@/utils/tools'
import { getNowTime, getSecond } from '@/utils/date-util' import { getNowTime, getSecond } from '@/utils/date-util'
import { db } from '@/indexedDB'
export default { export default {
name: 'Header', name: 'Header',
@@ -219,17 +255,37 @@ export default {
newPwd2: '' newPwd2: ''
}, },
changePassFormRules: { changePassFormRules: {
oldPwd: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }], oldPwd: [{
newPwd: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }], required: true,
newPwd2: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }, { validator: passwordComparison, trigger: 'blur' }] message: this.$t('validate.required'),
trigger: 'blur'
}],
newPwd: [{
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
}],
newPwd2: [{
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
}, {
validator: passwordComparison,
trigger: 'blur'
}]
}, },
showMenu: false, showMenu: false,
dropDownValue: '', dropDownValue: '',
breadcrumbColumnValueListShow: [], breadcrumbColumnValueListShow: [],
curTabProp: '',
dnsRcodeMapData: new Map(),
dnsQtypeMapData: new Map(),
isDnsMapType: false,
valueMeta: [], valueMeta: [],
showBackground: false, showBackground: false,
selected: false, selected: false,
valueMenuId: '', valueMenuId: '',
fromRoute: fromRoute,
detectionMenuList: [ detectionMenuList: [
{ {
name: 'securityEvents', name: 'securityEvents',
@@ -270,12 +326,24 @@ export default {
}, },
breadcrumb () { breadcrumb () {
const breadcrumbMap = [] const breadcrumbMap = []
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
this.$store.getters.menuList.forEach(menu => { this.$store.getters.menuList.forEach(menu => {
if (this.$_.isEmpty(menu.children) && menu.route) { if (this.$_.isEmpty(menu.children) && menu.route) {
breadcrumbMap.push({ name: this.$t(menu.i18n), path: menu.route, columnName: menu.columnName, columnValue: menu.columnValue }) breadcrumbMap.push({
name: this.$t(menu.i18n),
path: menu.route,
columnName: menu.columnName,
columnValue: menu.columnValue
})
} else if (!this.$_.isEmpty(menu.children)) { } else if (!this.$_.isEmpty(menu.children)) {
menu.children.forEach(child => { 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 }) 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
})
}) })
} }
}) })
@@ -312,14 +380,26 @@ export default {
} }
} }
}, },
mounted () { async mounted () {
this.from = Object.keys(this.entityType)[0] this.from = Object.keys(this.entityType)[0]
// 是否需要dns的qtype和rcode的数据字典
if(this.$route.params.typeName === fromRoute.dnsServiceInsights) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
}
this.initDropdownList() this.initDropdownList()
}, },
setup () { setup () {
const dateRangeValue = 60 const dateRangeValue = 60
const { startTime, endTime } = getNowTime(dateRangeValue) const {
const chartTimeFilter = ref({ startTime, endTime, dateRangeValue }) startTime,
endTime
} = getNowTime(dateRangeValue)
const chartTimeFilter = ref({
startTime,
endTime,
dateRangeValue
})
return { return {
chartTimeFilter, chartTimeFilter,
entityType // 所有entity类型用于header下拉框选择 entityType // 所有entity类型用于header下拉框选择
@@ -350,7 +430,18 @@ export default {
window.location.reload() window.location.reload()
}) })
}, },
initDropdownList () { getCurTabByLabel (label) {
let curTab = null
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
if (curTableInCode && curTableInCode.tabList) {
curTab = curTableInCode.tabList.find(item => item.label == label)
}
return curTab
},
async initDropdownList () {
// 是否需要dns的qtype和rcode的数据字典
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
const currentValue = document.getElementById('breadcrumbValue') ? document.getElementById('breadcrumbValue').innerText : '' const currentValue = document.getElementById('breadcrumbValue') ? document.getElementById('breadcrumbValue').innerText : ''
const columnName = this.getUrlParam(this.curTabState.thirdMenu, '') const columnName = this.getUrlParam(this.curTabState.thirdMenu, '')
let type = 'ip' let type = 'ip'
@@ -369,14 +460,24 @@ export default {
type: type, type: type,
name: this.dropDownValue ? this.dropDownValue : '' name: this.dropDownValue ? this.dropDownValue : ''
} }
get(curTableInCode.url.drilldownList, params).then(response => { get(curTableInCode.url.drilldownList, params).then(async response => {
if (response.code === 200) { if (response.code === 200) {
this.breadcrumbColumnValueListShow = response.data.result this.breadcrumbColumnValueListShow = response.data.result
if(this.from === fromRoute.dnsServiceInsights) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
}
this.$nextTick(() => { this.$nextTick(() => {
this.breadcrumbColumnValueListShow.forEach(item => { this.breadcrumbColumnValueListShow.forEach(item => {
const selectedDom = document.getElementById(item) const selectedDom = document.getElementById(item)
if (selectedDom) { if (selectedDom) {
if (item === currentValue) { let itemName = item
if (this.curTabProp === 'qtype') {
itemName = this.dnsQtypeMapData.get(item)
} else if (this.curTabProp === 'rcode') {
itemName = this.dnsRcodeMapData.get(item)
}
if (itemName === currentValue) {
selectedDom.style.cssText = 'color:#0091ff;font-weight: bold;' selectedDom.style.cssText = 'color:#0091ff;font-weight: bold;'
} else { } else {
selectedDom.style.cssText = '' selectedDom.style.cssText = ''
@@ -407,32 +508,34 @@ export default {
}, },
changeValue (value) { changeValue (value) {
// 设置面包屑显示的内容及hover时的title // 设置面包屑显示的内容及hover时的title
let valName = value
if (this.tab === 'qtype') {
valName = this.dnsQtypeMapData.get(value)
} else if (this.tab === 'rcode') {
valName = this.dnsRcodeMapData.get(value)
}
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
document.getElementById('breadcrumbValue').innerText = value document.getElementById('breadcrumbValue').innerText = value
document.getElementById('breadcrumbButton').setAttribute('title', value) document.getElementById(this.valueMenuId).setAttribute('title', valName)
document.getElementById(this.valueMenuId).setAttribute('title', value)
document.getElementById('breadcrumbButton').click() document.getElementById('breadcrumbButton').click()
// const columnName = this.$store.getters.getBreadcrumbColumnName
const columnName = this.getUrlParam(this.curTabState.thirdMenu, '') const columnName = this.getUrlParam(this.curTabState.thirdMenu, '')
const tabObjGroup = networkOverviewTabList.filter(item => item.label == columnName) const curTab = this.getCurTabByLabel(columnName)
if (tabObjGroup && tabObjGroup.length > 0) { if (curTab) {
const curTab = tabObjGroup[0] const queryCondition = []
if (curTab) { const searchProps = curTab.dillDownProp
const queryCondition = [] if (curTab.prop === 'protocolPort') {
const searchProps = curTab.dillDownProp const valueGroup = value.split(':')
if (curTab.prop === 'protocolPort') { if (valueGroup) {
const valueGroup = value.split(':') queryCondition.push('common_l7_protocol=\'' + valueGroup[0] + '\'')
if (valueGroup) { queryCondition.push('common_server_port=' + valueGroup[1])
queryCondition.push("common_l7_protocol='" + valueGroup[0] + "'")
queryCondition.push('common_server_port=' + valueGroup[1])
}
console.log(queryCondition.join(' AND '))
this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' AND ')
} else {
searchProps.forEach(item => {
queryCondition.push(item + "='" + value + "'")
})
this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' OR ')
} }
console.log(queryCondition.join(' AND '))
this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' AND ')
} else {
searchProps.forEach(item => {
queryCondition.push(item + '=\'' + handleSpecialValue(value) + '\'')
})
this.urlChangeParams[this.curTabState.queryCondition] = queryCondition.join(' OR ')
} }
} }
this.changeUrlTabState() this.changeUrlTabState()
@@ -444,7 +547,10 @@ export default {
submit () { submit () {
this.$refs.changePassForm.validate((valid) => { this.$refs.changePassForm.validate((valid) => {
if (valid) { if (valid) {
put(api.pin, { oldPin: this.changePassForm.oldPwd, newPin: this.changePassForm.newPwd }).then(res => { put(api.pin, {
oldPin: this.changePassForm.oldPwd,
newPin: this.changePassForm.newPwd
}).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.$message.success('Success') this.$message.success('Success')
this.showChangePin = false this.showChangePin = false
@@ -466,21 +572,9 @@ export default {
this.urlChangeParams = {} this.urlChangeParams = {}
}, },
async handleCurDrilldownTableConfig (thirdMenu, fourthMenu) { async handleCurDrilldownTableConfig (thirdMenu, fourthMenu) {
const userId = localStorage.getItem(storageKey.userId) // const userId = localStorage.getItem(storageKey.userId)
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview' const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const drillDownTableConfigs = await combinDrilldownTableWithUserConfig()
// 先从localStorage中获取用户定制的自定义配置如果没有则使用默认的自定义配置
const userLocalConfig = await db[dbDrilldownTableConfig].get({ id: userId })
let drillDownTableConfigs = []
if (userLocalConfig) {
drillDownTableConfigs = userLocalConfig.config
}
if (!drillDownTableConfigs || drillDownTableConfigs.length === 0) { // 未找到当前用户的配置,使用默认配置
const defaultConfig = await db[dbDrilldownTableConfig].get({ id: 'default' })
if (defaultConfig) {
drillDownTableConfigs = defaultConfig.config
}
}
const currentTableConfig = drillDownTableConfigs.find(config => config.route === tableType) const currentTableConfig = drillDownTableConfigs.find(config => config.route === tableType)
const tables = currentTableConfig ? currentTableConfig.tables : [] const tables = currentTableConfig ? currentTableConfig.tables : []
const commonTabList = currentTableConfig ? currentTableConfig.tabs : [] const commonTabList = currentTableConfig ? currentTableConfig.tabs : []
@@ -499,10 +593,6 @@ export default {
} }
} }
} }
await db[dbDrilldownTableConfig].put({
id: userId,
config: this.$_.cloneDeep(drillDownTableConfigs)
})
}, },
jump (route, columnName, columnValue, opeType) { jump (route, columnName, columnValue, opeType) {
this.showMenu = false this.showMenu = false
@@ -511,7 +601,9 @@ export default {
this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true) this.urlChangeParams[this.curTabState.tabOperationBeforeType] = this.getUrlParam(this.curTabState.tabOperationType, '', true)
this.urlChangeParams[this.curTabState.tabOperationType] = opeType this.urlChangeParams[this.curTabState.tabOperationType] = opeType
if (opeType === 3) { if (opeType === 3) {
this.urlChangeParams.queryCondition = '' if (route !== '/panel/networkOverview') {
this.urlChangeParams.queryCondition = ''
}
} }
} else { } else {
this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu this.urlChangeParams[this.curTabState.tabOperationType] = operationType.mainMenu
@@ -519,7 +611,6 @@ export default {
if (!columnName) { // 点击第二级菜单 if (!columnName) { // 点击第二级菜单
this.$store.commit('setNetworkOverviewTabList', []) this.$store.commit('setNetworkOverviewTabList', [])
} }
// 清空网络概况的特殊面包屑 // 清空网络概况的特殊面包屑
this.$store.getters.menuList.forEach(menu => { this.$store.getters.menuList.forEach(menu => {
if (!this.$_.isEmpty(menu.children)) { if (!this.$_.isEmpty(menu.children)) {
@@ -530,9 +621,11 @@ export default {
child.columnName = columnName child.columnName = columnName
this.urlChangeParams[this.curTabState.thirdMenu] = columnName this.urlChangeParams[this.curTabState.thirdMenu] = columnName
this.urlChangeParams[this.curTabState.fourthMenu] = columnValue this.urlChangeParams[this.curTabState.fourthMenu] = columnValue
const tabObjGroup = networkOverviewTabList.filter(item => item.label == columnName) // const tabObjGroup = networkOverviewTabList.filter(item => item.label == columnName)
const type = tabObjGroup && tabObjGroup[0] ? tabObjGroup[0].prop : '' // let curTab = this.getCurTabByLabel()
this.urlChangeParams[this.curTabState.dimensionType] = type // const type = curTab ? curTab.prop : ''
// this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
// this.urlChangeParams[this.curTabState.dimensionType] = type
this.urlChangeParams[this.curTabState.panelName] = columnValue this.urlChangeParams[this.curTabState.panelName] = columnValue
} else if (columnName) { // 点击的为列名 } else if (columnName) { // 点击的为列名
child.columnValue = '' child.columnValue = ''
@@ -569,11 +662,20 @@ export default {
}) })
} else if (opeType === 3) { } else if (opeType === 3) {
this.$router.push({ this.$router.push({
query: { ...this.$route.query, fourthPanel: '', t: +new Date() } query: {
...this.$route.query,
fourthPanel: '',
t: +new Date()
}
}) })
} else if (opeType != 4) { } else if (opeType != 4) {
this.$router.push({ this.$router.push({
query: { ...this.$route.query, fourthPanel: '', thirdPanel: '', t: +new Date() } query: {
...this.$route.query,
fourthPanel: '',
thirdPanel: '',
t: +new Date()
}
}) })
} }
if (route === this.route) { if (route === this.route) {

View File

@@ -74,6 +74,9 @@
size="small" size="small"
:format="dateFormat" :format="dateFormat"
:disabled="!!editObject.id" :disabled="!!editObject.id"
:disabled-date="startDisabledDate"
@change="startTimeChang"
@focus="startFocus"
prefix-icon="cn-icon cn-icon-shijian" prefix-icon="cn-icon cn-icon-shijian"
type="datetime" type="datetime"
placeholder=" " placeholder=" "
@@ -91,6 +94,9 @@
size="small" size="small"
:format="dateFormat" :format="dateFormat"
:disabled="!!editObject.id" :disabled="!!editObject.id"
:disabled-date="endDisabledDate"
@change="endTimeChange"
@focus="endFocus"
prefix-icon="cn-icon cn-icon-shijian" prefix-icon="cn-icon cn-icon-shijian"
type="datetime" type="datetime"
placeholder=" " placeholder=" "
@@ -277,6 +283,7 @@ import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
import { get, post, put } from '@/utils/http' import { get, post, put } from '@/utils/http'
import { dateFormat, getMillisecond } from '@/utils/date-util' import { dateFormat, getMillisecond } from '@/utils/date-util'
import { ref } from 'vue'
const paramValidator = (rule, value, callback) => { const paramValidator = (rule, value, callback) => {
let validate = true let validate = true
if (value && value.length > 0) { if (value && value.length > 0) {
@@ -305,6 +312,58 @@ export default {
categoryList: Array, categoryList: Array,
currentCategoryId: Number currentCategoryId: Number
}, },
setup () {
const startTime = ref('')
const endTime = ref('')
const focus = ref('')
const focusDate = ref('')
function endTimeChange (val) {
endTime.value = val
}
function startTimeChang (val) {
startTime.value = val
}
function startFocus (val) {
focus.value = val.target.value
}
function endFocus (val) {
focusDate.value = val.target.value
}
const endDisabledDate = (time) => {
if (time.getTime() > new Date()) {
return true
}
if (startTime.value != '' && startTime.value > time) {
return true
}
if (focusDate.value != '' && endTime.value > time) {
return false
} else if (endTime.value != '' && endTime.value < time) {
return true
}
}
const startDisabledDate = (time) => {
if (time.getTime() > new Date()) {
return true
}
if (focus.value != '' && startTime.value > time) {
return false
} else if (startTime.value != '' && startTime.value > time) {
return true
}
if (endTime.value != '' && endTime.value < time) {
return true
}
}
return {
endDisabledDate,
startDisabledDate,
startTimeChang,
endTimeChange,
startFocus,
endFocus
}
},
data () { data () {
return { return {
url: api.reportTemp, url: api.reportTemp,

View File

@@ -19,7 +19,6 @@
<el-table-column type="expand" width="30"> <el-table-column type="expand" width="30">
<template #default="props"> <template #default="props">
<div class="down"> <div class="down">
<loading :loading="loadingDown"></loading>
<div class="block drop-down-time"> <div class="block drop-down-time">
<el-date-picker <el-date-picker
v-model="timeRange" v-model="timeRange"
@@ -34,6 +33,7 @@
/> />
</div> </div>
<div class="expand"> <div class="expand">
<loading :loading="loadingDown"></loading>
<chart-no-data v-if="downDataList.length === 0 && !loadingDown"></chart-no-data> <chart-no-data v-if="downDataList.length === 0 && !loadingDown"></chart-no-data>
<div class="expand-cell" v-for="(item, index) in downDataList" :key="index"> <div class="expand-cell" v-for="(item, index) in downDataList" :key="index">
<div class="expand-right"> <div class="expand-right">

View File

@@ -9,7 +9,7 @@ import commonMixin from '@/mixins/common'
import { cancelWithChange, noData } from '@/utils/tools' import { cancelWithChange, noData } from '@/utils/tools'
import { ClickOutside } from 'element-plus/lib/directives' import { ClickOutside } from 'element-plus/lib/directives'
import i18n from '@/i18n' import i18n from '@/i18n'
import '@/mock/index.js' // import '@/mock/index.js'
import hljsVuePlugin from '@highlightjs/vue-plugin' import hljsVuePlugin from '@highlightjs/vue-plugin'
import 'highlight.js/styles/color-brewer.css' import 'highlight.js/styles/color-brewer.css'
import '@/assets/css/main.scss' // 样式入口 import '@/assets/css/main.scss' // 样式入口
@@ -20,18 +20,13 @@ import bus from 'tiny-emitter'
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange' import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
import TimeRefresh from '@/components/common/TimeRange/TimeRefresh' import TimeRefresh from '@/components/common/TimeRange/TimeRefresh'
import PanelChartList from '@/views/charts/PanelChartList' import PanelChartList from '@/views/charts/PanelChartList'
import Error from '@/components/common/Error'
import 'lib-flexible' import 'lib-flexible'
const emitter = new bus() const emitter = new bus()
const _ = require('lodash') // lodash工具 const _ = require('lodash') // lodash工具
/* dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(advancedFormat)
dayjs.extend(weekday)
window.$dayJs = dayjs */
const app = createApp(App) const app = createApp(App)
app.use(router) app.use(router)
@@ -52,6 +47,7 @@ app.mixin(commonMixin)
app.component('date-time-range', DateTimeRange) app.component('date-time-range', DateTimeRange)
app.component('time-refresh', TimeRefresh) app.component('time-refresh', TimeRefresh)
app.component('panel-chart-list', PanelChartList) app.component('panel-chart-list', PanelChartList)
app.component('chart-error', Error)
app.mount('#app') app.mount('#app')

View File

@@ -51,7 +51,15 @@ const panel = {
npmLocationCountry: '', // npm location的查询条件--国家 npmLocationCountry: '', // npm location的查询条件--国家
npmLocationSide: 'server', // npm location的查询条件--方向 npmLocationSide: 'server', // npm location的查询条件--方向
refreshTime: null, // 自动刷新时间的秒数 refreshTime: null, // 自动刷新时间的秒数
refreshFlag: true // 关闭自动刷新标志true为offfalse即开启自动刷新 refreshFlag: true, // 关闭自动刷新标志true为offfalse即开启自动刷新
timeRangeArray: [], // 时间范围列表:开始/结束时间
timeRangeFlag: null, // 时间范围标志默认60即一小时-1为手动选择的时间范围
routerPath: '', // 当前路由路径
httpCancel: null, // 终止http请求
rangeEchartsData: {}, // 框选echarts图表
routerHistoryList: [], // 路由跳转记录列表
dnsQtypeMapData:[],
dnsRcodeMapData:[]
}, },
mutations: { mutations: {
setShowRightBox (state, flag) { setShowRightBox (state, flag) {
@@ -102,36 +110,15 @@ const panel = {
setCurrentMap (state, currentMap) { setCurrentMap (state, currentMap) {
state.currentMap = currentMap state.currentMap = currentMap
}, },
// setPanelName (state, panelName) { setDnsQtypeMapData (state, dnsQtypeMapData) {
// state.panelName = panelName state.dnsQtypeMapData = dnsQtypeMapData
// }, },
// setBreadcrumbColumnName (state, breadcrumbColumnName) { setDnsRcodeMapData (state, dnsRcodeMapData) {
// state.breadcrumbColumnName = breadcrumbColumnName state.dnsRcodeMapData = dnsRcodeMapData
// }, },
// setDimensionType (state, dimensionType) {
// state.dimensionType = dimensionType
// },
// setBreadcrumbColumnValue (state, breadcrumbColumnValue) {
// state.breadcrumbColumnValue = breadcrumbColumnValue
// },
// setNetworkOverviewCurrentTab (state, networkOverviewCurrentTab) {
// state.networkOverviewCurrentTab = networkOverviewCurrentTab
// },
// setQueryCondition (state, queryCondition) {
// state.queryCondition = queryCondition
// },
setNetworkOverviewTabList (state, networkOverviewTabList) { setNetworkOverviewTabList (state, networkOverviewTabList) {
state.networkOverviewTabList = networkOverviewTabList state.networkOverviewTabList = networkOverviewTabList
}, },
// setTabOperationType (state, tabOperationType) {
// state.tabOperationType = tabOperationType
// },
// setNetworkOverviewBeforeTab (state, networkOverviewBeforeTab) {
// state.networkOverviewBeforeTab = networkOverviewBeforeTab
// },
// setTabOperationBeforeType (state, tabOperationBeforeType) {
// state.tabOperationBeforeType = tabOperationBeforeType
// },
setNpmLocationCountry (state, country) { setNpmLocationCountry (state, country) {
state.npmLocationCountry = country state.npmLocationCountry = country
}, },
@@ -146,6 +133,24 @@ const panel = {
}, },
setRefreshFlag (state, flag) { setRefreshFlag (state, flag) {
state.refreshFlag = flag state.refreshFlag = flag
},
setTimeRangeArray (state, array) {
state.timeRangeArray = array
},
setTimeRangeFlag (state, flag) {
state.timeRangeFlag = flag
},
setRouterPath (state, path) {
state.routerPath = path
},
setHttpCancel (state, cancel) {
state.httpCancel = cancel
},
setRangeEchartsData (state, data) {
state.rangeEchartsData = data
},
setRouterHistoryList (state, list) {
state.routerHistoryList = list
} }
}, },
getters: { getters: {
@@ -191,36 +196,15 @@ const panel = {
getCurrentMap (state) { getCurrentMap (state) {
return state.currentMap return state.currentMap
}, },
// getPanelName (state) { getDnsQtypeMapData (state) {
// return state.panelName return state.dnsQtypeMapData
// }, },
// getBreadcrumbColumnName (state) { getDnsRcodeMapData (state) {
// return state.breadcrumbColumnName return state.dnsRcodeMapData
// }, },
// getDimensionType (state) {
// return state.dimensionType
// },
// getBreadcrumbColumnValue (state) {
// return state.breadcrumbColumnValue
// },
// getNetworkOverviewCurrentTab (state) {
// return state.networkOverviewCurrentTab
// },
// getQueryCondition (state) {
// return state.queryCondition
// },
getNetworkOverviewTabList (state) { getNetworkOverviewTabList (state) {
return state.networkOverviewTabList return state.networkOverviewTabList
}, },
// getTabOperationType (state) {
// return state.tabOperationType
// },
// getNetworkOverviewBeforeTab (state) {
// return state.networkOverviewBeforeTab
// },
// getTabOperationBeforeType (state) {
// return state.tabOperationBeforeType
// },
getNpmLocationCountry (state) { getNpmLocationCountry (state) {
return state.npmLocationCountry return state.npmLocationCountry
}, },
@@ -229,6 +213,18 @@ const panel = {
}, },
getNpmThirdLevelMenuScore (state) { getNpmThirdLevelMenuScore (state) {
return state.npmThirdLevelMenuScore return state.npmThirdLevelMenuScore
},
getTimeRangeArray (state) {
return state.timeRangeArray
},
getTimeRangeFlag (state) {
return state.timeRangeFlag
},
getRouterPath (state) {
return state.routerPath
},
getRouterHistoryList (state) {
return state.routerHistoryList
} }
}, },
actions: { actions: {

View File

@@ -3,6 +3,7 @@ import router from '@/router'
import { sortByOrderNum, getWelcomeMenu } from '@/permission' import { sortByOrderNum, getWelcomeMenu } from '@/permission'
import { ElMessage } from 'element-plus' // dependent on utc plugin import { ElMessage } from 'element-plus' // dependent on utc plugin
import { storageKey, dbDrilldownTableConfig } from '@/utils/constants' import { storageKey, dbDrilldownTableConfig } from '@/utils/constants'
import { getConfigVersion } from '@/utils/tools'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { db } from '@/indexedDB' import { db } from '@/indexedDB'
@@ -11,7 +12,8 @@ const user = {
return { return {
menuList: [], menuList: [],
buttonList: [], buttonList: [],
roleList: [] roleList: [],
drilldownTableConfigList: []
} }
}, },
mutations: { mutations: {
@@ -24,6 +26,9 @@ const user = {
setRoleList (state, roleList) { setRoleList (state, roleList) {
state.roleList = [...roleList] state.roleList = [...roleList]
}, },
setDrilldownTableList (state, drilldownTableConfigList) {
state.drilldownTableConfigList = [...drilldownTableConfigList]
},
clean (state) { clean (state) {
state.menuList = [] state.menuList = []
state.buttonList = [] state.buttonList = []
@@ -39,6 +44,9 @@ const user = {
}, },
roleList (state) { roleList (state) {
return state.roleList return state.roleList
},
drilldownTableConfigList (state) {
return state.drilldownTableConfigList
} }
}, },
actions: { actions: {
@@ -82,13 +90,18 @@ const user = {
}) })
get(api.config, { ckey: 'drill_down_table_config' }).then(async res => { get(api.config, { ckey: 'drill_down_table_config' }).then(async res => {
if (res.code === 200 && res.page.list && res.page.list.length > 0) { if (res.code === 200 && res.page.list && res.page.list.length > 0) {
// 从接口返回整体配置,再读取用户缓存,将对应条目覆盖,作为使用的配置
const defaultConfigs = JSON.parse(res.page.list[0].cvalue)
await db[dbDrilldownTableConfig].put({ await db[dbDrilldownTableConfig].put({
id: 'default', id: 'default',
config: JSON.parse(res.page.list[0].cvalue) version: defaultConfigs.version,
config: defaultConfigs.config
}) })
// const a = await db[dbDrilldownTableConfig].get({ id: 'default' }) let userId = localStorage.getItem(storageKey.userId)
// console.info(a) let oldVersion = await getConfigVersion(userId)
// localStorage.setItem(storageKey.drillDownTableConfig, res.page.list[0].cvalue) if(oldVersion !== defaultConfigs.version ){
db[dbDrilldownTableConfig].delete(userId)
}
} }
}) })
get(api.config, { ckey: 'link_info' }).then(res => { get(api.config, { ckey: 'link_info' }).then(res => {

View File

@@ -161,12 +161,19 @@ export const api = {
appSslConDelay: '/interface/application/performance/overview/appSslConDelay', appSslConDelay: '/interface/application/performance/overview/appSslConDelay',
appTcpLostlenPercent: '/interface/application/performance/overview/appTcpLostlenPercent', appTcpLostlenPercent: '/interface/application/performance/overview/appTcpLostlenPercent',
appPacketRetransPercent: '/interface/application/performance/overview/appPacketRetransPercent', appPacketRetransPercent: '/interface/application/performance/overview/appPacketRetransPercent',
// 整体流量折线图
totalTrafficAnalysis: '/interface/application/performance/overview/totalTrafficAnalysis',
totalNetworkAnalysis: '/interface/application/performance/overview/totalNetworkAnalysis',
totalHttpResponseDelay: '/interface/application/performance/overview/totalHttpResponseDelay',
totalSslConDelay: '/interface/application/performance/overview/totalSslConDelay',
// 各维度下钻会话统计 // 各维度下钻会话统计
relatedSessions: '/interface/application/performance/relatedSessions', relatedSessions: '/interface/application/performance/relatedSessions',
// 各维度下钻流量曲线图 // 各维度下钻流量曲线图
trafficGraph: '/interface/application/performance/overview/drilldown/drilldown/dimension/trafficGraph', trafficGraph: '/interface/application/performance/overview/drilldown/drilldown/dimension/trafficGraph',
// 各维度下钻网络性能 // 各维度下钻网络性能
networkAnalysis: '/interface/application/performance/overview/drilldown/dimension/networkAnalysis', networkAnalysis: '/interface/application/performance/overview/drilldown/dimension/networkAnalysis',
// 各维度下钻网络性能 三级菜单
allNetworkAnalysis: '/interface/application/performance/overview/drilldown/dimension/allNetworkAnalysis',
// 下钻地图 // 下钻地图
map: '/interface/application/performance/overview/drilldown/dimension/clientLocations/world/trafficAnalysis', map: '/interface/application/performance/overview/drilldown/dimension/clientLocations/world/trafficAnalysis',
mapTcp: '/interface/application/performance/overview/drilldown/dimension/clientLocations/world/tcpSessionDelay', mapTcp: '/interface/application/performance/overview/drilldown/dimension/clientLocations/world/tcpSessionDelay',
@@ -220,7 +227,8 @@ export const api = {
recentEvents: '/interface/dnsInsight/recentEvents', recentEvents: '/interface/dnsInsight/recentEvents',
activeMaliciousDomain: '/interface/dnsInsight/activeMaliciousDomain', activeMaliciousDomain: '/interface/dnsInsight/activeMaliciousDomain',
totalTrafficAnalysis: '/interface/dns/overview/totalTrafficAnalysis', totalTrafficAnalysis: '/interface/dns/overview/totalTrafficAnalysis',
eventChart: '/interface/dnsInsight/eventChart' eventChart: '/interface/dnsInsight/eventChart',
drilldownTrafficAnalysis: '/interface/dns/overview/drilldown/trafficAnalysis'
} }
} }

View File

@@ -31,7 +31,7 @@ export const storageKey = {
echartLegendFontSize: 'echartLegendFontSize', echartLegendFontSize: 'echartLegendFontSize',
echartLabelFontSize: 'echartLabelFontSize', echartLabelFontSize: 'echartLabelFontSize',
tokenExpireCurrentPath: 'token-expire-current-path', tokenExpireCurrentPath: 'token-expire-current-path',
drillDownTableConfig: 'cn-drill-down-table-config', drillDownTableConfig: 'drilldownTableConfig',
userCustomizationConfig: 'userCustomizationConfig', userCustomizationConfig: 'userCustomizationConfig',
linkInfo: 'cn-link-info', linkInfo: 'cn-link-info',
history: 'cn-history' history: 'cn-history'
@@ -186,6 +186,21 @@ export const networkOverviewTabs = [
'network.protocolPorts' 'network.protocolPorts'
] */ ] */
export const metricOptions = [
{
value: 'Bits/s',
label: 'Bits/s'
},
{
value: 'Packets/s',
label: 'Packets/s'
},
{
value: 'Sessions/s',
label: 'Sessions/s'
}
]
export const operationType = { export const operationType = {
mainMenu: 0, // 菜单 mainMenu: 0, // 菜单
secondMenu: 2, // 二级菜单 secondMenu: 2, // 二级菜单
@@ -276,7 +291,8 @@ export const curTabState = {
fourthPanel: 'fourthPanel', fourthPanel: 'fourthPanel',
networkOverviewBeforeTab: 'networkOverviewBeforeTab', networkOverviewBeforeTab: 'networkOverviewBeforeTab',
tabOperationType: 'tabOperationType', tabOperationType: 'tabOperationType',
tabOperationBeforeType: 'tabOperationBeforeType' tabOperationBeforeType: 'tabOperationBeforeType',
tabIndex: 'tabIndex'
} }
export const scoreUrl = [ export const scoreUrl = [
@@ -287,11 +303,11 @@ export const scoreUrl = [
// AppPerformance类型表格的列有属性cycleDataUrl的代表此数据的来源为对应接口返回的数据无cycleDataUrl的属性代表数据来源于主urlnpmSearchUrl.curUrl、npmSearchUrl.cycleUrl、npmSearchUrl.drilldownCurUrl、npmSearchUrl.drilldownCycleUrl // AppPerformance类型表格的列有属性cycleDataUrl的代表此数据的来源为对应接口返回的数据无cycleDataUrl的属性代表数据来源于主urlnpmSearchUrl.curUrl、npmSearchUrl.cycleUrl、npmSearchUrl.drilldownCurUrl、npmSearchUrl.drilldownCycleUrl
export const customTableTitlesForAppPerformance = [ export const customTableTitlesForAppPerformance = [
{ label: 'network.ips', prop: 'tab', checked: true, tabColumn: true, columnType: tableColumnType.dillDown }, { label: 'IP', prop: 'tab', checked: true, tabColumn: true, columnType: tableColumnType.dillDown },
{ label: 'network.score', prop: 'score', checked: true, tabColumn: false, columnType: tableColumnType.normal }, { label: 'network.score', prop: 'score', checked: true, tabColumn: false, columnType: tableColumnType.normal },
{ {
label: 'networkAppPerformance.throughput', label: 'networkAppPerformance.throughput',
prop: 'through', prop: 'totalBytes',
checked: true, checked: true,
tabColumn: false, tabColumn: false,
columnType: tableColumnType.chainRatio, columnType: tableColumnType.chainRatio,
@@ -375,11 +391,11 @@ export const customTableTitlesForAppPerformance = [
} }
] ]
export const customTableTitlesForLinkMonitor = [ export const customTableTitlesForLinkMonitor = [
{ label: 'network.ips', prop: 'tab', checked: true, tabColumn: true, columnType: tableColumnType.dillDown }, { label: 'IP', prop: 'tab', checked: true, tabColumn: true, columnType: tableColumnType.dillDown },
{ label: 'network.score', prop: 'score', checked: true, tabColumn: false, columnType: tableColumnType.normal }, { label: 'network.score', prop: 'score', checked: true, tabColumn: false, columnType: tableColumnType.normal },
{ {
label: 'networkAppPerformance.throughput', label: 'networkAppPerformance.throughput',
prop: 'through', prop: 'totalBytes',
checked: true, checked: true,
tabColumn: false, tabColumn: false,
columnType: tableColumnType.chainRatio, columnType: tableColumnType.chainRatio,
@@ -479,7 +495,7 @@ export const customTableTitlesForDns = [
] ]
// NetworkOverview类型表格的列:prop 为接口响应数据中的属性名 // NetworkOverview类型表格的列:prop 为接口响应数据中的属性名
export const customTableTitlesForNetworkOverview = [ export const customTableTitlesForNetworkOverview = [
{ label: 'network.ips', prop: 'tab', checked: true, tabColumn: true, columnType: tableColumnType.dillDown }, { label: 'IP', prop: 'tab', checked: true, tabColumn: true, columnType: tableColumnType.dillDown },
{ {
label: 'network.total', label: 'network.total',
prop: 'total', prop: 'total',
@@ -637,6 +653,118 @@ export const networkOverviewTabList = [
checked: false, checked: false,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientIps',
prop: 'clientIp',
queryCycleTotalProp: 'clientIps',
dillDownProp: ['common_client_ip'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverIps',
prop: 'serverIp',
queryCycleTotalProp: 'serverIps',
dillDownProp: ['common_server_ip'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientCountries',
prop: 'clientCountry',
queryCycleTotalProp: 'clientCountries',
dillDownProp: ['client_country'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverCountries',
prop: 'serverCountry',
queryCycleTotalProp: 'serverCountries',
dillDownProp: ['server_country'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientProvinces',
prop: 'clientProvince',
queryCycleTotalProp: 'clientProvinces',
dillDownProp: ['client_province'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverProvinces',
prop: 'serverProvince',
queryCycleTotalProp: 'serverProvinces',
dillDownProp: ['server_province'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientCities',
prop: 'clientCity',
queryCycleTotalProp: 'clientCities',
dillDownProp: ['client_region'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverCities',
prop: 'serverCity',
queryCycleTotalProp: 'serverCities',
dillDownProp: ['server_region'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientIsps',
prop: 'clientIsp',
queryCycleTotalProp: 'clientIsps',
dillDownProp: ['client_isp'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverIsps',
prop: 'serverIsp',
queryCycleTotalProp: 'serverIsps',
dillDownProp: ['server_isp'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientIdcRenters',
prop: 'clientIdcRenter',
queryCycleTotalProp: 'clientIdcRenters',
dillDownProp: ['client_idc_renter'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverIdcRenters',
prop: 'serverIdcRenter',
queryCycleTotalProp: 'serverIdcRenters',
dillDownProp: ['server_idc_renter'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.clientAsns',
prop: 'clientAsn',
queryCycleTotalProp: 'clientAsns',
dillDownProp: ['client_asn'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
}, {
label: 'network.serverAsns',
prop: 'serverAsn',
queryCycleTotalProp: 'serverAsns',
dillDownProp: ['server_asn'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.networkOverview
} }
] ]
@@ -769,6 +897,118 @@ export const networkAppPerformanceTabList = [
checked: false, checked: false,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientIps',
prop: 'clientIp',
queryCycleTotalProp: 'clientIps',
dillDownProp: ['common_client_ip'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverIps',
prop: 'serverIp',
queryCycleTotalProp: 'serverIps',
dillDownProp: ['common_server_ip'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientCountries',
prop: 'clientCountry',
queryCycleTotalProp: 'clientCountries',
dillDownProp: ['client_country'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverCountries',
prop: 'serverCountry',
queryCycleTotalProp: 'serverCountries',
dillDownProp: ['server_country'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientProvinces',
prop: 'clientProvince',
queryCycleTotalProp: 'clientProvinces',
dillDownProp: ['client_province'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverProvinces',
prop: 'serverProvince',
queryCycleTotalProp: 'serverProvinces',
dillDownProp: ['server_province'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientCities',
prop: 'clientCity',
queryCycleTotalProp: 'clientCities',
dillDownProp: ['client_region'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverCities',
prop: 'serverCity',
queryCycleTotalProp: 'serverCities',
dillDownProp: ['server_region'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientIsps',
prop: 'clientIsp',
queryCycleTotalProp: 'clientIsps',
dillDownProp: ['client_isp'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverIsps',
prop: 'serverIsp',
queryCycleTotalProp: 'serverIsps',
dillDownProp: ['server_isp'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientIdcRenters',
prop: 'clientIdcRenter',
queryCycleTotalProp: 'clientIdcRenters',
dillDownProp: ['client_idc_renter'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverIdcRenters',
prop: 'serverIdcRenter',
queryCycleTotalProp: 'serverIdcRenters',
dillDownProp: ['server_idc_renter'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.clientAsns',
prop: 'clientAsn',
queryCycleTotalProp: 'clientAsns',
dillDownProp: ['client_asn'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
}, {
label: 'network.serverAsns',
prop: 'serverAsn',
queryCycleTotalProp: 'serverAsns',
dillDownProp: ['server_asn'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.npmOverviewCommon
} }
] ]
export const linkMonitorTabList = [ export const linkMonitorTabList = [
@@ -900,6 +1140,118 @@ export const linkMonitorTabList = [
checked: false, checked: false,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientIps',
prop: 'clientIp',
queryCycleTotalProp: 'clientIps',
dillDownProp: ['common_client_ip'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverIps',
prop: 'serverIp',
queryCycleTotalProp: 'serverIps',
dillDownProp: ['common_server_ip'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientCountries',
prop: 'clientCountry',
queryCycleTotalProp: 'clientCountries',
dillDownProp: ['client_country'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverCountries',
prop: 'serverCountry',
queryCycleTotalProp: 'serverCountries',
dillDownProp: ['server_country'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientProvinces',
prop: 'clientProvince',
queryCycleTotalProp: 'clientProvinces',
dillDownProp: ['client_province'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverProvinces',
prop: 'serverProvince',
queryCycleTotalProp: 'serverProvinces',
dillDownProp: ['server_province'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientCities',
prop: 'clientCity',
queryCycleTotalProp: 'clientCities',
dillDownProp: ['client_region'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverCities',
prop: 'serverCity',
queryCycleTotalProp: 'serverCities',
dillDownProp: ['server_region'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientIsps',
prop: 'clientIsp',
queryCycleTotalProp: 'clientIsps',
dillDownProp: ['client_isp'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverIsps',
prop: 'serverIsp',
queryCycleTotalProp: 'serverIsps',
dillDownProp: ['server_isp'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientIdcRenters',
prop: 'clientIdcRenter',
queryCycleTotalProp: 'clientIdcRenters',
dillDownProp: ['client_idc_renter'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverIdcRenters',
prop: 'serverIdcRenter',
queryCycleTotalProp: 'serverIdcRenters',
dillDownProp: ['server_idc_renter'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.clientAsns',
prop: 'clientAsn',
queryCycleTotalProp: 'clientAsns',
dillDownProp: ['client_asn'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
}, {
label: 'network.serverAsns',
prop: 'serverAsn',
queryCycleTotalProp: 'serverAsns',
dillDownProp: ['server_asn'],
checked: false,
disabled: false,
panelId: drillDownPanelTypeMapping.linkMonitor
} }
] ]
export const dnsServiceInsightsTabList = [ export const dnsServiceInsightsTabList = [
@@ -953,6 +1305,7 @@ export const dnsServiceInsightsTabList = [
prop: 'dnsServerRole', prop: 'dnsServerRole',
queryCycleTotalProp: 'roles', queryCycleTotalProp: 'roles',
dillDownProp: ['dns_server_role'], dillDownProp: ['dns_server_role'],
queryCondition: ['has(dns_server_role,\'$param\')'],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -971,6 +1324,7 @@ export const dnsServiceInsightsTabList = [
prop: 'sld', prop: 'sld',
queryCycleTotalProp: 'slds', queryCycleTotalProp: 'slds',
dillDownProp: ['dns_qname'], dillDownProp: ['dns_qname'],
queryCondition: ['cutToFirstSignificantSubdomain(dns_qname) = \'$param\''],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -980,6 +1334,7 @@ export const dnsServiceInsightsTabList = [
prop: 'tld', prop: 'tld',
queryCycleTotalProp: 'tlds', queryCycleTotalProp: 'tlds',
dillDownProp: ['dns_qname'], dillDownProp: ['dns_qname'],
queryCondition: [' topLevelDomain(dns_qname) = \'$param\''],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -989,6 +1344,7 @@ export const dnsServiceInsightsTabList = [
prop: 'qtype', prop: 'qtype',
queryCycleTotalProp: 'qtypes', queryCycleTotalProp: 'qtypes',
dillDownProp: ['dns_qtype'], dillDownProp: ['dns_qtype'],
queryCondition: ['dns_qtype = $param'],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -998,6 +1354,7 @@ export const dnsServiceInsightsTabList = [
prop: 'rcode', prop: 'rcode',
queryCycleTotalProp: 'rcodes', queryCycleTotalProp: 'rcodes',
dillDownProp: ['dns_rcode'], dillDownProp: ['dns_rcode'],
queryCondition: ['dns_rcode = $param'],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -1007,6 +1364,7 @@ export const dnsServiceInsightsTabList = [
prop: 'a', prop: 'a',
queryCycleTotalProp: 'a', queryCycleTotalProp: 'a',
dillDownProp: ['rr_a'], dillDownProp: ['rr_a'],
queryCondition: ['notEmpty(dns_rr) AND has(JSONExtractArrayRaw(JSON_QUERY(\'$.rr[*].type\', dns_rr) ), \'1\') AND arrayJoin(tupleElement(tupleElement(JSONExtract(dns_rr,\'Tuple(rr Nested(name String, type UInt32, a String))\'), \'rr\'), \'a\')) = \'$param\''],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -1016,6 +1374,7 @@ export const dnsServiceInsightsTabList = [
prop: 'aaaa', prop: 'aaaa',
queryCycleTotalProp: 'aaaa', queryCycleTotalProp: 'aaaa',
dillDownProp: ['rr_aaaa'], dillDownProp: ['rr_aaaa'],
queryCondition: ['notEmpty(dns_rr) and has(JSONExtractArrayRaw(JSON_QUERY(\'$.rr[*].type\', dns_rr) ), \'28\') AND arrayJoin(tupleElement(tupleElement(JSONExtract(dns_rr,\'Tuple(rr Nested(name String, type UInt32, aaaa String))\'), \'rr\'), \'aaaa\')) = \'$param\''],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -1025,6 +1384,7 @@ export const dnsServiceInsightsTabList = [
prop: 'cname', prop: 'cname',
queryCycleTotalProp: 'cnames', queryCycleTotalProp: 'cnames',
dillDownProp: ['rr_cname'], dillDownProp: ['rr_cname'],
queryCondition: ['notEmpty(dns_rr) and has(JSONExtractArrayRaw(JSON_QUERY(\'$.rr[*].type\', dns_rr) ), \'5\') AND arrayJoin(tupleElement(tupleElement(JSONExtract(dns_rr,\'Tuple(rr Nested(name String, type UInt32, cname String))\'), \'rr\'), \'cname\')) = \'$param\''],
checked: true, checked: true,
disabled: false, disabled: false,
panelId: drillDownPanelTypeMapping.dnsFourthMenu panelId: drillDownPanelTypeMapping.dnsFourthMenu
@@ -1033,7 +1393,7 @@ export const dnsServiceInsightsTabList = [
// 用于组织数据时的名称,对应的属性名称 // 用于组织数据时的名称,对应的属性名称
export const bytesColumnNameGroupForNpm = { export const bytesColumnNameGroupForNpm = {
through: 'throughBitsRate', totalBytes: 'totalBytes',
tcpConEstLatency: 'establishLatencyMs', tcpConEstLatency: 'establishLatencyMs',
packetLoss: 'tcpLostlenPercent', packetLoss: 'tcpLostlenPercent',
packetRetrans: 'pktRetransPercent', packetRetrans: 'pktRetransPercent',
@@ -1041,8 +1401,8 @@ export const bytesColumnNameGroupForNpm = {
httpResponseLatency: 'httpResponseLatency' httpResponseLatency: 'httpResponseLatency'
} }
export const bytesCycleColumnNameGroupForNmp = { export const bytesCycleColumnNameGroupForNpm = {
through: 'throughBitsRate' totalBytes: 'totalBytes'
} }
// 用于组织数据时的名称,对应的属性名称 // 用于组织数据时的名称,对应的属性名称
@@ -1059,14 +1419,14 @@ export const bytesCycleColumnNameGroupForDns = {
// networkOverview 当前周期返回数据对应的属性名称(与上一周期中的属性名称不一致,total,之所以写在这里是因为有bytepacketssessions3种如果只有一种可以直接写在customTableTitlesForAppPerformance // networkOverview 当前周期返回数据对应的属性名称(与上一周期中的属性名称不一致,total,之所以写在这里是因为有bytepacketssessions3种如果只有一种可以直接写在customTableTitlesForAppPerformance
export const bytesColumnNameGroup = { export const bytesColumnNameGroup = {
total: 'bytesTotalRate', total: 'totalBitsRate',
inbound: 'inboundBitsRate', inbound: 'inboundBitsRate',
outbound: 'outboundBitsRate', outbound: 'outboundBitsRate',
internal: 'internalBitsRate', internal: 'internalBitsRate',
through: 'throughBitsRate' through: 'throughBitsRate'
} }
export const packetsColumnNameGroup = { export const packetsColumnNameGroup = {
total: 'packetsTotalRate', total: 'totalPacketsRate',
inbound: 'inboundPacketsRate', inbound: 'inboundPacketsRate',
outbound: 'outboundPacketsRate', outbound: 'outboundPacketsRate',
internal: 'internalPacketsRate', internal: 'internalPacketsRate',
@@ -1077,7 +1437,7 @@ export const sessionsColumnNameGroup = {
} }
export const bytesCycleColumnNameGroup = { export const bytesCycleColumnNameGroup = {
total: 'bytesRate' total: 'bitsRate'
} }
export const packetsCycleColumnNameGroup = { export const packetsCycleColumnNameGroup = {
total: 'packetsRate' total: 'packetsRate'
@@ -1110,7 +1470,7 @@ export const networkTable = {
bytesColumnNameGroup: bytesColumnNameGroupForNpm, bytesColumnNameGroup: bytesColumnNameGroupForNpm,
packetsColumnNameGroup: {}, // 无metric下拉列表条件用不到此属性 packetsColumnNameGroup: {}, // 无metric下拉列表条件用不到此属性
sessionsColumnNameGroup: {}, // 无metric下拉列表条件用不到此属性 sessionsColumnNameGroup: {}, // 无metric下拉列表条件用不到此属性
bytesCycleColumnNameGroup: bytesCycleColumnNameGroupForNmp, bytesCycleColumnNameGroup: bytesCycleColumnNameGroupForNpm,
packetsCycleColumnNameGroup: {}, packetsCycleColumnNameGroup: {},
sessionsCycleColumnNameGroup: {} sessionsCycleColumnNameGroup: {}
}, },
@@ -1131,7 +1491,7 @@ export const networkTable = {
hasMetricSearch: false, // 是否有metric下拉列表 hasMetricSearch: false, // 是否有metric下拉列表
panelIdOfThirdMenu: drillDownPanelTypeMapping.linkMonitor, panelIdOfThirdMenu: drillDownPanelTypeMapping.linkMonitor,
bytesColumnNameGroup: bytesColumnNameGroupForNpm, bytesColumnNameGroup: bytesColumnNameGroupForNpm,
bytesCycleColumnNameGroup: bytesCycleColumnNameGroupForNmp bytesCycleColumnNameGroup: bytesCycleColumnNameGroupForNpm
} }
} }

View File

@@ -69,3 +69,26 @@ export function dateFormatToUTC (date, format = 'YYYY-MM-DD HH:mm:ss') {
d = window.$dayJs(d).tz().format(format) d = window.$dayJs(d).tz().format(format)
return d return d
} }
/**
* 时间戳转年月日时分秒置于数组中配合el-date-picker使用
* @param time
* @returns {number[]}
*/
export function timestampToList (time) {
// 根据地址获取当前时区
const newTimezone = window.$dayJs.tz().utcOffset() / 60
// 缓存的本地时区
const localTimezone = parseInt(localStorage.getItem(storageKey.timezoneLocalOffset))
const date = new Date(getMillisecond(time + (newTimezone - localTimezone) * 3600))
const Y = date.getFullYear()
const M = date.getMonth()
const D = date.getDate()
const H = date.getHours()
const m = date.getMinutes()
const s = date.getSeconds()
const arr = [Y, M, D, H, m, s]
return arr
}

View File

@@ -1,8 +1,20 @@
import axios from 'axios' import axios from 'axios'
import { storageKey } from '@/utils/constants' import { storageKey } from '@/utils/constants'
import store from '@/store'
const CancelToken = axios.CancelToken
axios.interceptors.request.use(config => { axios.interceptors.request.use(config => {
const token = localStorage.getItem(storageKey.token) const token = localStorage.getItem(storageKey.token)
// 添加http请求终止方法
const arr = []
const cancelToken = new CancelToken(function executor (c) {
arr.push(c)
store.commit('setHttpCancel', arr)
})
config.cancelToken = cancelToken
if (token) { if (token) {
config.headers['Cn-Authorization'] = token // 请求头token config.headers['Cn-Authorization'] = token // 请求头token
} }

View File

@@ -2,7 +2,7 @@ import { ElMessageBox, ElMessage } from 'element-plus'
import i18n from '@/i18n' import i18n from '@/i18n'
import _ from 'lodash' import _ from 'lodash'
import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, networkTable, dbDrilldownTableConfig } from '@/utils/constants' import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, networkTable, dbDrilldownTableConfig } from '@/utils/constants'
import { getIso36112JsonData } from '@/utils/api' import { getIso36112JsonData, getDictList } from '@/utils/api'
import { format } from 'echarts' import { format } from 'echarts'
import router from '@/router' import router from '@/router'
import { db } from '@/indexedDB' import { db } from '@/indexedDB'
@@ -780,66 +780,55 @@ export function getChainRatio (current, prev) {
} }
} }
export function computeScore (data, index) { export function computeScore (data) {
let score = 0 let score = 0
let k = 0 let k = 0
if (index === 0) { let totalScore = 0
k = 0.3 const scoreArr = []
if (!data.establishLatencyMs && data.establishLatencyMs !== 0) { let num = 0
score = 0 Object.keys(data).forEach(t => {
} else if (data.establishLatencyMs <= 50) { if (!data[t]) {
score = 1 num += 1
} else if (data.establishLatencyMs > 500) {
score = 0
} else {
score = (data.establishLatencyMs - 500) / (50 - 500)
} }
} else if (index === 1) { if (t === 'establishLatencyMs' || t === 'tcpLostlenPercent' || t === 'pktRetransPercent') {
k = 0.05 k = 0.3
if (!data.httpResponseLatency && data.httpResponseLatency !== 0) { } else if (t === 'httpResponseLatency' || t === 'sslConLatency') {
score = 1 k = 0.05
} else if (data.httpResponseLatency <= 50) {
score = 1
} else if (data.httpResponseLatency > 500) {
score = 0
} else {
score = (data.httpResponseLatency - 500) / (50 - 500)
} }
} else if (index === 2) { if (t === 'establishLatencyMs' || t === 'httpResponseLatency' || t === 'sslConLatency') {
k = 0.05 if (!data[t] && data[t] !== 0) {
if (!data.sslConLatency && data.sslConLatency !== 0) { score = 1
score = 1 } else if (data[t] <= 50) {
} else if (data.sslConLatency <= 50) { score = 1
score = 1 } else if (data[t] > 200) {
} else if (data.sslConLatency > 500) { score = 0
score = 0 } else {
} else { score = (data[t] - 200) / (50 - 200)
score = (data.sslConLatency - 500) / (50 - 500) }
} } else if (t === 'tcpLostlenPercent' || t === 'pktRetransPercent') {
} else if (index === 3) { if (!data[t] && data[t] !== 0) {
k = 0.3 score = 1
if (!data.tcpLostlenPercent && data.tcpLostlenPercent !== 0) { } else if (data[t] <= 0.01) {
score = 0 score = 1
} else if (data.tcpLostlenPercent <= 0.01) { } else if (data[t] > 0.05) {
score = 1 score = 0
} else if (data.tcpLostlenPercent > 0.5) { } else {
score = 0 score = (data[t] - 0.05) / (0.01 - 0.05)
} else { }
score = (data.tcpLostlenPercent - 0.5) / (0.01 - 0.5)
}
} else if (index === 4) {
k = 0.3
if (!data.pktRetransPercent && data.pktRetransPercent !== 0) {
score = 0
} else if (data.pktRetransPercent <= 0.01) {
score = 1
} else if (data.pktRetransPercent > 0.5) {
score = 0
} else {
score = (data.pktRetransPercent - 0.5) / (0.01 - 0.5)
} }
scoreArr.push(score * k)
})
scoreArr.forEach(t => {
totalScore += t
})
totalScore = Math.ceil(totalScore * 6)
if (totalScore > 6) {
totalScore = 6
} }
return score * k if (num === 5) {
return '-'
}
return totalScore
} }
// 改变tab状态(url中)当前tab // 改变tab状态(url中)当前tab
@@ -851,7 +840,6 @@ export function changeTabState (param, value) {
}) })
} }
export function getTabList (curTable, curMetric) { export function getTabList (curTable, curMetric) {
console.log('getTabList--------------')
let tabs = [] let tabs = []
if (curTable.hasMetricSearch) { // 有metric if (curTable.hasMetricSearch) { // 有metric
const metricsList = curTable ? curTable.metrics : [] const metricsList = curTable ? curTable.metrics : []
@@ -866,35 +854,61 @@ export function getTabList (curTable, curMetric) {
} }
return tabs return tabs
} }
export async function getDnsMapData (type) {
const codeValueMap = new Map()
const dnsData = await getDictList({ type: type, pageSize: -1 })
if (dnsData && dnsData.length > 0) {
dnsData.forEach(mapData => {
const code = mapData.code
if (code.indexOf('-') > -1) {
const range = mapData.code.split('-')
if (range && range.length >= 2) {
const start = range[0].trim()
const eEnd = range[1].trim()
mapData.value = (start <= code && code <= eEnd) ? mapData.value : code
for (let i = start; i <= eEnd; i++) {
codeValueMap.set(i, mapData.value)
}
}
} else {
codeValueMap.set(code, mapData.value)
}
})
}
return codeValueMap
}
export function handleSpecialValue(value){
value = value.replaceAll("'", "\\\\'")
.replaceAll('"','\\"')
.replaceAll('&','%26')
return value
}
export function combineTabList (tableType, list, commonTabList) { export function combineTabList (tableType, list, commonTabList) {
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
const listInCode = curTableInCode ? curTableInCode.tabList : [] const listInCode = curTableInCode ? curTableInCode.tabList : []
list.forEach(tab => { list.forEach(tab => {
// 配置的内容 // 配置的内容
const tabName = tab ? (tab.name ? tab.name : tab) : ''
// 配置的内容
const commonTab = commonTabList.find(item => item.name === tabName)
tab.label = commonTab ? commonTab.i18n : ''
tab.prop = commonTab ? commonTab.prop : ''
if (!tab.hasOwnProperty('checked')) { if (!tab.hasOwnProperty('checked')) {
const tabName = tab ? (tab.name ? tab.name : tab) : '' tab.checked = tab ? tab.show : true
// 配置的内容
tab = {}
const commonTab = commonTabList.find(item => item.name === tabName)
tab.label = commonTab ? commonTab.i18n : ''
tab.prop = commonTab ? commonTab.prop : ''
if (!tab.hasOwnProperty('checked')) {
tab.checked = tab ? tab.show : true
}
if (!tab.hasOwnProperty('disabled')) {
tab.disabled = tab ? !tab.enable : false
}
if (!tab.hasOwnProperty('panelId')) {
tab.panelId = tab ? tab.panelIdOfFourthMenu : null
}
// 代码里写死的
const tabInCode = listInCode ? listInCode.find(item => item.label === tab.label) : {}
tab.queryCycleTotalProp = tabInCode ? tabInCode.queryCycleTotalProp : null
tab.dillDownProp = tabInCode ? tabInCode.dillDownProp : []
} }
if (!tab.hasOwnProperty('disabled')) {
tab.disabled = tab ? !tab.enable : false
}
if (!tab.hasOwnProperty('panelId')) {
tab.panelId = tab ? tab.panelIdOfFourthMenu : null
}
// 代码里写死的
const tabInCode = listInCode ? listInCode.find(item => item.label === tab.label) : {}
tab.queryCycleTotalProp = tabInCode ? tabInCode.queryCycleTotalProp : null
tab.dillDownProp = tabInCode ? tabInCode.dillDownProp : []
}) })
} }
/*
export function setUserConfig () { export function setUserConfig () {
const userTableConfig = this.getUserLocalConfig() const userTableConfig = this.getUserLocalConfig()
if (userTableConfig) { if (userTableConfig) {
@@ -911,45 +925,71 @@ export function setUserConfig () {
this.list = newTabConfigs this.list = newTabConfigs
} }
} }
*/
export async function getDefaultCurTab (tableType, metric, columnName) { export async function getDefaultCurTab (tableType, metric, columnName) {
const tabList = await getUserDrilldownTableConfig(tableType, metric) const tabList = await getUserDrilldownTableConfig(tableType, metric)
const curTab = tabList.filter(item => item.label === columnName)[0] const curTab = tabList.filter(item => item.label === columnName)[0]
return curTab return curTab
} }
export async function getUserDrilldownTableConfig (tableType, curMetric) { export async function readDrilldownTableConfigByUser () {
let list = [] // 获取用户定制的自定义配置
// 先从localStorage中获取用户定制的自定义配置如果没有则使用默认的自定义配置
const userId = localStorage.getItem(storageKey.userId) const userId = localStorage.getItem(storageKey.userId)
const userLocalCongfig = await db[dbDrilldownTableConfig].get({ id: userId }) const userLocalCongfig = await db[dbDrilldownTableConfig].get({ id: userId })
let defaultDrillDownTableConfigs = [] let defaultDrillDownTableConfigs = []
if (userLocalCongfig) { if (userLocalCongfig) {
defaultDrillDownTableConfigs = userLocalCongfig.config defaultDrillDownTableConfigs = userLocalCongfig.config
} }
if (!defaultDrillDownTableConfigs || defaultDrillDownTableConfigs.length === 0) { // 未找到当前用户的配置,使用默认配置 return defaultDrillDownTableConfigs
console.log('default..............') }
const defaultCongfig = await db[dbDrilldownTableConfig].get({ id: 'default' })
if (defaultCongfig) { export async function getConfigVersion (id) {
defaultDrillDownTableConfigs = defaultCongfig.config let defaultCongfigInDb = await db[dbDrilldownTableConfig].get({ id: id })
} let version
if(defaultCongfigInDb) {
version = defaultCongfigInDb ? defaultCongfigInDb.version : ''
}else {
defaultCongfigInDb = await db[dbDrilldownTableConfig].get({ id: 'default' })
} }
const currentTableConfig = defaultDrillDownTableConfigs.find(config => config.route === tableType) return version
}
export async function combinDrilldownTableWithUserConfig () {
const defaultCongfigInDb = await db[dbDrilldownTableConfig].get({ id: 'default' })
const defaultConfigs = defaultCongfigInDb ? defaultCongfigInDb.config : []
const curUserConfig = await readDrilldownTableConfigByUser()
if (defaultConfigs && curUserConfig && curUserConfig.length > 0) {
defaultConfigs.forEach(defaultConfig => {
const currentTableConfig = curUserConfig.find(config => config.route === defaultConfig.route)
if (currentTableConfig) {
const tableConfig = defaultConfig.tables.find(table => table.id === defaultConfig.route)
const newTableConfig = currentTableConfig.tables.find(table => table.id === defaultConfig.route)
tableConfig.hiddenColumns = newTableConfig.hiddenColumns
tableConfig.tabs.forEach(tab => {
const newTab = newTableConfig.tabs.find(newTab => newTab.name === tab.name)
if (newTab) {
tab.hiddenDrilldownTabs = newTab.hiddenDrilldownTabs
tab.checked = newTab.checked
}
})
}
})
}
return defaultConfigs
}
export async function getUserDrilldownTableConfig (tableType, curMetric) {
let list = []
// 获取用户定制的自定义配置,如果没有,则使用默认的自定义配置
const drillDownTableConfigs = await combinDrilldownTableWithUserConfig()
const currentTableConfig = drillDownTableConfigs.find(config => config.route === tableType)
const commonTabList = currentTableConfig ? currentTableConfig.tabs : [] const commonTabList = currentTableConfig ? currentTableConfig.tabs : []
const tables = currentTableConfig ? currentTableConfig.tables : [] const tables = currentTableConfig ? currentTableConfig.tables : []
if (tables && tables.length > 0) { if (tables && tables.length > 0) {
const curTableOldConfig = tables.find(table => table.id === tableType) const curTableOldConfig = tables.find(table => table.id === tableType)
const curTable = curTableOldConfig || null const curTable = curTableOldConfig || null
if (curTable) { if (curTable) {
if (curTable.hasMetricSearch) { // 有metric list = curTable ? curTable.tabs : []
const metricsList = curTable ? curTable.metrics : []
if (metricsList && metricsList.length > 0) {
const metricTab = metricsList.find(metric => metric.name === curMetric)
list = metricTab ? metricTab.tabs : []
}
} else { // 无metric
list = curTable ? curTable.tabs : []
}
combineTabList(tableType, list, commonTabList) combineTabList(tableType, list, commonTabList)
} }
} }

View File

@@ -162,6 +162,12 @@ export function valueToRangeValue (value, unitType) {
} }
break break
} }
case unitTypes.qps: {
if (values[0] < 0.01) {
return ['<0.01', 'qps']
}
break
}
default: break default: break
} }
} }

View File

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

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-if="activeTab !== 709 && activeTab !== 710" class="panel-chart__no-data">No data</div> <div v-if="activeTab !== 709 && activeTab !== 710" class="panel-chart__no-data">{{ $t('npm.noData') }}</div>
<div v-else class="panel-chart__no-data all-clear"> <div v-else class="panel-chart__no-data all-clear">
<div class="no-recent-alerts"> <div class="no-recent-alerts">
<i class="el-icon-circle-check"></i> <i class="el-icon-circle-check"></i>

View File

@@ -474,10 +474,8 @@ export function stackedLineTooltipFormatter (params) {
str += '<div class="cn-chart-tooltip">' str += '<div class="cn-chart-tooltip">'
params.forEach((item, i) => { params.forEach((item, i) => {
str += '<span class="cn-chart-tooltip-box">' str += '<span class="cn-chart-tooltip-box">'
str += item.marker str += `<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:${item.borderColor};"></span>`
str += `<span class="cn-chart-tooltip-content"> str += `<span class="cn-chart-tooltip-content">${item.seriesName.split('(')[0]}</span>`
${item.seriesName}
</span>`
str += '</span>' str += '</span>'
}) })
str += '</div>' str += '</div>'

View File

@@ -4,6 +4,7 @@
<network-overview-line <network-overview-line
v-if="chart.type === typeMapping.networkOverview.line" v-if="chart.type === typeMapping.networkOverview.line"
:chart="chart" :chart="chart"
:metric="metric"
:time-filter="timeFilter" :time-filter="timeFilter"
ref="networkLine" ref="networkLine"
@toggleLoading="toggleLoading" @toggleLoading="toggleLoading"
@@ -23,6 +24,7 @@
<network-overview-tabs <network-overview-tabs
v-else-if="chart.type === typeMapping.networkOverview.table" v-else-if="chart.type === typeMapping.networkOverview.table"
:time-filter="timeFilter" :time-filter="timeFilter"
:metric="metric"
:chart="chart" :chart="chart"
:ref="`tab${chart.id}`" :ref="`tab${chart.id}`"
@toggleLoading="toggleLoading" @toggleLoading="toggleLoading"
@@ -38,6 +40,7 @@
<network-overview-apps <network-overview-apps
v-else-if="chart.type === typeMapping.networkOverview.appList" v-else-if="chart.type === typeMapping.networkOverview.appList"
:chart="chart" :chart="chart"
:metric="metric"
:time-filter="timeFilter" :time-filter="timeFilter"
@toggleLoading="toggleLoading" @toggleLoading="toggleLoading"
> >
@@ -188,8 +191,7 @@ import DnsEventChart from '@/views/charts2/charts/dnsInsight/DnsEventChart'
import DnsRecentEvents from '@/views/charts2/charts/dnsInsight/DnsRecentEvents' import DnsRecentEvents from '@/views/charts2/charts/dnsInsight/DnsRecentEvents'
import DnsTrafficLine from '@/views/charts2/charts/dnsInsight/DnsTrafficLine' import DnsTrafficLine from '@/views/charts2/charts/dnsInsight/DnsTrafficLine'
import { get } from '@/utils/http' import { getNowTime } from '@/utils/date-util'
import { getNowTime, getSecond } from '@/utils/date-util'
import { ref } from 'vue' import { ref } from 'vue'
import LinkDirectionGrid from '@/views/charts2/charts/linkMonitor/LinkDirectionGrid' import LinkDirectionGrid from '@/views/charts2/charts/linkMonitor/LinkDirectionGrid'
export default { export default {
@@ -224,6 +226,7 @@ export default {
}, },
props: { props: {
chart: Object, chart: Object,
metric: String,
timeFilter: Object, timeFilter: Object,
extraParams: Object extraParams: Object
}, },

View File

@@ -7,8 +7,8 @@
:col-num="24" :col-num="24"
:is-draggable="!panelLock" :is-draggable="!panelLock"
:is-resizable="!panelLock" :is-resizable="!panelLock"
:margin="[20, 20]" :margin="[rowMargin, colMargin]"
:row-height="40" :row-height="rowHeight"
:vertical-compact="true" :vertical-compact="true"
:use-css-transforms="false" :use-css-transforms="false"
> >
@@ -21,6 +21,7 @@
:key="item.i"> :key="item.i">
<chart <chart
:time-filter="timeFilter" :time-filter="timeFilter"
:metric="metric"
:extra-params="extraParams" :extra-params="extraParams"
:id="item.id" :id="item.id"
ref="chartGrid" ref="chartGrid"
@@ -36,7 +37,7 @@
import VueGridLayout from 'vue-grid-layout' import VueGridLayout from 'vue-grid-layout'
import _ from 'lodash' import _ from 'lodash'
import Chart from '@/views/charts2/Chart' import Chart from '@/views/charts2/Chart'
import { panelTypeAndRouteMapping, storageKey, drillDownPanelTypeMapping } from '@/utils/constants' import { panelTypeAndRouteMapping, drillDownPanelTypeMapping } from '@/utils/constants'
import { typeMapping } from '@/views/charts2/chart-tools' import { typeMapping } from '@/views/charts2/chart-tools'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
@@ -44,6 +45,7 @@ export default {
name: 'ChartList', name: 'ChartList',
props: { props: {
timeFilter: Object, timeFilter: Object,
metric: String,
panelType: Number, panelType: Number,
chartList: Array, chartList: Array,
panelLock: Boolean, panelLock: Boolean,
@@ -54,7 +56,11 @@ export default {
return { return {
panelTypeAndRouteMapping, panelTypeAndRouteMapping,
typeMapping, typeMapping,
layout: [] layout: [],
rowHeight: 40,
rowMargin: 20,
colMargin: 20,
debounceFunc: null
} }
}, },
components: { components: {
@@ -73,14 +79,16 @@ export default {
watch: { watch: {
chartList (n) { chartList (n) {
if (!_.isEmpty(n)) { if (!_.isEmpty(n)) {
let layout = []
if (this.panelType === panelTypeAndRouteMapping.networkAppPerformance) { if (this.panelType === panelTypeAndRouteMapping.networkAppPerformance) {
this.layout = n.filter(c => c.type === typeMapping.npm.npmTabs || c.params.tabIndex === this.npmTabIndex) layout = n.filter(c => c.type === typeMapping.npm.npmTabs || c.params.tabIndex === this.npmTabIndex)
} else if (Object.values(drillDownPanelTypeMapping).indexOf(this.panelType) >= -1) { } else if (Object.values(drillDownPanelTypeMapping).indexOf(this.panelType) >= -1) {
this.layout = n.filter(c => c.type === typeMapping.npm.npmTabs || c.params.tabIndex === this.npmTabIndex || !c.params.hasOwnProperty('tabIndex')) layout = n.filter(c => c.type === typeMapping.npm.npmTabs || c.params.tabIndex === this.npmTabIndex || !c.params.hasOwnProperty('tabIndex'))
} else { } else {
this.layout = [...n] layout = [...n]
} }
const overviewAppChart = n.find(c => c.type === typeMapping.networkOverview.appList)
/*const overviewAppChart = layout.find(c => c.type === typeMapping.networkOverview.appList)
let actuallyLength = 0 let actuallyLength = 0
if (overviewAppChart) { if (overviewAppChart) {
const params = overviewAppChart.params.app ? overviewAppChart.params : { app: [] } const params = overviewAppChart.params.app ? overviewAppChart.params : { app: [] }
@@ -90,6 +98,13 @@ export default {
actuallyLength = params.app.find(p => p.user === 'default').list.length + 1 actuallyLength = params.app.find(p => p.user === 'default').list.length + 1
} }
overviewAppChart.h = actuallyLength % 3 > 0 ? (Math.floor(actuallyLength / 3) + 1) * 2 + 2 : Math.floor(actuallyLength / 3) * 2 + 2 overviewAppChart.h = actuallyLength % 3 > 0 ? (Math.floor(actuallyLength / 3) + 1) * 2 + 2 : Math.floor(actuallyLength / 3) * 2 + 2
}*/
this.layout = layout
const overviewAppChart = layout.find(c => c.type === typeMapping.networkOverview.appList)
if (overviewAppChart) {
this.$nextTick(() => {
this.resizeAppChart()
})
} }
} }
}, },
@@ -106,11 +121,27 @@ export default {
}, },
resizeLine () { resizeLine () {
this.$refs.chartGrid.resizeLine() this.$refs.chartGrid.resizeLine()
},
resizeAppChart () {
const appCardsDom = document.querySelector('.app-cards')
const layout = _.cloneDeep(this.layout)
const overviewAppChart = layout.find(c => c.type === typeMapping.networkOverview.appList)
if (appCardsDom) {
const cardsHeight = appCardsDom.offsetHeight
if (cardsHeight) {
const headerHeight = 24
overviewAppChart.h = (cardsHeight + headerHeight + this.rowMargin) / (this.rowHeight + this.rowMargin)
this.layout = layout
}
}
} }
},
mounted () {
this.debounceFunc = _.debounce(this.resizeAppChart, 400)
window.addEventListener('resize', this.debounceFunc)
},
beforeUnmount () {
window.removeEventListener('resize', this.debounceFunc)
} }
} }
</script> </script>
<style scoped>
</style>

View File

@@ -3,31 +3,48 @@
<div class="panel__header"> <div class="panel__header">
<div class="panel__title">{{panelName?panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}} <div class="panel__title">{{panelName?panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}}
<div v-if="showScore" class="score"> <div v-if="showScore" class="score">
<div class="circle-icon" v-if="score <= 2" :class="{'data-score-red': score <= 2}" ></div> <div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div> <div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div> <div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}} Score:{{score}}
</div> </div>
</div> </div>
<div class="panel__time"> <div class="panel__tools">
<date-time-range <el-select
class="date-time-range" size="mini"
:start-time="timeFilter.startTime" v-model="metric"
:end-time="timeFilter.endTime" placeholder=""
:date-range="timeFilter.dateRangeValue" popper-class="common-select"
ref="dateTimeRange" v-if="panelType === panelTypeAndRouteMapping.networkOverview"
@change="reload" :popper-append-to-body="false"
/> @change="metricChange"
<time-refresh >
class="date-time-range" <template #prefix>
@change="timeRefreshChange" <span class="select-prefix">Metric:</span>
:end-time="timeFilter.endTime" </template>
/> <el-option v-for="item in metricOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<div class="panel__time">
<date-time-range
class="date-time-range"
:start-time="timeFilter.startTime"
:end-time="timeFilter.endTime"
:date-range="timeFilter.dateRangeValue"
ref="dateTimeRange"
@change="reload"
/>
<time-refresh
class="date-time-range"
@change="timeRefreshChange"
:end-time="timeFilter.endTime"
/>
</div>
</div> </div>
</div> </div>
<chart-list <chart-list
ref="chartList" ref="chartList"
:time-filter="timeFilter" :time-filter="timeFilter"
:metric="metric"
:chart-list="chartList" :chart-list="chartList"
:panel-type="panelType" :panel-type="panelType"
:panel-lock="panelLock" :panel-lock="panelLock"
@@ -42,18 +59,17 @@ import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
import { import {
panelTypeAndRouteMapping, panelTypeAndRouteMapping,
bytesColumnNameGroupForNpm,
scoreUrl,
customTableTitlesForAppPerformance,
operationType,
curTabState, curTabState,
drillDownPanelTypeMapping drillDownPanelTypeMapping,
metricOptions,
fromRoute
} from '@/utils/constants' } from '@/utils/constants'
import { getPanelList, getChartList } from '@/utils/api' import { getPanelList, getChartList, api } from '@/utils/api'
import { getNowTime, getSecond } from '@/utils/date-util' import { getNowTime, getSecond } from '@/utils/date-util'
import { getTypeCategory } from '@/views/charts/charts/tools' import { getTypeCategory } from '@/views/charts/charts/tools'
import { computeScore, urlParamsHandler, overwriteUrl } from '@/utils/tools' import { urlParamsHandler, overwriteUrl, getDnsMapData, computeScore } from '@/utils/tools'
import ChartList from '@/views/charts2/ChartList' import ChartList from '@/views/charts2/ChartList'
import { useStore } from 'vuex'
import { get } from '@/utils/http' import { get } from '@/utils/http'
export default { export default {
@@ -67,31 +83,57 @@ export default {
}, },
data () { data () {
return { return {
panelTypeAndRouteMapping,
metricOptions,
chartList: [], // 普通panel的chart chartList: [], // 普通panel的chart
panelLock: true, panelLock: true,
extraParams: {}, extraParams: {},
panelName: '', panelName: '',
dnsRcodeMapData: [],
dnsQtypeMapData: [],
score: null, score: null,
curTabState: curTabState curTabState: curTabState
} }
}, },
computed: { computed: {
npmThirdLevelMenuScore () { // npmThirdLevelMenuScore () {
return this.$store.getters.getNpmThirdLevelMenuScore // return this.$store.getters.getNpmThirdLevelMenuScore
} // }
}, },
watch: { watch: {
npmThirdLevelMenuScore: { // npmThirdLevelMenuScore: {
deep: true, // deep: true,
immediate: true, // immediate: true,
handler (n) { // handler (n) {
this.score = n // this.score = n
// }
// }
timeFilter: {
handler () {
if (this.$route.path === '/panel/networkAppPerformance' && (this.queryCondition || this.networkOverviewBeforeTab)) {
this.scoreCalculation()
}
} }
} }
}, },
async mounted () { async mounted () {
// this.panelName = this.$store.getters.getPanelName // this.panelName = this.$store.getters.getPanelName
this.panelName = this.$route.query.panelName ? this.$t(this.$route.query.panelName) : '' const pName = this.$route.query.panelName ? this.$t(this.$route.query.panelName) : ''
const curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
this.$store.commit('setDnsQtypeMapData', this.dnsQtypeMapData)
this.$store.commit('setDnsRcodeMapData', this.dnsRcodeMapData)
}
if (curTabProp === 'qtype') {
this.panelName = this.dnsQtypeMapData.get(pName)
} else if (curTabProp === 'rcode') {
this.panelName = this.dnsRcodeMapData.get(pName)
} else {
this.panelName = pName
}
// const curOperationType = this.$store.getters.getTabOperationType // const curOperationType = this.$store.getters.getTabOperationType
/* const curOperationType = this.getUrlParam(this.curTabState.tabOperationType, '', true) /* const curOperationType = this.getUrlParam(this.curTabState.tabOperationType, '', true)
if (this.panelName && this.$route.path === '/panel/networkAppPerformance' && curOperationType !== operationType.thirdMenu) { if (this.panelName && this.$route.path === '/panel/networkAppPerformance' && curOperationType !== operationType.thirdMenu) {
@@ -154,11 +196,39 @@ export default {
return chart return chart
}) })
}) })
if (this.$route.path === '/panel/networkAppPerformance' && (this.queryCondition || this.networkOverviewBeforeTab)) {
this.scoreCalculation()
}
}, },
setup (props, ctx) { setup (props) {
// todo 目前在panel页面测试后续会挪到router里
const store = useStore()
const cancelList = store.state.panel.httpCancel
// 进入页面时,发现有未结束的请求,终止请求
if (cancelList.length > 0) {
cancelList.forEach((cancel, index) => {
cancel()
delete cancelList[index]
})
}
const panel = ref({}) const panel = ref({})
let panelType = 1 // 取得panel的type let panelType = 1 // 取得panel的type
const { params, query } = useRoute() let { params, query, path } = useRoute()
// 获取路由跳转过的历史状态,赋值给当前界面,起到保留状态的作用,如浏览器的回退前进等
const routerObj = store.getters.getRouterHistoryList.find(item => item.t === query.t)
if (routerObj) {
params = routerObj.params
query = routerObj.query
path = routerObj.path
// 如果当前界面之前载入过,获取状态后更新地址栏,以便后续的赋值操作
const newUrl = urlParamsHandler(window.location.href, useRoute().query, query)
overwriteUrl(newUrl)
}
const thirdPanel = query.thirdPanel const thirdPanel = query.thirdPanel
const fourthPanel = query.fourthPanel const fourthPanel = query.fourthPanel
if (fourthPanel) { if (fourthPanel) {
@@ -177,6 +247,7 @@ export default {
const startTimeParam = query.startTime const startTimeParam = query.startTime
const endTimeParam = query.endTime const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。 // 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : (isEntityDetail(panelType) ? 60 * 24 : 60) const dateRangeValue = rangeParam ? parseInt(query.range) : (isEntityDetail(panelType) ? 60 * 24 : 60)
const timeFilter = ref({ dateRangeValue }) const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) { if (!startTimeParam || !endTimeParam) {
@@ -191,11 +262,25 @@ export default {
// npm是否展示分数 // npm是否展示分数
const showScorePanel = [drillDownPanelTypeMapping.npmOverviewIp, drillDownPanelTypeMapping.npmOverviewDomain, drillDownPanelTypeMapping.npmOverviewApp, drillDownPanelTypeMapping.npmOverviewCommon, drillDownPanelTypeMapping.npmThirdMenu] const showScorePanel = [drillDownPanelTypeMapping.npmOverviewIp, drillDownPanelTypeMapping.npmOverviewDomain, drillDownPanelTypeMapping.npmOverviewApp, drillDownPanelTypeMapping.npmOverviewCommon, drillDownPanelTypeMapping.npmThirdMenu]
const showScore = showScorePanel.indexOf(panelType) > -1 const showScore = showScorePanel.indexOf(panelType) > -1
const metric = ref(query.metric || 'Bits/s')
const queryCondition = ref(query.queryCondition || '')
const dimensionType = ref(query.dimensionType || '')
// 三级菜单判断
const tabOperationType = ref(query.tabOperationType)
const networkOverviewBeforeTab = ref(query.networkOverviewBeforeTab || '')
return { return {
panelType, panelType,
panel, panel,
timeFilter, timeFilter,
showScore showScore,
metric,
path,
queryCondition,
dimensionType,
tabOperationType,
networkOverviewBeforeTab
} }
}, },
methods: { methods: {
@@ -233,6 +318,9 @@ export default {
reload (startTime, endTime, dateRangeValue) { reload (startTime, endTime, dateRangeValue) {
this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime), dateRangeValue: dateRangeValue } this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime), dateRangeValue: dateRangeValue }
const { query } = this.$route const { query } = this.$route
this.$store.commit('setTimeRangeArray', [this.timeFilter.startTime, this.timeFilter.endTime])
this.$store.commit('setTimeRangeFlag', dateRangeValue.value)
const newUrl = urlParamsHandler(window.location.href, query, { const newUrl = urlParamsHandler(window.location.href, query, {
startTime: this.timeFilter.startTime, startTime: this.timeFilter.startTime,
endTime: this.timeFilter.endTime, endTime: this.timeFilter.endTime,
@@ -255,7 +343,95 @@ export default {
} else { } else {
return this.$route.query[param] ? this.$route.query[param] : defaultValue return this.$route.query[param] ? this.$route.query[param] : defaultValue
} }
},
metricChange (value) {
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
metric: value
})
overwriteUrl(newUrl)
},
scoreCalculation () {
let condition = ''
let url = ''
if (this.queryCondition.indexOf(' OR ') > -1) {
condition = this.queryCondition.split(/["|'](.*?)["|']/)
} else {
condition = this.queryCondition
}
const type = this.dimensionType || this.networkOverviewBeforeTab
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
cycle: 0
}
if (condition && (typeof condition !== 'object') && type) {
if (type === 'clientIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='client'`
} else if (type === 'serverIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='server'`
} else if (type === 'clientCity') {
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'`
} else if (type === 'serverCity') {
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'`
} else {
params.q = condition
}
params.type = type
} else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}'`
params.type = type
} else if (condition.length > 1 && type && type !== 'ip') {
if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') {
params.q = `${type}='${condition[1]}'`
params.type = type
} else if (type === 'idcRenter') {
params.q = `idc_renter='${condition[1]}'`
params.type = type
} else {
params.q = `${condition[0]}'${condition[1]}'`
params.type = type
}
}
if (parseFloat(this.tabOperationType) === 3) {
url = api.npm.overview.allNetworkAnalysis
} else {
url = api.npm.overview.networkAnalysis
}
if ((type && condition) || type) {
params.type = params.type || type
get(url, params).then(res => {
if (res.code === 200) {
const data = {
establishLatencyMs: res.data.result.establishLatencyMsAvg || null,
httpResponseLatency: res.data.result.httpResponseLatencyAvg || null,
sslConLatency: res.data.result.sslConLatencyAvg || null,
tcpLostlenPercent: res.data.result.tcpLostlenPercentAvg || null,
pktRetransPercent: res.data.result.pktRetransPercentAvg || null
}
this.score = computeScore(data)
}
})
}
} }
},
/**
* 页面销毁前,更新历史中已保存的状态
* 之所以会在下钻时、销毁前保存状态是因为panel第一次下钻时beforeUnmount获取不到下钻前参数
*/
beforeUnmount () {
const query = this.$_.cloneDeep(this.$route.query)
const routerObj = this.$store.getters.getRouterHistoryList.find(item => item.t === query.t)
// const routerObj = window.localRouterHistoryList.find(item => item.t === query.t)
if (routerObj !== undefined) {
if (Object.getOwnPropertyNames(query).length >= Object.getOwnPropertyNames(routerObj.query).length) {
routerObj.query = query
}
}
this.emitter.off('reloadChartList')
this.$store = null
this.emitter = null
} }
} }
</script> </script>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="line network dns-traffic-line"> <div class="line network dns-traffic-line">
<div class="line-header"> <chart-error v-if="showError" :content="errorMsg" />
<div class="line-header" v-if="!showError">
<div class="line-header-left"> <div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div> <div class="line-value-active" v-if="lineTab"></div>
<div class="line-value"> <div class="line-value">
@@ -138,7 +139,9 @@ export default {
leftOffset: 0, leftOffset: 0,
sizes: [3, 4, 6, 8, 9, 10], sizes: [3, 4, 6, 8, 9, 10],
dynamicVariable: '', dynamicVariable: '',
showMarkLine: true showMarkLine: true,
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -167,7 +170,7 @@ export default {
overwriteUrl(newUrl) overwriteUrl(newUrl)
}, },
timeFilter: { timeFilter: {
handler (n) { handler () {
if (this.lineTab) { if (this.lineTab) {
this.init(this.lineMetric, this.showMarkLine, 'active') this.init(this.lineMetric, this.showMarkLine, 'active')
} else { } else {
@@ -189,10 +192,23 @@ export default {
params.q = this.queryCondition params.q = this.queryCondition
} }
this.toggleLoading(true) this.toggleLoading(true)
get(api.dnsInsight.totalTrafficAnalysis, params).then((res) => { let url = api.dnsInsight.totalTrafficAnalysis
if (this.queryCondition) {
url = api.dnsInsight.drilldownTrafficAnalysis
}
get(url, params).then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
res.data.result.forEach((t, i) => { 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: '' }
]
}
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') { if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis mpackets[0].analysis = t.totalBitsRate.analysis
@@ -201,10 +217,12 @@ export default {
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : [] mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : [] mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : [] mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
let num = 0
mpackets.forEach(e => { mpackets.forEach(e => {
e.unitType = 'bps' e.unitType = 'bps'
if (e.name !== 'network.total' && e.analysis.avg == 0) { if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false e.show = false
num += 1
} else { } else {
e.show = true e.show = true
if (!active && show !== this.lineRefer) { if (!active && show !== this.lineRefer) {
@@ -212,16 +230,27 @@ export default {
} }
} }
if (this.lineTab === e.class) { if (this.lineTab === e.class) {
if (e.analysis.avg < 0) { if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = '' this.lineTab = ''
this.lineRefer = ''
this.init() this.init()
} }
} }
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { if (num === 3) {
this.echartsInit(this.mpackets, show) mpackets[0].invertTab = false
}) this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'queries' && val === 'Queries/s') { } else if (t.type === 'queries' && val === 'Queries/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalQueryRate.analysis mpackets[0].analysis = t.totalQueryRate.analysis
@@ -231,20 +260,26 @@ export default {
e.show = false e.show = false
} }
e.unitType = 'queries/s' e.unitType = 'queries/s'
if (show !== this.lineRefer) { e.invertTab = false
this.legendSelectChange(e, 0) this.lineTab = 'total'
} this.legendSelectChange(e, 0)
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { this.$nextTick(() => {
this.echartsInit(this.mpackets, show) this.echartsInit(this.mpackets, true)
}) })
} }
}) })
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.isNoData = true this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -270,7 +305,7 @@ export default {
width: 1 width: 1
}, },
stack: t.name !== 'network.total' ? 'network.total' : '', stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value, params) { symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0]) return _this.symbolSizeSortChange(i, value[0])
}, },
itemStyle: { itemStyle: {
@@ -314,7 +349,7 @@ export default {
} }
}) })
if (!show) { if (!show) {
this.chartOption.series.forEach((t, i) => { this.chartOption.series.forEach((t) => {
t.markLine.label.show = false t.markLine.label.show = false
t.markLine = [] t.markLine = []
}) })
@@ -346,20 +381,26 @@ export default {
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
}) })
const str = stackedLineTooltipFormatter(params) return stackedLineTooltipFormatter(params)
return str
} }
this.showMarkLine = true this.showMarkLine = true
this.myChart.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
}, },
activeChange (item, index) { activeChange (item, index) {
if (this.isNoData) return
this.lineTab = item.class this.lineTab = item.class
this.legendSelectChange(item, index, 'active') this.legendSelectChange(item, index, 'active')
this.showMarkLine = !item.invertTab this.showMarkLine = !item.invertTab
this.init(this.lineMetric, this.showMarkLine, 'active') this.init(this.lineMetric, this.showMarkLine, 'active')
}, },
mouseenter (item) { mouseenter (item) {
if (this.isNoData) return
this.mousemoveCursor = item.class this.mousemoveCursor = item.class
this.handleActiveBar(item.class) this.handleActiveBar(item.class)
}, },
@@ -409,7 +450,7 @@ export default {
}) })
} }
}, },
handleActiveBar (value) { handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) { if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active') const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
const activeBar = document.querySelector('.network .line-value-active') const activeBar = document.querySelector('.network .line-value-active')
@@ -424,7 +465,7 @@ export default {
this.lineTab = '' this.lineTab = ''
this.handleActiveBar() this.handleActiveBar()
this.showMarkLine = !this.showMarkLine this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e, i) => { this.mpackets.forEach((e) => {
if (!e.invertTab) { if (!e.invertTab) {
e.invertTab = true e.invertTab = true
} }

View File

@@ -1,13 +1,15 @@
<template> <template>
<div class="link-blocks"> <div class="link-blocks">
<div class="block-list" style="position: relative"> <div class="block-list" style="position: relative">
<div class="block-list__title">{{ $t('linkMonitor.links') }}</div> <div class="block-list__title" v-if="!showError">{{ $t('linkMonitor.links') }}</div>
<!--无数据noData--> <!--无数据noData-->
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<div class="block-list__list" v-show="!isNoData"> <div class="block-list__list" v-show="!isNoData">
<chart-error v-if="showError" :content="errorMsg1" />
<el-popover <el-popover
v-else
placement="bottom" placement="bottom"
trigger="hover" trigger="hover"
popper-class="link-block__popper" popper-class="link-block__popper"
@@ -54,13 +56,15 @@
</div> </div>
</div> </div>
<div class="block-list" style="position: relative"> <div class="block-list" >
<div class="block-list__title">{{ $t('linkMonitor.nextHopInternet') }}</div> <div class="block-list__title" v-if="!showError">{{ $t('linkMonitor.nextHopInternet') }}</div>
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<div class="block-list__list" v-show="!isNoData"> <div class="block-list__list" v-show="!isNoData">
<chart-error v-if="showError" :content="errorMsg2" />
<el-popover <el-popover
v-else
placement="bottom" placement="bottom"
trigger="hover" trigger="hover"
popper-class="link-block__popper" popper-class="link-block__popper"
@@ -120,11 +124,13 @@ import { colorGradientCalculation } from '@/utils/tools'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/constants' import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/constants'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'LinkBlock', name: 'LinkBlock',
mixins: [chartMixin], mixins: [chartMixin],
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
data () { data () {
@@ -133,7 +139,10 @@ export default {
unitTypes, unitTypes,
linkData: [], linkData: [],
nextHopData: [], nextHopData: [],
gradientColor: ['#FF005C', '#40537E'] // [start, end] gradientColor: ['#FF005C', '#40537E'], // [start, end]
showError: false,
errorMsg1: '',
errorMsg2: ''
} }
}, },
setup () { setup () {
@@ -145,7 +154,7 @@ export default {
}, },
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.init() this.init()
} }
} }
@@ -158,7 +167,8 @@ export default {
init () { init () {
this.toggleLoading(true) this.toggleLoading(true)
// 链路基本信息 // 链路基本信息
let linkInfo = localStorage.getItem(storageKey.linkInfo) let linkInfo = null
linkInfo = localStorage.getItem(storageKey.linkInfo)
linkInfo = JSON.parse(linkInfo) linkInfo = JSON.parse(linkInfo)
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
@@ -170,6 +180,8 @@ export default {
Promise.all([dataRequest, nextHopRequest]).then(res => { Promise.all([dataRequest, nextHopRequest]).then(res => {
if (res[0].code === 200 && res[1].code === 200) { if (res[0].code === 200 && res[1].code === 200) {
this.showError = false
const linkData = res[0].data.result const linkData = res[0].data.result
const nextHopData = res[1].data.result const nextHopData = res[1].data.result
@@ -223,7 +235,6 @@ export default {
}) })
this.linkData = sorted this.linkData = sorted
// todo 此处去重不优美,后续再处理
let directionArr = [] let directionArr = []
nextHopData.forEach((item) => { nextHopData.forEach((item) => {
if (item.egressLinkDirection !== '' && item.ingressLinkDirection !== '') { if (item.egressLinkDirection !== '' && item.ingressLinkDirection !== '') {
@@ -231,7 +242,7 @@ export default {
directionArr.push(item.ingressLinkDirection) directionArr.push(item.ingressLinkDirection)
} }
}) })
directionArr = [...new Set(directionArr)] directionArr = Array.from(new Set(directionArr))
const newNextHopData = [] const newNextHopData = []
@@ -275,11 +286,18 @@ export default {
this.nextHopData = nextHopSorted this.nextHopData = nextHopSorted
} else { } else {
this.isNoData = true this.isNoData = false
this.showError = true
this.errorMsg1 = res[0].message
this.errorMsg2 = res[1].message
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.isNoData = true this.isNoData = false
this.showError = true
// todo 此处数据还待验证
this.errorMsg1 = e.message
this.errorMsg2 = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -366,6 +384,9 @@ export default {
return newValue return newValue
} }
},
beforeUnmount () {
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -1,10 +1,10 @@
<template> <template>
<div class="link-direction-grid"> <div class="link-direction-grid">
<!--左侧链路出入口--> <!--左侧链路出入口-->
<popover-content :isNoData="isNoData" :gridData="gridData" style="width: 900px;"/> <popover-content :isNoData="isLinkNoData" :gridData="linkGridData" :showError="isLinkShowError" :content="linkErrorMsg" style="width: 900px;"/>
<!--右侧链路下一跳--> <!--右侧链路下一跳-->
<popover-content :isNoData="isNoData" :gridData="gridData2"/> <popover-content :isNoData="isNextNoData" :gridData="nextGridData" :showError="isNextShowError" :content="nextErrorMsg" />
</div> </div>
</template> </template>
@@ -22,9 +22,14 @@ export default {
mixins: [chartMixin], mixins: [chartMixin],
data () { data () {
return { return {
gridData: [], linkGridData: [],
gridData2: [], nextGridData: [],
isNoData: false isLinkNoData: false,
isNextNoData: false,
isLinkShowError: false, // 显示左侧链路报错标识
linkErrorMsg: '', // 左侧链路的报错信息
isNextShowError: false, // 显示右侧下一跳报错标识
nextErrorMsg: '' // 右侧下一跳的报错信息
} }
}, },
components: { components: {
@@ -32,7 +37,7 @@ export default {
}, },
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.init() this.init()
} }
} }
@@ -45,7 +50,6 @@ export default {
// 链路基本信息 // 链路基本信息
let linkInfo = localStorage.getItem(storageKey.linkInfo) let linkInfo = localStorage.getItem(storageKey.linkInfo)
linkInfo = JSON.parse(linkInfo) linkInfo = JSON.parse(linkInfo)
// console.log('LinkDirectionGrid.vue---init--获取链路基本信息缓存', linkInfo)
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
@@ -57,7 +61,8 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([dataRequest, nextHopRequest]).then(res => { Promise.all([dataRequest, nextHopRequest]).then(res => {
if (res[0].code === 200 && res[1].code === 200) { if (res[0].code === 200) {
this.isLinkShowError = false
// 链路流量数据 // 链路流量数据
const linkData = res[0].data.result const linkData = res[0].data.result
// 接口数据乱序根据入链路idingressLinkId大小排序之后 // 接口数据乱序根据入链路idingressLinkId大小排序之后
@@ -69,6 +74,70 @@ export default {
return a.egressLinkId - b.egressLinkId return a.egressLinkId - b.egressLinkId
}) })
this.isLinkNoData = linkData.length === 0
if (!this.isLinkNoData) {
// 链路流量数据
const linkGridData = []
linkData.forEach(d => {
const ingressLink = linkInfo.find(l => l.originalLinkId === d.ingressLinkId)
const egressLink = linkInfo.find(l => l.originalLinkId === d.egressLinkId)
if (ingressLink && egressLink) {
const data = linkGridData.find(g => g.linkId === ingressLink.linkId)
// 上行使用情况计算
const egressUsage = this.computeUsage(d.egressBitsRate, egressLink.bandwidth)
// 下行使用情况计算
const ingressUsage = this.computeUsage(d.ingressBitsRate, ingressLink.bandwidth)
// 宽带使用超过90%,赋红点
d.usageMore90 = egressUsage >= 0.9 || ingressUsage >= 0.9
// 计算npm分数
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3
if (data) {
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
if (!existedEgressLink) {
data.egress.push({
linkId: egressLink.linkId,
egressUsage: egressUsage,
ingressUsage: ingressUsage,
popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'),
valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'),
totalBitsRate: d.totalBitsRate,
...d
})
}
} else {
linkGridData.push({
linkId: ingressLink.linkId,
egress: [{
linkId: egressLink.linkId,
egressUsage: egressUsage,
ingressUsage: ingressUsage,
popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'),
valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'),
totalBitsRate: d.totalBitsRate,
...d
}]
})
}
}
})
this.linkGridData = linkGridData
}
} else {
this.isLinkNoData = false
this.isLinkShowError = true
this.linkErrorMsg = res[0].message
}
if (res[1].code === 200) {
this.isNextShowError = false
// 链路下一跳信息 // 链路下一跳信息
const nextLinkData = res[1].data.result const nextLinkData = res[1].data.result
// 接口数据乱序,根据出方向排序,再根据同个出方向下的入进行排序 // 接口数据乱序,根据出方向排序,再根据同个出方向下的入进行排序
@@ -79,145 +148,92 @@ export default {
return a.egressLinkDirection.localeCompare(b.egressLinkDirection) return a.egressLinkDirection.localeCompare(b.egressLinkDirection)
}) })
this.isNoData = linkData.length === 0 && nextLinkData.length === 0 this.isNextNoData = nextLinkData.length === 0
if (this.isNoData) { if (!this.isNextNoData) {
return // 链路下一跳数据
} const nextGridData = []
// 链路流量数据 nextLinkData.forEach(d => {
const gridData = [] const ingressLink = linkInfo.find(l => l.nextHop === d.ingressLinkDirection && l.direction === 'ingress')
// 链路下一跳数据 const egressLink = linkInfo.find(l => l.nextHop === d.egressLinkDirection && l.direction === 'egress')
const gridData2 = []
linkData.forEach(d => {
const ingressLink = linkInfo.find(l => l.originalLinkId === d.ingressLinkId)
const egressLink = linkInfo.find(l => l.originalLinkId === d.egressLinkId)
if (ingressLink && egressLink) {
const data = gridData.find(g => g.linkId === ingressLink.linkId)
// 上行使用情况计算 if (ingressLink && egressLink) {
const egressUsage = this.computeUsage(d.egressBitsRate, egressLink.bandwidth) const data = nextGridData.find(g => g.linkId === ingressLink.linkId)
// 下行使用情况计算
const ingressUsage = this.computeUsage(d.ingressBitsRate, ingressLink.bandwidth)
// 宽带使用超过90%,赋红点
d.usageMore90 = false
if (egressUsage >= 0.9 || ingressUsage >= 0.9) {
d.usageMore90 = true
}
// 计算npm分数
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
d.scoreLow3 = false
if (d.score < 3) {
d.scoreLow3 = true
}
if (data) { let egressBanwidth = 0
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId) let ingressBanwidth = 0
if (!existedEgressLink) { linkInfo.forEach((item) => {
data.egress.push({ if (item.nextHop === d.egressLinkDirection && item.direction === 'egress') {
linkId: egressLink.linkId, egressBanwidth += item.bandwidth
egressUsage: egressUsage, }
ingressUsage: ingressUsage, if (item.nextHop === d.ingressLinkDirection && item.direction === 'ingress') {
popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'), ingressBanwidth += item.bandwidth
valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'), }
totalBitsRate: d.totalBitsRate,
...d
})
}
} else {
gridData.push({
linkId: ingressLink.linkId,
egress: [{
linkId: egressLink.linkId,
egressUsage: egressUsage,
ingressUsage: ingressUsage,
popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'),
valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'),
totalBitsRate: d.totalBitsRate,
...d
}]
}) })
}
}
})
this.gridData = gridData // 上行使用情况计算
const egressUsage = this.computeUsage(d.egressBitsRate, egressBanwidth)
// 下行使用情况计算
const ingressUsage = this.computeUsage(d.ingressBitsRate, ingressBanwidth)
// 宽带使用超过90%,赋红点
nextLinkData.forEach(d => { d.usageMore90 = egressUsage >= 0.9 || ingressUsage >= 0.9
const ingressLink = linkInfo.find(l => l.nextHop === d.ingressLinkDirection && l.direction === 'ingress') // 计算npm分数
const egressLink = linkInfo.find(l => l.nextHop === d.egressLinkDirection && l.direction === 'egress') // 分数低于3分赋红点
d.score = this.localComputeScore(d)
if (ingressLink && egressLink) { d.scoreLow3 = d.score < 3
const data = gridData2.find(g => g.linkId === ingressLink.linkId)
let egressBanwidth = 0 if (data) {
let ingressBanwidth = 0 const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
linkInfo.forEach((item) => { if (!existedEgressLink) {
if (item.nextHop === d.egressLinkDirection && item.direction === 'egress') { data.egress.push({
egressBanwidth += item.bandwidth linkId: egressLink.linkId,
} nextHop: egressLink.nextHop,
if (item.nextHop === d.ingressLinkDirection && item.direction === 'ingress') { egressUsage: egressUsage,
ingressBanwidth += item.bandwidth ingressUsage: ingressUsage,
} popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'),
}) valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'),
totalBitsRate: d.totalBitsRate,
// 上行使用情况计算 ...d
const egressUsage = this.computeUsage(d.egressBitsRate, egressBanwidth) })
// 下行使用情况计算 }
const ingressUsage = this.computeUsage(d.ingressBitsRate, ingressBanwidth) } else {
// 宽带使用超过90%,赋红点 nextGridData.push({
d.usageMore90 = false linkId: ingressLink.linkId,
if (egressUsage >= 0.9 || ingressUsage >= 0.9) {
d.usageMore90 = true
}
// 计算npm分数
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
d.scoreLow3 = false
if (d.score < 3) {
d.scoreLow3 = true
}
if (data) {
const existedEgressLink = data.egress.find(e => e.linkId === egressLink.linkId)
if (!existedEgressLink) {
data.egress.push({
linkId: egressLink.linkId,
nextHop: egressLink.nextHop,
egressUsage: egressUsage,
ingressUsage: ingressUsage,
popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'),
valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'),
totalBitsRate: d.totalBitsRate,
...d
})
}
} else {
gridData2.push({
linkId: ingressLink.linkId,
nextHop: ingressLink.nextHop,
egress: [{
linkId: egressLink.linkId,
nextHop: ingressLink.nextHop, nextHop: ingressLink.nextHop,
egressUsage: egressUsage, egress: [{
ingressUsage: ingressUsage, linkId: egressLink.linkId,
popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'), nextHop: ingressLink.nextHop,
valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'), egressUsage: egressUsage,
totalBitsRate: d.totalBitsRate, ingressUsage: ingressUsage,
...d popoverWidth: this.computeWidth(egressUsage, ingressUsage, 'popover'),
}] valueWidth: this.computeWidth(egressUsage, ingressUsage, 'value'),
}) totalBitsRate: d.totalBitsRate,
...d
}]
})
}
} }
} })
})
this.gridData2 = gridData2 this.nextGridData = nextGridData
}
} else { } else {
this.isNoData = true this.isNextNoData = false
this.isNextShowError = true
// todo 此时返回的是msg后期记得改为message
this.nextErrorMsg = res[1].msg
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.isNoData = true
this.isLinkShowError = true
// todo 此时返回的是msg后期记得改为message
this.linkErrorMsg = e[0].msg
this.isNextShowError = true
this.nextErrorMsg = e[1].msg
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -235,18 +251,17 @@ export default {
/** /**
* 本地计算npm分数 * 本地计算npm分数
*/ */
localComputeScore (data, bandwidth) { localComputeScore (data) {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
let score = 0 let score = 0
keyPre.forEach((item, index) => { const dataScore = {
score = computeScore(data, index) establishLatencyMs: data.establishLatencyMs || null,
data[keyPre[index] + 'Score'] = score httpResponseLatency: data.httpResponseLatency || null,
}) sslConLatency: data.sslConLatency || null,
let npmScore = Math.ceil((data.tcpScore + data.httpScore + data.sslScore + data.tcpLostScore + data.packetRetransScore) * 6) tcpLostlenPercent: data.tcpLostlenPercent || null,
if (npmScore > 6) { pktRetransPercent: data.pktRetransPercent || null
npmScore = 6
} }
return npmScore score = computeScore(dataScore)
return score
}, },
/** /**
* 计算popover弹窗和右侧数据模块的宽度 * 计算popover弹窗和右侧数据模块的宽度

View File

@@ -5,7 +5,9 @@
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<div class="data-grid" v-show="!isNoData"> <chart-error class="link-block-error" v-if="showError" :content="content"/>
<div class="data-grid" v-show="!isNoData && !showError">
<div class="egress-row"> <div class="egress-row">
<div class="egress-id" v-for="(item, index) in gridData" :key="index"> <div class="egress-id" v-for="(item, index) in gridData" :key="index">
<!--兼容下一跳情况--> <!--兼容下一跳情况-->
@@ -73,7 +75,7 @@
<div class="block-content-item-name">{{ $t('linkMonitor.linkBlock.total') }}</div> <div class="block-content-item-name">{{ $t('linkMonitor.linkBlock.total') }}</div>
<div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}"> <div class="block-content-item-value" :style="{width: row.egress[index2].valueWidth + 'px'}">
{{unitConvert(row.egress[index2].totalBitsRate, unitTypes.bps).join('')}} {{ unitConvert(row.egress[index2].totalBitsRate, unitTypes.bps).join('') }}
</div> </div>
</div> </div>
</div> </div>
@@ -146,13 +148,18 @@
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { unitTypes } from '@/utils/constants' import { unitTypes } from '@/utils/constants'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'PopoverContent', name: 'PopoverContent',
props: { props: {
gridData: Array, gridData: Array,
isNoData: Boolean isNoData: Boolean,
showError: Boolean,
content: String
}, },
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
data () { data () {

View File

@@ -7,7 +7,8 @@
</link-traffic-drill-down-list> </link-traffic-drill-down-list>
<div class="line network link-traffic"> <div class="line network link-traffic">
<loading :loading="loading"></loading> <loading :loading="loading"></loading>
<div class="line-header"> <chart-error v-if="showError" :content="errorMsg" />
<div class="line-header" v-if="!showError">
<div class="line-header-left"> <div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div> <div class="line-value-active" v-if="lineTab"></div>
<div class="line-value"> <div class="line-value">
@@ -59,22 +60,21 @@
<script> <script>
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import linkTrafficDrillDownLine from '@/views/charts2/charts/linkMonitor/localComponents/LinkTrafficDrillDownLine'
import LinkTrafficDrillDownList from '@/views/charts2/charts/linkMonitor/localComponents/LinkTrafficDrillDownList' import LinkTrafficDrillDownList from '@/views/charts2/charts/linkMonitor/localComponents/LinkTrafficDrillDownList'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import Loading from '@/components/common/Loading' import Loading from '@/components/common/Loading'
import {useRoute} from 'vue-router' import { useRoute } from 'vue-router'
import {ref, shallowRef} from 'vue' import { ref, shallowRef } from 'vue'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import {chartColor3, chartColor4, unitTypes} from '@/utils/constants' import { chartColor3, chartColor4, unitTypes } from '@/utils/constants'
import {overwriteUrl, urlParamsHandler} from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import {getSecond} from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import {get} from '@/utils/http' import { get } from '@/utils/http'
import {api} from '@/utils/api' import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import {linkTrafficLineChartOption} from '@/views/charts2/charts/options/echartOption' import { linkTrafficLineChartOption } from '@/views/charts2/charts/options/echartOption'
import {stackedLineTooltipFormatter} from '@/views/charts/charts/tools' import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
export default { export default {
name: 'LinkTrafficLine', name: 'LinkTrafficLine',
@@ -126,7 +126,9 @@ export default {
sizes: [3, 4, 6, 8, 9, 10], sizes: [3, 4, 6, 8, 9, 10],
dynamicVariable: '', dynamicVariable: '',
showMarkLine: true, showMarkLine: true,
loading: false loading: false,
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -148,7 +150,7 @@ export default {
overwriteUrl(newUrl) overwriteUrl(newUrl)
}, },
timeFilter: { timeFilter: {
handler (n) { handler () {
if (this.lineTab) { if (this.lineTab) {
this.init(this.lineMetric, this.showMarkLine, 'active') this.init(this.lineMetric, this.showMarkLine, 'active')
} else { } else {
@@ -176,8 +178,17 @@ export default {
this.loading = true this.loading = true
get(api.linkMonitor.totalTrafficAnalysis, params).then((res) => { get(api.linkMonitor.totalTrafficAnalysis, params).then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
res.data.result.forEach((t, i) => { if (this.isNoData) {
this.lineTab = ''
this.mpackets = [
{ 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: '' }
]
}
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') { if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis mpackets[0].analysis = t.totalBitsRate.analysis
@@ -186,10 +197,12 @@ export default {
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : [] mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.ingressBitsRate.values ? t.ingressBitsRate.values : [] mpackets[1].data = t.ingressBitsRate.values ? t.ingressBitsRate.values : []
mpackets[2].data = t.egressBitsRate.values ? t.egressBitsRate.values : [] mpackets[2].data = t.egressBitsRate.values ? t.egressBitsRate.values : []
let num = 0
mpackets.forEach(e => { mpackets.forEach(e => {
e.unitType = 'bps' e.unitType = 'bps'
if (e.name !== 'network.total' && e.analysis.avg == 0) { if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false e.show = false
num += 1
} else { } else {
e.show = true e.show = true
if (!active && !show) { if (!active && !show) {
@@ -197,16 +210,27 @@ export default {
} }
} }
if (this.lineTab === e.class) { if (this.lineTab === e.class) {
if (e.analysis.avg < 1) { if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = '' this.lineTab = ''
this.lineRefer = ''
this.init() this.init()
} }
} }
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { if (num === 3) {
this.echartsInit(this.mpackets) mpackets[0].invertTab = false
}) this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'packets' && val === 'Packets/s') { } else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalPacketsRate.analysis mpackets[0].analysis = t.totalPacketsRate.analysis
@@ -215,10 +239,12 @@ export default {
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : [] mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.ingressPacketsRate.values ? t.ingressPacketsRate.values : [] mpackets[1].data = t.ingressPacketsRate.values ? t.ingressPacketsRate.values : []
mpackets[2].data = t.egressPacketsRate.values ? t.egressPacketsRate.values : [] mpackets[2].data = t.egressPacketsRate.values ? t.egressPacketsRate.values : []
let num = 0
mpackets.forEach(e => { mpackets.forEach(e => {
e.unitType = 'packets/s' e.unitType = 'packets/s'
if (e.name !== 'network.total' && e.analysis.avg == 0) { if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false e.show = false
num += 1
} else { } else {
e.show = true e.show = true
if (!active && !show) { if (!active && !show) {
@@ -226,22 +252,40 @@ export default {
} }
} }
if (this.lineTab === e.class) { if (this.lineTab === e.class) {
if (e.analysis.avg < 1) { if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = '' this.lineTab = ''
this.lineRefer = ''
this.init() this.init()
} }
} }
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { if (num === 3) {
this.echartsInit(this.mpackets) mpackets[0].invertTab = false
}) this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else {
this.$nextTick(() => {
this.echartsInit(this.mpackets)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} }
}) })
} else {
this.showError = true
// todo 此时返回的是msg后期记得改
this.errorMsg = res.msg
// this.errorMsg = res.message
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.isNoData = true this.showError = true
this.errorMsg = e.message
// this.isNoData = true
}).finally(() => { }).finally(() => {
this.loading = false this.loading = false
}) })
@@ -267,7 +311,7 @@ export default {
width: 1 width: 1
}, },
stack: t.name !== 'network.total' ? 'network.total' : '', stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value, params) { symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0]) return _this.symbolSizeSortChange(i, value[0])
}, },
itemStyle: { itemStyle: {
@@ -291,26 +335,33 @@ export default {
} }
]) ])
}, },
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number']), data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number'])
} }
}) })
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
}) })
const str = stackedLineTooltipFormatter(params) // const str = stackedLineTooltipFormatter(params)
return str return stackedLineTooltipFormatter(params)
} }
this.showMarkLine = true this.showMarkLine = true
this.myChart.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
}, },
activeChange (item, index) { activeChange (item, index) {
if (this.isNoData) return
this.lineTab = item.class this.lineTab = item.class
this.legendSelectChange(item, index, 'active') this.legendSelectChange(item, index, 'active')
this.showMarkLine = !item.invertTab this.showMarkLine = !item.invertTab
this.init(this.lineMetric, this.showMarkLine, 'active') this.init(this.lineMetric, this.showMarkLine, 'active')
}, },
mouseenter (item) { mouseenter (item) {
if (this.isNoData) return
this.mousemoveCursor = item.class this.mousemoveCursor = item.class
this.handleActiveBar(item.class) this.handleActiveBar(item.class)
}, },
@@ -360,7 +411,7 @@ export default {
}) })
} }
}, },
handleActiveBar (value) { handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) { if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active') const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
const activeBar = document.querySelector('.network .line-value-active') const activeBar = document.querySelector('.network .line-value-active')
@@ -375,7 +426,7 @@ export default {
this.lineTab = '' this.lineTab = ''
this.handleActiveBar() this.handleActiveBar()
this.showMarkLine = !this.showMarkLine this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e, i) => { this.mpackets.forEach((e) => {
if (!e.invertTab) { if (!e.invertTab) {
e.invertTab = true e.invertTab = true
} }

View File

@@ -2,21 +2,23 @@
<div class="link-traffic-sankey"> <div class="link-traffic-sankey">
<el-tabs v-model="tab"> <el-tabs v-model="tab">
<el-tab-pane :label="$t('linkMonitor.ingress')" name="0"> <el-tab-pane :label="$t('linkMonitor.ingress')" name="0">
<chart-error v-if="showError" :content="errorMsg"></chart-error>
<chart-no-data v-if="ingress"></chart-no-data> <chart-no-data v-if="ingress"></chart-no-data>
<div v-if="tab == 0" class="chart-drawing" id="link-traffic-sankey-0"></div> <div v-if="parseInt(tab) === 0 && !showError" class="chart-drawing" id="link-traffic-sankey-0"></div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('linkMonitor.egress')" name="1"> <el-tab-pane :label="$t('linkMonitor.egress')" name="1">
<chart-error v-if="showError" :content="errorMsg"></chart-error>
<chart-no-data v-if="egress"></chart-no-data> <chart-no-data v-if="egress"></chart-no-data>
<div v-if="tab == 1" class="chart-drawing" id="link-traffic-sankey-1"></div> <div v-if="parseInt(tab) === 1 && !showError" class="chart-drawing" id="link-traffic-sankey-1"></div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<template v-if="tab == 0 && !ingress"> <template v-if="parseInt(tab) === 0 && !ingress">
<div class="sankey__label" style="left: 5%;">External Locations</div> <div class="sankey__label" style="left: 5%;">External Locations</div>
<div class="sankey__label" style="left: 35%;">Next-Hop Internets</div> <div class="sankey__label" style="left: 35%;">Next-Hop Internets</div>
<div class="sankey__label" style="left: 63%;">Links</div> <div class="sankey__label" style="left: 63%;">Links</div>
<div class="sankey__label" style="right: 9%; transform: translateX(50%)">Internal Locations</div> <div class="sankey__label" style="right: 9%; transform: translateX(50%)">Internal Locations</div>
</template> </template>
<template v-else-if="tab == 1 && !egress"> <template v-else-if="parseInt(tab) === 1 && !egress">
<div class="sankey__label" style="left: 5%;">Internal Locations</div> <div class="sankey__label" style="left: 5%;">Internal Locations</div>
<div class="sankey__label" style="left: 33.2%;">Links</div> <div class="sankey__label" style="left: 33.2%;">Links</div>
<div class="sankey__label" style="left: 64.5%;">Next-Hop Internets</div> <div class="sankey__label" style="left: 64.5%;">Next-Hop Internets</div>
@@ -38,11 +40,13 @@ import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { storageKey, unitTypes } from '@/utils/constants' import { storageKey, unitTypes } from '@/utils/constants'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'LinksTrafficSankey', name: 'LinksTrafficSankey',
mixins: [chartMixin], mixins: [chartMixin],
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
setup () { setup () {
@@ -63,7 +67,9 @@ export default {
egress: false, egress: false,
unitConvert, unitConvert,
unitTypes, unitTypes,
cnLinkInfo: JSON.parse(localStorage.getItem(storageKey.linkInfo)) cnLinkInfo: JSON.parse(localStorage.getItem(storageKey.linkInfo)),
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -78,13 +84,14 @@ export default {
overwriteUrl(newUrl) overwriteUrl(newUrl)
}, },
timeFilter: { timeFilter: {
handler (n) { handler () {
this.linkTrafficSankeyDataRequest(this.tab) this.linkTrafficSankeyDataRequest(this.tab)
} }
} }
}, },
methods: { methods: {
linkTrafficSankeyDataRequest (n) { linkTrafficSankeyDataRequest (n) {
n = parseInt(n)
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
@@ -93,19 +100,19 @@ export default {
if (this.queryCondition) { if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ') const condition = this.queryCondition.toLowerCase().split(' or ')
if (condition.length > 1) { if (condition.length > 1) {
if (n == 0) { if (n === 0) {
params.q = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1) params.q = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1)
} else { } else {
params.q = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1) params.q = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1)
} }
} }
if (n == 0) { if (n === 0) {
url = api.linkMonitor.drilldownQuadrupleIngressAnalysis // 入口 url = api.linkMonitor.drilldownQuadrupleIngressAnalysis // 入口
} else { } else {
url = api.linkMonitor.drilldownQquadrupleEgressAnalysis // 出口 url = api.linkMonitor.drilldownQquadrupleEgressAnalysis // 出口
} }
} else { } else {
if (n == 0) { if (n === 0) {
url = api.linkMonitor.quadrupleIngressAnalysis // 入口 url = api.linkMonitor.quadrupleIngressAnalysis // 入口
} else { } else {
url = api.linkMonitor.quadrupleEgressAnalysis // 出口 url = api.linkMonitor.quadrupleEgressAnalysis // 出口
@@ -114,26 +121,32 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(url, params).then(res => { get(url, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
if (n == 0) { this.showError = false
if (n === 0) {
this.ingress = res.data.result.length === 0 this.ingress = res.data.result.length === 0
} else { } else {
this.egress = res.data.result.length === 0 this.egress = res.data.result.length === 0
} }
this.dataProcessing(res.data.result, n) this.dataProcessing(res.data.result, parseInt(n))
} else {
this.showError = true
this.errorMsg = res.message
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.egress = true this.egress = true
this.ingress = true this.ingress = true
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
}, },
dataProcessing (result, tab) { dataProcessing (result, tab) {
if (tab == 0) { if (tab === 0) {
result.forEach(t => { result.forEach(t => {
this.cnLinkInfo.forEach(e => { this.cnLinkInfo.forEach(e => {
if (t.commonIngressLinkId == e.originalLinkId) { if (t.commonIngressLinkId === e.originalLinkId) {
t.linkId = e.linkId t.linkId = e.linkId
t.linkDirection = e.nextHop t.linkDirection = e.nextHop
t.bandwidth = e.bandwidth t.bandwidth = e.bandwidth
@@ -146,7 +159,7 @@ export default {
} else { } else {
result.forEach(t => { result.forEach(t => {
this.cnLinkInfo.forEach(e => { this.cnLinkInfo.forEach(e => {
if (t.commonEgressLinkId == e.originalLinkId) { if (t.commonEgressLinkId === e.originalLinkId) {
t.linkId = e.linkId t.linkId = e.linkId
t.bandwidth = e.bandwidth t.bandwidth = e.bandwidth
t.linkDirection = e.nextHop t.linkDirection = e.nextHop
@@ -170,7 +183,7 @@ export default {
const links1 = [] const links1 = []
const links2 = [] const links2 = []
const linksAnalyze2 = [] const linksAnalyze2 = []
if (tab == 1) { if (tab === 1) {
result.forEach(r => { result.forEach(r => {
// 第一列 // 第一列
if (!data0.some(d => d.name === r.client)) { if (!data0.some(d => d.name === r.client)) {
@@ -357,18 +370,16 @@ export default {
echartsInit (tab, data, links) { echartsInit (tab, data, links) {
const _this = this const _this = this
let dom = '' let dom = ''
if (tab == 0) { if (tab === 0) {
dom = document.getElementById('link-traffic-sankey-0') dom = document.getElementById('link-traffic-sankey-0')
if (this.myChart) { if (this.myChart) {
this.myChart.dispose() this.myChart.dispose()
} }
this.myChart = echarts.init(dom)
} else { } else {
dom = document.getElementById('link-traffic-sankey-1') dom = document.getElementById('link-traffic-sankey-1')
if (this.myChart2) { if (this.myChart2) {
this.myChart2.dispose() this.myChart2.dispose()
} }
this.myChart2 = echarts.init(dom)
} }
this.chartOption = this.$_.cloneDeep(linksTrafficSankeyOption) this.chartOption = this.$_.cloneDeep(linksTrafficSankeyOption)
this.chartOption.tooltip.formatter = function (param) { this.chartOption.tooltip.formatter = function (param) {
@@ -400,14 +411,18 @@ export default {
} }
this.chartOption.series[0].data = data this.chartOption.series[0].data = data
this.chartOption.series[0].links = links this.chartOption.series[0].links = links
if (tab == 0) { this.$nextTick(() => {
this.myChart.setOption(this.chartOption) if (tab === 0) {
} else { this.myChart = echarts.init(dom)
this.myChart2.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
} } else {
this.myChart2 = echarts.init(dom)
this.myChart2.setOption(this.chartOption)
}
})
}, },
resize () { resize () {
if (this.tab == 0) { if (this.tab === 0) {
this.myChart.resize() this.myChart.resize()
} else { } else {
this.myChart2.resize() this.myChart2.resize()
@@ -415,6 +430,9 @@ export default {
} }
}, },
mounted () { mounted () {
this.myChart = null
this.myChart2 = null
this.chartOption = null
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
this.linkTrafficSankeyDataRequest(this.tab) this.linkTrafficSankeyDataRequest(this.tab)
}, 100) }, 100)
@@ -423,6 +441,19 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
if (this.myChart) {
this.myChart.dispose()
// 避免不能生效
this.myChart = null
}
if (this.myChart2) {
this.myChart2.dispose()
this.myChart2 = null
}
this.chartOption = null
this.cnLinkInfo = null
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -1,418 +0,0 @@
<template>
<div class="line network link-traffic">
<loading :loading="loading"></loading>
<div class="line-header">
<div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div>
<div class="line-value">
<div class="line-value-mpackets"
v-show="item.show"
:class=" {'is-active': lineTab === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
v-for="(item, index) in mpackets"
:key="index"
@mouseenter="mouseenter(item)"
@mouseleave="mouseleave(item)"
@click="activeChange(item, index)">
<div class="line-value-mpackets-name">
<div :class="item.class"></div>
<div class="mpackets-name">{{$t(item.name)}}</div>
</div>
<div class="line-value-unit">
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
<span class="line-value-unit-number2">
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span><span v-if="item.unitType">{{item.unitType}}</span>
</span>
</div>
</div>
</div>
</div>
<div class="line-select line-header-right">
<div class="line-select-metric">
<span>{{$t('network.metric')}}:</span>
<div class="line-select__operation">
<el-select
size="mini"
v-model="lineMetric"
popper-class="common-select"
:popper-append-to-body="false"
@change="metricSelectChange"
>
<el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</div>
</div>
</div>
</div>
<div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData" id="linkTrafficLineChart"></div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { linkTrafficLineChartOption } from '@/views/charts2/charts/options/echartOption'
import unitConvert from '@/utils/unit-convert'
import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
import { ref, shallowRef } from 'vue'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import _ from 'lodash'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import Loading from '@/components/common/Loading'
export default {
name: 'linkTrafficDrillDownLine',
components: {
ChartNoData,
Loading
},
setup () {
const { query } = useRoute()
const lineMetric = ref(query.lineMetric || 'Bits/s')
const lineTab = ref(query.lineTab || '')
const queryCondition = ref(query.queryCondition || '')
return {
lineMetric,
lineTab,
queryCondition,
myChart: shallowRef(null)
}
},
props: {
linkTrafficShow: Boolean,
linkTrafficData: Array
},
mixins: [chartMixin],
data () {
return {
options1: [
{
value: 'Bits/s',
label: 'Bits/s'
},
{
value: 'Packets/s',
label: 'Packets/s'
}
],
mpackets: [
{ 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: '' }
],
unitConvert,
unitTypes,
chartDateObject: [],
timer: null,
mousemoveCursor: '',
leftOffset: 0,
sizes: [3, 4, 6, 8, 9, 10],
dynamicVariable: '',
showMarkLine: true,
loading: false
}
},
watch: {
lineTab (n) {
this.$nextTick(() => {
this.handleActiveBar(n)
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
lineTab: n
})
overwriteUrl(newUrl)
})
},
lineMetric (n) {
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
lineMetric: n
})
overwriteUrl(newUrl)
},
timeFilter: {
handler (n) {
if (this.lineTab) {
this.init(this.lineMetric, this.showMarkLine, 'active')
} else {
this.init()
}
}
}
},
methods: {
init (val, show, active) {
if (!val) {
val = this.lineMetric
}
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ')
if (condition.length > 1) {
params.egressParam = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1)
params.ingressParam = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1)
}
}
this.loading = true
get(api.linkMonitor.totalTrafficAnalysis, params).then((res) => {
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
res.data.result.forEach((t, i) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis
mpackets[1].analysis = t.ingressBitsRate.analysis
mpackets[2].analysis = t.egressBitsRate.analysis
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.ingressBitsRate.values ? t.ingressBitsRate.values : []
mpackets[2].data = t.egressBitsRate.values ? t.egressBitsRate.values : []
mpackets.forEach(e => {
e.unitType = 'bps'
if (e.name !== 'network.total' && e.analysis.avg == 0) {
e.show = false
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (e.analysis.avg < 0) {
this.lineTab = ''
this.init()
}
}
})
this.mpackets = mpackets
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalPacketsRate.analysis
mpackets[1].analysis = t.ingressPacketsRate.analysis
mpackets[2].analysis = t.egressPacketsRate.analysis
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.ingressPacketsRate.values ? t.ingressPacketsRate.values : []
mpackets[2].data = t.egressPacketsRate.values ? t.egressPacketsRate.values : []
mpackets.forEach(e => {
e.unitType = 'packets/s'
if (e.name !== 'network.total' && e.analysis.avg == 0) {
e.show = false
} else {
e.show = true
if (!active && !show) {
this.legendSelectChange(e, 'index')
}
}
if (this.lineTab === e.class) {
if (e.analysis.avg < 0) {
this.lineTab = ''
this.init()
}
}
})
this.mpackets = mpackets
this.$nextTick(() => {
this.echartsInit(this.mpackets)
})
}
})
}
}).catch(e => {
console.error(e)
this.isNoData = true
}).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, params) {
return _this.symbolSizeSortChange(i, value[0])
},
itemStyle: {
emphasis: {
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']),
}
})
this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => {
t.seriesName = this.$t(t.seriesName)
})
const str = stackedLineTooltipFormatter(params)
return str
}
this.showMarkLine = true
this.myChart.setOption(this.chartOption)
},
activeChange (item, index) {
this.lineTab = item.class
this.legendSelectChange(item, index, 'active')
this.showMarkLine = !item.invertTab
this.init(this.lineMetric, this.showMarkLine, 'active')
},
mouseenter (item) {
this.mousemoveCursor = item.class
this.handleActiveBar(item.class)
},
mouseleave () {
this.mousemoveCursor = ''
},
dispatchLegendSelectAction (name) {
this.myChart && this.myChart.dispatchAction({
type: 'legendSelect',
name: name
})
},
dispatchLegendUnSelectAction (name) {
this.myChart && this.myChart.dispatchAction({
type: 'legendUnSelect',
name: name
})
},
legendSelectChange (item, index, val) {
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)
}
})
}
if (val === 'active') {
this.mpackets.forEach(t => {
if (item.name === t.name) {
t.invertTab = !t.invertTab
} else {
t.invertTab = true
}
if (t.invertTab && item.name === t.name) {
if (this.lineTab) {
this.lineTab = ''
} else {
this.lineTab = t.class
}
this.mpackets.forEach((e) => {
this.dispatchLegendSelectAction(e.name)
})
}
})
}
},
handleActiveBar (value) {
if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.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()
},
metricSelectChange (val) {
this.lineMetric = val
this.lineTab = ''
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e, i) => {
if (!e.invertTab) {
e.invertTab = true
}
})
this.init(val, this.showMarkLine)
},
symbolSizeSortChange (index, time) {
const dataIntegrationArray = []
if (linkTrafficLineChartOption.series[0]) {
const totalData = linkTrafficLineChartOption.series[0].data.find(t => t[0] === time) // [time, value]
if (totalData) {
dataIntegrationArray.push(totalData)
totalData[2] = 0
}
}
if (linkTrafficLineChartOption.series[1]) {
const ingressData = linkTrafficLineChartOption.series[1].data.find(t => t[0] === time)
if (ingressData) {
dataIntegrationArray.push(ingressData)
ingressData[2] = 1
}
}
if (linkTrafficLineChartOption.series[2]) {
const egressData = linkTrafficLineChartOption.series[2].data.find(t => t[0] === time)
if (egressData) {
dataIntegrationArray.push(egressData)
egressData[2] = 2
}
}
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex]
}
},
mounted () {
this.timer = setTimeout(() => {
if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning)
} else {
this.init()
}
}, 200)
window.addEventListener('resize', this.resize)
},
beforeUnmount () {
clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
}
}
</script>

View File

@@ -1,38 +1,42 @@
<template> <template>
<div class="link-traffic-list"> <div class="link-traffic-list">
<loading :loading="loading"></loading> <loading :loading="loading"></loading>
<div class="link-traffic-list-center">
<div class="link-traffic-list-center-label">{{$t('network.total')}}</div> <chart-error v-if="showError" :content="errorMsg" />
<div class="link-traffic-list-center-value">{{unitConvert(lineData[0].analysis.avg, unitTypes.bps).join('')}}</div> <div v-else>
</div> <div class="link-traffic-list-center">
<div class="link-traffic-list-center"> <div class="link-traffic-list-center-label">{{$t('network.total')}}</div>
<div class="link-traffic-list-center-label">{{$t('linkMonitor.bandwidthUsage')}}</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="bandWidth">{{unitConvert(lineData[0].analysis.avg / bandWidth, unitTypes.percent).join('')}}</div> </div>
<div class="link-traffic-list-center-value" v-else>-</div> <div class="link-traffic-list-center">
</div> <div class="link-traffic-list-center-label">{{$t('linkMonitor.bandwidthUsage')}}</div>
<div class="link-traffic-list-center"> <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-label">{{$t('linkMonitor.npmScore')}}</div> <div class="link-traffic-list-center-value" v-else>-</div>
<div class="link-traffic-list-center-value">{{linkTrafficListData.npmScore || '-'}}</div> </div>
</div> <div class="link-traffic-list-center">
<div class="link-traffic-list-center"> <div class="link-traffic-list-center-label">{{$t('linkMonitor.npmScore')}}</div>
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.tcpConnectionEstablishLatency')}}</div> <div class="link-traffic-list-center-value">{{linkTrafficListData.npmScore || '-'}}</div>
<div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.establishLatencyMs), unitTypes.time).join('')}}</div> </div>
</div> <div class="link-traffic-list-center">
<div class="link-traffic-list-center"> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.tcpConnectionEstablishLatency')}}</div>
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.httpResponse')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.establishLatencyMs), unitTypes.time).join('')}}</div>
<div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.httpResponseLatency), unitTypes.time).join('')}}</div> </div>
</div> <div class="link-traffic-list-center">
<div class="link-traffic-list-center"> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.httpResponse')}}</div>
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.sslResponseLatency')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.httpResponseLatency), unitTypes.time).join('')}}</div>
<div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.sslConLatency), unitTypes.time).join('')}}</div> </div>
</div> <div class="link-traffic-list-center">
<div class="link-traffic-list-center"> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.sslResponseLatency')}}</div>
<div class="link-traffic-list-center-label">{{$t('networkAppPerformance.packetLoss')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(Math.floor(linkTrafficListData.sslConLatency), unitTypes.time).join('')}}</div>
<div class="link-traffic-list-center-value">{{unitConvert(linkTrafficListData.tcpLostlenPercent, unitTypes.percent).join('')}}</div> </div>
</div> <div class="link-traffic-list-center">
<div class="link-traffic-list-center"> <div class="link-traffic-list-center-label">{{$t('networkAppPerformance.packetLoss')}}</div>
<div class="link-traffic-list-center-label">{{$t('overall.packetRetrans')}}</div> <div class="link-traffic-list-center-value">{{unitConvert(linkTrafficListData.tcpLostlenPercent, unitTypes.percent).join('')}}</div>
<div class="link-traffic-list-center-value">{{unitConvert(linkTrafficListData.pktRetransPercent, 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>
</div> </div>
</div> </div>
</template> </template>
@@ -48,6 +52,7 @@ import { useRoute } from 'vue-router'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { computeScore } from '@/utils/tools' import { computeScore } from '@/utils/tools'
import Loading from '@/components/common/Loading' import Loading from '@/components/common/Loading'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'linkTrafficList', name: 'linkTrafficList',
mixins: [chartMixin], mixins: [chartMixin],
@@ -62,6 +67,7 @@ export default {
} }
}, },
components: { components: {
ChartError,
Loading Loading
}, },
data () { data () {
@@ -72,7 +78,9 @@ export default {
linkTrafficListData: {}, linkTrafficListData: {},
cnLinkInfo: JSON.parse(localStorage.getItem(storageKey.linkInfo)), cnLinkInfo: JSON.parse(localStorage.getItem(storageKey.linkInfo)),
bandWidth: 0, bandWidth: 0,
loading: false loading: false,
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -91,6 +99,7 @@ export default {
if (this.queryCondition) { if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ') const condition = this.queryCondition.toLowerCase().split(' or ')
if (condition.length > 1) { if (condition.length > 1) {
// params.egressParam = true
params.egressParam = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1) params.egressParam = condition.find(c => c.indexOf('common_egress_link_id') > -1 || c.indexOf('egress_link_direction') > -1)
params.ingressParam = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1) params.ingressParam = condition.find(c => c.indexOf('common_ingress_link_id') > -1 || c.indexOf('ingress_link_direction') > -1)
let bandwidthAll = 0 let bandwidthAll = 0
@@ -109,10 +118,10 @@ export default {
} }
if (egressLinkId && ingressLinkId) { if (egressLinkId && ingressLinkId) {
this.cnLinkInfo.forEach(e => { this.cnLinkInfo.forEach(e => {
if (ingressLinkId == e.originalLinkId) { if (ingressLinkId === e.originalLinkId) {
bandwidthAll += e.bandwidth bandwidthAll += e.bandwidth
} }
if (egressLinkId == e.originalLinkId) { if (egressLinkId === e.originalLinkId) {
bandwidthAll += e.bandwidth bandwidthAll += e.bandwidth
} }
}) })
@@ -129,22 +138,28 @@ export default {
this.loading = true this.loading = true
get(api.linkMonitor.networkAnalysis, params).then(res => { get(api.linkMonitor.networkAnalysis, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
const dataArr = [0, 1, 2, 3, 4] const data = {
let scoreAll = 0 establishLatencyMs: res.data.result[0].establishLatencyMs || null,
dataArr.forEach(e => { httpResponseLatency: res.data.result[0].httpResponseLatency || null,
const score = computeScore(res.data.result[0], e) sslConLatency: res.data.result[0].sslConLatency || null,
scoreAll += score tcpLostlenPercent: res.data.result[0].tcpLostlenPercent || null,
}) pktRetransPercent: res.data.result[0].pktRetransPercent || null
this.linkTrafficListData = res.data.result[0]
this.linkTrafficListData.npmScore = Math.ceil(scoreAll * 6)
if (this.linkTrafficListData.npmScore > 6) {
this.linkTrafficListData.npmScore = 6
} }
this.linkTrafficListData = res.data.result[0]
this.linkTrafficListData.npmScore = computeScore(data)
} else {
this.showError = true
// todo 此时返回的是msg后期记得改
this.errorMsg = res.msg
// this.errorMsg = res.message
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.isNoData = true this.showError = true
this.errorMsg = e.message
// this.isNoData = true
}).finally(() => { }).finally(() => {
this.loading = false this.loading = false
}) })

View File

@@ -1,30 +1,20 @@
<template> <template>
<div class="network-overview-apps"> <div class="network-overview-apps">
<div class="line-select-metric"> <div class="network-overview-apps-header">
<span>{{$t('network.metric')}}:</span> <div class="network-overview-apps-title">{{$t('networkOverview.appType.providerAndApp')}}</div>
<div class="line-select__operation"> <chart-error v-if="showError" tooltip :content="errorMsg" max-width="350" width="280" />
<el-select
size="mini"
v-model="metricFilter"
placeholder=""
popper-class="common-select"
:popper-append-to-body="false"
@change="metricChange"
>
<el-option v-for="item in metricOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</div>
</div> </div>
<div class="app-cards"> <div class="app-cards">
<div class="app-card" v-for="(app, index) in appData" :key="index"> <div class="app-card" @mouseenter="mouseenter(app)" @mouseleave="mouseleave(app)" v-for="(app, index) in appData" :key="app.type + app.name">
<div class="app-card-title"> <div class="app-card-title">
<div class="app-card-title-name"> <div class="app-card-title-name">
<i class="cn-icon" :class="app.type === 'provider' ? 'cn-icon-entity' : 'cn-icon-app2'"></i> <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> <span @click="drillDownData(app.type, app.name)">{{app.name}}</span>
</div> </div>
<div class="app-card-title-more" v-ele-click-outside="clickOutSide"> <div class="app-card-title-more">
<span><i class="cn-icon cn-icon-more-dark" @click="moreChange(app)"></i></span> <span v-show="app.showMore"><i class="cn-icon cn-icon-more-dark" @mouseenter="mouseenterMore(app)"></i></span>
<span class="app-card-title-more-delete" @click="del(app, index)" v-show="app.moreOptions"><i class="cn-icon cn-icon-delete"></i>{{$t('overall.delete')}}</span> <span class="app-card-title-more-delete" @click="del(app, index)" v-show="app.moreOptions" @mouseleave="mouseleaveMore(app)"><i class="cn-icon cn-icon-delete"></i>{{$t('overall.delete')}}</span>
</div> </div>
</div> </div>
<div class="app-card__bodys"> <div class="app-card__bodys">
@@ -44,12 +34,13 @@
</span> </span>
<span v-else>>500.00%</span> <span v-else>>500.00%</span>
</div> </div>
<div v-else-if="app.value === '-' || app.value === 0" class="app-card__body-content-percent">0</div> <div v-else-if="app.value === '-' || app.value === 0" class="app-card__body-content-percent">+0.00%</div>
</div> </div>
</div> </div>
<div class="app-card__body-previous"> <div class="app-card__body-previous">
<div>Total</div> <div>Total</div>
<div>{{unitConvert(app.total, unitTypes.number).join(' ')}}</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> </div>
</div> </div>
<div class="chart__drawing" v-show="!isNoData" :id="`chart-${app.name}-${app.type}`"></div> <div class="chart__drawing" v-show="!isNoData" :id="`chart-${app.name}-${app.type}`"></div>
@@ -77,7 +68,7 @@
</div> </div>
<div class="add-app__body"> <div class="add-app__body">
<el-tabs v-model="appTypeTab" @tab-click="appTypeTabChange"> <el-tabs v-model="appTypeTab" @tab-click="appTypeTabChange">
<el-tab-pane :label="$t('networkOverview.appType.provider')" :name="0"> <el-tab-pane :label="$t('network.providers')" :name="0">
<div class="body__apps" :class="{'body__apps-no-grid': providerOptions.length === 0}"> <div class="body__apps" :class="{'body__apps-no-grid': providerOptions.length === 0}">
<loading :loading="loadingBody"></loading> <loading :loading="loadingBody"></loading>
<chart-no-data v-if="providerOptions.length === 0 && !loadingBody"></chart-no-data> <chart-no-data v-if="providerOptions.length === 0 && !loadingBody"></chart-no-data>
@@ -127,7 +118,7 @@ import unitConvert from '@/utils/unit-convert'
import { storageKey, unitTypes, networkTable, operationType, curTabState } from '@/utils/constants' import { storageKey, unitTypes, networkTable, operationType, curTabState } from '@/utils/constants'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { appListChartOption } from '@/views/charts2/charts/options/echartOption' import { appListChartOption } from '@/views/charts2/charts/options/echartOption'
import { ref, shallowRef } from 'vue' import { shallowRef } from 'vue'
import { get, put } from '@/utils/http' import { get, put } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import _ from 'lodash' import _ from 'lodash'
@@ -137,7 +128,6 @@ import loading from '@/components/common/Loading'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import { appStackedLineTooltipFormatter } from '@/views/charts/charts/tools' import { appStackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router'
export default { export default {
name: 'NetworkOverviewApps', name: 'NetworkOverviewApps',
@@ -148,20 +138,7 @@ export default {
mixins: [chartMixin], mixins: [chartMixin],
data () { data () {
return { return {
metricOptions: [ testData: '测试值',
{
value: 'Bits/s',
label: 'Bits/s'
},
{
value: 'Packets/s',
label: 'Packets/s'
},
{
value: 'Sessions/s',
label: 'Sessions/s'
}
],
appData: [], appData: [],
// 假数据 // 假数据
appTempData: [], appTempData: [],
@@ -188,25 +165,23 @@ export default {
timerSearch: null, timerSearch: null,
loadingBody: false, loadingBody: false,
curTabState: curTabState, curTabState: curTabState,
urlChangeParams: {} urlChangeParams: {},
showError: false,
errorMsg: ''
}
},
props: {
metric: {
type: String,
default: 'Bits/s'
} }
}, },
setup () { setup () {
const { query } = useRoute()
const metricFilter = ref(query.appListMetric || 'Bits/s')
return { return {
metricFilter,
myChart: shallowRef([]) myChart: shallowRef([])
} }
}, },
watch: { watch: {
metricFilter (n) {
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
appListMetric: n
})
overwriteUrl(newUrl)
},
showAddApp: { showAddApp: {
deep: true, deep: true,
handler (n) { handler (n) {
@@ -218,10 +193,11 @@ export default {
} }
} }
}, },
timeFilter: { timeFilter (n) {
handler (n) { this.init()
this.init() },
} metric (n) {
this.init()
} }
}, },
methods: { methods: {
@@ -249,7 +225,7 @@ export default {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
appLabels: appCards.map(item => { appLabels: appCards.map(item => {
return item.name return `'${item.name}'`
}).join(',') }).join(',')
} }
prevRequest = get(api.netWorkOverview.applicationCycleTrafficTotal, params) prevRequest = get(api.netWorkOverview.applicationCycleTrafficTotal, params)
@@ -261,7 +237,7 @@ export default {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
appCompanies: providerCards.map(item => { appCompanies: providerCards.map(item => {
return item.name return `'${item.name}'`
}).join(',') }).join(',')
} }
prevRequest = get(api.netWorkOverview.appCompanyCycleTrafficTotal, params) prevRequest = get(api.netWorkOverview.appCompanyCycleTrafficTotal, params)
@@ -273,13 +249,22 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([prevRequest, request]).then(res => { Promise.all([prevRequest, request]).then(res => {
this.isNoData = (res[0].data.result.length && res[1].data.result.length) === 0 this.isNoData = (res[0].data.result.length && res[1].data.result.length) === 0
if (this.isNoData) {
this.appData = this.appData.map(t => {
return {
name: t.name,
type: t.type
}
})
}
if (res[0].code === 200 && res[1].code === 200) { if (res[0].code === 200 && res[1].code === 200) {
this.showError = false
const prevData = res[0].data.result const prevData = res[0].data.result
const data = res[1].data.result const data = res[1].data.result
let toCompareType = 'bytes' let toCompareType = 'bytes'
if (this.metricFilter === 'Sessions/s') { if (this.metric === 'Sessions/s') {
toCompareType = 'sessions' toCompareType = 'sessions'
} else if (this.metricFilter === 'Packets/s') { } else if (this.metric === 'Packets/s') {
toCompareType = 'packets' toCompareType = 'packets'
} }
data.forEach(d => { data.forEach(d => {
@@ -301,9 +286,13 @@ export default {
}) })
} }
}) })
} else {
this.showError = true
this.errorMsg = res[0].message
} }
}).catch(e => { }).catch(e => {
console.error(e) this.showError = true
this.errorMsg = e.message
this.isNoData = true this.isNoData = true
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
@@ -420,18 +409,18 @@ export default {
...chartOption.series[0], ...chartOption.series[0],
data: obj.lineData.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number']), data: obj.lineData.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number']),
lineStyle: { lineStyle: {
color: obj.trend === 'up' ? '#7FA054' : '#35ADDA' color: '#35ADDA'
}, },
areaStyle: { areaStyle: {
opacity: 0.1, opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ {
offset: 0, offset: 0,
color: obj.trend === 'up' ? '#7FA054' : '#35ADDA' color: '#35ADDA'
}, },
{ {
offset: 1, offset: 1,
color: obj.trend === 'up' ? '#7FA054' : '#35ADDA' color: '#35ADDA'
} }
]) ])
} }
@@ -463,7 +452,6 @@ export default {
}, },
addApp (pageNo, val, show) { addApp (pageNo, val, show) {
this.showAddApp = true this.showAddApp = true
const letter = 'abcdefghijklmnopqrstuvwxyz'
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
@@ -482,7 +470,7 @@ export default {
} else { } else {
params.pageNo = 1 params.pageNo = 1
} }
if (this.appTypeTab == 0) { if (parseFloat(this.appTypeTab) === 0) {
params.type = 'overviewProvide' params.type = 'overviewProvide'
get(api.dict, params).then(res => { get(api.dict, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
@@ -507,7 +495,7 @@ export default {
this.loading = false this.loading = false
this.loadingBody = false this.loadingBody = false
}) })
} else if (this.appTypeTab == 1) { } else if (parseFloat(this.appTypeTab) === 1) {
params.type = 'overviewApp' params.type = 'overviewApp'
get(api.dict, params).then(res => { 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)) res.data.list = res.data.list.filter(l => !this.appData.some(pd => pd.type === 'app' && pd.name === l.value))
@@ -547,7 +535,7 @@ export default {
cancelApp () { cancelApp () {
this.showAddApp = false this.showAddApp = false
}, },
appTypeTabChange (val) { appTypeTabChange () {
this.pageObj.pageNo = 1 this.pageObj.pageNo = 1
this.searcherApp = '' this.searcherApp = ''
this.addApp() this.addApp()
@@ -690,17 +678,46 @@ export default {
}) })
} }
}, },
moreChange (app) { // moreChange (app) {
this.appData.forEach(t => { // this.appData.forEach(t => {
if (t.name === app.name && t.type === app.type) { // if (t.name === app.name && t.type === app.type) {
t.moreOptions = !t.moreOptions // t.moreOptions = !t.moreOptions
} // }
}) // })
}, // },
resize () { resize () {
this.myChart.forEach(t => { this.myChart.forEach(t => {
t.resize() t.resize()
}) })
},
mouseenterMore (app) {
this.appData.forEach(t => {
if (t.name === app.name && t.type === app.type) {
t.moreOptions = true
}
})
},
mouseleaveMore (app) {
this.appData.forEach(t => {
if (t.name === app.name && t.type === app.type) {
t.moreOptions = false
}
})
},
mouseenter (app) {
this.appData.forEach(t => {
if (t.name === app.name && t.type === app.type) {
t.showMore = true
}
})
},
mouseleave (app) {
this.appData.forEach(t => {
if (t.name === app.name && t.type === app.type) {
t.showMore = false
t.moreOptions = false
}
})
} }
}, },
mounted () { mounted () {
@@ -720,6 +737,8 @@ export default {
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
clearTimeout(this.timerScroll) clearTimeout(this.timerScroll)
clearTimeout(this.timerSearch) clearTimeout(this.timerSearch)
this.myChart = null
this.unitConvert = null
} }
} }
</script> </script>

View File

@@ -1,8 +1,13 @@
<template> <template>
<div class="ddos-detection"> <div class="ddos-detection">
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<div class="ddos-detection-title"><i class="cn-icon cn-icon-a-DDosDetection"></i>{{$t('network.ddosDetection')}}</div> <chart-error info v-if="showError" :content="errorMsg" />
<div class="ddos-detection-value" v-if="!isNoData">
<div class="ddos-detection-title">
<i class="cn-icon cn-icon-a-DDosDetection"></i>
{{$t('network.ddosDetection')}}
</div>
<div class="ddos-detection-value" v-if="!isNoData && !showError">
<div class="ddos-detection-type"> <div class="ddos-detection-type">
<div class="ddos-detection-type-value"> <div class="ddos-detection-type-value">
<div class="ddos-detection-type-value-name">{{$t('network.numberOfAttacks')}}</div> <div class="ddos-detection-type-value-name">{{$t('network.numberOfAttacks')}}</div>
@@ -28,21 +33,25 @@ import { get } from '@/utils/http'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NetworkOverviewDdosDetection', name: 'NetworkOverviewDdosDetection',
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
mixins: [chartMixin], mixins: [chartMixin],
data () { data () {
return { return {
ddosData: {}, ddosData: {},
isNoData: false isNoData: false,
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.ddosDetectDataRequests() this.ddosDetectDataRequests()
} }
} }
@@ -56,13 +65,22 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(api.netWorkOverview.ddosEventAnalysis, params).then(res => { get(api.netWorkOverview.ddosEventAnalysis, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
if (res.data.result.length === 0) { if (res.data.result.length === 0) {
this.isNoData = true this.isNoData = true
} else { } else {
this.ddosData = res.data.result[0] this.ddosData = res.data.result[0]
this.isNoData = false this.isNoData = false
} }
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
} }
}).catch((e) => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="line network"> <div class="line network">
<div class="line-header"> <chart-error v-if="showError" :content="errorMsg" />
<div class="line-header" v-if="!showError">
<div class="line-header-left"> <div class="line-header-left">
<div class="line-value-active" v-if="lineTab"></div> <div class="line-value-active" v-if="lineTab"></div>
<div class="line-value"> <div class="line-value">
@@ -26,20 +27,6 @@
</div> </div>
</div> </div>
<div class="line-select line-header-right"> <div class="line-select line-header-right">
<div class="line-select-metric">
<span>{{$t('network.metric')}}:</span>
<div class="line-select__operation">
<el-select
size="mini"
v-model="lineMetric"
popper-class="common-select"
:popper-append-to-body="false"
@change="metricSelectChange"
>
<el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</div>
</div>
<div class="line-select-reference-line"> <div class="line-select-reference-line">
<span>{{$t('network.referenceLine')}}:</span> <span>{{$t('network.referenceLine')}}:</span>
<div class="line-select__operation"> <div class="line-select__operation">
@@ -58,8 +45,12 @@
</div> </div>
</div> </div>
<div style="height: calc(100% - 74px); position: relative"> <div style="height: calc(100% - 74px); position: relative">
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div class="chart-drawing" v-show="showMarkLine && !isNoData" id="overviewLineChart"></div> <div class="chart-drawing" v-show="showMarkLine && !isNoData && !showError" id="overviewLineChart"></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>
</div> </div>
</template> </template>
@@ -79,42 +70,38 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NetworkOverviewLine', name: 'NetworkOverviewLine',
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
props: {
metric: {
type: String,
default: 'Bits/s'
}
},
setup () { setup () {
const { query } = useRoute() const { query } = useRoute()
const lineMetric = ref(query.lineMetric || 'Bits/s')
const lineRefer = ref(query.lineRefer || 'Average') const lineRefer = ref(query.lineRefer || 'Average')
const lineTab = ref(query.lineTab || '') const lineTab = ref(query.lineTab || '')
const queryCondition = ref(query.queryCondition || '') const queryCondition = ref(query.queryCondition || '')
const tabOperationType = ref(query.tabOperationType)
const networkOverviewBeforeTab = ref(query.networkOverviewBeforeTab)
return { return {
lineMetric,
lineRefer, lineRefer,
lineTab, lineTab,
queryCondition, queryCondition,
tabOperationType,
networkOverviewBeforeTab,
myChart: shallowRef(null) myChart: shallowRef(null)
} }
}, },
mixins: [chartMixin], mixins: [chartMixin],
data () { data () {
return { return {
options1: [
{
value: 'Bits/s',
label: 'Bits/s'
},
{
value: 'Packets/s',
label: 'Packets/s'
},
{
value: 'Sessions/s',
label: 'Sessions/s'
}
],
options2: [ options2: [
{ {
value: 'Average', value: 'Average',
@@ -145,7 +132,11 @@ export default {
leftOffset: 0, leftOffset: 0,
sizes: [3, 4, 6, 8, 9, 10], sizes: [3, 4, 6, 8, 9, 10],
dynamicVariable: '', dynamicVariable: '',
showMarkLine: true showMarkLine: true,
mouseDownFlag: false,
brushHistory: [],
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -159,13 +150,6 @@ export default {
overwriteUrl(newUrl) overwriteUrl(newUrl)
}) })
}, },
lineMetric (n) {
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
lineMetric: n
})
overwriteUrl(newUrl)
},
lineRefer (n) { lineRefer (n) {
const { query } = this.$route const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, { const newUrl = urlParamsHandler(window.location.href, query, {
@@ -174,34 +158,72 @@ export default {
overwriteUrl(newUrl) overwriteUrl(newUrl)
}, },
timeFilter: { timeFilter: {
handler (n) { handler () {
if (this.lineTab) { if (this.lineTab) {
this.init(this.lineMetric, this.showMarkLine, 'active') this.init(this.metric, this.showMarkLine, 'active')
} else { } else {
this.init() this.init()
} }
} }
},
metric (n) {
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e) => {
if (!e.invertTab) {
e.invertTab = true
}
})
this.init(n, this.showMarkLine, '', n)
} }
}, },
methods: { methods: {
init (val, show, active) { init (val, show, active, n) {
if (!val) { if (!val) {
val = this.lineMetric val = this.metric
} }
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
// const condition = this.$store.getters.getQueryCondition let condition = ''
// const condition = this.$route.query.queryCondition ? this.$route.query.queryCondition : '' if (this.queryCondition && this.tabOperationType !== '3') {
if (this.queryCondition) {
params.q = this.queryCondition params.q = this.queryCondition
} else if (this.tabOperationType == '3' && this.queryCondition) {
if (this.queryCondition.indexOf(' OR ') > -1) {
if (this.networkOverviewBeforeTab === 'isp') {
condition = this.queryCondition.split(/["|'= ](.*?)["|'= ]/)
params.q = `notEmpty(${condition[0]}) OR notEmpty(${condition[9]})`
} else {
condition = this.queryCondition.split(/["|'= ](.*?)["|'= ]/)
params.q = `notEmpty(${condition[0]}) OR notEmpty(${condition[5]})`
}
} else {
condition = this.queryCondition.split(/['=](.*?)['=]/)
params.q = `notEmpty(${condition[0]})`
}
} }
this.toggleLoading(true) this.toggleLoading(true)
get(api.netWorkOverview.totalTrafficAnalysis, params).then((res) => { get(api.netWorkOverview.totalTrafficAnalysis, params).then((res) => {
this.errorMsg = res.message
if (res.code === 200) { if (res.code === 200) {
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
res.data.result.forEach((t, i) => { 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: '' }
]
}
res.data.result.forEach((t) => {
if (t.type === 'bytes' && val === 'Bits/s') { if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalBitsRate.analysis mpackets[0].analysis = t.totalBitsRate.analysis
@@ -216,10 +238,12 @@ export default {
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : [] mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : [] mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : [] mpackets[5].data = t.other.values ? t.other.values : []
let num = 0
mpackets.forEach(e => { mpackets.forEach(e => {
e.unitType = 'bps' e.unitType = 'bps'
if (e.name !== 'network.total' && e.analysis.avg == 0) { if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false e.show = false
num += 1
} else { } else {
e.show = true e.show = true
if (!active && show !== this.lineRefer) { if (!active && show !== this.lineRefer) {
@@ -227,16 +251,28 @@ export default {
} }
} }
if (this.lineTab === e.class) { if (this.lineTab === e.class) {
if (e.analysis.avg < 0) { if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = '' this.lineTab = ''
this.lineRefer = ''
this.init() this.init()
} }
} }
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { if (num === 5) {
this.echartsInit(this.mpackets, show) mpackets[0].invertTab = false
}) this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
})
} else {
if (n) this.lineTab = ''
this.$nextTick(() => {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'packets' && val === 'Packets/s') { } else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalPacketsRate.analysis mpackets[0].analysis = t.totalPacketsRate.analysis
@@ -251,10 +287,12 @@ export default {
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : [] mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : [] mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : [] mpackets[5].data = t.other.values ? t.other.values : []
let num = 0
mpackets.forEach(e => { mpackets.forEach(e => {
e.unitType = 'packets/s' e.unitType = 'packets/s'
if (e.name !== 'network.total' && e.analysis.avg == 0) { if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
e.show = false e.show = false
num += 1
} else { } else {
e.show = true e.show = true
if (!active && show !== this.lineRefer) { if (!active && show !== this.lineRefer) {
@@ -262,16 +300,28 @@ export default {
} }
} }
if (this.lineTab === e.class) { if (this.lineTab === e.class) {
if (e.analysis.avg < 0) { if (parseFloat(e.analysis.avg) <= 0) {
this.lineTab = '' this.lineTab = ''
this.lineRefer = ''
this.init() this.init()
} }
} }
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { if (num === 5) {
this.echartsInit(this.mpackets, show) mpackets[0].invertTab = false
}) this.lineTab = 'total'
this.legendSelectChange(mpackets[0], 0)
this.$nextTick(() => {
this.echartsInit(this.mpackets, true)
})
} else {
if (n) this.lineTab = ''
this.$nextTick(() => {
this.echartsInit(this.mpackets, show)
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
} else if (t.type === 'sessions' && val === 'Sessions/s') { } else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets) const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].analysis = t.totalSessionsRate.analysis mpackets[0].analysis = t.totalSessionsRate.analysis
@@ -281,24 +331,51 @@ export default {
e.show = false e.show = false
} }
e.unitType = 'sessions/s' e.unitType = 'sessions/s'
if (show !== this.lineRefer) { e.invertTab = false
this.legendSelectChange(e, 0) this.lineTab = 'total'
} this.legendSelectChange(e, 0)
}) })
this.mpackets = mpackets this.mpackets = mpackets
this.$nextTick(() => { this.$nextTick(() => {
this.echartsInit(this.mpackets, show) this.echartsInit(this.mpackets, true)
}) })
} }
}) })
} else {
this.showError = true
this.errorMsg = res.message
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.isNoData = true this.showError = true
this.errorMsg = e.message
this.isNoData = false
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
}, },
/**
* 初始化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'
}
}
},
echartsInit (echartsData, show) { echartsInit (echartsData, show) {
if (this.lineTab) { if (this.lineTab) {
this.handleActiveBar() this.handleActiveBar()
@@ -307,8 +384,13 @@ export default {
echartsData = echartsData.filter(t => t.show === true) echartsData = echartsData.filter(t => t.show === true)
} }
const _this = this const _this = this
const dom = document.getElementById('overviewLineChart') // !this.myChart && (this.myChart = echarts.init(dom))
!this.myChart && (this.myChart = echarts.init(dom)) // 此处为验证是否因dom未销毁导致图表出错后续可能会改
let dom = null
dom = document.getElementById('overviewLineChart')
if (this.myChart) {
this.myChart.dispose()
}
this.chartOption = stackedLineChartOption this.chartOption = stackedLineChartOption
const chartOption = this.chartOption.series[0] const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => { this.chartOption.series = echartsData.map((t, i) => {
@@ -320,7 +402,7 @@ export default {
width: 1 width: 1
}, },
stack: t.name !== 'network.total' ? 'network.total' : '', stack: t.name !== 'network.total' ? 'network.total' : '',
symbolSize: function (value, params) { symbolSize: function (value) {
return _this.symbolSizeSortChange(i, value[0]) return _this.symbolSizeSortChange(i, value[0])
}, },
itemStyle: { itemStyle: {
@@ -364,7 +446,7 @@ export default {
} }
}) })
if (!show) { if (!show) {
this.chartOption.series.forEach((t, i) => { this.chartOption.series.forEach((t) => {
t.markLine.label.show = false t.markLine.label.show = false
t.markLine = [] t.markLine = []
}) })
@@ -396,20 +478,75 @@ export default {
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
}) })
const str = stackedLineTooltipFormatter(params) const str = stackedLineTooltipFormatter(params)
return str return str
} }
this.showMarkLine = true this.showMarkLine = true
this.myChart.setOption(this.chartOption) this.$nextTick(() => {
this.myChart = echarts.init(dom)
this.myChart.setOption(this.chartOption)
// 设置参见官网https://echarts.apache.org/zh/api.html#action.brush.brush
this.myChart.dispatchAction({
// 刷选模式的开关。使用此 action 可将当前鼠标变为可刷选状态。事实上,点击 toolbox 中的 brush 按钮时,就是通过这个 action将当前普通鼠标变为刷选器的。
type: 'takeGlobalCursor',
// 如果想变为“可刷选状态”,必须设置。不设置则会关闭“可刷选状态”。
key: 'brush',
brushOption: {
// 参见 brush 组件的 brushType。如果设置为 false 则关闭“可刷选状态”。
brushType: 'lineX',
xAxisIndex: 'all',
// 单击清除选框
brushMode: 'single',
// 选择完毕再返回所选数据
throttleType: 'debounce'
}
})
const self = this
this.myChart.on('brushEnd', function (params) {
self.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
if (!self.mouseDownFlag) {
// 避免点击空白区域报错
if (params.areas !== undefined && params.areas.length > 0) {
self.brushHistory.unshift({
startTime: _.cloneDeep(self.timeFilter.startTime) * 1000,
endTime: _.cloneDeep(self.timeFilter.endTime) * 1000
})
const rangeObj = {
startTime: Math.ceil(params.areas[0].coordRange[0]),
endTime: Math.ceil(params.areas[0].coordRange[1])
}
// todo 目前暂定框选最小范围为5分钟后续可能会变动
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) {
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000
}
_this.$store.commit('setRangeEchartsData', rangeObj)
}
}
})
})
}, },
activeChange (item, index) { activeChange (item, index) {
if (this.isNoData) return
this.lineTab = item.class this.lineTab = item.class
this.legendSelectChange(item, index, 'active') this.legendSelectChange(item, index, 'active')
this.showMarkLine = !item.invertTab this.showMarkLine = !item.invertTab
this.init(this.lineMetric, this.showMarkLine, 'active') this.init(this.metric, this.showMarkLine, 'active')
}, },
mouseenter (item) { mouseenter (item) {
if (this.isNoData) return
this.mousemoveCursor = item.class this.mousemoveCursor = item.class
this.handleActiveBar(item.class) this.handleActiveBar(item.class)
}, },
@@ -459,7 +596,7 @@ export default {
}) })
} }
}, },
handleActiveBar (value) { handleActiveBar () {
if (document.querySelector('.network .line-value-mpackets.is-active')) { if (document.querySelector('.network .line-value-mpackets.is-active')) {
const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active') const { offsetLeft, clientWidth, clientLeft } = document.querySelector('.network .line-value-mpackets.is-active')
const activeBar = document.querySelector('.network .line-value-active') const activeBar = document.querySelector('.network .line-value-active')
@@ -469,18 +606,6 @@ export default {
resize () { resize () {
this.myChart.resize() this.myChart.resize()
}, },
metricSelectChange (val) {
this.lineMetric = val
this.lineTab = ''
this.handleActiveBar()
this.showMarkLine = !this.showMarkLine
this.mpackets.forEach((e, i) => {
if (!e.invertTab) {
e.invertTab = true
}
})
this.init(val, this.showMarkLine)
},
referenceSelectChange (val) { referenceSelectChange (val) {
this.lineRefer = val this.lineRefer = val
this.echartsInit(this.mpackets, this.showMarkLine) this.echartsInit(this.mpackets, this.showMarkLine)
@@ -532,13 +657,33 @@ export default {
dataIntegrationArray.sort((a, b) => { return a[1] - b[1] }) dataIntegrationArray.sort((a, b) => { return a[1] - b[1] })
const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index) const sortIndex = dataIntegrationArray.findIndex(a => a[2] === index)
return this.sizes[sortIndex] return this.sizes[sortIndex]
},
/**
* 鼠标右键返回框选的时间范围
*/
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 () { mounted () {
// todo 初始化鼠标事件,开启右键返回
// this.domInit()
this.myChart = null
this.chartOption = null
this.timer = setTimeout(() => { this.timer = setTimeout(() => {
if (this.lineTab) { if (this.lineTab) {
const data = this.mpackets.find(t => t.class === this.lineTab) const data = this.mpackets.find(t => t.class === this.lineTab)
this.activeChange(data, data.positioning) if (data && data.positioning) {
this.activeChange(data, data.positioning)
}
} else { } else {
this.init() this.init()
} }
@@ -548,6 +693,15 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
let myChart = echarts.getInstanceByDom(document.getElementById('overviewLineChart'))
if (myChart) {
echarts.dispose(myChart)
}
this.myChart = null
// 检测时发现该方法占用较大内存,且未被释放
this.unitConvert = null
myChart = null
} }
} }
</script> </script>

View File

@@ -1,18 +1,24 @@
<template> <template>
<div class="performance-event"> <div class="performance-event">
<div class="performance-event-title"><i class="cn-icon cn-icon-a-NetworkPerformanceEvent"></i>{{$t('network.networkPerEvent')}}</div> <div class="performance-event-title">
<i class="cn-icon cn-icon-a-NetworkPerformanceEvent"></i>{{$t('network.networkPerEvent')}}
</div>
<div class="performance-event-value"> <div class="performance-event-value">
<div class="performance-event-pie"> <div class="performance-event-pie">
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<chart-error v-if="showError1" info :content="errorMsg1" />
<div class="chart-drawing" id="chart1" v-show="!isNoData"></div> <div class="chart-drawing" id="chart1" v-show="!isNoData"></div>
</div> </div>
<div class="performance-event-pie-hr"></div> <div class="performance-event-pie-hr"></div>
<div class="performance-event-pie"> <div class="performance-event-pie">
<chart-no-data v-if="isNoData2"></chart-no-data> <chart-no-data v-if="isNoData2"></chart-no-data>
<chart-error v-if="showError1" info :content="errorMsg1" />
<div class="chart-drawing" id="chart2" v-show="!isNoData2"></div> <div class="chart-drawing" id="chart2" v-show="!isNoData2"></div>
</div> </div>
</div> </div>
<el-button class="pie-button" size="small">{{$t('network.dashboards')}}<i class="cn-icon cn-icon-arrow-right"></i></el-button> <el-button class="pie-button" size="small" @click="routerJump">
{{$t('network.dashboards')}}<i class="cn-icon cn-icon-arrow-right"></i>
</el-button>
</div> </div>
</template> </template>
@@ -25,6 +31,7 @@ import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NetworkOverviewPerformanceEvent', name: 'NetworkOverviewPerformanceEvent',
setup () { setup () {
@@ -35,6 +42,7 @@ export default {
}, },
mixins: [chartMixin], mixins: [chartMixin],
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
data () { data () {
@@ -43,12 +51,17 @@ export default {
isNoData: false, isNoData: false,
isNoData2: false, isNoData2: false,
loading1: false, loading1: false,
loading2: false loading2: false,
showError1: false,
showError2: false,
errorMsg1: '',
errorMsg2: ''
} }
}, },
methods: { methods: {
init () { init () {
const params = { const params = {
// startTime: true,
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
@@ -60,6 +73,7 @@ export default {
this.loading1 = true this.loading1 = true
get(api.netWorkOverview.eventSeverity, params).then(res => { get(api.netWorkOverview.eventSeverity, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError1 = false
if (res.data.result.length === 0) { if (res.data.result.length === 0) {
this.isNoData = true this.isNoData = true
return return
@@ -88,8 +102,14 @@ export default {
this.chartOption.series[0].data = res.data.result.sort((a, b) => { return a.index - b.index }) this.chartOption.series[0].data = res.data.result.sort((a, b) => { return a.index - b.index })
this.myChart.setOption(this.chartOption) this.myChart.setOption(this.chartOption)
} else { } else {
this.isNoData = true this.isNoData = false
this.showError1 = true
this.errorMsg1 = res.message
} }
}).catch((e) => {
this.isNoData = false
this.showError1 = true
this.errorMsg1 = e.message
}).finally(() => { }).finally(() => {
this.loading1 = false this.loading1 = false
}) })
@@ -100,6 +120,8 @@ export default {
this.loading2 = true this.loading2 = true
get(api.netWorkOverview.eventType, params).then(res => { get(api.netWorkOverview.eventType, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError2 = false
if (res.data.result.length === 0) { if (res.data.result.length === 0) {
this.isNoData2 = true this.isNoData2 = true
return return
@@ -119,12 +141,29 @@ export default {
} }
this.chartOption2.series[0].data = res.data.result this.chartOption2.series[0].data = res.data.result
this.myChart2.setOption(this.chartOption2) this.myChart2.setOption(this.chartOption2)
} else {
this.isNoData2 = false
this.showError2 = true
this.errorMsg2 = res.message
} }
}).catch((e) => {
this.isNoData2 = false
this.showError2 = true
this.errorMsg2 = e.message
}).finally(() => { }).finally(() => {
this.loading2 = false this.loading2 = false
}) })
} }
}, },
routerJump () {
this.$router.push({
path: '/panel/networkAppPerformance',
query: {
tabIndex: 2,
t: +new Date()
}
})
},
resize () { resize () {
this.myChart.resize() this.myChart.resize()
this.myChart2.resize() this.myChart2.resize()
@@ -154,6 +193,8 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null
this.myChart2 = null
} }
} }
</script> </script>

View File

@@ -1,6 +1,8 @@
<template> <template>
<div class="npm-app"> <div class="npm-app" :class="showError?'npm-app-border':''">
<div class="npm-app-left"> <chart-error v-if="showError" :content="errorMsg" />
<div class="npm-app-left" v-if="!showError">
<div class="npm-app-letter" :class="{'npm-app-letter-no-data': isNoData}"> <div class="npm-app-letter" :class="{'npm-app-letter-no-data': isNoData}">
<div v-for="(letter, index) in colorPatchData" :key="index"> <div v-for="(letter, index) in colorPatchData" :key="index">
{{letter.letter}} {{letter.letter}}
@@ -21,7 +23,7 @@
class="app-table" class="app-table"
height="100%" height="100%"
empty-text=" " empty-text=" "
> v-if="!showError">
<template v-for="(item, index) in customTableTitles" :key="index"> <template v-for="(item, index) in customTableTitles" :key="index">
<el-table-column class="data-column"> <el-table-column class="data-column">
<template #header> <template #header>
@@ -134,9 +136,16 @@ import unitConvert from '@/utils/unit-convert'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http' import { get } from '@/utils/http'
import { getChainRatio, computeScore, changeCurTab, urlParamsHandler, overwriteUrl, getUserDrilldownTableConfig } from '@/utils/tools' import {
getChainRatio,
computeScore,
urlParamsHandler,
overwriteUrl,
getUserDrilldownTableConfig
} from '@/utils/tools'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmAppCategoryScore', name: 'NpmAppCategoryScore',
data () { data () {
@@ -162,16 +171,19 @@ export default {
], ],
isNoData: false, isNoData: false,
curTabState: curTabState, curTabState: curTabState,
urlChangeParams: {} urlChangeParams: {},
showError: false,
errorMsg: ''
} }
}, },
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
mixins: [chartMixin], mixins: [chartMixin],
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.init() this.init()
} }
} }
@@ -187,70 +199,98 @@ export default {
const lastCycleTrafficRequest = get(api.npm.overview.appTrafficAnalysis, { ...params, cycle: 1 }) const lastCycleTrafficRequest = get(api.npm.overview.appTrafficAnalysis, { ...params, cycle: 1 })
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([currentTrafficRequest, lastCycleTrafficRequest]).then(res => { Promise.all([currentTrafficRequest, lastCycleTrafficRequest]).then(res => {
const prevData = res[1].data.result if (res[0].code === 200 && res[1].code === 200) {
const data = res[0].data.result this.showError = false
if (data && data.length > 0) { this.errorMsg = ''
this.isNoData = false const prevData = res[1].data.result
const tableData = data.map(d => { const data = res[0].data.result
const mapping = npmCategoryInfoMapping.find(mapping => mapping.appSubcategory === d.appSubcategory) if (data && data.length > 0) {
const result = { this.isNoData = false
...mapping, const tableData = data.map(d => {
...d, const mapping = npmCategoryInfoMapping.find(mapping => mapping.appSubcategory === d.appSubcategory)
bytesRateChainRatio: '-', const result = {
inboundBytesRateChainRatio: '-', ...mapping,
outboundBytesRateChainRatio: '-' ...d,
} bytesRateChainRatio: '-',
inboundBytesRateChainRatio: '-',
outboundBytesRateChainRatio: '-'
}
const prev = prevData.find(p => p.appSubcategory === d.appSubcategory) const prev = prevData.find(p => p.appSubcategory === d.appSubcategory)
if (prev) { if (prev) {
result.bytesRateChainRatio = getChainRatio(d.totalBitsRate, prev.totalBitsRate) result.bytesRateChainRatio = getChainRatio(d.totalBitsRate, prev.totalBitsRate)
result.inboundBytesRateChainRatio = getChainRatio(d.inboundBitsRate, prev.inboundBitsRate) result.inboundBytesRateChainRatio = getChainRatio(d.inboundBitsRate, prev.inboundBitsRate)
result.outboundBytesRateChainRatio = getChainRatio(d.outboundBitsRate, prev.outboundBitsRate) result.outboundBytesRateChainRatio = getChainRatio(d.outboundBitsRate, prev.outboundBitsRate)
} }
return result return result
}) })
// 计算分数 // 计算分数
const tcpRequest = get(api.npm.overview.appTcpSessionDelay, params) const tcpRequest = get(api.npm.overview.appTcpSessionDelay, params)
const httpRequest = get(api.npm.overview.appHttpResponseDelay, params) const httpRequest = get(api.npm.overview.appHttpResponseDelay, params)
const sslRequest = get(api.npm.overview.appSslConDelay, params) const sslRequest = get(api.npm.overview.appSslConDelay, params)
const tcpLostRequest = get(api.npm.overview.appTcpLostlenPercent, params) const tcpLostRequest = get(api.npm.overview.appTcpLostlenPercent, params)
const packetRetransRequest = get(api.npm.overview.appPacketRetransPercent, params) const packetRetransRequest = get(api.npm.overview.appPacketRetransPercent, params)
Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res => { Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res => {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans'] const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
res.forEach((r, i) => { let msg = ''
if (r.code === 200) {
tableData.forEach(t => { res.forEach((r, i) => {
let score = 0 if (r.code === 200) {
const find = r.data.result.find(d => d.appSubcategory === t.appSubcategory) tableData.forEach(t => {
if (find) { const find = r.data.result.find(d => d.appSubcategory === t.appSubcategory)
score = computeScore(find, i) t[keyPre[i] + 'Score'] = find
})
} else {
this.showError = true
// todo 此处目前返回字段为msg后续字段会修改为message
msg = msg + ',' + r.message
if (msg.indexOf(',') === 0) {
msg = msg.substring(1, msg.length)
} }
t[keyPre[i] + 'Score'] = score if (msg.lastIndexOf(',') === msg.length - 1) {
}) msg = msg.substring(0, msg.length - 1)
} else { }
tableData.forEach(t => { this.errorMsg = msg
t[keyPre[i] + 'Score'] = 0 }
}) })
} tableData.forEach(t => {
const data = {
establishLatencyMs: t.tcpScore ? t.tcpScore.establishLatencyMs : null,
httpResponseLatency: t.httpScore ? t.httpScore.httpResponseLatency : null,
sslConLatency: t.sslScore ? t.sslScore.sslConLatency : null,
tcpLostlenPercent: t.tcpLostScore ? t.tcpLostScore.tcpLostlenPercent : null,
pktRetransPercent: t.packetRetransScore ? t.packetRetransScore.pktRetransPercent : null
}
t.score = computeScore(data)
})
this.tableData = tableData
}).finally(() => {
this.toggleLoading(false)
}) })
tableData.forEach(t => { } else {
t.score = Math.ceil((t.tcpScore + t.httpScore + t.sslScore + t.tcpLostScore + t.packetRetransScore) * 6) this.tableData = []
if (t.score > 6) { this.isNoData = true
t.score = 6
}
})
this.tableData = tableData
}).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) }
} else { } else {
this.tableData = []
this.isNoData = true
this.toggleLoading(false) this.toggleLoading(false)
this.showError = true
this.errorMsg = ''
let msg = res[0].message + ', ' + res[1].message
if (msg.indexOf(',') === 0) {
msg = msg.substring(1, msg.length)
}
if (msg.lastIndexOf(',') === msg.length - 1) {
msg = msg.substring(0, msg.length - 1)
}
this.errorMsg = msg
} }
}).catch(e => { }).catch(e => {
console.error(e) console.error(e)
this.toggleLoading(false) this.toggleLoading(false)
this.showError = true
this.errorMsg = ''
this.errorMsg = e.message
}) })
}, },
getUrlParam (param, defaultValue, isNumber) { getUrlParam (param, defaultValue, isNumber) {

View File

@@ -1,40 +1,41 @@
<template> <template>
<div class="npm-app-event"> <div class="npm-app-event">
<div class="metric-select" > <div class="metric-select">
<el-select v-model="metric" <el-select
class="option__select select-column" v-model="metric"
popper-class="option-popper common-select" class="option__select select-column"
:popper-append-to-body="false" popper-class="option-popper common-select"
key="tabMetric" :popper-append-to-body="false"
@change="changeMetric" key="tabMetric"
size="mini" @change="changeMetric"
width="100"> size="mini"
width="100">
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.label" :key="item.label"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
/> />
</el-select> </el-select>
<span>{{$t('network.metric')}}</span> <span>{{ $t('network.metric') }}</span>
</div> </div>
<el-table <el-table
:id="`tabTable_${index}`" :id="`tabTable_${index}`"
:ref="`dataTable_${index}`" :ref="`dataTable_${index}`"
:data="tableData" :data="tableData"
class="npm-app-event-table" class="npm-app-event-table"
height="100%" height="100%"
empty-text="" empty-text=""
> >
<template v-for="(item, index) in customTableTitles" :key="index"> <template v-for="(item, index) in customTableTitles" :key="index">
<el-table-column class="data-column" :min-width="columnWidth(index)"> <el-table-column class="data-column" :min-width="columnWidth(index)">
<template #header> <template #header>
<span class="data-column__span">{{$t(item.label)}}</span> <span class="data-column__span">{{ $t(item.label) }}</span>
</template> </template>
<template #default="scope" :column="item"> <template #default="scope" :column="item">
<div class="data-app-event-table"> <div class="data-app-event-table">
<template v-if="item.prop === 'domain' ||item.prop === 'appName' ||item.prop === 'serverIp' "> <template v-if="item.prop === 'domain' ||item.prop === 'appName' ||item.prop === 'serverIp' ">
<span class="data-applications">{{$t(scope.row[item.prop])}}</span> <span class="data-applications">{{ $t(scope.row[item.prop]) }}</span>
</template> </template>
<template v-else-if="item.prop === 'eventSeverity'"> <template v-else-if="item.prop === 'eventSeverity'">
<template v-if="scope.row[item.prop]==='critical'"> <template v-if="scope.row[item.prop]==='critical'">
@@ -56,14 +57,14 @@
<div v-for="item in 1" class="red-dot"></div> <div v-for="item in 1" class="red-dot"></div>
<div v-for="item in 4" class="grey-dot"></div> <div v-for="item in 4" class="grey-dot"></div>
</template> </template>
<span class="data-severity" >{{$t(scope.row[item.prop])}}</span> <span class="data-severity">{{ $t(scope.row[item.prop]) }}</span>
</template> </template>
<template v-else-if="item.prop === 'eventType'"> <template v-else-if="item.prop === 'eventType'">
<!-- <span class="data-eventType" v-for="type in scope.row[item.prop]">{{type}}</span>--> <!-- <span class="data-eventType" v-for="type in scope.row[item.prop]">{{type}}</span>-->
<span class="data-eventType" >{{$t(scope.row[item.prop])}}</span> <span class="data-eventType">{{ $t(scope.row[item.prop]) }}</span>
</template> </template>
<template v-else-if="item.prop === 'count'"> <template v-else-if="item.prop === 'count'">
<span class="data-eventCount">{{scope.row[item.prop]}}</span> <span class="data-eventCount">{{ scope.row[item.prop] }}</span>
</template> </template>
<span v-else>-</span> <span v-else>-</span>
</div> </div>
@@ -76,20 +77,23 @@
</div> </div>
</template> </template>
</el-table> </el-table>
<div class="table-error">
<chart-error v-if="showError" :content="errorMsg"></chart-error>
</div>
</div> </div>
</template> </template>
<script> <script>
import { unitTypes, npmCategoryInfoMapping } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http' import { get } from '@/utils/http'
import { getChainRatio, computeScore } from '@/utils/tools'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmAppEventTable', name: 'NpmAppEventTable',
components: { ChartError },
data () { data () {
return { return {
metric: 'appLabel', metric: 'appLabel',
@@ -115,18 +119,32 @@ export default {
dotList: ['grey-dot', 'grey-dot', 'grey-dot', 'grey-dot', 'grey-dot'], dotList: ['grey-dot', 'grey-dot', 'grey-dot', 'grey-dot', 'grey-dot'],
tableData: [], tableData: [],
customTableTitles: [ customTableTitles: [
{ label: 'network.applications', prop: 'serverIp' }, {
{ label: 'network.severity', prop: 'eventSeverity' }, label: 'network.applications',
{ label: 'network.eventType', prop: 'eventType' }, prop: 'serverIp'
{ label: 'network.eventCount', prop: 'count' } },
{
label: 'network.severity',
prop: 'eventSeverity'
},
{
label: 'network.eventType',
prop: 'eventType'
},
{
label: 'network.eventCount',
prop: 'count'
}
], ],
isNoData: false isNoData: false,
showError: false,
errorMsg: ''
} }
}, },
mixins: [chartMixin], mixins: [chartMixin],
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.init() this.init()
} }
} }
@@ -149,13 +167,20 @@ export default {
} }
get(api.npm.events.dimensionEvents, params).then(res => { get(api.npm.events.dimensionEvents, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
if (!res.data.result || res.data.result.length === 0) { if (!res.data.result || res.data.result.length === 0) {
this.isNoData = true this.isNoData = true
} }
this.tableData = res.data.result this.tableData = res.data.result
} else { } else {
this.isNoData = true this.isNoData = false
this.showError = true
this.errorMsg = res.message
} }
}).catch((e) => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -172,9 +197,7 @@ export default {
return '15%' return '15%'
} }
} }
}, },
computed: { computed: {}
}
} }
</script> </script>

View File

@@ -1,6 +1,9 @@
<template> <template>
<div class="npm-event"> <div class="npm-event">
<div class="npm-event-title">{{$t('network.eventByType')}}</div> <div class="npm-event-title">
{{$t('network.eventByType')}}
<chart-error tooltip v-if="showError" :content="errorMsg"></chart-error>
</div>
<div class="npm-event-pie"> <div class="npm-event-pie">
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<div class="npm-event-pies" v-else> <div class="npm-event-pies" v-else>
@@ -35,6 +38,7 @@ import { pieChartOption3 } from '@/views/charts2/charts/options/echartOption'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmEventsByType', name: 'NpmEventsByType',
@@ -42,6 +46,7 @@ export default {
type: String type: String
}, },
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
mixins: [chartMixin], mixins: [chartMixin],
@@ -54,7 +59,9 @@ export default {
return { return {
chartData: [], chartData: [],
timer: null, timer: null,
isNoData: false isNoData: false,
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -104,6 +111,7 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(api.npm.events.recentEvents, params).then(res => { get(api.npm.events.recentEvents, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
const arrData = [] const arrData = []
res.data.result.forEach(t => { res.data.result.forEach(t => {
@@ -121,8 +129,14 @@ export default {
this.init() this.init()
}) })
} else { } else {
this.isNoData = true this.isNoData = false
this.showError = true
this.errorMsg = res.message
} }
}).catch((e) => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -140,6 +154,8 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null
this.chartOption = null
} }
} }
</script> </script>

View File

@@ -5,7 +5,8 @@
<div class="npm-header-body-severity-icon" :class="item.eventSeverity"></div> <div class="npm-header-body-severity-icon" :class="item.eventSeverity"></div>
<div class="npm-header-body-severity-value">{{item.eventSeverity}}</div> <div class="npm-header-body-severity-value">{{item.eventSeverity}}</div>
</div> </div>
<div class="npm-header-body-total">{{item.count}}</div> <chart-error v-if="showError" tooltip :content="errorMsg" />
<div v-else class="npm-header-body-total">{{item.count}}</div>
</div> </div>
</div> </div>
</template> </template>
@@ -15,8 +16,10 @@ import { getSecond } from '@/utils/date-util'
import { get } from '@/utils/http' import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmEventsHeader', name: 'NpmEventsHeader',
components: { ChartError },
mixins: [chartMixin], mixins: [chartMixin],
data () { data () {
return { return {
@@ -47,12 +50,14 @@ export default {
index: 4 index: 4
} }
], ],
type: 'severity' type: 'severity',
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.recentEventsListData() this.recentEventsListData()
} }
} }
@@ -67,6 +72,7 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(api.npm.events.list, params).then(res => { get(api.npm.events.list, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
res.data.result.forEach(t => { res.data.result.forEach(t => {
this.chartData.forEach(d => { this.chartData.forEach(d => {
if (d.eventSeverity === t.eventSeverity) { if (d.eventSeverity === t.eventSeverity) {
@@ -74,7 +80,13 @@ export default {
} }
}) })
}) })
} else {
this.showError = true
this.errorMsg = res.message
} }
}).catch(error => {
this.showError = true
this.errorMsg = error.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })

View File

@@ -1,6 +1,9 @@
<template> <template>
<div class="cn-chart__map-title" v-if="tabIndex == 1">{{$t('npm.clientLocation')}}</div> <div class="cn-chart__map-title" v-if="queryCondition">
<div class="cn-chart__map" :class="{'cn-chart__map-drilldown': tabIndex == 1}"> {{$t('npm.clientLocation')}}
<chart-error v-if="showError" width="300" tooltip :content="errorMsg" />
</div>
<div class="cn-chart__map" :class="{'cn-chart__map-drilldown': queryCondition}">
<div class="map-canvas" id="npmDrillDownMap"></div> <div class="map-canvas" id="npmDrillDownMap"></div>
</div> </div>
</template> </template>
@@ -10,20 +13,24 @@ import { ref, shallowRef } from 'vue'
import * as am4Core from '@amcharts/amcharts4/core' import * as am4Core from '@amcharts/amcharts4/core'
import * as am4Maps from '@amcharts/amcharts4/maps' import * as am4Maps from '@amcharts/amcharts4/maps'
import { computeScore, getGeoData } from '@/utils/tools' import { computeScore, getGeoData } from '@/utils/tools'
import { storageKey, unitTypes, countryNameIdMapping, curTabState } from '@/utils/constants' import { storageKey, unitTypes, curTabState } from '@/utils/constants'
import { valueToRangeValue } from '@/utils/unit-convert' import { valueToRangeValue } from '@/utils/unit-convert'
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import { api, getData } from '@/utils/api' import { api, getData } from '@/utils/api'
import { get } from '@/utils/http' import { get } from '@/utils/http'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmIpMap', name: 'NpmIpMap',
components: { ChartError },
setup () { setup () {
const { query } = useRoute() const { query } = useRoute()
const tabIndex = ref(query.tabIndex || '') const tabIndex = ref(query.tabIndex || '')
const queryCondition = ref(query.queryCondition || '')
return { return {
tabIndex tabIndex,
queryCondition
} }
}, },
data () { data () {
@@ -35,7 +42,9 @@ export default {
countryImageSeries: null, countryImageSeries: null,
// Server | Client // Server | Client
trafficDirection: 'Server', trafficDirection: 'Server',
curTabState: curTabState curTabState: curTabState,
showError: false,
errorMsg: ''
} }
}, },
mixins: [chartMixin], mixins: [chartMixin],
@@ -88,52 +97,64 @@ export default {
// typeVal: this.$store.getters.getBreadcrumbColumnValue // typeVal: this.$store.getters.getBreadcrumbColumnValue
typeVal: this.getUrlParam(this.curTabState.fourthMenu, '') typeVal: this.getUrlParam(this.curTabState.fourthMenu, '')
} }
if (params.type === 'serverIp' || params.type === 'clientIp') params.type = 'ip'
getData(api.npm.overview.map, params).then(res => { getData(api.npm.overview.map, params).then(res => {
const subParams = { this.showError = false
...params, if (res && res.length > 0) {
params: res.map(r => r.country).join(',') const subParams = {
...params,
params: res.map(r => `'${r.country}'`).join(',')
}
// 计算分数
const tcpRequest = get(api.npm.overview.mapTcp, subParams)
const httpRequest = get(api.npm.overview.mapHttp, subParams)
const sslRequest = get(api.npm.overview.mapSsl, subParams)
const tcpLostRequest = get(api.npm.overview.mapPacketLoss, subParams)
const packetRetransRequest = get(api.npm.overview.mapPacketRetrans, subParams)
Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res2 => {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
const mapData = res
res2.forEach((r, i) => {
if (r.code === 200) {
mapData.forEach(t => {
const find = r.data.result.find(d => d.country === t.country)
t[keyPre[i] + 'Score'] = find
})
} else {
this.showError = true
this.errorMsg = r.message
}
})
mapData.forEach(t => {
const data = {
establishLatencyMs: t.tcpScore ? t.tcpScore.establishLatencyMs : null,
httpResponseLatency: t.httpScore ? t.httpScore.httpResponseLatency : null,
sslConLatency: t.sslScore ? t.sslScore.sslConLatency : null,
tcpLostlenPercent: t.tcpLostScore ? t.tcpLostScore.tcpLostlenPercent : null,
pktRetransPercent: t.packetRetransScore ? t.packetRetransScore.pktRetransPercent : null
}
t.score = computeScore(data)
if (t.score === '-') {
t.score = ''
}
})
this.loadMarkerData(imageSeries, mapData)
})
} }
// 计算分数 }).catch(e => {
const tcpRequest = get(api.npm.overview.mapTcp, subParams) this.showError = true
const httpRequest = get(api.npm.overview.mapHttp, subParams) this.errorMsg = e.message
const sslRequest = get(api.npm.overview.mapSsl, subParams)
const tcpLostRequest = get(api.npm.overview.mapPacketLoss, subParams)
const packetRetransRequest = get(api.npm.overview.mapPacketRetrans, subParams)
Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res2 => {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
const mapData = res
res2.forEach((r, i) => {
if (r.code === 200) {
mapData.forEach(t => {
let score = 0
const find = r.data.result.find(d => d.country === t.country)
if (find) {
score = computeScore(find, i)
}
t[keyPre[i] + 'Score'] = score
})
} else {
mapData.forEach(t => {
t[keyPre[i] + 'Score'] = 0
})
}
})
mapData.forEach(t => {
t.score = Math.ceil((t.tcpScore + t.httpScore + t.sslScore + t.tcpLostScore + t.packetRetransScore) * 6)
if (t.score > 6) {
t.score = 6
}
})
this.loadMarkerData(imageSeries, mapData)
})
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} catch (e) { } catch (e) {
console.error(e) console.error(e)
this.showError = true
this.errorMsg = e.message
} }
}, },
loadMarkerData (imageSeries, data) { loadMarkerData (imageSeries, data) {
data = data.filter(d => d.score || d.score === 0)
imageSeries.data = data.map(r => ({ imageSeries.data = data.map(r => ({
score: r.score, score: r.score,
name: r.province || r.country, name: r.province || r.country,

View File

@@ -3,7 +3,10 @@
<chart-no-data v-if="isNoData"></chart-no-data> <chart-no-data v-if="isNoData"></chart-no-data>
<template v-if="chartData.id === 11"> <template v-if="chartData.id === 11">
<div class="npm-line-header"> <div class="npm-line-header">
<div class="npm-line-header-title">{{$t(chartData.i18n) || chartData.name}}</div> <div class="npm-line-header-title">
{{$t(chartData.i18n) || chartData.name}}
<chart-error v-if="showError" tooltip :content="errorMsg"></chart-error>
</div>
<div class="npm-line-header-rights" v-if="chartData.params && chartData.params.showLegend && !isNoData"> <div class="npm-line-header-rights" v-if="chartData.params && chartData.params.showLegend && !isNoData">
<div class="npm-line-header-right" :class="{'active': item.show}" v-for="(item, index) in chartOptionLineData" :key="index" @click="highlightEvent(item)"> <div class="npm-line-header-right" :class="{'active': item.show}" v-for="(item, index) in chartOptionLineData" :key="index" @click="highlightEvent(item)">
<div class="npm-line-header-icon" :class="'icon' + index"></div> <div class="npm-line-header-icon" :class="'icon' + index"></div>
@@ -14,23 +17,38 @@
<div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div> <div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div>
</template> </template>
<template v-else-if="chartData.id === 12"> <template v-else-if="chartData.id === 12">
<div class="npm-line-title">{{$t(chartData.i18n) || chartData.name}}</div> <div class="npm-line-title">
{{$t(chartData.i18n) || chartData.name}}(ms)
<chart-error v-if="showError" tooltip :content="errorMsg"></chart-error>
</div>
<div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div> <div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div>
</template> </template>
<template v-else-if="chartData.id === 13"> <template v-else-if="chartData.id === 13">
<div class="npm-line-title">{{$t(chartData.i18n) || chartData.name}}</div> <div class="npm-line-title">
{{$t(chartData.i18n) || chartData.name}}(ms)
<chart-error v-if="showError" tooltip :content="errorMsg"></chart-error>
</div>
<div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div> <div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div>
</template> </template>
<template v-else-if="chartData.id === 14"> <template v-else-if="chartData.id === 14">
<div class="npm-line-title">{{$t(chartData.i18n) || chartData.name}}</div> <div class="npm-line-title">
{{$t(chartData.i18n) || chartData.name}}(ms)
<chart-error v-if="showError" tooltip :content="errorMsg"></chart-error>
</div>
<div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div> <div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div>
</template> </template>
<template v-else-if="chartData.id === 15"> <template v-else-if="chartData.id === 15">
<div class="npm-line-title">{{$t(chartData.i18n) || chartData.name}}</div> <div class="npm-line-title">
{{$t(chartData.i18n) || chartData.name}}(%)
<chart-error v-if="showError" tooltip :content="errorMsg"></chart-error>
</div>
<div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div> <div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div>
</template> </template>
<template v-else-if="chartData.id === 16"> <template v-else-if="chartData.id === 16">
<div class="npm-line-title">{{$t(chartData.i18n) || chartData.name}}</div> <div class="npm-line-title">
{{$t(chartData.i18n) || chartData.name}}(%)
<chart-error v-if="showError" tooltip :content="errorMsg"></chart-error>
</div>
<div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div> <div v-show="!isNoData" class="chart-drawing" :id="`chart${chartData.name}`"></div>
</template> </template>
</div> </div>
@@ -47,10 +65,13 @@ import { get } from '@/utils/http'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import unitConvert from '@/utils/unit-convert'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmLine', name: 'NpmLine',
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
mixins: [chartMixin], mixins: [chartMixin],
@@ -75,8 +96,10 @@ export default {
timer: null, timer: null,
myChartArray: [], myChartArray: [],
side: this.$store.state.panel.npmLocationSide, side: this.$store.state.panel.npmLocationSide,
country: this.$store.state.panel.npmLocationCountry country: this.$store.state.panel.npmLocationCountry,
// province: '', // province: '',
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -91,24 +114,29 @@ export default {
deep: true, deep: true,
handler (n) { handler (n) {
this.country = n this.country = n
this.init() this.init(n)
} }
}, },
timeFilter: { timeFilter: {
handler (n) { handler () {
this.init() this.init()
} }
} }
}, },
methods: { methods: {
init () { init (n) {
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
side: this.side, side: this.side
country: this.country // country: this.country
// province: this.province // province: this.province
} }
if (n) {
params.country = n
} else {
params.country = ''
}
this.toggleLoading(true) this.toggleLoading(true)
let url let url
if (this.chart.params) { if (this.chart.params) {
@@ -128,23 +156,34 @@ export default {
if (url) { if (url) {
get(url, params).then(res => { get(url, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
if (this.chart.params.index === 0) { if (!this.isNoData) {
res.data.result.forEach((t, i) => { if (this.chart.params.index === 0) {
if (t.type === 'totalBytesRate') { res.data.result.forEach((t, i) => {
this.chartOptionLineData[i].values = t.values if (t.type === 'totalBitsRate') {
} else if (t.type === 'inboundBytesRate') { this.chartOptionLineData[i].values = t.values
this.chartOptionLineData[i].values = t.values } else if (t.type === 'inboundBitsRate') {
} else if (t.type === 'outboundBytesRate') { this.chartOptionLineData[i].values = t.values
this.chartOptionLineData[i].values = t.values } else if (t.type === 'outboundBitsRate') {
} this.chartOptionLineData[i].values = t.values
}) }
const result = this.chartOptionLineData.filter(t => this.chartData.params.color.indexOf(t.color) > -1) })
this.echartsInit(result, this.chartData, this.chartData.params.unitType) const result = this.chartOptionLineData.filter(t => this.chartData.params.color.indexOf(t.color) > -1)
} else { this.echartsInit(result, this.chartData, this.chartData.params.unitType)
this.echartsInit(res.data.result, this.chartData, this.chartData.params.unitType) } else {
this.echartsInit(res.data.result, this.chartData, this.chartData.params.unitType)
}
} }
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
} }
}).catch((e) => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -161,11 +200,16 @@ export default {
this.myChart = echarts.init(dom) this.myChart = echarts.init(dom)
} }
this.chartOption = npmLineChartOption this.chartOption = npmLineChartOption
const seriesTemplate = this.chartOption.series[0]
this.chartOption.color = this.chartData.params.color this.chartOption.color = this.chartData.params.color
this.chartOption.series = data.map((t, i) => { this.chartOption.series = data.map((t, i) => {
return { return {
...seriesTemplate, type: 'line',
symbol: 'circle',
smooth: true,
showSymbol: false,
emphasis: {
focus: 'series'
},
name: t.legend ? t.legend : this.$t(chartData.i18n) || chartData.name, name: t.legend ? t.legend : this.$t(chartData.i18n) || chartData.name,
stack: this.chartData.params.isStack ? 'network.total' : '', stack: this.chartData.params.isStack ? 'network.total' : '',
lineStyle: { lineStyle: {
@@ -177,9 +221,24 @@ export default {
data: t.values.map((v) => [Number(v[0]) * 1000, Number(v[1]), type]) data: t.values.map((v) => [Number(v[0]) * 1000, Number(v[1]), type])
} }
}) })
this.chartOption.yAxis[0].axisLabel.formatter = (value) => {
if (type === 'percent') {
return unitConvert(value, type)[0]
} else {
return unitConvert(value, 'number').join('')
}
}
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.chartOptionLineData.forEach(e => {
if (this.$t(e.legend) === t.seriesName) {
t.borderColor = e.color
}
if (this.$t(chartData.i18n) === t.seriesName) {
t.borderColor = t.color
}
})
}) })
return stackedLineTooltipFormatter(params) return stackedLineTooltipFormatter(params)
} }
@@ -272,6 +331,7 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null
} }
} }
</script> </script>

View File

@@ -1,6 +1,8 @@
<template> <template>
<div class="cn-chart__map"> <div class="cn-chart__map">
<div class="map-canvas" id="npmMap"></div> <div class="map-canvas" id="npmMap"></div>
<chart-error v-if="showError" max-width="900" :content="errorMsg"></chart-error>
<div class="map-filter"> <div class="map-filter">
<el-select <el-select
size="mini" size="mini"
@@ -55,9 +57,11 @@ import { getSecond } from '@/utils/date-util'
import { api, getData } from '@/utils/api' import { api, getData } from '@/utils/api'
import { get } from '@/utils/http' import { get } from '@/utils/http'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { Rectangle3D } from '@amcharts/amcharts4/.internal/core/elements/3d/Rectangle3D' // import { Rectangle3D } from '@amcharts/amcharts4/.internal/core/elements/3d/Rectangle3D'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmMap', name: 'NpmMap',
components: { ChartError },
data () { data () {
return { return {
locationOptions, locationOptions,
@@ -68,7 +72,9 @@ export default {
countryImageSeries: null, countryImageSeries: null,
// Server | Client // Server | Client
trafficDirection: 'Server', trafficDirection: 'Server',
location: '' location: '',
showError: false,
errorMsg: ''
} }
}, },
mixins: [chartMixin], mixins: [chartMixin],
@@ -118,6 +124,7 @@ export default {
getData(api.npm.location.map, params).then(res => { getData(api.npm.location.map, params).then(res => {
if (res.length > 0) { if (res.length > 0) {
// 计算分数 // 计算分数
params.country = params.country ? `'${params.country}'` : ''
const tcpRequest = get(api.npm.location.mapTcp, params) const tcpRequest = get(api.npm.location.mapTcp, params)
const httpRequest = get(api.npm.location.mapHttp, params) const httpRequest = get(api.npm.location.mapHttp, params)
const sslRequest = get(api.npm.location.mapSsl, params) const sslRequest = get(api.npm.location.mapSsl, params)
@@ -126,43 +133,63 @@ export default {
Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res2 => { Promise.all([tcpRequest, httpRequest, sslRequest, tcpLostRequest, packetRetransRequest]).then(res2 => {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans'] const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
const mapData = res const mapData = res
let msg = ''
res2.forEach((r, i) => { res2.forEach((r, i) => {
if (r.code === 200) { if (r.code === 200) {
mapData.forEach(t => { mapData.forEach(t => {
let score = 0
const find = r.data.result.find(d => d.country === t.country) const find = r.data.result.find(d => d.country === t.country)
if (find) { t[keyPre[i] + 'Score'] = find
score = computeScore(find, i)
}
t[keyPre[i] + 'Score'] = score
}) })
} else { } else {
mapData.forEach(t => { this.showError = true
t[keyPre[i] + 'Score'] = 0 // todo 此处目前返回字段为msg后续字段会修改为message
}) msg = msg + ',' + r.message
if (msg.indexOf(',') === 0) {
msg = msg.substring(1, msg.length)
}
if (msg.lastIndexOf(',') === msg.length - 1) {
msg = msg.substring(0, msg.length - 1)
}
this.errorMsg = msg
} }
}) })
mapData.forEach(t => { mapData.forEach(t => {
t.score = Math.ceil((t.tcpScore + t.httpScore + t.sslScore + t.tcpLostScore + t.packetRetransScore) * 6) const data = {
if (t.score > 6) { establishLatencyMs: t.tcpScore ? t.tcpScore.establishLatencyMs : null,
t.score = 6 httpResponseLatency: t.httpScore ? t.httpScore.httpResponseLatency : null,
sslConLatency: t.sslScore ? t.sslScore.sslConLatency : null,
tcpLostlenPercent: t.tcpLostScore ? t.tcpLostScore.tcpLostlenPercent : null,
pktRetransPercent: t.packetRetransScore ? t.packetRetransScore.pktRetransPercent : null
}
t.score = computeScore(data)
if (t.score === '-') {
t.score = ''
} }
}) })
this.loadMarkerData(imageSeries, mapData) this.loadMarkerData(imageSeries, mapData)
}).catch((e) => {
this.showError = true
this.errorMsg = e.message
}) })
} else { } else {
imageSeries.data = [{}] imageSeries.data = [{}]
} }
}).catch((e) => {
this.showError = true
this.errorMsg = e.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
} catch (e) { } catch (e) {
this.showError = true
// todo 此处错误信息有待考证,后续可能会改动
this.errorMsg = e
console.error(e) console.error(e)
} }
}, },
loadMarkerData (imageSeries, data) { loadMarkerData (imageSeries, data) {
imageSeries.data = data.map(r => ({ imageSeries.data = data.map(r => ({
score: r.score, score: r.score || '&ndash;',
name: r.province || r.country, name: r.province || r.country,
throughput: valueToRangeValue(r.throughBitsRate, unitTypes.bps).join(' '), throughput: valueToRangeValue(r.throughBitsRate, unitTypes.bps).join(' '),
id: r.serverId, id: r.serverId,
@@ -304,7 +331,7 @@ export default {
} }
}, },
timeFilter: { timeFilter: {
handler (n) { handler () {
if (this.location) { if (this.location) {
this.loadAm4ChartMap(this.countrySeries, this.countryImageSeries) this.loadAm4ChartMap(this.countrySeries, this.countryImageSeries)
} else { } else {

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="npm-network-quantity"> <div class="npm-network-quantity">
<single-value <single-value
v-if="npmNetworkData.length>0"
:npm-network-name="npmNetworkName" :npm-network-name="npmNetworkName"
:npm-network-data="npmNetworkData" :npm-network-data="npmNetworkData"
></single-value> ></single-value>
@@ -14,7 +15,7 @@ import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api' import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import _ from 'lodash' import _ from 'lodash'
import { computeScore, getChainRatio } from '@/utils/tools' import { getChainRatio } from '@/utils/tools'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
export default { export default {
@@ -25,9 +26,15 @@ export default {
const { query } = useRoute() const { query } = useRoute()
const queryCondition = ref(query.queryCondition || '') const queryCondition = ref(query.queryCondition || '')
const dimensionType = ref(query.dimensionType || '') const dimensionType = ref(query.dimensionType || '')
const tabIndex = ref(query.tabIndex || 0)
const tabOperationType = ref(query.tabOperationType)
const networkOverviewBeforeTab = ref(query.networkOverviewBeforeTab || '')
return { return {
queryCondition, queryCondition,
dimensionType dimensionType,
tabIndex,
tabOperationType,
networkOverviewBeforeTab
} }
}, },
data () { data () {
@@ -43,7 +50,9 @@ export default {
npmNetworkLastCycleData: [], npmNetworkLastCycleData: [],
npmNetworkData: [], npmNetworkData: [],
side: '', side: '',
chartData: {} chartData: {},
timer1: null,
timer2: null
} }
}, },
watch: { watch: {
@@ -57,28 +66,36 @@ export default {
}, },
methods: { methods: {
npmNetworkCycleQuery () { npmNetworkCycleQuery () {
// const conditionStr = this.$route.query.queryCondition ? this.$route.query.queryCondition : ''
let condition = '' let condition = ''
let url = ''
if (this.queryCondition.indexOf(' OR ') > -1) { if (this.queryCondition.indexOf(' OR ') > -1) {
condition = this.queryCondition.split(/["|'](.*?)["|']/) condition = this.queryCondition.split(/["|'](.*?)["|']/)
} else { } else {
condition = this.queryCondition condition = this.queryCondition
} }
// const type = this.$store.getters.getDimensionType const type = this.dimensionType || this.networkOverviewBeforeTab
// const type = this.$route.query.dimensionType ? this.$route.query.dimensionType : ''
const type = this.dimensionType
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
cycle: 0 cycle: 0
} }
if (this.chartData.id === 23) { if (this.tabIndex == 0) {
this.side = 'client' this.side = 'client'
} else if (this.chartData.id === 26) { } else if (this.tabIndex == 1) {
this.side = 'server' this.side = 'server'
} }
if (condition && (typeof condition !== 'object') && type) { if (condition && (typeof condition !== 'object') && type) {
params.q = condition if (type === 'clientIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='client'`
} else if (type === 'serverIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='server'`
} else if (type === 'clientCity') {
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'`
} else if (type === 'serverCity') {
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'`
} else {
params.q = condition
}
params.type = type params.type = type
} else if (condition.length > 1 && type && type === 'ip') { } else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}' and side='${this.side}'` params.q = `${type}='${condition[1]}' and side='${this.side}'`
@@ -95,46 +112,19 @@ export default {
params.type = type params.type = type
} }
} }
if (type && condition) { if (parseFloat(this.tabOperationType) === 3) {
url = api.npm.overview.allNetworkAnalysis
} else {
url = api.npm.overview.networkAnalysis
}
if ((type && condition) || type) {
params.type = params.type || type
this.toggleLoading(true) this.toggleLoading(true)
get(api.npm.overview.networkAnalysis, params).then(res => { get(url, params).then(res => {
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
const scoreInfo = {}
let index = 0
if (res.code === 200) { if (res.code === 200) {
if (res.data.result.establishLatencyMsAvg || res.data.result.establishLatencyMsAvg === 0) {
res.data.result.establishLatencyMs = res.data.result.establishLatencyMsAvg
index = 0
scoreInfo[keyPre[index] + 'Score'] = computeScore(res.data.result, index)
}
if (res.data.result.httpResponseLatencyAvg || res.data.result.httpResponseLatencyAvg === 0) {
res.data.result.httpResponseLatency = res.data.result.httpResponseLatencyAvg
index = 1
scoreInfo[keyPre[index] + 'Score'] = computeScore(res.data.result, index)
}
if (res.data.result.tcpLostlenPercentAvg || res.data.result.tcpLostlenPercentAvg === 0) {
res.data.result.tcpLostlenPercent = res.data.result.tcpLostlenPercentAvg
index = 3
scoreInfo[keyPre[index] + 'Score'] = computeScore(res.data.result, index)
}
if (res.data.result.pktRetransPercentAvg || res.data.result.pktRetransPercentAvg === 0) {
res.data.result.pktRetransPercent = res.data.result.pktRetransPercentAvg
index = 4
scoreInfo[keyPre[index] + 'Score'] = computeScore(res.data.result, index)
}
if (res.data.result.sslConLatencyAvg || res.data.result.sslConLatencyAvg === 0) {
res.data.result.sslConLatency = res.data.result.sslConLatencyAvg
index = 2
scoreInfo[keyPre[index] + 'Score'] = computeScore(res.data.result, index)
}
this.npmNetworkCycleData = res.data.result this.npmNetworkCycleData = res.data.result
this.npmNetworkLastCycleQuery()
} }
scoreInfo.score = Math.ceil((scoreInfo.tcpScore + scoreInfo.httpScore + scoreInfo.sslScore + scoreInfo.tcpLostScore + scoreInfo.packetRetransScore) * 6) this.npmNetworkLastCycleQuery(url, params)
if (scoreInfo.score > 6) {
scoreInfo.score = 6
}
this.$store.commit('setNpmThirdLevelMenuScore', scoreInfo.score)
}).catch(e => { }).catch(e => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -147,96 +137,62 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => { Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
this.npmNetworkCycleData = [] this.npmNetworkCycleData = []
const keyPre = ['tcp', 'http', 'ssl', 'tcpLost', 'packetRetrans']
const scoreInfo = {}
let index = 0
res.forEach(t => { res.forEach(t => {
if (t.code === 200) { if (t.code === 200) {
if (t.data.result.establishLatencyMsAvg || t.data.result.establishLatencyMsAvg === 0) {
t.data.result.establishLatencyMs = t.data.result.establishLatencyMsAvg
index = 0
}
if (t.data.result.httpResponseLatencyAvg || t.data.result.httpResponseLatencyAvg === 0) {
t.data.result.httpResponseLatency = t.data.result.httpResponseLatencyAvg
index = 1
}
if (t.data.result.tcpLostlenPercentAvg || t.data.result.tcpLostlenPercentAvg === 0) {
t.data.result.tcpLostlenPercent = t.data.result.tcpLostlenPercentAvg
index = 3
}
if (t.data.result.pktRetransPercentAvg || t.data.result.pktRetransPercentAvg === 0) {
t.data.result.pktRetransPercent = t.data.result.pktRetransPercentAvg
index = 4
}
if (t.data.result.sslConLatencyAvg || t.data.result.sslConLatencyAvg === 0) {
t.data.result.sslConLatency = t.data.result.sslConLatencyAvg
index = 2
}
scoreInfo[keyPre[index] + 'Score'] = computeScore(t.data.result, index)
this.npmNetworkCycleData.push(t.data.result) this.npmNetworkCycleData.push(t.data.result)
} else {
this.npmNetworkCycleData.push(t)
} }
}) })
scoreInfo.score = Math.ceil((scoreInfo.tcpScore + scoreInfo.httpScore + scoreInfo.sslScore + scoreInfo.tcpLostScore + scoreInfo.packetRetransScore) * 6)
if (scoreInfo.score > 6) {
scoreInfo.score = 6
}
this.$store.commit('setNpmThirdLevelMenuScore', scoreInfo.score)
this.npmNetworkLastCycleQuery() this.npmNetworkLastCycleQuery()
}).catch(e => { }).catch(e => {
this.toggleLoading(false) this.toggleLoading(false)
// 此时e为数组
if (e instanceof Array) {
this.npmNetworkCycleData = []
e.forEach(t => {
this.npmNetworkCycleData.push(t)
})
this.npmNetworkLastCycleQuery()
}
}) })
} }
}, },
npmNetworkLastCycleQuery () { npmNetworkLastCycleQuery (url, param) {
let condition = ''
if (this.queryCondition.indexOf(' OR ') > -1) {
condition = this.queryCondition.split(/["|'](.*?)["|']/)
} else {
condition = this.queryCondition
}
// const type = this.$store.getters.getDimensionType
const type = this.dimensionType
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime),
cycle: 1 cycle: 1
} }
if (this.chartData.id === 23) { if (param && param.type && param.q) {
this.side = 'client' params.type = param.type
} else if (this.chartData.id === 26) { params.q = param.q
this.side = 'server'
} }
if (condition && (typeof condition !== 'object') && type) { if ((params.type && params.q) || (param && param.type)) {
params.q = condition params.type = params.type || param.type
params.type = type
} else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}' and side='${this.side}'`
params.type = type
} else if (condition.length > 1 && type && type !== 'ip') {
if (type === 'country' || type === 'asn' || type === 'province' || type === 'city' || type === 'isp') {
params.q = `${type}='${condition[1]}'`
params.type = type
} else if (type === 'idcRenter') {
params.q = `idc_renter='${condition[1]}'`
params.type = type
} else {
params.q = `${condition[0]}'${condition[1]}'`
params.type = type
}
}
if (type && condition) {
this.toggleLoading(true) this.toggleLoading(true)
get(api.npm.overview.networkAnalysis, params).then(res => { get(url, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.npmNetworkLastCycleData = res.data.result this.npmNetworkLastCycleData = res.data.result
let timer = null } else {
if (timer) { this.npmNetworkLastCycleData = [res]
clearTimeout(timer)
}
timer = setTimeout(() => {
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 0)
}, 300)
} }
let timer = null
if (timer) {
clearTimeout(timer)
}
this.timer1 = setTimeout(() => {
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 0)
}, 300)
}).catch((e) => {
let timer = null
if (timer) {
clearTimeout(timer)
}
this.npmNetworkLastCycleData = [e]
this.timer2 = setTimeout(() => {
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 0)
}, 300)
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -251,9 +207,19 @@ export default {
res.forEach((t, i) => { res.forEach((t, i) => {
if (t.code === 200) { if (t.code === 200) {
this.npmNetworkLastCycleData.push(t.data.result) this.npmNetworkLastCycleData.push(t.data.result)
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 1) } else {
this.npmNetworkLastCycleData.push(t)
} }
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 1)
}) })
}).catch((e) => {
// todo 此处的e可能为数组
if (e instanceof Array) {
e.forEach((t, i) => {
this.npmNetworkLastCycleData.push(t)
this.npmNetworkQuantity(this.npmNetworkCycleData, this.npmNetworkLastCycleData, 1)
})
}
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
@@ -312,6 +278,10 @@ export default {
this.chartData = _.cloneDeep(this.chart) this.chartData = _.cloneDeep(this.chart)
} }
this.npmNetworkCycleQuery() this.npmNetworkCycleQuery()
},
beforeUnmount () {
clearTimeout(this.timer1)
clearTimeout(this.timer2)
} }
} }
</script> </script>

View File

@@ -1,6 +1,9 @@
<template> <template>
<div class="npm-recent"> <div class="npm-recent">
<div class="npm-recent-title">{{ $t('network.recentEvents') }}</div> <div class="npm-recent-title">
{{ $t('network.recentEvents') }}
<chart-error v-if="showError" tooltip :content="errorMsg" />
</div>
<el-table <el-table
:id="`tabTable_${index}`" :id="`tabTable_${index}`"
:ref="`dataTable_${index}`" :ref="`dataTable_${index}`"
@@ -51,9 +54,11 @@ import { getSecond, dateFormatByAppearance } from '@/utils/date-util'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmRecentEvents', name: 'NpmRecentEvents',
components: { ChartError },
mixins: [chartMixin], mixins: [chartMixin],
setup () { setup () {
const { query } = useRoute() const { query } = useRoute()
@@ -71,7 +76,9 @@ export default {
{ label: 'network.severity', prop: 'eventSeverity' }, { label: 'network.severity', prop: 'eventSeverity' },
{ label: 'network.entity', prop: 'eventKey' }, { label: 'network.entity', prop: 'eventKey' },
{ label: 'detections.eventType', prop: 'eventType' } { label: 'detections.eventType', prop: 'eventType' }
] ],
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -83,11 +90,7 @@ export default {
}, },
methods: { methods: {
recentEventsListData () { recentEventsListData () {
// const condition = this.$store.getters.getQueryCondition.split(/["|'](.*?)["|']/)
// const conditionStr = this.$route.query.queryCondition ? this.$route.query.queryCondition : ''
const condition = this.queryCondition.split(/["|'](.*?)["|']/) const condition = this.queryCondition.split(/["|'](.*?)["|']/)
// const type = this.$store.getters.getDimensionType
// const type = this.$route.query.dimensionType ? this.$route.query.dimensionType : ''
let url = '' let url = ''
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
@@ -97,6 +100,7 @@ export default {
if (condition.length > 1 && this.dimensionType) { if (condition.length > 1 && this.dimensionType) {
params.param = condition[1] params.param = condition[1]
params.type = this.dimensionType params.type = this.dimensionType
if (params.type === 'serverIp' || params.type === 'clientIp') params.type = 'ip'
params.limit = 10 params.limit = 10
url = api.npm.events.recentEventsD url = api.npm.events.recentEventsD
this.customTableTitles = [ this.customTableTitles = [
@@ -110,6 +114,7 @@ export default {
this.toggleLoading(true) this.toggleLoading(true)
get(url, params).then(res => { get(url, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0 this.isNoData = res.data.result.length === 0
res.data.result.forEach(e => { res.data.result.forEach(e => {
if (e.startTime) { if (e.startTime) {
@@ -118,8 +123,13 @@ export default {
}) })
this.tableData = res.data.result this.tableData = res.data.result
} else { } else {
this.isNoData = true // this.isNoData = true
this.showError = true
this.errorMsg = res.message
} }
}).catch(error => {
this.showError = true
this.errorMsg = error.message
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })

View File

@@ -1,7 +1,8 @@
<template> <template>
<div class="npm-tabs"> <div class="npm-tabs">
<div class="npm-tabs__active-bar"></div> <div class="npm-tabs__active-bar"></div>
<el-tabs v-model="currentTab" ref="elTabs" type="border-card"> <el-tabs v-model="currentTab" ref="elTabs" type="border-card" v-show="isCurTabReady"
@tab-click="handleClick">
<el-tab-pane v-for="(tab,index) in tabs" :key="tab.i18n" :name="index" :disabled="tab.disable" > <el-tab-pane v-for="(tab,index) in tabs" :key="tab.i18n" :name="index" :disabled="tab.disable" >
<template #label> <template #label>
<div class="npm-tab__label"> <div class="npm-tab__label">
@@ -27,7 +28,9 @@ export default {
name: 'NpmTabs', name: 'NpmTabs',
data () { data () {
return { return {
leftOffset: 27 leftOffset: 27,
currentTab: ref(0),
isCurTabReady:false
} }
}, },
mixins: [chartMixin], mixins: [chartMixin],
@@ -39,11 +42,7 @@ export default {
item.disable = false item.disable = false
}) })
} }
const { query } = useRoute()
const tabIndexParam = query.tabIndex
const currentTab = ref(tabIndexParam ? parseInt(tabIndexParam) : 0)
return { return {
currentTab,
tabs tabs
} }
}, },
@@ -64,6 +63,13 @@ export default {
} }
}, },
methods: { methods: {
handleClick(tab){
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
tabIndex: tab.index
})
overwriteUrl(newUrl)
},
handleActiveBar (index) { handleActiveBar (index) {
const tabDom = document.getElementById('tab-' + index) const tabDom = document.getElementById('tab-' + index)
if (tabDom) { if (tabDom) {
@@ -73,9 +79,17 @@ export default {
const activeBar = document.querySelector('.npm-tabs .npm-tabs__active-bar') const activeBar = document.querySelector('.npm-tabs .npm-tabs__active-bar')
activeBar.style.cssText += `width: ${clientWidth + 2}px; left: ${offsetLeft + this.leftOffset + clientLeft - 1}px;` activeBar.style.cssText += `width: ${clientWidth + 2}px; left: ${offsetLeft + this.leftOffset + clientLeft - 1}px;`
} }
},
disableTab(n){
this.tabs[n].disable = true
const tabEle = document.getElementById('tab-'+n)
tabEle.style.cssText = 'cursor: not-allowed;'
} }
}, },
mounted () { mounted () {
const { query } = useRoute()
const tabIndexParam = query.tabIndex
let curTabIndexInUrl = tabIndexParam ? parseInt(tabIndexParam) : null
if (this.chart.panelId === drillDownPanelTypeMapping.npmOverviewIp) { if (this.chart.panelId === drillDownPanelTypeMapping.npmOverviewIp) {
const self = this const self = this
// 当client或server的session数为0时对应tab置灰不可选默认高亮另一个不为0的tab // 当client或server的session数为0时对应tab置灰不可选默认高亮另一个不为0的tab
@@ -93,34 +107,53 @@ export default {
self.serverSessions = self.sessionData.serverSessions / (self.sessionData.clientSessions * 1 + self.sessionData.serverSessions * 1) self.serverSessions = self.sessionData.serverSessions / (self.sessionData.clientSessions * 1 + self.sessionData.serverSessions * 1)
} }
}).finally(() => { }).finally(() => {
if (self.clientSessions === 0) { let thirdMenu = this.$route.query['thirdMenu']
self.currentTab = 1 self.tabs[0].disable = false
self.tabs[0].disable = true self.tabs[1].disable = false
self.tabs[1].disable = false let curTabIndex = 0
const tabEle = document.getElementById('tab-0') if(thirdMenu === 'network.clientIps'){
tabEle.style.cssText = 'cursor: not-allowed;' curTabIndex = 0
setTimeout(() => { if (self.serverSessions === 0) {
self.handleActiveBar(1) self.disableTab(1)
}, 400) }else if(curTabIndexInUrl !== null){
curTabIndex = curTabIndexInUrl
}
}else if(thirdMenu === 'network.serverIps'){
curTabIndex = 1
if (self.clientSessions === 0) {
self.disableTab(0)
}else if(curTabIndexInUrl !== null){
curTabIndex = curTabIndexInUrl
}
}else if (self.clientSessions === 0) {
curTabIndex = 1
self.disableTab(0)
} else if (self.serverSessions === 0) { } else if (self.serverSessions === 0) {
self.currentTab = 0 curTabIndex = 0
self.tabs[0].disable = false self.disableTab(1)
self.tabs[1].disable = true } else if(curTabIndexInUrl !== null){
setTimeout(() => { curTabIndex = curTabIndexInUrl
const tabEle = document.getElementById('tab-1')
tabEle.style.cssText = 'cursor: not-allowed;'
self.handleActiveBar(0)
}, 400)
} else {
self.currentTab = 0
self.tabs[0].disable = false
self.tabs[1].disable = false
setTimeout(() => {
self.handleActiveBar(0)
}, 400)
} }
this.$nextTick(() => {
self.currentTab = curTabIndex
self.isCurTabReady = true
//URL中tabIndex的设置client初始化时查询条件需要side条件
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
tabIndex: self.currentTab
})
overwriteUrl(newUrl)
setTimeout(() => {
self.handleActiveBar(self.currentTab)
}, 400)
setTimeout(() => {
this.$emit('tabChange', self.currentTab)
}, 400)
})
}) })
} else { } else {
this.currentTab = curTabIndexInUrl ? curTabIndexInUrl : 0
this.isCurTabReady = true
setTimeout(() => { setTimeout(() => {
this.handleActiveBar(this.currentTab) this.handleActiveBar(this.currentTab)
}, 400) }, 400)

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="npm-traffic-line"> <div class="npm-traffic-line">
<div class="npm-traffic-line-header"> <div class="npm-traffic-line-header">
<div class="npm-traffic-line-title">{{$t('overall.traffic')}}</div> <div class="npm-traffic-line-title"></div>
<div class="line-select-metric"> <div class="line-select-metric">
<span>{{$t('network.metric')}}:</span> <span>{{$t('network.metric')}}:</span>
<div class="line-select__operation"> <div class="line-select__operation">
@@ -19,8 +19,9 @@
</div> </div>
</div> </div>
<div class="npm-traffic-line-body"> <div class="npm-traffic-line-body">
<chart-no-data v-if="isNoData"></chart-no-data> <chart-error v-if="showError" :content="errorMsg" />
<div v-show="!isNoData" class="chart-drawing" id="chart"></div> <chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div v-show="!isNoData && !showError" class="chart-drawing" id="chart"></div>
</div> </div>
</div> </div>
</template> </template>
@@ -40,10 +41,12 @@ import _ from 'lodash'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools' import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'NpmTrafficLine', name: 'NpmTrafficLine',
mixins: [chartMixin], mixins: [chartMixin],
components: { components: {
ChartError,
ChartNoData ChartNoData
}, },
setup () { setup () {
@@ -51,10 +54,12 @@ export default {
const metricFilter = ref(query.lineMetric || 'Bits/s') const metricFilter = ref(query.lineMetric || 'Bits/s')
const queryCondition = ref(query.queryCondition || '') const queryCondition = ref(query.queryCondition || '')
const dimensionType = ref(query.dimensionType || '') const dimensionType = ref(query.dimensionType || '')
const tabIndex = ref(query.tabIndex || 0)
return { return {
metricFilter, metricFilter,
queryCondition, queryCondition,
dimensionType, dimensionType,
tabIndex,
myChart: shallowRef(null) myChart: shallowRef(null)
} }
}, },
@@ -64,19 +69,19 @@ export default {
unitTypes, unitTypes,
side: '', side: '',
mpackets: [ mpackets: [
{ name: 'network.total', show: true, positioning: 0, data: [], unitType: '' }, { name: 'network.total', show: true, positioning: 0, data: [], unitType: 'number' },
{ name: 'network.inbound', show: true, positioning: 1, data: [], unitType: '' }, { name: 'network.inbound', show: true, positioning: 1, data: [], unitType: 'number' },
{ name: 'network.outbound', show: true, positioning: 2, data: [], unitType: '' }, { name: 'network.outbound', show: true, positioning: 2, data: [], unitType: 'number' },
{ name: 'network.internal', show: true, positioning: 3, data: [], unitType: '' }, { name: 'network.internal', show: true, positioning: 3, data: [], unitType: 'number' },
{ name: 'network.through', show: true, positioning: 4, data: [], unitType: '' }, { name: 'network.through', show: true, positioning: 4, data: [], unitType: 'number' },
{ name: 'network.other', show: true, positioning: 5, data: [], unitType: '' } { name: 'network.other', show: true, positioning: 5, data: [], unitType: 'number' }
], ],
npmQuantity: [ npmQuantity: [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: '' }, { name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: '' }, { name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: '' }, { name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: '' }, { name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: '' } { name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
], ],
chartData: {}, chartData: {},
metricOptions: [ metricOptions: [
@@ -112,7 +117,9 @@ export default {
value: 'pktRetransPercent', value: 'pktRetransPercent',
label: this.$t('overall.packetRetrans') label: this.$t('overall.packetRetrans')
} }
] ],
showError: false,
errorMsg: ''
} }
}, },
watch: { watch: {
@@ -142,19 +149,30 @@ export default {
} else { } else {
condition = this.queryCondition condition = this.queryCondition
} }
// const type = this.$store.getters.getDimensionType if (this.tabIndex == 0) {
if (this.chartData.id === 24) {
this.side = 'client' this.side = 'client'
} else if (this.chartData.id === 29) { } else if (this.tabIndex == 1) {
this.side = 'server' this.side = 'server'
} }
const params = { const params = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime), endTime: getSecond(this.timeFilter.endTime)
type: type }
if (type) {
params.type = type
} }
if (condition && (typeof condition !== 'object') && type) { if (condition && (typeof condition !== 'object') && type) {
params.q = condition if (type === 'clientIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='client'`
} else if (type === 'serverIp') {
params.q = `ip='${condition.split(/'(.*?)'/)[1]}' and side='server'`
} else if (type === 'clientCity') {
params.q = `client_city='${condition.split(/'(.*?)'/)[1]}'`
} else if (type === 'serverCity') {
params.q = `server_city='${condition.split(/'(.*?)'/)[1]}'`
} else {
params.q = condition
}
} else if (condition.length > 1 && type && type === 'ip') { } else if (condition.length > 1 && type && type === 'ip') {
params.q = `${type}='${condition[1]}' and side='${this.side}'` params.q = `${type}='${condition[1]}' and side='${this.side}'`
} else if (condition.length > 1 && type && type !== 'ip') { } else if (condition.length > 1 && type && type !== 'ip') {
@@ -167,129 +185,379 @@ export default {
} }
} }
this.toggleLoading(true) this.toggleLoading(true)
get(api.npm.overview.trafficGraph, params).then((res) => { if (params.type && params.q) {
if (res.code === 200) { get(api.npm.overview.trafficGraph, params).then((res) => {
this.isNoData = res.data.result.length === 0 if (res.code === 200) {
res.data.result.forEach((t, i) => { this.showError = false
if (t.type === 'bytes' && val === 'Bits/s') { this.isNoData = res.data.result.length === 0
const mpackets = _.cloneDeep(this.mpackets) if (this.isNoData) {
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : [] this.mpackets = [
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : [] { name: 'network.total', show: true, positioning: 0, data: [], unitType: 'number' },
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : [] { name: 'network.inbound', show: true, positioning: 1, data: [], unitType: 'number' },
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : [] { name: 'network.outbound', show: true, positioning: 2, data: [], unitType: 'number' },
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : [] { name: 'network.internal', show: true, positioning: 3, data: [], unitType: 'number' },
mpackets[5].data = t.other.values ? t.other.values : [] { name: 'network.through', show: true, positioning: 4, data: [], unitType: 'number' },
mpackets.forEach((e, i) => { { name: 'network.other', show: true, positioning: 5, data: [], unitType: 'number' }
e.show = true ]
}) this.npmQuantity = [
this.mpackets = mpackets { name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
this.echartsInit(this.mpackets) { name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
} else if (t.type === 'packets' && val === 'Packets/s') { { name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
const mpackets = _.cloneDeep(this.mpackets) { name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : [] { name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : [] ]
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e, i) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[0].data = t.sessionsRate.values ? t.sessionsRate.values : []
npmQuantity.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity)
} else if (t.type === 'establishLatencyMs' && val === 'establishLatencyMs') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[0].data = t.establishLatencyMsAvg.values ? t.establishLatencyMsAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 0) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity)
} else if (t.type === 'httpResponseLatency' && val === 'httpResponseLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[1].data = t.httpResponseLatencyAvg.values ? t.httpResponseLatencyAvg.values : []
npmQuantity.forEach((e, i) => {
console.log(e)
if (i !== 1) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity)
} else if (t.type === 'sslConLatency' && val === 'sslConLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[2].data = t.sslConLatencyAvg.values ? t.sslConLatencyAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 2) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity)
} else if (t.type === 'tcpLostlenPercent' && val === 'tcpLostlenPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[3].data = t.tcpLostlenPercentAvg.values ? t.tcpLostlenPercentAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 3) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity)
} else if (t.type === 'pktRetransPercent' && val === 'pktRetransPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[4].data = t.pktRetransPercentAvg.values ? t.pktRetransPercentAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 4) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity)
} }
res.data.result.forEach((t, i) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e, i) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : []
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e, i) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalSessionsRate.values ? t.totalSessionsRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'establishLatencyMs' && val === 'establishLatencyMs') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[0].data = t.establishLatencyMsAvg.values ? t.establishLatencyMsAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 0) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'httpResponseLatency' && val === 'httpResponseLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[1].data = t.httpResponseLatencyAvg.values ? t.httpResponseLatencyAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 1) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'sslConLatency' && val === 'sslConLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[2].data = t.sslConLatencyAvg.values ? t.sslConLatencyAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 2) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'tcpLostlenPercent' && val === 'tcpLostlenPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[3].data = t.tcpLostlenPercentAvg.values ? t.tcpLostlenPercentAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 3) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
} else if (t.type === 'pktRetransPercent' && val === 'pktRetransPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[4].data = t.pktRetransPercentAvg.values ? t.pktRetransPercentAvg.values : []
npmQuantity.forEach((e, i) => {
if (i !== 4) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else {
if (val === 'Bits/s' || val === 'Packets/s' || val === 'Sessions/s') {
this.toggleLoading(true)
get(api.npm.overview.totalTrafficAnalysis, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.mpackets = [
{ name: 'network.total', show: true, positioning: 0, data: [], unitType: 'number' },
{ name: 'network.inbound', show: true, positioning: 1, data: [], unitType: 'number' },
{ name: 'network.outbound', show: true, positioning: 2, data: [], unitType: 'number' },
{ name: 'network.internal', show: true, positioning: 3, data: [], unitType: 'number' },
{ name: 'network.through', show: true, positioning: 4, data: [], unitType: 'number' },
{ name: 'network.other', show: true, positioning: 5, data: [], unitType: 'number' }
]
}
res.data.result.forEach((t, i) => {
if (t.type === 'bytes' && val === 'Bits/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalBitsRate.values ? t.totalBitsRate.values : []
mpackets[1].data = t.inboundBitsRate.values ? t.inboundBitsRate.values : []
mpackets[2].data = t.outboundBitsRate.values ? t.outboundBitsRate.values : []
mpackets[3].data = t.internalBitsRate.values ? t.internalBitsRate.values : []
mpackets[4].data = t.throughBitsRate.values ? t.throughBitsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e, i) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'packets' && val === 'Packets/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalPacketsRate.values ? t.totalPacketsRate.values : []
mpackets[1].data = t.inboundPacketsRate.values ? t.inboundPacketsRate.values : []
mpackets[2].data = t.outboundPacketsRate.values ? t.outboundPacketsRate.values : []
mpackets[3].data = t.internalPacketsRate.values ? t.internalPacketsRate.values : []
mpackets[4].data = t.throughPacketsRate.values ? t.throughPacketsRate.values : []
mpackets[5].data = t.other.values ? t.other.values : []
mpackets.forEach((e, i) => {
e.show = true
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
} else if (t.type === 'sessions' && val === 'Sessions/s') {
const mpackets = _.cloneDeep(this.mpackets)
mpackets[0].data = t.totalSessionsRate.values ? t.totalSessionsRate.values : []
mpackets.forEach((e, i) => {
if (i !== 0) {
e.show = false
}
})
this.mpackets = mpackets
this.echartsInit(this.mpackets)
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'establishLatencyMs' || val === 'tcpLostlenPercent' || val === 'pktRetransPercent') {
this.toggleLoading(true)
get(api.npm.overview.totalNetworkAnalysis, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach((t, i) => {
if (t.type === 'establishLatencyMs' && val === 'establishLatencyMs') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[0].data = t.establishLatencyMs.values ? t.establishLatencyMs.values : []
npmQuantity.forEach((e, i) => {
if (i !== 0) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
} else if (t.type === 'tcpLostlenPercent' && val === 'tcpLostlenPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[3].data = t.tcpLostlenPercent.values ? t.tcpLostlenPercent.values : []
npmQuantity.forEach((e, i) => {
if (i !== 3) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
} else if (t.type === 'pktRetransPercent' && val === 'pktRetransPercent') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[4].data = t.pktRetransPercent.values ? t.pktRetransPercent.values : []
npmQuantity.forEach((e, i) => {
if (i !== 4) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(%)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'httpResponseLatency') {
this.toggleLoading(true)
get(api.npm.overview.totalHttpResponseDelay, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach(t => {
if (t.type === 'httpResponseLatency' && val === 'httpResponseLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[1].data = t.httpResponseLatency.values ? t.httpResponseLatency.values : []
npmQuantity.forEach((e, i) => {
if (i !== 1) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
})
} else if (val === 'sslConLatency') {
this.toggleLoading(true)
get(api.npm.overview.totalSslConDelay, params).then(res => {
if (res.code === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (this.isNoData) {
this.npmQuantity = [
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent }
]
}
res.data.result.forEach(t => {
if (t.type === 'sslConLatency' && val === 'sslConLatency') {
const npmQuantity = _.cloneDeep(this.npmQuantity)
npmQuantity[2].data = t.sslConLatency.values ? t.sslConLatency.values : []
npmQuantity.forEach((e, i) => {
if (i !== 2) {
e.show = false
} else {
e.show = true
}
})
this.npmQuantity = npmQuantity
this.echartsInit(this.npmQuantity, '(ms)')
}
})
} else {
this.isNoData = false
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
this.isNoData = false
this.showError = true
this.errorMsg = e.message
}).finally(() => {
this.toggleLoading(false)
}) })
} else {
this.isNoData = true
} }
}).catch(e => { }
console.error(e)
this.isNoData = true
}).finally(() => {
this.toggleLoading(false)
})
}, },
echartsInit (echartsData) { echartsInit (echartsData, legendUnit) {
echartsData = echartsData.filter(t => t.show === true) echartsData = echartsData.filter(t => t.show === true)
const dom = document.getElementById('chart') const dom = document.getElementById('chart')
!this.myChart && (this.myChart = echarts.init(dom)) !this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = trafficLineChartOption this.chartOption = trafficLineChartOption
const chartOption = this.chartOption.series[0] const chartOption = this.chartOption.series[0]
this.chartOption.series = echartsData.map((t, i) => { this.chartOption.series = echartsData.map((t, i) => {
this.chartOption.yAxis[0].axisLabel.formatter = (value) => {
if (t.unitType === 'percent') {
return unitConvert(value, t.unitType)[0]
} else if (t.unitType === 'time') {
return unitConvert(value, 'number').join('')
} else {
return unitConvert(value, t.unitType).join('')
}
}
return { return {
...chartOption, ...chartOption,
name: this.$t(t.name), name: this.$t(t.name) + (legendUnit || ''),
lineStyle: { lineStyle: {
color: chartColor3[t.positioning], color: chartColor3[t.positioning],
width: 1 width: 1
@@ -308,12 +576,26 @@ export default {
} }
]) ])
}, },
data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), 'number']) data: t.data.map(v => [Number(v[0]) * 1000, Number(v[1]), t.unitType])
} }
}) })
this.chartOption.tooltip.formatter = (params) => { this.chartOption.tooltip.formatter = (params) => {
params.forEach(t => { params.forEach(t => {
t.seriesName = this.$t(t.seriesName) t.seriesName = this.$t(t.seriesName)
this.mpackets.forEach(e => {
if (this.$t(e.name) === t.seriesName) {
t.borderColor = chartColor3[e.positioning]
}
})
this.npmQuantity.forEach(d => {
const nameMs = this.$t(d.name) + '(ms)'
const namePrent = this.$t(d.name) + '(%)'
if (nameMs === t.seriesName) {
t.borderColor = chartColor3[d.positioning]
} else if (namePrent === t.seriesName) {
t.borderColor = chartColor3[d.positioning]
}
})
}) })
const str = stackedLineTooltipFormatter(params) const str = stackedLineTooltipFormatter(params)
return str return str
@@ -384,6 +666,7 @@ export default {
beforeUnmount () { beforeUnmount () {
clearTimeout(this.timer) clearTimeout(this.timer)
window.removeEventListener('resize', this.resize) window.removeEventListener('resize', this.resize)
this.myChart = null
} }
} }
</script> </script>

View File

@@ -1,9 +1,13 @@
<template> <template>
<div class="npm-sessions"> <div class="npm-sessions">
<div class="npm-sessions-title">{{$t('npm.relatedSessions')}}</div> <div class="npm-sessions-title">
{{$t('npm.relatedSessions')}}
<chart-error v-if="showError" tooltip :content="errorMsg" />
</div>
<div class="npm-sessions-div"> <div class="npm-sessions-div">
<div class="npm-sessions-div-green" id="green" v-show="clientSessions > 0"></div> <div class="npm-sessions-div-green" id="green" v-show="clientSessions > 0"></div>
<div class="npm-sessions-div-red" id="red" v-show="serverSessions > 0"></div> <div class="npm-sessions-div-red" id="red" v-show="serverSessions > 0"></div>
<div class="npm-sessions-div-gray" id="gray" v-show="showError || isNoData"></div>
</div> </div>
<div class="npm-sessions-body"> <div class="npm-sessions-body">
<div class="npm-sessions-body-left"> <div class="npm-sessions-body-left">
@@ -36,9 +40,11 @@ import { getSecond } from '@/utils/date-util'
import unitConvert from '@/utils/unit-convert' import unitConvert from '@/utils/unit-convert'
import { unitTypes } from '@/utils/constants' import { unitTypes } from '@/utils/constants'
import chartMixin from '@/views/charts2/chart-mixin' import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
export default { export default {
name: 'RelatedSessions', name: 'RelatedSessions',
components: { ChartError },
mixins: [chartMixin], mixins: [chartMixin],
data () { data () {
return { return {
@@ -46,12 +52,16 @@ export default {
unitConvert, unitConvert,
unitTypes, unitTypes,
clientSessions: 0, clientSessions: 0,
serverSessions: 0 serverSessions: 0,
showError: false,
errorMsg: '',
noData: false,
isNoData: false
} }
}, },
watch: { watch: {
timeFilter: { timeFilter: {
handler (n) { handler () {
this.relatedSessionsData() this.relatedSessionsData()
} }
} }
@@ -66,25 +76,63 @@ export default {
ip: condition[1] ip: condition[1]
} }
const divGreen = document.getElementById('green') const divGreen = document.getElementById('green')
const divred = document.getElementById('red') const divRed = document.getElementById('red')
const divGray = document.getElementById('gray')
get(api.npm.overview.relatedSessions, params).then(res => { get(api.npm.overview.relatedSessions, params).then(res => {
if (res.code === 200) { if (res.code === 200) {
this.showError = false
this.isNoData = false
this.sessionData = res.data.result this.sessionData = res.data.result
this.clientSessions = this.sessionData.clientSessions / (this.sessionData.clientSessions * 1 + this.sessionData.serverSessions * 1) const client = this.sessionData.clientSessions
this.serverSessions = this.sessionData.serverSessions / (this.sessionData.clientSessions * 1 + this.sessionData.serverSessions * 1) const server = this.sessionData.serverSessions
this.sessionData.clientSessions = unitConvert(this.clientSessions, unitTypes.percent).join('')
this.sessionData.serverSessions = unitConvert(this.serverSessions, unitTypes.percent).join('') // 如果返回数据为-,则不必计算比例
if (this.clientSessions === 1) { if (!isNaN(client) && !isNaN(server)) {
divGreen.style.borderRadius = 4 + 'px' this.clientSessions = client / (client * 1 + server * 1)
} else if (this.serverSessions === 1) { this.serverSessions = server / (client * 1 + server * 1)
divred.style.borderRadius = 4 + 'px' this.sessionData.clientSessions = unitConvert(this.clientSessions, unitTypes.percent).join('')
this.sessionData.serverSessions = unitConvert(this.serverSessions, unitTypes.percent).join('')
if (this.clientSessions === 1) {
divGreen.style.borderRadius = 4 + 'px'
} else if (this.serverSessions === 1) {
divRed.style.borderRadius = 4 + 'px'
} else if (this.clientSessions === 0 && this.serverSessions === 0) {
this.isNoData = true
divGray.style.borderRadius = 4 + 'px'
divGray.style.width = '100%'
}
divGreen.style.width = this.sessionData.clientSessions
divRed.style.width = this.sessionData.serverSessions
} else {
this.isNoData = true
this.changeByErrorOrNodata()
} }
divGreen.style.width = this.sessionData.clientSessions } else {
divred.style.width = this.sessionData.serverSessions this.showError = true
this.errorMsg = res.message
this.changeByErrorOrNodata()
} }
}).catch(error => {
this.showError = true
this.errorMsg = error.message
this.changeByErrorOrNodata()
}).finally(() => { }).finally(() => {
this.toggleLoading(false) this.toggleLoading(false)
}) })
},
/**
* 当无数据或者报错时改变界面样式,出现灰条
*/
changeByErrorOrNodata () {
const divGray = document.getElementById('gray')
divGray.style.borderRadius = 4 + 'px'
divGray.style.width = '100%'
this.sessionData.clientSessions = '—'
this.sessionData.serverSessions = '—'
this.clientSessions = 0
this.serverSessions = 0
} }
}, },
mounted () { mounted () {

View File

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

View File

@@ -44,7 +44,7 @@ export const pieChartOption1 = {
const data = pieChartOption1.series[0].data const data = pieChartOption1.series[0].data
let value let value
data.forEach(t => { data.forEach(t => {
if (t.name == name) { if (t.name === name) {
value = t.value value = t.value
} }
}) })
@@ -103,7 +103,7 @@ export const pieChartOption2 = {
const data = pieChartOption2.series[0].data const data = pieChartOption2.series[0].data
let value let value
data.forEach(t => { data.forEach(t => {
if (t.name == name) { if (t.name === name) {
value = t.value value = t.value
} }
}) })
@@ -164,6 +164,15 @@ export const stackedLineChartOption = {
trigger: 'axis', trigger: 'axis',
className: 'echarts-tooltip echarts-tooltip-dark' className: 'echarts-tooltip echarts-tooltip-dark'
}, },
toolbox: {
show: false
},
brush: {
toolbox: ['lineX'],
xAxisIndex: 'all',
throttleType: 'debounce',
transformable: false
},
legend: { legend: {
show: false show: false
}, },
@@ -177,7 +186,7 @@ export const stackedLineChartOption = {
xAxis: [ xAxis: [
{ {
type: 'time', type: 'time',
splitNumber: 12 splitNumber: 8
// axisLabel: { // axisLabel: {
// formatter: function (value) { // formatter: function (value) {
// const data = new Date(value) // const data = new Date(value)
@@ -234,7 +243,7 @@ export const linkTrafficLineChartOption = {
xAxis: [ xAxis: [
{ {
type: 'time', type: 'time',
splitNumber: 12 splitNumber: 8
// axisLabel: { // axisLabel: {
// formatter: function (value) { // formatter: function (value) {
// const data = new Date(value) // const data = new Date(value)
@@ -294,7 +303,7 @@ export const appListChartOption = {
bottom: 0 bottom: 0
}, },
animation: false, animation: false,
color: chartColor, color: chartColor3[2],
axisLabel: {}, axisLabel: {},
series: [ series: [
{ {
@@ -345,24 +354,13 @@ export const npmLineChartOption = {
show: false show: false
}, },
axisLabel: { axisLabel: {
formatter: function (value) { // formatter: function (value) {
return unitConvert(value, unitTypes.number).join('') // return unitConvert(value, unitTypes.number).join('')
} // }
} }
} }
], ],
series: [ series: []
{
type: 'line',
symbol: 'circle',
smooth: true,
showSymbol: false,
emphasis: {
focus: 'series'
},
data: []
}
]
} }
export const trafficLineChartOption = { export const trafficLineChartOption = {
@@ -384,7 +382,6 @@ export const trafficLineChartOption = {
padding: [0, 0, 0, 2], padding: [0, 0, 0, 2],
fontSize: 12, fontSize: 12,
color: '#717171', color: '#717171',
fontWeight: 400,
fontFamily: 'NotoSansSChineseRegular' fontFamily: 'NotoSansSChineseRegular'
} }
}, },
@@ -398,7 +395,7 @@ export const trafficLineChartOption = {
xAxis: [ xAxis: [
{ {
type: 'time', type: 'time',
splitNumber: 12 splitNumber: 8
// axisLabel: { // axisLabel: {
// formatter: function (value) { // formatter: function (value) {
// const data = new Date(value) // const data = new Date(value)
@@ -416,9 +413,9 @@ export const trafficLineChartOption = {
show: false show: false
}, },
axisLabel: { axisLabel: {
formatter: function (value) { // formatter: function (value) {
return unitConvert(value, unitTypes.number).join('') // return unitConvert(value, unitTypes.number).join('')
} // }
} }
} }
], ],
@@ -488,7 +485,7 @@ export const stackedBarChartOption = {
xAxis: { xAxis: {
type: 'time', type: 'time',
boundaryGap: ['1%', '3%'], boundaryGap: ['1%', '3%'],
splitNumber: 12, splitNumber: 8,
axisLine: { axisLine: {
show: false show: false
}, },

View File

@@ -1,11 +1,13 @@
<template> <template>
<div class="detection-filter-case"> <div class="detection-filter-case">
<div class="no-data" v-if="isNoData">No data</div> <div class="no-data" v-if="isNoData">No data</div>
<div class="new-detection-filter-title">{{$t('detections.filters')}}</div>
<template v-for="(filter, index) in filterData" :key="index"> <template v-for="(filter, index) in filterData" :key="index">
<div class="detection-filter" v-show="filter.data.length > 0"> <div class="detection-filter" v-show="filter.data.length > 0">
<div class="filter__header" @click="filter.collapse = !filter.collapse"> <div class="filter__header" @click="filter.collapse = !filter.collapse">
<i class="el-icon-arrow-right" :class="{ 'arrow-rotate': !filter.collapse }"></i> <span class="new-detection-filter-header-title">{{filter.title}}</span>
<span>{{filter.title}}</span> <i class="el-icon-arrow-right new-detection-filter-icon" :class="{ 'arrow-rotate': !filter.collapse }"></i>
</div> </div>
<el-collapse-transition> <el-collapse-transition>
<div class="filter__body" v-show="!filter.collapse"> <div class="filter__body" v-show="!filter.collapse">

View File

@@ -4,8 +4,10 @@
<div class="detection-list__content"> <div class="detection-list__content">
<div class="detection-list--list"> <div class="detection-list--list">
<div class="no-data" v-if="noData">No data</div> <div class="no-data" v-if="noData">No data</div>
<div v-if="!isCollapse" @click="collapse" class="cn-detection__shadow"></div> <div v-if="!isCollapse" @click="collapse" class="cn-detection__shadow new-cn-detection__shadow"></div>
<detection-row <detection-row
style="margin-bottom: 10px"
class="detection-border"
v-for="(data, index) in listData" v-for="(data, index) in listData"
:detection="data" :detection="data"
:page-type="pageType" :page-type="pageType"

View File

@@ -1,13 +1,22 @@
<template> <template>
<div class="cn-detection--list" :style="{zIndex: !isCollapse ? 1 : 'unset'}"> <div class="cn-detection--list" :style="{zIndex: !isCollapse ? 5 : 'unset'}">
<!-- 左侧下拉按钮 --> <!-- 左侧下拉按钮 -->
<div class="cn-detection__collapse"> <div class="cn-detection__collapse">
<span @click="switchCollapse" :class="{'reg-down': !isCollapse}"><i class="cn-icon cn-icon-arrow-right"></i></span> <div class="cn-detection__collapse-block" @click="switchCollapse">
<span :class="{'reg-down': !isCollapse}">
<i class="cn-icon cn-icon-arrow-right"></i>
</span>
</div>
</div> </div>
<div class="cn-detection__case"> <div class="cn-detection__case">
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[detection.eventSecurity]}`"></div> <div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[detection.eventSecurity]}`"></div>
<div class="cn-detection__row"> <div class="cn-detection__row">
<div class="cn-detection__header" v-if="pageType === detectionPageType.securityEvent"> <div class="cn-detection__header" v-if="pageType === detectionPageType.securityEvent">
<span
class="detection-event-severity-color-block"
:style="`background-color: ${eventSeverityColor[detection.eventSeverity]}`">
</span>
<span class="detection-event-severity-block">{{ detection.securityType || '-' }}</span>
<i class="cn-icon cn-icon-attacker" ></i>{{detection.offenderIp || '-'}} <i class="cn-icon cn-icon-attacker" ></i>{{detection.offenderIp || '-'}}
<div v-if="detection.domain" class="domain">{{detection.domain}}</div> <div v-if="detection.domain" class="domain">{{detection.domain}}</div>
<span class="line">-------</span> <span class="line">-------</span>
@@ -31,11 +40,6 @@
<span>{{$t('detections.eventSeverity')}}&nbsp;:&nbsp;&nbsp;</span> <span>{{$t('detections.eventSeverity')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.eventSeverity || '-'}}</span> <span>{{detection.eventSeverity || '-'}}</span>
</div> </div>
<div class="basic-info__item" v-if="detection.securityType">
<i class="cn-icon cn-icon-event-type"></i>
<span>{{$t('detection.list.securityType')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{detection.securityType || '-'}}</span>
</div>
<div class="basic-info__item" v-if="detection.eventType"> <div class="basic-info__item" v-if="detection.eventType">
<i class="cn-icon cn-icon-event-type"></i> <i class="cn-icon cn-icon-event-type"></i>
<span>{{$t('detections.eventType')}}&nbsp;:&nbsp;&nbsp;</span> <span>{{$t('detections.eventType')}}&nbsp;:&nbsp;&nbsp;</span>

View File

@@ -0,0 +1,94 @@
<template>
<div class="npm-tabs">
12345678
<div class="npm-tabs__active-bar"></div>
<el-tabs v-model="currentTab" ref="elTabs" type="border-card" @tab-click="jumpPage">
<el-tab-pane
v-for="(tab,index) in tabs"
:key="tab.i18n"
:name="index"
:disabled="tab.disable">
<template #label>
<div class="npm-tab__label">
<i :class="tab.icon"></i>
<span>{{ $t(tab.i18n) }}</span>
</div>
</template>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import chartMixin from '@/views/charts2/chart-mixin'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
export default {
name: 'DetectionTabs',
data () {
return {
leftOffset: 27
}
},
mixins: [chartMixin],
setup (props) {
const tabs = ref([])
if (props.chart) {
tabs.value = [...props.chart]
tabs.value.forEach(item => {
item.disable = false
})
}
const { query } = useRoute()
const tabIndexParam = query.tabIndex
const currentTab = ref(tabIndexParam ? parseInt(tabIndexParam) : 0)
return {
currentTab,
tabs
}
},
watch: {
currentTab (n) {
const { query } = this.$route
const newUrl = urlParamsHandler(window.location.href, query, {
tabIndex: n
})
overwriteUrl(newUrl)
this.$nextTick(() => {
this.handleActiveBar(n)
})
}
},
methods: {
handleActiveBar (index) {
const tabDom = document.getElementById('tab-' + index)
if (tabDom) {
const offsetLeft = tabDom.offsetLeft
const clientWidth = tabDom.clientWidth
const clientLeft = tabDom.clientLeft
const activeBar = document.querySelector('.npm-tabs .npm-tabs__active-bar')
activeBar.style.cssText += `width: ${clientWidth + 2}px; left: ${offsetLeft + this.leftOffset + clientLeft - 1}px;`
}
},
jumpPage (item) {
this.$router.push({
path: this.tabs[item.index].path,
query: {
t: +new Date(),
tabIndex: this.currentTab
}
})
}
},
mounted () {
// setTimeout(() => {
this.$nextTick(() => {
this.handleActiveBar(this.currentTab)
})
// }, 120)
}
}
</script>

View File

@@ -1,37 +1,57 @@
<template> <template>
<div <div class="entity-explorer entity-explorer--show-list">
class="entity-explorer entity-explorer--show-list"
>
<!-- 顶部工具栏在列表页显示 --> <!-- 顶部工具栏在列表页显示 -->
<div class="explorer-top-tools"> <div class="explorer-top-tools explorer-detection-top-tools">
<DateTimeRange class="date-time-range" :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" :date-range="timeFilter.dateRangeValue" ref="dateTimeRange" @change="reload"/> <div class="explorer-top-tools-title">{{$t('overall.detections')}}</div>
<TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/> <div style="display: flex">
<div class="explorer-top-tools-block">
<i class="cn-icon cn-icon-setting detection-icon-setting"></i>
<span>Configure Policies</span>
</div>
<DateTimeRange
class="date-time-range"
:start-time="timeFilter.startTime"
:end-time="timeFilter.endTime"
:date-range="timeFilter.dateRangeValue"
ref="dateTimeRange"
@change="reload"/>
<TimeRefresh
class="date-time-range"
@change="timeRefreshChange"
:end-time="timeFilter.endTime"/>
</div>
</div> </div>
<div style="width: 100%;padding-bottom: 26px;">
<detection-tabs :time-filter="timeFilter" :chart="tabsData" />
</div>
<!-- 搜索组件 --> <!-- 搜索组件 -->
<detection-search <detection-search
class="detection-border"
ref="search" ref="search"
:page-type="pageType" :page-type="pageType"
@search="search" @search="search"
></detection-search> ></detection-search>
<!-- 内容区 --> <!-- 内容区 -->
<div class="explorer-container" style="height: calc(100% - 20px); flex-direction: column"> <div class="explorer-container" style="height: calc(100% - 20px);flex-direction: column">
<loading :loading="loading"></loading> <loading :loading="loading"></loading>
<template v-if="isEventSeverityNoData"> <template v-if="isEventSeverityNoData">
<div class="no-data detection__event-severity-bar" >No data</div> <div class="no-data detection__event-severity-bar" >No data</div>
</template> </template>
<template v-if="!isEventSeverityNoData"> <template v-if="!isEventSeverityNoData">
<div class="detection__event-severity-bar" :id="`eventSeverityTrendBar${pageType}`"> <div class="detection__event-severity-bar detection-border" :id="`eventSeverityTrendBar${pageType}`">
</div> </div>
</template> </template>
<div style="display: flex; flex-grow: 1; height: 100%;"> <div style="display: flex; flex-grow: 1; height: 100%;">
<detection-filter <detection-filter
class="detection-border"
:filter-data="filterData[pageType]" :filter-data="filterData[pageType]"
:q="q" :q="q"
:time-filter="timeFilter" :time-filter="timeFilter"
></detection-filter> ></detection-filter>
<div class="detection__list"> <div class="detection__list">
<div class="detection__list-statistics"> <div class="detection__list-statistics detection-border">
<div class="statistics__severity"> <div class="statistics__severity">
<div class="chart-header"> <div class="chart-header">
<div class="chart-header__title">{{$t('detection.severity')}}</div> <div class="chart-header__title">{{$t('detection.severity')}}</div>
@@ -81,15 +101,14 @@
></detection-list> ></detection-list>
<div class="entity__pagination" > <div class="entity__pagination" >
<Pagination <Pagination
ref="pagination" ref="pagination"
:page-obj="pageObj" :page-obj="pageObj"
@pageNo='pageNo' @pageNo='pageNo'
@pageSize='pageSize' @pageSize='pageSize'
@size-change="pageSize" @size-change="pageSize"
@prev-click="prev" @prev-click="prev"
@next-click="next" @next-click="next"
> ></Pagination>
</Pagination>
</div> </div>
</div> </div>
</div> </div>
@@ -108,12 +127,13 @@ import { defaultPageSize, detectionPageType } from '@/utils/constants'
import { getNowTime, getSecond, rTime } from '@/utils/date-util' import { getNowTime, getSecond, rTime } from '@/utils/date-util'
import { ref, shallowRef } from 'vue' import { ref, shallowRef } from 'vue'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import { multipleBarOption, pieForSeverity, activeAttackBar, getAttackColor, getSeverityColor, getSeriesIndex } from '@/views/detections/options/detectionOptions' import { multipleBarOption, pieForSeverity, activeAttackBar, getAttackColor, getSeverityColor } from '@/views/detections/options/detectionOptions'
import { api, getData } from '@/utils/api' import { api, getData } from '@/utils/api'
import { reverseSortBy, sortBy, extensionEchartY } from '@/utils/tools' import { reverseSortBy, extensionEchartY } from '@/utils/tools'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import DetectionNoData from '@/views/detections/DetectionNoData' // import DetectionNoData from '@/views/detections/DetectionNoData'
import Loading from '@/components/common/Loading' import Loading from '@/components/common/Loading'
import DetectionTabs from '@/views/detections/DetectionTabs'
export default { export default {
name: 'Index', name: 'Index',
@@ -125,10 +145,31 @@ export default {
DetectionFilter, DetectionFilter,
DetectionList, DetectionList,
Pagination, Pagination,
DetectionNoData // DetectionNoData,
DetectionTabs
}, },
data () { data () {
return { return {
tabsData: [
{
name: 'SecurityEvents',
i18n: 'entities.securityEvents',
path: '/detection/securityEvent',
icon: 'cn-icon cn-icon-a-SecurityEvent'
},
{
name: 'Regulatory Risk Event',
i18n: 'entities.regulatoryRiskEvents',
path: '/detection/securityEvent',
icon: 'cn-icon cn-icon-a-RegulatoryRiskEvent'
},
{
name: 'PerformanceEvents',
i18n: 'overall.performanceEvents',
path: '/detection/performanceEvent',
icon: 'cn-icon cn-icon-a-PerformanceEvent'
}
],
chartInit: [], chartInit: [],
pageObj: { pageObj: {
pageNo: 1, pageNo: 1,
@@ -231,7 +272,6 @@ export default {
isStatisticsCategoryNoData: false, isStatisticsCategoryNoData: false,
isStatisticsActiveAttackNoData: false, isStatisticsActiveAttackNoData: false,
loading: false, loading: false,
oldActiveEntitySearchValue: '' oldActiveEntitySearchValue: ''
} }
}, },
@@ -258,7 +298,7 @@ export default {
const eventSeverityTrendOption = this.$_.cloneDeep(multipleBarOption) const eventSeverityTrendOption = this.$_.cloneDeep(multipleBarOption)
const xData = [] const xData = []
dataMap.forEach(function (value, key) { dataMap.forEach(function (value) {
// eventSeverityTrendOption.series[Number(getSeriesIndex(key))].data = value.map(v => Number(v[1])) // eventSeverityTrendOption.series[Number(getSeriesIndex(key))].data = value.map(v => Number(v[1]))
value.forEach(item => { value.forEach(item => {
if (xData.indexOf(item[0]) < 0) { if (xData.indexOf(item[0]) < 0) {
@@ -270,8 +310,10 @@ export default {
const seriesData = [] const seriesData = []
xData.forEach(item => { xData.forEach(item => {
if (dataMap.has(serie.name)) { if (dataMap.has(serie.name)) {
// todo 下面这行注释可解决eslint报红线的问题暂不知道原因后续解决
// eslint-disable-next-line array-callback-return
const hasX = dataMap.get(serie.name).some(function (v) { const hasX = dataMap.get(serie.name).some(function (v) {
if (item == v[0]) { if (item === v[0]) {
seriesData.push(Number(v[1])) seriesData.push(Number(v[1]))
return true return true
} }
@@ -296,7 +338,7 @@ export default {
// this.isEventSeverityNoData = true // this.isEventSeverityNoData = true
} }
}).catch(error => { }).catch(error => {
console.log(error)
}).finally(() => { }).finally(() => {
this.$nextTick(() => { this.$nextTick(() => {
this.loading = false this.loading = false
@@ -329,7 +371,7 @@ export default {
}) })
} }
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
initEventTypeData (params) { initEventTypeData (params) {
@@ -357,7 +399,7 @@ export default {
}) })
} }
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
initSecurityTypeData (params) { initSecurityTypeData (params) {
@@ -385,7 +427,7 @@ export default {
}) })
} }
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
initOffenderIpData (params) { initOffenderIpData (params) {
@@ -419,7 +461,7 @@ export default {
}) })
} }
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
@@ -430,7 +472,7 @@ export default {
this.filterData[this.pageType][2].showMore = showMore this.filterData[this.pageType][2].showMore = showMore
this.filterData[this.pageType][2].showIndex = showIndex this.filterData[this.pageType][2].showIndex = showIndex
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
initVictimLocationData (params) { initVictimLocationData (params) {
@@ -440,7 +482,7 @@ export default {
this.filterData[this.pageType][3].showMore = showMore this.filterData[this.pageType][3].showMore = showMore
this.filterData[this.pageType][3].showIndex = showIndex this.filterData[this.pageType][3].showIndex = showIndex
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
initOffenderLocationData (params) { initOffenderLocationData (params) {
@@ -450,7 +492,7 @@ export default {
this.filterData[this.pageType][5].showMore = showMore this.filterData[this.pageType][5].showMore = showMore
this.filterData[this.pageType][5].showIndex = showIndex this.filterData[this.pageType][5].showIndex = showIndex
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
initActiveEntity (params) { initActiveEntity (params) {
@@ -510,6 +552,7 @@ export default {
}) })
} }
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
triggerFilterDataValue (array, value) { triggerFilterDataValue (array, value) {
@@ -539,12 +582,12 @@ export default {
getData(api.detection[this.pageType].listBasic, params).then(data => { getData(api.detection[this.pageType].listBasic, params).then(data => {
this.listData = data this.listData = data
}).catch(error => { }).catch(error => {
console.log(error)
}) })
getData(api.detection[this.pageType].listCount, params).then(data => { getData(api.detection[this.pageType].listCount, params).then(data => {
this.pageObj.total = data this.pageObj.total = data
}).catch(error => { }).catch(error => {
console.log(error)
}) })
}, },
timeRefreshChange () { timeRefreshChange () {
@@ -720,7 +763,7 @@ export default {
} }
} }
}, },
timeFilter (n) { timeFilter () {
this.search(this.metaList, this.q) this.search(this.metaList, this.q)
}, },
'filterData.securityEvent.0.value': { 'filterData.securityEvent.0.value': {

View File

@@ -46,7 +46,7 @@
<div class="overview__title">{{$t('detections.goToEntity')}}</div> <div class="overview__title">{{$t('detections.goToEntity')}}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content"> <div class="row__content">
<span>{{ $t('detections.viewDetailOf') }}</span> &nbsp; <span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span <span
class="row__content--link" class="row__content--link"
@click="goDetail('app', detection.appName)">{{detection.appName}}</span> @click="goDetail('app', detection.appName)">{{detection.appName}}</span>

View File

@@ -49,7 +49,7 @@
<div class="overview__title">{{$t('detections.goToEntity')}}</div> <div class="overview__title">{{$t('detections.goToEntity')}}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content"> <div class="row__content">
<span>{{ $t('detections.viewDetailOf') }}</span> &nbsp; <span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span <span
class="row__content--link" class="row__content--link"
@click="goDetail('domain', computeSecondaryDomain(detection.domain))">{{detection.domain}}</span> @click="goDetail('domain', computeSecondaryDomain(detection.domain))">{{detection.domain}}</span>

View File

@@ -40,7 +40,7 @@
<div class="overview__title">{{$t('detections.goToEntity')}}</div> <div class="overview__title">{{$t('detections.goToEntity')}}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content"> <div class="row__content">
<span>{{ $t('detections.viewDetailOf') }}</span> &nbsp; <span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span <span
class="row__content--link" class="row__content--link"
@click="goDetail('ip', detection.serverIp)">{{detection.serverIp}}</span> @click="goDetail('ip', detection.serverIp)">{{detection.serverIp}}</span>

View File

@@ -164,7 +164,7 @@
<div class="overview__title">{{ $t('detections.goToVictim') }}</div> <div class="overview__title">{{ $t('detections.goToVictim') }}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content"> <div class="row__content">
<span>{{ $t('detections.viewDetailOf') }}</span> &nbsp; <span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span <span
class="row__content--link" class="row__content--link"
@click="goDetail('ip', basicInfo.victimIp)">{{ basicInfo.victimIp }}</span> @click="goDetail('ip', basicInfo.victimIp)">{{ basicInfo.victimIp }}</span>
@@ -173,7 +173,7 @@
<div class="overview__title">{{ $t('detections.goToOffender') }}</div> <div class="overview__title">{{ $t('detections.goToOffender') }}</div>
<div class="overview__row"> <div class="overview__row">
<div class="row__content"> <div class="row__content">
<span>{{ $t('detections.viewDetailOf') }}</span> &nbsp; <span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span <span
class="row__content--link" class="row__content--link"
@click="goDetail('ip', basicInfo.offenderIp)" @click="goDetail('ip', basicInfo.offenderIp)"

View File

@@ -143,7 +143,6 @@ import { api } from '@/utils/api'
import { getNowTime, getSecond } from '@/utils/date-util' import { getNowTime, getSecond } from '@/utils/date-util'
import { ref } from 'vue' import { ref } from 'vue'
import _ from 'lodash' import _ from 'lodash'
import Pagination from '@/components/common/Pagination'
import Loading from '@/components/common/Loading' import Loading from '@/components/common/Loading'
export default { export default {
@@ -154,8 +153,7 @@ export default {
DateTimeRange, DateTimeRange,
TimeRefresh, TimeRefresh,
EntityFilter, EntityFilter,
EntityList, EntityList
Pagination
}, },
data () { data () {
return { return {
@@ -345,7 +343,9 @@ export default {
loadingIpActive: false, loadingIpActive: false,
// 实体详情列表页面 左侧筛选条件 // 实体详情列表页面 左侧筛选条件
loadingLeft: false loadingLeft: false,
initFlag: false, // 初始化标志避免初始化时pageSize和pageNo会调用搜索
timer: null // 初始化标志的延时器,需要销毁
} }
}, },
methods: { methods: {
@@ -463,16 +463,36 @@ export default {
this.queryFilter({ entityType: 'dns', ...this.timeFilter }) this.queryFilter({ entityType: 'dns', ...this.timeFilter })
this.queryList({ ...this.pageObj, ...this.timeFilter }) this.queryList({ ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ ...this.timeFilter }) this.queryListTotal({ ...this.timeFilter })
// todo 当前页面选择其他值,重刷界面仍会被重置,后续记得添加上
// 延时一秒避免初始化时pageSize为20pageNo为1也会调用“搜索”的情况
if (!this.initFlag) {
this.timer = setTimeout(() => {
this.initFlag = true
}, 1000)
}
} }
}, },
pageSize (val) { pageSize (val) {
this.pageObj.pageSize = val this.pageObj.pageSize = val
this.search({ metaList: this.metaList, q: this.q }) if (this.initFlag) {
this.search({ metaList: this.metaList, q: this.q })
} else {
if (val !== 20) {
this.search({ metaList: this.metaList, q: this.q })
}
}
}, },
pageNo (val) { pageNo (val) {
this.pageObj.pageNo = val this.pageObj.pageNo = val
this.pageObj.resetPageNo = false this.pageObj.resetPageNo = false
this.search({ metaList: this.metaList, q: this.q }) if (this.initFlag) {
this.search({ metaList: this.metaList, q: this.q })
} else {
if (val !== 1) {
this.search({ metaList: this.metaList, q: this.q })
}
}
}, },
// 点击上一页箭头 // 点击上一页箭头
prev () { prev () {
@@ -703,6 +723,9 @@ export default {
return { return {
timeFilter timeFilter
} }
},
beforeUnmount () {
clearTimeout(this.timer)
} }
} }
</script> </script>