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
110 changed files with 2875 additions and 1401 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
}

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

View File

@@ -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;
@@ -1905,6 +1917,9 @@
&.update-dialog__table--psiphon3 {
height: calc(90vh - 190px - 200px - 50px - 10px);
}
&.update-dialog__table--system-user {
height: calc(100% - 139px);
}
}
.update-knowledge-form {
.el-upload {

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 = ''
@@ -791,10 +785,10 @@ export default {
})
return
}
/* if (route === this.route) {
if (route === this.route) {
this.refresh()
return
} */
}
if (route) {
this.$router.push({
path: route,

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') }}
@@ -59,7 +59,7 @@
<el-option
v-for="item in eventTypeList"
:key="item.value"
:label="$t(item.label)"
:label="item.label"
:value="item.value"
/>
</el-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"
@@ -20,12 +21,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 : '—'}}</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>
@@ -66,7 +67,9 @@
<div class="center-dialog">
<el-dialog v-model="showUpdateDialog"
:destroy-on-close="true"
:custom-class="showAddUpdateDialog ? 'update-knowledge update-knowledge--upload' : 'update-knowledge'"
:before-close="beforeClose"
:after-close="handleClose">
<div class="knowledge-update__top" >
<div class="update-left__icon">
@@ -75,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"
@@ -83,10 +86,11 @@
:active-value="1"
:inactive-value="0"
:before-change="(knowledgeId) => confirmSwitchLearning(updateKnowledge.knowledgeId)"
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">
@@ -108,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>
@@ -120,13 +124,13 @@
</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>
</div>
</div>
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' ? 'calc(90vh - 190px - 200px - 50px - 42px)' : 'calc(100% - 242px)', marginTop: '42px', position: 'absolute', width: 'calc(100% - 60px)'}">
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning' ? 'calc(90vh - 190px - 200px - 50px - 42px)' : 'calc(100% - 242px)', marginTop: '42px', position: 'absolute', width: 'calc(100% - 60px)'}">
<loading :loading="updateLogLoading"></loading>
</div>
<el-table ref="updateDataTable"
@@ -134,12 +138,20 @@
:data="updateHistoryList"
@selection-change="secondSelectionChange"
width="100%"
:class="updateKnowledge.source === 'cn_psiphon3_ip' ? 'update-dialog__table update-dialog__table--psiphon3' : 'update-dialog__table'"
class="update-dialog__table"
:class="{
'update-dialog__table--psiphon3': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning',
'update-dialog__table--system-user': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab !== 'intelligenceLearning'
}"
:header-cell-style="{background:'#f5f7fa',color:'#353636',fontWeight: '400',fontSize: '12px',borderRight: 'none',borderBottom: 'none'}"
cell-style="padding:6px 0px;font-size: 12px;color: #353636;font-weight: 400;line-height: 20px;border-right:none;"
header-cell-style="padding:8px 0px;font-size: 12px;color: #353636;font-weight: 500;border-right:none;">
<el-table-column prop="opTime" :label="$t('entities.tab.informationAggregation.updateTime')" width="150" ></el-table-column>
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="activeTab === 'updateRecord'">
<el-table-column prop="opTime" :label="$t('entities.tab.informationAggregation.updateTime')" width="150" >
<template #default="scope" :column="item">
<span>{{scope.row.opTime ? dateFormatByAppearance(scope.row.opTime) : '-'}}</span>
</template>
</el-table-column>
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="updateKnowledge.source !== 'cn_psiphon3_ip' || activeTab === 'updateRecord'">
<template #default="scope" :column="item">
<span>{{$_.get(scope.row, 'user.name', '-')}}</span>
</template>
@@ -154,7 +166,7 @@
</template>
</el-table>
<div class="psiphon3" v-if="updateKnowledge.source === 'cn_psiphon3_ip'">
<div class="psiphon3" v-if="updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning'">
<div class="psiphon3-title">{{$t('knowledgeBase.psiphon3IpCount')}}</div>
<div class="psiphon3-bar">
<chart-error v-if="showErrorForPsiphon3" :content="errorMsgForPsiphon3"/>
@@ -245,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>
@@ -272,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>
@@ -284,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'
@@ -328,6 +340,7 @@ export default {
psiphon3Loading: false,
updateLogLoading: false,
showConfirmSwitch: false,
// timer: null,
switchKnowledgeId: '',
activeTab: 'updateRecord',
isNoDataForPsiphon3: false,
@@ -337,6 +350,7 @@ export default {
tabType: 'total',
mousemoveCursor: '',
selectTime: 1440,
// hasUpdatingRecord: false,
tabs: [
{
name: 'knowledgeBase.total',
@@ -369,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,
@@ -386,8 +394,7 @@ export default {
fileList: ref([]),
uploadFileSizeLimit: 1024 * 1024 * 1024,
myChart: shallowRef(null),
chartOption: shallowRef(null),
timeFilter
chartOption: shallowRef(null)
}
},
methods: {
@@ -416,7 +423,6 @@ export default {
},
xAxis: {
type: 'time',
boundaryGap: ['1%', '3%'],
axisLine: {
show: false
},
@@ -460,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 => {
@@ -475,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)
@@ -523,26 +532,32 @@ export default {
})
},
timeChange () {
this.timeFilter.endTime = window.$dayJs.tz().valueOf()
this.timeFilter.startTime = this.timeFilter.endTime - this.selectTime * 60 * 1000
this.init()
this.$nextTick(() => {
this.handleActiveBar()
})
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
activeChange (item) { // isClick:代表是通过点击操作来的
if (item) {
this.tabType = item.class
}
this.legendSelectChange(item)
this.init()
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
},
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 = ''
@@ -572,15 +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()
this.init()
/* } else {
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
} */
},
beforeUpload (file) {
this.uploadLoading = true
@@ -592,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)
@@ -612,6 +624,13 @@ export default {
data.isSelected = val
this.$emit('checkboxStatusChange', val, data)
},
beforeClose (done) {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
done()
},
handleClose () {
this.showUpdateDialog = false
this.showAddUpdateDialog = false
@@ -631,11 +650,15 @@ export default {
this.updateKnowledge = data
this.showEnable = showEnable
await this.getCurTabData()
await this.init()
if (data.source === 'cn_psiphon3_ip') {
await this.init()
}
this.showUpdate()
this.$nextTick(() => {
this.handleActiveBar()
})
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
this.$nextTick(() => {
this.handleActiveBar()
})
}
},
uploadRecord () {
this.showAddUpdateDialog = true
@@ -648,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
@@ -667,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(() => {
@@ -676,6 +708,9 @@ export default {
// 切换tab
handleClick (tab) {
this.getCurTabData()
if (tab.index === '1') {
this.timeChange()
}
},
clearSelect () {
this.$nextTick(() => {
@@ -714,6 +749,9 @@ 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
@@ -736,21 +774,21 @@ export default {
},
watch: {
tabType (n) {
this.$nextTick(() => {
this.handleActiveBar()
})
this.timeChange()
},
timeFilter: {
handler () {
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) {
console.info(n)
if (this.tableData && this.tableData.length > 0) {
this.aiTaggingList = []
this.websketchList = []
@@ -772,16 +810,24 @@ export default {
}
}
},
activeTab (n) {
if (n === 'updateRecord') {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
}
},
showAddUpdateDialog: {
handler (n) {
if (!n) {
this.fileList = []
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
this.init()
}
this.timeChange()
} else {
this.myChart && this.myChart.dispose()
this.myChart = null
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
}
}
}
@@ -803,7 +849,7 @@ export default {
})
},
beforeUnmount () {
clearTimeout(this.timer)
//clearTimeout(this.timer)
window.removeEventListener('resize', this.resize)
const dom = document.getElementById('psiphonBarChart')
if (dom) {

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]

File diff suppressed because one or more lines are too long

View File

@@ -121,8 +121,6 @@ export function xAxisTimeFormatter (value) {
':' +
(date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes())
// 如果是一天的开始
console.info(date.getTime(), dayStart.getTime(), hourStart.getTime(), date.getTime() === dayStart.getTime(), date.getTime() === hourStart.getTime())
console.info(getSecond(date.getTime()), getSecond(dayStart.getTime()), getSecond(hourStart.getTime()))
if (getSecond(date.getTime()) === getSecond(dayStart.getTime())) {
return '{day|' + dateFormat(date, 'YYYY-MM-DD') + '}'
} else if (getSecond(date.getTime()) === getSecond(hourStart.getTime())) {

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,22 +109,17 @@ export default {
},
methods: {
initData () {
const params = {
resource: this.entity.entityName
// startTime: getSecond(this.oneDayTimeFilter.startTime),
// endTime: getSecond(this.oneDayTimeFilter.endTime)
}
const url = this.getUrlByEntityType(this.entity.entityType)
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: params })
const openPort = axios.get(url, { params: params })
// const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
// const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: this.getParamsByTabType(entityDetailTabsName.informationAggregation) })
const openPort = axios.get(url, { params: this.getParamsByTabType(entityDetailTabsName.openPort) })
const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.securityEvent) })
const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.performanceEvent) })
Promise.all([informationAggregation, openPort]).then(response => {
if (response[0].status === 200) {
Promise.allSettled([informationAggregation, openPort, security, performance]).then(response => {
const informationAggregationResponse = response[0].value
if (informationAggregationResponse.status === 200) {
const list = []
response[0].data.data.result.forEach(r => {
informationAggregationResponse.data.data.result.forEach(r => {
Object.keys(r).forEach(k => {
const aggregation = {
createTime: r[k].createTime,
@@ -152,40 +142,42 @@ export default {
}
})
})
this.initSetTag(entityDetailTabsName.informationAggregation, list.length)
}
if (response[1].status === 200) {
this.initSetTag(entityDetailTabsName.openPort, response[1].data.data.result.length)
const openPortResponse = response[1].value
if (openPortResponse.status === 200) {
this.initSetTag(entityDetailTabsName.openPort, openPortResponse.data.data.result.length)
}
// if (response[2].status === 200) {
// this.initSetTag(entityDetailTabsName.securityEvent, response[2].data.data.result.length)
const securityResponse = response[2].value
if (securityResponse.status === 200) {
this.initSetTag(entityDetailTabsName.securityEvent, securityResponse.data.data.result.length)
}
// let performanceResponse = response[3].value
// if (performanceResponse.status === 200) {
// this.initSetTag(entityDetailTabsName.performanceEvent, performanceResponse.data.data.result.length)
// }
// if (response[3].status === 200) {
// this.initSetTag(entityDetailTabsName.performanceEvent, response[3].data.data.result.length)
// }
this.initSetTag(entityDetailTabsName.securityEvent, 0)
this.initSetTag(entityDetailTabsName.performanceEvent, 0)
})
const relatedEntityParams = this.getParamsByTabType(entityDetailTabsName.relatedEntity)
// 域名解析
if (this.entity.entityType === 'app') {
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: relatedEntityParams })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: relatedEntityParams })
this.promiseData(ipsOfApp, domainsOfApp)
}
if (this.entity.entityType === 'ip') {
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
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

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

View File

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

View File

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

@@ -31,7 +31,7 @@
<div class="new-filter-content-content">
<el-checkbox-group v-model="checkEventType" @change="onChangeCategory" style="display: flex;flex-direction: column">
<el-checkbox v-for="item in eventTypeList" :key="item.name" class="new-filter-content-checkbox" :label="item.name">
{{ item.label }}
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
@@ -94,13 +94,7 @@ export default {
}
if (data.eventTypeList) {
this.eventTypeList = []
data.eventTypeList.forEach(item => {
const obj = detectionUnitList.eventTypeList.find(d => d.value === item.name)
if (obj) {
this.eventTypeList.push({ ...item, label: this.$t(obj.label) })
}
})
this.eventTypeList = data.eventTypeList
} else {
this.eventTypeList = []
}

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,16 +53,16 @@
</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">
<el-input size="mini" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
<el-input size="mini" maxlength="5" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
</el-form-item>
<div>times within</div>
<el-form-item prop="interval">
<el-input size="mini" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
<el-form-item prop="interval" class="policy-form-item">
<el-input size="mini" maxlength="5" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
</el-form-item>
<el-form-item prop="intervalVal">
@@ -79,8 +79,8 @@
<div class="trigger-block-item">
<div>With the counter resetting after no activity for</div>
<el-form-item prop="resetInterval">
<el-input size="mini" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
<el-form-item prop="resetInterval" class="policy-form-item">
<el-input size="mini" maxlength="5" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
</el-form-item>
<el-form-item prop="resetIntervalVal">
<el-select v-model="triggerObj.resetIntervalVal" class="form-trigger__select" placeholder=" " size="mini">
@@ -94,11 +94,11 @@
</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">
<el-input size="mini" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
<el-input size="mini" maxlength="5" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
</el-form-item>
<el-form-item prop="intervalVal">
@@ -113,7 +113,7 @@
</el-form-item>
内至少发生
<el-form-item prop="atLeast">
<el-input size="mini" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
<el-input size="mini" maxlength="5" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
</el-form-item>
</div>
@@ -121,7 +121,7 @@
<div class="trigger-block-item">
若连续
<el-form-item prop="resetInterval">
<el-input size="mini" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
<el-input size="mini" maxlength="5" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
</el-form-item>
<el-form-item prop="resetIntervalVal">
<el-select v-model="triggerObj.resetIntervalVal" class="form-trigger__select" placeholder=" " size="mini">
@@ -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,11 +162,45 @@ 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',
data () {
const intervalValidator = (rule, value, callback) => {
const obj = this.handleIntervalByDateType(rule, value, this.triggerObj.intervalVal)
if (!obj.flag && obj.msg) {
callback(new Error(obj.msg))
} else {
callback()
}
}
const intervalValValidator = (rule, value, callback) => {
const obj = this.handleIntervalByDateType(rule, this.triggerObj.intervalVal, value)
if (!obj.flag && obj.msg) {
this.$refs.form3.validateField('interval')
callback()
} else {
callback()
}
}
const resetIntervalValidator = (rule, value, callback) => {
const obj = this.handleIntervalByDateType(rule, value, this.triggerObj.resetIntervalVal)
if (!obj.flag && obj.msg) {
callback(new Error(obj.msg))
} else {
callback()
}
}
const resetIntervalValValidator = (rule, value, callback) => {
const obj = this.handleIntervalByDateType(rule, this.triggerObj.resetIntervalVal, value)
if (!obj.flag && obj.msg) {
this.$refs.form3.validateField('resetInterval')
callback()
} else {
callback()
}
}
return {
activeNames: ['1'],
rules: {
@@ -180,6 +216,10 @@ export default {
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
},
{
validator: intervalValidator,
trigger: 'blur'
}
],
intervalVal: [
@@ -187,6 +227,10 @@ export default {
required: true,
message: this.$t('validate.required'),
trigger: 'change'
},
{
validator: intervalValValidator,
trigger: 'change'
}
],
resetInterval: [
@@ -194,6 +238,10 @@ export default {
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
},
{
validator: resetIntervalValidator,
trigger: 'blur'
}
],
resetIntervalVal: [
@@ -201,13 +249,19 @@ export default {
required: true,
message: this.$t('validate.required'),
trigger: 'change'
},
{
validator: resetIntervalValValidator,
trigger: 'change'
}
]
},
intervalList: [],
editObj: {},
isComplete: true, // 参数完整标识默认完整照顾编辑模式false即不完整
language: 'en'
language: EN,
ZH,
EN
}
},
components: {
@@ -216,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()
@@ -280,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()
@@ -317,7 +371,7 @@ export default {
}
},
/** 创建policy */
createPolicy (flag) {
createPolicy () {
const settingLen = Object.keys(this.settingObj).length
const ruleLen = Object.keys(this.ruleObj).length
@@ -326,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)
@@ -352,7 +403,7 @@ export default {
})
this.$router.push({
path: '/detectionPolicy',
path: '/detection/policy',
query: {
t: +new Date()
}
@@ -377,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)
@@ -417,7 +474,157 @@ export default {
})
}
this.$message.error(this.$t('detection.create.informationFilled'))
},
handleIntervalByDateType (rule, value, type) {
if (value && (type === 'hours' || type === '小时')) {
if (parseInt(value) <= 24) {
return { flag: true }
} else {
return { flag: false, msg: this.$t('policy.dateTimeRangeHours') }
}
}
if (value && (type === 'minutes' || type === '分钟')) {
if (parseInt(value) <= 1440) {
return { flag: true }
} else {
return { flag: false, msg: this.$t('policy.dateTimeRangeMinutes') }
}
}
if (value && (type === 'seconds' || type === '秒')) {
if (parseInt(value) <= 86400) {
return { flag: true }
} else {
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>
@@ -56,9 +58,6 @@
<template v-else-if="item.prop === 'category'">
{{ changeCategory(scope.row[item.prop]) }}
</template>
<template v-else-if="item.prop === 'eventType'">
{{ changeEventType(scope.row[item.prop]) }}
</template>
<template v-else-if="item.prop === 'description'">
<div style="padding-right: 20px">{{ scope.row[item.prop] }}</div>
</template>
@@ -85,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',
@@ -92,6 +92,9 @@ export default {
isNoData: {
type: Boolean,
default: false
},
policyDetail: {
type: Object
}
},
mixins: [table],
@@ -149,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,
@@ -181,16 +195,6 @@ export default {
}
return label
}
},
changeEventType (value) {
if (value) {
const obj = detectionUnitList.eventTypeList.find(d => d.value === value)
let label = value
if (obj) {
label = this.$t(obj.label)
}
return label
}
}
}
}

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

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"
@@ -92,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">
@@ -174,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',
@@ -181,7 +201,8 @@ export default {
Loading,
ExplorerSearch,
EntityFilter,
EntityList
EntityList,
DateTimeRange
},
data () {
return {
@@ -200,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',
@@ -284,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: {
@@ -302,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) {
@@ -361,6 +383,7 @@ export default {
this.q = ''
this.metaList = []
}
this.getKeyword(param.keywordList)
// 参数q避免切换页码时地址栏参数q为空
let urlQ = ''
@@ -368,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添加到地址栏
@@ -389,7 +414,7 @@ export default {
if (!this.showList) {
// 首页进入搜索时重载页面,视觉上进入列表页面
this.$router.push({
path: '/entityExplorer',
path: '/entity',
query: {
listMode: this.listMode,
q: urlQ,
@@ -417,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 })
}
},
// 点击上一页箭头
@@ -489,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
@@ -548,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)
@@ -570,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
})
@@ -675,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]))
}
@@ -727,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即列表页并非首页则开始搜索
@@ -741,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)
}
@@ -751,6 +833,10 @@ export default {
this.$store.commit('resetScoreBase')
this.queryScoreBase()
}
if (!this.showList) {
this.getEntityIndexData()
}
},
watch: {
timeFilter () {
@@ -763,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 {
@@ -781,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,

View File

@@ -4,11 +4,11 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
<div class="row__content">{{$_.get(entityData, 'category.categoryName', '-') || '-'}}</div>
<div class="row__content" v-high-light="keywordList">{{ $_.get(entityData, 'category.categoryName', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.domainDetail.categoryGroup')}}</div>
<div class="row__content">{{$_.get(entityData, 'category.categoryGroup', '-') || '-'}}</div>
<div class="row__content" v-high-light="keywordList">{{ $_.get(entityData, 'category.categoryGroup', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.reputationLevel')}}</div>
@@ -20,7 +20,7 @@
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.org')}}</div>
<div class="row__content">{{$_.get(entityData, 'whois.registrantOrg', '-') || '-'}}</div>
<div class="row__content" v-high-light="keywordList">{{ $_.get(entityData, 'whois.registrantOrg', '-') || '-' }}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpCompanyName')}}</div>
@@ -97,7 +97,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>
@@ -106,7 +106,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>
@@ -119,7 +119,7 @@
<div v-if="loadingRelationshipTwo" style="position: relative;width: 450px;">
<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>
@@ -128,7 +128,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>
@@ -149,7 +149,7 @@
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__label row__label--width130">{{performance.startTime ? dateFormatByAppearance(Number(performance.startTime)) : '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
</div>
@@ -176,7 +176,7 @@
</div>
<div class="overview__row overview__row--small-font" v-for="(security, i) in entityData.securityList" :key="i">
<div class="row__label row__label--width130">{{dateFormatByAppearance(getMillisecond(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>
@@ -219,7 +219,7 @@ import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import axios from 'axios'
import relatedServer from '@/mixins/relatedServer'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond, getNowTime } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
@@ -229,6 +229,9 @@ export default {
Loading,
Chart
},
props: {
keywordList: Array
},
mixins: [entityDetailMixin, relatedServer],
data () {
return {
@@ -321,16 +324,21 @@ export default {
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
return {
resource: this.entity.entityValue
}
},
getQueryParamsWithTime () {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
domain: 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 () {
@@ -352,7 +360,7 @@ export default {
})
},
getBasicProperties () {
axios.get(this.basicProperties, { params: this.getQueryParamsWithTime() }).then(response => {
axios.get(this.basicProperties, { params: this.getQueryParams() }).then(response => {
if (response.status === 200) {
this.entityData = {
...this.entityData,

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