Compare commits

...

191 Commits

Author SHA1 Message Date
chenjinsong
e26d2b40ab CN-1456 feat: 折线图参考线等国际化 2023-11-21 14:33:16 +08:00
chenjinsong
6c1f1ed71b CN-1475 feat: subscribe配置 2023-11-20 18:48:10 +08:00
chenjinsong
b4b72fc1e1 CN-1468 fix: 补充一些开关的权限控制 2023-11-20 18:14:42 +08:00
hyx
b3d4bbd440 fix:CN-1470 调整新增/修改role页面权限树的逻辑:1.选中节点时,同步选中所有的父辈节点,子节点不进行同步操作;2.取消选中节点时,同步取消选中所有子节点,对父节点不进行同步操作; 2023-11-20 15:27:14 +08:00
刘洪洪
6c90ebe7bc fix: 实体详情tab恢复security事件 2023-11-20 11:52:52 +08:00
chenjinsong
ef3994e14a fix: 修复实体下拉中事件时间问题;behavior pattern的百分比计算逻辑调整 2023-11-20 10:54:03 +08:00
chenjinsong
abc1d07e6c CN-1456 feat: 内置知识库标题和描述增加国际化 2023-11-17 17:10:40 +08:00
hanyuxia
38af43b322 fix: 修改用户时点保存按钮无反应(密码验证代码处的问题) 2023-11-17 16:09:18 +08:00
hanyuxia
90ed543a84 fix:1 检测->事件类型饼图: resize情况下的,legend宽度自适应 2023-11-17 15:21:20 +08:00
chenjinsong
c66a6b7f59 fix: 实体详情security event tab取值问题修复 2023-11-17 14:21:37 +08:00
hanyuxia
a0af36b7c0 fix:1 检测->事件类型饼图legend宽度随分辨率而改变代码补充 2023-11-17 14:13:04 +08:00
hanyuxia
f95dc60cd3 Merge remote-tracking branch 'origin/dev' into dev 2023-11-17 13:54:39 +08:00
hanyuxia
7d64868ce0 fix:1 CN-1470 调整新增/修改role页面权限树的逻辑;2.检查模块下的事件类型饼图legend宽度随分辨率而改变; 2023-11-17 13:54:21 +08:00
chenjinsong
62a7a629e5 fix: 修改一些国际化 2023-11-17 11:41:25 +08:00
chenjinsong
e3c6c21545 fix: 补充一些国际化 2023-11-17 11:20:21 +08:00
chenjinsong
3b2eaf3851 fix: 搜索组件样式调整 2023-11-16 19:45:43 +08:00
chenjinsong
ee05f47f2d fix: 一些国际化和样式调整 2023-11-16 17:29:54 +08:00
hyx
07dd16f2c2 CN-1410 Detections模块中,无法根据面包屑导航进行跳转页面 2023-11-15 22:22:53 +08:00
chenjinsong
9103c454c2 fix: 调整.cn-container样式 2023-11-15 18:11:33 +08:00
chenjinsong
e5a7c2abfa fix: behavior pattern的bar设置最小高度 2023-11-15 17:53:34 +08:00
刘洪洪
e288e20fd7 fix: 实体、检测搜索框高度调整 2023-11-15 16:22:49 +08:00
刘洪洪
ec746f0fc7 CN-1478 fix: detection事件列表搜索框增加历史记录 2023-11-15 15:26:37 +08:00
刘洪洪
4ca33e9ede CN-1407 fix: Detections-Policies-Create Event Policy模块下,添加取消按钮 2023-11-15 14:47:51 +08:00
刘洪洪
b8105a4565 CN-1439 fix: Detections-Security events模块中,事件日志详情中添加与Policy的交互 2023-11-15 14:46:02 +08:00
hyx
29157ae7d3 代码提交错误修正 2023-11-15 10:02:59 +08:00
刘洪洪
9c0ce493ab fix: 实体列表ip实体下拉详情添加ISP信息 2023-11-14 15:49:53 +08:00
刘洪洪
4662061fc5 fix: 实体列表样式调整以及禁止detection右侧活跃攻击方条形图的文字点击 2023-11-14 14:42:42 +08:00
刘洪洪
0eb0346abc fix: 实体历史搜索的时间展示和其他时间统一为YYYY-MM-DD hh:mm:ss 2023-11-14 10:18:37 +08:00
刘洪洪
c8926177f7 fix: 修复单测报错的问题 2023-11-13 18:12:04 +08:00
刘洪洪
5aa5d77511 fix: 1、修复实体列表搜索后city两个值一样时,第一个值颜色变浅的问题;2、当搜索结果为空时,将隐藏相关实体按钮进行隐藏。 2023-11-13 17:52:23 +08:00
hyx
38368a6cb8 CN-1461 提取界面各功能的默认时间条件至config.js 2023-11-13 17:23:32 +08:00
刘洪洪
051aeadbca fix: 调整实体搜索高亮样式 2023-11-13 15:36:52 +08:00
刘洪洪
8c2119e773 CN-1458 fix: 修复detection列表点击echarts图形却没有执行搜索 2023-11-13 11:52:08 +08:00
刘洪洪
cb70fb0236 CN-1439 fix: Detections-Security events模块中,事件日志详情中添加Policy ID 2023-11-13 11:50:15 +08:00
刘洪洪
853fa79d4c CN-1448 fix: 实体搜索界面增加相关实体过滤功能 2023-11-10 18:26:24 +08:00
刘洪洪
2f919d1774 fix: 修复实体搜索切换页码不高亮,以及多个搜索关键字拼接为一个字段时不高亮等问题 2023-11-10 18:15:33 +08:00
刘洪洪
360fffde68 fix: detection列表左侧filter的status和Severity进行排序 2023-11-10 17:00:19 +08:00
刘洪洪
bb2a7676e6 CN-1458 fix: detection事件列表的筛选功能与实体的统一 2023-11-10 14:11:46 +08:00
chenjinsong
de698f0a71 CN-1468 fix: 为一些按钮增加权限控制 2023-11-10 11:43:11 +08:00
刘洪洪
1c1d354a6c CN-1449 fix: 实体下拉展开的相关实体添加搜索高亮 2023-11-09 18:42:41 +08:00
刘洪洪
8126618e54 fix: 修复实体、检测界面不能滚动的问题 2023-11-09 17:44:24 +08:00
chenjinsong
5a8796688a CN-1468 fix: 更改知识库的新路由 2023-11-09 17:35:04 +08:00
chenjinsong
be36b2604b fix: 暂时关闭下钻table的自动化测试用例 2023-11-09 16:58:25 +08:00
chenjinsong
330a4b0d3b CN-1468 fix: 路由动态设置 2023-11-09 16:17:25 +08:00
刘洪洪
1410f890f3 CN-1415 fix: 修复搜索历史排序与时间展示问题 2023-11-08 15:42:23 +08:00
刘洪洪
78105475fb CN-1457 fix: 实体列表检索新增时间筛选框 2023-11-08 14:28:37 +08:00
刘洪洪
02a9f35070 fix: 搜索历史面板添加国际化 2023-11-07 18:42:41 +08:00
刘洪洪
c89397da97 CN-1415 fix: 建议Entity列表页增加Search History用于查看和使用历史搜索条件 2023-11-07 18:36:01 +08:00
chenjinsong
36c3db5dee CN-1414 fix: policy搜索框参数从name改为q 2023-11-07 16:35:28 +08:00
hanyuxia
366bb8f17f CN-1443 Detections-Security events模块中,Event type 下的事件类型名称被遮挡 2023-11-07 10:50:49 +08:00
刘洪洪
1c00f568fa CN-1449 fix: 实体搜索结果对命中字段高亮显示 2023-11-06 19:50:29 +08:00
chenjinsong
93be846064 CN-1461 feat: dashboard增加从config.js取时间范围的逻辑 2023-11-06 17:52:55 +08:00
chenjinsong
ffb822d65d fix: 提取默认默认时间条件为配置 2023-11-06 17:40:24 +08:00
刘洪洪
f8e9d36c8a fix: linkMonitor网格图删除空行空列方法添加空判断 2023-11-06 10:35:15 +08:00
刘洪洪
241665cd80 CN-1416 fix: 增加一些字符的国际化映射 2023-11-06 10:33:47 +08:00
chenjinsong
ee57ca4c6c fix: 修改注释 2023-11-03 15:02:31 +08:00
hyx
2bb6c54cab fix:1.自定义知识库列表,引用列,两个引用之间无空隙 2023-11-02 15:10:09 +08:00
hyx
65827d27e4 fix: 1.CN-1446 学习IP趋势图里未按照选择的时间范围展示对应时间周期;2.初次进入知识库的学习引擎日志tab,底部柱状图未初始化当前tab 2023-11-01 16:05:34 +08:00
刘洪洪
98948ff179 fix: 调整实体搜索框的样式 2023-11-01 15:17:49 +08:00
刘洪洪
74dc057090 CN-1447 fix: 修复在Entity搜索框中添加两个重复的搜索条件和一个不重复条件时,页面报错的问题 2023-11-01 14:50:41 +08:00
hyx
e8959bab27 CN-1424 在Network Overview搜索Providers/Applications后无法滚动查看所有符合搜索条件的数据 2023-11-01 14:37:23 +08:00
刘洪洪
a7ce777e10 fix: 修复detection点击左侧filter后取消不生效的问题 2023-11-01 14:18:59 +08:00
刘洪洪
d7d450222b fix: 修复首次登录时语言没有存入缓存的问题 2023-11-01 12:51:00 +08:00
刘洪洪
3da4b4b20a fix: 调整语言的中英文变量 2023-11-01 12:10:54 +08:00
chenjinsong
a0d2160b43 CN-1406 fix: npm map的client、server增加国际化 2023-11-01 11:44:23 +08:00
chenjinsong
b94dba9ed3 fix: 修复内置知识库更新记录有时候会缺少user列的问题 2023-11-01 10:30:36 +08:00
陈劲松
696b03ad40 Merge branch 'cherry-pick-0152c46d' into 'dev'
fix: 修复知识库更新记录的时间丢失时区的问题

See merge request cyber-narrator/cn-ui!57
2023-11-01 02:02:42 +00:00
chenjinsong
f199442c60 fix: 修复知识库更新记录的时间丢失时区的问题
(cherry picked from commit 0152c46d05)
2023-11-01 02:02:35 +00:00
陈劲松
889903bb46 Merge branch 'cherry-pick-64f376e2' into 'dev'
CN-1445 fix: 修复切换不同知识库的更新页面后,psiphon3的柱状图不能显示的问题

See merge request cyber-narrator/cn-ui!56
2023-11-01 02:02:15 +00:00
chenjinsong
e12d76e2ad CN-1445 fix: 修复切换不同知识库的更新页面后,psiphon3的柱状图不能显示的问题
(cherry picked from commit 64f376e22a)
2023-11-01 02:02:03 +00:00
陈劲松
29df6ec60e Merge branch 'cherry-pick-5b89fca7' into 'dev'
CN-1404 fix: 去掉其他知识库的update按钮

See merge request cyber-narrator/cn-ui!55
2023-11-01 02:01:33 +00:00
chenjinsong
20857e1203 CN-1404 fix: 去掉其他知识库的update按钮
(cherry picked from commit 5b89fca77c)
2023-11-01 02:00:37 +00:00
刘洪洪
8d4924a421 fix: 1、修复实体搜索偶现参数解析错误的问题;2、去除detections--policy无效打印 2023-10-31 18:08:23 +08:00
刘洪洪
41d2f48766 CN-1442 fix: 修复Detections多个搜索条件时,去除其中一个,再点击左侧filter刚去除那项导致删除参数错误的问题 2023-10-31 17:54:51 +08:00
刘洪洪
f151415de6 fix: detection--policy的trigger去除秒时间选项 2023-10-31 17:19:26 +08:00
hanyuxia
73dec68e23 fix:CN-1377 1.日历插件的国际化;2.分页插件的国际化;3.”域名“国际化 2023-10-31 15:47:50 +08:00
刘洪洪
60e821fb16 fix: 修复detection列表刷新界面后页码重置为1的问题 2023-10-31 14:43:29 +08:00
刘洪洪
4cb4aba707 CN-1442 fix: 修复Detections-Security events模块中,去掉搜索框筛选条件,左侧菜单栏仍勾选相应条件的问题 2023-10-31 14:04:51 +08:00
hanyuxia
ba4893dae1 fix:CN-1377 1.知识库自定义列表,reference列更多进行国际化 2023-10-31 11:26:46 +08:00
hanyuxia
19c42021db fix:1.顶部菜单只第四级菜单显示title信息;2.实体界面去掉输入框中的信息(Enter...); 2023-10-31 10:23:38 +08:00
hyx
d6c9bd0ee2 Merge remote-tracking branch 'origin/dev' into dev 2023-10-30 16:41:51 +08:00
hyx
63fd8b268f CN-1389 用户密码修改时不允许包含#号 2023-10-30 16:41:37 +08:00
chenjinsong
bd1f755612 CN-1425 fix: 修复dashboard下钻后切换顶部最后一级面包屑时会回到下钻前页面的问题 2023-10-30 16:28:33 +08:00
刘洪洪
dd4f5e1fba fix: eventType取消国际化转换 2023-10-30 16:25:55 +08:00
刘洪洪
ed1d994d5e fix: eventType取消国际化转换 2023-10-30 16:17:01 +08:00
刘洪洪
815af776aa fix: 修复policy新建时点击save按钮不生效的问题 2023-10-30 16:05:29 +08:00
chenjinsong
a4da1dbfac fix: 去掉部分控制台打印 2023-10-30 15:38:07 +08:00
hyx
9a3bf1a4af Merge remote-tracking branch 'origin/dev' into dev 2023-10-30 15:08:29 +08:00
hyx
6a196ba3f0 CN-1377 国际化内容补充:1.时间选择器国际化(右侧操作板国家化);2.下钻table中,protocol+ports的国际化;3.知识库两个小标题的国际化数据,中文的内容也是英文,需要分别改为:智能情报学习、WebSketch集成; 2023-10-30 15:08:17 +08:00
刘洪洪
7b8ca90436 fix: 修复policy新建时form的时间提示被盖住,以及限制输入框内容长度的问题 2023-10-30 15:05:48 +08:00
刘洪洪
90827fd706 fix: policy新建时添加小时不得超过24等时间限制 2023-10-30 14:37:00 +08:00
刘洪洪
99f97b76a7 fix: 修复因映射关系找不到导致的报错问题 2023-10-30 13:25:28 +08:00
chenjinsong
b0260ea41a fix: 修复知识库柱状图横坐标不对的问题 2023-10-30 11:43:55 +08:00
chenjinsong
a7d6ffb4b4 fix: 修复知识库更新结束或者在上传页面返回后柱状图消失的问题 2023-10-30 11:26:24 +08:00
刘洪洪
dbc68077ca CN-1416 fix: 增加一些字符的国际化映射 2023-10-30 11:14:58 +08:00
chenjinsong
186e115adf fix: 工具方法打印排查问题 2023-10-30 10:33:53 +08:00
chenjinsong
6c5233a760 CN-1422 fix: 修复Behavior pattern扇形图比例问题 2023-10-27 19:09:42 +08:00
chenjinsong
2df67c1972 Merge remote-tracking branch 'origin/dev' into dev 2023-10-27 18:49:47 +08:00
chenjinsong
cb3b66bdf7 CN-1396 fix: 修复psiphon3知识库update页面的问题 2023-10-27 18:49:37 +08:00
刘洪洪
6ba0ca0a82 CN-1420 fix: Detections-Security events模块中,通过Event type查询异常 2023-10-27 15:49:20 +08:00
chenjinsong
00df0414c2 fix: 修复自定义知识库不能正常访问的问题 2023-10-27 11:45:58 +08:00
hyx
20a4bb22ef CN-1396 intelligence learnings更新记录功能开发 2023-10-27 07:46:34 +08:00
刘洪洪
00bb6f8c5f CN-1406 fix: 新增策略的trigger模块添加国际化 2023-10-26 18:15:22 +08:00
刘洪洪
e61c664802 CN-1417 fix: 修复Detections-Policies模块的Filters下的Status勾选Disabled和Enabled,列表只会显示第一次勾选内容的问题 2023-10-26 18:06:34 +08:00
chenjinsong
a4dff135d1 fix: 修复取值错误导致事件下拉可能报错的问题 2023-10-26 16:13:25 +08:00
刘洪洪
0bd87db92f CN-1403 fix: 一些ui细节调整 2023-10-26 15:09:14 +08:00
刘洪洪
407d9ec667 CN-1412 fix: 修复新增/编辑policy页library的入参错误问题 2023-10-26 11:36:13 +08:00
chenjinsong
e7637dd866 fix: 调整实体下拉无评分时的展示形式 2023-10-26 11:18:03 +08:00
刘洪洪
59c2b26d6c fix: 修复detection下拉详情的近期相关事件时间戳变成字符串格式,导致显示异常的问题 2023-10-26 10:58:59 +08:00
刘洪洪
8b5b36a621 CN-1412 fix: 新增/编辑policy页,library的下拉框添加可查询功能 2023-10-26 10:51:26 +08:00
chenjinsong
7762658864 Merge remote-tracking branch 'origin/dev' into dev 2023-10-26 10:18:16 +08:00
chenjinsong
eb25dc6aa7 CN-1372 fix: 撤回步长计算函数,步长改为在api计算 2023-10-26 10:18:05 +08:00
刘洪洪
1fff8f49c9 feat: 更新iconfont图标 2023-10-25 18:45:46 +08:00
刘洪洪
47d8212abe feat: 修复策略中trigger的时间选择列表,添加策略setting的国际化,添加setting的icon图标 2023-10-25 18:41:16 +08:00
chenjinsong
94940d745c CN-1372 fix: 提交步长计算函数 2023-10-25 18:08:59 +08:00
chenjinsong
030af380fd fix: 内置知识库上传限制放宽到1G 2023-10-25 17:34:48 +08:00
chenjinsong
e9f21038b5 CN-1404 fix: 去掉非智能学习知识库多余的启停开关 2023-10-25 16:44:27 +08:00
刘洪洪
e355e427fb feat: policy相关文件修改命名并放置到正确目录下 2023-10-25 15:30:04 +08:00
刘洪洪
6dea1ee890 fix: 1、修复detection下拉面板点击空白处,地址栏的eventId未被清除的问题;2、完善policy交互流程 2023-10-25 15:13:57 +08:00
刘洪洪
1fae281b8b fix: 修复detection下拉详情的地址信息取值报错问题 2023-10-25 11:26:33 +08:00
chenjinsong
60a527c765 fix: 修复detection有些取值不对的问题 2023-10-25 11:15:09 +08:00
chenjinsong
3daa875a25 fix: 修复detection的range参数传递错误的问题 2023-10-24 20:58:03 +08:00
chenjinsong
b4fcbd260b fix: 调整detection列表和下拉中的描述字段逻辑 2023-10-24 20:20:56 +08:00
刘洪洪
871781ab70 fix: 完善detection创建策略时信息填写完毕,不点击continue也可以创建 2023-10-24 18:39:49 +08:00
刘洪洪
9bb9967353 fix: 完善detection下拉详情的地址信息,修复相关事件的时间显示异常问题,修复搜索初始化一直为text模式问题 2023-10-24 18:11:17 +08:00
chenjinsong
327ae1956d CN-1391 fix: 调整detection列表和下拉中的展示字段 2023-10-24 18:01:59 +08:00
刘洪洪
4d4d197a80 fix: 修复detection列表和filter问题:1、搜索栏清除搜索,勾选未去除;2、filter选项包含大写,点击搜索时报错;3、搜索包含空格导致搜索失败;4、添加刷新保留状态 2023-10-24 16:02:25 +08:00
chenjinsong
46f1a880cd fix: 调整policy的一些字典项 2023-10-24 15:34:08 +08:00
chenjinsong
d1c9eba029 fix: 修复detection列表总数等问题 2023-10-24 13:52:55 +08:00
chenjinsong
643ada9172 CN-1400 fix: 调整知识库智能学习启停功能 2023-10-23 18:42:32 +08:00
chenjinsong
d0aa64fb9c fix: 调整detection菜单和一些文字描述 2023-10-23 18:04:29 +08:00
hyx
5b7dd6da90 CN-1384 1.behavior pattern:左边的列表垂直居中,右边的图像向左边移动 2023-10-23 17:56:36 +08:00
chenjinsong
97d793471c fix: 修复报错无法加载policies列表的问题 2023-10-23 16:34:29 +08:00
hyx
6a2d5a0efe CN-1384 1.把0的数据去掉,列表和图里都不展示;2.进入实体详情页时,查一次接口,确认非0数据的个数,个数显示在tab上;3.进入tab时,查更新tab顶部数值;4.数据全是0显示No Data; 2023-10-23 14:54:14 +08:00
chenjinsong
b90810470e fix: 更新遗漏的tag处理 2023-10-23 13:42:42 +08:00
chenjinsong
d8865b74d7 CN-1317 fix: 调整地图上俄罗斯的圆点位置 2023-10-23 10:39:06 +08:00
chenjinsong
012d873b71 CN-1400 fix: 界面支持智能学习启停 2023-10-22 23:10:00 +08:00
chenjinsong
fefca1588f CN-1382 fix: 暂时关闭评分相关的单元测试 2023-10-22 21:28:13 +08:00
chenjinsong
f6af25e5e6 CN-1223 fix: 暂时关闭评分相关的单元测试 2023-10-22 20:58:52 +08:00
chenjinsong
c5c7b58720 CN-1223 fix: score改版 2023-10-22 20:21:32 +08:00
chenjinsong
40d43acb6c CN-1391 fix: 修复一系列bug 2023-10-22 18:29:34 +08:00
hyx
7d34b8388c CN-1384 IP实体详情页behavior pattern tab功能开发 2023-10-20 17:41:01 +08:00
刘洪洪
d2feedb319 CN-1391 fix: 添加eventInfoObj字段 2023-10-20 16:02:24 +08:00
刘洪洪
0f52bd5362 CN-1391 fix: Detection列表按新版设计更新接口和样式 2023-10-20 15:45:11 +08:00
刘洪洪
48b3e2aebd fix: 修复检测规则新增时参数不符要求导致新增失败,以及编辑时修改状态不生效的问题 2023-10-20 14:56:56 +08:00
chenjinsong
9208a4fff2 fix: 实体展示新增dns server role类的tag 2023-10-18 15:52:27 +08:00
chenjinsong
19747cf326 CN-1273 fix: 调整内置tag字典 2023-10-18 10:52:25 +08:00
刘洪洪
c2de6c853c Merge remote-tracking branch 'origin/dev' into dev 2023-10-17 10:01:42 +08:00
刘洪洪
b0a7d44c42 fix: 修复detection数据缺少时显示异常的问题 2023-10-17 10:01:32 +08:00
hyx
3b641e1878 CN-1377 国际化内容补充:1.检查下拉框,去掉placeholder; 2023-10-17 10:01:08 +08:00
刘洪洪
0a9d5591d5 fix: 修复导入axios依赖携带版本的问题 2023-10-16 18:13:30 +08:00
刘洪洪
0e82bebaac CN-1173 fix: 检测功能UI开发与接口对接 2023-10-16 17:53:46 +08:00
chenjinsong
85db8cd745 CN-1273 fix: 修复知识库编辑页新增color后,左侧step高度异常的问题 2023-10-16 15:21:34 +08:00
chenjinsong
c960c60790 CN-1317 fix: 调整npm location地图逻辑
1.地图右上角筛选框只提供有数据的选项;
2.更新“国家名称-code”的字典;
3.少数国家/地区无下钻地理数据的,只缩放而不下钻,并提示暂无地图数据;
2023-10-13 16:29:43 +08:00
chenjinsong
18b62f1e3d fix: 修复链路下钻后参数会丢失大写格式的问题 2023-10-12 10:34:09 +08:00
刘洪洪
11540d3c34 fix: 修复曲线图在数据所有tab的max都为0的情况下会重复请求的问题 2023-10-11 18:40:29 +08:00
刘洪洪
5fbab86a80 CN-1351 fix: 实体列表、详情和graph的tag颜色从接口获取 2023-10-11 15:17:15 +08:00
chenjinsong
ed9a2c4e1f fix: 尝试修复依赖问题 2023-10-09 20:15:12 +08:00
chenjinsong
b94bc23a86 CN-1317 fix: 增加几个地理数据文件 2023-10-09 20:04:10 +08:00
chenjinsong
328f226a86 fix: 尝试修改依赖版本 2023-10-09 19:51:15 +08:00
chenjinsong
660950a73c fix: 尝试修改依赖版本 2023-10-09 19:24:44 +08:00
chenjinsong
8f9308a04e fix: 尝试修改依赖版本 2023-10-09 19:03:40 +08:00
chenjinsong
133c9a23c0 fix: 尝试修改依赖版本 2023-10-09 18:55:26 +08:00
chenjinsong
bc4a6c2a9e fix: 尝试修改依赖版本 2023-10-09 18:52:06 +08:00
chenjinsong
5798c0c5a7 fix: 尝试修改依赖版本 2023-10-09 18:38:21 +08:00
chenjinsong
15ac00d548 fix: 尝试修改依赖版本 2023-10-09 17:55:48 +08:00
chenjinsong
95217b84a5 fix: 尝试修改依赖版本 2023-10-09 17:40:37 +08:00
chenjinsong
24e549a444 fix: 尝试修改依赖版本 2023-10-09 17:28:23 +08:00
chenjinsong
e16d351efd Merge remote-tracking branch 'origin/dev' into dev 2023-10-09 17:01:40 +08:00
chenjinsong
ccec756baa feat: 更改后端端口配置 2023-10-09 17:01:28 +08:00
刘洪洪
a47e04fae2 fix: 尝试修复不识别$t的问题 2023-10-09 16:30:11 +08:00
刘洪洪
8cc6edbdd4 fix: 实体搜索允许通过语言添加俄语 2023-10-09 15:16:48 +08:00
hyx
afa86464d9 CN-1273 自定义library增加tag颜色选择功能 2023-10-09 13:46:28 +08:00
hyx
16e63ab905 CN-1273 自定义library增加tag颜色选择功能 2023-10-09 11:55:19 +08:00
刘洪洪
6f72b2258f fix: 修复实体搜索内容包含非英文时会弹窗提示非法字符串的问题 2023-10-08 17:26:32 +08:00
刘洪洪
5a4301e3aa fix: 实体详情曲线图去掉尾部最多4个0值点 2023-10-08 14:53:44 +08:00
chenjinsong
96c380001c fix: 解决编译问题 2023-10-08 14:25:58 +08:00
chenjinsong
40365decf6 fix: 删除galaxy proxy和chart内容 2023-10-08 14:17:51 +08:00
chenjinsong
ac9c16d464 Merge remote-tracking branch 'origin/dev' into dev 2023-10-08 11:58:22 +08:00
chenjinsong
7abe436127 fix: 更新package-lock.json 2023-10-08 11:58:11 +08:00
唐浩
d85e2d347f Update .gitlab-ci.yml file 2023-10-07 09:18:47 +00:00
chenjinsong
cc61cbc05c fix: 修复networkoverview line去掉尾部4个0值的代码导致测试用例不通过的问题 2023-10-07 10:33:13 +08:00
刘洪洪
7cc2439dd2 fix: 去除无效打印 2023-09-28 14:07:34 +08:00
刘洪洪
8ba062054c CN-1363 fix: 修复单引号内不能包含逗号的问题,以及语句中引号内包含逗号后添加连接has函数的逻辑 2023-09-28 14:05:41 +08:00
陈劲松
cdb1d4baec Merge branch 'cherry-pick-fc56a1fc' into 'dev'
fix: networkoverview的曲线图去掉尾部最多4个0值点

See merge request cyber-narrator/cn-ui!41
2023-09-28 05:25:06 +00:00
chenjinsong
1886f8214f fix: networkoverview的曲线图去掉尾部最多4个0值点
(cherry picked from commit fc56a1fc0c)
2023-09-28 13:25:00 +08:00
陈劲松
aaaecfd9a1 Merge branch 'cherry-pick-ccd2b3d0' into 'dev'
fix: 修复实体列表流量速率显示为横杠的问题

See merge request cyber-narrator/cn-ui!40
2023-09-28 02:02:24 +00:00
chenjinsong
9ab00df342 fix: 修复实体列表流量速率显示为横杠的问题
(cherry picked from commit ccd2b3d08b)
2023-09-28 10:02:18 +08:00
陈劲松
33293aaaa3 Merge branch 'cherry-pick-60bbb8b1' into 'dev'
fix: 修复实体列表实体属性为空串时,不显示'-'的问题

See merge request cyber-narrator/cn-ui!39
2023-09-27 11:54:10 +00:00
chenjinsong
5cb105784a fix: 修复实体列表实体属性为空串时,不显示'-'的问题
(cherry picked from commit 60bbb8b165)
2023-09-27 19:54:02 +08:00
刘洪洪
4e0e49585e CN-1362 fix: 更改折线图隐藏Tab的逻辑 2023-09-27 18:40:16 +08:00
157 changed files with 15263 additions and 28043 deletions

View File

@@ -39,9 +39,9 @@ build_project:
stage: build_project
script:
- echo "npm install ..."
- cnpm install --save-dev --unsafe-perm
- npm install --save-dev --unsafe-perm
- echo "npm run build"
- cnpm run build
- npm run build
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
when: on_success

27006
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,63 +10,64 @@
"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
},
"dependencies": {
"@amcharts/amcharts4": "^4.10.24",
"@amcharts/amcharts4": "~4.10.0",
"@amcharts/amcharts4-geodata": "^4.1.20",
"@antv/g6": "^4.8.17",
"axios": "^0.21.1",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-lodash": "~3.3.0",
"codemirror": "^5.65.1",
"core-js": "^3.6.5",
"core-js": "~3.31.0",
"dayjs": "^1.10.5",
"dexie": "^3.2.2",
"dexie": "~3.2.0",
"echarts": "^5.1.1",
"element-plus": "^1.0.2-beta.44",
"element-plus": "~1.0.2-beta.71",
"lib-flexible": "^0.3.2",
"lodash": "^4.17.21",
"mockjs": "^1.1.0",
"moment-timezone": "^0.5.33",
"node-sass": "^4.14.1",
"postcss-plugin-px2rem": "^0.8.1",
"node-sass": "~4.14.0",
"postcss-plugin-px2rem": "~0.8.1",
"postcss-px2rem-exclude": "0.0.6",
"sass-loader": "^8.0.2",
"sass-resources-loader": "^2.2.1",
"sass-loader": "~8.0.2",
"sass-resources-loader": "~2.2.5",
"tiny-emitter": "^2.1.0",
"vue": "^3.0.0",
"vue": "~3.3.0",
"vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.1.6",
"vue-router": "^4.0.8",
"vuex": "^4.0.1"
"vue-i18n": "~9.2.0",
"vue-router": "~4.2.0",
"vuex": "~4.1.0"
},
"devDependencies": {
"@babel/cli": "^7.12.1",
"@babel/core": "^7.11.4",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-private-methods": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.1",
"@babel/plugin-proposal-private-property-in-object": "^7.12.1",
"@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.10.4",
"@babel/cli": "~7.22.0",
"@babel/core": "~7.22.5",
"@babel/plugin-proposal-class-properties": "~7.18.0",
"@babel/plugin-proposal-private-methods": "~7.18.0",
"@babel/plugin-transform-runtime": "~7.22.0",
"@babel/plugin-proposal-private-property-in-object": "~7.21.0",
"@babel/preset-env": "~7.22.0",
"@babel/preset-typescript": "~7.22.0",
"@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.2",
"@highlightjs/vue-plugin": "^2.0.1",
"@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^6.0.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/user-event": "^14.4.3",
"@testing-library/vue": "^6.4.2",
"@types/jest": "^26.0.24",
"@testing-library/jest-dom": "~5.16.0",
"@testing-library/user-event": "~14.4.0",
"@testing-library/vue": "~6.6.0",
"@types/jest": "~26.0.0",
"@types/lodash": "^4.14.161",
"@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1",
"@vue/babel-plugin-jsx": "^1.0.0",
"@vue/babel-preset-app": "^5.0.8",
"@vue/babel-plugin-jsx": "~1.1.0",
"@vue/babel-preset-app": "~5.0.0",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"@vue/component-compiler-utils": "^3.2.0",
"@vue/test-utils": "^2.2.7",
"@vue/compiler-sfc": "~3.3.0",
"@vue/component-compiler-utils": "~3.3.0",
"@vue/test-utils": "~2.4.0",
"@vue/server-renderer": "~3.3.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.0.0",
"compression-webpack-plugin": "^8.0.1",
@@ -77,11 +78,10 @@
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-vue": "^7.7.0",
"jest": "^26.0.0",
"ts-jest": "^26.4.4",
"jest": "~26.6.0",
"ts-jest": "~26.5.0",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-jest": "^5.0.0-alpha.10",
"vue3-ace-editor": "^2.0.2"
"vue-jest": "^5.0.0-alpha.10"
},
"browserslist": [
"> 1%",

View File

@@ -1,5 +1,20 @@
const BASE_CONFIG = {
baseUrl: 'http://192.168.44.54:8091/',
version: '23.06',
baseUrl: 'http://192.168.44.54:8090/',
version: '23.10',
apiVersion: 'v1'
}
// 默认时间过滤条件,单位分钟. 0表示请求接口时不传时间参数
const DEFAULT_TIME_FILTER_RANGE = {
dashboard: 60,
entity: {
list: 60,
trafficLine: 60,
informationAggregation: 0,
relatedEntity: 60 * 24 * 7,
openPort: 60 * 24 * 7,
securityEvent: 60 * 24 * 7,
performanceEvent: 60 * 24 * 7,
behaviorPattern: 60 * 24 * 7
},
detection: 60
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -54,7 +54,8 @@ export default {
return {
loading: false,
username: '',
pin: ''
pin: '',
language: ''
}
},
methods: {
@@ -75,6 +76,9 @@ export default {
if (!_.isEmpty(res.data.data.user.lang)) {
localStorage.setItem(storageKey.language, res.data.data.user.lang)
}
if (!localStorage.getItem(storageKey.language)) {
localStorage.setItem(storageKey.language, this.language)
}
if (!_.isEmpty(res.data.data.user.theme)) {
localStorage.setItem(storageKey.theme, res.data.data.user.theme)
}
@@ -107,6 +111,7 @@ export default {
})
},
appearanceOut (data) {
this.language = data.lang || defaultLang
if (_.isEmpty(localStorage.getItem(storageKey.language))) {
localStorage.setItem(storageKey.language, data.lang || defaultLang)
}

View File

@@ -3,84 +3,92 @@
height: 100%;
.search__suffixes {
height: 38px;
&.entity-explorer-home {
margin-right: 1px;
color: #3976CB;
&.search__suffixes--text-mode, &.search__suffixes--tag-mode {
.search__suffix:last-of-type {
width: unset;
height: unset;
margin-right: 12px;
background-color: transparent;
.el-icon-search {
color: #3976CB;
}
}
}
}
&.search__suffixes--text-mode, &.search__suffixes--tag-mode {
position: absolute;
display: flex;
top: 10px;
right: 10px;
align-items: center;
top: 1px;
right: 0;
.search__suffix {
// margin-left: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
border-radius: 0 2px 2px 0;
.cn-icon-search-advance, .cn-icon-search-normal, .cn-icon-filter {
color: #A6AAAE;
font-size: 18px;
}
.el-icon-search {
color: #3976CB;
font-size: 22px;
}
&:hover {
cursor: pointer;
}
&:last-of-type {
margin-right: 0;
width: 41px;
height: 38px;
line-height: 39px;
background: #38ACD2;
.el-icon-search {
font-size: 22px;
color: #fff;
}
}
}
.search__suffix-close {
height: 40px;
line-height: 40px;
margin-top: -9px;
.el-icon-error {
font-size: 17px;
color: #C4C4C4;
margin-right: 12px;
cursor: pointer;
}
}
.entity-explorer-search {
color: #3976CB;
margin-top: -2px;
}
.margin-r-12 {
margin-right: 12px;
}
.new-search__suffix {
width: 41px;
height: 41px;
line-height: 41px;
background: #38ACD2;
text-align: center;
margin-top: -10px;
margin-right: -10px;
.el-icon-search {
color: #fff !important;
margin-top: 9px !important;
}
}
.my-popper-class .el-popper__arrow {
display: none;
}
}
&.search__suffixes--tag-mode__block {
background: #fff;
}
}
/*.search-tip--error {
font-size: 14px;
color: #F56C6C;
}*/
}
.advanced-search--show-list .CodeMirror, .advanced-search--show-list .tag-search {
border: none;
.detections {
.tag-search, .CodeMirror {
border: 1px solid #E2E5EC;
}
}
.tag-search {
display: flex;
align-items: center;
height: 40px;
overflow: auto hidden;
border: 1px solid #CECECE;
border-radius: 2px;
padding-left: 10px;
padding-right: 80px;
background-color: white;
border: 1px solid #DEDEDE;
&::-webkit-scrollbar {
width: 8px;
@@ -104,6 +112,7 @@
border-radius: 1px;
cursor: pointer;
transition: all linear .1s;
margin-right: 30px;
&:hover {
background-color: white;
@@ -167,6 +176,9 @@
}
}
}
.entity__search .tag-search {
padding-left: 65px;
}
.el-popover.my-popper-class {
width: auto !important;

View File

@@ -13,11 +13,16 @@
color: #ccc;
}
}
.entity__search {
.CodeMirror {
padding-left: 60px;
}
}
/* PADDING */
.CodeMirror-lines {
padding: 11px 5px; /* Vertical padding around content */
padding: 9px 5px; /* Vertical padding around content */
}
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {

View File

@@ -5,6 +5,7 @@
&>div {
height: 100%;
}
overflow-y: auto;
}
.cn-header {

View File

@@ -83,6 +83,71 @@
.entity-detail-event-block {
width: calc(100% - 2px);
.behavior-pattern {
height:100% ;
position: relative;
display:flex;
flex-direction: row;
.behavior-pattern-legend {
display:flex;
flex-direction: column;
justify-content: center;
height: 100%;
padding:10px 18px 10px 18px;
width:400px;
.behavior-pattern-legend__item {
display:flex;
flex-direction: row;
font-size: 12px;
color: #353636;
line-height: 12px;
margin-bottom:11px;
.legend-icon {
width: 8px;
height: 8px;
margin: 3px 8px 0 0;
border-radius: 1px;;
}
.legend-name {
width:180px;
font-weight: 400;
}
.legend-value{
display: flex;
justify-content: left;
margin-left:20px;
width:90px;
font-weight: 500;
}
.legend-percent {
margin-left:20px;
width:70px;
justify-content: left;
display: flex;
font-weight: 500;
}
}
}
.behavior-pattern-chart{
height: calc(100% - 50px);
width:calc(100% - 400px);
position: relative
}
.chart-bottom-dot__left {
position: absolute;
height: 0;
width: 195px;
border-bottom: 1px dashed #d3d3d3;
top: 319px;left: 575px;
}
.chart-bottom-dot__right {
position: absolute;
height: 0;
width: 195px;
border-bottom: 1px dashed #d3d3d3;
top: 319px;left: 830px;
}
}
}
.entity-detail-event-error {

View File

@@ -128,24 +128,25 @@ $blue: #046ECA;
}
}
}
.link-statistical-dimension {
.row-dot {
margin-top: 5px;
margin-right: 5px;
}
.row-dot {
margin-top: 5px;
margin-right: 5px;
}
.green-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #749F4D;
}
.green-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #749F4D;
}
.red-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #E26154;
.red-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #E26154;
}
}
.item-popover-up, .item-popover-down {

View File

@@ -15,8 +15,7 @@
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20px;
padding: 0 20px 20px;
padding: 20px;
.panel__title {
font-size: 24px;

View File

@@ -31,8 +31,12 @@
line-height: 16px;
margin-right: 10px;
.block-mode-icon1 {
font-size: 14px;
}
.block-mode-icon {
font-size: 16px;
font-size: 15px;
}
}
@@ -190,12 +194,13 @@
}
}
.form-setting__btn, .form-setting__btn1 {
.form-setting__btn, .form-setting__btn1, .policy-form__footer__btn {
width: 100%;
display: flex;
justify-content: flex-end;
.el-button {
width: 80px !important;
height: 30px !important;
min-height: 30px !important;
line-height: 30px !important;
@@ -208,9 +213,25 @@
font-weight: 500;
padding: 0 14px;
}
}
.form-setting__btn1 {
.btn1 {
margin-right: 10px;
.el-button {
background: #F5F6F7;
border: 1px solid rgba(215,215,215,1);
color: #353636;
}
}
}
.policy-form__footer__btn {
justify-content: center;
margin-top: 8px;
.btn1 {
margin-right: 16px;
}
}
.form-setting__btn1, .policy-form__footer__btn {
.el-button {
padding: 0 11px !important;
}

View File

@@ -10,9 +10,9 @@
}
.detection-form-content {
height: 100%;
height: calc(100% - 92px);
overflow: scroll;
padding-bottom: 40px;
padding-bottom: 20px;
.detection-form-collapse {
margin-top: 20px;
@@ -101,6 +101,12 @@
justify-content: flex-start;
height: 24px;
line-height: 24px;
.policy-form-item {
.el-form-item__error {
width: 260px;
}
}
}
.el-input--mini .el-input__inner {
@@ -124,6 +130,12 @@
}
}
}
.policy-form-trigger {
.el-collapse-item__content {
padding-bottom: 0 !important;
}
}
}
.el-input--mini, .el-input--mini .el-input__inner {
@@ -137,4 +149,13 @@
padding-bottom: 20px;
}
.policy-form__footer {
width: calc(100% + 40px);
height: 60px;
margin-left: -20px;
box-shadow: 0 -1px 4px 0 rgba(0,0,0,0.10);
display: flex;
align-items: center;
justify-content: center;
}
}

View File

@@ -15,7 +15,7 @@
background-color: rgba(0, 0, 0, 0.16) !important;
}
.detection-drawer-title, .basic-function-value, basic-description-value {
.detection-drawer-title, .basic-function-value, basic-description-value, .drawer-trigger-minutes {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #717171;
@@ -34,6 +34,15 @@
.drawer-basic-id {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
i {
font-size: 14px;
font-weight: 400;
cursor: pointer;
}
}
}
}
@@ -58,6 +67,10 @@
color: #046ECA;
}
.drawer-trigger-minutes {
color: #353636;
}
.detection-drawer-collapse {
background: #FFFFFF;
border: 1px solid rgba(226, 229, 236, 1);

View File

@@ -2,98 +2,124 @@
display: flex;
flex-direction: column;
width: 280px;
padding: 10px;
margin-right: 10px;
background-color: white;
margin-right: 12px;
overflow: auto;
z-index: 1;
border: 1px solid rgba(226, 229, 236, 1) !important;
border-radius: 4px !important;
.detection-filter {
display: flex;
flex-direction: column;
margin-bottom: 10px;
.filter__header {
display: flex;
flex: 0 0 32px;
align-items: center;
padding-left: 10px;
color: #666;
//background-color: #F3F7FA;
cursor: pointer;
span {
font-size: 14px;
padding-left: 6px;
}
i {
font-size: 12px;
transition: all linear .1s;
transform: rotate(0) translate(0, 2px);
}
i.arrow-rotate {
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 {
padding: 5px 0 0 15px;
.el-checkbox-group {
display: flex;
flex-direction: column;
.el-checkbox {
display: flex;
align-items: center;
padding: 5px 0;
margin-right: 5px;
.el-checkbox__label {
width: 100%;
}
.filter__checkbox-label {
display: flex;
justify-content: space-between;
align-items: center;
.severity-color-block {
width: 4px;
height: 15px;
border-radius: 2px;
}
}
&:last-of-type {
padding-bottom: 0;
}
}
}
}
.filter-case__header {
padding-left: 8px;
height: 36px;
line-height: 36px;
color: #666;
font-size: 14px;
background: #F7F7F7;
box-shadow: 0 1px 0 0 rgba(226,229,236,1);
border-radius: 4px 4px 0 0;
}
.new-detection-filter-title {
display: flex;
flex: 0 0 32px;
align-items: center;
padding-left: 27px;
background-color: #EFF2F5;
cursor: pointer;
.filter__header {
height: 46px;
line-height: 46px;
margin: 0 20px;
font-size: 14px;
color: #353636;
font-weight: 600;
margin: -10px;
margin-bottom: 10px;
font-weight: 500;
}
.filter__body {
width: calc(100% - 30px);
margin: 0 10px 0 20px;
max-height: 265px;
overflow-y: scroll;
overflow-x: hidden;
.filter__body-item {
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
.filter__body-item-left {
display: flex;
align-items: center;
font-size: 14px;
color: #353636;
font-weight: 400;
.filter__body-item-left-index {
width: 16px;
height: 16px;
text-align: center;
background: #EFF2F5;
border-radius: 2px;
margin-right: 6px;
font-family: NotoSansHans-Black;
font-size: 9px;
color: #96A2B0;
font-weight: 900;
display: flex;
align-items: center;
justify-content: center;
}
.filter__body-item-left-label {
max-width: 180px;
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #353636;
font-weight: 400;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.filter__body-item-right {
flex-shrink: 0;
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: #717171;
font-weight: 400;
margin-right: 10px;
}
}
}
}
.filter-country-flag {
width: 18px;
height: 12px;
margin-right: 6px;
border: 1px solid #E8E8E8;
}
.filter-show-more, .filter-no-show-more {
cursor: pointer;
height: 26px;
line-height: 26px;
margin-left: 20px;
color: #046ECA;
user-select: none; // 禁止文本选中
font-size: 12px;
}
.filter-no-show-more {
cursor: not-allowed;
color: rgba(16, 16, 16, 0.3);
}
.filter-hr {
width: calc(100% - 40px);
margin-left: 20px;
margin-top: 6px;
height: 1px;
background: #EFF2F5;
//background: #000;
}
.new-detection-filter-title {
height: 32px;
line-height: 32px;

View File

@@ -59,6 +59,7 @@
width: 5px;
height: 20px;
border-radius: 12px;
margin-top: 2px;
}
.cn-detection__row {
@@ -88,6 +89,7 @@
margin-left: 12px;
font-size: xx-small;
font-weight: bold;
margin-top: -1px;
}
.circle {
@@ -95,7 +97,7 @@
height: 10px;
border: 2px solid #da5656;
border-radius: 10px;
margin-top: 4px;
margin-top: 1px;
margin-right: 12px;
}
@@ -119,10 +121,12 @@
margin-right: 12px;
}
.detection-event-severity-block {
height: 20px;
line-height: 20px;
font-size: 12px;
color: #046EC9;
font-weight: 500;
padding: 2px 10px;
padding: 0 10px;
background: rgba(56,172,210,0.10);
border: 1px solid #ADC7DB;
box-shadow: 0 2px 4px 0 rgba(51,51,51,0.02);
@@ -145,7 +149,7 @@
flex-direction: row;
flex-wrap: wrap;
.basic-info__item {
.basic-info__item, .basic-info__item1 {
padding-right: 30px;
display: flex;
align-items: center;
@@ -168,6 +172,11 @@
color: #666;
}
}
.basic-info__item1 {
span: {
color: #666;
}
}
}
.show-detail {
@@ -262,3 +271,8 @@
color: #FFD82D !important;
}
}
.detection-list-icon {
margin-top: 1px;
font-size: 16px !important;
}

View File

@@ -60,12 +60,14 @@
font-weight: 400;
}
.row__content {
.row__content, .row__content1 {
display: flex;
//color: #3976CB;
color: #046ECA;
font-weight: 500;
font-size: 14px;
align-items: center;
flex-wrap: wrap;
&.row__content--link {
font-style: italic;
@@ -87,6 +89,21 @@
font-style: italic;
color: #1890FF;
}
.row__content__icon {
font-size: 14px;
margin-right: 5px;
color: #1890FF;
}
.row__content__svg {
font-size: 14px;
margin-right: 7px;
}
}
.row__content1 {
display: block;
padding-right: 50px;
}
}
}
@@ -186,6 +203,7 @@
color: #046ECA;
margin-bottom: 10px;
font-weight: 500;
height: 36px;
}
.timeline__start-time {
font-size: 12px;
@@ -215,13 +233,20 @@
}
}
}
.row__tag {
.row__tag, .row__tag__level {
display: flex;
justify-content: center;
align-items: center;
padding: 0 4px;
//color: white;
}
.row__tag__level {
height: 16px;
font-size: 12px;
font-weight: 400;
color: #fff;
border-radius: 2px;
}
.performance-event-remark {
font-family: NotoSansSChineseRegular;

View File

@@ -61,22 +61,19 @@
.detection-tag-status0 {
font-weight: 500;
font-family: NotoSansHans-Medium;
background: rgba(113, 113, 113, 0.12);
color: #717171;
padding: 0 12px;
padding: 0 10px;
}
.detection-tag-status1 {
font-weight: 500;
font-family: NotoSansHans-Medium;
background: rgba(126, 159, 84, 0.12);
color: #7E9F54;
padding: 0 8px;
padding: 0 10px;
}
.detection-table-library {
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: #046ECA;
font-weight: 400;

View File

@@ -4,6 +4,18 @@
width: 100%;
margin-bottom: 10px;
}
.detections {
.entity__pagination{
bottom: 9px;
height: 40px;
width: 100%;
}
.detections__container {
display: flex;
flex-direction: column;
padding-bottom: 20px;
}
}
.detection__list {
display: flex;
flex-direction: column;

View File

@@ -26,7 +26,7 @@
justify-content: flex-start;
}
.explorer-top-tools, .explorer-detection-top-tools {
.explorer-top-tools, .explorer-detection-top-tools, .explorer-entity-top-tools {
display: flex;
justify-content: flex-end;
align-items: center;
@@ -46,7 +46,10 @@
}
}
}
.explorer-detection-top-tools {
.explorer-entity-top-tools {
width: 100%;
}
.explorer-detection-top-tools, .explorer-entity-top-tools {
display: flex;
justify-content: space-between;
}
@@ -87,6 +90,13 @@
font-size: 14px;
color: #353636;
font-weight: 400;
.entity-hide-entity {
margin-left: 20px;
.el-checkbox__label {
padding-left: 6px;
}
}
}
.explorer-container, .explorer-container-new {
display: flex;

View File

@@ -2,7 +2,7 @@
display: flex;
flex-direction: column;
width: 320px;
margin-right: 20px;
margin-right: 12px;
overflow: auto;
z-index: 1;
border: 1px solid rgba(226, 229, 236, 1) !important;

View File

@@ -349,6 +349,25 @@
.data-score-green {
background: #749F4D;
}
.score-dot {
display: inline-block;
margin-bottom: 2px;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #CBCBCB;
margin-right: 4px;
&.score-dot--red {
background-color: #E26154;
}
&.score-dot--yellow {
background-color: #E5A219;
}
&.score-dot--green {
background-color: #749F4D;
}
}
height:24px;
font-size: 14px;
color: #046ECA;

View File

@@ -79,11 +79,22 @@
.cn-entity__header-title {
margin-right: 10px;
}
.cn-entity__header-tag {
.entity-related-entity {
font-size: 12px;
color: #717171;
cursor: pointer;
margin-right: 6px;
}
}
.entity-row-tag {
display: flex;
margin-left: 6px;
margin-top: 1px;
flex-wrap: wrap;
margin-bottom: -10px;
}
.cn-entity__body {
display: flex;
flex-direction: column;
@@ -98,7 +109,7 @@
flex-direction: row;
flex-wrap: wrap;
.basic-info__item {
.basic-info__item, .basic-info__item1 {
padding-right: 30px;
.item__box {
@@ -161,6 +172,17 @@
color: #666;
}
}
.basic-info__item1 {
span: {
color: #666;
}
span:first-of-type {
color: #666;
}
.row-item-label {
color: #999 !important;
}
}
.row-item-label {
font-family: NotoSansSChineseRegular;
@@ -174,6 +196,26 @@
font-size: 14px;
color: #353636;
font-weight: 400;
.score-dot {
display: inline-block;
margin-bottom: 2px;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #CBCBCB;
margin-right: 4px;
&.score-dot--red {
background-color: #E26154;
}
&.score-dot--yellow {
background-color: #E5A219;
}
&.score-dot--green {
background-color: #749F4D;
}
}
}
}
}

View File

@@ -26,7 +26,8 @@
padding: 0 20px;
&.explorer-search__input-case--question-mark-in-line {
flex-direction: row;
//flex-direction: row;
flex-direction: column;
padding: 0;
.explorer-search__input {
@@ -34,9 +35,8 @@
max-width: unset;
}
.explorer-search__input__border {
border: 1px #DEDEDE solid;
height: 43px;
.explorer-search__input--border .CodeMirror {
border: 1px solid #DEDEDE;
}
}
.search-symbol-inline {
@@ -53,14 +53,14 @@
max-width: 1000px;
height: 40px;
}
.explorer-search__foot {
.explorer-search__foot,.explorer-search__foot-list {
display: flex;
padding-top: 18px;
padding-top: 9px;
width: 100%;
max-width: 1000px;
position: relative;
justify-content: space-between;
font-weight: bold;
justify-content: flex-start;
//font-weight: bold;
.foot__item {
display: flex;
@@ -87,14 +87,15 @@
.search__history {
position: absolute;
top: 6px;
display: flex;
padding: 10px 0 0 0;
flex-direction: column;
width: 100%;
max-width: 1000px;
z-index: 2;
top: 47px;
border: 1px solid rgba(206,206,206,0.20);
//top: 47px;
border: 1px solid rgba(226,229,236,1);
border-radius: 2px;
background-color: white;
@@ -137,8 +138,32 @@
color: #66b1ff;
}
}
.history__items-new {
max-height: 300px;
overflow: auto;
.el-table th,.el-table td {
padding: 6px 0;
border: none !important;
}
.el-table {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #575757;
font-weight: 400;
thead {
font-family: NotoSansHans-Medium;
font-size: 14px;
color: #353636;
font-weight: 500;
}
.cell {
padding-left: 18px;
}
}
}
.clear-all {
padding-left: 30px;
padding-left: 18px;
font-weight: normal;
font-size: 14px;
height: 35px;
@@ -152,6 +177,71 @@
}
}
}
.explorer-search__foot-list {
max-width: 756px;
position: absolute;
left: 196px;
top: 44px;
.search__history {
top: 6px;
max-width: 756px;
margin-left: -196px;
.history__items-new {
max-height: 300px;
overflow: auto;
.el-table th,.el-table td {
padding: 4px 0;
border: none !important;
}
.el-table {
font-size: 12px;
thead {
font-size: 12px;
}
}
.el-table--scrollable-x .el-table__body-wrapper {
overflow-x: hidden;
}
}
}
}
}
}
.highlight__text {
background: #FEECC2;
padding: 0 3px;
}
.highlight__block {
background: #FEECC2;
}
.explorer-search__foot-list .explorer-search__block {
margin-left: -196px;
top: -44px;
}
.explorer-search__foot .explorer-search__block {
margin-left: 0;
top: -40px;
}
.explorer-search__block {
position: absolute;
padding-left: 15px;
height: 39px;
display: flex;
align-items: center;
cursor: pointer;
z-index: 1;
i {
font-size: 20px;
color: #999;
}
.search-dividing-line {
width: 1px;
height: 26px;
background: #DEDEDE;
margin-left: 15px;
margin-top: 2px;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "cn-icon"; /* Project id 2614877 */
src: url('iconfont.woff2?t=1693386443164') format('woff2'),
url('iconfont.woff?t=1693386443164') format('woff'),
url('iconfont.ttf?t=1693386443164') format('truetype');
src: url('iconfont.woff2?t=1699411209748') format('woff2'),
url('iconfont.woff?t=1699411209748') format('woff'),
url('iconfont.ttf?t=1699411209748') format('truetype');
}
.cn-icon {
@@ -13,6 +13,22 @@
-moz-osx-font-smoothing: grayscale;
}
.cn-icon-related:before {
content: "\e640";
}
.cn-icon-indicator-match:before {
content: "\e80c";
}
.cn-icon-threshold:before {
content: "\e80d";
}
.cn-icon-behavior:before {
content: "\e61c";
}
.cn-icon-category-group:before {
content: "\e6c7";
}

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

@@ -17,7 +17,7 @@
size="mini"
v-model="meta.column.label"
ref="columnSelect"
:placeholder="meta.column.label || ''"
:placeholder="meta.column.label || ' '"
@blur="columnBlur(meta, index)"
@change="(value) => selectColumn(value, meta)"
>
@@ -90,8 +90,8 @@
</template>
</div>
<div class="tag-search__add" @click="addCondition">{{$t('entities.advancedSearch.add')}}</div>
<div class="search__suffixes search__suffixes--tag-mode">
<div class="search__suffix" style="margin-right: 12px">
<div class="search__suffixes search__suffixes--tag-mode search__suffixes--tag-mode__block" :class="showList ? '' : 'entity-explorer-home'">
<span class="search__suffix">
<el-popover
popper-class="my-popper-class"
placement="top"
@@ -102,13 +102,13 @@
<i class="cn-icon cn-icon-search-normal" @click="changeMode"></i>
</template>
</el-popover>
</div>
<div v-show="metaList.length>0" class="search__suffix-close" @click="cleanMetaList">
</span>
<span v-show="metaList.length>0" class="search__suffix search__suffix-close" @click="cleanMetaList">
<i class="el-icon-error"></i>
</div>
<div class="search__suffix" :class="showList ? 'new-search__suffix' : 'entity-explorer-search'" @click="search">
</span>
<span class="search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</span>
</div>
</div>
</template>
@@ -202,6 +202,7 @@ export default {
if (this.isCustomized(value)) {
meta.column.type = columnType.fullText
meta.column.label = value
meta.column.isFullText = true
meta.resetOperator()
meta.resetValue()
} else {
@@ -248,6 +249,7 @@ export default {
if (meta.column && meta.column.type === 'fullText') {
meta.operator.value = '='
meta.column.show = false
meta.column.isFullText = true
meta.operator.show = false
const label = JSON.parse(JSON.stringify(meta.column.label))
meta.column.label = parser.getEntityTypeByValue(meta.column.label)
@@ -461,13 +463,21 @@ export default {
if (this.metaList.length > 0) {
const parser = new Parser(this.columnList)
const errorList = parser.validateMeta(this.metaList)
const keywordList = []
this.metaList.forEach(item => {
if (item.column && item.column.isFullText) {
keywordList.push({ type: 'fullText', value: item.value.value })
} else if (item.column && !item.column.isFullText) {
keywordList.push({ type: item.column.type, value: item.value.value })
}
})
if (_.isEmpty(errorList)) {
const strObj = parser.handleMetaListToStr(this.metaList)
const str = strObj.str ? strObj.str : strObj
const str2 = strObj.str2 ? strObj.str2 : strObj
// str为将metaList转成字符串的值str2为地址栏展示的值
const key = parser.handleEntityTypeByStr(str)
this.$emit('search', { ...parser.parseStr(key), str: str2 })
this.$emit('search', { ...parser.parseStr(key), str: str2, keywordList: keywordList })
} else {
this.$message.error(handleErrorTip(errorList[0]))
}
@@ -555,14 +565,17 @@ export default {
const column = this.columnList.find(c => {
return c.label === param.column
})
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
// 不是在首位则删除时顺带删除前一个indexand或or否则顺带删除后一个index
if (metaIndex > 0) {
this.metaList.splice(metaIndex - 1, 2)
} else if (this.metaList.length === 1) {
this.metaList.splice(metaIndex, 1)
} else {
this.metaList.splice(metaIndex, 2)
const obj = this.metaList.find(d => d.column && d.column.label === param.column && d.value && (d.value.value === `'${param.value[0]}'` || d.value.value === param.value[0]))
if (obj) {
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
// 不是在首位则删除时顺带删除前一个indexand或or否则顺带删除后一个index
if (metaIndex > 0) {
this.metaList.splice(metaIndex - 1, 2)
} else if (this.metaList.length === 1) {
this.metaList.splice(metaIndex, 1)
} else {
this.metaList.splice(metaIndex, 2)
}
}
})
},
@@ -622,8 +635,16 @@ export default {
let { q } = this.$route.query
if (q && !this.convertMetaList) {
const parser = new Parser(this.columnList)
if (q.indexOf('+') > -1) {
q = q.replace('+', '')
}
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
q = decodeURI(q)
} else {
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
if (q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
q = decodeURI(q)
}
}
this.metaList = parser.parseStr(q).metaList
}
@@ -645,6 +666,7 @@ export default {
if (item.column && item.column.type === 'fullText') {
item.operator.value = '='
item.column.show = false
item.column.isFullText = true
item.operator.show = false
const label = JSON.parse(JSON.stringify(item.column.label))
item.column.label = parser.getEntityTypeByValue(item.column.label)

View File

@@ -1,9 +1,11 @@
<template>
<textarea
style="text-indent: 65px;"
cols="40"
ref="textSearch"
></textarea>
<div class="search__suffixes search__suffixes--text-mode">
<div class="search__suffix">
<div class="search__suffixes search__suffixes--text-mode" :class="showList ? '' : 'entity-explorer-home'" style="padding-left: 1px">
<span class="search__suffix">
<el-popover
popper-class="my-popper-class"
placement="top"
@@ -11,16 +13,16 @@
:content="$t('entity.switchToAdvancedSearch')"
>
<template #reference>
<i class="cn-icon cn-icon-filter margin-r-12" @click="changeMode"></i>
<i class="cn-icon cn-icon-filter" @click="changeMode"></i>
</template>
</el-popover>
</div>
<div v-show="isCloseIcon" class="search__suffix-close" @click="cleanParams">
</span>
<span v-show="isCloseIcon" class="search__suffix search__suffix-close" @click="cleanParams">
<i class="el-icon-error"></i>
</div>
<div class="search__suffix" :class="showList ? 'new-search__suffix' : 'entity-explorer-search'" @click="search">
</span>
<span class="search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</span>
</div>
</template>
@@ -68,7 +70,7 @@ export default {
mode: {
name: 'sql'
},
placeholder: 'Enter...',
placeholder: '',
lineNumbers: false
})
this.codeMirror.setOption('extraKeys', {
@@ -97,10 +99,19 @@ export default {
if (str) {
const parser = new Parser(this.columnList)
const keyInfo = parser.comparedEntityKey(parser.handleEntityTypeByStr(str))
const metaList = parser.parseStr(_.cloneDeep(str)).metaList
const keywordList = []
metaList.forEach(item => {
if (item.column && item.column.type === columnType.fullText) {
keywordList.push({ type: item.column.type, value: item.column.label })
} else if (item.column && item.column.type === columnType.string) {
keywordList.push({ type: item.column.type, value: item.value.value })
}
})
if (keyInfo.isKey) {
const errorList = parser.validateStr(keyInfo.key)
if (_.isEmpty(errorList)) {
this.$emit('search', { ...parser.parseStr(keyInfo.key), str: str })
this.$emit('search', { ...parser.parseStr(keyInfo.key), str: str, keywordList: keywordList })
} else {
this.$message.error(handleErrorTip(errorList[0]))
}
@@ -223,8 +234,16 @@ export default {
toRaw(this.codeMirror).setValue(this.str)
}
if (q) {
if (q.indexOf('+') > -1) {
q = q.replace('+', '')
}
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
q = decodeURI(q)
} else {
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
if (q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
q = decodeURI(q)
}
}
// 为避免地址栏任意输入导致全查询的q带QUERY解析时不识别导致的语法错误
// 如地址栏输入116.178.222.171此时的q很长刷新界面时需要把q里的116.178.222.171拿出来进行搜索

View File

@@ -6,9 +6,10 @@ import { ElMessage } from 'element-plus'
import i18n from '@/i18n'
const strReg = {
all: /^[\da-zA-Z\s.'><!=-_(),%]$/,
// 需要不限制语言,正则过滤中英日俄语出错实现语言都通过。留个记录观察,后续校验
all: /^[\da-zA-Z\u4E00-\u9FA5\u3040-\u309F\u0800-\u4e00\u0400-\u04FF\u2000-\u206F\s.'><!=-_(),%]$/,
key: /^(?![\d])[\da-zA-Z\s.'-_]$/,
value: /^[\da-zA-Z\s.'-_%]$/
value: /^[\da-zA-Z\u4E00-\u9FA5\u3040-\u309F\u0800-\u4e00\u0400-\u04FF\u2000-\u206F\s.'-_%]$/
}
const operatorList = ['=', ' in ', ' IN ', ' like ', ' LIKE ', 'HAS(', 'has(']
@@ -527,7 +528,9 @@ export default class Parser {
}
// 在单引号里,又在括号里,则遇到逗号、右括号就报错
if (isInBracket && isInApostrophe) {
if ([',', ')'].indexOf(strArr[j]) > -1) {
// if ([',', ')'].indexOf(strArr[j]) > -1) {
// 目前有ip in ('1.1.1.1,2.2.2.2')该情况,后续待验证
if ([')'].indexOf(strArr[j]) > -1) {
errorList.push(new ParserError(j, errorTypes.syntaxError, errorDesc.syntaxError.unclosedApostrophe))
break
}
@@ -709,6 +712,13 @@ export default class Parser {
meta.column.label = token.value
meta.column.type = columnType.fullText
}
} else if (nextToken.type === types.rightBracket) {
// 此处操作为ip='1,2' and has(tag,'222')中 ,'222')的情况
if (prevToken) {
if (prevToken.type === types.comma && token.type === types.commonStr && nextToken.type === types.rightBracket) {
meta.value.value = token.value
}
}
} else {
errorList.push(new ParserError(token.end, errorTypes.syntaxError, errorDesc.syntaxError.unexpectedString))
break
@@ -1163,7 +1173,9 @@ export default class Parser {
const arr = []
// 如果出现this.columnList中的字段如IP\Domain\App\Country等则不进行模糊搜索将str返回出去
this.columnList.forEach(item => {
arr.push(item.label.toLowerCase())
// todo 取消了大小写校验,后续观察是否出现问题
// arr.push(item.label.toLowerCase())
arr.push(item.label)
})
// 因为手动输入时可能会输入and所以将操作符的AND转换为and统一处理
@@ -1173,7 +1185,9 @@ export default class Parser {
newStr = newStr.replace(new RegExp(arr[i], 'g'), arr[i])
}
// 检查str字段在arr中是否出现,true为出现过
const result = arr.some(item => newStr.toLowerCase().includes(item))
// todo 取消了大小写校验,后续观察是否出现问题
// const result = arr.some(item => newStr.toLowerCase().includes(item))
const result = arr.some(item => newStr.includes(item))
if (newStr.indexOf(' and ') > -1) {
// 将单引号包裹的and拿出来放到数组tempList里原来的单引号包裹内容用temp即'it is test keyword{键值}'代替
// 再将字符串用and转换为数组遍历数组发现值为temp的获取键值根据键值获取tempList的值组合起来
@@ -1184,7 +1198,7 @@ export default class Parser {
// 将单引号包裹的and内容集合起来
while ((match = regex.exec(newStr)) !== null) {
if (match[1].includes('and')) {
if (match[1].includes(' and ')) {
tempList.push(match[1])
}
}
@@ -1224,7 +1238,8 @@ export default class Parser {
// 不区分大小写用this.columnList里的label
arr.forEach(item => {
if (str.toLowerCase().indexOf(item.toLowerCase()) > -1) {
str = str.replace(new RegExp(item, 'gi'), item)
// todo 记录一下此处取消了转小写转换,后续搜索验证
// str = str.replace(new RegExp(item, 'gi'), item)
if (!operatorList.some(ite => str.includes(ite)) && str.toLowerCase() !== item.toLowerCase()) {
str = this.checkFormatByStr(str)
}
@@ -1399,8 +1414,8 @@ export default class Parser {
} else if (i.toLowerCase() === 'tag') {
lastObj[i] = `has(${i},${commonObj[i]})`
} else {
// 单独存在的,直接保留
lastObj[i] = `${i} = '${commonObj[i]}'`
// 单独存在的,直接保留 todo 后续观察当初添加单引号动机和问题
lastObj[i] = `${i} = ${commonObj[i]}`
}
}

View File

@@ -1,35 +1,39 @@
<template>
<div class="pagination" >
<el-pagination
ref="page"
@size-change="size"
@prev-click="prev"
@next-click="next"
@current-change="current"
:current-page="pageObj.pageNo"
:page-sizes="pageSizes?pageSizes:[20, 50, 100]"
:page-size="Number(pageObj.pageSize)"
:layout="layout"
:total="pageObj.total"
v-bind="bind"
>
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
:popper-class="popClass" @visible-change="popperVisible">
<el-option v-for="(item, index) in pageSizes" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
<el-config-provider :locale="locale">
<el-pagination
ref="page"
@size-change="size"
@prev-click="prev"
@next-click="next"
@current-change="current"
:current-page="pageObj.pageNo"
:page-sizes="pageSizes?pageSizes:[20, 50, 100]"
:page-size="Number(pageObj.pageSize)"
:layout="layout"
:total="pageObj.total"
v-bind="bind"
>
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
:popper-class="popClass" @visible-change="popperVisible">
<el-option v-for="(item, index) in pageSizes" :key="index" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-pagination>
</el-pagination>
</el-config-provider>
</div>
</template>
<script>
import { defaultPageSize, storageKey } from '@/utils/constants'
import { defaultPageSize, storageKey, ZH, EN } from '@/utils/constants'
import { urlParamsHandler, overwriteUrl } from '@/utils/tools'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { parseInt } from 'lodash'
import ElConfigProvider from 'element-plus'
import cn from 'element-plus/lib/locale/lang/zh-cn'
import en from 'element-plus/lib/locale/lang/en'
export default {
name: 'pagination',
@@ -60,9 +64,15 @@ export default {
const { query } = useRoute()
const pageSize = ref(defaultPageSize)
const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1))
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
let locale = en
if (language === ZH) {
locale = cn
}
return {
pageSize,
currentPageNo
currentPageNo,
locale
}
},
data () {

View File

@@ -18,28 +18,30 @@
<div v-if="dropdownFlag" class="date-range-panel">
<el-row class="date-range-panel-top" style="position: relative">
<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>
<el-date-picker
v-model="newDateValue"
ref="newDatePicker"
popper-class="my-date-picker"
style="position: absolute;top: -53px;left: -536px;"
:clearable="false"
:default-time="defaultTime"
:unlink-panels="true"
type="datetimerange"
@change="timeArrChange"
/>
<div class="content-title">From</div>
<div class="date-range-title" style="padding-left: 0">{{$t('dateTime.absoluteTimeRange')}}</div>
<el-config-provider :locale="locale">
<el-date-picker
v-model="newDateValue"
ref="newDatePicker"
popper-class="my-date-picker"
style="position: absolute;top: -53px;left: -536px;"
:clearable="false"
:default-time="defaultTime"
:unlink-panels="true"
type="datetimerange"
@change="timeArrChange"
/>
</el-config-provider>
<div class="content-title">{{$t('dateTime.from')}}</div>
<div @click="myDatePickerShow" tabindex="1" class="content-input">
{{ dateFormatByAppearance(getMillisecond(myStartTime)) }}
</div>
<div class="content-title">To</div>
<div class="content-title">{{$t('dateTime.to')}}</div>
<div @click="myDatePickerShow" tabindex="2" class="content-input">
{{ dateFormatByAppearance(getMillisecond(myEndTime)) }}
</div>
<div class="date-range-title" style="padding-left: 0">Recently used absolute ranges</div>
<div class="date-range-title" style="padding-left: 0">{{$t('dateTime.recentlyUsedRanges')}}</div>
<div class="date-range-history">
<div v-for="(item, index) in rangeHistoryArr" :key="index" class="date-range-history-item"
@click="historyChange(item)">
@@ -53,7 +55,7 @@
: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">{{$t('dateTime.relativeTimeRanges')}}</div>
<ul class="date-range-item">
<li v-for="item in dateRangeArr"
@click="quickChange(item.value)"
@@ -79,9 +81,12 @@
<script>
import { ref, computed, watch, reactive } from 'vue'
import { storageKey } from '@/utils/constants'
import { EN, storageKey, ZH } from '@/utils/constants'
import { getMillisecond, millTimestampDiffFromTz, timestampToList } from '@/utils/date-util'
import { useStore } from 'vuex'
import ElConfigProvider from 'element-plus'
import cn from 'element-plus/lib/locale/lang/zh-cn'
import en from 'element-plus/lib/locale/lang/en'
export default {
name: 'DateTimeRange',
@@ -105,6 +110,31 @@ export default {
}
},
emits: ['change'],
data () {
return {
dateRangeArr: [
{ value: 5, name: this.$t('dateTime.last5Mins') }, // 'last 5 mins'
{ value: 15, name: this.$t('dateTime.last15Mins') },
{ value: 30, name: this.$t('dateTime.last30Mins') },
{ value: 60, name: this.$t('dateTime.last1Hour') }, // dateTime.last1Hour
{ value: 180, name: this.$t('dateTime.last3Hours') },
{ value: 360, name: this.$t('dateTime.last6Hours') },
{ value: 720, name: this.$t('dateTime.last12Hours') },
{ value: 1440, name: this.$t('dateTime.last1Day') }, // dateTime.last2Days
{ value: 2880, name: this.$t('dateTime.last2Days') }
]
}
},
computed: {
showDetail () {
let str = ''
if (this.dateRangeValue !== -1) {
const rangeItem = this.dateRangeArr.find(item => item.value === this.dateRangeValue)
str = rangeItem ? rangeItem.name : this.dateRangeArr[0].name
}
return str
}
},
setup (props, ctx) {
// data
const store = useStore()
@@ -124,24 +154,12 @@ export default {
const rangeHistory = ref(localStorage.getItem(storageKey.dataRangeHistory) ? JSON.parse(localStorage.getItem(storageKey.dataRangeHistory)) : [])
const dateRangeValue = props.dateRange ? ref(props.dateRange) : ref(60)
const isCustom = ref(dateRangeValue.value === -1)
const dateRangeArr = [
{ value: 5, name: 'last 5 mins' },
{ value: 15, name: 'last 15 mins' },
{ value: 30, name: 'last 30 mins' },
{ value: 60, name: 'last 1 hour' },
{ value: 180, name: 'last 3 hours' },
{ value: 360, name: 'last 6 hours' },
{ value: 720, name: 'last 12 hours' },
{ value: 1440, name: 'last 1 day' },
{ value: 2880, name: 'last 2 days' }
]
const dropdownFlag = ref(false)
// 默认日历选择时间即开始时间YYYY-MM-DD 00:00:00,结束时间YYYY-MM-DD 59:59:59
const defaultTime = ref([
new Date(2023, 1, 1, 0, 0, 0),
new Date(2023, 1, 2, 23, 59, 59)
])
// computed
const utcStr = computed(() => {
let str = 'UTC '
@@ -159,13 +177,6 @@ export default {
str += ':00 '
return str
})
const showDetail = computed(() => {
let str = ''
if (dateRangeValue.value !== -1) {
str = dateRangeArr.find(item => item.value === dateRangeValue.value).name
}
return str
})
const rangeHistoryArr = rangeHistory
// refs
@@ -254,12 +265,14 @@ export default {
const returnValue = () => {
store.commit('setTimeFilter', { startTime: myStartTime.value, endTime: myEndTime.value, range: dateRangeValue.value })
cancelHttp()
const obj = rangeHistory.value.find(d => d.start === myStartTime.value && d.end === myEndTime.value)
if (!obj) {
rangeHistory.value.unshift({
start: myStartTime.value,
end: myEndTime.value
})
if (rangeHistory.value[0]) {
const d = rangeHistory.value[0]
if (d.start !== myStartTime.value || d.end !== myEndTime.value) {
rangeHistory.value.unshift({
start: myStartTime.value,
end: myEndTime.value
})
}
}
if (!rangeHistory.value[0]) {
rangeHistory.value.unshift({
@@ -287,6 +300,12 @@ export default {
})
}
}
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
let locale = en
if (language === ZH) {
locale = cn
}
return {
myStartTime,
myEndTime,
@@ -295,13 +314,11 @@ export default {
utcStr,
rangeEchartsData,
address,
dateRangeArr,
defaultTime,
dateRangeValue,
isCustom,
newDateValue,
newDatePicker,
showDetail,
rangeHistory,
rangeHistoryArr,
getMillisecond,
@@ -311,7 +328,8 @@ export default {
timeArrChange,
returnValue,
quickChange,
historyChange
historyChange,
locale
}
}
}

View File

@@ -58,7 +58,7 @@
<script>
import axios from 'axios'
import { storageKey } from '@/utils/constants'
import { storageKey, EN } from '@/utils/constants'
export default {
name: 'TopToolMoreOptions',
@@ -108,7 +108,7 @@ export default {
if (this.paramsType) {
form.append('type', this.paramsType)
}
form.append('language', localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : 'en')
form.append('language', localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : EN)
axios.post(this.importUrl, form, { 'Content-Type': 'multipart/form-data' }).then(response => {
if (response.status === 200 && response.data.msg === 'success') {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.importSuccess') })
@@ -128,7 +128,7 @@ export default {
this.importFile = null
},
downloadTemplate () {
const language = localStorage.getItem(storageKey.language) || 'en' // 初始未选择默认 en 英文
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
const fileName = this.exportFileName + '-' + this.$t('overall.template') + '-' + this.getTimeString() + '.json'
let url = null
@@ -159,7 +159,7 @@ export default {
})
}
params.pageSize = -1
params.language = localStorage.getItem(storageKey.language) || 'en'
params.language = localStorage.getItem(storageKey.language) || EN
this.export(this.exportUrl, params, this.exportFileName + '-' + this.getTimeString() + '.json')
this.closeDialog()

View File

@@ -13,12 +13,12 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<div id="header-to-english" :style="language === 'en'?'color:#0091ff':''" @click="changeLocal('en')">
<div id="header-to-english" :style="language === EN?'color:#0091ff':''" @click="changeLocal(EN)">
English
</div>
</el-dropdown-item>
<el-dropdown-item>
<div id="header-to-chinese" :style="language === 'cn'?'color:#0091ff':''" @click="changeLocal('cn')">
<div id="header-to-chinese" :style="language === ZH?'color:#0091ff':''" @click="changeLocal(ZH)">
中文
</div>
</el-dropdown-item>
@@ -42,11 +42,11 @@
</div>
<div class="cn-header__nav">
<i class="cn-icon cn-icon-a-NetworkAnalytics"></i>
<el-breadcrumb class="header__left-breadcrumb" separator=">">
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`" :title="item.value"
<el-breadcrumb class="header__left-breadcrumb" separator=">" v-if="route.startsWith('/panel')">
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`" :title="index===3?item.value:''"
v-for="(item,index) in breadcrumb" :key="item.value">
<template v-if="index===3">
<div class="header__left-breadcrumb-item-select">
<template v-if="index===3" >
<div class="header__left-breadcrumb-item-select" >
<el-popover placement="bottom-start"
ref="breadcrumbPopover"
:show-arrow="false"
@@ -99,7 +99,7 @@
</div>
</template>
<template v-else-if="index===2">
<span v-if="route===wholeScreenRouterMapping.dns">{{ $t(item.value) }}</span>
<span v-if="route===wholeScreenRouterMapping.dns || !route.startsWith('/panel')">{{ $t(item.value) }}</span>
<span v-else class="route-menu" @click="jump(route,item.value,'',3)">{{ $t(item.value) }}</span>
</template>
<!-- index=0和index=1的点击跳转由breadcrumb里的数据控制 -->
@@ -113,6 +113,13 @@
</template>
</el-breadcrumb-item>
</el-breadcrumb>
<el-breadcrumb class="header__left-breadcrumb" separator=">" v-else>
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`"
v-for="(item,index) in breadcrumb" :key="item.value">
<span v-if="item.clickable" class="route-menu" @click="jumpOther(item.route,index)">{{ item.value }}</span>
<span v-else>{{ item.value }}</span>
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 菜单 -->
@@ -196,7 +203,9 @@ import {
networkTable,
operationType,
storageKey,
wholeScreenRouterMapping
wholeScreenRouterMapping,
ZH,
EN
} from '@/utils/constants'
import { api } from '@/utils/api'
import { ref } from 'vue'
@@ -234,7 +243,7 @@ export default {
return {
username: localStorage.getItem(storageKey.username),
nickName: localStorage.getItem(storageKey.nickName),
language: localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : 'en',
language: localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : EN,
showChangePin: false,
from: '', // entity类型
changePassForm: {
@@ -296,7 +305,9 @@ export default {
curTabState: curTabState,
urlChangeParams: {},
wholeScreenRouterMapping,
logo: 'images/logo-header.svg'
logo: 'images/logo-header.svg',
ZH,
EN
}
},
computed: {
@@ -304,7 +315,7 @@ export default {
return this.$store.getters.menuList.find(menu => menu.code === 'networkAnalytics')
},
otherMenu () {
return this.$store.getters.menuList.filter(menu => ['networkAnalytics', 'chart', 'I18N', 'entityDetail', 'temp', 'entityGraph', 'detectionPolicy'].indexOf(menu.code) === -1)
return this.$store.getters.menuList.filter(menu => ['networkAnalytics', 'I18N', 'entityDetail', 'entityGraph', 'detectionPolicy'].indexOf(menu.code) === -1)
/* function excludeButton (menu) {
for (let i = 0; i < menu.length; i++) {
@@ -322,15 +333,17 @@ export default {
breadcrumb () {
const breadcrumb = []
this.generateBreadcrumb(breadcrumb, this.$store.getters.menuList)
// 写死一级和二级菜单是否可以点击跳转
if (breadcrumb[0]) {
if (['knowledgeBase'].indexOf(breadcrumb[0].code) > -1) {
breadcrumb[0].clickable = true
}
if (breadcrumb[1]) {
if (breadcrumb[1].route && breadcrumb[1].route.indexOf('/panel/') === 0) {
breadcrumb[1].clickable = true
}
if (breadcrumb) {
// panel菜单是否可以点击跳转一级菜单不可点击二级菜单可以点击
if (breadcrumb[0] && breadcrumb[1] && breadcrumb[1].route &&
breadcrumb[1].route.indexOf('/panel/') === 0) {
breadcrumb[1].clickable = true
} else { // 除panel外的菜单是否可以点击跳转:除了新增、编辑,其它均可点击
breadcrumb.forEach(item => {
if (item.value !== 'Create' && item.value !== 'Edit') {
item.clickable = true
}
})
}
}
@@ -366,7 +379,7 @@ export default {
},
async breadcrumb (n) {
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
if (this.dnsQtypeMapData.size === 0) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
}
@@ -384,7 +397,7 @@ export default {
async mounted () {
this.from = Object.keys(this.entityType)[0]
// 是否需要dns的qtype和rcode的数据字典
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
if (this.dnsQtypeMapData.size === 0) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
}
@@ -392,7 +405,7 @@ export default {
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
}
}
let path = this.$route.path;
const path = this.$route.path
if (path.indexOf('panel') > -1 && path.indexOf('linkMonitor') === -1) {
await this.initDropdownList()
}
@@ -405,10 +418,10 @@ export default {
const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
const dateRangeValue = rangeParam ? parseInt(rangeParam) : 60
const chartTimeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(60)
const { startTime, endTime } = getNowTime(dateRangeValue)
chartTimeFilter.value.startTime = startTime
chartTimeFilter.value.endTime = endTime
} else {
@@ -423,55 +436,6 @@ export default {
},
methods: {
generateBreadcrumb (breadcrumb, menus) {
if (this.route === '/entityDetail') {
const entityMenu = menus.find(m => m.route === '/entityExplorer')
const entityDetailMenu = menus.find(m => m.route === '/entityDetail')
breadcrumb.push({
code: entityMenu.code,
value: entityMenu.i18n ? this.$t(entityMenu.i18n) : entityMenu.name,
route: entityMenu.route,
type: entityMenu.type
})
breadcrumb.push({
code: entityDetailMenu.code,
value: entityDetailMenu.i18n ? this.$t(entityDetailMenu.i18n) : entityDetailMenu.name,
route: entityDetailMenu.route,
type: entityDetailMenu.type
})
return true
} else if (this.route === '/entityGraph') {
const entityMenu = menus.find(m => m.route === '/entityExplorer')
const entityGraphMenu = menus.find(m => m.route === '/entityGraph')
breadcrumb.push({
code: entityMenu.code,
value: entityMenu.i18n ? this.$t(entityMenu.i18n) : entityMenu.name,
route: entityMenu.route,
type: entityMenu.type
})
breadcrumb.push({
code: entityGraphMenu.code,
value: entityGraphMenu.i18n ? this.$t(entityGraphMenu.i18n) : entityGraphMenu.name,
route: entityGraphMenu.route,
type: entityGraphMenu.type
})
return true
} else if (this.route === '/detectionsNew') {
const detectionMenu = menus.find(m => m.route === '/detection')
const policyMenu = menus.find(m => m.route === '/detectionsNew')
breadcrumb.push({
code: detectionMenu.code,
value: detectionMenu.i18n ? this.$t(detectionMenu.i18n) : detectionMenu.name,
route: detectionMenu.route,
type: detectionMenu.type
})
breadcrumb.push({
code: policyMenu.code,
value: policyMenu.i18n ? this.$t(policyMenu.i18n) : policyMenu.name,
route: policyMenu.route,
type: policyMenu.type
})
return true
}
const menu = menus.find(m => m.route === this.route)
if (menu) {
breadcrumb.unshift({
@@ -523,7 +487,7 @@ export default {
},
getCurTabByLabel (label) {
let curTab = null
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
if (curTableInCode && curTableInCode.tabList) {
curTab = curTableInCode.tabList.find(item => item.label === label)
@@ -536,7 +500,7 @@ export default {
const currentValue = document.getElementById('breadcrumbValue') ? document.getElementById('breadcrumbValue').innerText : ''
const columnName = this.getUrlParam(this.curTabState.thirdMenu, '')
let type = 'ip'
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
if (curTableInCode && curTableInCode.tabList) {
const curTab = curTableInCode.tabList.find(item => item.label === columnName)
@@ -560,7 +524,7 @@ export default {
axios.get(curTableInCode.url.drilldownList, { params }).then(async response => {
if (response.status === 200) {
this.breadcrumbColumnValueListShow = response.data.data.result
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
if (this.dnsQtypeMapData.size === 0) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
}
@@ -691,7 +655,7 @@ export default {
},
async handleCurDrilldownTableConfig (thirdMenu) {
// const userId = localStorage.getItem(storageKey.userId)
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
const drillDownTableConfigs = await combineDrilldownTableWithUserConfig()
const currentTableConfig = drillDownTableConfigs.find(config => config.route === tableType)
@@ -712,7 +676,26 @@ export default {
}
}
},
// 仅处理除panel外的相关路径的导航
async jumpOther (route, index) {
route = route.replace('redirect:', '')
this.showMenu = false
if (route === this.route && index > 0) { // 当前只有一级菜单时,点击不进行刷新,重新跳转
this.refresh()
return
}
if (route) {
this.$router.push({
path: route,
query: {
t: +new Date()
}
})
}
},
// 仅处理panel相关路径的导航
async jump (route, columnName, columnValue, opeType) {
route = route.replace('redirect:', '')
if (route === '/panel/linkMonitor' && opeType === 3) {
return true
}
@@ -732,7 +715,7 @@ export default {
this.$store.commit('setNetworkOverviewTabList', [])
}
// 清空网络概况的特殊面包屑
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
const curTab = await getDefaultCurTab(tableType, metric, columnName)
this.$store.getters.menuList.forEach(menu => {
@@ -744,11 +727,6 @@ export default {
child.columnName = columnName
this.urlChangeParams[this.curTabState.thirdMenu] = columnName
this.urlChangeParams[this.curTabState.fourthMenu] = columnValue
// const tabObjGroup = networkOverviewTabList.filter(item => item.label == columnName)
// let curTab = this.getCurTabByLabel()
// 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
} else if (columnName) { // 点击的为列名
child.columnValue = ''
@@ -786,6 +764,7 @@ export default {
t: +new Date()
}
})
return
} else if (opeType === 3) {
this.$router.push({
query: {
@@ -794,6 +773,7 @@ export default {
t: +new Date()
}
})
return
} else if (opeType !== 4) {
this.$router.push({
query: {
@@ -803,6 +783,7 @@ export default {
t: +new Date()
}
})
return
}
if (route === this.route) {
this.refresh()

View File

@@ -1,408 +0,0 @@
<template>
<div class="right-box right-box-user">
<div class="right-box__header">
<div class="header__title">{{editObject.id ? $t('config.chart.edit') : $t('config.chart.add')}}</div>
<div class="header__operation">
<span v-cancel="{object: editObject, func: esc}"><i class="cn-icon cn-icon-close"></i></span>
</div>
</div>
<div class="right-box__container">
<div class="container__form">
<el-form ref="chartForm" :model="editObject" :rules="editObject.id ? rules2 : rules" label-position="top" label-width="120px">
<!--name-->
<el-form-item :label="$t('overall.name')" prop="name">
<el-input id="chart-input-name" v-model="editObject.name" :disabled="editObject.name==='admin' && editObject.id === 1"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--i18n-->
<el-form-item :label="$t('config.chart.i18n')" prop="i18n">
<el-input id="chart-input-i18n" v-model="editObject.i18n"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--type-->
<el-form-item :label="$t('config.chart.type')" prop="type">
<el-select id="chart-type"
v-model="editObject.type"
class="right-box__select"
clearable
collapse-tags
placeholder=""
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small"
@change="()=>{ this.$forceUpdate() }">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
<span style="float: left">{{ item.label }}</span><span style="float: right;color: #909399;font-size: 13px;">{{ item.remark }}</span>
</el-option>
</el-select>
</el-form-item>
<!--panel-->
<el-form-item :label="$t('config.chart.panel')" prop="panelId">
<el-select id="chart-input-panelId"
v-model="editObject.panelId"
class="right-box__select"
clearable
collapse-tags
placeholder=""
:disabled="editObject.id?true:false"
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small"
@change="getChartData">
<el-option :key="panelTypeAndRouteMapping.trafficSummary" :label="$t('trafficSummary.trafficSummary')" :value="panelTypeAndRouteMapping.trafficSummary"></el-option>
<el-option :key="panelTypeAndRouteMapping.networkAppPerformance" :label="$t('networkAppPerformance.networkAppPerformance')" :value="panelTypeAndRouteMapping.networkAppPerformance"></el-option>
<el-option :key="panelTypeAndRouteMapping.cryptocurrency" :label="$t('overall.cryptocurrency')" :value="panelTypeAndRouteMapping.cryptocurrency"></el-option>
<el-option :key="panelTypeAndRouteMapping.dnsServiceInsights" :label="$t('dnsServiceInsights.dnsServiceInsights')" :value="panelTypeAndRouteMapping.dnsServiceInsights"></el-option>
<el-option :key="panelTypeAndRouteMapping.ipEntityDetail" :label="$t('entities.ipEntityDetail')" :value="panelTypeAndRouteMapping.ipEntityDetail"></el-option>
<el-option :key="panelTypeAndRouteMapping.domainEntityDetail" :label="$t('entities.domainEntityDetail')" :value="panelTypeAndRouteMapping.domainEntityDetail"></el-option>
<el-option :key="panelTypeAndRouteMapping.appEntityDetail" :label="$t('entities.appEntityDetail')" :value="panelTypeAndRouteMapping.appEntityDetail"></el-option>
</el-select>
</el-form-item>
<!--pid-->
<el-form-item :label="$t('config.chart.pid')" prop="pid">
<el-select id="chart-pid"
v-model="editObject.pid"
class="right-box__select"
clearable
collapse-tags
placeholder=""
:disabled="editObject.id?true:false"
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small"
@change="()=>{ this.$forceUpdate() }">
<template v-for="chart in chartData" :key="chart.id">
<el-option :label="chart.name" :value="chart.id"></el-option>
</template>
</el-select>
</el-form-item>
<!--x-->
<el-form-item :label="$t('config.chart.x')" prop="x">
<el-input id="chart-input-x" v-model="editObject.x"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--y-->
<el-form-item :label="$t('config.chart.y')" prop="y">
<el-input id="chart-input-y" v-model="editObject.y"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--w-->
<el-form-item :label="$t('config.chart.w')" prop="w">
<el-input id="chart-input-x" v-model="editObject.w"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--h-->
<el-form-item :label="$t('config.chart.h')" prop="h">
<el-input id="chart-input-y" v-model="editObject.h"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--params-->
<el-form-item :label="$t('config.chart.params')" prop="params">
<v-ace-editor
v-model:value="editObject.params"
lang="json"
theme="chrome"
style="height: 300px" />
</el-form-item>
<!--remark-->
<el-form-item :label="$t('config.chart.remark')">
<el-input maxlength="1024" show-word-limit :rows="2" size='mini' type="textarea" v-model="editObject.remark" id="chart-box-remark"/>
</el-form-item>
</el-form>
</div>
</div>
<div class="right-box__footer">
<button id="chart-edit-cancel" v-cancel="{object: editObject, func: esc}" class="footer__btn footer__btn--light">
<span>{{$t('overall.cancel')}}</span>
</button>
<button id="chart-edit-save" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
<span>{{$t('overall.save')}}</span>
</button>
</div>
</div>
</template>
<script>
import rightBoxMixin from '@/mixins/right-box'
import axios from 'axios'
import { panelTypeAndRouteMapping, storageKey } from '@/utils/constants'
import { api } from '@/utils/api'
import { VAceEditor } from 'vue3-ace-editor'
import 'ace-builds/src-noconflict/mode-javascript'
import 'ace-builds/src-noconflict/mode-json'
import 'ace-builds/src-noconflict/theme-chrome'
export default {
name: 'ChartBox',
mixins: [rightBoxMixin],
components: {
VAceEditor
},
data () {
return {
url: api.chart,
loginName: localStorage.getItem(storageKey.username),
panelTypeAndRouteMapping: panelTypeAndRouteMapping,
rules: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
type: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
panel_id: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
x: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
y: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
w: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
h: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
]
},
rules2: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
type: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
panel_id: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
x: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
y: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
w: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
],
h: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
]
},
chartData: [],
options: [
{
value: 1,
label: 'map-1',
remark: '流量流向地图-连线'
},
{
value: 2,
label: 'map-2',
remark: '地图-色块'
}, {
value: 3,
label: 'map-3',
remark: '地图-点'
},
{
value: 4,
label: 'map-4',
remark: '地图-点'
}, {
value: 11,
label: 'line-1',
remark: '折线图-常规'
},
{
value: 61,
label: 'table-1',
remark: '表格'
}, {
value: 62,
label: 'table-2',
remark: 'DNS记录'
}, {
value: 63,
label: 'table-3',
remark: '挖矿活跃ip'
}, {
value: 31,
label: 'pie-1',
remark: '饼图-带联动表格'
}, {
value: 51,
label: 'singleValue-1',
remark: '单值图'
},
{
value: 91,
label: 'tab-container',
remark: 'tab标签(容器)'
}, {
value: 92,
label: 'tab-item',
remark: 'tab标签(标签页)'
},
{
value: 93,
label: 'title',
remark: '标题'
}, {
value: 94,
label: 'group',
remark: '组'
},
{
value: 12,
label: 'line-2',
remark: '折线图-带统计'
}, {
value: 52,
label: 'singleValue-2',
remark: '详情单值图'
},
{
value: 53,
label: 'singleValue-3',
remark: '单值图'
}, {
value: 13,
label: 'line-3',
remark: '折线图-堆叠面积'
},
{
value: 21,
label: 'bar-1',
remark: '柱状图'
}, {
value: 22,
label: 'bar-2',
remark: '开放端口'
}, {
value: 23,
label: 'bar-3',
remark: '挖矿事件统计(time类型柱状图)'
}, {
value: 24,
label: 'bar-4',
remark: '矿机所属单位(category类型柱状图)'
},
{
value: 32,
label: 'pie-2',
remark: '饼图-常规'
}, {
value: 33,
label: 'pie-3',
remark: '托管域名'
},
{
value: 34,
label: 'pie-4',
remark: '相关域名'
}, {
value: 42,
label: 'relation-2',
remark: '关系图谱'
},
{
value: 43,
label: 'relation-3',
remark: '访问链路图'
},
{
value: 82,
label: 'base-2',
remark: 'APP基本信息'
}, {
value: 83,
label: 'list-1',
remark: 'Whois'
},
{
value: 84,
label: 'list-2',
remark: 'DNS记录'
},
{
value: 85,
label: 'list-3',
remark: '近期挖矿事件'
}
]
}
},
setup () {
},
mounted () {
if (this.editObject.id) {
this.getChartData(this.editObject.panelId)
}
},
watch: {
'editObject.panelId': function (newValue, oldValue) {
this.editObject.pid = ''
}
},
methods: {
isCurrentUser (username) {
return localStorage.getItem(storageKey.username) === username
},
/* 密码失去焦点 检验确认密码 */
pinBlur () {
if (this.editObject.pin && this.editObject.pinChange) {
this.$refs.chartForm.validateField('pinChange')
}
},
save () {
if (this.blockOperation.save) { return }
this.blockOperation.save = true
this.$refs.chartForm.validate((valid) => {
if (valid) {
if (this.editObject.id) {
axios.put(this.url, this.editObject).then(res => {
this.blockOperation.save = false
if (res.status === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.data.message)
}
})
} else {
axios.post(this.url, this.editObject).then(res => {
this.blockOperation.save = false
if (res.status === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.data.message)
}
})
}
} else {
this.blockOperation.save = false
return false
}
})
},
async getChartData (value) {
await axios.get(api.chart, { params: { panelId: value } }).then(response => {
if (response.status === 200) {
this.chartData = response.data.data.list
}
})
}
}
}
</script>

View File

@@ -1,117 +0,0 @@
<template>
<div class="right-box">
<div class="right-box__header">
<div class="header__title">{{editObject.id ? $t('overall.edit') : $t('overall.new')}}</div>
<div class="header__operation">
<span v-cancel="{object: editObject, func: esc}"><i class="cn-icon cn-icon-close"></i></span>
</div>
</div>
<div class="right-box__container">
<div class="container__form">
<el-form ref="form" :model="editObject" :rules="rules" label-position="top" label-width="120px">
<!--name-->
<el-form-item :label="$t('overall.name')" prop="name">
<el-input id="proxy-name" v-model="editObject.name"
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--path-->
<el-form-item :label="$t('overall.path')" prop="path">
<el-input id="proxy-path" v-model="editObject.path"
placeholder="" show-word-limit size="small" type="text"></el-input>
</el-form-item>
<!--method-->
<el-form-item :label="$t('overall.method')" prop="method">
<el-select id="proxy-method"
v-model="editObject.method"
placeholder=" "
size="small"
class="right-box__select"
popper-class="right-box-select-dropdown prevent-clickoutside"
>
<el-option value="get"></el-option>
<el-option value="post"></el-option>
<el-option value="put"></el-option>
<el-option value="fetch"></el-option>
</el-select>
</el-form-item>
<!--version-->
<el-form-item :label="$t('overall.version')" prop="version">
<el-input id="proxy-version" v-model="editObject.version" placeholder="" size="small" type="text"></el-input>
</el-form-item>
<!--target-->
<el-form-item :label="$t('galaxyProxy.targetUrl')" prop="targetUrl">
<el-input id="proxy-targetUrl" v-model="editObject.targetUrl" placeholder="" size="small" type="text"></el-input>
</el-form-item>
<!--target param-->
<el-form-item :label="$t('galaxyProxy.targetParam')" prop="targetParam">
<!-- <prism-editor class="my-editor" v-model="editObject.targetParam" :highlight="jsonHl" line-numbers></prism-editor>-->
<v-ace-editor
v-model:value="editObject.targetParam"
lang="json"
theme="chrome"
style="height: 300px" />
</el-form-item>
<!--target header-->
<el-form-item :label="$t('galaxyProxy.targetHeader')" prop="targetHeader">
<v-ace-editor
v-model:value="editObject.targetHeader"
lang="json"
theme="chrome"
style="height: 300px" />
</el-form-item>
<!--pre handle-->
<el-form-item label="Pre handle" prop="preHandle">
<v-ace-editor
v-model:value="editObject.preHandle"
lang="javascript"
theme="chrome"
style="height: 300px" />
</el-form-item>
<!--post handle-->
<el-form-item label="Post handle" prop="postHandle">
<v-ace-editor
v-model:value="editObject.postHandle"
lang="javascript"
theme="chrome"
style="height: 300px" />
</el-form-item>
</el-form>
</div>
</div>
<div class="right-box__footer">
<button id="asset-edit-cancel" v-cancel="{object: editObject, func: esc}" class="footer__btn footer__btn--light">
<span>{{$t('overall.cancel')}}</span>
</button>
<button id="asset-edit-save" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
<span>{{$t('overall.save')}}</span>
</button>
</div>
</div>
</template>
<script>
import rightBoxMixin from '@/mixins/right-box'
import { api } from '@/utils/api'
import { VAceEditor } from 'vue3-ace-editor'
import 'ace-builds/src-noconflict/mode-javascript'
import 'ace-builds/src-noconflict/mode-json'
import 'ace-builds/src-noconflict/theme-chrome'
export default {
name: 'GalaxyProxyBox',
mixins: [rightBoxMixin],
components: {
VAceEditor
},
data () {
return {
url: api.galaxyProxy,
rules: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
]
}
}
}
}
</script>

View File

@@ -26,7 +26,7 @@
class="right-box__select"
clearable
collapse-tags
placeholder=""
placeholder=" "
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small"
@change="()=>{ this.$forceUpdate() }">

View File

@@ -23,7 +23,8 @@
<button type="button" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new option-btn" style="margin-left: 0px;" @click="expandAllOrNone" :class="{'btn-active':expandAllFlag}">展开/收缩</button>
<button type="button" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new option-btn" @click="selectAllOrNone" :class="{'btn-active':selectAllFlag}"><span ><i class="cn-icon cn-icon-delete"></i></span></button>
</div>-->
<el-tree :data="menus" :default-expand-all="expandAllFlag" :props="{label:labelFormatter}" @check-change="selectChange" class="tree-border" node-key="id" ref="menuTree" show-checkbox id="role-box-input-menus">
<el-checkbox v-model="isCheckAll" :label="$t('overall.all')" @change="checkAll"></el-checkbox>
<el-tree :data="menus" :default-expand-all="expandAllFlag" check-strictly="true" :props="{label:labelFormatter}" @check-change="selectChange" class="tree-border" node-key="id" ref="menuTree" show-checkbox id="role-box-input-menus">
<template #default="{ data }">
<span>
<i v-if="data.type === '1'" class="el-icon-menu"></i>
@@ -90,7 +91,8 @@ export default {
menus: [],
selectedIds: [],
selectAllFlag: false,
expandAllFlag: true
expandAllFlag: true,
isCheckAll: false
}
},
watch: {
@@ -150,9 +152,59 @@ export default {
labelFormatter: function (data, node) {
return data && data.i18n ? this.$t(data.i18n) : data.name
},
selectChange: function (data, isCheck, childIsCheck) {
getChildNodes (menu) {
let nodeGroup = []
if (menu.children && menu.children.length > 0) {
nodeGroup = menu.children
const _this = this
menu.children.forEach(node => {
const childNodes = _this.getChildNodes(node)
if (childNodes && childNodes.length > 0) {
nodeGroup = nodeGroup.concat(childNodes)
}
})
}
return nodeGroup
},
checkAll () {
if (this.$refs.menuTree) {
this.editRole.menuIds = this.$refs.menuTree.getCheckedKeys(true)
if (this.isCheckAll) {
let nodeGroup = this.menus
const _this = this
this.menus.forEach(menu => {
const childNodes = _this.getChildNodes(menu)
if (childNodes && childNodes.length > 0) {
nodeGroup = nodeGroup.concat(childNodes)
}
})
this.$refs.menuTree.setCheckedNodes(nodeGroup)
} else {
this.$refs.menuTree.setCheckedNodes([])
}
}
},
checkParentNode(node) {
if(node && this.$refs.menuTree.getNode(node)){
let parent = this.$refs.menuTree.getNode(node).parent
let parentNode = parent.data
if(parentNode && parentNode.id && parentNode.id !== 0 ){
this.$refs.menuTree.setChecked(parentNode,true,false)
this.checkParentNode(parentNode)
}
}
},
selectChange: function (data, isCheck, childIsCheck) {
if(isCheck) {//如果是选中节点,则同步选中所有的父辈节点(有全选和半选两种状态)
this.checkParentNode(data)
} else {//如果是取消节点,则同步取消选中所有子节点
if(data.children && data.children.length > 0) {
data.children.forEach(node => {
this.$refs.menuTree.setChecked(node, false, true)
})
}
}
if (this.$refs.menuTree) {
this.editRole.menuIds = this.$refs.menuTree.getCheckedKeys(false)
}
},
selectAllOrNone: function () {

View File

@@ -45,7 +45,7 @@
class="right-box__select"
clearable
collapse-tags
placeholder=""
placeholder=" "
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small"
@change="()=>{ this.$forceUpdate() }">
@@ -61,7 +61,7 @@
class="right-box__select"
clearable
collapse-tags
placeholder=""
placeholder=" "
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small">
<template v-for="lang in langData" :key="lang.value">
@@ -70,20 +70,20 @@
</el-select>
</el-form-item>
<!--theme-->
<el-form-item :label="$t('config.user.theme')" prop="i18n">
<!-- <el-form-item :label="$t('config.user.theme')" prop="i18n">
<el-select id="account-input-roleIds"
v-model="editObject.theme"
class="right-box__select"
clearable
collapse-tags
placeholder=""
placeholder=" "
popper-class="right-box-select-dropdown prevent-clickoutside"
size="small">
<template v-for="theme in themeData" :key="theme.value">
<el-option :label="theme.label" :value="theme.value"></el-option>
</template>
</el-select>
</el-form-item>
</el-form-item>-->
<!--enable-->
<el-form-item :label="$t('config.user.enable')">
<el-switch
@@ -125,16 +125,16 @@ export default {
mixins: [rightBoxMixin],
data () {
const validatePin = (rule, value, callback) => { // 确认密码
if (value.length < 5) {
if (value && value.length < 5) {
callback(new Error(this.$t('validate.atLeastFive')))
} else {
callback()
}
}
const validateConfirmPin = (rule, value, callback) => { // 确认密码的二次校验
if (value === '' && this.editObject.pin) {
if (_.isEmpty(value) && !_.isEmpty(this.editObject.pin)) {//密码有内容,确认密码没内容
callback(new Error(this.$t('config.user.confirmPin')))
} else if (value !== this.editObject.pin) {
} else if (!_.isEmpty(value) && value !== this.editObject.pin) {//密码有内容,确认密码也有内容,内容不一致
callback(new Error(this.$t('config.user.confirmPinErr')))
} else {
callback()
@@ -207,7 +207,7 @@ export default {
],
pinChange: [
{ validator: validateConfirmPin, trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9]{5,64}$/, message: this.$t('validate.atLeastFive') }
{ validator: validatePin, trigger: 'blur' }
],
roleIds: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }

View File

@@ -1,329 +0,0 @@
<template>
<el-dialog
v-model="show"
:top="top"
:modal="modal"
:custom-class="customClass"
:show-close="showClose"
:width="width"
@close="closeDebug"
@open="openDebug"
destroy-on-close
>
<div class="debug-wrapper">
<el-select v-model="name" filterable placeholder="Select" class="item item-1" allow-create
@change="changPath">
<template v-for="(proxy,index) in galaxyProxyData" :key="proxy.id">
<el-option :label="proxy.name" :value="index"></el-option>
</template>
</el-select>
<div class="item item-2 sub-grid-send">
<el-select v-model="method" filterable placeholder="Select" style="width:100px;" >
<el-option
v-for="item in methodOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
<el-input v-model="fullPathWithParams" placeholder="Please input" readonly/>
<el-button @click="send">{{$t('galaxyProxy.debug.send')}}</el-button>
</div>
<div class="item item-3 sub-grid-params">
<div class="sub-grid-params-add">
<span style="padding-left: 15px;">{{$t('galaxyProxy.debug.requestParams')}}</span>
<el-button size="mini" @click="handleAddDetails"><i class="cn-icon-add cn-icon"></i></el-button>
</div>
<el-table :data="paramsTableData"
width="100%"
@selection-change="handleDetailSelectionChange"
:row-class-name="rowClassName"
empty-text=""
ref="tb">
<el-table-column type="selection" align="center" label="" width="30px" style="border-left:0px;" />
<el-table-column prop="key" align="center" width="80px" >
<template #header>
<div class="table-operation-title">{{$t('galaxyProxy.debug.key')}}</div>
</template>
<template #default="scope">
<el-input @change="paramsChange(scope.row)" v-model="scope.row.key"></el-input>
</template>
</el-table-column>
<el-table-column prop="value" align="center" >
<template #header>
<div class="table-operation-title">{{$t('galaxyProxy.debug.value')}}</div>
</template>
<template #default="scope">
<el-input @change="paramsChange(scope.row)" v-model="scope.row.value"></el-input>
</template>
</el-table-column>
<el-table-column align="center" style="border-right:0px;" width="70px">
<template #header>
<div class="table-operation-title">{{$t('overall.option')}}</div>
</template>
<template #default="scope">
<div @click="deleteRow(scope.$index)" class="debug__params-delete">{{$t('overall.delete')}}</div>
</template>
</el-table-column>
<template v-slot:empty>
<div style="height:0px;"></div>
</template>
</el-table>
</div>
<div class="item item-4">
<div class="request-header" >{{$t('galaxyProxy.debug.request.header')}}</div>
<div class="request-header-content" ><pre v-html="proxyRequestHeader" style="height: 98%;width: 98%;margin-left: -15px;margin-top: -10px;"></pre></div>
<div class="response-header">{{$t('galaxyProxy.debug.response.header')}}</div>
<div class="response-body">{{$t('galaxyProxy.debug.response.body')}}</div>
<div class="response-header-content"><pre v-html="proxyResponseHeader" style="height: 98%;width: 98%;margin-left: -15px;margin-top: -10px;"></pre></div>
<div class="response-body-content color-pre__style">
<v-ace-editor
v-model:value="proxyResponseBody"
lang="json"
readonly="true"
theme="chrome"
style="height: 100%" />
</div>
</div>
</div>
</el-dialog>
</template>
<script>
import { api } from '@/utils/api'
import { getForDebug, postForDebug } from '@/utils/http'
import axios from 'axios'
import { VAceEditor } from 'vue3-ace-editor'
import 'ace-builds/src-noconflict/mode-javascript'
import 'ace-builds/src-noconflict/mode-json'
import 'ace-builds/src-noconflict/theme-chrome'
export default {
name: 'GalaxyProxyDebug',
components: {
VAceEditor
},
props: {
showDebug: Boolean,
top: {
type: String,
default: '5vh'
},
modal: {
type: Boolean,
default: true
},
customClass: {
type: String,
default: 'proxy-debug__dialog'
},
showClose: {
type: Boolean,
default: true
},
width: { // 高度需要通过customClass自行定义
type: [String, Number],
default: '90vw'
},
curGalaxyProxy: Object // 实体,{ name: '', path: '', icon: icon-class }
},
data () {
return {
show: false,
galaxyProxyData: [],
name: '',
paramsTableData: [
],
// 选中的数据
checkedDetail: {},
checkedRowData: [],
methodOptions: [
{
value: 'GET',
label: 'GET'
},
{
value: 'POST',
label: 'POST'
}
],
method: 'GET',
path: '', // 不带参数的路径用于请求URL
fullPathWithParams: '', // 带参数的完整路径,用于界面显示
proxyResponseBody: '',
proxyRequestHeader: '',
proxyResponseHeader: ''
}
},
setup () {
},
methods: {
handleAddDetails () {
if (this.paramsTableData == undefined) {
this.paramsTableData = new Array()
}
const obj = {}
obj.key = ''
obj.value = ''
this.paramsTableData.push(obj)
},
// 删除当前行
deleteRow (index) {
this.paramsTableData.splice(index, 1)
this.updateUrlParams()
},
rowClassName ({ row, rowIndex }) {
// 把每一行的索引放进row
row.index = rowIndex
},
// 参数发送改变时
paramsChange (row) {
this.fullPathWithParams = this.path
this.checkedDetail = {}
this.checkedRowData.forEach(t => {
if (t.key != '') {
this.checkedDetail[t.key] = t.value
}
})
if (this.fullPathWithParams != '') {
this.updateUrlParams()
}
},
updateUrlParams () {
this.fullPathWithParams = this.path
let params = ''
for (const key in this.checkedDetail) {
if (key != '') {
params = params + key + '=' + this.checkedDetail[key] + '&'
}
}
if (params.endsWith('&')) {
params = params.substring(0, params.length - 1)
}
if (params != '') {
this.fullPathWithParams = this.fullPathWithParams + '?' + params
}
},
// 单选框选中数据
handleDetailSelectionChange (selection) {
this.checkedDetail = {}
this.checkedRowData = selection
selection.forEach((item, index) => {
this.checkedDetail[item.key] = item.value
})
this.updateUrlParams()
},
async getGalaxyProxyData (value) {
await axios.get(api.galaxyProxy + '?pageSize=-1').then(response => {
if (response.status === 200) {
this.galaxyProxyData = response.data.data.list
}
})
},
syntaxUrl (json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2)
}
return json = json.replace(/,/g, '&').replace(/{/g, '').replace(/}/g, '').replace(/"/g, '').replace(/:/g, '=').replace(/ /g, '')
},
changPath (index) {
// 修改input的值
this.path = axios.defaults.baseURL + 'interface' + this.galaxyProxyData[index].path
this.updateUrlParams()
this.method = this.galaxyProxyData[index].method.toUpperCase()
},
send () {
// this.path = "galaxy/setting?pageNo=1&pageSize=1"
// 注意有GET与POST两种方式
if (this.method.toUpperCase() == 'GET') {
getForDebug(this.path, this.checkedDetail).then(response => {
this.proxyResponseBody = this.syntaxJson(JSON.parse(JSON.stringify(response.data)))
this.proxyResponseHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.headers)))
this.proxyRequestHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.config.headers)))
this.proxyResponseHeader = this.proxyResponseHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
this.proxyRequestHeader = this.proxyRequestHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
})
} else if (this.method.toUpperCase() == 'POST') {
postForDebug(this.path, this.checkedDetail).then(response => {
if (response) {
this.proxyResponseBody = this.syntaxJson(JSON.parse(JSON.stringify(response.data)))
this.proxyResponseHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.headers)))
this.proxyRequestHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.config.headers)))
this.proxyResponseHeader = this.proxyResponseHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
this.proxyRequestHeader = this.proxyRequestHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
}
})
}
},
// 格式化json
syntaxJson (json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2)
}
return json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
},
closeDebug () {
this.paramsTableData = []// JSON.parse(JSON.stringify(paramsTableData))
this.proxyResponseBody = ''
this.proxyResponseHeader = ''
this.proxyRequestHeader = ''
this.name = ''
this.path = ''
this.fullPathWithParams = ''
for (const key in this.curGalaxyProxy) {
delete this.curGalaxyProxy[key]
}
},
openDebug () {
if (this.curGalaxyProxy.path) {
this.name = this.curGalaxyProxy.name
this.path = axios.defaults.baseURL + 'interface' + this.curGalaxyProxy.path
this.fullPathWithParams = this.path
this.method = this.curGalaxyProxy.method.toUpperCase()
} else {
this.name = ''
this.path = ''
this.method = 'GET'
}
}
},
mounted () {
// 获取代理名称及路径
this.getGalaxyProxyData()
},
watch: {
showDebug (n, o) {
this.show = n
},
show (n, o) {
this.$emit('update:show-debug', n)
},
path (n, o) {
this.updateUrlParams()
}
}
}
</script>

View File

@@ -52,13 +52,17 @@
</template>
<template v-else-if="item.prop === 'status'">
<el-switch
v-if="scope.row.id"
v-if="scope.row.id && hasPermission('editUser')"
v-model="scope.row.status"
active-value="1"
:disabled="(scope.row.username === loginName) || (scope.row.username==='admin' && scope.row.id===1) || scope.row.buildIn === 1"
inactive-value="0"
@change="()=>{statusChange(scope.row)}">
</el-switch>
<template v-else>
<span v-if="scope.row.status === '1'">{{$t('detection.create.enabled')}}</span>
<span v-else>{{$t('detection.create.disabled')}}</span>
</template>
</template>
<span v-else>{{scope.row[item.prop] || '-'}}</span>
</template>

View File

@@ -4,56 +4,60 @@
<div class="block-mode">
<!--todo 图标没有后期换-->
<div class="block-mode-left">
<i class="cn-icon cn-icon-setting2 block-mode-icon"></i>
<i class="cn-icon cn-icon-indicator-match block-mode-icon1"></i>
</div>
<div class="block-mode-right">
<div class="block-mode-title">Indicator Match</div>
<div class="block-mode-right" style="position:relative;">
<div class="block-mode-title">{{ $t('detection.policy.indicatorMatch') }}</div>
<div class="block-mode-content">
Use indicators from intelligencesources to detect matchingevents and alerts.
{{ $t('detection.policy.indicatorMatchIntroduce') }}
<div v-if="language===ZH" style="color: rgba(0,0,0,0)">0</div>
</div>
<div :class="settingObj.ruleType===detectionRuleType.indicator?'block-mode-btn-active':'block-mode-btn'"
@click="selectMode(detectionRuleType.indicator)">select
@click="selectMode(detectionRuleType.indicator)">{{ $t('overall.select') }}
</div>
</div>
</div>
<div class="block-mode">
<!--todo 图标没有后期换-->
<div class="block-mode" style="cursor: no-drop;">
<div class="block-mode-left">
<i class="cn-icon cn-icon-setting2 block-mode-icon"></i>
<i class="cn-icon cn-icon-threshold block-mode-icon"></i>
</div>
<div class="block-mode-right">
<div class="block-mode-title">Threshold</div>
<div class="block-mode-title">{{ $t('detection.policy.threshold') }}</div>
<div class="block-mode-content">
Aggregate query results to detect when number of matches exceeds threshold.
{{ $t('detection.policy.thresholdIntroduce') }}
</div>
<div :class="settingObj.ruleType===detectionRuleType.threshold?'block-mode-btn-active':'block-mode-btn'"
@click="selectMode(detectionRuleType.threshold)">select
<!--todo 当前版本暂时不可选择-->
<div style="cursor: no-drop;" :class="settingObj.ruleType===detectionRuleType.threshold?'block-mode-btn-active':'block-mode-btn'">
{{ $t('overall.select') }}
</div>
<!-- <div :class="settingObj.ruleType===detectionRuleType.threshold?'block-mode-btn-active':'block-mode-btn'"-->
<!-- @click="selectMode(detectionRuleType.threshold)">select-->
<!-- </div>-->
</div>
</div>
</div>
<!--category-->
<el-form ref="form" :model="settingObj" label-position="top" :rules="rules">
<el-form-item label="Category" prop="category" class="form-setting__block margin-b-20">
<el-select v-model="settingObj.category" class="form-setting__select" placeholder=" " size="mini" @change="changeEditFlag">
<el-form-item :label="$t('overall.category')" prop="category" class="form-setting__block margin-b-20">
<el-select :disabled="settingObj.ruleId" v-model="settingObj.category" class="form-setting__select" placeholder=" " size="mini" @change="changeEditFlag">
<el-option
v-for="item in categoryList"
:key="item.value"
:label="item.label"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
</el-form-item>
<!--type-->
<el-form-item label="Type" prop="eventType" class="form-setting__block margin-b-20">
<el-form-item :label="$t('overall.type')" prop="eventType" class="form-setting__block margin-b-20">
<el-select v-model="settingObj.eventType" placeholder=" " size="mini" class="form-setting__select" @change="changeEditFlag">
<el-option
v-for="item in typeList"
v-for="item in eventTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
@@ -62,32 +66,32 @@
</el-form-item>
<!--name-->
<el-form-item label="Name" prop="name" class="form-setting__block margin-b-20">
<el-form-item :label="$t('overall.name')" prop="name" class="form-setting__block margin-b-20">
<el-input
maxlength="64"
show-word-limit
placeholder=""
v-model="settingObj.name"
@input="changeEditFlag"
@blur="changeEditFlag"
class="form-setting__input" />
</el-form-item>
<!--Description-->
<div class="form-setting__block margin-b-20">
<div class="block-title">Description</div>
<div class="block-title">{{ $t('config.dataSet.description') }}</div>
<el-input
maxlength="255"
show-word-limit
v-model="settingObj.description"
type="textarea"
resize='none'
@input="changeEditFlag"
@blur="changeEditFlag"
class="form-setting__textarea" />
</div>
</el-form>
<div class="form-setting__block margin-b-20">
<div class="block-title">Policy Status</div>
<div class="block-title">{{ $t('detection.create.policyStatus') }}</div>
<el-switch
v-model="settingObj.status"
@change="changeEditFlag"
@@ -95,30 +99,36 @@
inactive-color="#C0CEDB"
:active-value="1"
:inactive-value="0"
:active-text="switchStatus(settingObj.status)"/>
:active-text="$t(switchStatus(settingObj.status))"/>
</div>
<div class="form-setting__btn">
<el-button @click="onContinue">Continue</el-button>
<el-button @click="onContinue">{{ $t('detection.create.continue') }}</el-button>
</div>
</div>
</template>
<script>
import { detectionRuleType } from '@/utils/constants'
import { detectionRuleType, storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
import { switchStatus } from '@/utils/tools'
import axios from 'axios'
import { api } from '@/utils/api'
export default {
name: 'GeneralSettings',
props: {
editObj: {
type: Object
},
isComplete: {
type: Boolean
}
},
data () {
return {
detectionRuleType,
categoryList: [],
typeList: [],
eventTypeList: [],
settingObj: {
ruleType: detectionRuleType.threshold,
ruleType: detectionRuleType.indicator,
category: '',
eventType: '',
name: '',
@@ -149,6 +159,22 @@ export default {
trigger: 'blur'
}
]
},
language: EN,
ZH
}
},
watch: {
editObj (newVal) {
if (newVal.ruleId) {
this.settingObj = JSON.parse(JSON.stringify({ ...newVal }))
this.settingObj.editFlag = false
this.settingObj.saveFlag = true
}
},
isComplete (newVal) {
if (!newVal) {
this.onContinue()
}
}
},
@@ -158,15 +184,9 @@ export default {
methods: {
switchStatus,
initData () {
axios.get(api.detection.statistics, { params: { pageSize: -1 } }).then(response => {
if (response.status === 200) {
this.categoryList = response.data.data.categoryList || []
this.typeList = response.data.data.typeList || []
} else {
console.error(response.data)
}
}).finally(() => {
})
this.language = localStorage.getItem(storageKey.language) || EN
this.categoryList = detectionUnitList.categoryList
this.eventTypeList = detectionUnitList.eventTypeList
},
selectMode (ruleType) {
this.settingObj.ruleType = ruleType
@@ -174,9 +194,16 @@ export default {
},
changeEditFlag () {
this.settingObj.editFlag = true
if (this.settingObj.ruleType && this.settingObj.category && this.settingObj.eventType && this.settingObj.name) {
this.settingObj.settingNoContinue = true
this.onContinue()
}
},
/** 点击继续,进行第二步 */
onContinue () {
onContinue (e) {
if (e) {
delete this.settingObj.settingNoContinue
}
this.$refs.form.validate(valid => {
if (valid) {
this.settingObj.editFlag = false
@@ -188,7 +215,3 @@ export default {
}
}
</script>
<style lang="scss">
</style>

View File

@@ -9,8 +9,7 @@
<div class="key-search">
<el-input v-model="searchKey" @keyup.enter="onSearch" size="mini" placeholder="Search for">
<template #prefix>
<!--todo 该图标名称错误已在iconfont修改后续记得改过来-->
<i class="cn-icon cn-icon-serach key-search-icon"></i>
<i class="cn-icon cn-icon-search key-search-icon"></i>
</template>
</el-input>
@@ -24,7 +23,7 @@
<div class="key-table">
<loading :loading="loading"></loading>
<el-table :data="tableData" style="width: 100%" @row-click="rowClick">
<el-table :data="tableData" style="width: 100%" :row-class-name="tableRowClassName" @row-click="rowClick">
<el-table-column
v-for="(item, index) in tableTitle"
:key="`col-${index}`"
@@ -73,6 +72,9 @@ export default {
showDrawer: {
type: Boolean,
default: false
},
delKeyId: {
type: String
}
},
components: {
@@ -116,6 +118,16 @@ export default {
this.myDrawer = this.showDrawer
this.getTopKeysData()
},
watch: {
delKeyId (newVal) {
if (newVal) {
const obj = this.tableData.find(d => d.keyId === newVal)
if (obj) {
obj.filterKey = false
}
}
}
},
methods: {
unitConvert,
dateFormatByAppearance,
@@ -150,6 +162,7 @@ export default {
},
/** 单击topKeys弹窗某一项 */
rowClick (data) {
data.filterKey = true
this.$emit('keyRowClick', data)
},
onRefresh () {
@@ -161,6 +174,11 @@ export default {
httpError (e) {
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
},
tableRowClassName (row) {
if (row.row.filterKey) {
return 'key-click-row'
}
}
}
}
@@ -231,4 +249,8 @@ export default {
margin-left: 4px;
}
}
.el-table .key-click-row {
background: #F5F7FA !important;
}
</style>

View File

@@ -4,7 +4,7 @@
<div>
<el-form ref="form" :model="thresholdRuleObj" label-position="top" :rules="rules">
<!--source-->
<el-form-item label="Source" prop="dataSource" class="form-setting__block margin-b-20">
<el-form-item :label="$t('config.user.source')" prop="dataSource" class="form-setting__block margin-b-20">
<el-select v-model="thresholdRuleObj.dataSource" placeholder=" " size="mini" class="form-setting__select">
<el-option
v-for="item in sourceList"
@@ -18,7 +18,7 @@
<!--Dimensions-->
<div class="form-setting__block margin-b-20">
<div class="block-title">Dimensions</div>
<div class="block-title">{{ $t('detection.create.dimensions') }}</div>
<div class="block-dimension">
<div class="block-dimension-tag" v-for="(ite, ind) in dimensionList" :key="ind">{{ ite.label }}</div>
</div>
@@ -52,7 +52,7 @@
size="mini"
oninput="value=value.replace(/[^\d]/g,'')"
v-model="item.value"></el-input>
<i class="cn-icon cn-icon-close" @click="delFilterItem(index)"></i>
<i class="cn-icon cn-icon-close" @click="delFilterItem(index, item)"></i>
</div>
<div style="height: 10px;"></div>
@@ -65,7 +65,7 @@
<!--Condition模块-->
<div class="form-setting__block margin-b-20">
<div class="block-title">Condition</div>
<div class="block-title">{{ $t('detection.create.condition') }}</div>
<el-form ref="form2" :model="thresholdRuleObj" label-position="top">
<div class="definition-condition-block" v-for="(item, index) in thresholdRuleObj.conditionData" :key="index">
@@ -97,7 +97,7 @@
v-for="item in metricList"
:key="item.label"
:label="item.label"
:value="item.value"
:value="item.label"
/>
</el-select>
</el-form-item>
@@ -144,7 +144,7 @@
</el-form>
<div class="condition-add" @click="addCondition">
<i class="cn-icon cn-icon-add"></i>Add Condition
<i class="cn-icon cn-icon-add"></i>{{ $t('detection.create.addCondition') }}
</div>
</div>
</div>
@@ -154,6 +154,7 @@
<history-top-keys
v-if="showDrawer"
:showDrawer="showDrawer"
:delKeyId="delKeyId"
@closeDrawer="onCloseDrawer"
@keyRowClick="getRowClick"
></history-top-keys>
@@ -163,24 +164,24 @@
<div v-if="mySettingObj.ruleType===detectionRuleType.indicator">
<el-form ref="form" :model="indicatorRuleObj" label-position="top" :rules="rules">
<!--Source-->
<el-form-item label="Source" prop="dataSource" class="form-setting__block margin-b-20">
<el-select v-model="indicatorRuleObj.dataSource" class="form-setting__select" placeholder=" " size="mini">
<el-form-item :label="$t('config.user.source')" prop="dataSource" class="form-setting__block margin-b-20">
<el-select v-model="indicatorRuleObj.dataSource" class="form-setting__select" placeholder=" " size="mini" @change="handleParamsComplete">
<el-option
v-for="item in sourceList"
:key="item.value"
:label="item.label"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
</el-form-item>
<!--Library-->
<el-form-item label="Library" prop="knowledgeId" class="form-setting__block margin-b-20">
<el-select v-model="indicatorRuleObj.knowledgeId" class="form-setting__select" placeholder=" " size="mini">
<el-form-item :label="$t('detection.library')" prop="knowledgeId" class="form-setting__block margin-b-20">
<el-select v-model="indicatorRuleObj.knowledgeId" class="form-setting__select" filterable placeholder=" " size="mini" @change="handleParamsComplete">
<el-option
v-for="item in libraryList"
:key="item.knowledgeId"
:label="item.label"
:label="item.name"
:value="item.knowledgeId"
/>
</el-select>
@@ -188,7 +189,7 @@
<!--Level-->
<el-form-item :label="$t('detection.level')" prop="level" class="form-setting__block">
<el-select v-model="indicatorRuleObj.level" class="condition__select form-setting__select" placeholder=" " size="mini">
<el-select v-model="indicatorRuleObj.level" class="condition__select form-setting__select" placeholder=" " size="mini" @change="handleParamsComplete">
<template #prefix>
<div
class="condition__select__icon"
@@ -198,7 +199,7 @@
<el-option
v-for="item in levelList"
:key="item.label"
:label="item.label"
:label="$t(item.label)"
:value="item.value"
/>
</el-select>
@@ -207,22 +208,29 @@
</div>
<div class="form-setting__btn">
<el-button @click="onContinue">Continue</el-button>
<el-button @click="onContinue">{{ $t('detection.create.continue') }}</el-button>
</div>
</div>
</template>
<script>
import axios from 'axios'
import { api } from '@/utils/api'
import HistoryTopKeys from '@/components/table/detection/HistoryTopKeys'
import { eventSeverityColor, detectionRuleType } from '@/utils/constants'
import { eventSeverityColor, detectionRuleType, detectionUnitList, securityLevel } from '@/utils/constants'
import axios from 'axios'
import _ from 'lodash'
import { api } from '@/utils/api'
export default {
name: 'RuleDefinition',
props: {
settingObj: {
type: Object
},
editObj: {
type: Object
},
isComplete: {
type: Boolean
}
},
components: {
@@ -232,11 +240,31 @@ export default {
settingObj: {
immediate: true,
deep: true,
handler (newVal, oldVal) {
handler (newVal) {
if (!newVal.editFlag && newVal.saveFlag) {
this.mySettingObj = JSON.parse(JSON.stringify(newVal))
}
}
},
editObj: {
immediate: true,
deep: true,
handler (newVal) {
if (newVal) {
if (newVal.ruleType === detectionRuleType.indicator) {
this.indicatorRuleObj = this.$_.cloneDeep(newVal.ruleConfigObj)
this.indicatorRuleObj.knowledgeId = newVal.ruleConfigObj.knowledgeBase.knowledgeId
}
if (newVal.ruleType === detectionRuleType.threshold) {
this.thresholdRuleObj = this.$_.cloneDeep(newVal.ruleConfigObj)
}
}
}
},
isComplete (newVal) {
if (!newVal) {
this.onContinue()
}
}
},
data () {
@@ -244,7 +272,7 @@ export default {
eventSeverityColor,
detectionRuleType,
mySettingObj: {
ruleType: detectionRuleType.threshold
ruleType: detectionRuleType.indicator
},
dimensionList: [], // Dimensions数据
// ruleType为Indicator时表单数据
@@ -327,7 +355,8 @@ export default {
than: '>',
less: '<',
equal: '='
}
},
delKeyId: '' // 删除的keyId
}
},
mounted () {
@@ -341,18 +370,31 @@ export default {
},
methods: {
initData () {
axios.get(api.detection.statistics, { params: { pageSize: -1 } }).then(response => {
if (response.status === 200) {
this.sourceList = response.data.data.sourceList || []
this.levelList = response.data.data.levelList || []
this.conditionList = response.data.data.conditionList || []
this.metricList = response.data.data.metricList || []
this.libraryList = response.data.data.libraryList || []
} else {
console.error(response.data)
}
}).finally(() => {
})
this.sourceList = detectionUnitList.sourceList || []
this.levelList = securityLevel || []
// threshold模式还没确定所以数据暂时静态数据后续根据需要修改
this.conditionList = detectionUnitList.conditionList || []
this.metricList = detectionUnitList.metricList || []
if (this.mySettingObj.ruleType === this.detectionRuleType.indicator) {
axios.get(api.knowledgeBaseList, { params: { pageSize: -1 } }).then(response => {
if (response.status === 200) {
this.libraryList = _.get(response, 'data.data.list', []).filter(l => l.isBuiltIn === 0)
} else {
console.error(response.data.message)
this.libraryList = []
if (response.data.message) {
this.$message.error(response.data.message)
} else {
this.$message.error(this.$t('tip.somethingWentWrong'))
}
}
}).catch(e => {
console.error(e)
this.libraryList = []
this.$message.error(this.errorMsgHandler(e))
})
}
},
/** 单击History Top Keys列表某行filter添加数据 */
getRowClick (data) {
@@ -364,6 +406,7 @@ export default {
},
/** filter模块点击add按钮 */
addFilter (data) {
this.delKeyId = ''
this.showFilter = true
if (this.selectList.length === 0) {
this.getFilterList()
@@ -445,11 +488,15 @@ export default {
]
},
/** 删除filter某一项 */
delFilterItem (i) {
delFilterItem (i, item) {
this.delKeyId = item.keyId
this.thresholdRuleObj.filterList.splice(i, 1)
},
/** 点击继续,展开第三步 */
onContinue () {
onContinue (e) {
if (e) {
delete this.indicatorRuleObj.ruleNoContinue
}
this.$refs.form.validate(valid => {
if (valid) {
if (this.mySettingObj.ruleType === detectionRuleType.indicator) {
@@ -482,6 +529,12 @@ export default {
obj[item.level] = str
})
this.thresholdRuleObj.conditions = obj
},
handleParamsComplete () {
if (this.indicatorRuleObj.dataSource && this.indicatorRuleObj.knowledgeId && this.indicatorRuleObj.level) {
this.indicatorRuleObj.ruleNoContinue = true
this.onContinue()
}
}
}
}

View File

@@ -61,7 +61,7 @@
<div class="reference-tag__group">
<span class="reference-tag" v-for="(refer, index) in scope.row[item.prop].slice(0,2)" >{{refer}}</span>
</div>
<div class="reference-more">+{{scope.row[item.prop].length - 2}} more</div>
<div class="reference-more">+{{scope.row[item.prop].length - 2}} {{$t('overall.more')}}</div>
</div>
</template>
<div class="reference-tag__tip">
@@ -70,9 +70,11 @@
</el-popover>
</templage>
<template v-else>
<template v-for="(refer, index) in scope.row[item.prop]">
<div class="type-tag">{{refer}}</div>
</template>
<div class="reference-tag__show">
<div class="reference-tag__group">
<span class="reference-tag" v-for="(refer, index) in scope.row[item.prop]" >{{refer}}</span>
</div>
</div>
</template>
</template>
<template v-else-if="item.prop === 'opTime' || item.prop === 'ctime'">
@@ -96,6 +98,7 @@
</template>
<template v-else-if="item.prop === 'status'">
<el-switch v-model="scope.row.status"
v-if="hasPermission('editUserDefinedLibrary')"
active-color="#38ACD2"
inactive-color="#C0CEDB"
:active-value="1"
@@ -103,6 +106,15 @@
@change="changeStatus($event,scope.row.knowledgeId)"
>
</el-switch>
<template v-else>
<span v-if="scope.row.status === 1">{{$t('detection.create.enabled')}}</span>
<span v-else>{{$t('detection.create.disabled')}}</span>
</template>
</template>
<template v-else-if="item.prop === 'color'">
<div class="knowledge-color">
<span class="knowledge-color__icon" :class="colorName(scope.row[item.prop])"></span> <span>{{colorText(scope.row[item.prop])}}</span>
</div>
</template>
<span v-else>{{scope.row[item.prop] || '-'}}</span>
</template>
@@ -151,6 +163,11 @@ export default {
}, {
label: this.$t('knowledge.reference'),
prop: 'reference',
width: 190,
show: true
}, {
label: this.$t('overall.color'),
prop: 'color',
width: 180,
show: true
}, {
@@ -189,6 +206,23 @@ export default {
show: true,
width: 80
}
],
knowledgeBaseColor: [
{
label: this.$t('knowledge.info'),
value: 'rgb(119,131,145)',
name: 'info'
},
{
label: this.$t('knowledge.benign'),
value: 'rgb(116,159,77)',
name: 'benign'
},
{
label: this.$t('knowledge.malicious'),
value: 'rgb(226,97,84)',
name: 'malicious'
}
]
}
},
@@ -221,6 +255,20 @@ export default {
const t = knowledgeBaseSource.find(t => t.value === type)
return t ? t.name : ''
}
},
colorText () {
const vm = this
return function (color) {
const t = vm.knowledgeBaseColor.find(t => t.value === color)
return t ? t.label : vm.knowledgeBaseColor[0].label
}
},
colorName () {
const vm = this
return function (color) {
const t = vm.knowledgeBaseColor.find(t => t.value === color)
return t ? t.name : vm.knowledgeBaseColor[0].name
}
}
}
}

View File

@@ -3,38 +3,30 @@
<div class="card-type-title" v-if="aiTaggingList.length > 0">{{$t('knowledgeBase.intelligenceLearning')}}</div>
<el-checkbox-group v-model="checkList" >
<div class="card-box" v-for="data in aiTaggingList" :key="data.knowledgeId">
<div @click="isSelectedStatus && data.isBuiltIn !== 1 && clickCard(data,$event)" @mouseenter="mouseenter(data)" @mouseleave="mouseleave(data)" class="card-item" :class="data.isSelected ? 'card-selected' : ''">
<div @click="isSelectedStatus && data.isBuiltIn !== 1 && clickCard(data,$event)" @mouseenter="mouseenter(data)" @mouseleave="mouseleave(data)" class="card-item" :class="data.isSelected ? 'card-selected' : ''">
<div class="card-content">
<div class="card-operate">
<el-tooltip
effect="light"
trigger="hover"
:content="$t('tip.notAvailable')"
placement="right"
popper-class="panel-tooltip"
<el-switch v-model="data.status"
v-if="hasPermission('editBuiltInKnowledgeBase')"
class="card-enable"
active-color="#38ACD2"
inactive-color="#C0CEDB"
:active-value="1"
:inactive-value="0"
:before-change="(knowledgeId) => confirmSwitchLearning(data.knowledgeId)"
>
<el-switch v-model="data.status"
class="card-enable"
active-color="#38ACD2"
inactive-color="#C0CEDB"
:disabled="true"
:active-value="1"
:inactive-value="0"
@change="changeStatus($event,data.knowledgeId)"
>
</el-switch>
</el-tooltip>
</el-switch>
</div>
<div class="card-icon">
<img :src="data.iconUrl"/>
</div>
<div class="card-title">
<div class="card-title-name" :title="data.label">{{data.label}}</div>
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
</div>
<div class="card-desc" :title="data.desc">{{data.desc ? data.desc : '—'}}</div>
<div class="card-desc" :title="$t(data.desc)">{{$t(data.desc) || '—'}}</div>
</div>
<div class="card-operate__footer">
<button v-if="data.showUpdate"
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')"
class="top-tool-btn--update"
@click="jumpToUpdatePage(data,true)">
<i class="cn-icon-update-knowledge-base cn-icon"></i>
@@ -53,12 +45,12 @@
<img :src="data.iconUrl"/>
</div>
<div class="card-title">
<div class="card-title-name" :title="data.label">{{data.label}}</div>
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
</div>
<div class="card-desc" :title="data.desc ? data.desc:'—'">{{data.desc ? data.desc : '—'}}</div>
<div class="card-desc" :title="data.desc ? $t(data.desc) : '—'">{{data.desc ? $t(data.desc) : '—'}}</div>
</div>
<div class="card-operate__footer">
<button v-if="data.showUpdate" :title="$t('overall.update')" class="top-tool-btn--update"
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')" :title="$t('overall.update')" class="top-tool-btn--update"
@click="jumpToUpdatePage(data,false)">
<i class="cn-icon-update-knowledge-base cn-icon"></i>
<span>{{$t('overall.update')}}</span>
@@ -75,7 +67,9 @@
<div class="center-dialog">
<el-dialog v-model="showUpdateDialog"
:destroy-on-close="true"
:custom-class="showAddUpdateDialog ? 'update-knowledge update-knowledge--upload' : 'update-knowledge'"
:before-close="beforeClose"
:after-close="handleClose">
<div class="knowledge-update__top" >
<div class="update-left__icon">
@@ -84,60 +78,144 @@
<div class="update-right">
<div class="knowledge-enable">
<div class="update-title">
<div class="card-title-name" :title="updateKnowledge.label">{{updateKnowledge.label}}</div>
<div class="card-title-name" :title="$t(updateKnowledge.label)">{{$t(updateKnowledge.label)}}</div>
</div>
<el-tooltip
effect="light"
trigger="hover"
v-if="showEnable"
:content="$t('tip.notAvailable')"
placement="right"
popper-class="panel-tooltip"
<el-switch v-model="updateKnowledge.status"
active-color="#38ACD2"
inactive-color="#C0CEDB"
:active-value="1"
:inactive-value="0"
:before-change="(knowledgeId) => confirmSwitchLearning(updateKnowledge.knowledgeId)"
v-if="updateKnowledge.source === 'cn_psiphon3_ip' && hasPermission('editBuiltInKnowledgeBase')"
>
<el-switch v-model="updateKnowledge.status"
active-color="#38ACD2"
inactive-color="#C0CEDB"
:disabled="true"
:active-value="1"
:inactive-value="0"
@change="changeStatus($event,updateKnowledge.knowledgeId)"
>
</el-switch>
</el-tooltip>
</el-switch>
</div>
<div class="knowledge-desc" :title="updateKnowledge.desc">{{updateKnowledge.desc ? updateKnowledge.desc : '—'}}</div>
<div class="knowledge-desc" :title="updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'">{{updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'}}</div>
</div>
</div>
<template v-if="!showAddUpdateDialog">
<div class="knowledge-update" >
<div class="update-title">
<div class="card-title-name">update record</div>
</div>
<div class="knowledge-update__tab" v-if="showEnable">
<el-tabs v-model="activeTab"
class="update-log-tab"
@tab-click="handleClick"
>
<el-tab-pane :label="$t('knowledgeBase.updateRecord')"
name="updateRecord"
key="updateRecord"
ref="knowledgeUpdateRecordTab">
</el-tab-pane>
<el-tab-pane :label="$t('knowledgeBase.learningEngineLogs')"
name="intelligenceLearning"
key="intelligenceLearning"
ref="knowledgeIntelligenceLearningTab">
</el-tab-pane>
</el-tabs>
<div class="update-operate">
<button :title="$t('overall.update')" class="top-tool-btn--update"
@click="uploadRecord">
@click="uploadRecord"><!--:disabled="hasUpdatingRecord"-->
<i class="cn-icon-update-knowledge-base cn-icon"></i>
<span>{{$t('overall.update')}}</span>
</button>
</div>
</div>
<div class="knowledge-update" v-else>
<div class="update-title" >
<div class="card-title-name">{{$t('knowledgeBase.updateRecord')}}</div>
</div>
<div class="update-operate">
<button :title="$t('overall.update')" class="top-tool-btn--update"
@click="uploadRecord"><!-- :disabled="hasUpdatingRecord" -->
<i class="cn-icon-update-knowledge-base cn-icon"></i>
<span>{{$t('overall.update')}}</span>
</button>
</div>
</div>
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning' ? 'calc(90vh - 190px - 200px - 50px - 42px)' : 'calc(100% - 242px)', marginTop: '42px', position: 'absolute', width: 'calc(100% - 60px)'}">
<loading :loading="updateLogLoading"></loading>
</div>
<el-table ref="updateDataTable"
border
:data="updateHistoryList"
@selection-change="secondSelectionChange"
width="100%"
class="update-dialog__table"
:class="{
'update-dialog__table--psiphon3': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning',
'update-dialog__table--system-user': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab !== 'intelligenceLearning'
}"
:header-cell-style="{background:'#f5f7fa',color:'#353636',fontWeight: '400',fontSize: '12px',borderRight: 'none',borderBottom: 'none'}"
cell-style="padding:6px 0px;font-size: 12px;color: #353636;font-weight: 400;line-height: 20px;border-right:none;"
header-cell-style="padding:8px 0px;font-size: 12px;color: #353636;font-weight: 500;border-right:none;">
<el-table-column prop="opTime" label="Update time" width="150" ></el-table-column>
<el-table-column prop="user" label="Operating user" width="150" >
<el-table-column prop="opTime" :label="$t('entities.tab.informationAggregation.updateTime')" width="150" >
<template #default="scope" :column="item">
<span>{{scope.row.opTime ? dateFormatByAppearance(scope.row.opTime) : '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="updateKnowledge.source !== 'cn_psiphon3_ip' || activeTab === 'updateRecord'">
<template #default="scope" :column="item">
<span>{{$_.get(scope.row, 'user.name', '-')}}</span>
</template>
</el-table-column>
<el-table-column prop="commitVersion" label="Version information" width="150" ></el-table-column>
<el-table-column prop="description" label="Description"></el-table-column>
<el-table-column prop="commitVersion" :label="$t('overall.version')" width="150" ></el-table-column>
<el-table-column prop="description" :label="$t('overall.remark')"></el-table-column>
<template v-slot:empty >
<div class="table-no-data" v-if="updateHistoryList.length === 0 && !updateLogLoading">
<div class="table-no-data__title">{{ $t('npm.noData') }}</div>
</div>
<div v-else></div>
</template>
</el-table>
<div class="psiphon3" v-if="updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning'">
<div class="psiphon3-title">{{$t('knowledgeBase.psiphon3IpCount')}}</div>
<div class="psiphon3-bar">
<chart-error v-if="showErrorForPsiphon3" :content="errorMsgForPsiphon3"/>
<div class="bar-header" v-else>
<div class="bar-header-left">
<div class="bar-value-active" ></div>
<div class="bar-value">
<template v-for="(item, index) in tabs" :key="index">
<div class="bar-value-tabs"
:class=" {'is-active': tabType === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
@mouseenter="mouseenterTab(item)"
@mouseleave="mouseleaveTab(item)"
@click="activeChange(item)"
>
<div class="bar-value-tabs-name">
<div :class="item.class"></div>
<div class="tabs-name" >{{ $t(item.name) }}</div>
</div>
</div>
</template>
</div>
</div>
<div class="bar-select bar-header-right">
<div class="bar-select-time">
<div class="bar-select__operation">
<el-select
size="mini"
v-model="selectTime"
placeholder=" "
popper-class="common-select"
:popper-append-to-body="false"
@change="timeChange"
>
<template #prefix>
<div class="calendar-popover-text"><i class="cn-icon cn-icon-Data"></i></div>
</template>
<el-option v-for="item in dateRangeArr" :key="item.value" :label="item.name" :value="item.value"></el-option>
</el-select>
</div>
</div>
</div>
</div>
<div style="height: calc(100% - 24px); position: relative">
<chart-no-data v-if="isNoDataForPsiphon3 && !showErrorForPsiphon3 && !psiphon3Loading"></chart-no-data>
<loading :loading="psiphon3Loading"></loading>
<div class="chart-drawing" v-show="!isNoDataForPsiphon3 && !showErrorForPsiphon3" id="psiphonBarChart"></div>
</div>
</div>
</div>
</template>
<template v-if="showAddUpdateDialog">
<div class="update-knowledge-form">
@@ -179,7 +257,7 @@
</el-form>
</div>
<div class="dialog-footer">
<el-button @click="showAddUpdateDialog = false">{{ $t('overall.cancel') }}</el-button>
<el-button @click="cancle">{{ $t('overall.cancel') }}</el-button>
<el-button type="primary" @click="submitConfirm">{{ $t('tip.confirm') }}</el-button>
</div>
</template>
@@ -187,6 +265,7 @@
<el-dialog v-model="showConfirmDialog"
:title="$t('overall.tips')"
custom-class="update-knowledge-tip"
:width="480"
:before-close="handleConfirmClose">
<div class="dialog-message">{{$t('knowledge.updateTips')}}</div>
<template #footer>
@@ -196,18 +275,37 @@
</span>
</template>
</el-dialog>
<el-dialog
v-model="showConfirmSwitch"
:width="330"
custom-class="confirm-knowledge-switch"
:title="$t('overall.hint')"
>
<div class="dialog-message">{{ confirmSwitchLearningTip }}</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="cancleSwitch">{{ $t('overall.cancel') }}</el-button>
<el-button type="primary" @click="switchLearning">{{$t('tip.confirm')}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
import table from '@/mixins/table'
import Loading from '@/components/common/Loading'
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo } from '@/utils/constants'
import { ref } from 'vue'
import { getSecond, getMillisecond, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo, knowledgeCardUpdateRecordType } from '@/utils/constants'
import { ref, shallowRef } from 'vue'
import { api } from '@/utils/api'
import { detectionTooltipFormatter } from '@/views/charts/charts/tools'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import axios from 'axios'
import _ from 'lodash'
import * as echarts from 'echarts'
import unitConvert from '@/utils/unit-convert'
export default {
name: 'knowledgeBaseTableForCard',
mixins: [table],
@@ -221,7 +319,8 @@ export default {
}
},
components: {
Loading
Loading,
ChartNoData
},
data () {
return {
@@ -237,7 +336,48 @@ export default {
updateHistoryList: [],
updateObject: {},
currentVersion: 0,
uploadLoading: false
uploadLoading: false,
psiphon3Loading: false,
updateLogLoading: false,
showConfirmSwitch: false,
// timer: null,
switchKnowledgeId: '',
activeTab: 'updateRecord',
isNoDataForPsiphon3: false,
showErrorForPsiphon3: false,
errorMsgForPsiphon3: '',
leftOffset: 0,
tabType: 'total',
mousemoveCursor: '',
selectTime: 1440,
// hasUpdatingRecord: false,
tabs: [
{
name: 'knowledgeBase.total',
class: 'total',
color: '#00A7AB',
data: []
},
{
name: 'knowledgeBase.active',
class: 'active',
color: '#7FA054',
data: []
},
{
name: 'knowledgeBase.new',
class: 'new',
color: '#98709B',
data: []
}
],
dateRangeArr: [
{ value: 1440, name: this.$t('dateTime.last1Day') },
{ value: 2880, name: this.$t('dateTime.last2Days') },
{ value: 10080, name: this.$t('dateTime.last7Days') },
{ value: 21600, name: this.$t('dateTime.last15Days') },
{ value: 43200, name: this.$t('dateTime.last30Days') }
]
}
},
setup () {
@@ -252,13 +392,177 @@ export default {
uploadErrorTip,
fileTypeLimit: '.csv',
fileList: ref([]),
uploadFileSizeLimit: 100 * 1024 * 1024
uploadFileSizeLimit: 1024 * 1024 * 1024,
myChart: shallowRef(null),
chartOption: shallowRef(null)
}
},
methods: {
echartsInit (echartsData) {
const _this = this
const curTab = this.tabs.find(item => item.class === _this.tabType)
this.chartOption = {
color: curTab.color,
legend: {
show: false
},
tooltip: {
show: true,
formatter: (params) => {
params.seriesName = this.$t(params.seriesName)
params.borderColor = params.color
return detectionTooltipFormatter(params)
}
},
grid: {
top: '12%',
left: '2%',
right: '2%',
bottom: 24,
containLabel: true
},
xAxis: {
type: 'time',
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
formatter: xAxisTimeFormatter,
rich: xAxisTimeRich
}
},
yAxis: {
type: 'value',
splitLine: {
show: true,
lineStyle: {
color: '#ECECEC'
}
},
axisLabel: {
margin: 20
},
minInterval: 1
},
series: [
{
name: curTab.name,
data: echartsData,
type: 'bar',
barWidth: 26
}
]
}
this.$nextTick(() => {
if (!this.myChart) {
this.myChart = echarts.init(document.getElementById('psiphonBarChart'))
}
this.myChart.setOption(this.chartOption)
})
},
init (val, show, active, n) {
this.psiphon3Loading = true
const endTime = window.$dayJs.tz().valueOf()
const params = {
startTime: getSecond(endTime - this.selectTime * 60 * 1000),
endTime: getSecond(endTime)
}
const url = api.knowledgeBaseTimedistribution.replace('{{knowledgeId}}', this.updateKnowledge.knowledgeId).replace('{{type}}', this.tabType)
axios.get(url, { params: params }).then(response => {
const res = response.data
if (response.status === 200) {
this.isNoDataForPsiphon3 = res.data.result.length === 0
this.showErrorForPsiphon3 = false
if (!this.isNoDataForPsiphon3) {
const chartsData = res.data.result.map(item => {
return [getMillisecond(item.statTime), item.count]
})
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.echartsInit(chartsData)
}
}
} else {
this.httpError(res)
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.psiphon3Loading = false
})
},
httpError (e) {
this.isNoDataForPsiphon3 = false
this.showErrorForPsiphon3 = true
this.errorMsgForPsiphon3 = this.errorMsgHandler(e)
},
handleActiveBar () {
if (document.querySelector('.psiphon3-bar .bar-value-tabs.is-active')) {
const {
offsetLeft,
clientWidth,
clientLeft
} = document.querySelector('.psiphon3-bar .bar-value-tabs.is-active')
const activeBar = document.querySelector('.psiphon3-bar .bar-value-active')
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
}
},
resize () {
if (this.myChart) {
this.myChart.resize()
}
},
dispatchSelectAction (type, name) {
this.myChart && this.myChart.dispatchAction({
type: type,
name: name
})
},
legendSelectChange (item) {
this.dispatchSelectAction('legendSelect', item.name)
this.tabs.forEach((t) => {
if (t.name !== item.name) {
this.dispatchSelectAction('legendUnSelect', t.name)
}
})
},
timeChange () {
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
activeChange (item) { // isClick:代表是通过点击操作来的
if (item) {
this.tabType = item.class
}
this.legendSelectChange(item)
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
},
mouseenterTab (item) {
if (this.isNoDataForPsiphon3) return
this.mousemoveCursor = item.class
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
mouseleaveTab () {
this.mousemoveCursor = ''
},
fileChange (file, fileList) {
console.info(!_.endsWith(file.name, '.csv'))
console.info(file.size > this.uploadFileSizeLimit)
// 判断后缀,仅支持.csv
if (!_.endsWith(file.name, '.csv')) {
this.fileList = []
@@ -283,17 +587,9 @@ export default {
uploadSuccess (response) {
this.uploadLoading = false
this.uploaded = true
/* this.uploaded = response.code === 200
if (response.code === 200) { */
this.$message.success(this.$t('tip.success'))
this.showAddUpdateDialog = false
axios.get(api.knowledgeBaseLog + '/' + this.updateKnowledge.knowledgeId, { params: { pageSize: 999 } }).then(res => {
this.updateHistoryList = res.data.data.list
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
})
/* } else {
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
} */
this.getCurTabData()
},
beforeUpload (file) {
this.uploadLoading = true
@@ -305,6 +601,9 @@ export default {
submit () {
this.$refs.knowledgeUpload.submit()
},
cancle () {
this.showAddUpdateDialog = false
},
clickCard (data, event) {
if (data.isSelected) { // 原来为选中,当前点击后未选中
const index = this.checkList.indexOf(data)
@@ -325,6 +624,13 @@ export default {
data.isSelected = val
this.$emit('checkboxStatusChange', val, data)
},
beforeClose (done) {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
done()
},
handleClose () {
this.showUpdateDialog = false
this.showAddUpdateDialog = false
@@ -340,14 +646,19 @@ export default {
this.showUpdateDialog = true
this.showAddUpdateDialog = false
},
jumpToUpdatePage (data, showEnable) {
axios.get(api.knowledgeBaseLog + '/' + data.knowledgeId, { params: { pageSize: 999 } }).then(res => {
this.updateKnowledge = data
this.updateHistoryList = res.data.data.list
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
this.showEnable = showEnable
this.showUpdate()
})
async jumpToUpdatePage (data, showEnable) {
this.updateKnowledge = data
this.showEnable = showEnable
await this.getCurTabData()
if (data.source === 'cn_psiphon3_ip') {
await this.init()
}
this.showUpdate()
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
uploadRecord () {
this.showAddUpdateDialog = true
@@ -355,6 +666,52 @@ export default {
this.updateObject.label = this.updateKnowledge.label
this.updateObject.description = ''
},
getCurTabData () { // showEnable:true 为psiphon3的知识库false为其它知识库
let params = {
pageSize: -1
}
if (this.showEnable) {
if (this.activeTab === knowledgeCardUpdateRecordType.updateRecord) {
params = {
...params,
opUser: -1
}
} else if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
params = {
...params,
opUser: 0
}
}
}
this.updateLogLoading = true
this.updateHistoryList = []
axios.get(api.knowledgeBaseLog + '/' + this.updateKnowledge.knowledgeId, { params: params }).then(res => {
this.updateHistoryList = res.data.data.list
if (this.updateHistoryList[0]) {
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
}
/*
this.hasUpdatingRecord = false
this.updateHistoryList.forEach(item => {
if (item.isUpdating) { // if(item.isUpdating){//????????
this.hasUpdatingRecord = true
}
})
*/
}).catch(e => {
console.error(e)
}).finally(() => {
this.updateLogLoading = false
})
},
// 切换tab
handleClick (tab) {
this.getCurTabData()
if (tab.index === '1') {
this.timeChange()
}
},
clearSelect () {
this.$nextTick(() => {
this.checkList = []
@@ -366,18 +723,10 @@ export default {
})
},
mouseenter (card) {
this.tableData.forEach(t => {
if (t.knowledgeId === card.knowledgeId) {
card.showUpdate = true
}
})
card.showUpdate = true
},
mouseleave (card) {
this.tableData.forEach(t => {
if (t.knowledgeId === card.knowledgeId) {
card.showUpdate = false
}
})
card.showUpdate = false
},
del (data) {
this.$emit('delete', data)
@@ -394,9 +743,50 @@ export default {
dataType: dataType
}
})
},
confirmSwitchLearning (id) {
this.showConfirmSwitch = true
this.switchKnowledgeId = id
return false
},
cancleSwitch () {
this.showConfirmSwitch = false
},
switchLearning () {
const hint = this.aiTaggingList.find(d => d.knowledgeId === this.switchKnowledgeId)
const toStatus = hint.status === 0 ? 1 : 0
const url = toStatus === 0 ? api.knowledgeBaseLearningStop : api.knowledgeBaseLearningStart
axios.post(`${url}?knowledgeId=${hint.knowledgeId}`).then(res => {
if (res.status === 200) {
hint.status = toStatus
this.$message.success(this.$t('tip.success'))
} else {
console.error(res.message)
this.$message.error(this.errorMsgHandler(res))
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
}).finally(() => {
this.showConfirmSwitch = false
})
}
},
watch: {
tabType (n) {
this.timeChange()
},
/*
hasUpdatingRecord (n) {
if (n) { // update record页存在“正在更新”的记录时每20秒自动请求一次接口
this.timer = setTimeout(() => {
this.getCurTabData()
}, 20000)
} else { // 直到出现新的记录,出现新记录后(失败或者成功),取消定时请求接口,右上角"update"按钮恢复可用。"正在更新"和"失败都会有对应的强调样式
clearTimeout(this.timer)
}
},
*/
tableData: {
handler (n) {
if (this.tableData && this.tableData.length > 0) {
@@ -420,15 +810,33 @@ export default {
}
}
},
activeTab (n) {
if (n === 'updateRecord') {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
}
},
showAddUpdateDialog: {
handler (n) {
if (!n) {
this.fileList = []
this.timeChange()
} else {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
}
}
}
},
mounted () {
this.myChart = null
this.chartOption = null
window.addEventListener('resize', this.resize)
this.aiTaggingList = []
this.websketchList = []
this.tableData.forEach(item => {
@@ -440,6 +848,22 @@ export default {
}
})
},
beforeUnmount () {
//clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
const dom = document.getElementById('psiphonBarChart')
if (dom) {
let myChart = echarts.getInstanceByDom(document.getElementById('psiphonBarChart'))
if (myChart) {
echarts.dispose(myChart)
}
myChart = null
}
if (this.myChart) {
echarts.dispose(this.myChart)
}
},
computed: {
uploadParams () {
return {
@@ -447,6 +871,20 @@ export default {
action: 'overwrite',
description: this.updateObject.description
}
},
confirmSwitchLearningTip () {
let tip = ''
if (this.switchKnowledgeId) {
const find = this.aiTaggingList.find(item => item.knowledgeId === this.switchKnowledgeId)
if (find) {
if (find.status === 0) {
tip = this.$t('tip.confirmEnablePsiphon3') + '?'
} else if (find.status === 1) {
tip = this.$t('tip.confirmDisablePsiphon3') + '?'
}
}
}
return tip
}
}
}

View File

@@ -1,10 +1,10 @@
import { createI18n } from 'vue-i18n/index'
import { storageKey } from '@/utils/constants'
import { storageKey, EN } from '@/utils/constants'
import { getI18n } from '@/utils/api'
import store from '@/store'
const i18n = createI18n({
locale: localStorage.getItem(storageKey.language) || 'en'
locale: localStorage.getItem(storageKey.language) || EN
})
export async function loadI18n () {
if (!store.state.i18n) {

View File

@@ -5,9 +5,8 @@ import router from '@/router'
import store from '@/store'
import App from '@/App.vue'
import '@/utils/http.js'
import { hasPermission } from '@/permission'
import commonMixin from '@/mixins/common'
import { cancelWithChange, noData } from '@/utils/tools'
import { cancelWithChange, noData, myHighLight } from '@/utils/tools'
import { ClickOutside } from 'element-plus/lib/directives'
import i18n from '@/i18n'
// import '@/mock/index.js'
@@ -37,10 +36,10 @@ app.use(i18n)
app.use(hljsVuePlugin)
app.use(VueGridLayout)
app.directive('has', hasPermission) // 注册指令
app.directive('ele-click-outside', ClickOutside)
app.directive('cancel', cancelWithChange)
app.directive('no-data', noData)
app.directive('high-light', myHighLight)
app.config.globalProperties.$_ = _
app.mixin(commonMixin)

View File

@@ -1,4 +1,4 @@
import { hasButton } from '@/permission'
import { hasPermission } from '@/permission'
import { dateFormatByAppearance } from '@/utils/date-util'
import { commonErrorTip } from '@/utils/constants'
export default {
@@ -28,9 +28,7 @@ export default {
}
},
methods: {
hasButton (code) {
return hasButton(this.$store.getters.buttonList, code)
},
hasPermission,
errorMsgHandler (axiosError) {
if (axiosError.response) {
if (axiosError.response.data) {

View File

@@ -110,64 +110,26 @@ export default {
if (this.listUrl) {
listUrl = this.listUrl
}
// todo 此段是为了避免mock没开启打开detection界面报错提示后续再开发detection时删除
if (listUrl === api.detection.list) {
const list = []
for (let i = 0; i < 20; i++) {
const obj = {
ruleId: 100000 + i,
ruleType: 'indicator_match',
status: 1,
name: 'name123',
category: 'Security Event',
eventType: 'C&C',
description: 'Built-in darkweb IoC',
ruleConfig: {
knowledge: {
name: 'VPN Server IP',
category: 'user_defined'
}
}
}
if (i % 2 === 0) {
obj.ruleType = 'threshold'
obj.ruleConfig = {
dimensions: 'Destination IP/CIDR'
}
obj.description = 'abuse.ch is providing community driven threat intelligence on \n' +
'cyber threats. It is the home of a couple of projects that are \n' +
'helping internet service providers and network operators protect …'
} else {
obj.status = 0
}
list.push(obj)
}
this.tableData = list
this.pageObj.total = list.length
this.loading = false
} else {
axios.get(listUrl, { params: this.searchLabel }).then(response => {
if (response.status === 200) {
this.tableData = _.get(response, 'data.data.list', [])
this.pageObj.total = _.get(response, 'data.data.total', 0)
this.isNoData = !this.tableData || this.tableData.length === 0
} else {
console.error(response)
this.isNoData = true
if (response.data.message) {
this.$message.error(response.data.message)
} else {
this.$message.error(this.$t('tip.somethingWentWrong'))
}
}
}).catch(() => {
axios.get(listUrl, { params: this.searchLabel }).then(response => {
if (response.status === 200) {
this.tableData = _.get(response, 'data.data.list', [])
this.pageObj.total = _.get(response, 'data.data.total', 0)
this.isNoData = !this.tableData || this.tableData.length === 0
} else {
console.error(response)
this.isNoData = true
}).finally(() => {
this.toggleLoading(false)
this.loading = false
})
}
if (response.data.message) {
this.$message.error(response.data.message)
} else {
this.$message.error(this.$t('tip.somethingWentWrong'))
}
}
}).catch(() => {
this.isNoData = true
}).finally(() => {
this.toggleLoading(false)
this.loading = false
})
},
del (row) {
this.$confirm(this.$t('tip.confirmDelete'), {
@@ -400,10 +362,23 @@ export default {
this.searchLabel.orderBy = orderBy
this.getTableData()
},
search (params) {
search (params, flag, list) {
this.pageObj.pageNo = 1
delete this.searchLabel.category
delete this.searchLabel.source
if (flag !== 'detection') {
delete this.searchLabel.category
delete this.searchLabel.source
}
if (list && list.length > 0) {
if (list.indexOf('status') > -1) {
delete this.searchLabel.status
}
if (list.indexOf('category') > -1) {
delete this.searchLabel.category
}
if (list.indexOf('eventType') > -1) {
delete this.searchLabel.eventType
}
}
this.getTableData(params)
},
getTimeString () {

View File

@@ -17,7 +17,7 @@ export default {
// 请求数据 relationshipUrlOne => 路由 refOne => ref
getRelatedServerDataOne (relationshipUrlOne, refOne) {
this.loadingRelationshipOne = true
axios.get(relationshipUrlOne, { params: this.getQueryParams() }).then(response => {
axios.get(relationshipUrlOne, { params: this.getQueryParams(DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity) }).then(response => {
if (response.status === 200) {
const relationshipDataOne = []
if (response.data.data.result.length > 0) {
@@ -33,7 +33,7 @@ export default {
},
getRelatedServerDataTwo (relationshipUrlTow, refTow) {
this.loadingRelationshipTwo = true
axios.get(relationshipUrlTow, { params: this.getQueryParams() }).then(response => {
axios.get(relationshipUrlTow, { params: this.getQueryParams(DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity) }).then(response => {
if (response.status === 200) {
const relationshipDataTwo = []
if (response.data.data.result.length > 0) {
@@ -96,5 +96,41 @@ export default {
this.relationshipShowMoreTwo = false
this.relationshipShowMoreOne = false
}
},
computed: {
scoreDot () {
const dots = []
if (this.score === '-') {
for (let i = 0; i < 6; i++) {
dots.push({
class: 'score-dot'
})
}
} else {
for (let i = 0; i < 6; i++) {
if (i < this.score) {
dots.push({
class: `score-dot ${handleClass(this.score)}`
})
} else {
dots.push({
class: 'score-dot'
})
}
}
}
return dots
function handleClass (score) {
if (score <= 2) {
return 'score-dot--red'
} else if (score <= 4) {
return 'score-dot--yellow'
} else if (score <= 6) {
return 'score-dot--green'
}
return ''
}
}
}
}

View File

@@ -1,4 +1,4 @@
import { chartTableOrderOptionsMapping, storageKey, knowledgeCategoryValue } from '@/utils/constants'
import { chartTableOrderOptionsMapping, storageKey, knowledgeCategoryValue, ZH } from '@/utils/constants'
import { getWidthByLanguage } from '@/utils/tools'
import { api } from '@/utils/api'
import axios from 'axios'
@@ -58,7 +58,7 @@ export default {
const language = localStorage.getItem(storageKey.language)
// 文字所占宽度一个英文字母占7px中文16px
let num = getWidthByLanguage(language) || 7
if (language !== 'cn') {
if (language !== ZH) {
num = num + 1 // 最后一位加空格
}

View File

@@ -7,7 +7,7 @@ if (openMock) {
const list = []
for (let i = 0; i < 20; i++) {
const obj = {
ruleId: 100000 + i,
ruleId: 163 + i,
ruleType: 'indicator_match',
status: 1,
name: 'name123',
@@ -50,51 +50,17 @@ if (openMock) {
Mock.mock(new RegExp(urlAndVersion + '/detection/statistics.*'), 'get', function (requestObj) {
const data = {
statusList: [
{ status: 1 },
{ status: 0 }
{ status: 1, count: 34 },
{ status: 0, count: 28 }
],
categoryList: [
{ value: 'security', label: 'Security Event' },
{ value: 'performance', label: 'Performance Event' },
{ value: 'regulatory_risk', label: 'Regulatory Risk Event' }
{ name: 'Security Event', count: 32 },
{ name: 'Performance Event', count: 28 }
],
typeList: [
{ value: 'c&c', label: 'C&C' },
{ value: 'ddos', label: 'DDos' },
{ value: 'lateral_movement', label: 'Lateral movement' },
{ value: 'brute_force', label: 'Brute force' }
],
sourceList: [
{ value: 'ip_metric', label: 'IP metric' },
{ value: 'performance_event', label: 'performance event' }
],
levelList: [
{ value: 'critical', label: 'Critical' },
{ value: 'high', label: 'High' },
{ value: 'medium', label: 'Medium' },
{ value: 'low', label: 'Low' },
{ value: 'info', label: 'Info' }
],
metricList: [
{ value: 'tcp_lostlen_ratio', label: 'Bits/second' },
{ value: 's2c_byte_retrans_ratio', label: 'Packets/second' },
{ value: 's2c_byte_retrans_ratio1', label: 'Sessions/second' }
],
conditionList: [
{ value: 'than', label: 'Greater Than' },
{ value: 'less', label: 'Greater Less' },
{ value: 'equal', label: 'Greater Equal' }
],
libraryList: [
{ value: 'library name2', knowledgeId: '101', label: 'Library name' },
{ value: 'library name1', knowledgeId: '102', label: 'Library name1' },
{ value: 'library name2', knowledgeId: '103', label: 'Library name2' }
],
intervalList: [
{ value: 'minutes', label: 'minutes' },
{ value: 'hours', label: 'hours' },
{ value: 'days', label: 'days' },
{ value: 'weeks', label: 'weeks' }
eventTypeList: [
{ name: 'DDos', count: 15 },
{ name: 'Lateral movement', count: 17 },
{ name: 'Brute force', count: 12 }
]
}
@@ -129,28 +95,50 @@ if (openMock) {
const ruleId = getLastValue(requestObj.url)
const data = {
name: 'name123',
category: 'Security Event',
category: 'security_event',
ruleType: 'indicator_match',
eventType: 'C&C',
description: 'Built-in darkweb IoC',
status: 1,
ruleConfig: {
dataSource: 'VPN Server IP',
knowledgeId: 10,
level: 10
knowledgeBase: {
knowledgeId: 10,
name: 'cn_ioc_darkweb',
category: 'websketch',
source: 'cn_ioc_darkweb'
},
level: 'critical'
},
trigger: {
ruleConfigObj: {
dataSource: 'VPN Server IP',
knowledgeBase: {
knowledgeId: '101',
name: 'cn_ioc_darkweb',
category: 'websketch',
source: 'cn_ioc_darkweb'
},
level: 'critical'
},
ruleTrigger: {
atLeast: 1,
interval: 'PT5M',
resetInterval: 'PT10M'
},
ruleTriggerObj: {
atLeast: 1,
interval: 'PT5M',
resetInterval: 'PT10M'
}
}
data.ruleConfig = JSON.stringify(data.ruleConfig)
data.trigger = JSON.stringify(data.trigger)
if (ruleId % 2 === 0) {
data.ruleType = 'threshold'
data.status = 1
} else {
data.status = 0
} else {
data.status = 1
}
return {

353
src/mock/detectionList.js Normal file
View File

@@ -0,0 +1,353 @@
import Mock from 'mockjs'
const urlAndVersion = BASE_CONFIG.baseUrl + BASE_CONFIG.apiVersion
const openMock = true
if (openMock) {
Mock.mock(new RegExp(urlAndVersion + '/detection/security/list.*'), 'get', function (requestObj) {
const result = []
for (let i = 0; i < 20; i++) {
const obj = {
eventId: 1212,
eventType: 'Anonymity',
eventName: 'Tor',
eventKey: '2,1.1.1,12.2.2.2',
ruleId: 2,
ruleType: 'indicator_match',
isBuiltin: 1,
severity: 'critical',
offenderIp: '1.1.1.1',
victimIp: '2.2.2.2',
domain: '*.vioee.com',
app: 'vio',
startTime: 1697092617,
endTime: 1697092777,
durationS: 30000,
matchTimes: 1,
status: 1,
eventInfo: "{\'knowledge_id\': 123, \'name\': \'example_ioc_malware\', \'ioc_type\': \'domain\', \'ioc_value\': \'iocentity.com\'}",
eventInfoObj: {
knowledge_id: 123,
name: 'example_ioc_malware',
ioc_type: 'domain',
ioc_value: 'iocentity.com'
}
}
if (i % 2 === 0) {
obj.status = 0
}
result.push(obj)
}
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/severity/timedistribution.*'), 'get', function (requestObj) {
const result = [
{
statTime: 1697523900,
severity: 'critical',
count: 25
},
{
statTime: 1697524200,
severity: 'high',
count: 31
},
{
statTime: 1697524500,
severity: 'info',
count: 21
},
{
statTime: 1697524800,
severity: 'low',
count: 25
},
{
statTime: 1697525100,
severity: 'medium',
count: 32
},
{
statTime: 1697525400,
severity: 'critical',
count: 22
},
{
statTime: 1697525700,
severity: 'critical',
count: 23
},
{
statTime: 1697526000,
severity: 'critical',
count: 25
},
{
statTime: 1697526300,
severity: 'critical',
count: 21
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/severity/statistics.*'), 'get', function (requestObj) {
const result = [
{
severity: 'critical',
count: 25
},
{
severity: 'high',
count: 31
},
{
severity: 'info',
count: 21
},
{
severity: 'low',
count: 25
},
{
severity: 'medium',
count: 32
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/event-type/statistics.*'), 'get', function (requestObj) {
const result = [
{
eventType: 'Anonymity',
count: 25
},
{
eventType: 'Command and Control',
count: 31
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/offender-ip/statistics.*'), 'get', function (requestObj) {
const result = [
{
offenderIp: '221.7.1.20',
count: 25
},
{
offenderIp: '58.247.118.253',
count: 31
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/victim-ip/statistics.*'), 'get', function (requestObj) {
const result = [
{
victimIp: '21.77.1.201',
count: 25
},
{
victimIp: '58.47.118.153',
count: 31
},
{
victimIp: '22.47.223.57',
count: 43
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/status/statistics.*'), 'get', function (requestObj) {
const result = [
{
status: 1,
count: 25
},
{
status: 0,
count: 31
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/ip/relation/event.*'), 'get', function (requestObj) {
const result = [
{
eventId: 10010,
severity: 'high',
eventType: 'Command and Control',
offenderIp: '1.1.1.1',
victimIp: '2.2.2.2',
startTime: 1697092617
}
]
const data = {
resultType: 'table',
result: result
}
return {
msg: 'OK',
code: 200,
data: data
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/count.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
resultType: 'single',
result: 123
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/ip.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
asn: {
id: 2,
asn: '14061',
organization: 'DIGITALOCEAN-ASN - DigitalOcean, LLC, US'
},
malware: {
threatType: 'command and control',
malwareName: 'IcedID',
malwareAlias: 'BokBot,IceID',
mitreAttackDescription: '[IcedID](https://attack.mitre.org/software/S0483) is a modular banking malware designed to steal financial information that has been observed in the wild since at least 2017. [IcedID](https://attack.mitre.org/software/S0483) has been downloaded by [Emotet](https://attack.mitre.org/software/S0367) in multiple campaigns.(Citation: IBM IcedID November 2017)(Citation: Juniper IcedID June 2020)',
mitreAttackPlatforms: 'Windows',
mitreAttackTechniques: '[""Asymmetric Cryptography(T1573.002)"",""Asynchronous Procedure Call(T1055.004)"",""Browser Session Hijacking(T1185)"",""Domain Account(T1087.002)"",""Ingress Tool Transfer(T1105)"",""Malicious File(T1204.002)"",""Msiexec(T1218.007)"",""Native API(T1106)"",""Obfuscated Files or Information(T1027)"",""Permission Groups Discovery(T1069)"",""Registry Run Keys / Startup Folder(T1547.001)"",""Scheduled Task(T1053.005)"",""Software Packing(T1027.002)"",""Spearphishing Attachment(T1566.001)"",""Steganography(T1027.003)"",""System Information Discovery(T1082)"",""Visual Basic(T1059.005)"",""Web Protocols(T1071.001)"",""Windows Management Instrumentation(T1047)""]',
mitreAttackGroups: '[""TA551(G0127)""]',
confidenceLevel: 'high',
reference: ''
},
location: {
continent: 'North America',
country: 'United States',
province: 'New York',
city: '',
lngwgs: '-74.006',
latwgs: '40.713',
isp: 'dba Omsoft',
owner: 'tie net'
}
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/domain.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
malware: {
threatType: 'command and control',
malwareName: 'IcedID',
malwareAlias: 'BokBot,IceID',
mitreAttackDescription: '[IcedID](https://attack.mitre.org/software/S0483) is a modular banking malware designed to steal financial information that has been observed in the wild since at least 2017. [IcedID](https://attack.mitre.org/software/S0483) has been downloaded by [Emotet](https://attack.mitre.org/software/S0367) in multiple campaigns.(Citation: IBM IcedID November 2017)(Citation: Juniper IcedID June 2020)',
mitreAttackPlatforms: 'Windows',
mitreAttackTechniques: '[""Asymmetric Cryptography(T1573.002)"",""Asynchronous Procedure Call(T1055.004)"",""Browser Session Hijacking(T1185)"",""Domain Account(T1087.002)"",""Ingress Tool Transfer(T1105)"",""Malicious File(T1204.002)"",""Msiexec(T1218.007)"",""Native API(T1106)"",""Obfuscated Files or Information(T1027)"",""Permission Groups Discovery(T1069)"",""Registry Run Keys / Startup Folder(T1547.001)"",""Scheduled Task(T1053.005)"",""Software Packing(T1027.002)"",""Spearphishing Attachment(T1566.001)"",""Steganography(T1027.003)"",""System Information Discovery(T1082)"",""Visual Basic(T1059.005)"",""Web Protocols(T1071.001)"",""Windows Management Instrumentation(T1047)""]',
mitreAttackGroups: '[""TA551(G0127)""]',
confidenceLevel: 'high',
reference: ''
},
category: {
categoryName: '门户网站',
categoryGroup: '互联网',
reputationLevel: 'high'
}
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/app.*'), 'get', function (requestObj) {
return {
msg: 'OK',
code: 200,
data: {
category: {
appName: 'QQ',
appId: 333,
appCategory: '娱乐',
appSubcategory: '聊天',
appRisk: 'low',
appDescription: '聊天社交软件',
appLongname: 'Tencent qq',
appTechnology: 'socket',
appCompany: 'tencent',
appCompanyCategory: '互联网'
}
}
}
})
}

View File

@@ -3,3 +3,4 @@ import './linkMonitor'
import './dns'
import './entity'
import './detection'
import './detectionList'

View File

@@ -32,14 +32,19 @@ router.beforeEach(async (to, from, next) => {
store.commit('setMenuList', menuList)
store.commit('setButtonList', buttonList)
store.commit('setRoleList', roleList)
}
if (to.path) {
next()
/* if (hasMenu(store.getters.menuList, to.path)) {
const homeRoute = {
path: '/',
name: 'home',
component: () => import('@/components/layout/Home'),
children: []
}
handleRoutes(menuList, homeRoute.children)
router.addRoute(homeRoute)
next({ ...to, replace: true })
} else {
if (to.path) {
next()
} else {
ElMessage.error('No access') // TODO 国际化
} */
}
}
}
} else {
@@ -60,14 +65,14 @@ router.beforeEach(async (to, from, next) => {
}
})
// menuList中是否包含route权限
export function hasMenu (menuList, route) {
// menuList中是否包含code
export function hasMenu (menuList, code) {
return menuList.some(menu => {
if (menu.route === route) {
if (menu.code === code) {
return true
} else {
if (menu.children) {
if (hasMenu(menu.children, route)) {
if (hasMenu(menu.children, code)) {
return true
}
}
@@ -96,36 +101,9 @@ export function hasButton (buttonList, code) {
return buttonList.some(button => button === code)
}
// 用法 v-has="code" | v-has="[code...]" 任意匹配一个 | v-has:all="[code...]" 全匹配
export const hasPermission = {
beforeMount (el, binding) {
// 节点权限处理
const buttonCode = binding.value
const arg = binding.arg
if (buttonCode) {
if (buttonCode instanceof Array) {
let has = true
if (arg && arg === 'all') { // 全匹配
buttonCode.forEach(button => {
if (has) {
has = hasButton(store.getters.buttonList, button)
}
})
} else { // 任意匹配
has = buttonCode.some(button => {
return hasButton(store.getters.buttonList, button)
})
}
if (!has) {
el.parentNode.removeChild(el)
}
} else { // 单个匹配
if (!hasButton(store.getters.buttonList, buttonCode)) {
el.parentNode && el.parentNode.removeChild(el)
}
}
}
}
// 根据code从menuList和buttonList中判断是否有权限
export function hasPermission (code) {
return hasButton(store.getters.buttonList, code) || hasMenu(store.getters.menuList, code)
}
// 根据orderNum排序
@@ -160,3 +138,100 @@ export function getWelcomeMenu (menu) {
}
}
}
export function handleComponent (code) {
switch (code) {
case 'networkOverview':
case 'networkAppPerformance':
case 'dnsServiceInsights':
case 'linkMonitor':
return () => import('@/views/charts2/Panel')
case 'entity':
return () => import('@/views/entityExplorer/EntityExplorer')
case 'entityDetail':
return () => import('@/views/entityExplorer/EntityDetail')
case 'entityGraph':
return () => import('@/views/entityExplorer/EntityGraph')
case 'securityEvents':
case 'performanceEvents':
return () => import('@/views/detections/Index')
case 'detectionPolicy':
return () => import('@/views/detections/detectionPolicies/Index')
case 'createDetectionPolicy':
case 'editDetectionPolicy':
return () => import('@/views/detections/detectionPolicies/PolicyForm')
case 'report':
return () => import('@/views/report/Report')
case 'knowledgeBase':
return () => import('@/views/setting/KnowledgeBase')
case 'userDefinedLibrary':
return () => import('@/views/setting/KnowledgeBaseUserDefinedList')
case 'createUserDefinedLibrary':
case 'editUserDefinedLibrary':
return () => import('@/views/setting/KnowledgeBaseForm')
case 'administration':
return () => import('@/views/administration/Index')
case 'user':
return () => import('@/views/administration/User')
case 'role':
return () => import('@/views/administration/Roles')
case 'operationLog':
return () => import('@/views/administration/OperationLog')
case 'appearance':
return () => import('@/views/administration/Appearance')
case 'I18N':
return () => import('@/views/administration/I18n')
default:
return null
}
}
export function handleRoutes (menus, routes) {
menus.forEach(menu => {
if (menu.route === '' && (!menu.children || menu.children.length < 0)) {
return false
}
// administration的路由使用了嵌套其他的是平铺
if (menu.pid === 0 && menu.code === 'administration') {
const path = menu.route.replace('redirect:', '')
if (menu.children && menu.children.length > 0) {
const route = {
name: menu.name,
path,
redirect: menu.children[0].route,
component: handleComponent(menu.code),
children: []
}
menu.children.forEach(c => {
route.children.push({
name: c.name,
path: c.route,
component: handleComponent(c.code)
})
})
routes.push(route)
}
} else {
if (menu.route && menu.route.startsWith('redirect:')) {
const path = menu.route.replace('redirect:', '')
if (menu.children && menu.children.length > 0) {
routes.push({
name: menu.name,
path,
redirect: menu.children[0].route
})
handleRoutes(menu.children, routes)
}
} else {
routes.push({
name: menu.code,
path: menu.route,
component: handleComponent(menu.code)
})
}
if (menu.children && menu.children.length > 0) {
handleRoutes(menu.children, routes)
}
}
})
}

View File

@@ -5,111 +5,6 @@ const routes = [
{
path: '/login',
component: () => import('@/Login')
},
{
path: '/',
component: () => import('@/components/layout/Home'),
children: [
{
name: 'panel',
path: '/panel/:typeName',
component: () => import('@/views/charts2/Panel')
},
{
path: '/report/builtIn',
component: () => import('@/views/report/Report')
},
{
path: '/entityExplorer',
component: () => import('@/views/entityExplorer/EntityExplorer')
},
{
path: '/entityDetail',
component: () => import('@/views/entityExplorer/EntityDetail')
},
{
path: '/entityGraph',
component: () => import('@/views/entityExplorer/EntityGraph')
},
{
path: '/detection',
redirect: '/detection/securityEvent'
},
{
path: '/detection/:typeName',
component: () => import('@/views/detections/Index')
},
{
path: '/businessLog/viewer',
component: () => import('@/views/businessLog/Viewer')
},
{
path: '/knowledgeBase',
component: () => import('@/views/setting/KnowledgeBase')
},
{
path: '/knowledgeBase/userDefinedLibrary',
component: () => import('@/views/setting/KnowledgeBase')
},
{
path: '/knowledgeBase/userDefinedLibrary/create',
component: () => import('@/views/setting/KnowledgeBaseForm')
},
{
path: '/knowledgeBase/userDefinedLibrary/edit',
component: () => import('@/views/setting/KnowledgeBaseForm')
},
{
name: 'Administration',
path: '/administration',
redirect: '/administration/user',
component: () => import('@/views/administration/Index'),
children: [
{
name: 'User',
path: '/administration/user',
component: () => import('@/views/administration/User')
},
{
name: 'Role',
path: '/administration/role',
component: () => import('@/views/administration/Roles')
},
{
name: 'OperationLog',
path: '/administration/operationLog',
component: () => import('@/views/administration/OperationLog')
},
{
name: 'Appearance',
path: '/administration/appearance',
component: () => import('@/views/administration/Appearance')
}
]
},
{
name: 'I18n',
path: '/i18n',
component: () => import('@/views/administration/I18n')
},
{
name: 'Chart',
path: '/chart',
component: () => import('@/views/administration/Chart')
},
{
path: '/detectionsNew',
component: () => import('@/views/detectionsNew/Index')
},
{
path: '/detection/policies',
component: () => import('@/views/detectionsNew/Index')
},
{
path: '/detection/policies/create',
component: () => import('@/views/detectionsNew/DetectionForm')
}
]
}
]

View File

@@ -60,6 +60,29 @@ const panel = {
routerHistoryList: [], // 路由跳转记录列表
dnsQtypeMapData: [],
dnsRcodeMapData: [],
scoreBase: {
isReady: false,
establishLatencyMs: {
p10: null,
p90: null
},
httpResponseLatency: {
p10: null,
p90: null
},
sslConLatency: {
p10: null,
p90: null
},
tcpLostlenPercent: {
p10: null,
p90: null
},
pktRetransPercent: {
p10: null,
p90: null
}
},
chartTabList: null // chartTabs组件的tab状态点击列表初始化为null方便原有逻辑计算
},
mutations: {
@@ -153,6 +176,56 @@ const panel = {
setRouterHistoryList (state, list) {
state.routerHistoryList = list
},
resetScoreBase (state) {
state.scoreBase = {
isReady: false,
establishLatencyMs: {
p10: null,
p90: null
},
httpResponseLatency: {
p10: null,
p90: null
},
sslConLatency: {
p10: null,
p90: null
},
tcpLostlenPercent: {
p10: null,
p90: null
},
pktRetransPercent: {
p10: null,
p90: null
}
}
},
setScoreBase (state, scoreBase) {
state.scoreBase = {
isReady: true,
establishLatencyMs: {
p10: scoreBase.establishLatencyMsP10,
p90: scoreBase.establishLatencyMsP90
},
httpResponseLatency: {
p10: scoreBase.httpResponseLatencyP10,
p90: scoreBase.httpResponseLatencyP90
},
sslConLatency: {
p10: scoreBase.sslConLatencyP10,
p90: scoreBase.sslConLatencyP90
},
tcpLostlenPercent: {
p10: scoreBase.tcpLostlenPercentP10,
p90: scoreBase.tcpLostlenPercentP90
},
pktRetransPercent: {
p10: scoreBase.pktRetransPercentP10,
p90: scoreBase.pktRetransPercentP90
}
}
},
setChartTabList (state, list) {
state.chartTabList = list
}
@@ -232,6 +305,12 @@ const panel = {
},
getChartTabList (state) {
return state.chartTabList
},
scoreBaseReady (state) {
return state.scoreBase.isReady
},
getScoreBase (state) {
return state.scoreBase
}
},
actions: {

View File

@@ -1,6 +1,6 @@
import axios from 'axios'
import router from '@/router'
import { getWelcomeMenu, sortByOrderNum } from '@/permission'
import { getWelcomeMenu, sortByOrderNum, handleRoutes } from '@/permission'
import { ElMessage } from 'element-plus' // dependent on utc plugin
import { dbDrilldownTableConfig, storageKey } from '@/utils/constants'
import { getConfigVersion } from '@/utils/tools'
@@ -64,7 +64,14 @@ const user = {
store.commit('setMenuList', menuList)
store.commit('setButtonList', res2.data.buttons)
store.commit('setRoleList', res2.data.roles)
const homeRoute = {
path: '/',
name: 'home',
component: () => import('@/components/layout/Home'),
children: []
}
handleRoutes(menuList, homeRoute.children)
router.addRoute(homeRoute)
if (res.loginSuccessPath) {
let tempArr = res.loginSuccessPath.split('?')
const path = tempArr[0]
@@ -118,7 +125,7 @@ const user = {
localStorage.setItem(storageKey.linkInfo, res.page.list[0].cvalue)
}
})
axios.get(api.config, { params: { ckey: 'schema_entity_explore' } }).then(response => {
axios.get(api.config, { params: { ckey: 'schema_explore' } }).then(response => {
const res = response.data
if (response.status === 200 && res.page.list && res.page.list.length > 0) {
localStorage.setItem(storageKey.schemaEntityExplore, res.page.list[0].cvalue)

View File

@@ -38,9 +38,12 @@ export const api = {
knowledgeBase: apiVersion + '/knowledgeBase',
knowledgeBaseList: apiVersion + '/knowledgeBase/list',
knowledgeBaseEnable: apiVersion + '/knowledgeBase/status',
knowledgeBaseLearningStart: apiVersion + '/knowledgeBase/intelligence-learning/start',
knowledgeBaseLearningStop: apiVersion + '/knowledgeBase/intelligence-learning/stop',
knowledgeBaseStatistics: apiVersion + '/knowledgeBase/statistics',
updateKnowledgeUrl: apiVersion + '/knowledgeBase/items/batch',
knowledgeBaseLog: apiVersion + '/knowledgeBase/audit/log',
knowledgeBaseTimedistribution: apiVersion + '/knowledgeBase/{{knowledgeId}}/{{type}}/timedistribution',
// 报告相关
reportJob: '/report/job',
@@ -123,7 +126,20 @@ export const api = {
listBasic: '/interface/detection/security/list/basic',
listCount: '/interface/detection/security/list/count',
overviewBasic: '/interface/detection/security/detail/overview/basic',
overviewEvent: '/interface/detection/security/detail/overview/event'
overviewEvent: '/interface/detection/security/detail/overview/event',
securityList: apiVersion + '/detection/security/list', // 安全事件列表
timeDistribution: apiVersion + '/detection/security/severity/timedistribution', // 事件严重等级分布(顶部柱状图)
severityStatistics: apiVersion + '/detection/security/severity/statistics', // 事件严重等级统计(左侧filter事件严重等级和饼图)
statusStatistics: apiVersion + '/detection/security/status/statistics', // 事件状态统计
eventTypeStatistics: apiVersion + '/detection/security/event-type/statistics', // 事件类型统计
offenderIpStatistics: apiVersion + '/detection/security/offender-ip/statistics', // 攻击者IP统计
victimIpStatistics: apiVersion + '/detection/security/victim-ip/statistics', // 受害者IP统计
relationEvent: apiVersion + '/detection/security/ip/relation/event', // IP相关近期事件
securityCount: apiVersion + '/detection/security/count', // 安全事件总数
detail: apiVersion + '/detection/security/entity/detail', // 安全事件实体详情,后面得加上实体类型
ipDetail: apiVersion + '/detection/security/entity/detail/ip', // 安全事件实体详情ip响应
domainDetail: apiVersion + '/detection/security/entity/detail/domain', // 安全事件实体详情domain响应
appDetail: apiVersion + '/detection/security/entity/detail/app' // 安全事件实体详情app响应
},
performanceEvent: {
eventSeverityTrend: '/interface/detection/performance/filter/severityTrend',
@@ -139,13 +155,13 @@ export const api = {
},
list: apiVersion + '/rule/detection/list', // 检测规则列表
detail: apiVersion + '/rule/detection', // 检测规则详情
delete: apiVersion + '/rule', // 检测规则删除
delete: apiVersion + '/rule/detection', // 检测规则删除
// 获取单位列表如source、type、metric等
statistics: apiVersion + '/detection/statistics',
statistics: apiVersion + '/rule/detection/statistics',
// 规则新建模块
create: {
topKeys: apiVersion + '/detection/topKeys', // topKeys列表
create: apiVersion + '/rule/detection/create' // todo 规则新建编辑此api为模拟后续需要修改
create: apiVersion + '/rule/detection'
}
},
// Dashboard
@@ -248,6 +264,7 @@ export const api = {
throughput: apiVersion + '/entity/detail/traffic/throughput',
security: apiVersion + '/entity/detail/event/security',
performance: apiVersion + '/entity/detail/event/performance',
behaviorPattern: apiVersion + '/entity/detail/behavior/ip',
// 域名解析ip相关app、domain
domainNameResolutionAboutAppsOfIp: apiVersion + '/entity/detail/ip/relate/apps',
domainNameResolutionAboutDomainsOfIp: apiVersion + '/entity/detail/ip/relate/domains',

File diff suppressed because one or more lines are too long

View File

@@ -43,7 +43,7 @@ export function rTime (date) {
}
// 日期转换为时间戳
export function toTime (date) {
return new Date(date).getTime()
return new Date(parseFloat(date)).getTime()
}
// 时间格式转换
export function dateFormat (date, format = 'YYYY-MM-DD HH:mm:ss') {
@@ -121,9 +121,9 @@ export function xAxisTimeFormatter (value) {
':' +
(date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes())
// 如果是一天的开始
if (date.getTime() === dayStart.getTime()) {
if (getSecond(date.getTime()) === getSecond(dayStart.getTime())) {
return '{day|' + dateFormat(date, 'YYYY-MM-DD') + '}'
} else if (date.getTime() === hourStart.getTime()) {
} else if (getSecond(date.getTime()) === getSecond(hourStart.getTime())) {
return '{hour|' + HHmm + '}'
} else {
return HHmm
@@ -137,3 +137,69 @@ export const xAxisTimeRich = {
fontWeight: 'bold'
}
}
function switchDateTypeByStr (str) {
switch (str) {
// case 'Y':
// return 'years'
// case 'M':
// return 'months'
// case 'W':
// return 'weeks'
case 'D':
return 'days'
case 'H':
return 'hours'
case 'M':
return 'minutes'
case 'S':
return 'seconds'
}
}
function switchValueByDateType (str) {
switch (str) {
// case 'years':
// return 'Y'
// case 'months':
// return 'M'
// case 'weeks':
// return 'W'
case 'days':
return 'D'
case 'hours':
return 'H'
case 'minutes':
return 'M'
case 'seconds':
return 'S'
}
}
export function getTimeByDurations (str) {
if (str && str.indexOf('P') === 0) {
const obj = {
type: '',
value: ''
}
for (let i = 1; i < str.length; i++) {
const item = str[i]
// P5D表示持续5天PT5M持续5分钟T是位于时间分量之前的时间指示符即T之前是年月周日T之后是时分秒
if (isNaN(item) && item !== 'T') {
obj.type = switchDateTypeByStr(item)
} else if (!isNaN(item)) {
obj.value += item
}
}
return obj
}
}
export function getDurationsTimeByType (number, type) {
let T = ''
if (['hours', 'minutes', 'seconds'].indexOf(type) > -1) {
T = 'T'
}
return `P${T}${number}${switchValueByDateType(type)}`
}

View File

@@ -15,42 +15,42 @@ _this.$t = _this.t
export const dataForNpmTrafficLine = {
tabs: [
{
name: _this.$t('network.total'),
name: 'network.total',
show: true,
positioning: 0,
data: [],
unitType: 'number'
},
{
name: _this.$t('network.inbound'),
name: 'network.inbound',
show: true,
positioning: 1,
data: [],
unitType: 'number'
},
{
name: _this.$t('network.outbound'),
name: 'network.outbound',
show: true,
positioning: 2,
data: [],
unitType: 'number'
},
{
name: _this.$t('network.internal'),
name: 'network.internal',
show: true,
positioning: 3,
data: [],
unitType: 'number'
},
{
name: _this.$t('network.through'),
name: 'network.through',
show: true,
positioning: 4,
data: [],
unitType: 'number'
},
{
name: _this.$t('network.other'),
name: 'network.other',
show: true,
positioning: 5,
data: [],
@@ -58,21 +58,21 @@ export const dataForNpmTrafficLine = {
}
],
npmQuantity: [
{ name: _this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 },
{ name: _this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 },
{ name: _this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 },
{ name: _this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 },
{ name: _this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 },
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 },
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 },
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 },
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
],
metricOptions: [
/* { value: 'Bits/s', label: 'Bits/s' },
{ value: 'Packets/s', label: 'Packets/s' },
{ value: 'Sessions/s', label: 'Sessions/s' }, */
{ value: 'establishLatencyMs', label: _this.$t('networkAppPerformance.tcpConnectionEstablishLatency') },
{ value: 'httpResponseLatency', label: _this.$t('networkAppPerformance.httpResponse') },
{ value: 'sslConLatency', label: _this.$t('networkAppPerformance.sslResponseLatency') },
{ value: 'tcpLostlenPercent', label: _this.$t('networkAppPerformance.packetLoss') },
{ value: 'pktRetransPercent', label: _this.$t('overall.packetRetrans') }
{ value: 'establishLatencyMs', label: 'networkAppPerformance.tcpConnectionEstablishLatency' },
{ value: 'httpResponseLatency', label: 'networkAppPerformance.httpResponse' },
{ value: 'sslConLatency', label: 'networkAppPerformance.sslResponseLatency' },
{ value: 'tcpLostlenPercent', label: 'networkAppPerformance.packetLoss' },
{ value: 'pktRetransPercent', label: 'overall.packetRetrans' }
]
}
@@ -80,15 +80,15 @@ export const dataForNetworkOverviewLine = {
options2: [
{
value: 'Average',
label: 'Average'
label: 'overall.average'
},
{
value: '95th Percentile',
label: '95th Percentile'
label: ['overall.percentileNumber', { number: 95 }]
},
{
value: 'Maximum',
label: 'Maximum'
label: 'overall.maximum'
}
],
tabsTemplate: [
@@ -175,9 +175,9 @@ export const dataForLinkTrafficLine = {
export const dataForNpmLine = {
chartOptionLineData: [
{ legend: _this.$t('network.total'), index: 0, invertTab: true, show: false, color: '#749F4D' },
{ legend: _this.$t('network.inbound'), index: 1, invertTab: true, show: false, color: '#98709B' },
{ legend: _this.$t('network.outbound'), index: 2, invertTab: true, show: false, color: '#E5A219' }
{ legend: 'network.total', index: 0, invertTab: true, show: false, color: '#749F4D' },
{ legend: 'network.inbound', index: 1, invertTab: true, show: false, color: '#98709B' },
{ legend: 'network.outbound', index: 2, invertTab: true, show: false, color: '#E5A219' }
],
npmLineColor: [
{ legend: '', color: '#749F4D' },
@@ -200,15 +200,15 @@ export const dataForDnsTrafficLine = {
options2: [
{
value: 'Average',
label: 'Average'
label: 'overall.average'
},
{
value: '95th Percentile',
label: '95th Percentile'
label: ['overall.percentileNumber', { number: 95 }]
},
{
value: 'Maximum',
label: 'Maximum'
label: 'overall.maximum'
}
],
tabs: [
@@ -221,27 +221,27 @@ export const dataForDnsTrafficLine = {
export const dataForNpmEventsHeader = {
chartData: [
{
eventSeverity: 'critical',
eventSeverity: 'overall.critical',
count: '-',
index: 0
},
{
eventSeverity: 'high',
eventSeverity: 'overall.high',
count: '-',
index: 1
},
{
eventSeverity: 'medium',
eventSeverity: 'overall.medium',
count: '-',
index: 2
},
{
eventSeverity: 'low',
eventSeverity: 'overall.low',
count: '-',
index: 3
},
{
eventSeverity: 'info',
eventSeverity: 'overall.info',
count: '-',
index: 4
}
@@ -334,11 +334,91 @@ export const columnList1 = [
}
}
]
const securityEvent = [
{
name: 'event_type',
type: 'string',
label: 'event_type',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'event_name',
type: 'string',
label: 'event_name',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'severity',
type: 'string',
label: 'severity',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'offender_ip',
type: 'string',
label: 'offender Ip',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'victim_ip',
type: 'string',
label: 'victim Ip',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'domain',
type: 'string',
label: 'domain',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
},
{
name: 'app',
type: 'string',
label: 'app',
doc: {
constraints: {
operator_functions: '=,in,like'
}
}
}
]
let schemaEntityExplore = localStorage.getItem(storageKey.schemaEntityExplore)
schemaEntityExplore = schemaEntityExplore ? JSON.parse(schemaEntityExplore).entityMetadata.searchColumns : columnList1
const schema = localStorage.getItem(storageKey.schemaEntityExplore)
const schemaEntityExplore = schema ? JSON.parse(schema).entityMetadata.searchColumns : columnList1
export const columnList = schemaEntityExplore
let securityEventMetadata = securityEvent
if (schema) {
if (JSON.parse(schema).securityEventMetadata) {
securityEventMetadata = JSON.parse(schema).securityEventMetadata.searchColumns
}
}
export const schemaDetectionSecurity = securityEventMetadata
export const operatorList = ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE']
export const connectionList = [
{

View File

@@ -1,12 +1,13 @@
import { ElMessageBox, ElMessage } from 'element-plus'
import i18n from '@/i18n'
import _ from 'lodash'
import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, networkTable, dbDrilldownTableConfig } from '@/utils/constants'
import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, networkTable, dbDrilldownTableConfig, ZH, EN } from '@/utils/constants'
import { getIso36112JsonData, getDictList } from '@/utils/api'
import { format } from 'echarts'
import router from '@/router'
import store from '@/store'
import indexedDBUtils from '@/indexedDB'
import { columnType } from '@/components/advancedSearch/meta/meta'
export const tableSort = {
// 是否需要排序
@@ -784,7 +785,7 @@ export function getChainRatio (current, prev) {
}
}
export function computeScore (data) {
export function computeScore (data, scoreBase) {
let score = 0
let k = 0
let totalScore = 0
@@ -799,26 +800,14 @@ export function computeScore (data) {
} else if (t === 'httpResponseLatency' || t === 'sslConLatency') {
k = 0.05
}
if (t === 'establishLatencyMs' || t === 'httpResponseLatency' || t === 'sslConLatency') {
if (!data[t] && data[t] !== 0) {
score = 1
} else if (data[t] <= 50) {
score = 1
} else if (data[t] > 200) {
score = 0
} else {
score = (data[t] - 200) / (50 - 200)
}
} else if (t === 'tcpLostlenPercent' || t === 'pktRetransPercent') {
if (!data[t] && data[t] !== 0) {
score = 1
} else if (data[t] <= 0.01) {
score = 1
} else if (data[t] > 0.05) {
score = 0
} else {
score = (data[t] - 0.05) / (0.01 - 0.05)
}
if (!data[t] && data[t] !== 0) {
score = 1
} else if (data[t] <= scoreBase[t].p10) {
score = 1
} else if (data[t] >= scoreBase[t].p90) {
score = 0
} else {
score = (data[t] - scoreBase[t].p90) / (scoreBase[t].p10 - scoreBase[t].p90)
}
scoreArr.push(score * k)
})
@@ -1260,10 +1249,10 @@ export function getQueryByFlag2 (type, condition) {
*/
export function getWidthByLanguage (language) {
switch (language) {
case 'en': {
case EN: {
return 7
}
case 'cn': {
case ZH: {
return 16
}
}
@@ -1318,12 +1307,13 @@ export function numberWithCommas (num) {
* @returns {string}
*/
export function switchStatus (status) {
switch (status) {
case 0:
return 'Disabled'
case 1:
return 'Enabled'
switch (status + '') {
case '0':
return 'detection.create.disabled'
case '1':
return 'detection.create.enabled'
}
return null
}
/**
@@ -1356,3 +1346,104 @@ export function beforeRouterPush () {
}
store.commit('setRouterHistoryList', historyList)
}
/**
* 配置tag颜色此为接口返回的颜色字段color为rgb格式
* @param color
* @returns {string}
*/
export function getTagColor (color) {
if (color) {
let backgroundColor = ''
if (color.indexOf('rgb(') > -1) {
backgroundColor = color.replace('rgb(', 'rgba(').replace(')', ',0.06)')
}
return `color: ${color};border-color: ${color};background-color: ${backgroundColor};`
}
}
/**
* 字段高亮
* 用法: <span v-high-light=[{type: 'string', value: '搜索'}, {type: 'fullText', value: '高亮'}]>搜索关键字高亮<span>
* 其中type为fullText的为模糊搜索
* @type {{updated(*, *): (*|undefined)}}
*/
export const myHighLight = {
updated (el, binding) {
if (el && binding.value) {
const { value } = binding
if (value && value.length > 0) {
const text = _.cloneDeep(el.innerHTML)
const regex = new RegExp(value.map(item => `${item.value}`).join('|'), 'g')
if (el.getElementsByClassName('highlight__text').length === 0) {
const newText = text.replace(regex, (match) => {
// 将value中的value提取出来对比string即精准搜索fullText模糊搜索
for (const item of value) {
if ((item.type === columnType.string && item.value === el.innerHTML) || el.className.indexOf('high-location') > -1) {
if (el.className.indexOf('high-light-block') > -1) {
return `<span class="highlight__block">${match}</span>`
} else {
return `<span class="highlight__text">${match}</span>`
}
} else if (item.type === columnType.fullText && item.value === match) {
if (el.className.indexOf('high-light-block') > -1) {
return `<span class="highlight__block">${match}</span>`
} else {
return `<span class="highlight__text">${match}</span>`
}
}
}
return match
})
if (newText && newText !== '-') {
// 此处不用el.className.indexOf('high-light-block')判断是因为block可能会有多个有一个满足所有的都会渲染
if (newText.indexOf('highlight__block') > -1) {
el.style.cssText = el.style.cssText + 'background: #FEECC2;'
// 此处是相关app、相关ip、相关domain弹窗获取不到dom的草错
let dom
if (document.getElementById('showRelatedApp')) {
dom = document.getElementById('showRelatedApp')
} else if (document.getElementById('showRelatedDomain')) {
dom = document.getElementById('showRelatedDomain')
}
if (dom) {
const itemDom = dom.getElementsByClassName('high-light-block')
if (itemDom) {
for (let i = 0; i < itemDom.length; i++) {
if (itemDom[i].innerHTML === el.innerHTML) {
itemDom[i].style.cssText = itemDom[i].style.cssText + 'background: #FEECC2;'
}
}
}
}
} else {
el.innerHTML = newText
}
} else {
return newText
}
}
}
}
}
}
export const changeTimeByDate = (date) => {
if (date) {
const nowDate = Date.now()
const oldDate = isNaN(date) ? Date.parse(date) : date
const diff = Math.floor((nowDate - oldDate) / 1000)
if (diff < 60) {
return diff + i18n.global.t('entity.search.secondsAgo')
} else if (diff >= 60 && diff < 3600) {
const minutes = Math.floor(diff / 60)
return minutes === 1 ? `${minutes} ${i18n.global.t('entity.search.minuteAgo')}` : `${minutes} ${i18n.global.t('entity.search.minutesAgo')}`
} else if (diff >= 3600 && diff < 86400) {
const hours = Math.floor(diff / 3600)
return hours === 1 ? `${hours} ${i18n.global.t('entity.search.hourAgo')}` : `${hours} ${i18n.global.t('entity.search.hoursAgo')}`
} else {
return date
}
}
}

View File

@@ -48,7 +48,9 @@
</el-form-item>
</el-form>
<div class="edit-appearance-base__footer">
<button style="position: relative;" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
<button style="position: relative;" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save"
v-if="hasPermission('editAppearence')"
>
<loading size="small" :loading="blockOperation.save"></loading>
<span>{{$t('overall.save')}}</span>
</button>
@@ -58,7 +60,7 @@
</template>
<script>
import { api } from '@/utils/api'
import { storageKey } from '@/utils/constants'
import { storageKey, ZH, EN } from '@/utils/constants'
import axios from 'axios'
import dayjs from 'dayjs'
@@ -97,12 +99,12 @@ export default {
{
id: 1,
label: 'English',
value: 'en'
value: EN
},
{
id: 2,
label: '中文',
value: 'zh'
value: ZH
}
]
}

View File

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

View File

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

View File

@@ -11,16 +11,19 @@
>
<template v-slot:top-tool-left>
<button id="roles-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
v-if="hasPermission('createRole')"
@click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{$t('overall.create')}}</span>
</button>
<button id="roles-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
v-if="hasPermission('editRole')"
@click="edit">
<i class="cn-icon-edit cn-icon"></i>
<span>{{$t('overall.edit')}}</span>
</button>
<button id="roles-delete" class="top-tool-btn margin-r-10" :disabled="disableDelete"
v-if="hasPermission('deleteRole')"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{$t('overall.delete')}}</span>

View File

@@ -11,16 +11,19 @@
>
<template #top-tool-left>
<button id="account-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
v-if="hasPermission('createUser')"
@click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{$t('overall.create')}}</span>
</button>
<button id="account-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
v-if="hasPermission('editUser')"
@click="editSelectRecord">
<i class="cn-icon-edit cn-icon"></i>
<span>{{$t('overall.edit')}}</span>
</button>
<button id="account-delete" class="top-tool-btn margin-r-10" :disabled="disableDelete"
v-if="hasPermission('deleteUser')"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{$t('overall.delete')}}</span>

View File

@@ -58,7 +58,7 @@
size="mini"
v-model="table.limit"
class="option__select select-topn"
placeholder=""
placeholder=" "
popper-class="option-popper"
@change="tableLimitChange"
>
@@ -125,7 +125,7 @@
size="mini"
v-model="copyOrderPieTable"
class="option__select select-column"
placeholder=""
placeholder=" "
popper-class="option-popper"
@change="orderPieTableChange"
>

View File

@@ -5,12 +5,12 @@
v-if="panel.params && panel.params.wholeScreenScroll"
id="wholeScreenBox"
>
<dns-screen v-if="currentPath === wholeScreenRouterMapping.dns"
<!-- <dns-screen v-if="currentPath === wholeScreenRouterMapping.dns"
:copy-data-list="chartList"
ref="dnsScreen"
:time-filter="timeFilter"
:entity="entity">
</dns-screen>
</dns-screen>-->
</div>
<div id="panelList" v-if="!isEntityDetail" class="panel-list">
<div id="cn-panel" class="cn-panel2">
@@ -49,7 +49,7 @@
<script>
import { useRoute, useRouter } from 'vue-router'
import { ref } from 'vue'
import DnsScreen from '@/views/charts/wholeScreenScroll/DnsScreen'
// import DnsScreen from '@/views/charts/wholeScreenScroll/DnsScreen'
import { panelTypeAndRouteMapping, wholeScreenRouterMapping } from '@/utils/constants'
import { api, getPanelList, getChartList } from '@/utils/api'
import { getNowTime } from '@/utils/date-util'
@@ -66,9 +66,9 @@ export default {
isEntityDetail: Boolean,
typeName: String
},
components: {
/* components: {
DnsScreen
},
}, */
data () {
return {
chartList: [], // 普通panel的chart
@@ -104,8 +104,8 @@ export default {
setup (props, ctx) {
const panel = ref({})
let panelType = 1 // 取得panel的type
const { params } = useRoute()
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName]
const { path } = useRoute()
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[path.replace('/panel/', '')]
// date
const dateRangeValue = 60

View File

@@ -338,7 +338,10 @@ export function axisFormatter (params) {
return str
}
export function tooLongFormatter (name) {
return format.truncateText(name, 110, '12px')
return format.truncateText(name, 160, '12px')
}
export function tooLongFormatterFor2Columns (name) {
return format.truncateText(name, 100, '12px')
}
export function timeHorizontalFormatter (params) {
let str = '<div>'

View File

@@ -1,7 +1,7 @@
<template>
<div class="panel-box2" :class="{'panel-box2--entity-detail': entity && entity.entityType}">
<div class="panel__header" v-if="!entity">
<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 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>
@@ -24,14 +24,14 @@
<el-select
size="mini"
v-model="metric"
placeholder=""
placeholder=" "
popper-class="common-select"
v-if="showMetric"
:popper-append-to-body="false"
@change="metricChange"
>
<template #prefix>
<span class="select-prefix">Metric:</span>
<span class="select-prefix">{{$t('detections.metric')}}:</span>
</template>
<el-option v-for="item in metricOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
@@ -104,7 +104,9 @@ export default {
dnsRcodeMapData: [],
dnsQtypeMapData: [],
score: null,
curTabState: curTabState
curTabState: curTabState,
performanceData: {},
scoreDataState: false // 评分数据是否加载完成
}
},
computed: {
@@ -114,29 +116,39 @@ export default {
// 显示顶部的Metric单位选项标识
showMetric () {
return this.panelType === panelTypeAndRouteMapping.networkOverview || this.panelType === panelTypeAndRouteMapping.networkOverviewDrillDown
},
scoreBaseState () {
return this.$store.getters.scoreBaseReady
}
},
watch: {
// npmThirdLevelMenuScore: {
// deep: true,
// immediate: true,
// handler (n) {
// this.score = n
// }
// }
timeFilter: {
handler () {
if (this.$route.path === '/panel/networkAppPerformance' && (this.lineQueryCondition || this.networkOverviewBeforeTab)) {
this.scoreCalculation()
if (this.$route.path === '/panel/networkAppPerformance') {
this.$store.commit('resetScoreBase')
this.queryScoreBase()
if (this.lineQueryCondition || this.networkOverviewBeforeTab) {
this.scoreCalculation()
}
}
}
},
scoreBaseState (n) {
if (n && this.scoreDataState) {
this.handleScoreData()
}
},
scoreDataState (n) {
if (n && this.scoreBaseState) {
this.handleScoreData()
}
}
},
async mounted () {
// this.panelName = this.$store.getters.getPanelName
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) {
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
this.$store.commit('setDnsQtypeMapData', this.dnsQtypeMapData)
@@ -212,8 +224,14 @@ export default {
return chart
})
})
if (this.$route.path === '/panel/networkAppPerformance' && (this.lineQueryCondition || this.networkOverviewBeforeTab)) {
this.scoreCalculation()
if (this.$route.path === '/panel/networkAppPerformance') {
if (this.lineQueryCondition || this.networkOverviewBeforeTab) {
this.scoreCalculation()
}
}
if (this.$route.path === '/panel/networkAppPerformance' || this.$route.path === '/panel/linkMonitor') {
this.$store.commit('resetScoreBase')
this.queryScoreBase()
}
},
setup (props) {
@@ -231,12 +249,11 @@ export default {
const panel = ref({})
let panelType = 1 // 取得panel的type
let { params, query, path } = useRoute()
let { 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
@@ -274,19 +291,19 @@ export default {
} else if (thirdPanel) {
panelType = Number(thirdPanel)
} else {
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName]
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[path.replace('/panel/', '')]
}
// 获取url携带的range、startTime、endTime
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
// 优先级url > config.js > 默认值。
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.dashboard || 60)
const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(60)
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = getSecond(startTime)
timeFilter.value.endTime = getSecond(endTime)
// 如果没有时间参数就将参数写入url
@@ -396,6 +413,44 @@ export default {
})
overwriteUrl(newUrl)
},
// 动态查询评分基准
queryScoreBase () {
const params = {
startTime: this.timeFilter.startTime,
endTime: this.timeFilter.endTime
}
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params })
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params })
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params })
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params })
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params })
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
const scoreBase = {}
res.forEach((t, i) => {
if (t.status === 200) {
if (i === 0) {
scoreBase.establishLatencyMsP10 = _.get(t.data, 'data.result.establishLatencyMsP10', null)
scoreBase.establishLatencyMsP90 = _.get(t.data, 'data.result.establishLatencyMsP90', null)
} else if (i === 1) {
scoreBase.httpResponseLatencyP10 = _.get(t.data, 'data.result.httpResponseLatencyP10', null)
scoreBase.httpResponseLatencyP90 = _.get(t.data, 'data.result.httpResponseLatencyP90', null)
} else if (i === 2) {
scoreBase.sslConLatencyP10 = _.get(t.data, 'data.result.sslConLatencyP10', null)
scoreBase.sslConLatencyP90 = _.get(t.data, 'data.result.sslConLatencyP90', null)
} else if (i === 3) {
scoreBase.tcpLostlenPercentP10 = _.get(t.data, 'data.result.tcpLostlenPercentP10', null)
scoreBase.tcpLostlenPercentP90 = _.get(t.data, 'data.result.tcpLostlenPercentP90', null)
} else if (i === 4) {
scoreBase.pktRetransPercentP10 = _.get(t.data, 'data.result.pktRetransPercentP10', null)
scoreBase.pktRetransPercentP90 = _.get(t.data, 'data.result.pktRetransPercentP90', null)
}
}
})
this.$store.commit('setScoreBase', scoreBase)
}).catch((e) => {
}).finally(() => {
})
},
scoreCalculation () {
let condition = ''
let url = ''
@@ -443,30 +498,36 @@ export default {
url = api.npm.overview.networkAnalysis
}
if ((type && condition) || type) {
this.scoreDataState = false
this.performanceData = {}
params.type = params.type || type
axios.get(url, { params }).then(res => {
if (res.status === 200) {
const data = {
this.performanceData = {
establishLatencyMs: _.get(res, 'data.data.result.establishLatencyMsAvg', null),
httpResponseLatency: _.get(res, 'data.data.result.httpResponseLatencyAvg', null),
sslConLatency: _.get(res, 'data.data.result.sslConLatencyAvg', null),
tcpLostlenPercent: _.get(res, 'data.data.result.tcpLostlenPercentAvg', null),
pktRetransPercent: _.get(res, 'data.data.result.pktRetransPercentAvg', null)
}
this.score = computeScore(data)
}
}).finally(() => {
this.scoreDataState = true
})
}
},
jumpEntityDetail () {
const { href } = this.$router.resolve({
path: '/entityDetail',
path: '/entity/detail',
query: {
entityType: this.entityType,
entityName: this.entityValue
}
})
window.open(href, '_blank')
},
handleScoreData () {
this.score = computeScore(this.performanceData, this.$store.getters.getScoreBase)
}
},
/**

View File

@@ -1,4 +1,5 @@
import { entityDetailRelatedEntitiesShowSize } from '@/utils/constants'
import { entityDetailRelatedEntitiesShowSize, entityDetailTabsName } from '@/utils/constants'
import { getSecond } from '@/utils/date-util'
export default {
props: {
@@ -28,6 +29,59 @@ export default {
}
}
},
getParamsByTabType (tabType) {
let params = {
resource: this.entity.entityName
}
let dataRangeValue = 60 * 24 * 7
switch (tabType) {
case entityDetailTabsName.relatedEntity:
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity
break
case entityDetailTabsName.performanceEvent:
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.performanceEvent
break
case entityDetailTabsName.securityEvent:
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.securityEvent
break
case entityDetailTabsName.openPort:
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.openPort
break
case entityDetailTabsName.informationAggregation:
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.informationAggregation
break
case entityDetailTabsName.behaviorPattern:
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.behaviorPattern
break
default:
dataRangeValue = 60 * 24 * 7
}
if (dataRangeValue !== 0) {
const endTime = window.$dayJs.tz().valueOf()
const startTime = endTime - dataRangeValue * 60 * 1000
params = {
...params,
startTime: getSecond(startTime),
endTime: getSecond(endTime)
}
}
return params
},
getParams () {
const range = this.timeFilter.dateRangeValue
let params = {
resource: this.entity.entityName
}
if (range !== 0) {
params = {
...params,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
}
return params
},
handleShowDataNum (showListInfo, allList) {
if (allList.length <= entityDetailRelatedEntitiesShowSize) {
showListInfo.num = allList.length

View File

@@ -27,12 +27,13 @@
</div>
</div>
<div class="line-select line-header-right">
<div class="line-select-metric">
<!-- <div class="line-select-metric">
<span>{{$t('network.metric')}}:</span>
<div class="line-select__operation">
<el-select
size="mini"
v-model="lineMetric"
placeholder=" "
popper-class="common-select"
:popper-append-to-body="false"
@change="metricSelectChange"
@@ -40,19 +41,22 @@
<el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</div>
</div>
</div>-->
<div class="line-select-reference-line">
<span>{{$t('network.referenceLine')}}:</span>
<span>{{$t('network.referenceLine')}}&nbsp;:&nbsp;</span>
<div class="line-select__operation">
<el-select
size="mini"
v-model="lineRefer"
placeholder=" "
:disabled="!lineTab"
popper-class="common-select"
:popper-append-to-body="false"
@change="referenceSelectChange"
>
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"></el-option>
<el-option :key="options2[0].value" :label="$t(options2[0].label)" :value="options2[0].value"></el-option>
<el-option :key="options2[1].value" :label="$t(options2[1].label[0], options2[1].label[1])" :value="options2[1].value"></el-option>
<el-option :key="options2[2].value" :label="$t(options2[2].label)" :value="options2[2].value"></el-option>
</el-select>
</div>
</div>
@@ -174,6 +178,9 @@ export default {
if (res.status === 200) {
this.showError = false
this.isNoData = res.data.data.result.length === 0
if (!active) {
this.tabs = _.cloneDeep(dataForDnsTrafficLine.tabs)
}
if (this.isNoData) {
this.lineTab = ''
this.tabs = _.cloneDeep(dataForDnsTrafficLine.tabs)
@@ -250,6 +257,14 @@ export default {
label: {
formatter (params) {
const arr = unitConvert(params.value, unitTypes.number).join('')
const referIndex = _this.options2.findIndex(o => o.value === _this.lineRefer)
if (referIndex > -1) {
if (referIndex === 1) {
return _this.$t(_this.options2[1].label[0], _this.options2[1].label[1]) + '(' + arr + echartsData[0].unitType + ')'
} else {
return _this.$t(_this.options2[referIndex].label) + '(' + arr + echartsData[0].unitType + ')'
}
}
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
},
position: 'insideStartTop',
@@ -445,6 +460,7 @@ export default {
this.legendSelectChange(e, 0)
})
this.tabs = tabs
this.lineRefer = 'Average'
this.echartsInit(this.tabs, true)
} else {
const unit = 'bps'
@@ -464,7 +480,7 @@ export default {
dnsData.forEach(e => {
e.unitType = type
if (parseFloat(e.analysis.avg) === 0 || isNaN(parseFloat(e.analysis.avg))) {
if (parseFloat(e.analysis.max) === 0 || isNaN(parseFloat(e.analysis.max))) {
e.show = false
num += 1
} else {
@@ -474,13 +490,18 @@ export default {
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
if (parseFloat(e.analysis.max) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
// this.init() // 后续多关注
}
}
})
const emptyData = dnsData.filter(d => parseFloat(d.analysis.max) === 0)
this.isNoData = emptyData.length === dnsData.length
if (this.isNoData) {
return true
}
this.tabs = dnsData
// 如果三者avg都为0时至少保证total显示
@@ -489,10 +510,10 @@ export default {
let ingressAvg = 0
let egressAvg = 0
if (ingressObj) {
ingressAvg = parseFloat(ingressObj.analysis.avg) || 0
ingressAvg = parseFloat(ingressObj.analysis.max) || 0
}
if (egressObj) {
egressAvg = parseFloat(egressObj.analysis.avg) || 0
egressAvg = parseFloat(egressObj.analysis.max) || 0
}
if ((ingressAvg + egressAvg) === 0) {
const totalObj = dnsData.find(d => d.name === 'network.total')

View File

@@ -1,7 +1,7 @@
<template>
<div class="entity-detail-basic-info">
<chart-error v-if="showError" :content="errorMsg"/>
<div class="entity-type">{{entityType[entity.entityType]}}</div>
<div class="entity-type">{{entityTypeName}}</div>
<div class="entity-basic-info">
<div class="entity-basic-info__name">
<span id="entityName">{{entity.entityName}}</span>
@@ -32,7 +32,13 @@
</el-popover>
</div>
<div class="entity-tags" v-if="!hideTagArea">
<div v-for="tag in levelTwoTags" :key="tag.value" class="entity-tag" :class="`entity-tag--level-two-${tag.type}`">{{tag.value}}</div>
<div v-for="tag in levelTwoTags"
:key="tag.value"
class="entity-tag"
:class="`entity-tag--level-two-${tag.type}`"
:style="getTagColor(tag.color)">
{{tag.value}}
</div>
</div>
<!-- 分割线-->
<div class="dividing-line"></div>
@@ -54,10 +60,11 @@ import {
drillDownPanelTypeMapping,
entityType,
entityDetailTags,
psiphon3IpType,
riskLevelMapping
tagValueLabelMapping,
riskLevelMapping,
entityDefaultColor
} from '@/utils/constants'
import { selectElementText, copySelectionText } from '@/utils/tools'
import { selectElementText, copySelectionText, getTagColor } from '@/utils/tools'
import { ref } from 'vue'
import i18n from '@/i18n'
import { useRouter } from 'vue-router'
@@ -83,17 +90,34 @@ export default {
hideTagArea: false
}
},
methods: {
tagValueHandler (k, k2, value) {
if (k === 'psiphon3Ip') {
if (k2 === 'type') {
const find = psiphon3IpType.find(t => t.value === value)
if (find) {
return find.name
}
computed: {
entityTypeName () {
const type = this.entity.entityType
let entityTypeName = '-'
switch (type) {
case ('ip'): {
entityTypeName = 'IP'
break
}
case ('domain'): {
entityTypeName = this.$t('overall.domain')
break
}
case ('app'): {
entityTypeName = 'APP'
break
}
default:
break
}
return value
return entityTypeName
}
},
methods: {
getTagColor,
tagValueHandler (value) {
const find = tagValueLabelMapping.find(t => t.value === value)
return find ? find.name : value
},
getData () {
this.toggleLoading(true)
@@ -114,13 +138,13 @@ export default {
Object.keys(res.data[k]).forEach(k2 => {
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
if (find) {
this.levelTwoTags.push({ key: k2, value: this.tagValueHandler(k, k2, res.data[k][k2]), type: find.type })
this.levelTwoTags.push({ key: k2, value: this.tagValueHandler(res.data[k][k2]), type: find.type })
}
})
}
})
if (_.isArray(res.data.userDefinedTags)) {
this.levelTwoTags = _.concat(this.levelTwoTags, res.data.userDefinedTags.map(tag => ({ value: tag.tagValue, type: 'normal' })))
this.levelTwoTags = _.concat(this.levelTwoTags, res.data.userDefinedTags.map(tag => ({ value: tag.tagValue, color: tag.knowledgeBase ? tag.knowledgeBase.color : entityDefaultColor })))
}
this.hideTagArea = _.isEmpty(this.levelTwoTags)
this.$nextTick(() => {
@@ -258,7 +282,7 @@ export default {
icon: 'cn-icon cn-icon-graph',
label: i18n.global.t('entities.graph'),
url: resolvePath({
path: '/entityGraph',
path: '/entity/graph',
query: {
entityType: props.entity.entityType,
entityName: props.entity.entityName

View File

@@ -50,7 +50,7 @@
<el-select
size="mini"
v-model="metric"
placeholder=""
placeholder=" "
popper-class="common-select"
:popper-append-to-body="false"
@change="metricChange"
@@ -66,6 +66,7 @@
size="mini"
v-model="lineRefer"
:disabled="!lineTab"
placeholder=" "
popper-class="common-select"
:popper-append-to-body="false"
@change="referenceSelectChange"
@@ -123,12 +124,12 @@ export default {
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
// 优先级url > config.js > 默认值。
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.entity.trafficLine || 60)
const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(60)
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
} else {
@@ -226,6 +227,9 @@ export default {
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (!active) {
this.tabs = _.cloneDeep(this.tabsTemplate)
}
if (this.isNoData) {
this.lineTab = ''
this.tabs = _.cloneDeep(this.tabsTemplate)
@@ -497,7 +501,7 @@ export default {
})
}
if (data !== undefined && data.length > 0) {
if (data && data.length > 0) {
newData.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
@@ -510,6 +514,24 @@ export default {
})
}
lineData.splice(0, 1)
// TODO 下面的逻辑是判断total曲线的尾部数据从尾往前数0值的个数若个数大于0所有曲线都从尾部去掉相同数量的点最多4个
const totalData = lineData[0]
if (_.get(totalData, 'values', []).length > 4) {
let count = 0
for (let i = totalData.values.length - 1; i >= totalData.values.length - 4; i--) {
if (totalData.values[i].length > 1 && totalData.values[i][1] === 0) {
count++
} else {
break
}
}
if (count > 0) {
lineData.forEach(l => {
l.values.splice(l.values.length - count, count)
})
}
}
if (val === 'Sessions/s') {
const tabs = _.cloneDeep(this.tabsTemplate)
lineData.forEach((d, i) => {
@@ -527,6 +549,7 @@ export default {
})
this.tabs = tabs
this.$nextTick(() => {
this.lineRefer = 'Average'
this.echartsInit(this.tabs, true)
})
} else {
@@ -544,7 +567,7 @@ export default {
const self = this
tabs.forEach(e => {
e.unitType = type
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
if (e.name !== 'network.total' && parseFloat(e.analysis.max) === 0) {
e.show = false
num += 1
} else {
@@ -554,13 +577,18 @@ export default {
}
}
if (self.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
if (parseFloat(e.analysis.max) <= 0) {
self.lineTab = ''
self.lineRefer = ''
self.init()
// self.init() // 后续多测试关注
}
}
})
const emptyData = tabs.filter(d => parseFloat(d.analysis.max) === 0)
this.isNoData = emptyData.length === tabs.length
if (this.isNoData) {
return true
}
this.tabs = tabs
if (num === 5) {
tabs[0].invertTab = false

View File

@@ -14,12 +14,13 @@
<span :style="{color: tab.name === activeTab ? '#046ECA' : '#717171'}">{{ tab.tag }}</span>
</el-tag>
</template>
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></domain-name-resolution>
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></open-port>
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></domain-name-resolution>
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" @checkTag="setTag" />
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></open-port>
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></behavior-pattern>
</el-tab-pane>
</el-tabs>
</div>
@@ -28,24 +29,27 @@
<script>
import chartMixin from '@/views/charts2/chart-mixin'
import i18n from '@/i18n'
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
import { entityDetailTabsName, entityDetailTags } from '@/utils/constants'
import { reactive, ref } from 'vue'
import InformationAggregation from '@/views/charts2/charts/entityDetail/tabs/InformationAggregation'
import DomainNameResolution from '@/views/charts2/charts/entityDetail/tabs/DomainNameResolution'
import SecurityEvent from '@/views/charts2/charts/entityDetail/tabs/SecurityEvent'
import PerformanceEvent from '@/views/charts2/charts/entityDetail/tabs/PerformanceEvent'
import BehaviorPattern from '@/views/charts2/charts/entityDetail/tabs/BehaviorPattern'
import OpenPort from '@/views/charts2/charts/entityDetail/tabs/OpenPort'
import DigitalCertificate from '@/views/charts2/charts/entityDetail/tabs/DigitalCertificate'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { useRoute } from 'vue-router'
import axios from 'axios'
import { api } from '@/utils/api'
import { tagValueLabelMapping } from '../../../../utils/constants'
export default {
name: 'EntityDetailTabs',
mixins: [chartMixin],
components: {
PerformanceEvent,
BehaviorPattern,
SecurityEvent,
InformationAggregation,
DomainNameResolution,
@@ -54,12 +58,7 @@ export default {
},
data () {
return {
timer: null,
// 最近一天的时间
oneDayTimeFilter: {
startTime: window.$dayJs.tz().valueOf() - 1440 * 60 * 1000,
endTime: window.$dayJs.tz().valueOf()
}
timer: null
}
},
watch: {
@@ -90,6 +89,9 @@ export default {
if (entityType !== 'app') {
tabs.unshift({ name: entityDetailTabsName.informationAggregation, label: i18n.global.t('entities.informationAggregation'), icon: 'cn-icon cn-icon-information-aggregation', tag: 0 })
}
if (entityType === 'ip') {
tabs.push({ name: entityDetailTabsName.behaviorPattern, label: i18n.global.t('entities.behaviorPattern'), icon: 'cn-icon cn-icon-behavior', tag: 0 })
}
const activeTab = ref(tabs[0].name)
const { query } = useRoute()
@@ -107,22 +109,17 @@ export default {
},
methods: {
initData () {
const params = {
resource: this.entity.entityName
// startTime: getSecond(this.oneDayTimeFilter.startTime),
// endTime: getSecond(this.oneDayTimeFilter.endTime)
}
const url = this.getUrlByEntityType(this.entity.entityType)
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: params })
const openPort = axios.get(url, { params: params })
// const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
// const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: this.getParamsByTabType(entityDetailTabsName.informationAggregation) })
const openPort = axios.get(url, { params: this.getParamsByTabType(entityDetailTabsName.openPort) })
const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.securityEvent) })
const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.performanceEvent) })
Promise.all([informationAggregation, openPort]).then(response => {
if (response[0].status === 200) {
Promise.allSettled([informationAggregation, openPort, security, performance]).then(response => {
const informationAggregationResponse = response[0].value
if (informationAggregationResponse.status === 200) {
const list = []
response[0].data.data.result.forEach(r => {
informationAggregationResponse.data.data.result.forEach(r => {
Object.keys(r).forEach(k => {
const aggregation = {
createTime: r[k].createTime,
@@ -136,7 +133,7 @@ export default {
Object.keys(r[k]).forEach(k2 => {
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
if (find) {
aggregation.intelligenceContent.push({ key: k2, value: this.tagValueHandler(k, k2, r[k][k2]), type: find.type })
aggregation.intelligenceContent.push({ key: k2, value: this.tagValueHandler(r[k][k2]), type: find.type })
}
})
}
@@ -145,39 +142,42 @@ export default {
}
})
})
this.initSetTag(entityDetailTabsName.informationAggregation, list.length)
}
if (response[1].status === 200) {
this.initSetTag(entityDetailTabsName.openPort, response[1].data.data.result.length)
const openPortResponse = response[1].value
if (openPortResponse.status === 200) {
this.initSetTag(entityDetailTabsName.openPort, openPortResponse.data.data.result.length)
}
// if (response[2].status === 200) {
// this.initSetTag(entityDetailTabsName.securityEvent, response[2].data.data.result.length)
const securityResponse = response[2].value
if (securityResponse.status === 200) {
this.initSetTag(entityDetailTabsName.securityEvent, securityResponse.data.data.result.length)
}
// let performanceResponse = response[3].value
// if (performanceResponse.status === 200) {
// this.initSetTag(entityDetailTabsName.performanceEvent, performanceResponse.data.data.result.length)
// }
// if (response[3].status === 200) {
// this.initSetTag(entityDetailTabsName.performanceEvent, response[3].data.data.result.length)
// }
this.initSetTag(entityDetailTabsName.securityEvent, 0)
this.initSetTag(entityDetailTabsName.performanceEvent, 0)
})
const relatedEntityParams = this.getParamsByTabType(entityDetailTabsName.relatedEntity)
// 域名解析
if (this.entity.entityType === 'app') {
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: relatedEntityParams })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: relatedEntityParams })
this.promiseData(ipsOfApp, domainsOfApp)
}
if (this.entity.entityType === 'ip') {
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
this.promiseData(appsOfIp, domainsOfIp)
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: relatedEntityParams })
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: relatedEntityParams })
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: relatedEntityParams })
this.promiseData(appsOfIp, domainsOfIp, behaviorPattern)
}
if (this.entity.entityType === 'domain') {
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: params })
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: params })
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: params })
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: relatedEntityParams })
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: relatedEntityParams })
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: relatedEntityParams })
this.promiseData(appsOfDomain, ipsOfDomain, fqdnsOfDomain)
}
},
@@ -191,6 +191,18 @@ export default {
const len1 = res[0].status === 200 ? res0.data.result.length : 0
const len2 = res[1].status === 200 ? res1.data.result.length : 0
this.initSetTag(entityDetailTabsName.relatedEntity, len1 + len2)
const behaviorPatternData = res[2].status === 200 ? res[2].data.data.result : 0
let behaviorPatternLen = 0
if (behaviorPatternData && behaviorPatternData[0]) {
const dataObject = behaviorPatternData[0]
Object.keys(dataObject).forEach(key => {
const value = Number(dataObject[key]) ? Number(dataObject[key]) : 0
if (value !== 0) {
behaviorPatternLen++
}
})
}
this.initSetTag(entityDetailTabsName.behaviorPattern, behaviorPatternLen)
break
}
case 'domain': {
@@ -251,16 +263,9 @@ export default {
case 'app': return api.entity.openPortOfApp
}
},
tagValueHandler (k, k2, value) {
if (k === 'psiphon3Ip') {
if (k2 === 'type') {
const find = psiphon3IpType.find(t => t.value === value)
if (find) {
return find.name
}
}
}
return value
tagValueHandler (value) {
const find = tagValueLabelMapping.find(t => t.value === value)
return find ? find.name : value
}
},
beforeUnmount () {

View File

@@ -0,0 +1,190 @@
<template>
<chart-error v-if="showError" :content="errorMsg" class="entity-detail-event-error"></chart-error>
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div v-if="!isNoData && !showError" class="entity-detail-event-block" style="height: 100%;width: 100%;position: relative;">
<div class="behavior-pattern" >
<div class="behavior-pattern-legend" >
<div class="behavior-pattern-legend__item" v-for="(data, index) in tableData" :key="index">
<div class="legend-icon" :style="`background:${chartColorForBehaviorPattern[index%10]};`"></div>
<div class="legend-name">{{data.name}}</div>
<div class="legend-value" >{{ valueToRangeValue(data.value, unitTypes.number).join('')}}</div>
<div class="legend-percent">{{ valueToRangeValue(data.percent, unitTypes.percent).join('') }}</div>
</div>
</div>
<div id="entityIpRoseType" class="behavior-pattern-chart"></div>
<div class="chart-bottom-dot__right"></div>
<div class="chart-bottom-dot__left"></div>
</div>
</div>
</template>
<script>
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
import * as echarts from 'echarts'
import { pieChartOption4 } from '@/views/charts2/charts/options/echartOption'
import { shallowRef, ref } from 'vue'
import { entityDetailTabsName, chartColorForBehaviorPattern, unitTypes } from '@/utils/constants'
import { valueToRangeValue } from '@/utils/unit-convert'
import axios from 'axios'
import { api } from '@/utils/api'
import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
import { toUpperCaseByString, reverseSortBy } from '@/utils/tools'
import ChartNoData from '@/views/charts/charts/ChartNoData'
export default {
name: 'BehaviorPattern',
components: { ChartError, ChartNoData },
mixins: [chartMixin],
data () {
return {
showError: false,
errorMsg: '',
tableData: []
}
},
setup () {
const { query } = useRoute()
const entityType = query.entityType
const entityName = query.entityName
// range取 config.js 中配置的值
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.behaviorPattern
const timeFilter = ref({ dateRangeValue })
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
return {
entityType,
entityName,
myChart: shallowRef(null),
chartColorForBehaviorPattern,
unitTypes,
timeFilter
}
},
async mounted () {
await this.initData()
this.toggleLoading(true)
const timer = setTimeout(() => {
this.toggleLoading(false)
clearInterval(timer)
}, 200)
},
methods: {
valueToRangeValue,
toUpperCaseByString,
dateFormatByAppearance,
initEcharts () {
this.chartOption = pieChartOption4
this.chartOption.angleAxis.data = []
this.chartOption.series[0].data = []
this.tableData.forEach((item, index) => {
this.chartOption.angleAxis.data.push(item.name)
this.chartOption.series[0].data.push(item.value)
})
const len = this.tableData.length
const endIndex = len + 1
this.tableData.forEach((item, i) => {
if (i !== endIndex) {
this.chartOption.angleAxis.data.push(item.name + '2')
this.chartOption.series[0].data.push(0)
}
})
const axisLabel = this.chartOption.angleAxis.axisLabel
this.chartOption.angleAxis.axisLabel = {
...axisLabel,
formatter: function (params, index) {
if (len > 15) {
if (index === 7) {
return params + '\n'
} else if (index === 8) {
return '\n' + params
}
}
if (index === endIndex) {
return params + '\n'
} else {
return params
}
}
}
const self = this
this.$nextTick(() => {
if (self.myChart) {
self.myChart.dispose()
}
const dom = document.getElementById('entityIpRoseType')
if (dom) {
self.myChart = echarts.init(dom)
self.myChart.setOption(this.chartOption)
self.myChart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
brushOption: {
brushType: 'lineX',
xAxisIndex: 'all',
brushMode: 'single',
throttleType: 'debounce'
}
})
}
})
},
async initData () {
const params = this.getParams()
this.toggleLoading(true)
await axios.get(`${api.entity.behaviorPattern}`, { params: params }).then(response => {
const res = response.data
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (!this.isNoData) {
const data = res.data.result
this.tableData = []
if (data && data[0]) {
const dataObject = data[0]
Object.keys(dataObject).forEach(key => {
const value = Number(dataObject[key]) ? Number(dataObject[key]) : 0
if (value !== 0 && key !== 'total') {
this.tableData.push({
name: key,
value: value,
percent: dataObject.total ? value / dataObject.total : '-'
})
}
})
this.tableData = this.tableData.sort(reverseSortBy('value'))
this.$emit('checkTag', entityDetailTabsName.behaviorPattern, this.tableData.length)
if (this.tableData.length === 0) {
this.isNoData = true
}
this.initEcharts()
}
}
} else {
this.httpError(res)
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
this.$emit('checkTag', entityDetailTabsName.behaviorPattern, 0)
}
}
}
</script>

View File

@@ -70,15 +70,15 @@
<script>
import axios from 'axios'
import { api } from '@/utils/api'
import { getNowTime } from '@/utils/date-util'
import chartMixin from '@/views/charts2/chart-mixin'
import chartNoData from '@/views/charts/charts/ChartNoData'
import { entityDetailTabsName } from '@/utils/constants'
import { ref } from 'vue'
export default {
name: 'DomainNameResolution',
mixins: [chartMixin],
props: {
},
components: {
chartNoData
},
@@ -105,16 +105,24 @@ export default {
errorMsg2: ''
}
},
setup (props) {
// range取 config.js 中配置的值
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity
const timeFilter = ref({ dateRangeValue })
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
return {
timeFilter
}
},
mounted () {
this.initData()
},
methods: {
initData () {
const params = {
resource: this.entity.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
const params = this.getParams()
if (this.entity.entityType === 'app') {
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })

View File

@@ -71,9 +71,10 @@
import chartMixin from '@/views/charts2/chart-mixin'
import axios from 'axios'
import { api } from '@/utils/api'
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
import { dateFormatByAppearance } from '@/utils/date-util'
import { entityDetailTabsName, entityDetailTags, tagValueLabelMapping } from '@/utils/constants'
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
import chartNoData from '@/views/charts/charts/ChartNoData'
import { ref } from 'vue'
export default {
name: 'InformationAggregation',
@@ -83,6 +84,18 @@ export default {
loading: true
}
},
setup (props) {
// range取 config.js 中配置的值
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.informationAggregation
const timeFilter = ref({ dateRangeValue })
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
return {
timeFilter
}
},
mixins: [chartMixin],
components: { chartNoData },
methods: {
@@ -95,7 +108,7 @@ export default {
tagValueHandler (k, k2, value) {
if (k === 'psiphon3Ip') {
if (k2 === 'type') {
const find = psiphon3IpType.find(t => t.value === value)
const find = tagValueLabelMapping.find(t => t.value === value)
if (find) {
return find.name
}
@@ -108,7 +121,12 @@ export default {
this.showError = false
this.toggleLoading(true)
this.informationAggregationList = []
axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`).then(response => {
const params = this.getParams()
let timeStr = ''
if (params.startTime && params.endTime) {
timeStr = '&startTime=' + params.startTime + '&endTime=' + params.endTime
}
axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1${timeStr}`).then(response => {
const res = response.data
if (response.status === 200) {
// this.isNoData = res.data.result.length === 0

View File

@@ -7,7 +7,7 @@
<div class="type-data">
<div class="type-title">
<span class="title-mark"></span>
<span class="type-title-word">{{ $t('entities.tab.currentDevelopmentPortsAndServices') }}</span>({{ openPortList.length }})
<span class="type-title-word">{{ $t('entities.tab.currentOpenPortsAndServices') }}</span>({{ openPortList.length }})
</div>
<div class="type-content">
<div v-for="(openPort, index) in openPortList.slice(0,showOpenPortListInfo.num)" :key="index" class="data-item">
@@ -27,6 +27,8 @@ import chartMixin from '@/views/charts2/chart-mixin'
import { api } from '@/utils/api'
import chartNoData from '@/views/charts/charts/ChartNoData'
import { entityDetailTabsName } from '@/utils/constants'
import { ref } from 'vue'
import { getNowTime } from '@/utils/date-util'
export default {
name: 'OpenPort',
@@ -50,16 +52,21 @@ export default {
mounted () {
this.initData()
},
setup () {
setup (props) {
// range取 config.js 中配置的值
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.openPort
const timeFilter = ref({ dateRangeValue })
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
return {
timeFilter
}
},
methods: {
initData () {
const params = {
resource: this.entity.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
const params = this.getParams()
this.toggleLoading(true)
const url = this.getUrlByEntityType(this.entity.entityType)
axios.get(url, { params: params }).then(response => {

View File

@@ -26,7 +26,7 @@
<div class="basic-info__item" v-if="item.eventSeverity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{ $t('network.severity') }}&nbsp;:&nbsp;&nbsp;</span>
<span :test-id="`severity${index}`">{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
<span :test-id="`severity${index}`">{{ changeSecurity(item.eventSeverity) }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-time2"></i>
@@ -50,8 +50,8 @@
</template>
<script>
import { dateFormatByAppearance } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from 'axios'
import { api } from '@/utils/api'
@@ -60,6 +60,7 @@ import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
import { toUpperCaseByString } from '@/utils/tools'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import { ref } from 'vue'
export default {
name: 'PerformanceEvent',
@@ -77,33 +78,36 @@ export default {
const { query } = useRoute()
const entityType = query.entityType
const entityName = query.entityName
// range取 config.js 中配置的值
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.performanceEvent
const timeFilter = ref({ dateRangeValue })
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
return {
entityType,
entityName
entityName,
timeFilter
}
},
mounted () {
// this.initData()
this.initData()
/*
this.isNoData = true
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
this.toggleLoading(true)
const timer = setTimeout(() => {
this.toggleLoading(false)
clearInterval(timer)
}, 200)
}, 200) */
},
methods: {
unitConvert,
toUpperCaseByString,
dateFormatByAppearance,
initData () {
const params = {
resource: this.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
const params = this.getParams()
this.toggleLoading(true)
axios.get(`${api.entity.performance}/${this.entityType}`, { params: params }).then(response => {
const res = response.data
@@ -130,6 +134,18 @@ export default {
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
},
changeSecurity (value) {
if (value) {
const obj = securityLevel.find(d => d.value === value)
let label = value
if (obj) {
label = this.$t(obj.label)
}
return label
} else {
return '-'
}
}
}
}

View File

@@ -9,7 +9,7 @@
:key="item.eventId">
<div class="cn-detection--list">
<div class="cn-detection__case entity-detail-security">
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
<div class="cn-detection__icon"></div>
<div class="cn-detection__row">
<div class="cn-detection__header">
<span
@@ -17,7 +17,7 @@
class="detection-event-severity-color-block"
:style="`background-color: ${eventSeverityColor[item.eventSeverity]}`">
</span>
<span class="detection-event-severity-block">{{ toUpperCaseByString(item.securityType) || '-' }}</span>
<span class="detection-event-severity-block">{{ item.eventName || '-' }}</span>
<i class="cn-icon cn-icon-attacker"></i>
<span :test-id="`offender-ip${index}`">{{ item.offenderIp || '-' }}</span>
<div class="domain">{{ item.offenderDomain }}</div>
@@ -38,7 +38,7 @@
<div class="basic-info__item" v-if="item.eventSeverity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{ $t('network.severity') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
<span>{{ changeSecurity(item.eventSeverity) }}</span>
</div>
<div class="basic-info__item" v-if="item.eventType">
<i class="cn-icon cn-icon-event-type"></i>
@@ -76,8 +76,8 @@
</template>
<script>
import { dateFormatByAppearance } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from 'axios'
import { api } from '@/utils/api'
@@ -85,6 +85,7 @@ import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
import { toUpperCaseByString } from '@/utils/tools'
import chartNoData from '@/views/charts/charts/ChartNoData'
import { ref } from 'vue'
export default {
name: 'SecurityEvent',
@@ -102,33 +103,36 @@ export default {
const { query } = useRoute()
const entityType = query.entityType
const entityName = query.entityName
// range取 config.js 中配置的值
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.securityEvent
const timeFilter = ref({ dateRangeValue })
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
return {
entityType,
entityName
entityName,
timeFilter
}
},
mounted () {
// this.initData()
this.initData()
/*
this.isNoData = true
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
this.toggleLoading(true)
const timer = setTimeout(() => {
this.toggleLoading(false)
clearInterval(timer)
}, 200)
}, 200) */
},
methods: {
unitConvert,
toUpperCaseByString,
dateFormatByAppearance,
initData () {
const params = {
resource: this.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
const params = this.getParams()
this.toggleLoading(true)
axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
const res = response.data
@@ -155,6 +159,18 @@ export default {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
},
changeSecurity (value) {
if (value) {
const obj = securityLevel.find(d => d.value === value)
let label = value
if (obj) {
label = this.$t(obj.label)
}
return label
} else {
return '-'
}
}
}
}

View File

@@ -355,12 +355,12 @@ export default {
if (out < 0.0001 && out !== 0) {
outUsage = '< 0.01%'
} else {
outUsage = JSON.stringify(parseFloat((out * 100).toFixed(2)))
outUsage = JSON.stringify(parseFloat(out * 100).toFixed(2))
}
if (_in < 0.0001 && _in !== 0) {
inUsage = '< 0.01%'
} else {
inUsage = JSON.stringify(parseFloat((_in * 100).toFixed(2)))
inUsage = JSON.stringify(parseFloat(_in * 100).toFixed(2))
}
length = outUsage.length + inUsage.length

View File

@@ -12,7 +12,7 @@
import chartMixin from '@/views/charts2/chart-mixin'
import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api'
import { storageKey } from '@/utils/constants'
import { storageKey, ZH } from '@/utils/constants'
import PopoverContent from './LinkDirectionGrid/PopoverContent'
import { computeScore } from '@/utils/tools'
import axios from 'axios'
@@ -30,7 +30,8 @@ export default {
isLinkShowError: false, // 显示左侧链路报错标识
linkErrorMsg: '', // 左侧链路的报错信息
isNextShowError: false, // 显示右侧下一跳报错标识
nextErrorMsg: '' // 右侧下一跳的报错信息
nextErrorMsg: '', // 右侧下一跳的报错信息
scoreDataState: false // 评分数据是否加载完成
}
},
components: {
@@ -41,6 +42,23 @@ export default {
handler () {
this.init()
}
},
scoreBaseState (n) {
if (n && this.scoreDataState) {
this.handleScoreData(this.linkGridData)
this.handleScoreData(this.nextGridData)
}
},
scoreDataState (n) {
if (n && this.scoreBaseState) {
this.handleScoreData(this.linkGridData)
this.handleScoreData(this.nextGridData)
}
}
},
computed: {
scoreBaseState () {
return this.$store.getters.scoreBaseReady
}
},
mounted () {
@@ -48,6 +66,7 @@ export default {
},
methods: {
init () {
this.scoreDataState = false
// 链路基本信息
let linkInfo = localStorage.getItem(storageKey.linkInfo)
linkInfo = JSON.parse(linkInfo)
@@ -117,9 +136,9 @@ export default {
d.usageMore90 = outUsage >= 0.9 || inUsage >= 0.9
// 计算npm分数
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
// d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3 || d.score === '-'
// d.scoreLow3 = d.score < 3 || d.score === '-'
const xAxis = inLink.linkId.split('Hundredgige').pop() - 1
const yAxis = outLink.linkId.split('Hundredgige').pop() - 1
@@ -137,11 +156,9 @@ export default {
})
// 一行如果无数据则删除该行默认10*10矩阵
const rowXIndex = 0
this.handleXRowNoData(linkGridData, rowXIndex)
this.handleXRowNoData(linkGridData, 0)
// 一列如果无数据则删除该列默认10*10矩阵
const rowYIndex = 0
this.handleYRowNoData(linkGridData, rowYIndex)
this.handleYRowNoData(linkGridData, 0)
this.isLinkNoData = linkGridData.length === 0
this.linkGridData = linkGridData
}
@@ -159,9 +176,9 @@ export default {
// 接口数据乱序,根据入方向排序,再根据同个入方向下的出方向进行排序
nextLinkData.sort((a, b) => {
if (a.inLinkDirection !== b.inLinkDirection) {
return a.inLinkDirection.localeCompare(b.inLinkDirection, 'zh')
return a.inLinkDirection.localeCompare(b.inLinkDirection, ZH)
}
return a.outLinkDirection.localeCompare(b.outLinkDirection, 'zh')
return a.outLinkDirection.localeCompare(b.outLinkDirection, ZH)
})
this.isNextNoData = nextLinkData.length === 0
@@ -210,9 +227,9 @@ export default {
d.usageMore90 = outUsage >= 0.9 || inUsage >= 0.9
// 计算npm分数
// 分数低于3分赋红点
d.score = this.localComputeScore(d)
// d.score = this.localComputeScore(d)
d.scoreLow3 = d.score < 3 || d.score === '-'
// d.scoreLow3 = d.score < 3 || d.score === '-'
const xAxis = inLink.linkId
const yAxis = outLink.linkId
@@ -238,11 +255,9 @@ export default {
})
// 一行如果无数据则删除该行默认3*3矩阵
const rowXIndex = 0
this.handleXRowNoData(nextGridData, rowXIndex)
this.handleXRowNoData(nextGridData, 0)
// 一列如果无数据则删除该列默认3*3矩阵
const rowYIndex = 0
this.handleYRowNoData(nextGridData, rowYIndex)
this.handleYRowNoData(nextGridData, 0)
this.isNextNoData = nextGridData.length === 0
this.nextGridData = nextGridData
@@ -254,6 +269,7 @@ export default {
}
}
}).finally(() => {
this.scoreDataState = true
this.toggleLoading(false)
})
},
@@ -282,6 +298,23 @@ export default {
score = computeScore(dataScore)
return score
},
handleScoreData (data) {
data.forEach(d => {
if (d.out) {
d.out.forEach(t => {
const data = {
establishLatencyMs: t.establishLatencyMs,
httpResponseLatency: t.httpResponseLatency,
sslConLatency: t.sslConLatency,
tcpLostlenPercent: t.tcpLostlenPercent,
pktRetransPercent: t.pktRetransPercent
}
t.score = computeScore(data, this.$store.getters.getScoreBase)
t.scoreLow3 = t.score < 3 || t.score === '-'
})
}
})
},
/**
* 计算popover弹窗和右侧数据模块的宽度
* 弹窗最小宽度为360px右侧数据最小宽度为75px右侧数据每大一位popover弹窗宽度增加7px
@@ -320,7 +353,7 @@ export default {
* @param index
*/
handleXRowNoData (data, index) {
if (data) {
if (data && data.length > 0) {
const item = data[index]
let tempList = []
if (item) {
@@ -343,7 +376,7 @@ export default {
*/
handleYRowNoData (data, index) {
const rowList = []
if (data) {
if (data && data.length > 0) {
data.forEach(item => {
if (item.out[index]) {
if (item.out[index].noData) {

View File

@@ -37,11 +37,12 @@
</div>
<div class="line-select line-header-right">
<div class="line-select-metric">
<span>{{$t('network.metric')}}:</span>
<span>{{$t('detections.metric')}}:</span>
<div class="line-select__operation">
<el-select
size="mini"
v-model="lineMetric"
placeholder=" "
popper-class="common-select"
:popper-append-to-body="false"
@change="metricSelectChange"
@@ -156,7 +157,7 @@ export default {
endTime: getSecond(this.timeFilter.endTime)
}
if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ')
const condition = this.queryCondition.split(' or ')
if (condition.length > 1) {
params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -1)
params.inParam = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1)
@@ -168,6 +169,9 @@ export default {
if (response.status === 200) {
this.showError = false
this.isNoData = res.data.result.length === 0
if (!active) {
this.tabs = dataForLinkTrafficLine.tabs
}
if (this.isNoData) {
this.lineTab = ''
this.tabs = dataForLinkTrafficLine.tabs
@@ -387,7 +391,7 @@ export default {
let num = 0
linkData.forEach(e => {
e.unitType = type
if (parseFloat(e.analysis.avg) === 0 || isNaN(parseFloat(e.analysis.avg))) {
if (parseFloat(e.analysis.max) === 0 || isNaN(parseFloat(e.analysis.max))) {
e.show = false
num += 1
} else {
@@ -397,13 +401,18 @@ export default {
}
}
if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
if (parseFloat(e.analysis.max) <= 0) {
this.lineTab = ''
this.lineRefer = ''
this.init()
// this.init() // 暂时注掉,后续观察
}
}
})
const emptyData = linkData.filter(d => parseFloat(d.analysis.max) === 0)
this.isNoData = emptyData.length === linkData.length
if (this.isNoData) {
return true
}
this.tabs = linkData
// 如果三者avg都为0时至少保证total显示
const ingressObj = linkData.find(d => d.name === 'linkMonitor.ingress')
@@ -411,10 +420,10 @@ export default {
let ingressAvg = 0
let egressAvg = 0
if (ingressObj) {
ingressAvg = parseFloat(ingressObj.analysis.avg) || 0
ingressAvg = parseFloat(ingressObj.analysis.max) || 0
}
if (egressObj) {
egressAvg = parseFloat(egressObj.analysis.avg) || 0
egressAvg = parseFloat(egressObj.analysis.max) || 0
}
if ((ingressAvg + egressAvg) === 0) {
const totalObj = linkData.find(d => d.name === 'network.total')

View File

@@ -100,7 +100,7 @@ export default {
}
let url = ''
if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ')
const condition = this.queryCondition.split(' or ')
if (condition.length > 1) {
if (n === 0) {
params.q = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1)

View File

@@ -87,7 +87,14 @@ export default {
bandWidth: 0,
loading: false,
showError: false,
errorMsg: ''
errorMsg: '',
scoreDataState: false,
performanceData: {}
}
},
computed: {
scoreBaseState () {
return this.$store.getters.scoreBaseReady
}
},
watch: {
@@ -95,6 +102,16 @@ export default {
handler (n) {
this.linkTrafficData()
}
},
scoreBaseState (n) {
if (n && this.scoreDataState) {
this.handleScoreData()
}
},
scoreDataState (n) {
if (n && this.scoreBaseState) {
this.handleScoreData()
}
}
},
methods: {
@@ -103,8 +120,11 @@ export default {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
this.loading = true
this.performanceData = {}
this.scoreDataState = false
if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ')
const condition = this.queryCondition.split(' or ')
if (condition.length > 1) {
// params.outParam = true
params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -1)
@@ -142,7 +162,6 @@ export default {
this.bandWidth = bandwidthAll
}
}
this.loading = true
axios.get(api.linkMonitor.networkAnalysis, { params: params }).then(response => {
const res = response.data
if (response.status === 200) {
@@ -152,7 +171,7 @@ export default {
this.linkTrafficListData = {}
this.linkTrafficListData.npmScore = '-'
} else {
const data = {
this.performanceData = {
establishLatencyMs: _.get(res.data.result[0], 'establishLatencyMs', null),
httpResponseLatency: _.get(res.data.result[0], 'httpResponseLatency', null),
sslConLatency: _.get(res.data.result[0], 'sslConLatency', null),
@@ -160,19 +179,24 @@ export default {
pktRetransPercent: _.get(res.data.result[0], 'pktRetransPercent', null)
}
this.linkTrafficListData = res.data.result[0]
this.linkTrafficListData.npmScore = computeScore(data)
// this.linkTrafficListData.npmScore = computeScore(data)
}
} else {
this.showError = true
this.errorMsg = res.message
}
}).catch(e => {
console.error(e)
this.showError = true
this.errorMsg = e.message
this.isNoData = false
}).finally(() => {
this.loading = false
this.scoreDataState = true
})
},
handleScoreData () {
this.linkTrafficListData.npmScore = computeScore(this.performanceData, this.$store.getters.getScoreBase)
}
},
mounted () {

View File

@@ -1,994 +0,0 @@
export default [
{
value: 'Afghanistan',
label: 'Afghanistan'
},
{
value: 'Albania',
label: 'Albania'
},
{
value: 'Algeria',
label: 'Algeria'
},
{
value: 'American Samoa',
label: 'American Samoa'
},
{
value: 'Andorra',
label: 'Andorra'
},
{
value: 'Angola',
label: 'Angola'
},
{
value: 'Anguilla',
label: 'Anguilla'
},
{
value: 'Antarctica',
label: 'Antarctica'
},
{
value: 'Antigua and Barbuda',
label: 'Antigua and Barbuda'
},
{
value: 'Argentina',
label: 'Argentina'
},
{
value: 'Armenia',
label: 'Armenia'
},
{
value: 'Aruba',
label: 'Aruba'
},
{
value: 'Australia',
label: 'Australia'
},
{
value: 'Austria',
label: 'Austria'
},
{
value: 'Azerbaijan',
label: 'Azerbaijan'
},
{
value: 'Bahamas (the)',
label: 'Bahamas (the)'
},
{
value: 'Bahrain',
label: 'Bahrain'
},
{
value: 'Bangladesh',
label: 'Bangladesh'
},
{
value: 'Barbados',
label: 'Barbados'
},
{
value: 'Belarus',
label: 'Belarus'
},
{
value: 'Belgium',
label: 'Belgium'
},
{
value: 'Belize',
label: 'Belize'
},
{
value: 'Benin',
label: 'Benin'
},
{
value: 'Bermuda',
label: 'Bermuda'
},
{
value: 'Åland Islands',
label: 'Åland Islands'
},
{
value: 'Bhutan',
label: 'Bhutan'
},
{
value: 'Bolivia (Plurinational State of)',
label: 'Bolivia (Plurinational State of)'
},
{
value: 'Bonaire, Sint Eustatius and Saba',
label: 'Bonaire, Sint Eustatius and Saba'
},
{
value: 'Bosnia and Herzegovina',
label: 'Bosnia and Herzegovina'
},
{
value: 'Botswana',
label: 'Botswana'
},
{
value: 'Bouvet Island',
label: 'Bouvet Island'
},
{
value: 'Brazil',
label: 'Brazil'
},
{
value: 'British Indian Ocean Territory (the)',
label: 'British Indian Ocean Territory (the)'
},
{
value: 'Brunei Darussalam',
label: 'Brunei Darussalam'
},
{
value: 'Bulgaria',
label: 'Bulgaria'
},
{
value: 'Burkina Faso',
label: 'Burkina Faso'
},
{
value: 'Burundi',
label: 'Burundi'
},
{
value: 'Cabo Verde',
label: 'Cabo Verde'
},
{
value: 'Cambodia',
label: 'Cambodia'
},
{
value: 'Cameroon',
label: 'Cameroon'
},
{
value: 'Canada',
label: 'Canada'
},
{
value: 'Cayman Islands (the)',
label: 'Cayman Islands (the)'
},
{
value: 'Central African Republic (the)',
label: 'Central African Republic (the)'
},
{
value: 'Chad',
label: 'Chad'
},
{
value: 'Chile',
label: 'Chile'
},
{
value: 'China',
label: 'China'
},
{
value: 'Christmas Island',
label: 'Christmas Island'
},
{
value: 'Cocos (Keeling) Islands (the)',
label: 'Cocos (Keeling) Islands (the)'
},
{
value: 'Colombia',
label: 'Colombia'
},
{
value: 'Comoros (the)',
label: 'Comoros (the)'
},
{
value: 'Congo (the Democratic Republic of the)',
label: 'Congo (the Democratic Republic of the)'
},
{
value: 'Congo (the)',
label: 'Congo (the)'
},
{
value: 'Cook Islands (the)',
label: 'Cook Islands (the)'
},
{
value: 'Costa Rica',
label: 'Costa Rica'
},
{
value: 'Croatia',
label: 'Croatia'
},
{
value: 'Cuba',
label: 'Cuba'
},
{
value: 'Curaçao',
label: 'Curaçao'
},
{
value: 'Cyprus',
label: 'Cyprus'
},
{
value: 'Czech Republic',
label: 'Czech Republic'
},
{
value: "Côte d'Ivoire",
label: "Côte d'Ivoire"
},
{
value: 'Denmark',
label: 'Denmark'
},
{
value: 'Djibouti',
label: 'Djibouti'
},
{
value: 'Dominica',
label: 'Dominica'
},
{
value: 'Dominican Republic (the)',
label: 'Dominican Republic (the)'
},
{
value: 'Ecuador',
label: 'Ecuador'
},
{
value: 'Egypt',
label: 'Egypt'
},
{
value: 'El Salvador',
label: 'El Salvador'
},
{
value: 'Equatorial Guinea',
label: 'Equatorial Guinea'
},
{
value: 'Eritrea',
label: 'Eritrea'
},
{
value: 'Estonia',
label: 'Estonia'
},
{
value: 'Eswatini',
label: 'Eswatini'
},
{
value: 'Ethiopia',
label: 'Ethiopia'
},
{
value: 'Falkland Islands (the) [Malvinas]',
label: 'Falkland Islands (the) [Malvinas]'
},
{
value: 'Faroe Islands (the)',
label: 'Faroe Islands (the)'
},
{
value: 'Fiji',
label: 'Fiji'
},
{
value: 'Finland',
label: 'Finland'
},
{
value: 'France',
label: 'France'
},
{
value: 'French Guiana',
label: 'French Guiana'
},
{
value: 'French Polynesia',
label: 'French Polynesia'
},
{
value: 'French Southern Territories (the)',
label: 'French Southern Territories (the)'
},
{
value: 'Gabon',
label: 'Gabon'
},
{
value: 'Gambia (the)',
label: 'Gambia (the)'
},
{
value: 'Georgia',
label: 'Georgia'
},
{
value: 'Germany',
label: 'Germany'
},
{
value: 'Ghana',
label: 'Ghana'
},
{
value: 'Gibraltar',
label: 'Gibraltar'
},
{
value: 'Greece',
label: 'Greece'
},
{
value: 'Greenland',
label: 'Greenland'
},
{
value: 'Grenada',
label: 'Grenada'
},
{
value: 'Guadeloupe',
label: 'Guadeloupe'
},
{
value: 'Guam',
label: 'Guam'
},
{
value: 'Guatemala',
label: 'Guatemala'
},
{
value: 'Guernsey',
label: 'Guernsey'
},
{
value: 'Guinea',
label: 'Guinea'
},
{
value: 'Guinea-Bissau',
label: 'Guinea-Bissau'
},
{
value: 'Guyana',
label: 'Guyana'
},
{
value: 'Haiti',
label: 'Haiti'
},
{
value: 'Heard Island and McDonald Islands',
label: 'Heard Island and McDonald Islands'
},
{
value: 'Holy See (the)',
label: 'Holy See (the)'
},
{
value: 'Honduras',
label: 'Honduras'
},
{
value: 'Hong Kong',
label: 'Hong Kong'
},
{
value: 'Hungary',
label: 'Hungary'
},
{
value: 'Iceland',
label: 'Iceland'
},
{
value: 'India',
label: 'India'
},
{
value: 'Indonesia',
label: 'Indonesia'
},
{
value: 'Iran (Islamic Republic of)',
label: 'Iran (Islamic Republic of)'
},
{
value: 'Iraq',
label: 'Iraq'
},
{
value: 'Ireland',
label: 'Ireland'
},
{
value: 'Isle of Man',
label: 'Isle of Man'
},
{
value: 'Israel',
label: 'Israel'
},
{
value: 'Italy',
label: 'Italy'
},
{
value: 'Jamaica',
label: 'Jamaica'
},
{
value: 'Japan',
label: 'Japan'
},
{
value: 'Jersey',
label: 'Jersey'
},
{
value: 'Jordan',
label: 'Jordan'
},
{
value: 'Kazakhstan',
label: 'Kazakhstan'
},
{
value: 'Kenya',
label: 'Kenya'
},
{
value: 'Kiribati',
label: 'Kiribati'
},
{
value: 'Korea',
label: 'Korea'
},
{
value: 'Kuwait',
label: 'Kuwait'
},
{
value: 'Kyrgyzstan',
label: 'Kyrgyzstan'
},
{
value: "Lao People's Democratic Republic (the)",
label: "Lao People's Democratic Republic (the)"
},
{
value: 'Latvia',
label: 'Latvia'
},
{
value: 'Lebanon',
label: 'Lebanon'
},
{
value: 'Lesotho',
label: 'Lesotho'
},
{
value: 'Liberia',
label: 'Liberia'
},
{
value: 'Libya',
label: 'Libya'
},
{
value: 'Liechtenstein',
label: 'Liechtenstein'
},
{
value: 'Lithuania',
label: 'Lithuania'
},
{
value: 'Luxembourg',
label: 'Luxembourg'
},
{
value: 'Macao',
label: 'Macao'
},
{
value: 'Madagascar',
label: 'Madagascar'
},
{
value: 'Malawi',
label: 'Malawi'
},
{
value: 'Malaysia',
label: 'Malaysia'
},
{
value: 'Maldives',
label: 'Maldives'
},
{
value: 'Mali',
label: 'Mali'
},
{
value: 'Malta',
label: 'Malta'
},
{
value: 'Marshall Islands (the)',
label: 'Marshall Islands (the)'
},
{
value: 'Martinique',
label: 'Martinique'
},
{
value: 'Mauritania',
label: 'Mauritania'
},
{
value: 'Mauritius',
label: 'Mauritius'
},
{
value: 'Mayotte',
label: 'Mayotte'
},
{
value: 'Mexico',
label: 'Mexico'
},
{
value: 'Micronesia (Federated States of)',
label: 'Micronesia (Federated States of)'
},
{
value: 'Moldova (the Republic of)',
label: 'Moldova (the Republic of)'
},
{
value: 'Monaco',
label: 'Monaco'
},
{
value: 'Mongolia',
label: 'Mongolia'
},
{
value: 'Montenegro',
label: 'Montenegro'
},
{
value: 'Montserrat',
label: 'Montserrat'
},
{
value: 'Morocco',
label: 'Morocco'
},
{
value: 'Mozambique',
label: 'Mozambique'
},
{
value: 'Myanmar',
label: 'Myanmar'
},
{
value: 'Namibia',
label: 'Namibia'
},
{
value: 'Nauru',
label: 'Nauru'
},
{
value: 'Nepal',
label: 'Nepal'
},
{
value: 'Netherlands',
label: 'Netherlands'
},
{
value: 'New Caledonia',
label: 'New Caledonia'
},
{
value: 'New Zealand',
label: 'New Zealand'
},
{
value: 'Nicaragua',
label: 'Nicaragua'
},
{
value: 'Niger',
label: 'Niger'
},
{
value: 'Nigeria',
label: 'Nigeria'
},
{
value: 'Niue',
label: 'Niue'
},
{
value: 'Norfolk Island',
label: 'Norfolk Island'
},
{
value: 'North Macedonia',
label: 'North Macedonia'
},
{
value: 'Northern Mariana Islands (the)',
label: 'Northern Mariana Islands (the)'
},
{
value: 'Norway',
label: 'Norway'
},
{
value: 'Oman',
label: 'Oman'
},
{
value: 'Pakistan',
label: 'Pakistan'
},
{
value: 'Palau',
label: 'Palau'
},
{
value: 'Palestine, State of',
label: 'Palestine, State of'
},
{
value: 'Panama',
label: 'Panama'
},
{
value: 'Papua New Guinea',
label: 'Papua New Guinea'
},
{
value: 'Paraguay',
label: 'Paraguay'
},
{
value: 'Peru',
label: 'Peru'
},
{
value: 'Philippines',
label: 'Philippines'
},
{
value: 'Pitcairn',
label: 'Pitcairn'
},
{
value: 'Poland',
label: 'Poland'
},
{
value: 'Portugal',
label: 'Portugal'
},
{
value: 'Puerto Rico',
label: 'Puerto Rico'
},
{
value: 'Qatar',
label: 'Qatar'
},
{
value: 'Romania',
label: 'Romania'
},
{
value: 'Russian Federation',
label: 'Russian Federation'
},
{
value: 'Rwanda',
label: 'Rwanda'
},
{
value: 'Réunion',
label: 'Réunion'
},
{
value: 'Saint Barthélemy',
label: 'Saint Barthélemy'
},
{
value: 'Saint Helena, Ascension and Tristan da Cunha',
label: 'Saint Helena, Ascension and Tristan da Cunha'
},
{
value: 'Saint Kitts and Nevis',
label: 'Saint Kitts and Nevis'
},
{
value: 'Saint Lucia',
label: 'Saint Lucia'
},
{
value: 'Saint Martin',
label: 'Saint Martin'
},
{
value: 'Saint Pierre and Miquelon',
label: 'Saint Pierre and Miquelon'
},
{
value: 'Saint Vincent and the Grenadines',
label: 'Saint Vincent and the Grenadines'
},
{
value: 'Samoa',
label: 'Samoa'
},
{
value: 'San Marino',
label: 'San Marino'
},
{
value: 'Sao Tome and Principe',
label: 'Sao Tome and Principe'
},
{
value: 'Saudi Arabia',
label: 'Saudi Arabia'
},
{
value: 'Senegal',
label: 'Senegal'
},
{
value: 'Serbia',
label: 'Serbia'
},
{
value: 'Seychelles',
label: 'Seychelles'
},
{
value: 'Sierra Leone',
label: 'Sierra Leone'
},
{
value: 'Singapore',
label: 'Singapore'
},
{
value: 'Sint Maarten',
label: 'Sint Maarten'
},
{
value: 'Slovakia',
label: 'Slovakia'
},
{
value: 'Slovenia',
label: 'Slovenia'
},
{
value: 'Solomon Islands',
label: 'Solomon Islands'
},
{
value: 'Somalia',
label: 'Somalia'
},
{
value: 'South Africa',
label: 'South Africa'
},
{
value: 'South Georgia and the South Sandwich Islands',
label: 'South Georgia and the South Sandwich Islands'
},
{
value: 'South Sudan',
label: 'South Sudan'
},
{
value: 'Spain',
label: 'Spain'
},
{
value: 'Sri Lanka',
label: 'Sri Lanka'
},
{
value: 'Sudan',
label: 'Sudan'
},
{
value: 'Suriname',
label: 'Suriname'
},
{
value: 'Svalbard and Jan Mayen',
label: 'Svalbard and Jan Mayen'
},
{
value: 'Sweden',
label: 'Sweden'
},
{
value: 'Switzerland',
label: 'Switzerland'
},
{
value: 'Syrian Arab Republic',
label: 'Syrian Arab Republic'
},
{
value: 'Taiwan',
label: 'Taiwan'
},
{
value: 'Tajikistan',
label: 'Tajikistan'
},
{
value: 'Tanzania, the United Republic of',
label: 'Tanzania, the United Republic of'
},
{
value: 'Thailand',
label: 'Thailand'
},
{
value: 'Timor-Leste',
label: 'Timor-Leste'
},
{
value: 'Togo',
label: 'Togo'
},
{
value: 'Tokelau',
label: 'Tokelau'
},
{
value: 'Tonga',
label: 'Tonga'
},
{
value: 'Trinidad and Tobago',
label: 'Trinidad and Tobago'
},
{
value: 'Tunisia',
label: 'Tunisia'
},
{
value: 'Turkey',
label: 'Turkey'
},
{
value: 'Turkmenistan',
label: 'Turkmenistan'
},
{
value: 'Turks and Caicos Islands',
label: 'Turks and Caicos Islands'
},
{
value: 'Tuvalu',
label: 'Tuvalu'
},
{
value: 'Uganda',
label: 'Uganda'
},
{
value: 'Ukraine',
label: 'Ukraine'
},
{
value: 'United Arab Emirates',
label: 'United Arab Emirates'
},
{
value: 'United Kingdom of Great Britain and Northern Ireland',
label: 'United Kingdom of Great Britain and Northern Ireland'
},
{
value: 'United States Minor Outlying Islands',
label: 'United States Minor Outlying Islands'
},
{
value: 'United States',
label: 'United States'
},
{
value: 'Uruguay',
label: 'Uruguay'
},
{
value: 'Uzbekistan',
label: 'Uzbekistan'
},
{
value: 'Vanuatu',
label: 'Vanuatu'
},
{
value: 'Venezuela (Bolivarian Republic of)',
label: 'Venezuela (Bolivarian Republic of)'
},
{
value: 'Viet Nam',
label: 'Viet Nam'
},
{
value: 'Virgin Islands (British)',
label: 'Virgin Islands (British)'
},
{
value: 'Virgin Islands (U.S.)',
label: 'Virgin Islands (U.S.)'
},
{
value: 'Wallis and Futuna',
label: 'Wallis and Futuna'
},
{
value: 'Western Sahara*',
label: 'Western Sahara*'
},
{
value: 'Yemen',
label: 'Yemen'
},
{
value: 'Zambia',
label: 'Zambia'
},
{
value: 'Zimbabwe',
label: 'Zimbabwe'
}
]

View File

@@ -326,7 +326,7 @@ export default {
if (tabType) {
const oldCurTab = this.getUrlParam(this.curTabState.networkOverviewBeforeTab, '')
const curTable = networkTable.networkOverview
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
const list = await getUserDrilldownTableConfig(tableType, metric)
const tabGroup = list.filter(item => item.label === tabType)
@@ -493,11 +493,9 @@ export default {
}
})
})
if (val) {
if (!show) { // 非滚动滚动条操作,直接覆盖之前的数据
this.providerOptions = res.data.list
} else if (!val && !show) {
this.providerOptions = res.data.list
} else {
} else { // 滚动条操作,则将新数据和之前的数据组合
this.providerOptions.push(...res.data.list)
this.appListData([], this.providerOptions)
}
@@ -519,11 +517,9 @@ export default {
}
})
})
if (val) {
if (!show) { // 非滚动滚动条操作,直接覆盖之前的数据
this.appOptions = res.data.list
} else if (!val && !show) {
this.appOptions = res.data.list
} else {
} else { // 滚动条操作,则将新数据和之前的数据组合
this.appOptions.push(...res.data.list)
this.appListData(this.appOptions, [])
}

View File

@@ -66,7 +66,7 @@ export default {
startTime: this.timeFilter && this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : '',
endTime: this.timeFilter && this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : ''
}
/*this.toggleLoading(true)
/* this.toggleLoading(true)
axios.get(api.netWorkOverview.ddosEventAnalysis, { params: params }).then(response => {
const res = response.data
if (response.status === 200) {
@@ -85,7 +85,7 @@ export default {
this.errorMsg = this.errorMsgHandler(e)
}).finally(() => {
this.toggleLoading(false)
})*/
}) */
this.toggleLoading(false)
}
},

View File

@@ -32,17 +32,20 @@
</div>
<div class="line-select line-header-right">
<div class="line-select-reference-line">
<span>{{ $t('network.referenceLine') }}:</span>
<span>{{ $t('network.referenceLine') }}&nbsp;:&nbsp;</span>
<div class="line-select__operation">
<el-select
size="mini"
v-model="lineRefer"
:disabled="!lineTab"
placeholder=" "
popper-class="common-select"
:popper-append-to-body="false"
@change="referenceSelectChange"
>
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"></el-option>
<el-option :key="options2[0].value" :label="$t(options2[0].label)" :value="options2[0].value"></el-option>
<el-option :key="options2[1].value" :label="$t(options2[1].label[0], options2[1].label[1])" :value="options2[1].value"></el-option>
<el-option :key="options2[2].value" :label="$t(options2[2].label)" :value="options2[2].value"></el-option>
</el-select>
</div>
</div>
@@ -192,6 +195,9 @@ export default {
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (!active) {
this.tabs = _.cloneDeep(this.tabsTemplate)
}
if (this.isNoData) {
this.lineTab = ''
this.tabs = _.cloneDeep(this.tabsTemplate)
@@ -270,6 +276,14 @@ export default {
label: {
formatter (params) {
const arr = valueToRangeValue(params.value, unitTypes.number).join('')
const referIndex = _this.options2.findIndex(o => o.value === _this.lineRefer)
if (referIndex > -1) {
if (referIndex === 1) {
return _this.$t(_this.options2[1].label[0], _this.options2[1].label[1]) + '(' + arr + echartsData[0].unitType + ')'
} else {
return _this.$t(_this.options2[referIndex].label) + '(' + arr + echartsData[0].unitType + ')'
}
}
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
},
position: 'insideStartTop',
@@ -464,8 +478,7 @@ export default {
newData.push(obj)
})
}
if (data !== undefined && data.length > 0) {
if (data && data.length > 0) {
newData.forEach((item) => {
item.type = getLineType(item.type)
if (item.type === val) {
@@ -478,6 +491,24 @@ export default {
})
}
lineData.splice(0, 1)
// TODO 下面的逻辑是判断total曲线的尾部数据从尾往前数0值的个数若个数大于0所有曲线都从尾部去掉相同数量的点最多4个
const totalData = lineData[0]
if (_.get(totalData, 'values', []).length > 4) {
let count = 0
for (let i = totalData.values.length - 1; i >= totalData.values.length - 4; i--) {
if (totalData.values[i].length > 1 && totalData.values[i][1] === 0) {
count++
} else {
break
}
}
if (count > 0) {
lineData.forEach(l => {
l.values.splice(l.values.length - count, count)
})
}
}
if (val === 'Sessions/s') {
const tabs = _.cloneDeep(this.tabsTemplate)
lineData.forEach((d, i) => {
@@ -495,6 +526,7 @@ export default {
})
this.tabs = tabs
this.$nextTick(() => {
this.lineRefer = 'Average'
this.echartsInit(this.tabs, true)
})
} else {
@@ -512,7 +544,7 @@ export default {
const self = this
tabs.forEach(e => {
e.unitType = type
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
if (e.name !== 'network.total' && parseFloat(e.analysis.max) === 0) {
e.show = false
num += 1
} else {
@@ -522,13 +554,18 @@ export default {
}
}
if (self.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) {
if (parseFloat(e.analysis.max) <= 0) {
self.lineTab = ''
self.lineRefer = ''
self.init()
// self.init()
}
}
})
const emptyData = tabs.filter(d => parseFloat(d.analysis.max) === 0)
this.isNoData = emptyData.length === tabs.length
if (this.isNoData) {
return true
}
this.tabs = tabs
if (num === 5) {
tabs[0].invertTab = false

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