Compare commits

..

91 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
111 changed files with 2765 additions and 1626 deletions

View File

@@ -3,3 +3,18 @@ const BASE_CONFIG = {
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
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

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

@@ -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

@@ -194,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;
@@ -222,8 +223,15 @@
}
}
}
.policy-form__footer__btn {
justify-content: center;
margin-top: 8px;
.form-setting__btn1 {
.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;
@@ -130,6 +130,12 @@
}
}
}
.policy-form-trigger {
.el-collapse-item__content {
padding-bottom: 0 !important;
}
}
}
.el-input--mini, .el-input--mini .el-input__inner {
@@ -143,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

@@ -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;
}
}
}
}

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

@@ -149,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;
@@ -172,6 +172,11 @@
color: #666;
}
}
.basic-info__item1 {
span: {
color: #666;
}
}
}
.show-detail {

View File

@@ -203,6 +203,7 @@
color: #046ECA;
margin-bottom: 10px;
font-weight: 500;
height: 36px;
}
.timeline__start-time {
font-size: 12px;

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

@@ -351,6 +351,7 @@
}
.score-dot {
display: inline-block;
margin-bottom: 2px;
width: 6px;
height: 6px;
border-radius: 50%;

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;
@@ -177,6 +199,7 @@
.score-dot {
display: inline-block;
margin-bottom: 2px;
width: 6px;
height: 6px;
border-radius: 50%;

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;
}
}

View File

@@ -1798,6 +1798,12 @@
margin-right:5px;
}
}
.top-tool-btn--update:disabled {
cursor: not-allowed;
opacity: 0.66;
i {
}
}
.top-tool-btn--update:hover {
background-color: #57B8D9 !important;
border-color: #2E88A6 !important;
@@ -1846,6 +1852,12 @@
margin-right:5px;
}
}
.top-tool-btn--update:disabled {
cursor: not-allowed;
opacity: 0.66;
i {
}
}
.top-tool-btn--update:hover {
background-color: #57B8D9 !important;
border-color: #2E88A6 !important;

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "cn-icon"; /* Project id 2614877 */
src: url('iconfont.woff2?t=1698229141457') format('woff2'),
url('iconfont.woff?t=1698229141457') format('woff'),
url('iconfont.ttf?t=1698229141457') 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,10 @@
-moz-osx-font-smoothing: grayscale;
}
.cn-icon-related:before {
content: "\e640";
}
.cn-icon-indicator-match:before {
content: "\e80c";
}

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,6 +635,9 @@ 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 {
@@ -650,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,6 +234,9 @@ 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 {

View File

@@ -1414,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
@@ -289,6 +300,12 @@ export default {
})
}
}
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
let locale = en
if (language === ZH) {
locale = cn
}
return {
myStartTime,
myEndTime,
@@ -297,13 +314,11 @@ export default {
utcStr,
rangeEchartsData,
address,
dateRangeArr,
defaultTime,
dateRangeValue,
isCustom,
newDateValue,
newDatePicker,
showDetail,
rangeHistory,
rangeHistoryArr,
getMillisecond,
@@ -313,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"
@@ -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')
}
@@ -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,39 +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
}
const menu = menus.find(m => m.route === this.route)
if (menu) {
breadcrumb.unshift({
@@ -507,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)
@@ -520,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)
@@ -544,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')
}
@@ -675,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)
@@ -696,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
}
@@ -716,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 => {
@@ -728,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 = ''

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

@@ -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

@@ -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

@@ -11,7 +11,7 @@
<div class="block-mode-title">{{ $t('detection.policy.indicatorMatch') }}</div>
<div class="block-mode-content">
{{ $t('detection.policy.indicatorMatchIntroduce') }}
<div v-if="language==='cn'" style="color: rgba(0,0,0,0)">0</div>
<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)">{{ $t('overall.select') }}
@@ -109,7 +109,7 @@
</template>
<script>
import { detectionRuleType, storageKey, detectionUnitList } from '@/utils/constants'
import { detectionRuleType, storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
import { switchStatus } from '@/utils/tools'
export default {
@@ -160,7 +160,8 @@ export default {
}
]
},
language: 'en'
language: EN,
ZH
}
},
watch: {
@@ -183,7 +184,7 @@ export default {
methods: {
switchStatus,
initData () {
this.language = localStorage.getItem(storageKey.language) || 'en'
this.language = localStorage.getItem(storageKey.language) || EN
this.categoryList = detectionUnitList.categoryList
this.eventTypeList = detectionUnitList.eventTypeList
},

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,10 @@
@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">
@@ -156,7 +163,7 @@ export default {
}, {
label: this.$t('knowledge.reference'),
prop: 'reference',
width: 180,
width: 190,
show: true
}, {
label: this.$t('overall.color'),

View File

@@ -7,6 +7,7 @@
<div class="card-content">
<div class="card-operate">
<el-switch v-model="data.status"
v-if="hasPermission('editBuiltInKnowledgeBase')"
class="card-enable"
active-color="#38ACD2"
inactive-color="#C0CEDB"
@@ -17,15 +18,15 @@
</el-switch>
</div>
<div class="card-icon">
<img :src="data.iconUrl" style="max-height: 50px;"/>
<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>
@@ -44,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>
@@ -77,7 +78,7 @@
<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-switch v-model="updateKnowledge.status"
active-color="#38ACD2"
@@ -85,11 +86,11 @@
:active-value="1"
:inactive-value="0"
:before-change="(knowledgeId) => confirmSwitchLearning(updateKnowledge.knowledgeId)"
v-if="updateKnowledge.source === 'cn_psiphon3_ip'"
v-if="updateKnowledge.source === 'cn_psiphon3_ip' && hasPermission('editBuiltInKnowledgeBase')"
>
</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">
@@ -111,7 +112,7 @@
</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>
@@ -123,7 +124,7 @@
</div>
<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>
@@ -150,7 +151,7 @@
<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="activeTab === 'updateRecord'">
<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>
@@ -256,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>
@@ -283,8 +284,8 @@
<div class="dialog-message">{{ confirmSwitchLearningTip }}</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showConfirmSwitch = false">{{ $t('overall.cancel') }}</el-button>
<el-button type="primary" @click="switchLearning">OK</el-button>
<el-button @click="cancleSwitch">{{ $t('overall.cancel') }}</el-button>
<el-button type="primary" @click="switchLearning">{{$t('tip.confirm')}}</el-button>
</span>
</template>
</el-dialog>
@@ -295,7 +296,7 @@
import table from '@/mixins/table'
import Loading from '@/components/common/Loading'
import { getSecond, getMillisecond, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo } from '@/utils/constants'
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'
@@ -339,6 +340,7 @@ export default {
psiphon3Loading: false,
updateLogLoading: false,
showConfirmSwitch: false,
// timer: null,
switchKnowledgeId: '',
activeTab: 'updateRecord',
isNoDataForPsiphon3: false,
@@ -348,6 +350,7 @@ export default {
tabType: 'total',
mousemoveCursor: '',
selectTime: 1440,
// hasUpdatingRecord: false,
tabs: [
{
name: 'knowledgeBase.total',
@@ -380,12 +383,6 @@ export default {
setup () {
// 没上传过文件的提示
const uploadErrorTip = ref('')
const nowMill = window.$dayJs.tz().valueOf()
const timeFilter = ref({
startTime: nowMill - 1000 * 60 * 60 * 24,
endTime: nowMill,
dateRangeValue: 1440
})
return {
baseUrl: BASE_CONFIG.baseUrl,
apiVersion: BASE_CONFIG.apiVersion,
@@ -397,8 +394,7 @@ export default {
fileList: ref([]),
uploadFileSizeLimit: 1024 * 1024 * 1024,
myChart: shallowRef(null),
chartOption: shallowRef(null),
timeFilter
chartOption: shallowRef(null)
}
},
methods: {
@@ -427,7 +423,6 @@ export default {
},
xAxis: {
type: 'time',
boundaryGap: ['1%', '3%'],
axisLine: {
show: false
},
@@ -471,9 +466,10 @@ export default {
},
init (val, show, active, n) {
this.psiphon3Loading = true
const endTime = window.$dayJs.tz().valueOf()
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
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 => {
@@ -486,7 +482,9 @@ export default {
const chartsData = res.data.result.map(item => {
return [getMillisecond(item.statTime), item.count]
})
this.echartsInit(chartsData)
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.echartsInit(chartsData)
}
}
} else {
this.httpError(res)
@@ -534,14 +532,14 @@ export default {
})
},
timeChange () {
this.timeFilter.endTime = window.$dayJs.tz().valueOf()
this.timeFilter.startTime = this.timeFilter.endTime - this.selectTime * 60 * 1000
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
this.$nextTick(() => {
this.handleActiveBar()
})
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
activeChange (item) { // isClick:代表是通过点击操作来的
if (item) {
@@ -555,9 +553,11 @@ export default {
mouseenterTab (item) {
if (this.isNoDataForPsiphon3) return
this.mousemoveCursor = item.class
this.$nextTick(() => {
this.handleActiveBar()
})
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
mouseleaveTab () {
this.mousemoveCursor = ''
@@ -587,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
this.getCurTabData()
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
/* } else {
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
} */
},
beforeUpload (file) {
this.uploadLoading = true
@@ -609,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)
@@ -659,9 +654,11 @@ export default {
await this.init()
}
this.showUpdate()
this.$nextTick(() => {
this.handleActiveBar()
})
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
uploadRecord () {
this.showAddUpdateDialog = true
@@ -674,12 +671,12 @@ export default {
pageSize: -1
}
if (this.showEnable) {
if (this.activeTab === 'updateRecord') {
if (this.activeTab === knowledgeCardUpdateRecordType.updateRecord) {
params = {
...params,
opUser: -1
}
} else if (this.activeTab === 'intelligenceLearning') {
} else if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
params = {
...params,
opUser: 0
@@ -693,6 +690,15 @@ export default {
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(() => {
@@ -703,7 +709,7 @@ export default {
handleClick (tab) {
this.getCurTabData()
if (tab.index === '1') {
this.init()
this.timeChange()
}
},
clearSelect () {
@@ -743,48 +749,44 @@ export default {
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
if (hint.knowledgeId === 101 || hint.knowledgeId === 102) {
hint.status = toStatus
this.$message.success(this.$t('tip.success'))
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
} else {
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.$nextTick(() => {
this.handleActiveBar()
})
this.timeChange()
},
timeFilter: {
handler () {
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
this.$nextTick(() => {
this.handleActiveBar()
})
/*
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) {
@@ -805,40 +807,6 @@ export default {
}
}
})
// 2024-01-15 以下两个是为105环境演示准备的假数据
const data1 = {
knowledgeId: 101,
name: 'CyberGhost',
category: 'ai_tagging',
description: 'CyberGhost is a VPN service used to unblock sites and browse privately and anonymously.',
isBuiltIn: 1,
isPublished: 1,
status: 1,
showUpdate: false
}
const basicInfo1 = builtInKnowledgeBaseBasicInfo.find(bi => bi.knowledgeId === data1.knowledgeId)
this.aiTaggingList.push({
...data1,
...basicInfo1
})
const data2 = {
knowledgeId: 102,
name: 'HotSpotshield VPN',
category: 'ai_tagging',
description: 'Hotspot Shield is a public VPN service, providing\n' +
'a secure proxy connection through an encrypted\n' +
'channel between your device and the target\n' +
'website, using VPN technology.',
isBuiltIn: 1,
isPublished: 1,
status: 1,
showUpdate: false
}
const basicInfo2 = builtInKnowledgeBaseBasicInfo.find(bi => bi.knowledgeId === data2.knowledgeId)
this.aiTaggingList.push({
...data2,
...basicInfo2
})
}
}
},
@@ -854,9 +822,7 @@ export default {
handler (n) {
if (!n) {
this.fileList = []
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
this.timeChange()
} else {
if (this.myChart) {
this.myChart.dispose()
@@ -883,7 +849,7 @@ export default {
})
},
beforeUnmount () {
clearTimeout(this.timer)
//clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
const dom = document.getElementById('psiphonBarChart')
if (dom) {
@@ -912,9 +878,9 @@ export default {
const find = this.aiTaggingList.find(item => item.knowledgeId === this.switchKnowledgeId)
if (find) {
if (find.status === 0) {
tip = this.$t('tip.confirmEnable') + '?'
tip = this.$t('tip.confirmEnablePsiphon3') + '?'
} else if (find.status === 1) {
tip = this.$t('tip.confirmDisable') + '?'
tip = this.$t('tip.confirmDisablePsiphon3') + '?'
}
}
}

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

@@ -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) {

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

@@ -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,106 +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')
},
{
path: '/detectionPolicy',
component: () => import('@/views/detections/detectionPolicies/Index')
},
{
path: '/detectionPolicy/create',
component: () => import('@/views/detections/detectionPolicies/PolicyForm')
},
{
path: '/detectionPolicy/edit',
component: () => import('@/views/detections/detectionPolicies/PolicyForm')
}
]
}
]

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]

View File

@@ -38,6 +38,7 @@ export const storageKey = {
leftMenuShrink: 'cn-left-menu-shrink',
unsavedChange: 'cn-unsaved-change',
entitySearchHistory: 'cn-entity-search-history',
detectionSearchHistory: 'cn-detection-search-history',
echartLegendFontSize: 'echartLegendFontSize',
echartLabelFontSize: 'echartLabelFontSize',
tokenExpireCurrentPath: 'token-expire-current-path',
@@ -74,6 +75,7 @@ export const panelTypeAndRouteMapping = {
ipEntityDetail: 21,
domainEntityDetail: 22,
appEntityDetail: 23,
subscribeEntityDetail: 24,
cryptocurrency: 7,
ipDrillDownTest: 8,
linkMonitor: 14,
@@ -98,6 +100,11 @@ export const entityType = {
ip: 'IP'
}
export const knowledgeCardUpdateRecordType = {
updateRecord: 'updateRecord',
intelligenceLearning: 'intelligenceLearning'
}
export const entityDetailTabsName = {
informationAggregation: 'informationAggregation',
relatedEntity: 'relatedEntity',
@@ -213,7 +220,7 @@ export const detectionPageType = {
}
export const listScrollPath = [
'/entityExplorer',
'/entity',
'/detection/performanceEvent',
'/detection/securityEvent'
]
@@ -1792,6 +1799,8 @@ export const langData = [
{ value: 'zh', label: 'zh' },
{ value: 'en', label: 'en' }
]
export const ZH = 'zh'
export const EN = 'en'
export const performanceMetricMapping = {
'dns error': 'DNS Error Rate',
@@ -1804,77 +1813,61 @@ export const builtInKnowledgeBaseBasicInfo = [
knowledgeId: 10,
label: 'Psiphon3 VPN',
iconUrl: 'images/knowledge-base-logo/psiphon3-vpn.png',
desc: 'Psiphon3 is a circumvention software for Windows and other platforms that provides uncensored access to Internet content.'
desc: 'knowledgeBase.desc.psiphon3'
},
{
knowledgeId: 5,
label: 'Domain Category',
label: 'network.domainCategory',
iconUrl: 'images/knowledge-base-logo/fqdn.png',
desc: 'Domain category provides basic information including categories, providers, reputation score.'
desc: 'knowledgeBase.desc.domainCategory'
},
{
knowledgeId: 6,
label: 'Domain Whois',
label: 'knowledgeBase.domainWhois',
iconUrl: 'images/knowledge-base-logo/fqdn-whois.png',
desc: 'Domain whois contains registration and ownership information for domain names. It includes details like domain registrar, registrant, creation/expiry dates, and contact information.'
desc: 'knowledgeBase.desc.domainWhois'
},
{
knowledgeId: 2,
label: 'IP ASN',
iconUrl: 'images/knowledge-base-logo/ip-asn.png',
desc: 'ASN Database associates IP addresses with their corresponding Autonomous System Numbers, which are unique identifiers assigned to internet networks. This database helps identify the network and its owner, facilitating network analysis and monitoring tasks.'
desc: 'knowledgeBase.desc.ipAsn'
},
{
knowledgeId: 3,
label: 'DNS Server Info',
label: 'knowledgeBase.dnsServerInfo',
iconUrl: 'images/knowledge-base-logo/dns-server-info.png',
desc: 'A DNS Server Info stores information about Domain Name System (DNS) servers. It includes details such as IP addresses, host names, locations, software name, operation system, and roles of the servers.'
desc: 'knowledgeBase.desc.dnsServer'
},
{
knowledgeId: 9,
label: 'APP Category',
label: 'knowledgeBase.appCategory',
iconUrl: 'images/knowledge-base-logo/app-category.png',
desc: 'APP category provides basic information for over 3000 popular applications, including their categories and service providers.'
desc: 'knowledgeBase.desc.appCategory'
},
{
knowledgeId: 7,
label: 'Indicators of Compromise',
label: 'knowledgeBase.ioc',
iconUrl: 'images/knowledge-base-logo/indicators-of-compromise.png',
desc: 'Indicator of Compromise (IoC) refers to forensic artifacts, such as unusual network traffic or malicious files, indicating a security breach or cyberattack.'
desc: 'knowledgeBase.desc.ioc'
},
{
knowledgeId: 4,
label: 'ICP',
iconUrl: 'images/knowledge-base-logo/icp.png',
desc: 'ICP (Internet Content Provider) license is a permit issued by Chinese authorities, mandatory for websites to legally operate and publish content within mainland China.'
desc: 'knowledgeBase.desc.icp'
},
{
knowledgeId: 1,
label: 'IP Location',
label: 'knowledgeBase.ipLocation',
iconUrl: 'images/knowledge-base-logo/ip-location.png',
desc: 'IP location Database is a repository containing geographical data associated with IP addresses, such as country, city, ISP, organization, latitude, longitude, and other relevant details.'
desc: 'knowledgeBase.desc.ipLocation'
},
{
knowledgeId: 8,
label: 'Anonymity',
label: 'eventType.anonymity',
iconUrl: 'images/knowledge-base-logo/anonymity.png',
desc: 'Communication system that conceals users\' identities and activities to protect privacy and prevent tracking or surveillance. This database provides lists of Tor nodes, I2P nodes, obfs4, etc.'
},
// 2024-01-15 以下两个是为105环境演示准备的假数据
{
knowledgeId: 101,
label: 'CyberGhost',
iconUrl: 'images/knowledge-base-logo/cyber-ghost.png',
desc: 'CyberGhost is a VPN service used to unblock sites and browse privately and anonymously.'
},
{
knowledgeId: 102,
label: 'HotSpotshield VPN',
iconUrl: 'images/knowledge-base-logo/hospot-vpn.png',
desc: 'Hotspot Shield is a public VPN service, providing\n' +
'a secure proxy connection through an encrypted\n' +
'channel between your device and the target\n' +
'website, using VPN technology.'
desc: 'knowledgeBase.desc.anonymity'
}
]

View File

@@ -13,17 +13,11 @@ export function getMillisecond (time) {
ms = window.$dayJs.tz(new Date(time)).valueOf()
} else if (_.isNumber(time)) {
const timeStr = _.toString(time)
/* const difference = timeStr.length - 13
const difference = timeStr.length - 13
if (difference >= 0) {
ms = window.$dayJs.tz(new Date(Number(timeStr.slice(0, 13)))).valueOf()
} else {
ms = window.$dayJs.tz(new Date(Math.floor(time * (10 ** (0 - difference))))).valueOf()
} */
// 判断9位和10位数为秒12位和13位为毫秒。其他位数不做处理
if (timeStr.length === 9 || timeStr.length === 10) {
ms = window.$dayJs.tz(new Date(Number(time * 1000))).valueOf()
} else {
ms = window.$dayJs.tz(new Date(Number(time))).valueOf()
}
} else if (_.isString(time)) {
try {

View File

@@ -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: [
@@ -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: [

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 = {
// 是否需要排序
@@ -1248,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
}
}
@@ -1360,3 +1361,89 @@ export function getTagColor (color) {
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

@@ -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

@@ -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>
@@ -31,7 +31,7 @@
@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>
@@ -148,7 +148,7 @@ export default {
// 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)
@@ -249,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
@@ -292,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
@@ -519,7 +518,7 @@ export default {
},
jumpEntityDetail () {
const { href } = this.$router.resolve({
path: '/entityDetail',
path: '/entity/detail',
query: {
entityType: this.entityType,
entityName: this.entityValue

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

@@ -43,7 +43,7 @@
</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"
@@ -54,7 +54,9 @@
: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>
@@ -255,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',

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>
@@ -90,6 +90,29 @@ export default {
hideTagArea: false
}
},
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 entityTypeName
}
},
methods: {
getTagColor,
tagValueHandler (value) {
@@ -259,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

@@ -124,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 {

View File

@@ -14,13 +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>
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></behavior-pattern>
<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>
@@ -58,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: {
@@ -114,21 +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 })
Promise.all([informationAggregation, openPort]).then(response => {
if (response[0].status === 200) {
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.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,
@@ -151,44 +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)
if (this.entity.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entity.entityName === '218.77.183.150') {
this.initSetTag(entityDetailTabsName.securityEvent, 3)
this.initSetTag(entityDetailTabsName.performanceEvent, 1)
}
})
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 })
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: params })
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)
}
},

View File

@@ -8,8 +8,8 @@
<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" >{{ unitConvert(data.value, unitTypes.number).join('')}}</div>
<div class="legend-percent">{{ unitConvert(data.percent, unitTypes.percent).join('') }}</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>
@@ -20,12 +20,12 @@
</template>
<script>
import { dateFormatByAppearance } from '@/utils/date-util'
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
import * as echarts from 'echarts'
import { pieChartOption4 } from '@/views/charts2/charts/options/echartOption'
import { shallowRef } from 'vue'
import { shallowRef, ref } from 'vue'
import { entityDetailTabsName, chartColorForBehaviorPattern, unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import { valueToRangeValue } from '@/utils/unit-convert'
import axios from 'axios'
import { api } from '@/utils/api'
import { useRoute } from 'vue-router'
@@ -49,13 +49,20 @@ export default {
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
unitTypes,
timeFilter
}
},
async mounted () {
@@ -67,7 +74,7 @@ export default {
}, 200)
},
methods: {
unitConvert,
valueToRangeValue,
toUpperCaseByString,
dateFormatByAppearance,
initEcharts () {
@@ -130,9 +137,7 @@ export default {
})
},
async initData () {
const params = {
resource: this.entityName
}
const params = this.getParams()
this.toggleLoading(true)
await axios.get(`${api.entity.behaviorPattern}`, { params: params }).then(response => {
const res = response.data
@@ -144,22 +149,18 @@ export default {
if (!this.isNoData) {
const data = res.data.result
this.tableData = []
let sum = 0
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) {
sum = sum + value
if (value !== 0 && key !== 'total') {
this.tableData.push({
name: key,
value: value
value: value,
percent: dataObject.total ? value / dataObject.total : '-'
})
}
})
this.tableData.forEach(item => {
item.percent = item.value / sum
})
this.tableData = this.tableData.sort(reverseSortBy('value'))
this.$emit('checkTag', entityDetailTabsName.behaviorPattern, this.tableData.length)
if (this.tableData.length === 0) {

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

@@ -72,8 +72,9 @@ import chartMixin from '@/views/charts2/chart-mixin'
import axios from 'axios'
import { api } from '@/utils/api'
import { entityDetailTabsName, entityDetailTags, tagValueLabelMapping } from '@/utils/constants'
import { dateFormatByAppearance } from '@/utils/date-util'
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: {
@@ -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

@@ -11,7 +11,7 @@
<div class="cn-detection__case entity-detail-performance">
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
<div class="cn-detection__row">
<div class="cn-detection__header" style="padding-bottom: 0">
<div class="cn-detection__header">
<span
:test-id="`severity-color-block${index}`"
class="detection-event-severity-color-block"
@@ -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>
@@ -38,11 +38,6 @@
<span>{{ $t('overall.duration') }}&nbsp;:&nbsp;&nbsp;&nbsp;</span>
<span :test-id="`duration-time${index}`">{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-traffic-overview"></i>
<span>{{ $t('entity.detail.anomaly') }}&nbsp;:&nbsp;&nbsp;&nbsp;</span>
<div id="anomalyChart" style="height: 20px; width: 100px;"></div>
</div>
</div>
</div>
</div>
@@ -55,20 +50,17 @@
</template>
<script>
import { dateFormatByAppearance } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName, unitTypes } 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'
import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
import ChartError from '@/components/common/Error'
import { reverseSortBy, sortBy, toUpperCaseByString } from '@/utils/tools'
import { toUpperCaseByString } from '@/utils/tools'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import { markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions'
import * as echarts from 'echarts'
import _ from 'lodash'
import { ref } from 'vue'
export default {
name: 'PerformanceEvent',
@@ -86,98 +78,56 @@ 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,
chartOption: metricOption
timeFilter
}
},
mounted () {
this.initData()
/*this.isNoData = true
/*
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
if (this.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entityName === '218.77.183.150') {
setTimeout(() => {
this.toggleLoading(false)
this.isNoData = false
this.eventList = [
{
"serverIp": "1.1.1.1",
"domain": "www.baidu.com",
"appName": "ab",
"eventSeverity": "critical",
"eventType": "Http error",
"durationMs": 840000,
"startTime": new Date().getTime() - 1957 * 1000,
"endTime": 2222222222
}
]
this.metricList = [
[new Date().getTime() / 1000 - 2677, 2],
[new Date().getTime() / 1000 - 2557, 3],
[new Date().getTime() / 1000 - 2437, 2],
[new Date().getTime() / 1000 - 2317, 7],
[new Date().getTime() / 1000 - 2197, 8],
[new Date().getTime() / 1000 - 2077, 38],
[new Date().getTime() / 1000 - 1857, 12],
[new Date().getTime() / 1000 - 1637, 8],
[new Date().getTime() / 1000 - 1517, 7],
[new Date().getTime() / 1000 - 1277, 3],
[new Date().getTime() / 1000 - 1157, 1],
[new Date().getTime() / 1000 - 1037, 2]
]
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 1)
this.$nextTick(() => {
this.initChart()
})
}, 200)
} else {
setTimeout(() => {
this.isNoData = true
this.toggleLoading(false)
this.eventList = []
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
}, 200)
/*axios.get(`${api.entity.performance}/${this.entityType}`, {params: params}).then(response => {
const res = response.data
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkTag', entityDetailTabsName.performanceEvent, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.eventList = res.data.result
}
} else {
this.httpError(res)
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkTag', entityDetailTabsName.performanceEvent, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.eventList = res.data.result
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})*/
}
} else {
this.httpError(res)
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})
},
httpError (e) {
this.isNoData = false
@@ -185,18 +135,17 @@ export default {
this.errorMsg = this.errorMsgHandler(e)
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
},
initChart () {
this.metricChart = markRaw(echarts.init(document.getElementById('anomalyChart')))
this.chartOptionMetric = _.cloneDeep(this.chartOption)
this.chartOptionMetric.series[0].data = this.metricList.slice(0, 4).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
this.chartOptionMetric.series[1].data = this.metricList.slice(3, 9).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
this.chartOptionMetric.series[2].data = this.metricList.slice(8, 11).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
this.chartOptionMetric.series.forEach(item => {
item.name = 'Http error'
})
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
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
@@ -25,7 +25,7 @@
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span :test-id="`victim-ip${index}`">{{ item.victimIp || '-' }}</span>
<div class="domain">{{ item.domain }}</div>
<div class="domain">{{ item.victimDomain }}</div>
</div>
<div class="cn-detection__body">
<div class="body__basic-info">
@@ -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>
@@ -58,7 +58,7 @@
<div class="basic-info__item">
<i class="cn-icon cn-icon-time2"></i>
<span>{{ $t('detection.list.startTime') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ dateFormatByAppearance(parseFloat(item.startTime)) || '-' }}</span>
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-duration"></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,135 +103,74 @@ 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.isNoData = true
/*
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)
if (this.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entityName === '218.77.183.150') {
setTimeout(() => {
this.toggleLoading(false)
this.isNoData = false
this.eventList = [
{
eventId: '1717034000326447105',
eventType: 'Command and Control',
eventName: 'Mirai',
eventKey: '5,26.26.26.1,192.168.38.73',
ruleId: '5',
ruleType: 'indicator_match',
isBuiltin: '1',
eventSeverity: 'critical',
offenderIp: '119.102.149.177',
victimIp: '218.77.183.150',
domain: 'hqzc.wssp.hainan.gov.cn',
app: '',
startTime: new Date().getTime() - 3600 * 1000,
endTime: '1698207720',
durationMs: 1613000,
matchTimes: '1',
status: '1',
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
},
{
eventId: '1717034000326447105',
eventType: 'Command and Control',
eventName: 'Bashlite',
eventKey: '5,26.26.26.1,192.168.38.73',
ruleId: '5',
ruleType: 'indicator_match',
isBuiltin: '1',
eventSeverity: 'critical',
offenderIp: '142.4.196.195',
victimIp: '218.77.183.150',
domain: 'hqzc.wssp.hainan.gov.cn',
app: '',
startTime: new Date().getTime() - 1600 * 1000,
endTime: '1698207720',
durationMs: 1285000,
matchTimes: '1',
status: '1',
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
},
{
eventId: '1717034000326447105',
eventType: 'Command and Control',
eventName: 'Mirai',
eventKey: '5,26.26.26.1,192.168.38.73',
ruleId: '5',
ruleType: 'indicator_match',
isBuiltin: '1',
eventSeverity: 'critical',
offenderIp: '103.119.112.54',
victimIp: '218.77.183.150',
domain: 'hqzc.wssp.hainan.gov.cn',
app: '',
startTime: new Date().getTime() - 2600 * 1000,
endTime: '1698207720',
durationMs: 2280000,
matchTimes: '1',
status: '1',
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
}
]
this.$emit('checkTag', entityDetailTabsName.securityEvent, 3)
}, 200)
} else {
setTimeout(() => {
this.isNoData = true
this.toggleLoading(false)
this.eventList = []
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
}, 200)
/*axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
const res = response.data
axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
const res = response.data
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkTag', entityDetailTabsName.securityEvent, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.eventList = res.data.result
}
} else {
this.httpError(res)
if (response.status === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkTag', entityDetailTabsName.securityEvent, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.eventList = res.data.result
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})*/
}
} else {
this.httpError(res)
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})
},
httpError (e) {
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
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

@@ -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'
@@ -156,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
}
@@ -178,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
@@ -188,9 +186,9 @@ export default {
// 链路下一跳数据
let nextGridData = []
const nextGridTemplate = [
{ linkId: 'Hundredgige2', nextHop: 'City2', out: [] },
{ linkId: 'Hundredgige1', nextHop: 'City1', out: [] },
{ linkId: 'Hundredgige4', nextHop: 'City3', out: [] }
{ linkId: 'Hundredgige2', nextHop: '太原', out: [] },
{ linkId: 'Hundredgige1', nextHop: '西安', out: [] },
{ linkId: 'Hundredgige4', nextHop: '西宁', out: [] }
]
nextGridData = JSON.parse(JSON.stringify(nextGridTemplate))
nextGridData.forEach(link => {
@@ -257,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
@@ -357,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) {
@@ -380,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,7 +37,7 @@
</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"

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,7 +32,7 @@
</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"
@@ -43,7 +43,9 @@
: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>
@@ -274,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',

View File

@@ -289,7 +289,9 @@ import {
dbDrilldownTableConfig,
fromRoute,
drillDownPanelTypeMapping,
commonErrorTip
commonErrorTip,
ZH,
EN
} from '@/utils/constants'
import axios from 'axios'
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
@@ -501,7 +503,7 @@ export default {
const currentValue = document.getElementById('tabSearchValue' + tabProp) ? document.getElementById('tabSearchValue' + tabProp).value : ''
const columnName = curTab ? curTab.label : ''
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)
@@ -534,7 +536,7 @@ export default {
axios.get(url, { params }).then(async response => {
if (response.status === 200) {
this.tabSearchColumnValueListShow = 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')
}
@@ -1170,10 +1172,10 @@ export default {
// 数字可按照排序的要求进行自定义,我这边产品的要求是
// 数字0->9->大写字母A->Z->小写字母a->z->中文拼音a->z
if (/^[\u4e00-\u9fa5]$/.test(char)) {
return ['zh', 300]
return [ZH, 300]
}
if (/^[a-zA-Z]$/.test(char)) {
return ['en', 200]
return [EN, 200]
}
if (/^[0-9]$/.test(char)) {
return ['number', 100]
@@ -1222,9 +1224,9 @@ export default {
if (char1 === char2) {
continue
} else {
if (char1Type[0] === 'zh') {
if (char1Type[0] === ZH) {
res = char2.localeCompare(char1)
} else if (char1Type[0] === 'en') {
} else if (char1Type[0] === EN) {
res = char2.charCodeAt(0) - char1.charCodeAt(0)
} else {
res = char2 - char1
@@ -1262,9 +1264,9 @@ export default {
if (char1 === char2) {
continue
} else {
if (char1Type[0] === 'zh') {
if (char1Type[0] === ZH) {
res = char1.localeCompare(char2)
} else if (char1Type[0] === 'en') {
} else if (char1Type[0] === EN) {
res = char1.charCodeAt(0) - char2.charCodeAt(0)
} else {
res = char1 - char2
@@ -2260,10 +2262,9 @@ export default {
this.drillDownTableConfigs = null
this.curTable = null
this.commonColumnList = null
this.userId = localStorage.getItem(storageKey.userId)
this.drillDownTableConfigs = await combineDrilldownTableWithUserConfig()
this.tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
this.tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
// 是否需要dns的qtype和rcode的数据字典
if (this.tableType === fromRoute.dnsServiceInsights) {
this.dnsQtypeMapData = this.$store.getters.getDnsQtypeMapData
@@ -2345,8 +2346,6 @@ export default {
await this.saveUserLocalConfig()
this.getChartData()
},
setup (props) {
},
beforeUnmount () {
// 以下元素,检测到内存并未释放
this.unitConvert = null

View File

@@ -322,7 +322,7 @@ export default {
const tabType = 'network.applicationCategories'
const oldCurTab = this.getUrlParam(this.curTabState.networkOverviewBeforeTab, '')
const curTable = networkTable.networkAppPerformance
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)

View File

@@ -150,7 +150,7 @@ export default {
limit: 10,
type: this.metric
}
/*axios.get(api.npm.events.dimensionEvents, { params }).then(res => {
/* axios.get(api.npm.events.dimensionEvents, { params }).then(res => {
if (res.status === 200) {
this.showError = false
if (!res.data.data.result || res.data.data.result.length === 0) {
@@ -168,7 +168,7 @@ export default {
this.errorMsg = this.errorMsgHandler(e)
}).finally(() => {
this.toggleLoading(false)
})*/
}) */
this.isNoData = true
this.toggleLoading(false)
},

View File

@@ -110,7 +110,7 @@ export default {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
/*this.toggleLoading(true)
/* this.toggleLoading(true)
axios.get(api.npm.events.recentEvents, { params: params }).then(response => {
const res = response.data
if (response.status === 200) {
@@ -151,7 +151,7 @@ export default {
this.errorMsg = this.errorMsgHandler(e)
}).finally(() => {
this.toggleLoading(false)
})*/
}) */
this.isNoData = true
this.toggleLoading(false)
},

View File

@@ -12,15 +12,15 @@
placeholder=" "
:popper-append-to-body="false"
>
<el-option value="Server">Server</el-option>
<el-option value="Client">Client</el-option>
<el-option value="Server" :label="$t('overall.server')">{{$t('overall.server')}}</el-option>
<el-option value="Client" :label="$t('overall.client')">{{$t('overall.client')}}</el-option>
</el-select>
<el-select
size="mini"
v-model="location"
class="map-select map-select__location"
clearable
placeholder="All"
:placeholder="$t('overall.country')"
filterable
popper-class="map-select-down"
:popper-append-to-body="false"

View File

@@ -21,7 +21,7 @@
<template #default="scope" :column="item">
<div class="data-recent-table">
<template v-if="item.prop === 'eventSeverity'">
<span class="data-recent-table-severity" :class="scope.row[item.prop]" :test-id="`eventSeverity-${scope.row.eventSeverity}-${scope.$index}`">{{scope.row[item.prop]}}</span>
<span class="data-recent-table-severity" :class="scope.row[item.prop]" :test-id="`eventSeverity-${scope.row.eventSeverity}-${scope.$index}`">{{ getEventSeverity(scope.row[item.prop]) }}</span>
</template>
<template v-else-if="item.prop === 'eventKey'">
<span class="data-recent-table-entity click-active" @click="jumpPage(scope.row)" :test-id="`eventKey-${splitEventKey(scope.row.eventKey)}-${scope.$index}`">{{splitEventKey(scope.row[item.prop])}}</span>
@@ -57,6 +57,7 @@ import ChartError from '@/components/common/Error'
import axios from 'axios'
import { dataForNpmRecentEvents } from '@/utils/static-data'
import { beforeRouterPush } from '@/utils/tools'
import { securityLevel } from '@/utils/constants'
export default {
name: 'NpmRecentEvents',
@@ -158,6 +159,14 @@ export default {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(res)
},
getEventSeverity (severity) {
const obj = securityLevel.find(d => d.value === severity)
if (obj) {
return this.$t(obj.label)
} else {
return severity
}
}
},
mounted () {

View File

@@ -3,7 +3,7 @@
<div class="npm-traffic-line-header">
<div class="npm-traffic-line-title"></div>
<div class="line-select-metric">
<span>{{$t('network.metric')}}:</span>
<span>{{$t('detections.metric')}}:</span>
<div class="line-select__operation">
<el-select
size="mini"

View File

@@ -257,7 +257,8 @@ export const pieChartOption4 = {
show: false,
position: 'middle',
formatter: '{b}: {c}'
}
},
barMinHeight: 5
}],
animation: false
}

View File

@@ -1,31 +1,36 @@
<template>
<div class="detection-filter-case">
<div class="new-detection-filter-title">{{$t('detections.filters')}}</div>
<div class="filter-case__header">{{$t('detections.filters')}}</div>
<div class="no-data" v-if="isNoData">{{ $t('npm.noData') }}</div>
<template v-for="(filter, index) in filterData" :key="index">
<div class="detection-filter" v-show="filter.data.length > 0">
<div class="filter__header" @click="filter.collapse = !filter.collapse">
<span class="new-detection-filter-header-title">{{filter.title}}</span>
<i class="el-icon-arrow-right new-detection-filter-icon" :class="{ 'arrow-rotate': !filter.collapse }"></i>
</div>
<el-collapse-transition>
<div class="filter__body" v-show="!filter.collapse">
<el-checkbox-group v-model="filter.value">
<template v-for="(d, i) in filter.data" :key="i">
<el-checkbox :label="d.value" v-if="!filter.showIndex || filter.showIndex >= i">
<div class="filter__checkbox-label">
<div style="display: flex">
<span class="severity-color-block" v-if="filter.column === 'eventSeverity'" :style="`background-color: ${eventSeverityColor[d.value]}`"></span>
<span :style="filter.column === 'eventSeverity' ? 'padding-left: 10px' : ''">{{d.label}}</span>
</div>
<div v-if="d.count || d.count === 0">{{d.count}}</div>
</div>
</el-checkbox>
</template>
</el-checkbox-group>
<div class="filter__more" v-if="filter.showMore" @click="showMore(filter)">{{$t('overall.showMore')}}</div>
<div class="filter__header">{{filter.title}}</div>
<div class="filter__body" style="position: relative">
<loading :loading="loadingLeft" style="top: -5px;"></loading>
<div class="filter__body-item"
v-for="(data, i) in filter.data.slice(0, filter.showIndex)"
:key="i"
@click="clickFilterItem(data.label, filter.column, index)">
<div class="filter__body-item-left">
<div class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">
<span>{{ data.label }}</span>
</span>
</el-tooltip>
</div>
</div>
<div class="filter__body-item-right">{{ data.count }}</div>
</div>
</el-collapse-transition>
</div>
<div v-show="filter.showMore" @click="showMore(filter)"
:class="filter.showDisabled? 'filter-no-show-more' : 'filter-show-more'">
{{ $t('overall.showMore') }}
</div>
<div class="filter-hr"></div>
</div>
</template>
</div>
@@ -42,13 +47,36 @@ export default {
},
data () {
return {
eventSeverityColor
eventSeverityColor,
disabledLabel: true
}
},
methods: {
showMore (filter) {
filter.showIndex && (filter.showIndex += 10)
filter.showIndex >= (filter.data.length - 1) && (filter.showMore = false)
filter.showIndex && (filter.showIndex += 5)
filter.showIndex >= (filter.data.length - 1) && (filter.showDisabled = true)
},
clickFilterItem (name, data, index) {
if (index === 0) {
let status = 0
if (name === this.$t('detections.active')) {
status = '0'
} else if (name === this.$t('detections.ended')) {
status = '1'
}
this.$emit('filter', status, data)
} else {
this.$emit('filter', name, data)
}
},
handleMouse (id) {
const dom = document.getElementById(id)
if (dom) {
const width = document.getElementById(id).offsetWidth
this.disabledLabel = width < 180
} else {
this.disabledLabel = true
}
}
},
computed: {

View File

@@ -1,7 +1,7 @@
<template>
<div class="explorer-search explorer-search--show-list">
<div class="explorer-search__input-case explorer-search__input-case--question-mark-in-line">
<div class="explorer-search__input">
<div class="explorer-search__input-case explorer-search__input-case--question-mark-in-line" style="position: relative">
<div class="explorer-search__input entity__search">
<advanced-search
ref="search"
:column-list="columnList[pageType]"
@@ -13,6 +13,36 @@
@search="search"
></advanced-search>
</div>
<div class="explorer-search__foot-list" v-ele-click-outside="esc">
<div class="explorer-search__block" @click="triggerHistory">
<i class="cn-icon cn-icon-time"></i>
<div class="search-dividing-line"></div>
</div>
<transition name="el-zoom-in-top">
<div class="search__history" v-if="showHistory">
<div class="history__items-new">
<el-table
:data="history"
scrollbar-always-on="false"
@row-click="selectHistory"
style="overflow-x: unset"
empty-text=" "
>
<el-table-column prop="str" :label="$t('overall.expression')" min-width="560" />
<el-table-column prop="date" :label="$t('overall.time')" sortable width="200">
<template #default="scope">
<span>{{ changeTimeByDate(scope.row.date) }}</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="clear-all">
<span @click="clearHistory" v-if="!$_.isEmpty(history)">{{$t('entity.clearAll')}}</span>
<div v-else>{{$t('overall.noRecords')}}</div>
</div>
</div>
</transition>
</div>
<!-- <div class="search-symbol-inline">-->
<!-- <i class="cn-icon cn-icon-help"></i>-->
<!-- </div>-->
@@ -25,6 +55,9 @@ import AdvancedSearch from '@/components/advancedSearch/Index'
import { schemaDetectionSecurity } from '@/utils/static-data'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import { storageKey } from '@/utils/constants'
import _ from 'lodash'
import { changeTimeByDate } from '@/utils/tools'
export default {
name: 'DetectionSearch',
props: {
@@ -95,7 +128,9 @@ export default {
label: 'OR'
}
],
showList: true
showList: true,
showHistory: false,
history: []
}
},
emits: ['search'],
@@ -122,70 +157,66 @@ export default {
}
this.$emit('search', metaList, sql)
}, */
search ({ q, metaList }) {
changeTimeByDate,
search ({ str, q, metaList }) {
if (str) {
// 加入搜索记录将记录数量控制在30以内
const oldHistory = localStorage.getItem(storageKey.detectionSearchHistory)
let arr = []
const newItem = { str, date: this.dateFormatByAppearance(new Date()) }
if (!_.isEmpty(oldHistory)) {
const oldArr = JSON.parse(oldHistory)
if (str === oldArr[0].str) {
oldArr[0].date = this.dateFormatByAppearance(new Date())
} else {
oldArr.unshift(newItem)
}
arr = [...oldArr]
if (arr.length > 30) {
arr = arr.slice(0, 30)
}
} else {
arr.push(newItem)
}
localStorage.setItem(storageKey.detectionSearchHistory, JSON.stringify(arr))
}
this.$emit('search', { q, metaList })
},
changeParams (params) { // params: { column: columnName, oldValue: [...], newValue: [...] }
// 向下传递时需要再转换一次param格式为[{column, operator, value}, ...]
if (params.oldValue.length === 0 && params.newValue.length === 1) {
// 1.参数值数量从0到1直接addParams
const p = {
column: params.column,
operator: '=',
value: params.newValue
}
this.$refs.search.addParams([p])
} else if (params.oldValue.length === 1 && params.newValue.length === 0) {
// 2.参数值数量从1到0直接removeParams
const p = {
column: params.column,
operator: '=',
value: params.oldValue
}
this.$refs.search.removeParams([p])
} else if (params.oldValue.length === 2 && params.newValue.length === 1) {
// 3.参数值数量从多到1operator由'in'改为'='
const oldParam = {
column: params.column,
operator: 'IN',
value: params.oldValue
}
const newParam = {
column: params.column,
operator: '=',
value: params.newValue
}
this.$refs.search.changeParams([{ newParam, oldParam }])
} else if (params.oldValue.length === 1 && params.newValue.length === 2) {
// 4.参数值数量从1到多, operator由'='改为'in'
const oldParam = {
column: params.column,
operator: '=',
value: params.oldValue
}
const newParam = {
column: params.column,
operator: 'IN',
value: params.newValue
}
this.$refs.search.changeParams([{ newParam, oldParam }])
} else {
// 5.参数值数量从多到多加1或者减1
const oldParam = {
column: params.column,
operator: 'IN',
value: params.oldValue
}
const newParam = {
column: params.column,
operator: 'IN',
value: params.newValue
}
this.$refs.search.changeParams([{ newParam, oldParam }])
}
changeParams (params) {
this.$refs.search.addParams(params)
},
selectHistory (row) {
this.$refs.search.setStr(row.str)
this.showHistory = false
this.$nextTick(() => {
this.emitter.emit('advanced-search')
if (this.$refs.search.$refs.textMode) {
this.$refs.search.$refs.textMode.focus()
}
})
if (this.showList) {
this.$nextTick(() => {
this.emitter.emit('advanced-search')
})
}
},
clearHistory () {
localStorage.setItem(storageKey.detectionSearchHistory, '')
this.history = []
},
triggerHistory () {
this.showHistory = !this.showHistory
if (this.showHistory) {
const history = localStorage.getItem(storageKey.detectionSearchHistory)
if (!_.isEmpty(history)) {
this.history = JSON.parse(history)
}
}
},
esc () {
const timer = setTimeout(() => {
this.showHistory = false
clearTimeout(timer)
}, 100)
}
}
}

View File

@@ -4,7 +4,7 @@
<div class="explorer-top-tools explorer-detection-top-tools">
<div class="explorer-top-tools-title">{{$t('overall.detections')}}</div>
<div style="display: flex">
<div class="explorer-top-tools-block" @click="jumpNewDetetion">
<div class="explorer-top-tools-block" @click="jumpNewDetetion" v-if="hasPermission('detectionPolicy')">
<i class="cn-icon cn-icon-configure-policies detection-icon-setting"></i>
<span>{{$t('config.detections.configurePolicies')}}</span>
</div>
@@ -21,13 +21,12 @@
:end-time="timeFilter.endTime"/>
</div>
</div>
<div style="width: 100%;padding-bottom: 47px;">
<div style="width: 100%; padding-bottom: 50px;">
<chart-tabs :data="tabsData" router></chart-tabs>
</div>
<!-- 搜索组件 -->
<detection-search
class="detection-border"
ref="search"
:page-type="pageType"
@search="search"
@@ -48,6 +47,7 @@
:filter-data="filterData[pageType]"
:q="q"
:time-filter="timeFilter"
@filter="getFilter"
></detection-filter>
<div class="detection__list">
@@ -138,6 +138,8 @@ import { useRoute } from 'vue-router'
import Loading from '@/components/common/Loading'
import ChartTabs from '@/components/common/ChartTabs'
import { useStore } from 'vuex'
import { tooLongFormatter } from '@/views/charts/charts/tools'
import { format } from 'echarts'
export default {
name: 'Index',
@@ -172,12 +174,12 @@ export default {
// }
],
chartInit: [],
pageObj: {
pageNo: 1,
pageSize: defaultPageSize,
total: 0,
resetPageNo: true
},
// pageObj: {
// pageNo: 1,
// pageSize: defaultPageSize,
// total: 0,
// resetPageNo: true
// },
q: '',
detectionPageType,
filterData: {
@@ -188,6 +190,7 @@ export default {
topColumn: 'status',
collapse: false,
value: [], // value之间是or的关系
showMore: false,
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
},
{
@@ -196,6 +199,7 @@ export default {
topColumn: 'severity',
collapse: false,
value: [], // value之间是or的关系
showMore: false,
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
},
{
@@ -204,6 +208,9 @@ export default {
topColumn: 'event_type',
collapse: false,
value: [],
showMore: true,
showDisabled: true,
showIndex: 5, // index作为showMore分割从1开始而非0
data: [] // 从接口动态获取
},
{
@@ -213,43 +220,21 @@ export default {
collapse: false,
value: [],
showMore: true,
showIndex: 9,
showDisabled: true,
showIndex: 5,
data: [] // 从接口动态获取
},
// {
// title: this.$t('detections.victimLocation'),
// column: 'victimLocationCountry',
// collapse: false,
// value: [],
// showMore: false,
// showIndex: 9,
// data: [
// {
// label: 'China',
// value: 'china',
// count: 50
// }
// ] // 从接口动态获取
// },
{
title: this.$t('detections.offenderIp'),
column: 'offenderIP',
topColumn: 'offender_ip',
collapse: false,
value: [],
showMore: false,
showIndex: 9,
showMore: true,
showDisabled: true,
showIndex: 5,
data: [] // 从接口动态获取
}
// {
// title: this.$t('detections.offenderLocation'),
// column: 'offenderLocationCountry',
// collapse: false,
// value: [],
// showMore: false,
// showIndex: 9,
// data: [] // 从接口动态获取
// }
],
performanceEvent: [
{
@@ -286,7 +271,8 @@ export default {
isStatisticsActiveAttackNoData: false,
loading: false,
oldActiveEntitySearchValue: '',
initFlag: true // 初始化标识初始化时保证mounted执行
initFlag: true, // 初始化标识初始化时保证mounted执行
detectionChart:shallowRef(null)
}
},
methods: {
@@ -294,6 +280,11 @@ export default {
axios.get(api.detection[this.pageType].statusStatistics, { params }).then(res => {
if (res.status === 200) {
const data = res.data.data.result
if (data && data.length > 0) {
data.sort((a, b) => {
return Number(b.count) - Number(a.count)
})
}
this.filterData[this.pageType][0].data = data.map(r => {
let label = ''
if (r.status === '0') {
@@ -388,6 +379,11 @@ export default {
initEventSeverityData (params) {
axios.get(api.detection[this.pageType].severityStatistics, { params }).then(res => {
const data = res.data.data.result
if (data && data.length > 0) {
data.sort((a, b) => {
return Number(b.count) - Number(a.count)
})
}
this.statisticsSeverityData = data
if (!this.$_.isEmpty(data)) {
this.filterData[this.pageType][1].data = data.map(r => ({ label: r.severity, value: r.severity, count: r.count }))
@@ -410,6 +406,7 @@ export default {
if (this.pageType === 'performanceEvent') {
vm.filterData.performanceEvent[0].value = vm.triggerFilterDataValue(vm.filterData.performanceEvent[0].value, e.data.name)
} else if (this.pageType === 'securityEvent') {
this.getFilter(e.data.name, vm.filterData.securityEvent[1].column)
vm.filterData.securityEvent[1].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[1].value, e.data.name)
}
})
@@ -430,6 +427,11 @@ export default {
value: r.eventType,
count: r.count
}))
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][2].data)
this.filterData[this.pageType][2].showMore = showMore
this.filterData[this.pageType][2].showIndex = showIndex
this.filterData[this.pageType][2].showDisabled = showDisabled
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
let detectionChart = echarts.getInstanceByDom(chartDom)
if (detectionChart) {
@@ -441,6 +443,15 @@ export default {
securityTypeOption.series[0].data = data.map(d => {
return { value: d.count, name: d.eventType }
})
if (chartDom) {
let oneColumnWidth = (chartDom.clientWidth * 0.56) - 30
if (data.length > 6) {
oneColumnWidth = (chartDom.clientWidth * 0.56) / 2 - 30
}
securityTypeOption.legend.formatter = function (name) {
return format.truncateText(name, oneColumnWidth, '12px')
}
}
detectionChart.setOption(securityTypeOption)
const vm = this
@@ -452,6 +463,9 @@ export default {
}).catch(e => {
console.error(e)
this.filterData[this.pageType][2].data = []
this.filterData[this.pageType][2].showMore = false
this.filterData[this.pageType][2].showIndex = 5
this.filterData[this.pageType][2].showDisabled = true
this.$message.error(this.errorMsgHandler(e))
})
},
@@ -467,28 +481,45 @@ export default {
count: r.count
}))
this.isCheckFilterByQ(params, 2)
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][2].data)
this.filterData[this.pageType][2].showMore = showMore
this.filterData[this.pageType][2].showIndex = showIndex
this.filterData[this.pageType][2].showDisabled = showDisabled
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
let detectionChart = echarts.getInstanceByDom(chartDom)
if (detectionChart) {
echarts.dispose(detectionChart)
this.detectionChart = echarts.getInstanceByDom(chartDom)
if (this.detectionChart) {
echarts.dispose(this.detectionChart)
}
detectionChart = echarts.init(chartDom)
this.chartInit.push(shallowRef(detectionChart))
this.detectionChart = echarts.init(chartDom)
//this.chartInit.push(shallowRef(detectionChart))
const securityTypeOption = this.$_.cloneDeep(pieForSeverity)
securityTypeOption.series[0].data = data.map(d => {
return { value: d.count, name: d.eventType, itemStyle: { color: getAttackColor(d.eventType) } }
})
detectionChart.setOption(securityTypeOption)
if (chartDom) {
let oneColumnWidth = (chartDom.clientWidth * 0.56) - 30
if (data.length > 6) {
oneColumnWidth = (chartDom.clientWidth * 0.56) / 2 - 30
}
securityTypeOption.legend.formatter = function (name) {
return format.truncateText(name, oneColumnWidth, '12px')
}
}
this.detectionChart.setOption(securityTypeOption)
const vm = this
detectionChart.off('click')
detectionChart.on('click', e => {
this.detectionChart.off('click')
this.detectionChart.on('click', e => {
this.getFilter(e.data.name, vm.filterData.securityEvent[2].column)
vm.filterData.securityEvent[2].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[2].value, e.data.name)
})
}
}).catch(e => {
console.error(e)
this.filterData[this.pageType][2].data = []
this.filterData[this.pageType][2].showMore = false
this.filterData[this.pageType][2].showIndex = 5
this.filterData[this.pageType][2].showDisabled = true
this.$message.error(this.errorMsgHandler(e))
})
},
@@ -504,9 +535,10 @@ export default {
count: r.count
}))
this.isCheckFilterByQ(params, 4)
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][4].data)
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][4].data)
this.filterData[this.pageType][4].showMore = showMore
this.filterData[this.pageType][4].showIndex = showIndex
this.filterData[this.pageType][4].showDisabled = showDisabled
const chartDom = document.getElementById(`detectionActiveAttacker${this.pageType}`)
let detectionChart = echarts.getInstanceByDom(chartDom)
@@ -526,14 +558,18 @@ export default {
const vm = this
detectionChart.off('click')
detectionChart.on('click', e => {
vm.filterData.securityEvent[4].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[4].value, e.data[1])
if (e.data) {
vm.getFilter(e.data[1], vm.filterData.securityEvent[4].column)
vm.filterData.securityEvent[4].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[4].value, e.data[1])
}
})
}
}).catch(e => {
console.error(e)
this.filterData[this.pageType][4].data = []
this.filterData[this.pageType][4].showMore = false
this.filterData[this.pageType][4].showIndex = 9
this.filterData[this.pageType][4].showIndex = 5
this.filterData[this.pageType][4].showDisabled = true
this.$message.error(this.errorMsgHandler(e))
})
},
@@ -543,14 +579,16 @@ export default {
const data = res.data.data.result
this.filterData[this.pageType][3].data = data.map(r => ({ label: r.victimIp, value: r.victimIp, count: r.count }))
this.isCheckFilterByQ(params, 3)
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][3].data)
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][3].data)
this.filterData[this.pageType][3].showMore = showMore
this.filterData[this.pageType][3].showIndex = showIndex
this.filterData[this.pageType][3].showDisabled = showDisabled
}).catch(e => {
console.error(e)
this.filterData[this.pageType][3].data = []
this.filterData[this.pageType][3].showMore = false
this.filterData[this.pageType][3].showIndex = 9
this.filterData[this.pageType][3].showIndex = 5
this.filterData[this.pageType][3].showDisabled = true
this.$message.error(this.errorMsgHandler(e))
})
},
@@ -631,8 +669,9 @@ export default {
},
computeFilterPage (data) {
return {
showMore: data.length > 10,
showIndex: 9
showMore: data.length > 0,
showDisabled: data.length <= 5,
showIndex: 5
}
},
queryList (q) {
@@ -716,7 +755,7 @@ export default {
this.q = ''
this.metaList = []
}
if (this.pageObj.resetPageNo) {
if (this.pageObj.resetPageNo && !this.initFlag) {
this.pageObj.pageNo = 1
} else {
this.pageObj.resetPageNo = true
@@ -774,11 +813,11 @@ export default {
this.search(this.metaList, this.q)
},
pageNo (val) {
this.pageObj.pageNo = val || 1
this.pageObj.resetPageNo = false
// 初始化时mounted和pageNo都会调用列表接口且pageNo先执行
// 初始化保证mounted执行后续pageNo变动则不影响接口调用
if (!this.initFlag) {
this.pageObj.pageNo = val || 1
this.pageObj.resetPageNo = false
this.search(this.metaList, this.q)
}
},
@@ -803,10 +842,28 @@ export default {
},
resize () {
this.chartInit.forEach(e => { e.value.resize() })
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
this.detectionChart = echarts.getInstanceByDom(chartDom)
if(this.detectionChart){
let securityTypeOption = this.$_.cloneDeep(this.detectionChart.getOption())
echarts.dispose(this.detectionChart)
if (chartDom) {
this.detectionChart = echarts.init(chartDom)
let oneColumnWidth = (chartDom.clientWidth * 0.56) - 30
if (this.statisticsCategoryData.length > 6) {
oneColumnWidth = (chartDom.clientWidth * 0.56) / 2 - 30
}
securityTypeOption.legend[0].formatter = function (name) {
return format.truncateText(name, oneColumnWidth, '12px')
}
this.detectionChart.setOption(securityTypeOption)
}
}
},
jumpNewDetetion () {
this.$router.push({
path: '/detectionPolicy',
path: '/detection/policy',
query: {
t: +new Date()
}
@@ -823,11 +880,34 @@ export default {
if (obj) {
this.filterData[this.pageType][index].value = [obj.value]
this.filterData[this.pageType][index].flag = true
} else {
this.filterData[this.pageType][index].value = []
this.filterData[this.pageType][index].flag = true
}
} else {
this.filterData[this.pageType][index].value = []
this.filterData[this.pageType][index].flag = true
}
},
getFilter (name, topColumn) {
if (topColumn === 'tag') {
const params = {
column: topColumn,
operator: 'has',
value: name
}
this.$refs.search.changeParams([params])
} else {
const params = {
column: topColumn,
operator: '=',
value: name
}
this.$refs.search.changeParams([params])
}
this.$nextTick(() => {
this.emitter.emit('advanced-search')
})
}
},
mounted () {
@@ -846,7 +926,11 @@ export default {
}
}
this.queryFilter(q)
this.initFlag = false
if (this.initFlag) {
this.timer = setTimeout(() => {
this.initFlag = false
}, 1000)
}
this.queryList(q)
this.debounceFunc = this.$_.debounce(this.resize, 300)
window.addEventListener('resize', this.debounceFunc)
@@ -916,78 +1000,6 @@ export default {
},
timeFilter () {
this.search(this.metaList, this.q)
},
'filterData.securityEvent.0.value': {
deep: true,
handler (n, o) {
if (!this.filterData.securityEvent[0].flag) {
this.$refs.search.changeParams({ column: this.filterData.securityEvent[0].column, oldValue: o, newValue: n })
} else {
this.filterData.securityEvent[0].flag = false
}
}
},
'filterData.securityEvent.1.value': {
deep: true,
handler (n, o) {
if (!this.filterData.securityEvent[1].flag) {
this.$refs.search.changeParams({ column: this.filterData.securityEvent[1].column, oldValue: o, newValue: n })
} else {
this.filterData.securityEvent[1].flag = false
}
}
},
'filterData.securityEvent.2.value': {
deep: true,
handler (n, o) {
if (!this.filterData.securityEvent[2].flag) {
this.$refs.search.changeParams({ column: this.filterData.securityEvent[2].column, oldValue: o, newValue: n })
} else {
this.filterData.securityEvent[2].flag = false
}
}
},
'filterData.securityEvent.3.value': {
deep: true,
handler (n, o) {
if (!this.filterData.securityEvent[3].flag) {
this.$refs.search.changeParams({ column: this.filterData.securityEvent[3].column, oldValue: o, newValue: n })
} else {
this.filterData.securityEvent[3].flag = false
}
}
},
'filterData.securityEvent.4.value': {
deep: true,
handler (n, o) {
if (!this.filterData.securityEvent[4].flag) {
this.$refs.search.changeParams({ column: this.filterData.securityEvent[4].column, oldValue: o, newValue: n })
} else {
this.filterData.securityEvent[4].flag = false
}
}
},
'filterData.securityEvent.5.value': {
deep: true,
handler (n, o) {
if (!this.filterData.securityEvent[5].flag) {
this.$refs.search.changeParams({ column: this.filterData.securityEvent[5].column, oldValue: o, newValue: n })
} else {
this.filterData.securityEvent[5].flag = false
}
}
},
'filterData.performanceEvent.0.value': {
deep: true,
handler (n, o) {
this.$refs.search.changeParams({ column: this.filterData.performanceEvent[0].column, oldValue: o, newValue: n })
}
},
'filterData.performanceEvent.1.value': {
deep: true,
handler (n, o) {
this.$refs.search.changeParams({ column: this.filterData.performanceEvent[1].column, oldValue: o, newValue: n })
}
}
},
beforeUnmount () {
@@ -995,11 +1007,10 @@ export default {
},
setup () {
const store = useStore()
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
@@ -1007,15 +1018,16 @@ export default {
const newUrl = urlParamsHandler(window.location.href, useRoute().query, query)
overwriteUrl(newUrl)
}
const pageType = params.typeName
const pageType = path.replace('/detection/', '')
// 获取url携带的range、startTime、endTime
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
// 优先级url > config.js > 默认值。
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.detection || 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
@@ -1025,9 +1037,17 @@ export default {
timeFilter.value.startTime = parseInt(startTimeParam)
timeFilter.value.endTime = parseInt(endTimeParam)
}
const pageObj = ref({
pageNo: query.pageNo ? parseInt(query.pageNo) : 1,
// 是否重置pageNo在执行新搜索时是true
resetPageNo: true,
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize,
total: 0
})
return {
timeFilter,
pageType
pageType,
pageObj
}
}
}

View File

@@ -30,6 +30,7 @@
:isNoData="isNoData"
:custom-table-title="tools.customTableTitle"
:table-data="tableData"
:policy-detail="policyDetail"
:is-selected-status="isSelectedStatus"
:all-count="18"
@selectionChange="selectionChange"
@@ -75,7 +76,7 @@
<div class="detection-drawer">
<el-drawer v-model="showDrawer" :with-header="false">
<detection-drawer :drawer-info="drawerInfo"></detection-drawer>
<detection-drawer :drawer-info="drawerInfo" @edit="onEdit" />
</el-drawer>
</div>
@@ -89,6 +90,7 @@ import { api } from '@/utils/api'
import dataListMixin from '@/mixins/data-list'
import DetectionDrawer from '@/views/detections/detectionPolicies/PolicyDrawer'
import axios from 'axios'
import { useRoute } from 'vue-router'
export default {
name: 'Index',
@@ -118,14 +120,40 @@ export default {
drawerInfo: {},
filterParams: {},
policyTotal: 0,
policyEnabledNum: 0
policyEnabledNum: 0,
policyDetail: {}
}
},
mounted () {
const { query } = useRoute()
if (query.name && query.ruleId) {
this.getPolicyDetail(query.ruleId)
}
},
methods: {
getPolicyDetail (id) {
if (id) {
axios.get(`${api.detection.detail}/${id}`).then(res => {
if (res.status === 200) {
if (!res.data.data) {
throw new Error('No data found, id: ' + this.ruleId)
}
this.policyDetail = res.data.data
this.selectionChange([res.data.data])
this.onRowDoubleClick(res.data.data)
} else {
console.error(res.data)
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
})
}
},
onSearch (keyWord) {
this.filterParams = {
...this.filterParams,
name: keyWord
q: keyWord
}
this.search(this.filterParams, 'detection')
},
@@ -140,7 +168,7 @@ export default {
},
onCreate () {
this.$router.push({
path: '/detectionPolicy/create',
path: '/detection/policy/create',
query: {
t: +new Date()
}
@@ -149,11 +177,12 @@ export default {
onEdit () {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/detectionPolicy/edit',
path: '/detection/policy/edit',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: this.batchDeleteObjs[0].ruleId
id: this.batchDeleteObjs[0].ruleId,
name: this.$route.query.name || ''
}
})
},

View File

@@ -2,7 +2,10 @@
<div class="detection-drawer" style="height: 100vh;overflow: scroll;padding-bottom: 90px">
<div class="drawer-basic">
<div class="drawer-basic-header">
<div class="drawer-basic-id">ID: {{ drawerInfo.ruleId }}</div>
<div class="drawer-basic-id">
<div>ID: {{ drawerInfo.ruleId }}</div>
<i @click="onEdit" class="cn-icon cn-icon-bianji1"></i>
</div>
<div :class="`detection-tag-status${drawerInfo.status}`">
{{ $t(switchStatus(drawerInfo.status)) }}
</div>
@@ -82,7 +85,7 @@
<div class="detection-drawer-collapse" style="margin: 20px 0">
<el-collapse v-model="activeTrigger">
<el-collapse-item :title="$t('detection.create.trigger')" name="trigger">
<div class="drawer-collapse-content" v-if="language==='en'">
<div class="drawer-collapse-content" v-if="language===EN">
<div class="drawer-collapse-trigger">
Triggered when conditions occur at least
<span style="color: #046ECA">
@@ -102,7 +105,7 @@
</div>
</div>
</div>
<div class="drawer-collapse-content" v-if="language==='cn'">
<div class="drawer-collapse-content" v-if="language===ZH">
<div class="drawer-collapse-trigger">
当条件为
<span style="color: #046ECA">
@@ -131,7 +134,7 @@
<script>
import { switchStatus, toUpperCaseByString } from '@/utils/tools'
import { detectionUnitList, eventSeverityColor, securityLevel, storageKey } from '@/utils/constants'
import { detectionUnitList, eventSeverityColor, securityLevel, storageKey, ZH, EN } from '@/utils/constants'
import axios from 'axios'
import { api } from '@/utils/api'
@@ -149,9 +152,11 @@ export default {
detailData: {},
eventSeverityColor,
severityList: [],
language: 'en',
language: EN,
atLeast: 0,
times: 'time'
times: 'time',
ZH,
EN
}
},
watch: {
@@ -166,7 +171,7 @@ export default {
}
},
mounted () {
this.language = localStorage.getItem(storageKey.language) || 'en'
this.language = localStorage.getItem(storageKey.language) || EN
},
methods: {
switchStatus,
@@ -243,6 +248,9 @@ export default {
} else {
return '-'
}
},
onEdit () {
this.$emit('edit')
}
}
}

View File

@@ -6,8 +6,8 @@
{{ ruleId ? $t('detection.editEventPolicies') : $t('detection.createEventPolicies') }}
</div>
<!--第一步General Settings-->
<div class="detection-form-content">
<!--第一步General Settings-->
<div class="detection-form-collapse">
<el-collapse v-model="activeNames">
<el-collapse-item name="1">
@@ -43,7 +43,7 @@
<!--第三步Trigger-->
<div class="detection-form-collapse">
<el-collapse v-model="activeNames">
<el-collapse v-model="activeNames" class="policy-form-trigger">
<el-collapse-item name="3">
<template #title>
<div class="form-collapse-header">
@@ -53,7 +53,7 @@
</template>
<div class="form-collapse-content margin-t-18">
<el-form v-if="language==='en'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
<el-form v-if="language===EN" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
<div class="trigger-block-item margin-b-10">
<div>At least</div>
<el-form-item prop="atLeast">
@@ -94,7 +94,7 @@
</el-form-item>
</div>
</el-form>
<el-form v-if="language==='cn'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
<el-form v-if="language===ZH" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
<div class="trigger-block-item margin-b-10">
<el-form-item prop="interval">
@@ -136,18 +136,20 @@
不活跃将重置计数
</div>
</el-form>
<div class="form-setting__btn1">
<div class="btn1">
<el-button @click="createPolicy('')">{{ $t('overall.save') }}</el-button>
</div>
<el-button @click="createPolicy('enabled')">{{ $t('overall.save') }} & {{ $t('detection.create.enablePolicy') }}</el-button>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
<div class="policy-form__footer">
<div class="policy-form__footer__btn">
<div class="btn1">
<el-button @click="cancel">{{ $t('overall.cancel') }}</el-button>
</div>
<el-button @click="createPolicy">{{ $t('overall.save') }}</el-button>
</div>
</div>
</div>
</template>
@@ -160,7 +162,7 @@ import { useRoute } from 'vue-router'
import { ref } from 'vue'
import { getDurationsTimeByType, getTimeByDurations } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { storageKey, detectionUnitList } from '@/utils/constants'
import { storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
export default {
name: 'DetectionForm',
@@ -257,7 +259,9 @@ export default {
intervalList: [],
editObj: {},
isComplete: true, // 参数完整标识默认完整照顾编辑模式false即不完整
language: 'en'
language: EN,
ZH,
EN
}
},
components: {
@@ -266,10 +270,10 @@ export default {
Loading
},
mounted () {
this.language = localStorage.getItem(storageKey.language) || 'en'
if (this.language === 'en') {
this.language = localStorage.getItem(storageKey.language) || EN
if (this.language === EN) {
this.intervalList = detectionUnitList.intervalList
} else if (this.language === 'cn') {
} else if (this.language === ZH) {
this.intervalList = detectionUnitList.intervalListCN
}
this.getDetailInfo()
@@ -330,7 +334,7 @@ export default {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
this.$router.push({
path: '/detectionPolicy',
path: '/detection/policy',
query: {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
@@ -367,7 +371,7 @@ export default {
}
},
/** 创建policy */
createPolicy (flag) {
createPolicy () {
const settingLen = Object.keys(this.settingObj).length
const ruleLen = Object.keys(this.ruleObj).length
@@ -376,9 +380,6 @@ export default {
if (valid) {
// 最终提交form
const formObj = this.$_.cloneDeep({ ...this.settingObj, ruleConfig: JSON.stringify(this.ruleObj), ruleTrigger: this.triggerObj })
if (flag) {
formObj.status = 1
}
// 将时间转为参数所需如5分钟转为PT5M
formObj.ruleTrigger.resetInterval = getDurationsTimeByType(formObj.ruleTrigger.resetInterval, formObj.ruleTrigger.resetIntervalVal)
formObj.ruleTrigger.interval = getDurationsTimeByType(formObj.ruleTrigger.interval, formObj.ruleTrigger.intervalVal)
@@ -402,7 +403,7 @@ export default {
})
this.$router.push({
path: '/detectionPolicy',
path: '/detection/policy',
query: {
t: +new Date()
}
@@ -418,7 +419,6 @@ export default {
this.myLoading = false
})
} else {
console.log('进来')
this.myLoading = true
axios.put(api.detection.create.create, formObj).then(response => {
if (response.status === 200) {
@@ -428,12 +428,18 @@ export default {
message: this.$t('tip.saveSuccess')
})
const { query } = this.$route
const queryInfo = {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
if (query.name && query.id) {
queryInfo.ruleId = query.id
queryInfo.name = this.settingObj.name
}
this.$router.push({
path: '/detectionPolicy',
query: {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
path: '/detection/policy',
query: queryInfo
})
} else {
console.error(response.data.message)
@@ -491,7 +497,134 @@ export default {
return { flag: false, msg: this.$t('policy.dateTimeRangeSeconds') }
}
}
},
cancel () {
const { query } = this.$route
const queryInfo = {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
}
if (query.name && query.id) {
queryInfo.ruleId = query.id
queryInfo.name = this.settingObj.name
}
this.$refs.form3.validate(valid => {
if (this.settingObj.settingNoContinue || this.ruleObj.settingNoContinue || !valid) {
this.$confirm(this.$t('tip.leavePage'), {
confirmButtonText: this.$t('tip.confirm'),
cancelButtonText: this.$t('overall.cancel'),
message: this.$t('tip.leavePageTips'),
title: this.$t('tip.leavePage'),
type: 'warning',
iconClass: 'width:0px;height:0px;',
customClass: 'del-model'
}).then(() => {
this.$router.push({
path: '/detection/policy',
query: queryInfo
})
}).catch(() => {})
} else {
this.$router.push({
path: '/detection/policy',
query: queryInfo
})
}
})
}
}
}
</script>
<style lang="scss">
.del-model {
display: flex;
flex-direction: column;
padding-bottom: 0 !important;
width: 480px !important;
height: 190px;
.el-message-box__header {
display: flex;
flex-direction: row;
border-bottom: 1px solid #eee;
height: 42px;
background: #F7F7F7;
box-shadow: 0 1px 0 0 rgba(53, 54, 54, 0.08);
padding-left: 20px;
padding-top: 14px;
padding-bottom: 14px;
.el-message-box__headerbtn {
display: flex !important;
flex-direction: row-reverse;
justify-content: center;
align-items: center;
font-size: 10px;
line-height: 10px;
padding-right: 5px !important;
i {
width: 10px;
height: 10px;
}
}
.el-message-box__title {
font-size: 14px !important;
color: #353636;
letter-spacing: 0;
font-weight: 400;
}
}
.el-message-box__content {
height: 96px;
font-size: 14px;
color: #353636;
letter-spacing: 0;
line-height: 22px;
font-weight: 400;
padding-top: 8px;
padding-right: 20px;
padding-left: 20px;
.el-message-box__message {
padding-left: 0 !important;
padding-right: 0 !important;
}
}
.el-message-box__btns {
height: 52px;
border-top: 1px solid #eee;
box-shadow: inset 0 -1px 0 0 rgba(0, 0, 0, 0.07);
padding: 11px 0 12px !important;
.el-button--small {
padding: 8px 21px !important;
line-height: 12px;
font-family: NotoSansHans-Medium !important;
font-size: 12px;
font-weight: 500;
min-height: 28px;
}
.el-button:nth-child(1) {
margin-right: 20px;
width: 80px;
height: 28px;
color: #353636;
}
.el-button:nth-child(2) {
width: 80px;
height: 28px;
margin-right: 20px;
margin-left: 0 !important;
background-color: #2d8cf0;
border-color: #2d8cf0;
}
}
}
</style>

View File

@@ -5,6 +5,7 @@
ref="dataTable"
:data="tableData"
height="100%"
tooltip-effect="light"
border
empty-text=" "
@header-dragend="dragend"
@@ -31,6 +32,7 @@
:sortable="item.sortable"
:width="`${item.width}`"
class="data-column"
:show-overflow-tooltip="['library'].indexOf(item.prop) > -1"
>
<template #header>
<span class="data-column__span">{{ item.label }}</span>
@@ -82,6 +84,7 @@ import { dateFormatByAppearance } from '@/utils/date-util'
import { switchStatus } from '@/utils/tools'
import _ from 'lodash'
import { detectionUnitList } from '@/utils/constants'
import { useRoute } from 'vue-router'
export default {
name: 'DetectionTable',
@@ -89,6 +92,9 @@ export default {
isNoData: {
type: Boolean,
default: false
},
policyDetail: {
type: Object
}
},
mixins: [table],
@@ -146,6 +152,17 @@ export default {
]
}
},
mounted () {
const { query } = useRoute()
// 初始化勾选状态
if (query.name && query.ruleId && this.policyDetail) {
this.selectionChange([this.policyDetail])
const timer = setTimeout(() => {
this.$refs.dataTable.toggleAllSelection([this.policyDetail])
clearTimeout(timer)
}, 400)
}
},
watch: {
tableData: {
immediate: true,

View File

@@ -3,6 +3,7 @@
<div class="top-tools__left">
<button
id="knowledge-base-add"
v-if="hasPermission('createDetectionPolicy')"
:title="$t('knowledgeBase.createKnowledgeBase')"
class="top-tool-btn margin-r-10 top-tool-btn--create"
@click="onCreate"
@@ -13,6 +14,7 @@
<button
:disabled="disableEdit"
v-if="hasPermission('editDetectionPolicy')"
id="knowledge-base-edit"
:title="$t('knowledgeBase.editKnowledgeBase')"
class="top-tool-btn margin-r-10"
@@ -24,6 +26,7 @@
<button
:disabled="disableDelete"
v-if="hasPermission('deleteDetectionPolicy')"
id="knowledge-base-delete"
:title="$t('knowledgeBase.deleteKnowledgeBase')"
class="top-tool-btn margin-r-10"
@@ -47,6 +50,9 @@
</template>
<script>
import { useRoute } from 'vue-router'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
export default {
name: 'DetectionTools',
props: {
@@ -64,9 +70,21 @@ export default {
keyWord: ''
}
},
mounted () {
const { query } = useRoute()
if (query.name) {
this.keyWord = query.name
this.onSearch()
}
},
methods: {
onSearch () {
this.$emit('search', this.keyWord)
if (!this.keyWord) {
const query = this.$route.query
delete query.name
this.reloadUrl(query, 'clear')
}
},
onCreate () {
this.$emit('create')
@@ -76,6 +94,14 @@ export default {
},
onDelete (data) {
this.$emit('delete', data)
},
reloadUrl (newParam, clean) {
const { query } = this.$route
let newUrl = urlParamsHandler(window.location.href, query, newParam)
if (clean) {
newUrl = urlParamsHandler(window.location.href, query, newParam, clean)
}
overwriteUrl(newUrl)
}
}
}

View File

@@ -187,7 +187,7 @@ export const pieForSeverity = {
legend: {
orient: 'vertical',
type: 'plain',
left: '60%',
left: '44%',
top: 'middle',
icon: 'circle',
itemWidth: 10, // 设置宽度
@@ -203,7 +203,7 @@ export const pieForSeverity = {
type: 'pie',
selectedMode: 'single',
radius: ['43%', '65%'],
center: ['30%', '50%'],
center: ['22%', '50%'],
data: [],
label: {
show: false
@@ -308,10 +308,6 @@ export const metricOption = {
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], unitTypes.time).join(' ')}
</span>`
} else if (item.seriesName === 'Http error') {
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], unitTypes.number, '', '', 0).join(' ')}
</span>`
} else {
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], unitTypes.percent, '', '', 0).join(' ')}

View File

@@ -243,7 +243,7 @@ export default {
},
goDetail (type, name) {
const { href } = this.$router.resolve({
path: '/entityDetail',
path: '/entity/detail',
query: {
entityType: type,
entityName: name

View File

@@ -274,7 +274,7 @@ export default {
},
goDetail (type, name) {
const { href } = this.$router.resolve({
path: '/entityDetail',
path: '/entity/detail',
query: {
entityType: type,
entityName: name

View File

@@ -235,7 +235,7 @@ export default {
},
goDetail (type, name) {
const { href } = this.$router.resolve({
path: '/entityDetail',
path: '/entity/detail',
query: {
entityType: type,
entityName: name

View File

@@ -10,7 +10,7 @@
<span class="row__content--link">{{detection.victimIp}}</span>&nbsp;&nbsp;communicated with&nbsp;<span class="row__content--link">{{detection.offenderIp}}</span>&nbsp;&nbsp;that was associated with the indicator of {{detection.eventName}}.
</div>
<div class="row__content1" v-else>
{{basicInfo.ruleDescription || '-'}}
{{ $_.get(basicInfo, 'ruleInfo.description', '-') || '-' }}
</div>
</div>
<div class="overview__title">Fields</div>
@@ -71,7 +71,7 @@
<div class="row__content">{{ $_.get(basicInfo, 'domainInfo.category.categoryGroup', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label">{{ $t('entities.reputationLevel') }}</div>
<div class="row__label">{{ $t('detection.domainReputationLevel') }}</div>
<div class="row__content" v-if="$_.get(basicInfo, 'domainInfo.category.reputationLevel')">
<div
class="row__tag row__tag__level"
@@ -242,6 +242,17 @@
</template>
</div>
<div class="overview__right">
<div v-if="$_.get(basicInfo, 'ruleInfo.ruleId') >= 10000">
<div class="overview__title">{{ $t('detections.goToPolicy') }}</div>
<div class="overview__row">
<div class="row__content">
<span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span> &nbsp;
<span class="row__content--link" @click="goPolicyPage">
{{ $_.get(basicInfo, 'ruleInfo.name', '-') || '-' }} (ID: {{ $_.get(basicInfo, 'ruleInfo.ruleId', '-') || '-' }})
</span>
</div>
</div>
</div>
<div class="overview__title">{{ $t('detections.goToVictim') }}</div>
<div class="overview__row">
<div class="row__content">
@@ -431,10 +442,7 @@ export default {
dateFormatByAppearance,
/** 初始化实体详情 */
initEntityDetail () {
// 为完整填充IP信息攻击者ip、受害者ip都进行调用
// 根据detection的eventInfo对象的ioc_type进行判断若为domainmalware信息从domain详情中获取并填充domain信息
// 若ioc_type为ip则调用ip接口填充malware信息
// 最后调用app填充app信息。经上获取完整实体详情则最少需要调用4次接口
// 调接口查询攻击者和受害者IP、Domain、APP的更多信息
if (this.detection.offenderIp) {
axios.get(`${api.detection.securityEvent.ipDetail}?resource=${this.detection.offenderIp}`).then(res => {
if (res.status === 200) {
@@ -466,7 +474,7 @@ export default {
if (this.detection.ruleId) {
axios.get(`${api.detection.detail}/${this.detection.ruleId}`).then(res => {
if (res.status === 200) {
this.basicInfo.ruleDescription = res.data.data.description
this.basicInfo.ruleInfo = res.data.data
}
})
}
@@ -491,7 +499,7 @@ export default {
goDetail (type, name) {
if (name) {
const { href } = this.$router.resolve({
path: '/entityDetail',
path: '/entity/detail',
query: {
entityType: type,
entityName: name
@@ -499,6 +507,19 @@ export default {
})
window.open(href, '_blank')
}
},
goPolicyPage () {
if (this.basicInfo.ruleInfo.name && Number(this.basicInfo.ruleInfo.ruleId) >= 10000) {
const { href } = this.$router.resolve({
path: '/detection/policy',
query: {
t: +new Date(),
name: this.basicInfo.ruleInfo.name,
ruleId: this.basicInfo.ruleInfo.ruleId
}
})
window.open(href, '_blank')
}
}
},
mounted () {

View File

@@ -39,6 +39,11 @@ export default {
entityData.appName = query.entityName
break
}
case 'subscribe': {
panelType = panelTypeAndRouteMapping.subscribeEntityDetail
entityData.appName = query.entityName
break
}
default: {
panelType = panelTypeAndRouteMapping.ipEntityDetail
break

View File

@@ -4,8 +4,16 @@
:class="{'entity-explorer--show-list': showList}">
<!-- 顶部工具栏在列表页显示 -->
<div class="explorer-top-tools explorer-top-tools-new" style="margin: 2px 0;" v-show="showList">
<div class="explorer-detection-top-tools">
<div class="explorer-entity-top-tools">
<div class="explorer-top-tools-title" style="padding: 0;margin-left: -10px;">{{$t('network.entity')}}</div>
<date-time-range
class="date-time-range"
:start-time="timeFilter.startTime"
:end-time="timeFilter.endTime"
:date-range="timeFilter.dateRangeValue"
ref="dateTimeRange"
@change="reload"
/>
</div>
</div>
<!-- 搜索组件 -->
@@ -36,16 +44,26 @@
<div style="display: flex;flex-direction: column;height: calc(100% - 42px);">
<div class="explorer-result" v-if="showList" style="position: relative;">
<loading :loading="loadingCount" style="width: 240px"></loading>
<span>{{ summaryCount.totalCount }}&nbsp;</span>resultsIP
<span>{{ summaryCount.ipCount }}</span>Domain
<span>{{ summaryCount.totalCount }}&nbsp;</span>{{$t('overall.results')}}IP
<span>{{ summaryCount.ipCount }}</span>{{$t('overall.domain')}}
<span>{{ summaryCount.domainCount }}</span>APP
<span>{{ summaryCount.appCount }}</span>
<span class="entity-hide-entity" v-if="q">
<el-checkbox
v-model="isHideRelatedEntities"
:label="$t('entity.hideRelatedEntities')"
:disabled="listData.length===0"
@change="hideRelatedEntities"
size="large" />
</span>
</div>
<entity-list
style="width: 100%;"
:list-data="listData"
:list-mode="listMode"
:keywordList="keywordList"
:pageObj="pageObj"
:time-filter="timeFilter"
@pageSize="pageSize"
@@ -71,8 +89,7 @@
<div class="right-label">{{ $t('network.total') }}</div>
<div class="right-label-loading">
<loading :loading="loadingApp" size="small"></loading>
<!-- <div class="right-value">{{ numberWithCommas(entityAppTotal) }}</div>-->
<div class="right-value">837</div>
<div class="right-value">{{ numberWithCommas(entityAppTotal) }}</div>
</div>
</div>
@@ -93,7 +110,7 @@
<div class="entity-overview">
<div class="overview-left">
<span class="overview-left-loading">
<span class="overview-left-loading-span">Domain</span>
<span class="overview-left-loading-span">{{$t('overall.domain')}}</span>
</span>
</div>
<div class="overview-right">
@@ -102,8 +119,7 @@
<div class="right-label">{{ $t('network.total') }}</div>
<div class="right-label-loading">
<loading :loading="loadingDomain" size="small"></loading>
<!-- <div class="right-value">{{ numberWithCommas(entityDomainTotal) }}</div>-->
<div class="right-value">1,032,544</div>
<div class="right-value">{{ numberWithCommas(entityDomainTotal) }}</div>
</div>
</div>
@@ -133,8 +149,7 @@
<div class="right-label">{{ $t('network.total') }}</div>
<div class="right-label-loading">
<loading :loading="loadingIp" size="small"></loading>
<!-- <div class="right-value">{{ numberWithCommas(entityIpTotal) }}</div>-->
<div class="right-value">1,900,804</div>
<div class="right-value">{{ numberWithCommas(entityIpTotal) }}</div>
</div>
</div>
@@ -177,6 +192,8 @@ import Parser from '@/components/advancedSearch/meta/parser'
import { handleErrorTip } from '@/components/advancedSearch/meta/error'
import { columnList } from '@/utils/static-data'
import { useRoute } from 'vue-router'
import { columnType } from '@/components/advancedSearch/meta/meta'
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
export default {
name: 'entity-explorer',
@@ -184,7 +201,8 @@ export default {
Loading,
ExplorerSearch,
EntityFilter,
EntityList
EntityList,
DateTimeRange
},
data () {
return {
@@ -203,13 +221,6 @@ export default {
entityIpNew: '-',
entityIpActive: '-',
// pageObj: {
// pageNo: 1,
// // 是否重置pageNo在执行新搜索时是true
// resetPageNo: true,
// pageSize: defaultPageSize,
// total: 0
// },
newFilterData: [
{
icon: 'cn-icon cn-icon-registration-country',
@@ -287,12 +298,13 @@ export default {
initFlag: true, // 初始化标志避免初始化时pageSize和pageNo会调用搜索
timer: null, // 初始化标志的延时器,需要销毁
summaryCount: {
total: 0,
totalCount: 0,
domainCount: 0,
ipCount: 0,
appCount: 0
},
loadingCount: false // 实体基数统计的loading
loadingCount: false, // 实体基数统计的loading
keywordList: []
}
},
methods: {
@@ -305,6 +317,13 @@ export default {
},
reload (s, e, v) {
this.dateTimeRangeChange(s, e, v)
const { startTime, endTime } = getNowTime(this.timeFilter.dateRangeValue)
const newParam = {
range: this.timeFilter.dateRangeValue,
startTime: getSecond(startTime),
endTime: getSecond(endTime)
}
this.reloadUrl(newParam)
},
// methods
dateTimeRangeChange (s, e, v) {
@@ -364,6 +383,7 @@ export default {
this.q = ''
this.metaList = []
}
this.getKeyword(param.keywordList)
// 参数q避免切换页码时地址栏参数q为空
let urlQ = ''
@@ -371,6 +391,8 @@ export default {
urlQ = encodeURI(param.str)
} else if (this.q) {
urlQ = encodeURI(this.q)
} else if (!this.q) {
this.isHideRelatedEntities = false
}
// 在非列表模式下选择tag模式在地址栏输入内容时将mode添加到地址栏
@@ -392,7 +414,7 @@ export default {
if (!this.showList) {
// 首页进入搜索时重载页面,视觉上进入列表页面
this.$router.push({
path: '/entityExplorer',
path: '/entity',
query: {
listMode: this.listMode,
q: urlQ,
@@ -420,19 +442,21 @@ export default {
},
pageSize (val) {
this.pageObj.pageSize = val
const keywordList = this.getKeywordListByMetaList(this.metaList)
if (this.initFlag) {
if (val !== 20) {
this.search({ metaList: this.metaList, q: this.q })
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
}
} else {
this.search({ metaList: this.metaList, q: this.q })
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
}
},
pageNo (val) {
if (!this.initFlag) {
this.pageObj.pageNo = val
this.pageObj.resetPageNo = false
this.search({ metaList: this.metaList, q: this.q })
const keywordList = this.getKeywordListByMetaList(this.metaList)
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
}
},
// 点击上一页箭头
@@ -492,8 +516,8 @@ export default {
/** 新版查询filter数据 */
queryFilterNew (params) {
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime),
resource: params.q || ''
}
this.loadingLeft = true
@@ -551,16 +575,16 @@ export default {
const queryParams = {
pageSize: params.pageSize,
pageNo: params.pageNo,
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || ''
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime),
resource: params.q || '',
hideRelated: this.isHideRelatedEntities
}
axios.get(api.entity.entityList.list, { params: queryParams }).then(response => {
if (response.status === 200) {
this.listData = []
this.$nextTick(() => {
this.listData = response.data.data.list
this.pageObj.total = response.data.data.total
})
} else {
this.$message.error(response.data.message)
@@ -573,19 +597,21 @@ export default {
queryCount (params) {
this.loadingCount = true
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || ''
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime),
resource: params.q || '',
hideRelated: this.isHideRelatedEntities
}
axios.get(api.entity.entityList.summaryCount, { params: queryParams }).then(response => {
if (response.status === 200) {
this.summaryCount = response.data.data
this.pageObj.total = response.data.data.totalCount
} else {
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
this.summaryCount = { totalCount: 0, domainCount: 0, ipCount: 0, appCount: 0 }
}
}).catch(e => {
console.log(e)
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
console.error(e)
this.summaryCount = { totalCount: 0, domainCount: 0, ipCount: 0, appCount: 0 }
}).finally(() => {
this.loadingCount = false
})
@@ -678,11 +704,13 @@ export default {
}
}
const parser = new Parser(columnList)
const metaList = parser.parseStr(_.cloneDeep(str)).metaList
const keywordList = this.getKeywordListByMetaList(metaList)
const keyInfo = parser.comparedEntityKey(parser.handleEntityTypeByStr(str))
if (keyInfo.isKey) {
const errorList = parser.validateStr(keyInfo.key)
if (_.isEmpty(errorList)) {
this.search({ ...parser.parseStr(keyInfo.key), str: str })
this.search({ ...parser.parseStr(keyInfo.key), str: str, keywordList: keywordList })
} else {
this.$message.error(handleErrorTip(errorList[0]))
}
@@ -730,10 +758,55 @@ export default {
}).catch((e) => {
}).finally(() => {
})
},
getKeywordListByMetaList (metaList) {
if (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 })
}
})
return keywordList
}
},
getKeyword (list) {
if (list) {
const metaList = JSON.parse(JSON.stringify(list))
const keyList = []
metaList.forEach(item => {
if (item.value) {
keyList.push({ type: item.type, value: this.getKeyValue(item.value) })
}
})
this.keywordList = keyList
} else {
this.keywordList = []
}
},
getKeyValue (str) {
if (str[0] === "'" && str[str.length - 1] === "'") {
str = str.substring(1, str.length)
str = str.substring(0, str.length - 1)
}
if (str[0] === '%') {
str = str.substring(1, str.length)
}
if (str[str.length - 1] === '%') {
str = str.substring(0, str.length - 1)
}
return str
},
hideRelatedEntities (e) {
this.isHideRelatedEntities = e
this.reloadUrl({ hideRelated: e })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
}
},
mounted () {
this.getEntityIndexData()
let { q, listMode } = this.$route.query
// 如果地址栏有listMode即列表页并非首页则开始搜索
@@ -744,7 +817,13 @@ export default {
q = decodeURI(q)
}
// %位置不为0即内容包含非英文时
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
let str1 = ''
if (q) {
str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
}
if (q && q.indexOf('+') > -1) {
q = q.replace('+', '')
}
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
q = decodeURI(q)
}
@@ -754,6 +833,10 @@ export default {
this.$store.commit('resetScoreBase')
this.queryScoreBase()
}
if (!this.showList) {
this.getEntityIndexData()
}
},
watch: {
timeFilter () {
@@ -766,11 +849,11 @@ 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.list || 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 {
@@ -784,9 +867,11 @@ export default {
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize,
total: 0
})
const isHideRelatedEntities = ref(query.hideRelated ? JSON.parse(query.hideRelated) : false) // 隐藏相关实体默认false不隐藏
return {
timeFilter,
pageObj
pageObj,
isHideRelatedEntities
}
},
beforeUnmount () {

View File

@@ -5,7 +5,7 @@
<div class="graph-detail__icon"><i :class="iconClass"></i></div>
<div class="graph-detail-header">
<div class="entity-graph-type">{{ $_.get(node, 'myData.entityType') ? entityType[$_.get(node, 'myData.entityType')] : '-' }}</div>
<div class="entity-graph-type">{{entityTypeName}}</div>
<div class="graph-basic-info">
<div class="graph-basic-info-name__block">
<div class="graph-basic-info-name" :title="$_.get(node, 'id', '')" id="entityName">{{ $_.get(node, 'id', '') }}</div>
@@ -144,6 +144,27 @@ export default {
}
return className
},
entityTypeName () {
const type = _.get(this.node, 'myData.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 entityTypeName
},
handleDate () {
return function (key) {
const date = _.get(this.node, key, '')

View File

@@ -11,6 +11,7 @@
v-for="(data, index) in listData"
:entity="data"
:listMode="listMode"
:keywordList="keywordList"
:timeFilter="timeFilter"
:key="index"
:ref="`entityRow${index}`"
@@ -65,7 +66,8 @@ export default {
pageObj: Object,
loading: Boolean,
timeFilter: Object,
listMode: String
listMode: String,
keywordList: Array
},
components: {
'entity-card': Card,

View File

@@ -11,8 +11,20 @@
<div class="cn-entity__row">
<!--标签-->
<div class="cn-entity__header" style="display: flex;">
<span class="cn-entity__header-title">{{ entityData.entityValue || 'Unknown' }}</span>
<span class="entity-detail" style="display: flex;margin-left: 6px;margin-top: 1px;flex-wrap: wrap;margin-bottom: -10px;">
<span class="cn-entity__header-title" v-high-light="keywordList">{{ entityData.entityValue || 'Unknown' }}</span>
<span v-show="entityData.isRelated">
<el-popover
popper-class="my-popper-class"
placement="right"
trigger="hover"
:content="$t('entity.relatedEntities')"
>
<template #reference>
<i class="cn-icon cn-icon-related entity-related-entity"></i>
</template>
</el-popover>
</span>
<span class="entity-detail entity-row-tag" :style="{'margin-top' : entityData.isRelated ? '4px':'1px'}">
<span v-for="(item, index) in levelTwoTags"
:key="index"
class="entity-tag entity-tag--small margin-r-10 margin-b-10"
@@ -30,29 +42,29 @@
<div class="basic-info__item">
<i class="cn-icon cn-icon-country"></i>
<span class="row-item-label">{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ $_.get(entityData, 'location.country', '-') || '-' }}</span>
<span class="row-item-value" v-high-light="keywordList">{{ $_.get(entityData, 'location.country', '-') || '-' }}</span>
</div>
<div class="basic-info__item">
<div class="basic-info__item1">
<i class="cn-icon cn-icon-position"></i>
<span class="row-item-label">{{ $t('overall.city') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.location ? ipLocationRegion(entityData.location) : '-' }}</span>
<span class="row-item-value high-location" v-high-light="keywordList">{{ entityData.location ? ipLocationRegion(entityData.location) : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-cloud"></i>
<span class="row-item-label">{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ $_.get(entityData, 'asn.asn', '-') || '-' }}</span>
<span class="row-item-value" v-high-light="keywordList">{{ $_.get(entityData, 'asn.asn', '-') || '-' }}</span>
</div>
</template>
<template v-else-if="entityData.entityType === 'domain'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-category-group"></i>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ $_.get(entityData, 'category.categoryGroup', '-') || '-' }}</span>
<span class="row-item-value" v-high-light="keywordList">{{ $_.get(entityData, 'category.categoryGroup', '-') || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ $_.get(entityData, 'category.categoryName', '-') || '-' }}</span>
<span class="row-item-value" v-high-light="keywordList">{{ $_.get(entityData, 'category.categoryName', '-') || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-credit-rating"></i>
@@ -64,7 +76,7 @@
<div class="basic-info__item">
<i class="cn-icon cn-icon-category2"></i>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ $_.get(entityData, 'category.appCategory', '-') || '-' }}</span>
<span class="row-item-value" v-high-light="keywordList">{{ $_.get(entityData, 'category.appCategory', '-') || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i>
@@ -146,7 +158,7 @@
<template v-if="!loadingNetworkQuality && score !=='-'">
<span v-for="(dot, i) in scoreDot" :key="i" :class="dot.class"></span>
</template>
<span>{{score}}</span>
<span style="padding-left: 4px;">{{score}}</span>
<loading :loading="loadingNetworkQuality" size="small"></loading>
</span>
</div>
@@ -176,7 +188,7 @@
<el-collapse-transition>
<div class="cn-entity__detail-overview" v-if="!isCollapse">
<el-divider></el-divider>
<detail-overview :entity="entityData" :time-filter="timeFilter" @reloadEntity="getEntity" />
<detail-overview :entity="entityData" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity" />
</div>
</el-collapse-transition>
</div>
@@ -199,7 +211,8 @@ export default {
props: {
index: Number,
timeFilter: Object,
listMode: String
listMode: String,
keywordList: Array
},
components: {
Loading,

View File

@@ -8,11 +8,11 @@
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
<div class="row__content">{{$_.get(entity, 'category.appCategory', '-') || '-'}}</div>
<div class="row__content" v-high-light="keywordList">{{ $_.get(entity, 'category.appCategory', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.subcategory')}}</div>
<div class="row__content">{{$_.get(entity, 'category.appSubcategory', '-') || '-'}}</div>
<div class="row__content" v-high-light="keywordList">{{ $_.get(entity, 'category.appSubcategory', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.riskLevel')}}</div>
@@ -20,7 +20,9 @@
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{$_.get(entity, 'category.appDescription', '-') || '-'}}</div>
<div class="row__content">
<span v-high-light="keywordList">{{ $_.get(entity, 'category.appDescription', '-') || '-' }}</span>
</div>
</div>
</div>
</div>
@@ -89,7 +91,7 @@
<loading :loading="loadingRelationshipOne" size="small" style="left: 1rem;"></loading>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<div class="data-item high-light-block" v-high-light="keywordList" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataOne.length===0 && !loadingRelationshipOne">-</div>
@@ -98,7 +100,7 @@
<div class="data-item show-more-related" id="related-app-more" @click.stop="showMoreApp" style="position: relative">...</div>
<div v-if="isShowMoreApp" class="app-popover_block" id="showRelatedApp">
<div class="popover-content" v-for="(item, index) in relationshipDataOne" :key="index">
<span v-if="!item.show">{{item.value}}</span>
<span v-if="!item.show" class="high-light-block" v-high-light="keywordList">{{item.value}}</span>
</div>
</div>
</div>
@@ -113,7 +115,7 @@
<loading :loading="loadingRelationshipTwo" size="small" style="left: 1rem;"></loading>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<div class="data-item high-light-block" v-high-light="keywordList" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataTwo.length===0 && !loadingRelationshipTwo">-</div>
@@ -122,7 +124,7 @@
<div class="data-item show-more-related" id="related-domain-more" @click.stop="showMoreDomain" style="position: relative">...</div>
<div v-if="isShowMoreDomain" class="domain-popover_block" id="showRelatedDomain">
<div v-for="(item, index) in relationshipDataTwo" :key="index">
<span v-if="!item.show">{{item.value}}</span>
<span v-if="!item.show" class="high-light-block" v-high-light="keywordList">{{item.value}}</span>
</div>
</div>
</div>
@@ -170,7 +172,7 @@
</div>
<div class="overview__row overview__row--small-font" v-for="(security, index) in entityData.securityList" :key="index">
<div class="row__label row__label--width130">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__label row__label--width130">{{security.startTime ? dateFormatByAppearance(Number(security.startTime)) : '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
</div>
@@ -214,7 +216,7 @@ import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import axios from 'axios'
import relatedServer from '@/mixins/relatedServer'
import { dateFormatByAppearance, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond, getNowTime } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
@@ -225,6 +227,9 @@ export default {
Chart,
Loading
},
props: {
keywordList: Array
},
data () {
return {
// entityData: {}
@@ -315,9 +320,21 @@ export default {
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
return {
resource: this.entity.entityValue
getQueryParams (dateRangeValue) {
if (dateRangeValue) {
// range取 config.js 中配置的值
const { startTime, endTime } = getNowTime(dateRangeValue)
return {
startTime: getSecond(startTime),
endTime: getSecond(endTime),
resource: this.entity.entityValue
}
} else {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
resource: this.entity.entityValue
}
}
},
getPerformanceQueryParams () {

View File

@@ -1,13 +1,13 @@
<template>
<div class="entity-detail-overview">
<template v-if="entity.entityType === 'ip'">
<ip-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></ip-overview>
<ip-overview :entity="entity" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity"></ip-overview>
</template>
<template v-else-if="entity.entityType === 'domain'">
<domain-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></domain-overview>
<domain-overview :entity="entity" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity"></domain-overview>
</template>
<template v-else-if="entity.entityType === 'app'">
<app-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></app-overview>
<app-overview :entity="entity" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity"></app-overview>
</template>
</div>
</template>
@@ -22,7 +22,8 @@ export default {
name: 'DetailOverview',
props: {
entity: Object,
timeFilter: Object
timeFilter: Object,
keywordList: Array
},
components: {
'domain-overview': Domain,

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