Compare commits
91 Commits
23.10.demo
...
dev-23.11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e26d2b40ab | ||
|
|
6c1f1ed71b | ||
|
|
b4b72fc1e1 | ||
|
|
b3d4bbd440 | ||
|
|
6c90ebe7bc | ||
|
|
ef3994e14a | ||
|
|
abc1d07e6c | ||
|
|
38af43b322 | ||
|
|
90ed543a84 | ||
|
|
c66a6b7f59 | ||
|
|
a0af36b7c0 | ||
|
|
f95dc60cd3 | ||
|
|
7d64868ce0 | ||
|
|
62a7a629e5 | ||
|
|
e3c6c21545 | ||
|
|
3b2eaf3851 | ||
|
|
ee05f47f2d | ||
|
|
07dd16f2c2 | ||
|
|
9103c454c2 | ||
|
|
e5a7c2abfa | ||
|
|
e288e20fd7 | ||
|
|
ec746f0fc7 | ||
|
|
4ca33e9ede | ||
|
|
b8105a4565 | ||
|
|
29157ae7d3 | ||
|
|
9c0ce493ab | ||
|
|
4662061fc5 | ||
|
|
0eb0346abc | ||
|
|
c8926177f7 | ||
|
|
5aa5d77511 | ||
|
|
38368a6cb8 | ||
|
|
051aeadbca | ||
|
|
8c2119e773 | ||
|
|
cb70fb0236 | ||
|
|
853fa79d4c | ||
|
|
2f919d1774 | ||
|
|
360fffde68 | ||
|
|
bb2a7676e6 | ||
|
|
de698f0a71 | ||
|
|
1c1d354a6c | ||
|
|
8126618e54 | ||
|
|
5a8796688a | ||
|
|
be36b2604b | ||
|
|
330a4b0d3b | ||
|
|
1410f890f3 | ||
|
|
78105475fb | ||
|
|
02a9f35070 | ||
|
|
c89397da97 | ||
|
|
36c3db5dee | ||
|
|
366bb8f17f | ||
|
|
1c00f568fa | ||
|
|
93be846064 | ||
|
|
ffb822d65d | ||
|
|
f8e9d36c8a | ||
|
|
241665cd80 | ||
|
|
ee57ca4c6c | ||
|
|
2bb6c54cab | ||
|
|
65827d27e4 | ||
|
|
98948ff179 | ||
|
|
74dc057090 | ||
|
|
e8959bab27 | ||
|
|
a7ce777e10 | ||
|
|
d7d450222b | ||
|
|
3da4b4b20a | ||
|
|
a0d2160b43 | ||
|
|
b94dba9ed3 | ||
|
|
696b03ad40 | ||
|
|
f199442c60 | ||
|
|
889903bb46 | ||
|
|
e12d76e2ad | ||
|
|
29df6ec60e | ||
|
|
20857e1203 | ||
|
|
8d4924a421 | ||
|
|
41d2f48766 | ||
|
|
f151415de6 | ||
|
|
73dec68e23 | ||
|
|
60e821fb16 | ||
|
|
4cb4aba707 | ||
|
|
ba4893dae1 | ||
|
|
19c42021db | ||
|
|
d6c9bd0ee2 | ||
|
|
63fd8b268f | ||
|
|
bd1f755612 | ||
|
|
dd4f5e1fba | ||
|
|
ed1d994d5e | ||
|
|
815af776aa | ||
|
|
a4da1dbfac | ||
|
|
9a3bf1a4af | ||
|
|
6a196ba3f0 | ||
|
|
7b8ca90436 | ||
|
|
90827fd706 |
@@ -3,3 +3,18 @@ const BASE_CONFIG = {
|
||||
version: '23.10',
|
||||
apiVersion: 'v1'
|
||||
}
|
||||
// 默认时间过滤条件,单位分钟. 0表示请求接口时不传时间参数
|
||||
const DEFAULT_TIME_FILTER_RANGE = {
|
||||
dashboard: 60,
|
||||
entity: {
|
||||
list: 60,
|
||||
trafficLine: 60,
|
||||
informationAggregation: 0,
|
||||
relatedEntity: 60 * 24 * 7,
|
||||
openPort: 60 * 24 * 7,
|
||||
securityEvent: 60 * 24 * 7,
|
||||
performanceEvent: 60 * 24 * 7,
|
||||
behaviorPattern: 60 * 24 * 7
|
||||
},
|
||||
detection: 60
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
&>div {
|
||||
height: 100%;
|
||||
}
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.cn-header {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
}
|
||||
|
||||
.detection-form-content {
|
||||
height: 100%;
|
||||
height: calc(100% - 92px);
|
||||
overflow: scroll;
|
||||
padding-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.detection-form-collapse {
|
||||
margin-top: 20px;
|
||||
@@ -130,6 +130,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.policy-form-trigger {
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--mini, .el-input--mini .el-input__inner {
|
||||
@@ -143,4 +149,13 @@
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.policy-form__footer {
|
||||
width: calc(100% + 40px);
|
||||
height: 60px;
|
||||
margin-left: -20px;
|
||||
box-shadow: 0 -1px 4px 0 rgba(0,0,0,0.10);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-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;
|
||||
}
|
||||
|
||||
.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 {
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
margin: 0 20px;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 600;
|
||||
}
|
||||
.new-detection-filter-icon {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 2px;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter__body {
|
||||
padding: 5px 0 0 15px;
|
||||
width: calc(100% - 30px);
|
||||
margin: 0 10px 0 20px;
|
||||
max-height: 265px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
.el-checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-checkbox {
|
||||
.filter__body-item {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.new-detection-filter-title {
|
||||
display: flex;
|
||||
flex: 0 0 32px;
|
||||
align-items: center;
|
||||
padding-left: 27px;
|
||||
background-color: #EFF2F5;
|
||||
cursor: pointer;
|
||||
|
||||
.filter__body-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 600;
|
||||
margin: -10px;
|
||||
margin-bottom: 10px;
|
||||
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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
color: #046ECA;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 500;
|
||||
height: 36px;
|
||||
}
|
||||
.timeline__start-time {
|
||||
font-size: 12px;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -351,6 +351,7 @@
|
||||
}
|
||||
.score-dot {
|
||||
display: inline-block;
|
||||
margin-bottom: 2px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
@@ -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,6 +565,8 @@ export default {
|
||||
const column = this.columnList.find(c => {
|
||||
return c.label === param.column
|
||||
})
|
||||
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))
|
||||
// 不是在首位,则删除时顺带删除前一个index(and或or),否则顺带删除后一个index
|
||||
if (metaIndex > 0) {
|
||||
@@ -564,6 +576,7 @@ export default {
|
||||
} 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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="pagination" >
|
||||
<el-config-provider :locale="locale">
|
||||
<el-pagination
|
||||
ref="page"
|
||||
@size-change="size"
|
||||
@@ -20,16 +21,19 @@
|
||||
</el-select>
|
||||
|
||||
</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 () {
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<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>
|
||||
<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"
|
||||
@@ -30,16 +31,17 @@
|
||||
type="datetimerange"
|
||||
@change="timeArrChange"
|
||||
/>
|
||||
<div class="content-title">From</div>
|
||||
</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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
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 = ''
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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' }
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="block-mode-title">{{ $t('detection.policy.indicatorMatch') }}</div>
|
||||
<div class="block-mode-content">
|
||||
{{ $t('detection.policy.indicatorMatchIntroduce') }}
|
||||
<div v-if="language==='cn'" style="color: rgba(0,0,0,0)">0</div>
|
||||
<div v-if="language===ZH" style="color: rgba(0,0,0,0)">0</div>
|
||||
</div>
|
||||
<div :class="settingObj.ruleType===detectionRuleType.indicator?'block-mode-btn-active':'block-mode-btn'"
|
||||
@click="selectMode(detectionRuleType.indicator)">{{ $t('overall.select') }}
|
||||
@@ -109,7 +109,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { detectionRuleType, storageKey, detectionUnitList } from '@/utils/constants'
|
||||
import { detectionRuleType, storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
|
||||
export default {
|
||||
@@ -160,7 +160,8 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
language: 'en'
|
||||
language: EN,
|
||||
ZH
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -183,7 +184,7 @@ export default {
|
||||
methods: {
|
||||
switchStatus,
|
||||
initData () {
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
this.language = localStorage.getItem(storageKey.language) || EN
|
||||
this.categoryList = detectionUnitList.categoryList
|
||||
this.eventTypeList = detectionUnitList.eventTypeList
|
||||
},
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<div class="card-content">
|
||||
<div class="card-operate">
|
||||
<el-switch v-model="data.status"
|
||||
v-if="hasPermission('editBuiltInKnowledgeBase')"
|
||||
class="card-enable"
|
||||
active-color="#38ACD2"
|
||||
inactive-color="#C0CEDB"
|
||||
@@ -17,15 +18,15 @@
|
||||
</el-switch>
|
||||
</div>
|
||||
<div class="card-icon">
|
||||
<img :src="data.iconUrl" style="max-height: 50px;"/>
|
||||
<img :src="data.iconUrl"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
|
||||
</div>
|
||||
<div class="card-desc" :title="data.desc">{{data.desc ? data.desc : '—'}}</div>
|
||||
<div class="card-desc" :title="$t(data.desc)">{{$t(data.desc) || '—'}}</div>
|
||||
</div>
|
||||
<div class="card-operate__footer">
|
||||
<button v-if="data.showUpdate"
|
||||
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')"
|
||||
class="top-tool-btn--update"
|
||||
@click="jumpToUpdatePage(data,true)">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
@@ -44,12 +45,12 @@
|
||||
<img :src="data.iconUrl"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
|
||||
</div>
|
||||
<div class="card-desc" :title="data.desc ? data.desc:'—'">{{data.desc ? data.desc : '—'}}</div>
|
||||
<div class="card-desc" :title="data.desc ? $t(data.desc) : '—'">{{data.desc ? $t(data.desc) : '—'}}</div>
|
||||
</div>
|
||||
<div class="card-operate__footer">
|
||||
<button v-if="data.showUpdate" :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')" :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="jumpToUpdatePage(data,false)">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
@@ -77,7 +78,7 @@
|
||||
<div class="update-right">
|
||||
<div class="knowledge-enable">
|
||||
<div class="update-title">
|
||||
<div class="card-title-name" :title="updateKnowledge.label">{{updateKnowledge.label}}</div>
|
||||
<div class="card-title-name" :title="$t(updateKnowledge.label)">{{$t(updateKnowledge.label)}}</div>
|
||||
</div>
|
||||
<el-switch v-model="updateKnowledge.status"
|
||||
active-color="#38ACD2"
|
||||
@@ -85,11 +86,11 @@
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:before-change="(knowledgeId) => confirmSwitchLearning(updateKnowledge.knowledgeId)"
|
||||
v-if="updateKnowledge.source === 'cn_psiphon3_ip'"
|
||||
v-if="updateKnowledge.source === 'cn_psiphon3_ip' && hasPermission('editBuiltInKnowledgeBase')"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
<div class="knowledge-desc" :title="updateKnowledge.desc">{{updateKnowledge.desc ? updateKnowledge.desc : '—'}}</div>
|
||||
<div class="knowledge-desc" :title="updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'">{{updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="!showAddUpdateDialog">
|
||||
@@ -111,7 +112,7 @@
|
||||
</el-tabs>
|
||||
<div class="update-operate">
|
||||
<button :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="uploadRecord">
|
||||
@click="uploadRecord"><!--:disabled="hasUpdatingRecord"-->
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
</button>
|
||||
@@ -123,7 +124,7 @@
|
||||
</div>
|
||||
<div class="update-operate">
|
||||
<button :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="uploadRecord">
|
||||
@click="uploadRecord"><!-- :disabled="hasUpdatingRecord" -->
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
</button>
|
||||
@@ -150,7 +151,7 @@
|
||||
<span>{{scope.row.opTime ? dateFormatByAppearance(scope.row.opTime) : '-'}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="activeTab === 'updateRecord'">
|
||||
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="updateKnowledge.source !== 'cn_psiphon3_ip' || activeTab === 'updateRecord'">
|
||||
<template #default="scope" :column="item">
|
||||
<span>{{$_.get(scope.row, 'user.name', '-')}}</span>
|
||||
</template>
|
||||
@@ -256,7 +257,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="showAddUpdateDialog = false">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button @click="cancle">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submitConfirm">{{ $t('tip.confirm') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -283,8 +284,8 @@
|
||||
<div class="dialog-message">{{ confirmSwitchLearningTip }}</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showConfirmSwitch = false">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="switchLearning">OK</el-button>
|
||||
<el-button @click="cancleSwitch">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="switchLearning">{{$t('tip.confirm')}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -295,7 +296,7 @@
|
||||
import table from '@/mixins/table'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { getSecond, getMillisecond, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo } from '@/utils/constants'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo, knowledgeCardUpdateRecordType } from '@/utils/constants'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { api } from '@/utils/api'
|
||||
import { detectionTooltipFormatter } from '@/views/charts/charts/tools'
|
||||
@@ -339,6 +340,7 @@ export default {
|
||||
psiphon3Loading: false,
|
||||
updateLogLoading: false,
|
||||
showConfirmSwitch: false,
|
||||
// timer: null,
|
||||
switchKnowledgeId: '',
|
||||
activeTab: 'updateRecord',
|
||||
isNoDataForPsiphon3: false,
|
||||
@@ -348,6 +350,7 @@ export default {
|
||||
tabType: 'total',
|
||||
mousemoveCursor: '',
|
||||
selectTime: 1440,
|
||||
// hasUpdatingRecord: false,
|
||||
tabs: [
|
||||
{
|
||||
name: 'knowledgeBase.total',
|
||||
@@ -380,12 +383,6 @@ export default {
|
||||
setup () {
|
||||
// 没上传过文件的提示
|
||||
const uploadErrorTip = ref('')
|
||||
const nowMill = window.$dayJs.tz().valueOf()
|
||||
const timeFilter = ref({
|
||||
startTime: nowMill - 1000 * 60 * 60 * 24,
|
||||
endTime: nowMill,
|
||||
dateRangeValue: 1440
|
||||
})
|
||||
return {
|
||||
baseUrl: BASE_CONFIG.baseUrl,
|
||||
apiVersion: BASE_CONFIG.apiVersion,
|
||||
@@ -397,8 +394,7 @@ export default {
|
||||
fileList: ref([]),
|
||||
uploadFileSizeLimit: 1024 * 1024 * 1024,
|
||||
myChart: shallowRef(null),
|
||||
chartOption: shallowRef(null),
|
||||
timeFilter
|
||||
chartOption: shallowRef(null)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -427,7 +423,6 @@ export default {
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: ['1%', '3%'],
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
@@ -471,9 +466,10 @@ export default {
|
||||
},
|
||||
init (val, show, active, n) {
|
||||
this.psiphon3Loading = true
|
||||
const endTime = window.$dayJs.tz().valueOf()
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
startTime: getSecond(endTime - this.selectTime * 60 * 1000),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
const url = api.knowledgeBaseTimedistribution.replace('{{knowledgeId}}', this.updateKnowledge.knowledgeId).replace('{{type}}', this.tabType)
|
||||
axios.get(url, { params: params }).then(response => {
|
||||
@@ -486,8 +482,10 @@ export default {
|
||||
const chartsData = res.data.result.map(item => {
|
||||
return [getMillisecond(item.statTime), item.count]
|
||||
})
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.echartsInit(chartsData)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
@@ -534,14 +532,14 @@ export default {
|
||||
})
|
||||
},
|
||||
timeChange () {
|
||||
this.timeFilter.endTime = window.$dayJs.tz().valueOf()
|
||||
this.timeFilter.startTime = this.timeFilter.endTime - this.selectTime * 60 * 1000
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
activeChange (item) { // isClick:代表是通过点击操作来的
|
||||
if (item) {
|
||||
@@ -555,9 +553,11 @@ export default {
|
||||
mouseenterTab (item) {
|
||||
if (this.isNoDataForPsiphon3) return
|
||||
this.mousemoveCursor = item.class
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
mouseleaveTab () {
|
||||
this.mousemoveCursor = ''
|
||||
@@ -587,17 +587,9 @@ export default {
|
||||
uploadSuccess (response) {
|
||||
this.uploadLoading = false
|
||||
this.uploaded = true
|
||||
/* this.uploaded = response.code === 200
|
||||
if (response.code === 200) { */
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
this.showAddUpdateDialog = false
|
||||
this.getCurTabData()
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
/* } else {
|
||||
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
|
||||
} */
|
||||
},
|
||||
beforeUpload (file) {
|
||||
this.uploadLoading = true
|
||||
@@ -609,6 +601,9 @@ export default {
|
||||
submit () {
|
||||
this.$refs.knowledgeUpload.submit()
|
||||
},
|
||||
cancle () {
|
||||
this.showAddUpdateDialog = false
|
||||
},
|
||||
clickCard (data, event) {
|
||||
if (data.isSelected) { // 原来为选中,当前点击后未选中
|
||||
const index = this.checkList.indexOf(data)
|
||||
@@ -659,9 +654,11 @@ export default {
|
||||
await this.init()
|
||||
}
|
||||
this.showUpdate()
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
uploadRecord () {
|
||||
this.showAddUpdateDialog = true
|
||||
@@ -674,12 +671,12 @@ export default {
|
||||
pageSize: -1
|
||||
}
|
||||
if (this.showEnable) {
|
||||
if (this.activeTab === 'updateRecord') {
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.updateRecord) {
|
||||
params = {
|
||||
...params,
|
||||
opUser: -1
|
||||
}
|
||||
} else if (this.activeTab === 'intelligenceLearning') {
|
||||
} else if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
params = {
|
||||
...params,
|
||||
opUser: 0
|
||||
@@ -693,6 +690,15 @@ export default {
|
||||
if (this.updateHistoryList[0]) {
|
||||
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
|
||||
}
|
||||
/*
|
||||
this.hasUpdatingRecord = false
|
||||
this.updateHistoryList.forEach(item => {
|
||||
if (item.isUpdating) { // if(item.isUpdating){//????????
|
||||
this.hasUpdatingRecord = true
|
||||
}
|
||||
})
|
||||
|
||||
*/
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
}).finally(() => {
|
||||
@@ -703,7 +709,7 @@ export default {
|
||||
handleClick (tab) {
|
||||
this.getCurTabData()
|
||||
if (tab.index === '1') {
|
||||
this.init()
|
||||
this.timeChange()
|
||||
}
|
||||
},
|
||||
clearSelect () {
|
||||
@@ -743,15 +749,13 @@ export default {
|
||||
this.switchKnowledgeId = id
|
||||
return false
|
||||
},
|
||||
cancleSwitch () {
|
||||
this.showConfirmSwitch = false
|
||||
},
|
||||
switchLearning () {
|
||||
const hint = this.aiTaggingList.find(d => d.knowledgeId === this.switchKnowledgeId)
|
||||
const toStatus = hint.status === 0 ? 1 : 0
|
||||
const url = toStatus === 0 ? api.knowledgeBaseLearningStop : api.knowledgeBaseLearningStart
|
||||
if (hint.knowledgeId === 101 || hint.knowledgeId === 102) {
|
||||
hint.status = toStatus
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
this.showConfirmSwitch = false
|
||||
} else {
|
||||
axios.post(`${url}?knowledgeId=${hint.knowledgeId}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
hint.status = toStatus
|
||||
@@ -767,24 +771,22 @@ export default {
|
||||
this.showConfirmSwitch = false
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tabType (n) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
this.timeChange()
|
||||
},
|
||||
timeFilter: {
|
||||
handler () {
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
/*
|
||||
hasUpdatingRecord (n) {
|
||||
if (n) { // update record页存在“正在更新”的记录时,每20秒自动请求一次接口
|
||||
this.timer = setTimeout(() => {
|
||||
this.getCurTabData()
|
||||
}, 20000)
|
||||
} else { // 直到出现新的记录,出现新记录后(失败或者成功),取消定时请求接口,右上角"update"按钮恢复可用。"正在更新"和"失败”都会有对应的强调样式。
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
},
|
||||
*/
|
||||
tableData: {
|
||||
handler (n) {
|
||||
if (this.tableData && this.tableData.length > 0) {
|
||||
@@ -805,40 +807,6 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
// 2024-01-15 以下两个是为105环境演示准备的假数据
|
||||
const data1 = {
|
||||
knowledgeId: 101,
|
||||
name: 'CyberGhost',
|
||||
category: 'ai_tagging',
|
||||
description: 'CyberGhost is a VPN service used to unblock sites and browse privately and anonymously.',
|
||||
isBuiltIn: 1,
|
||||
isPublished: 1,
|
||||
status: 1,
|
||||
showUpdate: false
|
||||
}
|
||||
const basicInfo1 = builtInKnowledgeBaseBasicInfo.find(bi => bi.knowledgeId === data1.knowledgeId)
|
||||
this.aiTaggingList.push({
|
||||
...data1,
|
||||
...basicInfo1
|
||||
})
|
||||
const data2 = {
|
||||
knowledgeId: 102,
|
||||
name: 'HotSpotshield VPN',
|
||||
category: 'ai_tagging',
|
||||
description: 'Hotspot Shield is a public VPN service, providing\n' +
|
||||
'a secure proxy connection through an encrypted\n' +
|
||||
'channel between your device and the target\n' +
|
||||
'website, using VPN technology.',
|
||||
isBuiltIn: 1,
|
||||
isPublished: 1,
|
||||
status: 1,
|
||||
showUpdate: false
|
||||
}
|
||||
const basicInfo2 = builtInKnowledgeBaseBasicInfo.find(bi => bi.knowledgeId === data2.knowledgeId)
|
||||
this.aiTaggingList.push({
|
||||
...data2,
|
||||
...basicInfo2
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -854,9 +822,7 @@ export default {
|
||||
handler (n) {
|
||||
if (!n) {
|
||||
this.fileList = []
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
this.timeChange()
|
||||
} else {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
@@ -883,7 +849,7 @@ export default {
|
||||
})
|
||||
},
|
||||
beforeUnmount () {
|
||||
clearTimeout(this.timer)
|
||||
//clearTimeout(this.timer)
|
||||
window.removeEventListener('resize', this.resize)
|
||||
const dom = document.getElementById('psiphonBarChart')
|
||||
if (dom) {
|
||||
@@ -912,9 +878,9 @@ export default {
|
||||
const find = this.aiTaggingList.find(item => item.knowledgeId === this.switchKnowledgeId)
|
||||
if (find) {
|
||||
if (find.status === 0) {
|
||||
tip = this.$t('tip.confirmEnable') + '?'
|
||||
tip = this.$t('tip.confirmEnablePsiphon3') + '?'
|
||||
} else if (find.status === 1) {
|
||||
tip = this.$t('tip.confirmDisable') + '?'
|
||||
tip = this.$t('tip.confirmDisablePsiphon3') + '?'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 // 最后一位加空格
|
||||
}
|
||||
|
||||
|
||||
@@ -32,14 +32,19 @@ router.beforeEach(async (to, from, next) => {
|
||||
store.commit('setMenuList', menuList)
|
||||
store.commit('setButtonList', buttonList)
|
||||
store.commit('setRoleList', roleList)
|
||||
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()
|
||||
/* if (hasMenu(store.getters.menuList, 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -38,6 +38,7 @@ export const storageKey = {
|
||||
leftMenuShrink: 'cn-left-menu-shrink',
|
||||
unsavedChange: 'cn-unsaved-change',
|
||||
entitySearchHistory: 'cn-entity-search-history',
|
||||
detectionSearchHistory: 'cn-detection-search-history',
|
||||
echartLegendFontSize: 'echartLegendFontSize',
|
||||
echartLabelFontSize: 'echartLabelFontSize',
|
||||
tokenExpireCurrentPath: 'token-expire-current-path',
|
||||
@@ -74,6 +75,7 @@ export const panelTypeAndRouteMapping = {
|
||||
ipEntityDetail: 21,
|
||||
domainEntityDetail: 22,
|
||||
appEntityDetail: 23,
|
||||
subscribeEntityDetail: 24,
|
||||
cryptocurrency: 7,
|
||||
ipDrillDownTest: 8,
|
||||
linkMonitor: 14,
|
||||
@@ -98,6 +100,11 @@ export const entityType = {
|
||||
ip: 'IP'
|
||||
}
|
||||
|
||||
export const knowledgeCardUpdateRecordType = {
|
||||
updateRecord: 'updateRecord',
|
||||
intelligenceLearning: 'intelligenceLearning'
|
||||
}
|
||||
|
||||
export const entityDetailTabsName = {
|
||||
informationAggregation: 'informationAggregation',
|
||||
relatedEntity: 'relatedEntity',
|
||||
@@ -213,7 +220,7 @@ export const detectionPageType = {
|
||||
}
|
||||
|
||||
export const listScrollPath = [
|
||||
'/entityExplorer',
|
||||
'/entity',
|
||||
'/detection/performanceEvent',
|
||||
'/detection/securityEvent'
|
||||
]
|
||||
@@ -1792,6 +1799,8 @@ export const langData = [
|
||||
{ value: 'zh', label: 'zh' },
|
||||
{ value: 'en', label: 'en' }
|
||||
]
|
||||
export const ZH = 'zh'
|
||||
export const EN = 'en'
|
||||
|
||||
export const performanceMetricMapping = {
|
||||
'dns error': 'DNS Error Rate',
|
||||
@@ -1804,77 +1813,61 @@ export const builtInKnowledgeBaseBasicInfo = [
|
||||
knowledgeId: 10,
|
||||
label: 'Psiphon3 VPN',
|
||||
iconUrl: 'images/knowledge-base-logo/psiphon3-vpn.png',
|
||||
desc: 'Psiphon3 is a circumvention software for Windows and other platforms that provides uncensored access to Internet content.'
|
||||
desc: 'knowledgeBase.desc.psiphon3'
|
||||
},
|
||||
{
|
||||
knowledgeId: 5,
|
||||
label: 'Domain Category',
|
||||
label: 'network.domainCategory',
|
||||
iconUrl: 'images/knowledge-base-logo/fqdn.png',
|
||||
desc: 'Domain category provides basic information including categories, providers, reputation score.'
|
||||
desc: 'knowledgeBase.desc.domainCategory'
|
||||
},
|
||||
{
|
||||
knowledgeId: 6,
|
||||
label: 'Domain Whois',
|
||||
label: 'knowledgeBase.domainWhois',
|
||||
iconUrl: 'images/knowledge-base-logo/fqdn-whois.png',
|
||||
desc: 'Domain whois contains registration and ownership information for domain names. It includes details like domain registrar, registrant, creation/expiry dates, and contact information.'
|
||||
desc: 'knowledgeBase.desc.domainWhois'
|
||||
},
|
||||
{
|
||||
knowledgeId: 2,
|
||||
label: 'IP ASN',
|
||||
iconUrl: 'images/knowledge-base-logo/ip-asn.png',
|
||||
desc: 'ASN Database associates IP addresses with their corresponding Autonomous System Numbers, which are unique identifiers assigned to internet networks. This database helps identify the network and its owner, facilitating network analysis and monitoring tasks.'
|
||||
desc: 'knowledgeBase.desc.ipAsn'
|
||||
},
|
||||
{
|
||||
knowledgeId: 3,
|
||||
label: 'DNS Server Info',
|
||||
label: 'knowledgeBase.dnsServerInfo',
|
||||
iconUrl: 'images/knowledge-base-logo/dns-server-info.png',
|
||||
desc: 'A DNS Server Info stores information about Domain Name System (DNS) servers. It includes details such as IP addresses, host names, locations, software name, operation system, and roles of the servers.'
|
||||
desc: 'knowledgeBase.desc.dnsServer'
|
||||
},
|
||||
{
|
||||
knowledgeId: 9,
|
||||
label: 'APP Category',
|
||||
label: 'knowledgeBase.appCategory',
|
||||
iconUrl: 'images/knowledge-base-logo/app-category.png',
|
||||
desc: 'APP category provides basic information for over 3000 popular applications, including their categories and service providers.'
|
||||
desc: 'knowledgeBase.desc.appCategory'
|
||||
},
|
||||
{
|
||||
knowledgeId: 7,
|
||||
label: 'Indicators of Compromise',
|
||||
label: 'knowledgeBase.ioc',
|
||||
iconUrl: 'images/knowledge-base-logo/indicators-of-compromise.png',
|
||||
desc: 'Indicator of Compromise (IoC) refers to forensic artifacts, such as unusual network traffic or malicious files, indicating a security breach or cyberattack.'
|
||||
desc: 'knowledgeBase.desc.ioc'
|
||||
},
|
||||
{
|
||||
knowledgeId: 4,
|
||||
label: 'ICP',
|
||||
iconUrl: 'images/knowledge-base-logo/icp.png',
|
||||
desc: 'ICP (Internet Content Provider) license is a permit issued by Chinese authorities, mandatory for websites to legally operate and publish content within mainland China.'
|
||||
desc: 'knowledgeBase.desc.icp'
|
||||
},
|
||||
{
|
||||
knowledgeId: 1,
|
||||
label: 'IP Location',
|
||||
label: 'knowledgeBase.ipLocation',
|
||||
iconUrl: 'images/knowledge-base-logo/ip-location.png',
|
||||
desc: 'IP location Database is a repository containing geographical data associated with IP addresses, such as country, city, ISP, organization, latitude, longitude, and other relevant details.'
|
||||
desc: 'knowledgeBase.desc.ipLocation'
|
||||
},
|
||||
{
|
||||
knowledgeId: 8,
|
||||
label: 'Anonymity',
|
||||
label: 'eventType.anonymity',
|
||||
iconUrl: 'images/knowledge-base-logo/anonymity.png',
|
||||
desc: 'Communication system that conceals users\' identities and activities to protect privacy and prevent tracking or surveillance. This database provides lists of Tor nodes, I2P nodes, obfs4, etc.'
|
||||
},
|
||||
// 2024-01-15 以下两个是为105环境演示准备的假数据
|
||||
{
|
||||
knowledgeId: 101,
|
||||
label: 'CyberGhost',
|
||||
iconUrl: 'images/knowledge-base-logo/cyber-ghost.png',
|
||||
desc: 'CyberGhost is a VPN service used to unblock sites and browse privately and anonymously.'
|
||||
},
|
||||
{
|
||||
knowledgeId: 102,
|
||||
label: 'HotSpotshield VPN',
|
||||
iconUrl: 'images/knowledge-base-logo/hospot-vpn.png',
|
||||
desc: 'Hotspot Shield is a public VPN service, providing\n' +
|
||||
'a secure proxy connection through an encrypted\n' +
|
||||
'channel between your device and the target\n' +
|
||||
'website, using VPN technology.'
|
||||
desc: 'knowledgeBase.desc.anonymity'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -13,17 +13,11 @@ export function getMillisecond (time) {
|
||||
ms = window.$dayJs.tz(new Date(time)).valueOf()
|
||||
} else if (_.isNumber(time)) {
|
||||
const timeStr = _.toString(time)
|
||||
/* const difference = timeStr.length - 13
|
||||
const difference = timeStr.length - 13
|
||||
if (difference >= 0) {
|
||||
ms = window.$dayJs.tz(new Date(Number(timeStr.slice(0, 13)))).valueOf()
|
||||
} else {
|
||||
ms = window.$dayJs.tz(new Date(Math.floor(time * (10 ** (0 - difference))))).valueOf()
|
||||
} */
|
||||
// 判断9位和10位数为秒,12位和13位为毫秒。其他位数不做处理
|
||||
if (timeStr.length === 9 || timeStr.length === 10) {
|
||||
ms = window.$dayJs.tz(new Date(Number(time * 1000))).valueOf()
|
||||
} else {
|
||||
ms = window.$dayJs.tz(new Date(Number(time))).valueOf()
|
||||
}
|
||||
} else if (_.isString(time)) {
|
||||
try {
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>-->
|
||||
<div class="line-select-reference-line">
|
||||
<span>{{$t('network.referenceLine')}}:</span>
|
||||
<span>{{$t('network.referenceLine')}} : </span>
|
||||
<div class="line-select__operation">
|
||||
<el-select
|
||||
size="mini"
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
</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>
|
||||
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></domain-name-resolution>
|
||||
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" @checkTag="setTag" />
|
||||
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
|
||||
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
|
||||
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></open-port>
|
||||
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></behavior-pattern>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -58,12 +58,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
timer: null,
|
||||
// 最近一天的时间
|
||||
oneDayTimeFilter: {
|
||||
startTime: window.$dayJs.tz().valueOf() - 1440 * 60 * 1000,
|
||||
endTime: window.$dayJs.tz().valueOf()
|
||||
}
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -114,21 +109,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entity.entityName
|
||||
// startTime: getSecond(this.oneDayTimeFilter.startTime),
|
||||
// endTime: getSecond(this.oneDayTimeFilter.endTime)
|
||||
}
|
||||
|
||||
const url = this.getUrlByEntityType(this.entity.entityType)
|
||||
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: params })
|
||||
const openPort = axios.get(url, { params: params })
|
||||
// const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
|
||||
// const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
|
||||
Promise.all([informationAggregation, openPort]).then(response => {
|
||||
if (response[0].status === 200) {
|
||||
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: this.getParamsByTabType(entityDetailTabsName.informationAggregation) })
|
||||
const openPort = axios.get(url, { params: this.getParamsByTabType(entityDetailTabsName.openPort) })
|
||||
const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.securityEvent) })
|
||||
const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.performanceEvent) })
|
||||
|
||||
Promise.allSettled([informationAggregation, openPort, security, performance]).then(response => {
|
||||
const informationAggregationResponse = response[0].value
|
||||
if (informationAggregationResponse.status === 200) {
|
||||
const list = []
|
||||
response[0].data.data.result.forEach(r => {
|
||||
informationAggregationResponse.data.data.result.forEach(r => {
|
||||
Object.keys(r).forEach(k => {
|
||||
const aggregation = {
|
||||
createTime: r[k].createTime,
|
||||
@@ -151,44 +142,42 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.initSetTag(entityDetailTabsName.informationAggregation, list.length)
|
||||
}
|
||||
if (response[1].status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.openPort, response[1].data.data.result.length)
|
||||
const openPortResponse = response[1].value
|
||||
if (openPortResponse.status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.openPort, openPortResponse.data.data.result.length)
|
||||
}
|
||||
// if (response[2].status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.securityEvent, response[2].data.data.result.length)
|
||||
const securityResponse = response[2].value
|
||||
if (securityResponse.status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, securityResponse.data.data.result.length)
|
||||
}
|
||||
// let performanceResponse = response[3].value
|
||||
// if (performanceResponse.status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.performanceEvent, performanceResponse.data.data.result.length)
|
||||
// }
|
||||
// if (response[3].status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.performanceEvent, response[3].data.data.result.length)
|
||||
// }
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, 0)
|
||||
this.initSetTag(entityDetailTabsName.performanceEvent, 0)
|
||||
if (this.entity.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entity.entityName === '218.77.183.150') {
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, 3)
|
||||
this.initSetTag(entityDetailTabsName.performanceEvent, 1)
|
||||
}
|
||||
})
|
||||
|
||||
const relatedEntityParams = this.getParamsByTabType(entityDetailTabsName.relatedEntity)
|
||||
// 域名解析
|
||||
if (this.entity.entityType === 'app') {
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: relatedEntityParams })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: relatedEntityParams })
|
||||
this.promiseData(ipsOfApp, domainsOfApp)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'ip') {
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
|
||||
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: params })
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: relatedEntityParams })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: relatedEntityParams })
|
||||
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: relatedEntityParams })
|
||||
this.promiseData(appsOfIp, domainsOfIp, behaviorPattern)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'domain') {
|
||||
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: params })
|
||||
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: params })
|
||||
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: params })
|
||||
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: relatedEntityParams })
|
||||
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: relatedEntityParams })
|
||||
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: relatedEntityParams })
|
||||
this.promiseData(appsOfDomain, ipsOfDomain, fqdnsOfDomain)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="cn-detection__case entity-detail-performance">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header" style="padding-bottom: 0">
|
||||
<div class="cn-detection__header">
|
||||
<span
|
||||
:test-id="`severity-color-block${index}`"
|
||||
class="detection-event-severity-color-block"
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span :test-id="`severity${index}`">{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
|
||||
<span :test-id="`severity${index}`">{{ changeSecurity(item.eventSeverity) }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
@@ -38,11 +38,6 @@
|
||||
<span>{{ $t('overall.duration') }} : </span>
|
||||
<span :test-id="`duration-time${index}`">{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-traffic-overview"></i>
|
||||
<span>{{ $t('entity.detail.anomaly') }} : </span>
|
||||
<div id="anomalyChart" style="height: 20px; width: 100px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -55,20 +50,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, unitTypes } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import ChartError from '@/components/common/Error'
|
||||
import { reverseSortBy, sortBy, toUpperCaseByString } from '@/utils/tools'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { markRaw } from 'vue'
|
||||
import { metricOption } from '@/views/detections/options/detectionOptions'
|
||||
import * as echarts from 'echarts'
|
||||
import _ from 'lodash'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'PerformanceEvent',
|
||||
@@ -86,79 +78,38 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.performanceEvent
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName,
|
||||
chartOption: metricOption
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initData()
|
||||
/*this.isNoData = true
|
||||
/*
|
||||
this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)*/
|
||||
}, 200) */
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
|
||||
if (this.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entityName === '218.77.183.150') {
|
||||
setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
this.isNoData = false
|
||||
this.eventList = [
|
||||
{
|
||||
"serverIp": "1.1.1.1",
|
||||
"domain": "www.baidu.com",
|
||||
"appName": "ab",
|
||||
"eventSeverity": "critical",
|
||||
"eventType": "Http error",
|
||||
"durationMs": 840000,
|
||||
"startTime": new Date().getTime() - 1957 * 1000,
|
||||
"endTime": 2222222222
|
||||
}
|
||||
]
|
||||
this.metricList = [
|
||||
[new Date().getTime() / 1000 - 2677, 2],
|
||||
[new Date().getTime() / 1000 - 2557, 3],
|
||||
[new Date().getTime() / 1000 - 2437, 2],
|
||||
[new Date().getTime() / 1000 - 2317, 7],
|
||||
[new Date().getTime() / 1000 - 2197, 8],
|
||||
[new Date().getTime() / 1000 - 2077, 38],
|
||||
[new Date().getTime() / 1000 - 1857, 12],
|
||||
[new Date().getTime() / 1000 - 1637, 8],
|
||||
[new Date().getTime() / 1000 - 1517, 7],
|
||||
[new Date().getTime() / 1000 - 1277, 3],
|
||||
[new Date().getTime() / 1000 - 1157, 1],
|
||||
[new Date().getTime() / 1000 - 1037, 2]
|
||||
]
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 1)
|
||||
this.$nextTick(() => {
|
||||
this.initChart()
|
||||
})
|
||||
}, 200)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.isNoData = true
|
||||
this.toggleLoading(false)
|
||||
this.eventList = []
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
}, 200)
|
||||
/*axios.get(`${api.entity.performance}/${this.entityType}`, {params: params}).then(response => {
|
||||
axios.get(`${api.entity.performance}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
@@ -176,8 +127,7 @@ export default {
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})*/
|
||||
}
|
||||
})
|
||||
},
|
||||
httpError (e) {
|
||||
this.isNoData = false
|
||||
@@ -185,18 +135,17 @@ export default {
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
},
|
||||
initChart () {
|
||||
this.metricChart = markRaw(echarts.init(document.getElementById('anomalyChart')))
|
||||
this.chartOptionMetric = _.cloneDeep(this.chartOption)
|
||||
this.chartOptionMetric.series[0].data = this.metricList.slice(0, 4).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
|
||||
this.chartOptionMetric.series[1].data = this.metricList.slice(3, 9).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
|
||||
this.chartOptionMetric.series[2].data = this.metricList.slice(8, 11).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
|
||||
|
||||
this.chartOptionMetric.series.forEach(item => {
|
||||
item.name = 'Http error'
|
||||
})
|
||||
|
||||
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
|
||||
changeSecurity (value) {
|
||||
if (value) {
|
||||
const obj = securityLevel.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:key="item.eventId">
|
||||
<div class="cn-detection--list">
|
||||
<div class="cn-detection__case entity-detail-security">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__icon"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header">
|
||||
<span
|
||||
@@ -25,7 +25,7 @@
|
||||
<span class="circle"></span>
|
||||
<i class="cn-icon cn-icon-attacked"></i>
|
||||
<span :test-id="`victim-ip${index}`">{{ item.victimIp || '-' }}</span>
|
||||
<div class="domain">{{ item.domain }}</div>
|
||||
<div class="domain">{{ item.victimDomain }}</div>
|
||||
</div>
|
||||
<div class="cn-detection__body">
|
||||
<div class="body__basic-info">
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span>{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
|
||||
<span>{{ changeSecurity(item.eventSeverity) }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.eventType">
|
||||
<i class="cn-icon cn-icon-event-type"></i>
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
<span>{{ $t('detection.list.startTime') }} : </span>
|
||||
<span>{{ dateFormatByAppearance(parseFloat(item.startTime)) || '-' }}</span>
|
||||
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-duration"></i>
|
||||
@@ -76,8 +76,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
@@ -85,6 +85,7 @@ import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'SecurityEvent',
|
||||
@@ -102,110 +103,38 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.securityEvent
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName
|
||||
entityName,
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initData()
|
||||
/*this.isNoData = true
|
||||
/*
|
||||
this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)*/
|
||||
}, 200) */
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
if (this.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entityName === '218.77.183.150') {
|
||||
setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
this.isNoData = false
|
||||
this.eventList = [
|
||||
{
|
||||
eventId: '1717034000326447105',
|
||||
eventType: 'Command and Control',
|
||||
eventName: 'Mirai',
|
||||
eventKey: '5,26.26.26.1,192.168.38.73',
|
||||
ruleId: '5',
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: '1',
|
||||
eventSeverity: 'critical',
|
||||
offenderIp: '119.102.149.177',
|
||||
victimIp: '218.77.183.150',
|
||||
domain: 'hqzc.wssp.hainan.gov.cn',
|
||||
app: '',
|
||||
startTime: new Date().getTime() - 3600 * 1000,
|
||||
endTime: '1698207720',
|
||||
durationMs: 1613000,
|
||||
matchTimes: '1',
|
||||
status: '1',
|
||||
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
|
||||
},
|
||||
{
|
||||
eventId: '1717034000326447105',
|
||||
eventType: 'Command and Control',
|
||||
eventName: 'Bashlite',
|
||||
eventKey: '5,26.26.26.1,192.168.38.73',
|
||||
ruleId: '5',
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: '1',
|
||||
eventSeverity: 'critical',
|
||||
offenderIp: '142.4.196.195',
|
||||
victimIp: '218.77.183.150',
|
||||
domain: 'hqzc.wssp.hainan.gov.cn',
|
||||
app: '',
|
||||
startTime: new Date().getTime() - 1600 * 1000,
|
||||
endTime: '1698207720',
|
||||
durationMs: 1285000,
|
||||
matchTimes: '1',
|
||||
status: '1',
|
||||
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
|
||||
},
|
||||
{
|
||||
eventId: '1717034000326447105',
|
||||
eventType: 'Command and Control',
|
||||
eventName: 'Mirai',
|
||||
eventKey: '5,26.26.26.1,192.168.38.73',
|
||||
ruleId: '5',
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: '1',
|
||||
eventSeverity: 'critical',
|
||||
offenderIp: '103.119.112.54',
|
||||
victimIp: '218.77.183.150',
|
||||
domain: 'hqzc.wssp.hainan.gov.cn',
|
||||
app: '',
|
||||
startTime: new Date().getTime() - 2600 * 1000,
|
||||
endTime: '1698207720',
|
||||
durationMs: 2280000,
|
||||
matchTimes: '1',
|
||||
status: '1',
|
||||
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
|
||||
}
|
||||
]
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 3)
|
||||
}, 200)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.isNoData = true
|
||||
this.toggleLoading(false)
|
||||
this.eventList = []
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
}, 200)
|
||||
/*axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
|
||||
axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
@@ -223,14 +152,25 @@ export default {
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})*/
|
||||
}
|
||||
})
|
||||
},
|
||||
httpError (e) {
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
},
|
||||
changeSecurity (value) {
|
||||
if (value) {
|
||||
const obj = securityLevel.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import { api } from '@/utils/api'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { storageKey, ZH } from '@/utils/constants'
|
||||
import PopoverContent from './LinkDirectionGrid/PopoverContent'
|
||||
import { computeScore } from '@/utils/tools'
|
||||
import axios from 'axios'
|
||||
@@ -156,11 +156,9 @@ export default {
|
||||
})
|
||||
|
||||
// 一行如果无数据,则删除该行,默认10*10矩阵
|
||||
const rowXIndex = 0
|
||||
this.handleXRowNoData(linkGridData, rowXIndex)
|
||||
this.handleXRowNoData(linkGridData, 0)
|
||||
// 一列如果无数据,则删除该列,默认10*10矩阵
|
||||
const rowYIndex = 0
|
||||
this.handleYRowNoData(linkGridData, rowYIndex)
|
||||
this.handleYRowNoData(linkGridData, 0)
|
||||
this.isLinkNoData = linkGridData.length === 0
|
||||
this.linkGridData = linkGridData
|
||||
}
|
||||
@@ -178,9 +176,9 @@ export default {
|
||||
// 接口数据乱序,根据入方向排序,再根据同个入方向下的出方向进行排序
|
||||
nextLinkData.sort((a, b) => {
|
||||
if (a.inLinkDirection !== b.inLinkDirection) {
|
||||
return a.inLinkDirection.localeCompare(b.inLinkDirection, 'zh')
|
||||
return a.inLinkDirection.localeCompare(b.inLinkDirection, ZH)
|
||||
}
|
||||
return a.outLinkDirection.localeCompare(b.outLinkDirection, 'zh')
|
||||
return a.outLinkDirection.localeCompare(b.outLinkDirection, ZH)
|
||||
})
|
||||
|
||||
this.isNextNoData = nextLinkData.length === 0
|
||||
@@ -188,9 +186,9 @@ export default {
|
||||
// 链路下一跳数据
|
||||
let nextGridData = []
|
||||
const nextGridTemplate = [
|
||||
{ linkId: 'Hundredgige2', nextHop: 'City2', out: [] },
|
||||
{ linkId: 'Hundredgige1', nextHop: 'City1', out: [] },
|
||||
{ linkId: 'Hundredgige4', nextHop: 'City3', out: [] }
|
||||
{ linkId: 'Hundredgige2', nextHop: '太原', out: [] },
|
||||
{ linkId: 'Hundredgige1', nextHop: '西安', out: [] },
|
||||
{ linkId: 'Hundredgige4', nextHop: '西宁', out: [] }
|
||||
]
|
||||
nextGridData = JSON.parse(JSON.stringify(nextGridTemplate))
|
||||
nextGridData.forEach(link => {
|
||||
@@ -257,11 +255,9 @@ export default {
|
||||
})
|
||||
|
||||
// 一行如果无数据,则删除该行,默认3*3矩阵
|
||||
const rowXIndex = 0
|
||||
this.handleXRowNoData(nextGridData, rowXIndex)
|
||||
this.handleXRowNoData(nextGridData, 0)
|
||||
// 一列如果无数据,则删除该列,默认3*3矩阵
|
||||
const rowYIndex = 0
|
||||
this.handleYRowNoData(nextGridData, rowYIndex)
|
||||
this.handleYRowNoData(nextGridData, 0)
|
||||
|
||||
this.isNextNoData = nextGridData.length === 0
|
||||
this.nextGridData = nextGridData
|
||||
@@ -357,7 +353,7 @@ export default {
|
||||
* @param index
|
||||
*/
|
||||
handleXRowNoData (data, index) {
|
||||
if (data) {
|
||||
if (data && data.length > 0) {
|
||||
const item = data[index]
|
||||
let tempList = []
|
||||
if (item) {
|
||||
@@ -380,7 +376,7 @@ export default {
|
||||
*/
|
||||
handleYRowNoData (data, index) {
|
||||
const rowList = []
|
||||
if (data) {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach(item => {
|
||||
if (item.out[index]) {
|
||||
if (item.out[index].noData) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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, [])
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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') }} : </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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -257,7 +257,8 @@ export const pieChartOption4 = {
|
||||
show: false,
|
||||
position: 'middle',
|
||||
formatter: '{b}: {c}'
|
||||
}
|
||||
},
|
||||
barMinHeight: 5
|
||||
}],
|
||||
animation: false
|
||||
}
|
||||
|
||||
@@ -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 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>
|
||||
<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 class="filter__body-item-right">{{ data.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>
|
||||
</el-collapse-transition>
|
||||
<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: {
|
||||
|
||||
@@ -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,71 +157,67 @@ 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.参数值数量从多到1,operator由'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(() => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -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 => {
|
||||
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)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 || ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
{{ ruleId ? $t('detection.editEventPolicies') : $t('detection.createEventPolicies') }}
|
||||
</div>
|
||||
|
||||
<!--第一步:General Settings-->
|
||||
<div class="detection-form-content">
|
||||
<!--第一步:General Settings-->
|
||||
<div class="detection-form-collapse">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="1">
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<!--第三步:Trigger-->
|
||||
<div class="detection-form-collapse">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse v-model="activeNames" class="policy-form-trigger">
|
||||
<el-collapse-item name="3">
|
||||
<template #title>
|
||||
<div class="form-collapse-header">
|
||||
@@ -53,7 +53,7 @@
|
||||
</template>
|
||||
|
||||
<div class="form-collapse-content margin-t-18">
|
||||
<el-form v-if="language==='en'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<el-form v-if="language===EN" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<div class="trigger-block-item margin-b-10">
|
||||
<div>At least</div>
|
||||
<el-form-item prop="atLeast">
|
||||
@@ -94,7 +94,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<el-form v-if="language==='cn'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<el-form v-if="language===ZH" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<div class="trigger-block-item margin-b-10">
|
||||
在
|
||||
<el-form-item prop="interval">
|
||||
@@ -136,18 +136,20 @@
|
||||
不活跃将重置计数
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="form-setting__btn1">
|
||||
<div class="btn1">
|
||||
<el-button @click="createPolicy('')">{{ $t('overall.save') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="createPolicy('enabled')">{{ $t('overall.save') }} & {{ $t('detection.create.enablePolicy') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="policy-form__footer">
|
||||
<div class="policy-form__footer__btn">
|
||||
<div class="btn1">
|
||||
<el-button @click="cancel">{{ $t('overall.cancel') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="createPolicy">{{ $t('overall.save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -160,7 +162,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import { getDurationsTimeByType, getTimeByDurations } from '@/utils/date-util'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { storageKey, detectionUnitList } from '@/utils/constants'
|
||||
import { storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'DetectionForm',
|
||||
@@ -257,7 +259,9 @@ export default {
|
||||
intervalList: [],
|
||||
editObj: {},
|
||||
isComplete: true, // 参数完整标识,默认完整(照顾编辑模式),false即不完整
|
||||
language: 'en'
|
||||
language: EN,
|
||||
ZH,
|
||||
EN
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -266,10 +270,10 @@ export default {
|
||||
Loading
|
||||
},
|
||||
mounted () {
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
if (this.language === 'en') {
|
||||
this.language = localStorage.getItem(storageKey.language) || EN
|
||||
if (this.language === EN) {
|
||||
this.intervalList = detectionUnitList.intervalList
|
||||
} else if (this.language === 'cn') {
|
||||
} else if (this.language === ZH) {
|
||||
this.intervalList = detectionUnitList.intervalListCN
|
||||
}
|
||||
this.getDetailInfo()
|
||||
@@ -330,7 +334,7 @@ export default {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
path: '/detection/policy',
|
||||
query: {
|
||||
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
@@ -367,7 +371,7 @@ export default {
|
||||
}
|
||||
},
|
||||
/** 创建policy */
|
||||
createPolicy (flag) {
|
||||
createPolicy () {
|
||||
const settingLen = Object.keys(this.settingObj).length
|
||||
const ruleLen = Object.keys(this.ruleObj).length
|
||||
|
||||
@@ -376,9 +380,6 @@ export default {
|
||||
if (valid) {
|
||||
// 最终提交form
|
||||
const formObj = this.$_.cloneDeep({ ...this.settingObj, ruleConfig: JSON.stringify(this.ruleObj), ruleTrigger: this.triggerObj })
|
||||
if (flag) {
|
||||
formObj.status = 1
|
||||
}
|
||||
// 将时间转为参数所需,如5分钟转为PT5M
|
||||
formObj.ruleTrigger.resetInterval = getDurationsTimeByType(formObj.ruleTrigger.resetInterval, formObj.ruleTrigger.resetIntervalVal)
|
||||
formObj.ruleTrigger.interval = getDurationsTimeByType(formObj.ruleTrigger.interval, formObj.ruleTrigger.intervalVal)
|
||||
@@ -402,7 +403,7 @@ export default {
|
||||
})
|
||||
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
path: '/detection/policy',
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
@@ -418,7 +419,6 @@ export default {
|
||||
this.myLoading = false
|
||||
})
|
||||
} else {
|
||||
console.log('进来')
|
||||
this.myLoading = true
|
||||
axios.put(api.detection.create.create, formObj).then(response => {
|
||||
if (response.status === 200) {
|
||||
@@ -428,12 +428,18 @@ export default {
|
||||
message: this.$t('tip.saveSuccess')
|
||||
})
|
||||
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
query: {
|
||||
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: '/detection/policy',
|
||||
query: queryInfo
|
||||
})
|
||||
} else {
|
||||
console.error(response.data.message)
|
||||
@@ -491,7 +497,134 @@ export default {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeSeconds') }
|
||||
}
|
||||
}
|
||||
},
|
||||
cancel () {
|
||||
const { query } = this.$route
|
||||
const queryInfo = {
|
||||
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
}
|
||||
if (query.name && query.id) {
|
||||
queryInfo.ruleId = query.id
|
||||
queryInfo.name = this.settingObj.name
|
||||
}
|
||||
this.$refs.form3.validate(valid => {
|
||||
if (this.settingObj.settingNoContinue || this.ruleObj.settingNoContinue || !valid) {
|
||||
this.$confirm(this.$t('tip.leavePage'), {
|
||||
confirmButtonText: this.$t('tip.confirm'),
|
||||
cancelButtonText: this.$t('overall.cancel'),
|
||||
message: this.$t('tip.leavePageTips'),
|
||||
title: this.$t('tip.leavePage'),
|
||||
type: 'warning',
|
||||
iconClass: 'width:0px;height:0px;',
|
||||
customClass: 'del-model'
|
||||
}).then(() => {
|
||||
this.$router.push({
|
||||
path: '/detection/policy',
|
||||
query: queryInfo
|
||||
})
|
||||
}).catch(() => {})
|
||||
} else {
|
||||
this.$router.push({
|
||||
path: '/detection/policy',
|
||||
query: queryInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.del-model {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0 !important;
|
||||
width: 480px !important;
|
||||
height: 190px;
|
||||
|
||||
.el-message-box__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 42px;
|
||||
background: #F7F7F7;
|
||||
box-shadow: 0 1px 0 0 rgba(53, 54, 54, 0.08);
|
||||
padding-left: 20px;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
|
||||
.el-message-box__headerbtn {
|
||||
display: flex !important;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
padding-right: 5px !important;
|
||||
|
||||
i {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__title {
|
||||
font-size: 14px !important;
|
||||
color: #353636;
|
||||
letter-spacing: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__content {
|
||||
height: 96px;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
letter-spacing: 0;
|
||||
line-height: 22px;
|
||||
font-weight: 400;
|
||||
padding-top: 8px;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
|
||||
.el-message-box__message {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__btns {
|
||||
height: 52px;
|
||||
border-top: 1px solid #eee;
|
||||
box-shadow: inset 0 -1px 0 0 rgba(0, 0, 0, 0.07);
|
||||
padding: 11px 0 12px !important;
|
||||
|
||||
.el-button--small {
|
||||
padding: 8px 21px !important;
|
||||
line-height: 12px;
|
||||
font-family: NotoSansHans-Medium !important;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.el-button:nth-child(1) {
|
||||
margin-right: 20px;
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
color: #353636;
|
||||
}
|
||||
|
||||
.el-button:nth-child(2) {
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
margin-right: 20px;
|
||||
margin-left: 0 !important;
|
||||
background-color: #2d8cf0;
|
||||
border-color: #2d8cf0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
ref="dataTable"
|
||||
:data="tableData"
|
||||
height="100%"
|
||||
tooltip-effect="light"
|
||||
border
|
||||
empty-text=" "
|
||||
@header-dragend="dragend"
|
||||
@@ -31,6 +32,7 @@
|
||||
:sortable="item.sortable"
|
||||
:width="`${item.width}`"
|
||||
class="data-column"
|
||||
:show-overflow-tooltip="['library'].indexOf(item.prop) > -1"
|
||||
>
|
||||
<template #header>
|
||||
<span class="data-column__span">{{ item.label }}</span>
|
||||
@@ -82,6 +84,7 @@ import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
import _ from 'lodash'
|
||||
import { detectionUnitList } from '@/utils/constants'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'DetectionTable',
|
||||
@@ -89,6 +92,9 @@ export default {
|
||||
isNoData: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
policyDetail: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
mixins: [table],
|
||||
@@ -146,6 +152,17 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const { query } = useRoute()
|
||||
// 初始化勾选状态
|
||||
if (query.name && query.ruleId && this.policyDetail) {
|
||||
this.selectionChange([this.policyDetail])
|
||||
const timer = setTimeout(() => {
|
||||
this.$refs.dataTable.toggleAllSelection([this.policyDetail])
|
||||
clearTimeout(timer)
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tableData: {
|
||||
immediate: true,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ export const pieForSeverity = {
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
type: 'plain',
|
||||
left: '60%',
|
||||
left: '44%',
|
||||
top: 'middle',
|
||||
icon: 'circle',
|
||||
itemWidth: 10, // 设置宽度
|
||||
@@ -203,7 +203,7 @@ export const pieForSeverity = {
|
||||
type: 'pie',
|
||||
selectedMode: 'single',
|
||||
radius: ['43%', '65%'],
|
||||
center: ['30%', '50%'],
|
||||
center: ['22%', '50%'],
|
||||
data: [],
|
||||
label: {
|
||||
show: false
|
||||
@@ -308,10 +308,6 @@ export const metricOption = {
|
||||
str += `<span class="cn-chart-tooltip-value">
|
||||
${unitConvert(item.data[1], unitTypes.time).join(' ')}
|
||||
</span>`
|
||||
} else if (item.seriesName === 'Http error') {
|
||||
str += `<span class="cn-chart-tooltip-value">
|
||||
${unitConvert(item.data[1], unitTypes.number, '', '', 0).join(' ')}
|
||||
</span>`
|
||||
} else {
|
||||
str += `<span class="cn-chart-tooltip-value">
|
||||
${unitConvert(item.data[1], unitTypes.percent, '', '', 0).join(' ')}
|
||||
|
||||
@@ -243,7 +243,7 @@ export default {
|
||||
},
|
||||
goDetail (type, name) {
|
||||
const { href } = this.$router.resolve({
|
||||
path: '/entityDetail',
|
||||
path: '/entity/detail',
|
||||
query: {
|
||||
entityType: type,
|
||||
entityName: name
|
||||
|
||||
@@ -274,7 +274,7 @@ export default {
|
||||
},
|
||||
goDetail (type, name) {
|
||||
const { href } = this.$router.resolve({
|
||||
path: '/entityDetail',
|
||||
path: '/entity/detail',
|
||||
query: {
|
||||
entityType: type,
|
||||
entityName: name
|
||||
|
||||
@@ -235,7 +235,7 @@ export default {
|
||||
},
|
||||
goDetail (type, name) {
|
||||
const { href } = this.$router.resolve({
|
||||
path: '/entityDetail',
|
||||
path: '/entity/detail',
|
||||
query: {
|
||||
entityType: type,
|
||||
entityName: name
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<span class="row__content--link">{{detection.victimIp}}</span> communicated with <span class="row__content--link">{{detection.offenderIp}}</span> 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>
|
||||
<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进行判断,若为domain,malware信息从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 () {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }} </span>results,IP
|
||||
<span>{{ summaryCount.ipCount }}</span>,Domain
|
||||
<span>{{ summaryCount.totalCount }} </span>{{$t('overall.results')}},IP
|
||||
<span>{{ summaryCount.ipCount }}</span>,{{$t('overall.domain')}}
|
||||
<span>{{ summaryCount.domainCount }}</span>,APP
|
||||
<span>{{ summaryCount.appCount }}</span>
|
||||
|
||||
<span class="entity-hide-entity" v-if="q">
|
||||
<el-checkbox
|
||||
v-model="isHideRelatedEntities"
|
||||
:label="$t('entity.hideRelatedEntities')"
|
||||
:disabled="listData.length===0"
|
||||
@change="hideRelatedEntities"
|
||||
size="large" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<entity-list
|
||||
style="width: 100%;"
|
||||
:list-data="listData"
|
||||
:list-mode="listMode"
|
||||
:keywordList="keywordList"
|
||||
:pageObj="pageObj"
|
||||
:time-filter="timeFilter"
|
||||
@pageSize="pageSize"
|
||||
@@ -71,8 +89,7 @@
|
||||
<div class="right-label">{{ $t('network.total') }}</div>
|
||||
<div class="right-label-loading">
|
||||
<loading :loading="loadingApp" size="small"></loading>
|
||||
<!-- <div class="right-value">{{ numberWithCommas(entityAppTotal) }}</div>-->
|
||||
<div class="right-value">837</div>
|
||||
<div class="right-value">{{ numberWithCommas(entityAppTotal) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -93,7 +110,7 @@
|
||||
<div class="entity-overview">
|
||||
<div class="overview-left">
|
||||
<span class="overview-left-loading">
|
||||
<span class="overview-left-loading-span">Domain</span>
|
||||
<span class="overview-left-loading-span">{{$t('overall.domain')}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="overview-right">
|
||||
@@ -102,8 +119,7 @@
|
||||
<div class="right-label">{{ $t('network.total') }}</div>
|
||||
<div class="right-label-loading">
|
||||
<loading :loading="loadingDomain" size="small"></loading>
|
||||
<!-- <div class="right-value">{{ numberWithCommas(entityDomainTotal) }}</div>-->
|
||||
<div class="right-value">1,032,544</div>
|
||||
<div class="right-value">{{ numberWithCommas(entityDomainTotal) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -133,8 +149,7 @@
|
||||
<div class="right-label">{{ $t('network.total') }}</div>
|
||||
<div class="right-label-loading">
|
||||
<loading :loading="loadingIp" size="small"></loading>
|
||||
<!-- <div class="right-value">{{ numberWithCommas(entityIpTotal) }}</div>-->
|
||||
<div class="right-value">1,900,804</div>
|
||||
<div class="right-value">{{ numberWithCommas(entityIpTotal) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -177,6 +192,8 @@ import Parser from '@/components/advancedSearch/meta/parser'
|
||||
import { handleErrorTip } from '@/components/advancedSearch/meta/error'
|
||||
import { columnList } from '@/utils/static-data'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { columnType } from '@/components/advancedSearch/meta/meta'
|
||||
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
|
||||
|
||||
export default {
|
||||
name: 'entity-explorer',
|
||||
@@ -184,7 +201,8 @@ export default {
|
||||
Loading,
|
||||
ExplorerSearch,
|
||||
EntityFilter,
|
||||
EntityList
|
||||
EntityList,
|
||||
DateTimeRange
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -203,13 +221,6 @@ export default {
|
||||
entityIpNew: '-',
|
||||
entityIpActive: '-',
|
||||
|
||||
// pageObj: {
|
||||
// pageNo: 1,
|
||||
// // 是否重置pageNo,在执行新搜索时是true
|
||||
// resetPageNo: true,
|
||||
// pageSize: defaultPageSize,
|
||||
// total: 0
|
||||
// },
|
||||
newFilterData: [
|
||||
{
|
||||
icon: 'cn-icon cn-icon-registration-country',
|
||||
@@ -287,12 +298,13 @@ export default {
|
||||
initFlag: true, // 初始化标志,避免初始化时pageSize和pageNo会调用搜索
|
||||
timer: null, // 初始化标志的延时器,需要销毁
|
||||
summaryCount: {
|
||||
total: 0,
|
||||
totalCount: 0,
|
||||
domainCount: 0,
|
||||
ipCount: 0,
|
||||
appCount: 0
|
||||
},
|
||||
loadingCount: false // 实体基数统计的loading
|
||||
loadingCount: false, // 实体基数统计的loading
|
||||
keywordList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -305,6 +317,13 @@ export default {
|
||||
},
|
||||
reload (s, e, v) {
|
||||
this.dateTimeRangeChange(s, e, v)
|
||||
const { startTime, endTime } = getNowTime(this.timeFilter.dateRangeValue)
|
||||
const newParam = {
|
||||
range: this.timeFilter.dateRangeValue,
|
||||
startTime: getSecond(startTime),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
this.reloadUrl(newParam)
|
||||
},
|
||||
// methods
|
||||
dateTimeRangeChange (s, e, v) {
|
||||
@@ -364,6 +383,7 @@ export default {
|
||||
this.q = ''
|
||||
this.metaList = []
|
||||
}
|
||||
this.getKeyword(param.keywordList)
|
||||
|
||||
// 参数q,避免切换页码时,地址栏参数q为空
|
||||
let urlQ = ''
|
||||
@@ -371,6 +391,8 @@ export default {
|
||||
urlQ = encodeURI(param.str)
|
||||
} else if (this.q) {
|
||||
urlQ = encodeURI(this.q)
|
||||
} else if (!this.q) {
|
||||
this.isHideRelatedEntities = false
|
||||
}
|
||||
|
||||
// 在非列表模式下选择tag模式,在地址栏输入内容时将mode添加到地址栏
|
||||
@@ -392,7 +414,7 @@ export default {
|
||||
if (!this.showList) {
|
||||
// 首页进入搜索时重载页面,视觉上进入列表页面
|
||||
this.$router.push({
|
||||
path: '/entityExplorer',
|
||||
path: '/entity',
|
||||
query: {
|
||||
listMode: this.listMode,
|
||||
q: urlQ,
|
||||
@@ -420,19 +442,21 @@ export default {
|
||||
},
|
||||
pageSize (val) {
|
||||
this.pageObj.pageSize = val
|
||||
const keywordList = this.getKeywordListByMetaList(this.metaList)
|
||||
if (this.initFlag) {
|
||||
if (val !== 20) {
|
||||
this.search({ metaList: this.metaList, q: this.q })
|
||||
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
|
||||
}
|
||||
} else {
|
||||
this.search({ metaList: this.metaList, q: this.q })
|
||||
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
|
||||
}
|
||||
},
|
||||
pageNo (val) {
|
||||
if (!this.initFlag) {
|
||||
this.pageObj.pageNo = val
|
||||
this.pageObj.resetPageNo = false
|
||||
this.search({ metaList: this.metaList, q: this.q })
|
||||
const keywordList = this.getKeywordListByMetaList(this.metaList)
|
||||
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
|
||||
}
|
||||
},
|
||||
// 点击上一页箭头
|
||||
@@ -492,8 +516,8 @@ export default {
|
||||
/** 新版查询filter数据 */
|
||||
queryFilterNew (params) {
|
||||
const queryParams = {
|
||||
// startTime: getSecond(params.startTime),
|
||||
// endTime: getSecond(params.endTime),
|
||||
startTime: getSecond(params.startTime),
|
||||
endTime: getSecond(params.endTime),
|
||||
resource: params.q || ''
|
||||
}
|
||||
this.loadingLeft = true
|
||||
@@ -551,16 +575,16 @@ export default {
|
||||
const queryParams = {
|
||||
pageSize: params.pageSize,
|
||||
pageNo: params.pageNo,
|
||||
// startTime: getSecond(params.startTime),
|
||||
// endTime: getSecond(params.endTime),
|
||||
resource: params.q || ''
|
||||
startTime: getSecond(params.startTime),
|
||||
endTime: getSecond(params.endTime),
|
||||
resource: params.q || '',
|
||||
hideRelated: this.isHideRelatedEntities
|
||||
}
|
||||
axios.get(api.entity.entityList.list, { params: queryParams }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.listData = []
|
||||
this.$nextTick(() => {
|
||||
this.listData = response.data.data.list
|
||||
this.pageObj.total = response.data.data.total
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response.data.message)
|
||||
@@ -573,19 +597,21 @@ export default {
|
||||
queryCount (params) {
|
||||
this.loadingCount = true
|
||||
const queryParams = {
|
||||
// startTime: getSecond(params.startTime),
|
||||
// endTime: getSecond(params.endTime),
|
||||
resource: params.q || ''
|
||||
startTime: getSecond(params.startTime),
|
||||
endTime: getSecond(params.endTime),
|
||||
resource: params.q || '',
|
||||
hideRelated: this.isHideRelatedEntities
|
||||
}
|
||||
axios.get(api.entity.entityList.summaryCount, { params: queryParams }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.summaryCount = response.data.data
|
||||
this.pageObj.total = response.data.data.totalCount
|
||||
} else {
|
||||
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
this.summaryCount = { totalCount: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
}
|
||||
}).catch(e => {
|
||||
console.log(e)
|
||||
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
console.error(e)
|
||||
this.summaryCount = { totalCount: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
}).finally(() => {
|
||||
this.loadingCount = false
|
||||
})
|
||||
@@ -678,11 +704,13 @@ export default {
|
||||
}
|
||||
}
|
||||
const parser = new Parser(columnList)
|
||||
const metaList = parser.parseStr(_.cloneDeep(str)).metaList
|
||||
const keywordList = this.getKeywordListByMetaList(metaList)
|
||||
const keyInfo = parser.comparedEntityKey(parser.handleEntityTypeByStr(str))
|
||||
if (keyInfo.isKey) {
|
||||
const errorList = parser.validateStr(keyInfo.key)
|
||||
if (_.isEmpty(errorList)) {
|
||||
this.search({ ...parser.parseStr(keyInfo.key), str: str })
|
||||
this.search({ ...parser.parseStr(keyInfo.key), str: str, keywordList: keywordList })
|
||||
} else {
|
||||
this.$message.error(handleErrorTip(errorList[0]))
|
||||
}
|
||||
@@ -730,10 +758,55 @@ export default {
|
||||
}).catch((e) => {
|
||||
}).finally(() => {
|
||||
})
|
||||
},
|
||||
getKeywordListByMetaList (metaList) {
|
||||
if (metaList) {
|
||||
const keywordList = []
|
||||
metaList.forEach(item => {
|
||||
if (item.column && item.column.type === columnType.fullText) {
|
||||
keywordList.push({ type: item.column.type, value: item.column.label })
|
||||
} else if (item.column && item.column.type === columnType.string) {
|
||||
keywordList.push({ type: item.column.type, value: item.value.value })
|
||||
}
|
||||
})
|
||||
return keywordList
|
||||
}
|
||||
},
|
||||
getKeyword (list) {
|
||||
if (list) {
|
||||
const metaList = JSON.parse(JSON.stringify(list))
|
||||
const keyList = []
|
||||
metaList.forEach(item => {
|
||||
if (item.value) {
|
||||
keyList.push({ type: item.type, value: this.getKeyValue(item.value) })
|
||||
}
|
||||
})
|
||||
this.keywordList = keyList
|
||||
} else {
|
||||
this.keywordList = []
|
||||
}
|
||||
},
|
||||
getKeyValue (str) {
|
||||
if (str[0] === "'" && str[str.length - 1] === "'") {
|
||||
str = str.substring(1, str.length)
|
||||
str = str.substring(0, str.length - 1)
|
||||
}
|
||||
if (str[0] === '%') {
|
||||
str = str.substring(1, str.length)
|
||||
}
|
||||
if (str[str.length - 1] === '%') {
|
||||
str = str.substring(0, str.length - 1)
|
||||
}
|
||||
return str
|
||||
},
|
||||
hideRelatedEntities (e) {
|
||||
this.isHideRelatedEntities = e
|
||||
this.reloadUrl({ hideRelated: e })
|
||||
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
|
||||
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getEntityIndexData()
|
||||
let { q, listMode } = this.$route.query
|
||||
|
||||
// 如果地址栏有listMode,即列表页,并非首页,则开始搜索
|
||||
@@ -744,7 +817,13 @@ export default {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
// %位置不为0,即内容包含非英文时
|
||||
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
let str1 = ''
|
||||
if (q) {
|
||||
str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
}
|
||||
if (q && q.indexOf('+') > -1) {
|
||||
q = q.replace('+', '')
|
||||
}
|
||||
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
@@ -754,6 +833,10 @@ export default {
|
||||
this.$store.commit('resetScoreBase')
|
||||
this.queryScoreBase()
|
||||
}
|
||||
|
||||
if (!this.showList) {
|
||||
this.getEntityIndexData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
timeFilter () {
|
||||
@@ -766,11 +849,11 @@ export default {
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
// 优先级:url > config.js > 默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.entity.list || 60)
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
} else {
|
||||
@@ -784,9 +867,11 @@ export default {
|
||||
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize,
|
||||
total: 0
|
||||
})
|
||||
const isHideRelatedEntities = ref(query.hideRelated ? JSON.parse(query.hideRelated) : false) // 隐藏相关实体,默认false不隐藏
|
||||
return {
|
||||
timeFilter,
|
||||
pageObj
|
||||
pageObj,
|
||||
isHideRelatedEntities
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
|
||||
@@ -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, '')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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') }} : </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') }} : </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') }} : </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') }} : </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') }} : </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') }} : </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,
|
||||
|
||||
@@ -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,10 +320,22 @@ export default {
|
||||
methods: {
|
||||
getMillisecond,
|
||||
dateFormatByAppearance,
|
||||
getQueryParams () {
|
||||
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 () {
|
||||
return {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="entity-detail-overview">
|
||||
<template v-if="entity.entityType === 'ip'">
|
||||
<ip-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></ip-overview>
|
||||
<ip-overview :entity="entity" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity"></ip-overview>
|
||||
</template>
|
||||
<template v-else-if="entity.entityType === 'domain'">
|
||||
<domain-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></domain-overview>
|
||||
<domain-overview :entity="entity" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity"></domain-overview>
|
||||
</template>
|
||||
<template v-else-if="entity.entityType === 'app'">
|
||||
<app-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></app-overview>
|
||||
<app-overview :entity="entity" :time-filter="timeFilter" :keywordList="keywordList" @reloadEntity="getEntity"></app-overview>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -22,7 +22,8 @@ export default {
|
||||
name: 'DetailOverview',
|
||||
props: {
|
||||
entity: Object,
|
||||
timeFilter: Object
|
||||
timeFilter: Object,
|
||||
keywordList: Array
|
||||
},
|
||||
components: {
|
||||
'domain-overview': Domain,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user