Compare commits
91 Commits
dev-23.10-
...
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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -101,6 +101,12 @@
|
||||
justify-content: flex-start;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
|
||||
.policy-form-item {
|
||||
.el-form-item__error {
|
||||
width: 260px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--mini .el-input__inner {
|
||||
@@ -124,6 +130,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.policy-form-trigger {
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--mini, .el-input--mini .el-input__inner {
|
||||
@@ -137,4 +149,13 @@
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.policy-form__footer {
|
||||
width: calc(100% + 40px);
|
||||
height: 60px;
|
||||
margin-left: -20px;
|
||||
box-shadow: 0 -1px 4px 0 rgba(0,0,0,0.10);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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__header {
|
||||
display: flex;
|
||||
flex: 0 0 32px;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
color: #666;
|
||||
//background-color: #F3F7FA;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
i {
|
||||
font-size: 12px;
|
||||
transition: all linear .1s;
|
||||
transform: rotate(0) translate(0, 2px);
|
||||
}
|
||||
i.arrow-rotate {
|
||||
transform: rotate(90deg) translate(2px, 3px);
|
||||
}
|
||||
.new-detection-filter-header-title {
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 600;
|
||||
}
|
||||
.new-detection-filter-icon {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 2px;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
}
|
||||
|
||||
.filter__body {
|
||||
padding: 5px 0 0 15px;
|
||||
|
||||
.el-checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
margin-right: 5px;
|
||||
.el-checkbox__label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter__checkbox-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.severity-color-block {
|
||||
width: 4px;
|
||||
height: 15px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.filter-case__header {
|
||||
padding-left: 8px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
background: #F7F7F7;
|
||||
box-shadow: 0 1px 0 0 rgba(226,229,236,1);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.new-detection-filter-title {
|
||||
display: flex;
|
||||
flex: 0 0 32px;
|
||||
align-items: center;
|
||||
padding-left: 27px;
|
||||
background-color: #EFF2F5;
|
||||
cursor: pointer;
|
||||
|
||||
.filter__header {
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
margin: 0 20px;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 600;
|
||||
margin: -10px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter__body {
|
||||
width: calc(100% - 30px);
|
||||
margin: 0 10px 0 20px;
|
||||
max-height: 265px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
.filter__body-item {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
.filter__body-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
.filter__body-item-left-index {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
text-align: center;
|
||||
background: #EFF2F5;
|
||||
border-radius: 2px;
|
||||
margin-right: 6px;
|
||||
font-family: NotoSansHans-Black;
|
||||
font-size: 9px;
|
||||
color: #96A2B0;
|
||||
font-weight: 900;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.filter__body-item-left-label {
|
||||
max-width: 180px;
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.filter__body-item-right {
|
||||
flex-shrink: 0;
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 12px;
|
||||
color: #717171;
|
||||
font-weight: 400;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-country-flag {
|
||||
width: 18px;
|
||||
height: 12px;
|
||||
margin-right: 6px;
|
||||
border: 1px solid #E8E8E8;
|
||||
}
|
||||
|
||||
.filter-show-more, .filter-no-show-more {
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
margin-left: 20px;
|
||||
color: #046ECA;
|
||||
user-select: none; // 禁止文本选中
|
||||
font-size: 12px;
|
||||
}
|
||||
.filter-no-show-more {
|
||||
cursor: not-allowed;
|
||||
color: rgba(16, 16, 16, 0.3);
|
||||
}
|
||||
|
||||
.filter-hr {
|
||||
width: calc(100% - 40px);
|
||||
margin-left: 20px;
|
||||
margin-top: 6px;
|
||||
height: 1px;
|
||||
background: #EFF2F5;
|
||||
//background: #000;
|
||||
}
|
||||
|
||||
.new-detection-filter-title {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
|
||||
@@ -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;
|
||||
@@ -1905,6 +1917,9 @@
|
||||
&.update-dialog__table--psiphon3 {
|
||||
height: calc(90vh - 190px - 200px - 50px - 10px);
|
||||
}
|
||||
&.update-dialog__table--system-user {
|
||||
height: calc(100% - 139px);
|
||||
}
|
||||
}
|
||||
.update-knowledge-form {
|
||||
.el-upload {
|
||||
|
||||
@@ -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,14 +565,17 @@ export default {
|
||||
const column = this.columnList.find(c => {
|
||||
return c.label === param.column
|
||||
})
|
||||
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
|
||||
// 不是在首位,则删除时顺带删除前一个index(and或or),否则顺带删除后一个index
|
||||
if (metaIndex > 0) {
|
||||
this.metaList.splice(metaIndex - 1, 2)
|
||||
} else if (this.metaList.length === 1) {
|
||||
this.metaList.splice(metaIndex, 1)
|
||||
} else {
|
||||
this.metaList.splice(metaIndex, 2)
|
||||
const obj = this.metaList.find(d => d.column && d.column.label === param.column && d.value && (d.value.value === `'${param.value[0]}'` || d.value.value === param.value[0]))
|
||||
if (obj) {
|
||||
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
|
||||
// 不是在首位,则删除时顺带删除前一个index(and或or),否则顺带删除后一个index
|
||||
if (metaIndex > 0) {
|
||||
this.metaList.splice(metaIndex - 1, 2)
|
||||
} else if (this.metaList.length === 1) {
|
||||
this.metaList.splice(metaIndex, 1)
|
||||
} else {
|
||||
this.metaList.splice(metaIndex, 2)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -622,6 +635,9 @@ export default {
|
||||
let { q } = this.$route.query
|
||||
if (q && !this.convertMetaList) {
|
||||
const parser = new Parser(this.columnList)
|
||||
if (q.indexOf('+') > -1) {
|
||||
q = q.replace('+', '')
|
||||
}
|
||||
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
|
||||
q = decodeURI(q)
|
||||
} else {
|
||||
@@ -650,6 +666,7 @@ export default {
|
||||
if (item.column && item.column.type === 'fullText') {
|
||||
item.operator.value = '='
|
||||
item.column.show = false
|
||||
item.column.isFullText = true
|
||||
item.operator.show = false
|
||||
const label = JSON.parse(JSON.stringify(item.column.label))
|
||||
item.column.label = parser.getEntityTypeByValue(item.column.label)
|
||||
|
||||
@@ -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,35 +1,39 @@
|
||||
<template>
|
||||
<div class="pagination" >
|
||||
<el-pagination
|
||||
ref="page"
|
||||
@size-change="size"
|
||||
@prev-click="prev"
|
||||
@next-click="next"
|
||||
@current-change="current"
|
||||
:current-page="pageObj.pageNo"
|
||||
:page-sizes="pageSizes?pageSizes:[20, 50, 100]"
|
||||
:page-size="Number(pageObj.pageSize)"
|
||||
:layout="layout"
|
||||
:total="pageObj.total"
|
||||
v-bind="bind"
|
||||
>
|
||||
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
|
||||
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
|
||||
:popper-class="popClass" @visible-change="popperVisible">
|
||||
<el-option v-for="(item, index) in pageSizes" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
<el-config-provider :locale="locale">
|
||||
<el-pagination
|
||||
ref="page"
|
||||
@size-change="size"
|
||||
@prev-click="prev"
|
||||
@next-click="next"
|
||||
@current-change="current"
|
||||
:current-page="pageObj.pageNo"
|
||||
:page-sizes="pageSizes?pageSizes:[20, 50, 100]"
|
||||
:page-size="Number(pageObj.pageSize)"
|
||||
:layout="layout"
|
||||
:total="pageObj.total"
|
||||
v-bind="bind"
|
||||
>
|
||||
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
|
||||
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
|
||||
:popper-class="popClass" @visible-change="popperVisible">
|
||||
<el-option v-for="(item, index) in pageSizes" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
|
||||
</el-pagination>
|
||||
</el-pagination>
|
||||
</el-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defaultPageSize, storageKey } from '@/utils/constants'
|
||||
|
||||
import { defaultPageSize, storageKey, ZH, EN } from '@/utils/constants'
|
||||
import { urlParamsHandler, overwriteUrl } from '@/utils/tools'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { parseInt } from 'lodash'
|
||||
import ElConfigProvider from 'element-plus'
|
||||
import cn from 'element-plus/lib/locale/lang/zh-cn'
|
||||
import en from 'element-plus/lib/locale/lang/en'
|
||||
|
||||
export default {
|
||||
name: 'pagination',
|
||||
@@ -60,9 +64,15 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const pageSize = ref(defaultPageSize)
|
||||
const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1))
|
||||
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
|
||||
let locale = en
|
||||
if (language === ZH) {
|
||||
locale = cn
|
||||
}
|
||||
return {
|
||||
pageSize,
|
||||
currentPageNo
|
||||
currentPageNo,
|
||||
locale
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
@@ -18,28 +18,30 @@
|
||||
<div v-if="dropdownFlag" class="date-range-panel">
|
||||
<el-row class="date-range-panel-top" style="position: relative">
|
||||
<el-col :span="16" class="date-range-panel-content date-range-panel-content-left">
|
||||
<div class="date-range-title" style="padding-left: 0">Absolute time range</div>
|
||||
<el-date-picker
|
||||
v-model="newDateValue"
|
||||
ref="newDatePicker"
|
||||
popper-class="my-date-picker"
|
||||
style="position: absolute;top: -53px;left: -536px;"
|
||||
:clearable="false"
|
||||
:default-time="defaultTime"
|
||||
:unlink-panels="true"
|
||||
type="datetimerange"
|
||||
@change="timeArrChange"
|
||||
/>
|
||||
<div class="content-title">From</div>
|
||||
<div class="date-range-title" style="padding-left: 0">{{$t('dateTime.absoluteTimeRange')}}</div>
|
||||
<el-config-provider :locale="locale">
|
||||
<el-date-picker
|
||||
v-model="newDateValue"
|
||||
ref="newDatePicker"
|
||||
popper-class="my-date-picker"
|
||||
style="position: absolute;top: -53px;left: -536px;"
|
||||
:clearable="false"
|
||||
:default-time="defaultTime"
|
||||
:unlink-panels="true"
|
||||
type="datetimerange"
|
||||
@change="timeArrChange"
|
||||
/>
|
||||
</el-config-provider>
|
||||
<div class="content-title">{{$t('dateTime.from')}}</div>
|
||||
<div @click="myDatePickerShow" tabindex="1" class="content-input">
|
||||
{{ dateFormatByAppearance(getMillisecond(myStartTime)) }}
|
||||
</div>
|
||||
<div class="content-title">To</div>
|
||||
<div class="content-title">{{$t('dateTime.to')}}</div>
|
||||
<div @click="myDatePickerShow" tabindex="2" class="content-input">
|
||||
{{ dateFormatByAppearance(getMillisecond(myEndTime)) }}
|
||||
</div>
|
||||
|
||||
<div class="date-range-title" style="padding-left: 0">Recently used absolute ranges</div>
|
||||
<div class="date-range-title" style="padding-left: 0">{{$t('dateTime.recentlyUsedRanges')}}</div>
|
||||
<div class="date-range-history">
|
||||
<div v-for="(item, index) in rangeHistoryArr" :key="index" class="date-range-history-item"
|
||||
@click="historyChange(item)">
|
||||
@@ -53,7 +55,7 @@
|
||||
:span="8"
|
||||
class="date-range-panel-content date-range-panel-content-right"
|
||||
style="border-left: 1px solid rgba(0,0,0,0.09);">
|
||||
<div class="date-range-title">Relatime time ranges</div>
|
||||
<div class="date-range-title">{{$t('dateTime.relativeTimeRanges')}}</div>
|
||||
<ul class="date-range-item">
|
||||
<li v-for="item in dateRangeArr"
|
||||
@click="quickChange(item.value)"
|
||||
@@ -79,9 +81,12 @@
|
||||
|
||||
<script>
|
||||
import { ref, computed, watch, reactive } from 'vue'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { EN, storageKey, ZH } from '@/utils/constants'
|
||||
import { getMillisecond, millTimestampDiffFromTz, timestampToList } from '@/utils/date-util'
|
||||
import { useStore } from 'vuex'
|
||||
import ElConfigProvider from 'element-plus'
|
||||
import cn from 'element-plus/lib/locale/lang/zh-cn'
|
||||
import en from 'element-plus/lib/locale/lang/en'
|
||||
|
||||
export default {
|
||||
name: 'DateTimeRange',
|
||||
@@ -105,6 +110,31 @@ export default {
|
||||
}
|
||||
},
|
||||
emits: ['change'],
|
||||
data () {
|
||||
return {
|
||||
dateRangeArr: [
|
||||
{ value: 5, name: this.$t('dateTime.last5Mins') }, // 'last 5 mins'
|
||||
{ value: 15, name: this.$t('dateTime.last15Mins') },
|
||||
{ value: 30, name: this.$t('dateTime.last30Mins') },
|
||||
{ value: 60, name: this.$t('dateTime.last1Hour') }, // dateTime.last1Hour
|
||||
{ value: 180, name: this.$t('dateTime.last3Hours') },
|
||||
{ value: 360, name: this.$t('dateTime.last6Hours') },
|
||||
{ value: 720, name: this.$t('dateTime.last12Hours') },
|
||||
{ value: 1440, name: this.$t('dateTime.last1Day') }, // dateTime.last2Days
|
||||
{ value: 2880, name: this.$t('dateTime.last2Days') }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showDetail () {
|
||||
let str = ''
|
||||
if (this.dateRangeValue !== -1) {
|
||||
const rangeItem = this.dateRangeArr.find(item => item.value === this.dateRangeValue)
|
||||
str = rangeItem ? rangeItem.name : this.dateRangeArr[0].name
|
||||
}
|
||||
return str
|
||||
}
|
||||
},
|
||||
setup (props, ctx) {
|
||||
// data
|
||||
const store = useStore()
|
||||
@@ -124,24 +154,12 @@ export default {
|
||||
const rangeHistory = ref(localStorage.getItem(storageKey.dataRangeHistory) ? JSON.parse(localStorage.getItem(storageKey.dataRangeHistory)) : [])
|
||||
const dateRangeValue = props.dateRange ? ref(props.dateRange) : ref(60)
|
||||
const isCustom = ref(dateRangeValue.value === -1)
|
||||
const dateRangeArr = [
|
||||
{ value: 5, name: 'last 5 mins' },
|
||||
{ value: 15, name: 'last 15 mins' },
|
||||
{ value: 30, name: 'last 30 mins' },
|
||||
{ value: 60, name: 'last 1 hour' },
|
||||
{ value: 180, name: 'last 3 hours' },
|
||||
{ value: 360, name: 'last 6 hours' },
|
||||
{ value: 720, name: 'last 12 hours' },
|
||||
{ value: 1440, name: 'last 1 day' },
|
||||
{ value: 2880, name: 'last 2 days' }
|
||||
]
|
||||
const dropdownFlag = ref(false)
|
||||
// 默认日历选择时间,即开始时间YYYY-MM-DD 00:00:00,结束时间YYYY-MM-DD 59:59:59
|
||||
const defaultTime = ref([
|
||||
new Date(2023, 1, 1, 0, 0, 0),
|
||||
new Date(2023, 1, 2, 23, 59, 59)
|
||||
])
|
||||
|
||||
// computed
|
||||
const utcStr = computed(() => {
|
||||
let str = 'UTC '
|
||||
@@ -159,13 +177,6 @@ export default {
|
||||
str += ':00 '
|
||||
return str
|
||||
})
|
||||
const showDetail = computed(() => {
|
||||
let str = ''
|
||||
if (dateRangeValue.value !== -1) {
|
||||
str = dateRangeArr.find(item => item.value === dateRangeValue.value).name
|
||||
}
|
||||
return str
|
||||
})
|
||||
const rangeHistoryArr = rangeHistory
|
||||
|
||||
// refs
|
||||
@@ -289,6 +300,12 @@ export default {
|
||||
})
|
||||
}
|
||||
}
|
||||
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
|
||||
let locale = en
|
||||
if (language === ZH) {
|
||||
locale = cn
|
||||
}
|
||||
|
||||
return {
|
||||
myStartTime,
|
||||
myEndTime,
|
||||
@@ -297,13 +314,11 @@ export default {
|
||||
utcStr,
|
||||
rangeEchartsData,
|
||||
address,
|
||||
dateRangeArr,
|
||||
defaultTime,
|
||||
dateRangeValue,
|
||||
isCustom,
|
||||
newDateValue,
|
||||
newDatePicker,
|
||||
showDetail,
|
||||
rangeHistory,
|
||||
rangeHistoryArr,
|
||||
getMillisecond,
|
||||
@@ -313,7 +328,8 @@ export default {
|
||||
timeArrChange,
|
||||
returnValue,
|
||||
quickChange,
|
||||
historyChange
|
||||
historyChange,
|
||||
locale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
breadcrumb[1].clickable = true
|
||||
}
|
||||
if (breadcrumb) {
|
||||
// panel菜单是否可以点击跳转:一级菜单不可点击,二级菜单可以点击
|
||||
if (breadcrumb[0] && breadcrumb[1] && breadcrumb[1].route &&
|
||||
breadcrumb[1].route.indexOf('/panel/') === 0) {
|
||||
breadcrumb[1].clickable = true
|
||||
} else { // 除panel外的菜单是否可以点击跳转:除了新增、编辑,其它均可点击
|
||||
breadcrumb.forEach(item => {
|
||||
if (item.value !== 'Create' && item.value !== 'Edit') {
|
||||
item.clickable = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +379,7 @@ export default {
|
||||
},
|
||||
async breadcrumb (n) {
|
||||
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
if (this.dnsQtypeMapData.size === 0) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
}
|
||||
@@ -384,7 +397,7 @@ export default {
|
||||
async mounted () {
|
||||
this.from = Object.keys(this.entityType)[0]
|
||||
// 是否需要dns的qtype和rcode的数据字典
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
if (this.dnsQtypeMapData.size === 0) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
}
|
||||
@@ -405,10 +418,10 @@ export default {
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : 60
|
||||
const chartTimeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
chartTimeFilter.value.startTime = startTime
|
||||
chartTimeFilter.value.endTime = endTime
|
||||
} else {
|
||||
@@ -423,39 +436,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
generateBreadcrumb (breadcrumb, menus) {
|
||||
if (this.route === '/entityDetail') {
|
||||
const entityMenu = menus.find(m => m.route === '/entityExplorer')
|
||||
const entityDetailMenu = menus.find(m => m.route === '/entityDetail')
|
||||
breadcrumb.push({
|
||||
code: entityMenu.code,
|
||||
value: entityMenu.i18n ? this.$t(entityMenu.i18n) : entityMenu.name,
|
||||
route: entityMenu.route,
|
||||
type: entityMenu.type
|
||||
})
|
||||
breadcrumb.push({
|
||||
code: entityDetailMenu.code,
|
||||
value: entityDetailMenu.i18n ? this.$t(entityDetailMenu.i18n) : entityDetailMenu.name,
|
||||
route: entityDetailMenu.route,
|
||||
type: entityDetailMenu.type
|
||||
})
|
||||
return true
|
||||
} else if (this.route === '/entityGraph') {
|
||||
const entityMenu = menus.find(m => m.route === '/entityExplorer')
|
||||
const entityGraphMenu = menus.find(m => m.route === '/entityGraph')
|
||||
breadcrumb.push({
|
||||
code: entityMenu.code,
|
||||
value: entityMenu.i18n ? this.$t(entityMenu.i18n) : entityMenu.name,
|
||||
route: entityMenu.route,
|
||||
type: entityMenu.type
|
||||
})
|
||||
breadcrumb.push({
|
||||
code: entityGraphMenu.code,
|
||||
value: entityGraphMenu.i18n ? this.$t(entityGraphMenu.i18n) : entityGraphMenu.name,
|
||||
route: entityGraphMenu.route,
|
||||
type: entityGraphMenu.type
|
||||
})
|
||||
return true
|
||||
}
|
||||
const menu = menus.find(m => m.route === this.route)
|
||||
if (menu) {
|
||||
breadcrumb.unshift({
|
||||
@@ -507,7 +487,7 @@ export default {
|
||||
},
|
||||
getCurTabByLabel (label) {
|
||||
let curTab = null
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
|
||||
if (curTableInCode && curTableInCode.tabList) {
|
||||
curTab = curTableInCode.tabList.find(item => item.label === label)
|
||||
@@ -520,7 +500,7 @@ export default {
|
||||
const currentValue = document.getElementById('breadcrumbValue') ? document.getElementById('breadcrumbValue').innerText : ''
|
||||
const columnName = this.getUrlParam(this.curTabState.thirdMenu, '')
|
||||
let type = 'ip'
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
|
||||
if (curTableInCode && curTableInCode.tabList) {
|
||||
const curTab = curTableInCode.tabList.find(item => item.label === columnName)
|
||||
@@ -544,7 +524,7 @@ export default {
|
||||
axios.get(curTableInCode.url.drilldownList, { params }).then(async response => {
|
||||
if (response.status === 200) {
|
||||
this.breadcrumbColumnValueListShow = response.data.data.result
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
if (this.dnsQtypeMapData.size === 0) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
}
|
||||
@@ -675,7 +655,7 @@ export default {
|
||||
},
|
||||
async handleCurDrilldownTableConfig (thirdMenu) {
|
||||
// const userId = localStorage.getItem(storageKey.userId)
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
|
||||
const drillDownTableConfigs = await combineDrilldownTableWithUserConfig()
|
||||
const currentTableConfig = drillDownTableConfigs.find(config => config.route === tableType)
|
||||
@@ -696,7 +676,26 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
// 仅处理除panel外的相关路径的导航
|
||||
async jumpOther (route, index) {
|
||||
route = route.replace('redirect:', '')
|
||||
this.showMenu = false
|
||||
if (route === this.route && index > 0) { // 当前只有一级菜单时,点击不进行刷新,重新跳转
|
||||
this.refresh()
|
||||
return
|
||||
}
|
||||
if (route) {
|
||||
this.$router.push({
|
||||
path: route,
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
// 仅处理panel相关路径的导航
|
||||
async jump (route, columnName, columnValue, opeType) {
|
||||
route = route.replace('redirect:', '')
|
||||
if (route === '/panel/linkMonitor' && opeType === 3) {
|
||||
return true
|
||||
}
|
||||
@@ -716,7 +715,7 @@ export default {
|
||||
this.$store.commit('setNetworkOverviewTabList', [])
|
||||
}
|
||||
// 清空网络概况的特殊面包屑
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
|
||||
const curTab = await getDefaultCurTab(tableType, metric, columnName)
|
||||
this.$store.getters.menuList.forEach(menu => {
|
||||
@@ -728,11 +727,6 @@ export default {
|
||||
child.columnName = columnName
|
||||
this.urlChangeParams[this.curTabState.thirdMenu] = columnName
|
||||
this.urlChangeParams[this.curTabState.fourthMenu] = columnValue
|
||||
// const tabObjGroup = networkOverviewTabList.filter(item => item.label == columnName)
|
||||
// let curTab = this.getCurTabByLabel()
|
||||
// const type = curTab ? curTab.prop : ''
|
||||
// this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
|
||||
// this.urlChangeParams[this.curTabState.dimensionType] = type
|
||||
this.urlChangeParams[this.curTabState.panelName] = columnValue
|
||||
} else if (columnName) { // 点击的为列名
|
||||
child.columnValue = ''
|
||||
@@ -791,10 +785,10 @@ export default {
|
||||
})
|
||||
return
|
||||
}
|
||||
/* if (route === this.route) {
|
||||
if (route === this.route) {
|
||||
this.refresh()
|
||||
return
|
||||
} */
|
||||
}
|
||||
if (route) {
|
||||
this.$router.push({
|
||||
path: route,
|
||||
|
||||
@@ -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') }}
|
||||
@@ -59,7 +59,7 @@
|
||||
<el-option
|
||||
v-for="item in eventTypeList"
|
||||
:key="item.value"
|
||||
:label="$t(item.label)"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -109,7 +109,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { detectionRuleType, storageKey, detectionUnitList } from '@/utils/constants'
|
||||
import { detectionRuleType, storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
|
||||
export default {
|
||||
@@ -160,7 +160,8 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
language: 'en'
|
||||
language: EN,
|
||||
ZH
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -183,7 +184,7 @@ export default {
|
||||
methods: {
|
||||
switchStatus,
|
||||
initData () {
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
this.language = localStorage.getItem(storageKey.language) || EN
|
||||
this.categoryList = detectionUnitList.categoryList
|
||||
this.eventTypeList = detectionUnitList.eventTypeList
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
@@ -20,12 +21,12 @@
|
||||
<img :src="data.iconUrl"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
|
||||
</div>
|
||||
<div class="card-desc" :title="data.desc">{{data.desc ? data.desc : '—'}}</div>
|
||||
<div class="card-desc" :title="$t(data.desc)">{{$t(data.desc) || '—'}}</div>
|
||||
</div>
|
||||
<div class="card-operate__footer">
|
||||
<button v-if="data.showUpdate"
|
||||
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')"
|
||||
class="top-tool-btn--update"
|
||||
@click="jumpToUpdatePage(data,true)">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
@@ -44,12 +45,12 @@
|
||||
<img :src="data.iconUrl"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
|
||||
</div>
|
||||
<div class="card-desc" :title="data.desc ? data.desc:'—'">{{data.desc ? data.desc : '—'}}</div>
|
||||
<div class="card-desc" :title="data.desc ? $t(data.desc) : '—'">{{data.desc ? $t(data.desc) : '—'}}</div>
|
||||
</div>
|
||||
<div class="card-operate__footer">
|
||||
<button v-if="data.showUpdate" :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')" :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="jumpToUpdatePage(data,false)">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
@@ -66,7 +67,9 @@
|
||||
|
||||
<div class="center-dialog">
|
||||
<el-dialog v-model="showUpdateDialog"
|
||||
:destroy-on-close="true"
|
||||
:custom-class="showAddUpdateDialog ? 'update-knowledge update-knowledge--upload' : 'update-knowledge'"
|
||||
:before-close="beforeClose"
|
||||
:after-close="handleClose">
|
||||
<div class="knowledge-update__top" >
|
||||
<div class="update-left__icon">
|
||||
@@ -75,7 +78,7 @@
|
||||
<div class="update-right">
|
||||
<div class="knowledge-enable">
|
||||
<div class="update-title">
|
||||
<div class="card-title-name" :title="updateKnowledge.label">{{updateKnowledge.label}}</div>
|
||||
<div class="card-title-name" :title="$t(updateKnowledge.label)">{{$t(updateKnowledge.label)}}</div>
|
||||
</div>
|
||||
<el-switch v-model="updateKnowledge.status"
|
||||
active-color="#38ACD2"
|
||||
@@ -83,10 +86,11 @@
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:before-change="(knowledgeId) => confirmSwitchLearning(updateKnowledge.knowledgeId)"
|
||||
v-if="updateKnowledge.source === 'cn_psiphon3_ip' && hasPermission('editBuiltInKnowledgeBase')"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
<div class="knowledge-desc" :title="updateKnowledge.desc">{{updateKnowledge.desc ? updateKnowledge.desc : '—'}}</div>
|
||||
<div class="knowledge-desc" :title="updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'">{{updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="!showAddUpdateDialog">
|
||||
@@ -108,7 +112,7 @@
|
||||
</el-tabs>
|
||||
<div class="update-operate">
|
||||
<button :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="uploadRecord">
|
||||
@click="uploadRecord"><!--:disabled="hasUpdatingRecord"-->
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
</button>
|
||||
@@ -120,13 +124,13 @@
|
||||
</div>
|
||||
<div class="update-operate">
|
||||
<button :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="uploadRecord">
|
||||
@click="uploadRecord"><!-- :disabled="hasUpdatingRecord" -->
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' ? 'calc(90vh - 190px - 200px - 50px - 42px)' : 'calc(100% - 242px)', marginTop: '42px', position: 'absolute', width: 'calc(100% - 60px)'}">
|
||||
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning' ? 'calc(90vh - 190px - 200px - 50px - 42px)' : 'calc(100% - 242px)', marginTop: '42px', position: 'absolute', width: 'calc(100% - 60px)'}">
|
||||
<loading :loading="updateLogLoading"></loading>
|
||||
</div>
|
||||
<el-table ref="updateDataTable"
|
||||
@@ -134,12 +138,20 @@
|
||||
:data="updateHistoryList"
|
||||
@selection-change="secondSelectionChange"
|
||||
width="100%"
|
||||
:class="updateKnowledge.source === 'cn_psiphon3_ip' ? 'update-dialog__table update-dialog__table--psiphon3' : 'update-dialog__table'"
|
||||
class="update-dialog__table"
|
||||
:class="{
|
||||
'update-dialog__table--psiphon3': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning',
|
||||
'update-dialog__table--system-user': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab !== 'intelligenceLearning'
|
||||
}"
|
||||
:header-cell-style="{background:'#f5f7fa',color:'#353636',fontWeight: '400',fontSize: '12px',borderRight: 'none',borderBottom: 'none'}"
|
||||
cell-style="padding:6px 0px;font-size: 12px;color: #353636;font-weight: 400;line-height: 20px;border-right:none;"
|
||||
header-cell-style="padding:8px 0px;font-size: 12px;color: #353636;font-weight: 500;border-right:none;">
|
||||
<el-table-column prop="opTime" :label="$t('entities.tab.informationAggregation.updateTime')" width="150" ></el-table-column>
|
||||
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="activeTab === 'updateRecord'">
|
||||
<el-table-column prop="opTime" :label="$t('entities.tab.informationAggregation.updateTime')" width="150" >
|
||||
<template #default="scope" :column="item">
|
||||
<span>{{scope.row.opTime ? dateFormatByAppearance(scope.row.opTime) : '-'}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="updateKnowledge.source !== 'cn_psiphon3_ip' || activeTab === 'updateRecord'">
|
||||
<template #default="scope" :column="item">
|
||||
<span>{{$_.get(scope.row, 'user.name', '-')}}</span>
|
||||
</template>
|
||||
@@ -154,7 +166,7 @@
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="psiphon3" v-if="updateKnowledge.source === 'cn_psiphon3_ip'">
|
||||
<div class="psiphon3" v-if="updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning'">
|
||||
<div class="psiphon3-title">{{$t('knowledgeBase.psiphon3IpCount')}}</div>
|
||||
<div class="psiphon3-bar">
|
||||
<chart-error v-if="showErrorForPsiphon3" :content="errorMsgForPsiphon3"/>
|
||||
@@ -245,7 +257,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="showAddUpdateDialog = false">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button @click="cancle">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submitConfirm">{{ $t('tip.confirm') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -272,8 +284,8 @@
|
||||
<div class="dialog-message">{{ confirmSwitchLearningTip }}</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showConfirmSwitch = false">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="switchLearning">OK</el-button>
|
||||
<el-button @click="cancleSwitch">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="switchLearning">{{$t('tip.confirm')}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -284,7 +296,7 @@
|
||||
import table from '@/mixins/table'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { getSecond, getMillisecond, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo } from '@/utils/constants'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo, knowledgeCardUpdateRecordType } from '@/utils/constants'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { api } from '@/utils/api'
|
||||
import { detectionTooltipFormatter } from '@/views/charts/charts/tools'
|
||||
@@ -328,6 +340,7 @@ export default {
|
||||
psiphon3Loading: false,
|
||||
updateLogLoading: false,
|
||||
showConfirmSwitch: false,
|
||||
// timer: null,
|
||||
switchKnowledgeId: '',
|
||||
activeTab: 'updateRecord',
|
||||
isNoDataForPsiphon3: false,
|
||||
@@ -337,6 +350,7 @@ export default {
|
||||
tabType: 'total',
|
||||
mousemoveCursor: '',
|
||||
selectTime: 1440,
|
||||
// hasUpdatingRecord: false,
|
||||
tabs: [
|
||||
{
|
||||
name: 'knowledgeBase.total',
|
||||
@@ -369,12 +383,6 @@ export default {
|
||||
setup () {
|
||||
// 没上传过文件的提示
|
||||
const uploadErrorTip = ref('')
|
||||
const nowMill = window.$dayJs.tz().valueOf()
|
||||
const timeFilter = ref({
|
||||
startTime: nowMill - 1000 * 60 * 60 * 24,
|
||||
endTime: nowMill,
|
||||
dateRangeValue: 1440
|
||||
})
|
||||
return {
|
||||
baseUrl: BASE_CONFIG.baseUrl,
|
||||
apiVersion: BASE_CONFIG.apiVersion,
|
||||
@@ -386,8 +394,7 @@ export default {
|
||||
fileList: ref([]),
|
||||
uploadFileSizeLimit: 1024 * 1024 * 1024,
|
||||
myChart: shallowRef(null),
|
||||
chartOption: shallowRef(null),
|
||||
timeFilter
|
||||
chartOption: shallowRef(null)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -416,7 +423,6 @@ export default {
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: ['1%', '3%'],
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
@@ -460,9 +466,10 @@ export default {
|
||||
},
|
||||
init (val, show, active, n) {
|
||||
this.psiphon3Loading = true
|
||||
const endTime = window.$dayJs.tz().valueOf()
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
startTime: getSecond(endTime - this.selectTime * 60 * 1000),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
const url = api.knowledgeBaseTimedistribution.replace('{{knowledgeId}}', this.updateKnowledge.knowledgeId).replace('{{type}}', this.tabType)
|
||||
axios.get(url, { params: params }).then(response => {
|
||||
@@ -475,7 +482,9 @@ export default {
|
||||
const chartsData = res.data.result.map(item => {
|
||||
return [getMillisecond(item.statTime), item.count]
|
||||
})
|
||||
this.echartsInit(chartsData)
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.echartsInit(chartsData)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
@@ -523,26 +532,32 @@ export default {
|
||||
})
|
||||
},
|
||||
timeChange () {
|
||||
this.timeFilter.endTime = window.$dayJs.tz().valueOf()
|
||||
this.timeFilter.startTime = this.timeFilter.endTime - this.selectTime * 60 * 1000
|
||||
this.init()
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
activeChange (item) { // isClick:代表是通过点击操作来的
|
||||
if (item) {
|
||||
this.tabType = item.class
|
||||
}
|
||||
this.legendSelectChange(item)
|
||||
this.init()
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
mouseenterTab (item) {
|
||||
if (this.isNoDataForPsiphon3) return
|
||||
this.mousemoveCursor = item.class
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
mouseleaveTab () {
|
||||
this.mousemoveCursor = ''
|
||||
@@ -572,15 +587,9 @@ export default {
|
||||
uploadSuccess (response) {
|
||||
this.uploadLoading = false
|
||||
this.uploaded = true
|
||||
/* this.uploaded = response.code === 200
|
||||
if (response.code === 200) { */
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
this.showAddUpdateDialog = false
|
||||
this.getCurTabData()
|
||||
this.init()
|
||||
/* } else {
|
||||
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
|
||||
} */
|
||||
},
|
||||
beforeUpload (file) {
|
||||
this.uploadLoading = true
|
||||
@@ -592,6 +601,9 @@ export default {
|
||||
submit () {
|
||||
this.$refs.knowledgeUpload.submit()
|
||||
},
|
||||
cancle () {
|
||||
this.showAddUpdateDialog = false
|
||||
},
|
||||
clickCard (data, event) {
|
||||
if (data.isSelected) { // 原来为选中,当前点击后未选中
|
||||
const index = this.checkList.indexOf(data)
|
||||
@@ -612,6 +624,13 @@ export default {
|
||||
data.isSelected = val
|
||||
this.$emit('checkboxStatusChange', val, data)
|
||||
},
|
||||
beforeClose (done) {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
done()
|
||||
},
|
||||
handleClose () {
|
||||
this.showUpdateDialog = false
|
||||
this.showAddUpdateDialog = false
|
||||
@@ -631,11 +650,15 @@ export default {
|
||||
this.updateKnowledge = data
|
||||
this.showEnable = showEnable
|
||||
await this.getCurTabData()
|
||||
await this.init()
|
||||
if (data.source === 'cn_psiphon3_ip') {
|
||||
await this.init()
|
||||
}
|
||||
this.showUpdate()
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
uploadRecord () {
|
||||
this.showAddUpdateDialog = true
|
||||
@@ -648,12 +671,12 @@ export default {
|
||||
pageSize: -1
|
||||
}
|
||||
if (this.showEnable) {
|
||||
if (this.activeTab === 'updateRecord') {
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.updateRecord) {
|
||||
params = {
|
||||
...params,
|
||||
opUser: -1
|
||||
}
|
||||
} else if (this.activeTab === 'intelligenceLearning') {
|
||||
} else if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
params = {
|
||||
...params,
|
||||
opUser: 0
|
||||
@@ -667,6 +690,15 @@ export default {
|
||||
if (this.updateHistoryList[0]) {
|
||||
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
|
||||
}
|
||||
/*
|
||||
this.hasUpdatingRecord = false
|
||||
this.updateHistoryList.forEach(item => {
|
||||
if (item.isUpdating) { // if(item.isUpdating){//????????
|
||||
this.hasUpdatingRecord = true
|
||||
}
|
||||
})
|
||||
|
||||
*/
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
}).finally(() => {
|
||||
@@ -676,6 +708,9 @@ export default {
|
||||
// 切换tab
|
||||
handleClick (tab) {
|
||||
this.getCurTabData()
|
||||
if (tab.index === '1') {
|
||||
this.timeChange()
|
||||
}
|
||||
},
|
||||
clearSelect () {
|
||||
this.$nextTick(() => {
|
||||
@@ -714,6 +749,9 @@ export default {
|
||||
this.switchKnowledgeId = id
|
||||
return false
|
||||
},
|
||||
cancleSwitch () {
|
||||
this.showConfirmSwitch = false
|
||||
},
|
||||
switchLearning () {
|
||||
const hint = this.aiTaggingList.find(d => d.knowledgeId === this.switchKnowledgeId)
|
||||
const toStatus = hint.status === 0 ? 1 : 0
|
||||
@@ -736,21 +774,21 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
tabType (n) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
this.timeChange()
|
||||
},
|
||||
timeFilter: {
|
||||
handler () {
|
||||
this.init()
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
/*
|
||||
hasUpdatingRecord (n) {
|
||||
if (n) { // update record页存在“正在更新”的记录时,每20秒自动请求一次接口
|
||||
this.timer = setTimeout(() => {
|
||||
this.getCurTabData()
|
||||
}, 20000)
|
||||
} else { // 直到出现新的记录,出现新记录后(失败或者成功),取消定时请求接口,右上角"update"按钮恢复可用。"正在更新"和"失败”都会有对应的强调样式。
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
},
|
||||
*/
|
||||
tableData: {
|
||||
handler (n) {
|
||||
console.info(n)
|
||||
if (this.tableData && this.tableData.length > 0) {
|
||||
this.aiTaggingList = []
|
||||
this.websketchList = []
|
||||
@@ -772,16 +810,24 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
activeTab (n) {
|
||||
if (n === 'updateRecord') {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
}
|
||||
},
|
||||
showAddUpdateDialog: {
|
||||
handler (n) {
|
||||
if (!n) {
|
||||
this.fileList = []
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
this.timeChange()
|
||||
} else {
|
||||
this.myChart && this.myChart.dispose()
|
||||
this.myChart = null
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -803,7 +849,7 @@ export default {
|
||||
})
|
||||
},
|
||||
beforeUnmount () {
|
||||
clearTimeout(this.timer)
|
||||
//clearTimeout(this.timer)
|
||||
window.removeEventListener('resize', this.resize)
|
||||
const dom = document.getElementById('psiphonBarChart')
|
||||
if (dom) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
if (to.path) {
|
||||
next()
|
||||
/* if (hasMenu(store.getters.menuList, to.path)) {
|
||||
const homeRoute = {
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('@/components/layout/Home'),
|
||||
children: []
|
||||
}
|
||||
handleRoutes(menuList, homeRoute.children)
|
||||
router.addRoute(homeRoute)
|
||||
next({ ...to, replace: true })
|
||||
} else {
|
||||
if (to.path) {
|
||||
next()
|
||||
} else {
|
||||
ElMessage.error('No access') // TODO 国际化
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -60,14 +65,14 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
// menuList中是否包含route权限
|
||||
export function hasMenu (menuList, route) {
|
||||
// menuList中是否包含code
|
||||
export function hasMenu (menuList, code) {
|
||||
return menuList.some(menu => {
|
||||
if (menu.route === route) {
|
||||
if (menu.code === code) {
|
||||
return true
|
||||
} else {
|
||||
if (menu.children) {
|
||||
if (hasMenu(menu.children, route)) {
|
||||
if (hasMenu(menu.children, code)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -96,36 +101,9 @@ export function hasButton (buttonList, code) {
|
||||
return buttonList.some(button => button === code)
|
||||
}
|
||||
|
||||
// 用法 v-has="code" | v-has="[code...]" 任意匹配一个 | v-has:all="[code...]" 全匹配
|
||||
export const hasPermission = {
|
||||
beforeMount (el, binding) {
|
||||
// 节点权限处理
|
||||
const buttonCode = binding.value
|
||||
const arg = binding.arg
|
||||
if (buttonCode) {
|
||||
if (buttonCode instanceof Array) {
|
||||
let has = true
|
||||
if (arg && arg === 'all') { // 全匹配
|
||||
buttonCode.forEach(button => {
|
||||
if (has) {
|
||||
has = hasButton(store.getters.buttonList, button)
|
||||
}
|
||||
})
|
||||
} else { // 任意匹配
|
||||
has = buttonCode.some(button => {
|
||||
return hasButton(store.getters.buttonList, button)
|
||||
})
|
||||
}
|
||||
if (!has) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
} else { // 单个匹配
|
||||
if (!hasButton(store.getters.buttonList, buttonCode)) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 根据code从menuList和buttonList中判断是否有权限
|
||||
export function hasPermission (code) {
|
||||
return hasButton(store.getters.buttonList, code) || hasMenu(store.getters.menuList, code)
|
||||
}
|
||||
|
||||
// 根据orderNum排序
|
||||
@@ -160,3 +138,100 @@ export function getWelcomeMenu (menu) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function handleComponent (code) {
|
||||
switch (code) {
|
||||
case 'networkOverview':
|
||||
case 'networkAppPerformance':
|
||||
case 'dnsServiceInsights':
|
||||
case 'linkMonitor':
|
||||
return () => import('@/views/charts2/Panel')
|
||||
case 'entity':
|
||||
return () => import('@/views/entityExplorer/EntityExplorer')
|
||||
case 'entityDetail':
|
||||
return () => import('@/views/entityExplorer/EntityDetail')
|
||||
case 'entityGraph':
|
||||
return () => import('@/views/entityExplorer/EntityGraph')
|
||||
case 'securityEvents':
|
||||
case 'performanceEvents':
|
||||
return () => import('@/views/detections/Index')
|
||||
case 'detectionPolicy':
|
||||
return () => import('@/views/detections/detectionPolicies/Index')
|
||||
case 'createDetectionPolicy':
|
||||
case 'editDetectionPolicy':
|
||||
return () => import('@/views/detections/detectionPolicies/PolicyForm')
|
||||
case 'report':
|
||||
return () => import('@/views/report/Report')
|
||||
case 'knowledgeBase':
|
||||
return () => import('@/views/setting/KnowledgeBase')
|
||||
case 'userDefinedLibrary':
|
||||
return () => import('@/views/setting/KnowledgeBaseUserDefinedList')
|
||||
case 'createUserDefinedLibrary':
|
||||
case 'editUserDefinedLibrary':
|
||||
return () => import('@/views/setting/KnowledgeBaseForm')
|
||||
case 'administration':
|
||||
return () => import('@/views/administration/Index')
|
||||
case 'user':
|
||||
return () => import('@/views/administration/User')
|
||||
case 'role':
|
||||
return () => import('@/views/administration/Roles')
|
||||
case 'operationLog':
|
||||
return () => import('@/views/administration/OperationLog')
|
||||
case 'appearance':
|
||||
return () => import('@/views/administration/Appearance')
|
||||
case 'I18N':
|
||||
return () => import('@/views/administration/I18n')
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function handleRoutes (menus, routes) {
|
||||
menus.forEach(menu => {
|
||||
if (menu.route === '' && (!menu.children || menu.children.length < 0)) {
|
||||
return false
|
||||
}
|
||||
// administration的路由使用了嵌套,其他的是平铺
|
||||
if (menu.pid === 0 && menu.code === 'administration') {
|
||||
const path = menu.route.replace('redirect:', '')
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
const route = {
|
||||
name: menu.name,
|
||||
path,
|
||||
redirect: menu.children[0].route,
|
||||
component: handleComponent(menu.code),
|
||||
children: []
|
||||
}
|
||||
menu.children.forEach(c => {
|
||||
route.children.push({
|
||||
name: c.name,
|
||||
path: c.route,
|
||||
component: handleComponent(c.code)
|
||||
})
|
||||
})
|
||||
routes.push(route)
|
||||
}
|
||||
} else {
|
||||
if (menu.route && menu.route.startsWith('redirect:')) {
|
||||
const path = menu.route.replace('redirect:', '')
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
routes.push({
|
||||
name: menu.name,
|
||||
path,
|
||||
redirect: menu.children[0].route
|
||||
})
|
||||
handleRoutes(menu.children, routes)
|
||||
}
|
||||
} else {
|
||||
routes.push({
|
||||
name: menu.code,
|
||||
path: menu.route,
|
||||
component: handleComponent(menu.code)
|
||||
})
|
||||
}
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
handleRoutes(menu.children, routes)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -121,8 +121,6 @@ export function xAxisTimeFormatter (value) {
|
||||
':' +
|
||||
(date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes())
|
||||
// 如果是一天的开始
|
||||
console.info(date.getTime(), dayStart.getTime(), hourStart.getTime(), date.getTime() === dayStart.getTime(), date.getTime() === hourStart.getTime())
|
||||
console.info(getSecond(date.getTime()), getSecond(dayStart.getTime()), getSecond(hourStart.getTime()))
|
||||
if (getSecond(date.getTime()) === getSecond(dayStart.getTime())) {
|
||||
return '{day|' + dateFormat(date, 'YYYY-MM-DD') + '}'
|
||||
} else if (getSecond(date.getTime()) === getSecond(hourStart.getTime())) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
<span :style="{color: tab.name === activeTab ? '#046ECA' : '#717171'}">{{ tab.tag }}</span>
|
||||
</el-tag>
|
||||
</template>
|
||||
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
|
||||
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></domain-name-resolution>
|
||||
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></open-port>
|
||||
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></behavior-pattern>
|
||||
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
|
||||
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></domain-name-resolution>
|
||||
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" @checkTag="setTag" />
|
||||
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
|
||||
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
|
||||
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></open-port>
|
||||
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></behavior-pattern>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -58,12 +58,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
timer: null,
|
||||
// 最近一天的时间
|
||||
oneDayTimeFilter: {
|
||||
startTime: window.$dayJs.tz().valueOf() - 1440 * 60 * 1000,
|
||||
endTime: window.$dayJs.tz().valueOf()
|
||||
}
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -114,22 +109,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entity.entityName
|
||||
// startTime: getSecond(this.oneDayTimeFilter.startTime),
|
||||
// endTime: getSecond(this.oneDayTimeFilter.endTime)
|
||||
}
|
||||
|
||||
const url = this.getUrlByEntityType(this.entity.entityType)
|
||||
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: params })
|
||||
const openPort = axios.get(url, { params: params })
|
||||
// const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
|
||||
// const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
|
||||
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: this.getParamsByTabType(entityDetailTabsName.informationAggregation) })
|
||||
const openPort = axios.get(url, { params: this.getParamsByTabType(entityDetailTabsName.openPort) })
|
||||
const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.securityEvent) })
|
||||
const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.performanceEvent) })
|
||||
|
||||
Promise.all([informationAggregation, openPort]).then(response => {
|
||||
if (response[0].status === 200) {
|
||||
Promise.allSettled([informationAggregation, openPort, security, performance]).then(response => {
|
||||
const informationAggregationResponse = response[0].value
|
||||
if (informationAggregationResponse.status === 200) {
|
||||
const list = []
|
||||
response[0].data.data.result.forEach(r => {
|
||||
informationAggregationResponse.data.data.result.forEach(r => {
|
||||
Object.keys(r).forEach(k => {
|
||||
const aggregation = {
|
||||
createTime: r[k].createTime,
|
||||
@@ -152,40 +142,42 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.initSetTag(entityDetailTabsName.informationAggregation, list.length)
|
||||
}
|
||||
if (response[1].status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.openPort, response[1].data.data.result.length)
|
||||
const openPortResponse = response[1].value
|
||||
if (openPortResponse.status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.openPort, openPortResponse.data.data.result.length)
|
||||
}
|
||||
// if (response[2].status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.securityEvent, response[2].data.data.result.length)
|
||||
const securityResponse = response[2].value
|
||||
if (securityResponse.status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, securityResponse.data.data.result.length)
|
||||
}
|
||||
// let performanceResponse = response[3].value
|
||||
// if (performanceResponse.status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.performanceEvent, performanceResponse.data.data.result.length)
|
||||
// }
|
||||
// if (response[3].status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.performanceEvent, response[3].data.data.result.length)
|
||||
// }
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, 0)
|
||||
this.initSetTag(entityDetailTabsName.performanceEvent, 0)
|
||||
})
|
||||
|
||||
const relatedEntityParams = this.getParamsByTabType(entityDetailTabsName.relatedEntity)
|
||||
// 域名解析
|
||||
if (this.entity.entityType === 'app') {
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: relatedEntityParams })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: relatedEntityParams })
|
||||
this.promiseData(ipsOfApp, domainsOfApp)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'ip') {
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
|
||||
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: params })
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: relatedEntityParams })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: relatedEntityParams })
|
||||
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: relatedEntityParams })
|
||||
this.promiseData(appsOfIp, domainsOfIp, behaviorPattern)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'domain') {
|
||||
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: params })
|
||||
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: params })
|
||||
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: params })
|
||||
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: relatedEntityParams })
|
||||
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: relatedEntityParams })
|
||||
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: relatedEntityParams })
|
||||
this.promiseData(appsOfDomain, ipsOfDomain, fqdnsOfDomain)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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>
|
||||
@@ -50,8 +50,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
@@ -60,6 +60,7 @@ import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import ChartError from '@/components/common/Error'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'PerformanceEvent',
|
||||
@@ -77,33 +78,36 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.performanceEvent
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName
|
||||
entityName,
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// this.initData()
|
||||
this.initData()
|
||||
/*
|
||||
this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
}, 200) */
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
axios.get(`${api.entity.performance}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
@@ -130,6 +134,18 @@ export default {
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
},
|
||||
changeSecurity (value) {
|
||||
if (value) {
|
||||
const obj = securityLevel.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:key="item.eventId">
|
||||
<div class="cn-detection--list">
|
||||
<div class="cn-detection__case entity-detail-security">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__icon"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header">
|
||||
<span
|
||||
@@ -17,7 +17,7 @@
|
||||
class="detection-event-severity-color-block"
|
||||
:style="`background-color: ${eventSeverityColor[item.eventSeverity]}`">
|
||||
</span>
|
||||
<span class="detection-event-severity-block">{{ toUpperCaseByString(item.securityType) || '-' }}</span>
|
||||
<span class="detection-event-severity-block">{{ item.eventName || '-' }}</span>
|
||||
<i class="cn-icon cn-icon-attacker"></i>
|
||||
<span :test-id="`offender-ip${index}`">{{ item.offenderIp || '-' }}</span>
|
||||
<div class="domain">{{ item.offenderDomain }}</div>
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span>{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
|
||||
<span>{{ changeSecurity(item.eventSeverity) }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.eventType">
|
||||
<i class="cn-icon cn-icon-event-type"></i>
|
||||
@@ -76,8 +76,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
@@ -85,6 +85,7 @@ import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'SecurityEvent',
|
||||
@@ -102,33 +103,36 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.securityEvent
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName
|
||||
entityName,
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// this.initData()
|
||||
this.initData()
|
||||
/*
|
||||
this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
}, 200) */
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
@@ -155,6 +159,18 @@ export default {
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
},
|
||||
changeSecurity (value) {
|
||||
if (value) {
|
||||
const obj = securityLevel.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import { api } from '@/utils/api'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { storageKey, ZH } from '@/utils/constants'
|
||||
import PopoverContent from './LinkDirectionGrid/PopoverContent'
|
||||
import { computeScore } from '@/utils/tools'
|
||||
import axios from 'axios'
|
||||
@@ -156,11 +156,9 @@ export default {
|
||||
})
|
||||
|
||||
// 一行如果无数据,则删除该行,默认10*10矩阵
|
||||
const rowXIndex = 0
|
||||
this.handleXRowNoData(linkGridData, rowXIndex)
|
||||
this.handleXRowNoData(linkGridData, 0)
|
||||
// 一列如果无数据,则删除该列,默认10*10矩阵
|
||||
const rowYIndex = 0
|
||||
this.handleYRowNoData(linkGridData, rowYIndex)
|
||||
this.handleYRowNoData(linkGridData, 0)
|
||||
this.isLinkNoData = linkGridData.length === 0
|
||||
this.linkGridData = linkGridData
|
||||
}
|
||||
@@ -178,9 +176,9 @@ export default {
|
||||
// 接口数据乱序,根据入方向排序,再根据同个入方向下的出方向进行排序
|
||||
nextLinkData.sort((a, b) => {
|
||||
if (a.inLinkDirection !== b.inLinkDirection) {
|
||||
return a.inLinkDirection.localeCompare(b.inLinkDirection, 'zh')
|
||||
return a.inLinkDirection.localeCompare(b.inLinkDirection, ZH)
|
||||
}
|
||||
return a.outLinkDirection.localeCompare(b.outLinkDirection, 'zh')
|
||||
return a.outLinkDirection.localeCompare(b.outLinkDirection, ZH)
|
||||
})
|
||||
|
||||
this.isNextNoData = nextLinkData.length === 0
|
||||
@@ -257,11 +255,9 @@ export default {
|
||||
})
|
||||
|
||||
// 一行如果无数据,则删除该行,默认3*3矩阵
|
||||
const rowXIndex = 0
|
||||
this.handleXRowNoData(nextGridData, rowXIndex)
|
||||
this.handleXRowNoData(nextGridData, 0)
|
||||
// 一列如果无数据,则删除该列,默认3*3矩阵
|
||||
const rowYIndex = 0
|
||||
this.handleYRowNoData(nextGridData, rowYIndex)
|
||||
this.handleYRowNoData(nextGridData, 0)
|
||||
|
||||
this.isNextNoData = nextGridData.length === 0
|
||||
this.nextGridData = nextGridData
|
||||
@@ -357,7 +353,7 @@ export default {
|
||||
* @param index
|
||||
*/
|
||||
handleXRowNoData (data, index) {
|
||||
if (data) {
|
||||
if (data && data.length > 0) {
|
||||
const item = data[index]
|
||||
let tempList = []
|
||||
if (item) {
|
||||
@@ -380,7 +376,7 @@ export default {
|
||||
*/
|
||||
handleYRowNoData (data, index) {
|
||||
const rowList = []
|
||||
if (data) {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach(item => {
|
||||
if (item.out[index]) {
|
||||
if (item.out[index].noData) {
|
||||
|
||||
@@ -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>
|
||||
<el-collapse-transition>
|
||||
<div class="filter__body" v-show="!filter.collapse">
|
||||
<el-checkbox-group v-model="filter.value">
|
||||
<template v-for="(d, i) in filter.data" :key="i">
|
||||
<el-checkbox :label="d.value" v-if="!filter.showIndex || filter.showIndex >= i">
|
||||
<div class="filter__checkbox-label">
|
||||
<div style="display: flex">
|
||||
<span class="severity-color-block" v-if="filter.column === 'eventSeverity'" :style="`background-color: ${eventSeverityColor[d.value]}`"></span>
|
||||
<span :style="filter.column === 'eventSeverity' ? 'padding-left: 10px' : ''">{{d.label}}</span>
|
||||
</div>
|
||||
<div v-if="d.count || d.count === 0">{{d.count}}</div>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</template>
|
||||
</el-checkbox-group>
|
||||
<div class="filter__more" v-if="filter.showMore" @click="showMore(filter)">{{$t('overall.showMore')}}</div>
|
||||
<div class="filter__header">{{filter.title}}</div>
|
||||
|
||||
<div class="filter__body" style="position: relative">
|
||||
<loading :loading="loadingLeft" style="top: -5px;"></loading>
|
||||
|
||||
<div class="filter__body-item"
|
||||
v-for="(data, i) in filter.data.slice(0, filter.showIndex)"
|
||||
:key="i"
|
||||
@click="clickFilterItem(data.label, filter.column, index)">
|
||||
<div class="filter__body-item-left">
|
||||
<div class="filter__body-item-left-index">{{ i+1 }}</div>
|
||||
<div class="filter__body-item-left-label">
|
||||
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
|
||||
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">
|
||||
<span>{{ data.label }}</span>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter__body-item-right">{{ data.count }}</div>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
<div v-show="filter.showMore" @click="showMore(filter)"
|
||||
:class="filter.showDisabled? 'filter-no-show-more' : 'filter-show-more'">
|
||||
{{ $t('overall.showMore') }}
|
||||
</div>
|
||||
<div class="filter-hr"></div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -42,13 +47,36 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
eventSeverityColor
|
||||
eventSeverityColor,
|
||||
disabledLabel: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showMore (filter) {
|
||||
filter.showIndex && (filter.showIndex += 10)
|
||||
filter.showIndex >= (filter.data.length - 1) && (filter.showMore = false)
|
||||
filter.showIndex && (filter.showIndex += 5)
|
||||
filter.showIndex >= (filter.data.length - 1) && (filter.showDisabled = true)
|
||||
},
|
||||
clickFilterItem (name, data, index) {
|
||||
if (index === 0) {
|
||||
let status = 0
|
||||
if (name === this.$t('detections.active')) {
|
||||
status = '0'
|
||||
} else if (name === this.$t('detections.ended')) {
|
||||
status = '1'
|
||||
}
|
||||
this.$emit('filter', status, data)
|
||||
} else {
|
||||
this.$emit('filter', name, data)
|
||||
}
|
||||
},
|
||||
handleMouse (id) {
|
||||
const dom = document.getElementById(id)
|
||||
if (dom) {
|
||||
const width = document.getElementById(id).offsetWidth
|
||||
this.disabledLabel = width < 180
|
||||
} else {
|
||||
this.disabledLabel = true
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="explorer-search explorer-search--show-list">
|
||||
<div class="explorer-search__input-case explorer-search__input-case--question-mark-in-line">
|
||||
<div class="explorer-search__input">
|
||||
<div class="explorer-search__input-case explorer-search__input-case--question-mark-in-line" style="position: relative">
|
||||
<div class="explorer-search__input entity__search">
|
||||
<advanced-search
|
||||
ref="search"
|
||||
:column-list="columnList[pageType]"
|
||||
@@ -13,6 +13,36 @@
|
||||
@search="search"
|
||||
></advanced-search>
|
||||
</div>
|
||||
<div class="explorer-search__foot-list" v-ele-click-outside="esc">
|
||||
<div class="explorer-search__block" @click="triggerHistory">
|
||||
<i class="cn-icon cn-icon-time"></i>
|
||||
<div class="search-dividing-line"></div>
|
||||
</div>
|
||||
<transition name="el-zoom-in-top">
|
||||
<div class="search__history" v-if="showHistory">
|
||||
<div class="history__items-new">
|
||||
<el-table
|
||||
:data="history"
|
||||
scrollbar-always-on="false"
|
||||
@row-click="selectHistory"
|
||||
style="overflow-x: unset"
|
||||
empty-text=" "
|
||||
>
|
||||
<el-table-column prop="str" :label="$t('overall.expression')" min-width="560" />
|
||||
<el-table-column prop="date" :label="$t('overall.time')" sortable width="200">
|
||||
<template #default="scope">
|
||||
<span>{{ changeTimeByDate(scope.row.date) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="clear-all">
|
||||
<span @click="clearHistory" v-if="!$_.isEmpty(history)">{{$t('entity.clearAll')}}</span>
|
||||
<div v-else>{{$t('overall.noRecords')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- <div class="search-symbol-inline">-->
|
||||
<!-- <i class="cn-icon cn-icon-help"></i>-->
|
||||
<!-- </div>-->
|
||||
@@ -25,6 +55,9 @@ import AdvancedSearch from '@/components/advancedSearch/Index'
|
||||
import { schemaDetectionSecurity } from '@/utils/static-data'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import _ from 'lodash'
|
||||
import { changeTimeByDate } from '@/utils/tools'
|
||||
export default {
|
||||
name: 'DetectionSearch',
|
||||
props: {
|
||||
@@ -95,7 +128,9 @@ export default {
|
||||
label: 'OR'
|
||||
}
|
||||
],
|
||||
showList: true
|
||||
showList: true,
|
||||
showHistory: false,
|
||||
history: []
|
||||
}
|
||||
},
|
||||
emits: ['search'],
|
||||
@@ -122,70 +157,66 @@ export default {
|
||||
}
|
||||
this.$emit('search', metaList, sql)
|
||||
}, */
|
||||
search ({ q, metaList }) {
|
||||
changeTimeByDate,
|
||||
search ({ str, q, metaList }) {
|
||||
if (str) {
|
||||
// 加入搜索记录,将记录数量控制在30以内
|
||||
const oldHistory = localStorage.getItem(storageKey.detectionSearchHistory)
|
||||
let arr = []
|
||||
const newItem = { str, date: this.dateFormatByAppearance(new Date()) }
|
||||
if (!_.isEmpty(oldHistory)) {
|
||||
const oldArr = JSON.parse(oldHistory)
|
||||
if (str === oldArr[0].str) {
|
||||
oldArr[0].date = this.dateFormatByAppearance(new Date())
|
||||
} else {
|
||||
oldArr.unshift(newItem)
|
||||
}
|
||||
arr = [...oldArr]
|
||||
if (arr.length > 30) {
|
||||
arr = arr.slice(0, 30)
|
||||
}
|
||||
} else {
|
||||
arr.push(newItem)
|
||||
}
|
||||
localStorage.setItem(storageKey.detectionSearchHistory, JSON.stringify(arr))
|
||||
}
|
||||
this.$emit('search', { q, metaList })
|
||||
},
|
||||
changeParams (params) { // params: { column: columnName, oldValue: [...], newValue: [...] }
|
||||
// 向下传递时需要再转换一次param格式为[{column, operator, value}, ...]
|
||||
if (params.oldValue.length === 0 && params.newValue.length === 1) {
|
||||
// 1.参数值数量从0到1,直接addParams
|
||||
const p = {
|
||||
column: params.column,
|
||||
operator: '=',
|
||||
value: params.newValue
|
||||
}
|
||||
this.$refs.search.addParams([p])
|
||||
} else if (params.oldValue.length === 1 && params.newValue.length === 0) {
|
||||
// 2.参数值数量从1到0,直接removeParams
|
||||
const p = {
|
||||
column: params.column,
|
||||
operator: '=',
|
||||
value: params.oldValue
|
||||
}
|
||||
this.$refs.search.removeParams([p])
|
||||
} else if (params.oldValue.length === 2 && params.newValue.length === 1) {
|
||||
// 3.参数值数量从多到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(() => {
|
||||
this.emitter.emit('advanced-search')
|
||||
if (this.$refs.search.$refs.textMode) {
|
||||
this.$refs.search.$refs.textMode.focus()
|
||||
}
|
||||
})
|
||||
if (this.showList) {
|
||||
this.$nextTick(() => {
|
||||
this.emitter.emit('advanced-search')
|
||||
})
|
||||
}
|
||||
},
|
||||
clearHistory () {
|
||||
localStorage.setItem(storageKey.detectionSearchHistory, '')
|
||||
this.history = []
|
||||
},
|
||||
triggerHistory () {
|
||||
this.showHistory = !this.showHistory
|
||||
if (this.showHistory) {
|
||||
const history = localStorage.getItem(storageKey.detectionSearchHistory)
|
||||
if (!_.isEmpty(history)) {
|
||||
this.history = JSON.parse(history)
|
||||
}
|
||||
}
|
||||
},
|
||||
esc () {
|
||||
const timer = setTimeout(() => {
|
||||
this.showHistory = false
|
||||
clearTimeout(timer)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="explorer-top-tools explorer-detection-top-tools">
|
||||
<div class="explorer-top-tools-title">{{$t('overall.detections')}}</div>
|
||||
<div style="display: flex">
|
||||
<div class="explorer-top-tools-block" @click="jumpNewDetetion">
|
||||
<div class="explorer-top-tools-block" @click="jumpNewDetetion" v-if="hasPermission('detectionPolicy')">
|
||||
<i class="cn-icon cn-icon-configure-policies detection-icon-setting"></i>
|
||||
<span>{{$t('config.detections.configurePolicies')}}</span>
|
||||
</div>
|
||||
@@ -21,13 +21,12 @@
|
||||
:end-time="timeFilter.endTime"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 100%;padding-bottom: 47px;">
|
||||
<div style="width: 100%; padding-bottom: 50px;">
|
||||
<chart-tabs :data="tabsData" router></chart-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 搜索组件 -->
|
||||
<detection-search
|
||||
class="detection-border"
|
||||
ref="search"
|
||||
:page-type="pageType"
|
||||
@search="search"
|
||||
@@ -48,6 +47,7 @@
|
||||
:filter-data="filterData[pageType]"
|
||||
:q="q"
|
||||
:time-filter="timeFilter"
|
||||
@filter="getFilter"
|
||||
></detection-filter>
|
||||
|
||||
<div class="detection__list">
|
||||
@@ -138,6 +138,8 @@ import { useRoute } from 'vue-router'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import ChartTabs from '@/components/common/ChartTabs'
|
||||
import { useStore } from 'vuex'
|
||||
import { tooLongFormatter } from '@/views/charts/charts/tools'
|
||||
import { format } from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
@@ -172,12 +174,12 @@ export default {
|
||||
// }
|
||||
],
|
||||
chartInit: [],
|
||||
pageObj: {
|
||||
pageNo: 1,
|
||||
pageSize: defaultPageSize,
|
||||
total: 0,
|
||||
resetPageNo: true
|
||||
},
|
||||
// pageObj: {
|
||||
// pageNo: 1,
|
||||
// pageSize: defaultPageSize,
|
||||
// total: 0,
|
||||
// resetPageNo: true
|
||||
// },
|
||||
q: '',
|
||||
detectionPageType,
|
||||
filterData: {
|
||||
@@ -188,6 +190,7 @@ export default {
|
||||
topColumn: 'status',
|
||||
collapse: false,
|
||||
value: [], // value之间是or的关系
|
||||
showMore: false,
|
||||
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
|
||||
},
|
||||
{
|
||||
@@ -196,6 +199,7 @@ export default {
|
||||
topColumn: 'severity',
|
||||
collapse: false,
|
||||
value: [], // value之间是or的关系
|
||||
showMore: false,
|
||||
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
|
||||
},
|
||||
{
|
||||
@@ -204,6 +208,9 @@ export default {
|
||||
topColumn: 'event_type',
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: true,
|
||||
showDisabled: true,
|
||||
showIndex: 5, // index作为showMore分割,从1开始而非0
|
||||
data: [] // 从接口动态获取
|
||||
},
|
||||
{
|
||||
@@ -213,43 +220,21 @@ export default {
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: true,
|
||||
showIndex: 9,
|
||||
showDisabled: true,
|
||||
showIndex: 5,
|
||||
data: [] // 从接口动态获取
|
||||
},
|
||||
// {
|
||||
// title: this.$t('detections.victimLocation'),
|
||||
// column: 'victimLocationCountry',
|
||||
// collapse: false,
|
||||
// value: [],
|
||||
// showMore: false,
|
||||
// showIndex: 9,
|
||||
// data: [
|
||||
// {
|
||||
// label: 'China',
|
||||
// value: 'china',
|
||||
// count: 50
|
||||
// }
|
||||
// ] // 从接口动态获取
|
||||
// },
|
||||
{
|
||||
title: this.$t('detections.offenderIp'),
|
||||
column: 'offenderIP',
|
||||
topColumn: 'offender_ip',
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: false,
|
||||
showIndex: 9,
|
||||
showMore: true,
|
||||
showDisabled: true,
|
||||
showIndex: 5,
|
||||
data: [] // 从接口动态获取
|
||||
}
|
||||
// {
|
||||
// title: this.$t('detections.offenderLocation'),
|
||||
// column: 'offenderLocationCountry',
|
||||
// collapse: false,
|
||||
// value: [],
|
||||
// showMore: false,
|
||||
// showIndex: 9,
|
||||
// data: [] // 从接口动态获取
|
||||
// }
|
||||
],
|
||||
performanceEvent: [
|
||||
{
|
||||
@@ -286,7 +271,8 @@ export default {
|
||||
isStatisticsActiveAttackNoData: false,
|
||||
loading: false,
|
||||
oldActiveEntitySearchValue: '',
|
||||
initFlag: true // 初始化标识,初始化时保证mounted执行
|
||||
initFlag: true, // 初始化标识,初始化时保证mounted执行
|
||||
detectionChart:shallowRef(null)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -294,6 +280,11 @@ export default {
|
||||
axios.get(api.detection[this.pageType].statusStatistics, { params }).then(res => {
|
||||
if (res.status === 200) {
|
||||
const data = res.data.data.result
|
||||
if (data && data.length > 0) {
|
||||
data.sort((a, b) => {
|
||||
return Number(b.count) - Number(a.count)
|
||||
})
|
||||
}
|
||||
this.filterData[this.pageType][0].data = data.map(r => {
|
||||
let label = ''
|
||||
if (r.status === '0') {
|
||||
@@ -388,6 +379,11 @@ export default {
|
||||
initEventSeverityData (params) {
|
||||
axios.get(api.detection[this.pageType].severityStatistics, { params }).then(res => {
|
||||
const data = res.data.data.result
|
||||
if (data && data.length > 0) {
|
||||
data.sort((a, b) => {
|
||||
return Number(b.count) - Number(a.count)
|
||||
})
|
||||
}
|
||||
this.statisticsSeverityData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
this.filterData[this.pageType][1].data = data.map(r => ({ label: r.severity, value: r.severity, count: r.count }))
|
||||
@@ -410,6 +406,7 @@ export default {
|
||||
if (this.pageType === 'performanceEvent') {
|
||||
vm.filterData.performanceEvent[0].value = vm.triggerFilterDataValue(vm.filterData.performanceEvent[0].value, e.data.name)
|
||||
} else if (this.pageType === 'securityEvent') {
|
||||
this.getFilter(e.data.name, vm.filterData.securityEvent[1].column)
|
||||
vm.filterData.securityEvent[1].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[1].value, e.data.name)
|
||||
}
|
||||
})
|
||||
@@ -430,6 +427,11 @@ export default {
|
||||
value: r.eventType,
|
||||
count: r.count
|
||||
}))
|
||||
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][2].data)
|
||||
this.filterData[this.pageType][2].showMore = showMore
|
||||
this.filterData[this.pageType][2].showIndex = showIndex
|
||||
this.filterData[this.pageType][2].showDisabled = showDisabled
|
||||
|
||||
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
|
||||
let detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
if (detectionChart) {
|
||||
@@ -441,6 +443,15 @@ export default {
|
||||
securityTypeOption.series[0].data = data.map(d => {
|
||||
return { value: d.count, name: d.eventType }
|
||||
})
|
||||
if (chartDom) {
|
||||
let oneColumnWidth = (chartDom.clientWidth * 0.56) - 30
|
||||
if (data.length > 6) {
|
||||
oneColumnWidth = (chartDom.clientWidth * 0.56) / 2 - 30
|
||||
}
|
||||
securityTypeOption.legend.formatter = function (name) {
|
||||
return format.truncateText(name, oneColumnWidth, '12px')
|
||||
}
|
||||
}
|
||||
detectionChart.setOption(securityTypeOption)
|
||||
|
||||
const vm = this
|
||||
@@ -452,6 +463,9 @@ export default {
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][2].data = []
|
||||
this.filterData[this.pageType][2].showMore = false
|
||||
this.filterData[this.pageType][2].showIndex = 5
|
||||
this.filterData[this.pageType][2].showDisabled = true
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
@@ -467,28 +481,45 @@ export default {
|
||||
count: r.count
|
||||
}))
|
||||
this.isCheckFilterByQ(params, 2)
|
||||
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][2].data)
|
||||
this.filterData[this.pageType][2].showMore = showMore
|
||||
this.filterData[this.pageType][2].showIndex = showIndex
|
||||
this.filterData[this.pageType][2].showDisabled = showDisabled
|
||||
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
|
||||
let detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
if (detectionChart) {
|
||||
echarts.dispose(detectionChart)
|
||||
this.detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
if (this.detectionChart) {
|
||||
echarts.dispose(this.detectionChart)
|
||||
}
|
||||
detectionChart = echarts.init(chartDom)
|
||||
this.chartInit.push(shallowRef(detectionChart))
|
||||
this.detectionChart = echarts.init(chartDom)
|
||||
//this.chartInit.push(shallowRef(detectionChart))
|
||||
const securityTypeOption = this.$_.cloneDeep(pieForSeverity)
|
||||
securityTypeOption.series[0].data = data.map(d => {
|
||||
return { value: d.count, name: d.eventType, itemStyle: { color: getAttackColor(d.eventType) } }
|
||||
})
|
||||
detectionChart.setOption(securityTypeOption)
|
||||
if (chartDom) {
|
||||
let oneColumnWidth = (chartDom.clientWidth * 0.56) - 30
|
||||
if (data.length > 6) {
|
||||
oneColumnWidth = (chartDom.clientWidth * 0.56) / 2 - 30
|
||||
}
|
||||
securityTypeOption.legend.formatter = function (name) {
|
||||
return format.truncateText(name, oneColumnWidth, '12px')
|
||||
}
|
||||
}
|
||||
this.detectionChart.setOption(securityTypeOption)
|
||||
|
||||
const vm = this
|
||||
detectionChart.off('click')
|
||||
detectionChart.on('click', e => {
|
||||
this.detectionChart.off('click')
|
||||
this.detectionChart.on('click', e => {
|
||||
this.getFilter(e.data.name, vm.filterData.securityEvent[2].column)
|
||||
vm.filterData.securityEvent[2].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[2].value, e.data.name)
|
||||
})
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][2].data = []
|
||||
this.filterData[this.pageType][2].showMore = false
|
||||
this.filterData[this.pageType][2].showIndex = 5
|
||||
this.filterData[this.pageType][2].showDisabled = true
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
@@ -504,9 +535,10 @@ export default {
|
||||
count: r.count
|
||||
}))
|
||||
this.isCheckFilterByQ(params, 4)
|
||||
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][4].data)
|
||||
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][4].data)
|
||||
this.filterData[this.pageType][4].showMore = showMore
|
||||
this.filterData[this.pageType][4].showIndex = showIndex
|
||||
this.filterData[this.pageType][4].showDisabled = showDisabled
|
||||
|
||||
const chartDom = document.getElementById(`detectionActiveAttacker${this.pageType}`)
|
||||
let detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
@@ -526,14 +558,18 @@ export default {
|
||||
const vm = this
|
||||
detectionChart.off('click')
|
||||
detectionChart.on('click', e => {
|
||||
vm.filterData.securityEvent[4].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[4].value, e.data[1])
|
||||
if (e.data) {
|
||||
vm.getFilter(e.data[1], vm.filterData.securityEvent[4].column)
|
||||
vm.filterData.securityEvent[4].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[4].value, e.data[1])
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][4].data = []
|
||||
this.filterData[this.pageType][4].showMore = false
|
||||
this.filterData[this.pageType][4].showIndex = 9
|
||||
this.filterData[this.pageType][4].showIndex = 5
|
||||
this.filterData[this.pageType][4].showDisabled = true
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
@@ -543,14 +579,16 @@ export default {
|
||||
const data = res.data.data.result
|
||||
this.filterData[this.pageType][3].data = data.map(r => ({ label: r.victimIp, value: r.victimIp, count: r.count }))
|
||||
this.isCheckFilterByQ(params, 3)
|
||||
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][3].data)
|
||||
const { showMore, showIndex, showDisabled } = this.computeFilterPage(this.filterData[this.pageType][3].data)
|
||||
this.filterData[this.pageType][3].showMore = showMore
|
||||
this.filterData[this.pageType][3].showIndex = showIndex
|
||||
this.filterData[this.pageType][3].showDisabled = showDisabled
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][3].data = []
|
||||
this.filterData[this.pageType][3].showMore = false
|
||||
this.filterData[this.pageType][3].showIndex = 9
|
||||
this.filterData[this.pageType][3].showIndex = 5
|
||||
this.filterData[this.pageType][3].showDisabled = true
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
@@ -631,8 +669,9 @@ export default {
|
||||
},
|
||||
computeFilterPage (data) {
|
||||
return {
|
||||
showMore: data.length > 10,
|
||||
showIndex: 9
|
||||
showMore: data.length > 0,
|
||||
showDisabled: data.length <= 5,
|
||||
showIndex: 5
|
||||
}
|
||||
},
|
||||
queryList (q) {
|
||||
@@ -716,7 +755,7 @@ export default {
|
||||
this.q = ''
|
||||
this.metaList = []
|
||||
}
|
||||
if (this.pageObj.resetPageNo) {
|
||||
if (this.pageObj.resetPageNo && !this.initFlag) {
|
||||
this.pageObj.pageNo = 1
|
||||
} else {
|
||||
this.pageObj.resetPageNo = true
|
||||
@@ -774,11 +813,11 @@ export default {
|
||||
this.search(this.metaList, this.q)
|
||||
},
|
||||
pageNo (val) {
|
||||
this.pageObj.pageNo = val || 1
|
||||
this.pageObj.resetPageNo = false
|
||||
// 初始化时,mounted和pageNo都会调用列表接口,且pageNo先执行,
|
||||
// 初始化保证mounted执行,后续pageNo变动则不影响接口调用
|
||||
if (!this.initFlag) {
|
||||
this.pageObj.pageNo = val || 1
|
||||
this.pageObj.resetPageNo = false
|
||||
this.search(this.metaList, this.q)
|
||||
}
|
||||
},
|
||||
@@ -803,10 +842,28 @@ export default {
|
||||
},
|
||||
resize () {
|
||||
this.chartInit.forEach(e => { e.value.resize() })
|
||||
|
||||
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
|
||||
this.detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
if(this.detectionChart){
|
||||
let securityTypeOption = this.$_.cloneDeep(this.detectionChart.getOption())
|
||||
echarts.dispose(this.detectionChart)
|
||||
if (chartDom) {
|
||||
this.detectionChart = echarts.init(chartDom)
|
||||
let oneColumnWidth = (chartDom.clientWidth * 0.56) - 30
|
||||
if (this.statisticsCategoryData.length > 6) {
|
||||
oneColumnWidth = (chartDom.clientWidth * 0.56) / 2 - 30
|
||||
}
|
||||
securityTypeOption.legend[0].formatter = function (name) {
|
||||
return format.truncateText(name, oneColumnWidth, '12px')
|
||||
}
|
||||
this.detectionChart.setOption(securityTypeOption)
|
||||
}
|
||||
}
|
||||
},
|
||||
jumpNewDetetion () {
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
path: '/detection/policy',
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
@@ -823,11 +880,34 @@ export default {
|
||||
if (obj) {
|
||||
this.filterData[this.pageType][index].value = [obj.value]
|
||||
this.filterData[this.pageType][index].flag = true
|
||||
} else {
|
||||
this.filterData[this.pageType][index].value = []
|
||||
this.filterData[this.pageType][index].flag = true
|
||||
}
|
||||
} else {
|
||||
this.filterData[this.pageType][index].value = []
|
||||
this.filterData[this.pageType][index].flag = true
|
||||
}
|
||||
},
|
||||
getFilter (name, topColumn) {
|
||||
if (topColumn === 'tag') {
|
||||
const params = {
|
||||
column: topColumn,
|
||||
operator: 'has',
|
||||
value: name
|
||||
}
|
||||
this.$refs.search.changeParams([params])
|
||||
} else {
|
||||
const params = {
|
||||
column: topColumn,
|
||||
operator: '=',
|
||||
value: name
|
||||
}
|
||||
this.$refs.search.changeParams([params])
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.emitter.emit('advanced-search')
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -846,7 +926,11 @@ export default {
|
||||
}
|
||||
}
|
||||
this.queryFilter(q)
|
||||
this.initFlag = false
|
||||
if (this.initFlag) {
|
||||
this.timer = setTimeout(() => {
|
||||
this.initFlag = false
|
||||
}, 1000)
|
||||
}
|
||||
this.queryList(q)
|
||||
this.debounceFunc = this.$_.debounce(this.resize, 300)
|
||||
window.addEventListener('resize', this.debounceFunc)
|
||||
@@ -916,78 +1000,6 @@ export default {
|
||||
},
|
||||
timeFilter () {
|
||||
this.search(this.metaList, this.q)
|
||||
},
|
||||
'filterData.securityEvent.0.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
if (!this.filterData.securityEvent[0].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[0].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[0].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.1.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
if (!this.filterData.securityEvent[1].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[1].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[1].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.2.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
if (!this.filterData.securityEvent[2].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[2].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[2].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.3.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
if (!this.filterData.securityEvent[3].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[3].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[3].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.4.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
if (!this.filterData.securityEvent[4].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[4].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[4].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.5.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
if (!this.filterData.securityEvent[5].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[5].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[5].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.performanceEvent.0.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.performanceEvent[0].column, oldValue: o, newValue: n })
|
||||
}
|
||||
},
|
||||
'filterData.performanceEvent.1.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.performanceEvent[1].column, oldValue: o, newValue: n })
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
@@ -995,11 +1007,10 @@ export default {
|
||||
},
|
||||
setup () {
|
||||
const store = useStore()
|
||||
let { params, query, path } = useRoute()
|
||||
let { query, path } = useRoute()
|
||||
// 获取路由跳转过的历史状态,赋值给当前界面,起到保留状态的作用,如浏览器的回退前进等
|
||||
const routerObj = store.getters.getRouterHistoryList.find(item => item.t === query.t)
|
||||
if (routerObj) {
|
||||
params = routerObj.params
|
||||
query = routerObj.query
|
||||
path = routerObj.path
|
||||
|
||||
@@ -1007,15 +1018,16 @@ export default {
|
||||
const newUrl = urlParamsHandler(window.location.href, useRoute().query, query)
|
||||
overwriteUrl(newUrl)
|
||||
}
|
||||
const pageType = params.typeName
|
||||
const pageType = path.replace('/detection/', '')
|
||||
// 获取url携带的range、startTime、endTime
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
// 优先级:url > config.js > 默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.detection || 60)
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = getSecond(startTime)
|
||||
timeFilter.value.endTime = getSecond(endTime)
|
||||
// 如果没有时间参数,就将参数写入url
|
||||
@@ -1025,9 +1037,17 @@ export default {
|
||||
timeFilter.value.startTime = parseInt(startTimeParam)
|
||||
timeFilter.value.endTime = parseInt(endTimeParam)
|
||||
}
|
||||
const pageObj = ref({
|
||||
pageNo: query.pageNo ? parseInt(query.pageNo) : 1,
|
||||
// 是否重置pageNo,在执行新搜索时是true
|
||||
resetPageNo: true,
|
||||
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize,
|
||||
total: 0
|
||||
})
|
||||
return {
|
||||
timeFilter,
|
||||
pageType
|
||||
pageType,
|
||||
pageObj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="new-filter-content-content">
|
||||
<el-checkbox-group v-model="checkEventType" @change="onChangeCategory" style="display: flex;flex-direction: column">
|
||||
<el-checkbox v-for="item in eventTypeList" :key="item.name" class="new-filter-content-checkbox" :label="item.name">
|
||||
{{ item.label }}
|
||||
{{ item.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
@@ -94,13 +94,7 @@ export default {
|
||||
}
|
||||
|
||||
if (data.eventTypeList) {
|
||||
this.eventTypeList = []
|
||||
data.eventTypeList.forEach(item => {
|
||||
const obj = detectionUnitList.eventTypeList.find(d => d.value === item.name)
|
||||
if (obj) {
|
||||
this.eventTypeList.push({ ...item, label: this.$t(obj.label) })
|
||||
}
|
||||
})
|
||||
this.eventTypeList = data.eventTypeList
|
||||
} else {
|
||||
this.eventTypeList = []
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
{{ ruleId ? $t('detection.editEventPolicies') : $t('detection.createEventPolicies') }}
|
||||
</div>
|
||||
|
||||
<!--第一步:General Settings-->
|
||||
<div class="detection-form-content">
|
||||
<!--第一步:General Settings-->
|
||||
<div class="detection-form-collapse">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="1">
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<!--第三步:Trigger-->
|
||||
<div class="detection-form-collapse">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse v-model="activeNames" class="policy-form-trigger">
|
||||
<el-collapse-item name="3">
|
||||
<template #title>
|
||||
<div class="form-collapse-header">
|
||||
@@ -53,16 +53,16 @@
|
||||
</template>
|
||||
|
||||
<div class="form-collapse-content margin-t-18">
|
||||
<el-form v-if="language==='en'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<el-form v-if="language===EN" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<div class="trigger-block-item margin-b-10">
|
||||
<div>At least</div>
|
||||
<el-form-item prop="atLeast">
|
||||
<el-input size="mini" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
<div>times within</div>
|
||||
|
||||
<el-form-item prop="interval">
|
||||
<el-input size="mini" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
<el-form-item prop="interval" class="policy-form-item">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="intervalVal">
|
||||
@@ -79,8 +79,8 @@
|
||||
|
||||
<div class="trigger-block-item">
|
||||
<div>With the counter resetting after no activity for</div>
|
||||
<el-form-item prop="resetInterval">
|
||||
<el-input size="mini" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
<el-form-item prop="resetInterval" class="policy-form-item">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="resetIntervalVal">
|
||||
<el-select v-model="triggerObj.resetIntervalVal" class="form-trigger__select" placeholder=" " size="mini">
|
||||
@@ -94,11 +94,11 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<el-form v-if="language==='cn'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<el-form v-if="language===ZH" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<div class="trigger-block-item margin-b-10">
|
||||
在
|
||||
<el-form-item prop="interval">
|
||||
<el-input size="mini" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="intervalVal">
|
||||
@@ -113,7 +113,7 @@
|
||||
</el-form-item>
|
||||
内至少发生
|
||||
<el-form-item prop="atLeast">
|
||||
<el-input size="mini" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
次
|
||||
</div>
|
||||
@@ -121,7 +121,7 @@
|
||||
<div class="trigger-block-item">
|
||||
若连续
|
||||
<el-form-item prop="resetInterval">
|
||||
<el-input size="mini" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="resetIntervalVal">
|
||||
<el-select v-model="triggerObj.resetIntervalVal" class="form-trigger__select" placeholder=" " size="mini">
|
||||
@@ -136,18 +136,20 @@
|
||||
不活跃将重置计数
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="form-setting__btn1">
|
||||
<div class="btn1">
|
||||
<el-button @click="createPolicy('')">{{ $t('overall.save') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="createPolicy('enabled')">{{ $t('overall.save') }} & {{ $t('detection.create.enablePolicy') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="policy-form__footer">
|
||||
<div class="policy-form__footer__btn">
|
||||
<div class="btn1">
|
||||
<el-button @click="cancel">{{ $t('overall.cancel') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="createPolicy">{{ $t('overall.save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -160,11 +162,45 @@ import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import { getDurationsTimeByType, getTimeByDurations } from '@/utils/date-util'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { storageKey, detectionUnitList } from '@/utils/constants'
|
||||
import { storageKey, detectionUnitList, ZH, EN } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'DetectionForm',
|
||||
data () {
|
||||
const intervalValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, value, this.triggerObj.intervalVal)
|
||||
if (!obj.flag && obj.msg) {
|
||||
callback(new Error(obj.msg))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const intervalValValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, this.triggerObj.intervalVal, value)
|
||||
if (!obj.flag && obj.msg) {
|
||||
this.$refs.form3.validateField('interval')
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const resetIntervalValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, value, this.triggerObj.resetIntervalVal)
|
||||
if (!obj.flag && obj.msg) {
|
||||
callback(new Error(obj.msg))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const resetIntervalValValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, this.triggerObj.resetIntervalVal, value)
|
||||
if (!obj.flag && obj.msg) {
|
||||
this.$refs.form3.validateField('resetInterval')
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
activeNames: ['1'],
|
||||
rules: {
|
||||
@@ -180,6 +216,10 @@ export default {
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: intervalValidator,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
intervalVal: [
|
||||
@@ -187,6 +227,10 @@ export default {
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'change'
|
||||
},
|
||||
{
|
||||
validator: intervalValValidator,
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
resetInterval: [
|
||||
@@ -194,6 +238,10 @@ export default {
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: resetIntervalValidator,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
resetIntervalVal: [
|
||||
@@ -201,13 +249,19 @@ export default {
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'change'
|
||||
},
|
||||
{
|
||||
validator: resetIntervalValValidator,
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
},
|
||||
intervalList: [],
|
||||
editObj: {},
|
||||
isComplete: true, // 参数完整标识,默认完整(照顾编辑模式),false即不完整
|
||||
language: 'en'
|
||||
language: EN,
|
||||
ZH,
|
||||
EN
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -216,10 +270,10 @@ export default {
|
||||
Loading
|
||||
},
|
||||
mounted () {
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
if (this.language === 'en') {
|
||||
this.language = localStorage.getItem(storageKey.language) || EN
|
||||
if (this.language === EN) {
|
||||
this.intervalList = detectionUnitList.intervalList
|
||||
} else if (this.language === 'cn') {
|
||||
} else if (this.language === ZH) {
|
||||
this.intervalList = detectionUnitList.intervalListCN
|
||||
}
|
||||
this.getDetailInfo()
|
||||
@@ -280,7 +334,7 @@ export default {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
path: '/detection/policy',
|
||||
query: {
|
||||
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
@@ -317,7 +371,7 @@ export default {
|
||||
}
|
||||
},
|
||||
/** 创建policy */
|
||||
createPolicy (flag) {
|
||||
createPolicy () {
|
||||
const settingLen = Object.keys(this.settingObj).length
|
||||
const ruleLen = Object.keys(this.ruleObj).length
|
||||
|
||||
@@ -326,9 +380,6 @@ export default {
|
||||
if (valid) {
|
||||
// 最终提交form
|
||||
const formObj = this.$_.cloneDeep({ ...this.settingObj, ruleConfig: JSON.stringify(this.ruleObj), ruleTrigger: this.triggerObj })
|
||||
if (flag) {
|
||||
formObj.status = 1
|
||||
}
|
||||
// 将时间转为参数所需,如5分钟转为PT5M
|
||||
formObj.ruleTrigger.resetInterval = getDurationsTimeByType(formObj.ruleTrigger.resetInterval, formObj.ruleTrigger.resetIntervalVal)
|
||||
formObj.ruleTrigger.interval = getDurationsTimeByType(formObj.ruleTrigger.interval, formObj.ruleTrigger.intervalVal)
|
||||
@@ -352,7 +403,7 @@ export default {
|
||||
})
|
||||
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
path: '/detection/policy',
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
@@ -377,12 +428,18 @@ export default {
|
||||
message: this.$t('tip.saveSuccess')
|
||||
})
|
||||
|
||||
const { query } = this.$route
|
||||
const queryInfo = {
|
||||
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
}
|
||||
if (query.name && query.id) {
|
||||
queryInfo.ruleId = query.id
|
||||
queryInfo.name = this.settingObj.name
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
query: {
|
||||
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
}
|
||||
path: '/detection/policy',
|
||||
query: queryInfo
|
||||
})
|
||||
} else {
|
||||
console.error(response.data.message)
|
||||
@@ -417,7 +474,157 @@ export default {
|
||||
})
|
||||
}
|
||||
this.$message.error(this.$t('detection.create.informationFilled'))
|
||||
},
|
||||
handleIntervalByDateType (rule, value, type) {
|
||||
if (value && (type === 'hours' || type === '小时')) {
|
||||
if (parseInt(value) <= 24) {
|
||||
return { flag: true }
|
||||
} else {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeHours') }
|
||||
}
|
||||
}
|
||||
if (value && (type === 'minutes' || type === '分钟')) {
|
||||
if (parseInt(value) <= 1440) {
|
||||
return { flag: true }
|
||||
} else {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeMinutes') }
|
||||
}
|
||||
}
|
||||
if (value && (type === 'seconds' || type === '秒')) {
|
||||
if (parseInt(value) <= 86400) {
|
||||
return { flag: true }
|
||||
} else {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeSeconds') }
|
||||
}
|
||||
}
|
||||
},
|
||||
cancel () {
|
||||
const { query } = this.$route
|
||||
const queryInfo = {
|
||||
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
}
|
||||
if (query.name && query.id) {
|
||||
queryInfo.ruleId = query.id
|
||||
queryInfo.name = this.settingObj.name
|
||||
}
|
||||
this.$refs.form3.validate(valid => {
|
||||
if (this.settingObj.settingNoContinue || this.ruleObj.settingNoContinue || !valid) {
|
||||
this.$confirm(this.$t('tip.leavePage'), {
|
||||
confirmButtonText: this.$t('tip.confirm'),
|
||||
cancelButtonText: this.$t('overall.cancel'),
|
||||
message: this.$t('tip.leavePageTips'),
|
||||
title: this.$t('tip.leavePage'),
|
||||
type: 'warning',
|
||||
iconClass: 'width:0px;height:0px;',
|
||||
customClass: 'del-model'
|
||||
}).then(() => {
|
||||
this.$router.push({
|
||||
path: '/detection/policy',
|
||||
query: queryInfo
|
||||
})
|
||||
}).catch(() => {})
|
||||
} else {
|
||||
this.$router.push({
|
||||
path: '/detection/policy',
|
||||
query: queryInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.del-model {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 0 !important;
|
||||
width: 480px !important;
|
||||
height: 190px;
|
||||
|
||||
.el-message-box__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 42px;
|
||||
background: #F7F7F7;
|
||||
box-shadow: 0 1px 0 0 rgba(53, 54, 54, 0.08);
|
||||
padding-left: 20px;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
|
||||
.el-message-box__headerbtn {
|
||||
display: flex !important;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
padding-right: 5px !important;
|
||||
|
||||
i {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__title {
|
||||
font-size: 14px !important;
|
||||
color: #353636;
|
||||
letter-spacing: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__content {
|
||||
height: 96px;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
letter-spacing: 0;
|
||||
line-height: 22px;
|
||||
font-weight: 400;
|
||||
padding-top: 8px;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
|
||||
.el-message-box__message {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__btns {
|
||||
height: 52px;
|
||||
border-top: 1px solid #eee;
|
||||
box-shadow: inset 0 -1px 0 0 rgba(0, 0, 0, 0.07);
|
||||
padding: 11px 0 12px !important;
|
||||
|
||||
.el-button--small {
|
||||
padding: 8px 21px !important;
|
||||
line-height: 12px;
|
||||
font-family: NotoSansHans-Medium !important;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.el-button:nth-child(1) {
|
||||
margin-right: 20px;
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
color: #353636;
|
||||
}
|
||||
|
||||
.el-button:nth-child(2) {
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
margin-right: 20px;
|
||||
margin-left: 0 !important;
|
||||
background-color: #2d8cf0;
|
||||
border-color: #2d8cf0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
ref="dataTable"
|
||||
:data="tableData"
|
||||
height="100%"
|
||||
tooltip-effect="light"
|
||||
border
|
||||
empty-text=" "
|
||||
@header-dragend="dragend"
|
||||
@@ -31,6 +32,7 @@
|
||||
:sortable="item.sortable"
|
||||
:width="`${item.width}`"
|
||||
class="data-column"
|
||||
:show-overflow-tooltip="['library'].indexOf(item.prop) > -1"
|
||||
>
|
||||
<template #header>
|
||||
<span class="data-column__span">{{ item.label }}</span>
|
||||
@@ -56,9 +58,6 @@
|
||||
<template v-else-if="item.prop === 'category'">
|
||||
{{ changeCategory(scope.row[item.prop]) }}
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'eventType'">
|
||||
{{ changeEventType(scope.row[item.prop]) }}
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'description'">
|
||||
<div style="padding-right: 20px">{{ scope.row[item.prop] }}</div>
|
||||
</template>
|
||||
@@ -85,6 +84,7 @@ import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
import _ from 'lodash'
|
||||
import { detectionUnitList } from '@/utils/constants'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'DetectionTable',
|
||||
@@ -92,6 +92,9 @@ export default {
|
||||
isNoData: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
policyDetail: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
mixins: [table],
|
||||
@@ -149,6 +152,17 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
const { query } = useRoute()
|
||||
// 初始化勾选状态
|
||||
if (query.name && query.ruleId && this.policyDetail) {
|
||||
this.selectionChange([this.policyDetail])
|
||||
const timer = setTimeout(() => {
|
||||
this.$refs.dataTable.toggleAllSelection([this.policyDetail])
|
||||
clearTimeout(timer)
|
||||
}, 400)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tableData: {
|
||||
immediate: true,
|
||||
@@ -181,16 +195,6 @@ export default {
|
||||
}
|
||||
return label
|
||||
}
|
||||
},
|
||||
changeEventType (value) {
|
||||
if (value) {
|
||||
const obj = detectionUnitList.eventTypeList.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -92,7 +110,7 @@
|
||||
<div class="entity-overview">
|
||||
<div class="overview-left">
|
||||
<span class="overview-left-loading">
|
||||
<span class="overview-left-loading-span">Domain</span>
|
||||
<span class="overview-left-loading-span">{{$t('overall.domain')}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="overview-right">
|
||||
@@ -174,6 +192,8 @@ import Parser from '@/components/advancedSearch/meta/parser'
|
||||
import { handleErrorTip } from '@/components/advancedSearch/meta/error'
|
||||
import { columnList } from '@/utils/static-data'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { columnType } from '@/components/advancedSearch/meta/meta'
|
||||
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
|
||||
|
||||
export default {
|
||||
name: 'entity-explorer',
|
||||
@@ -181,7 +201,8 @@ export default {
|
||||
Loading,
|
||||
ExplorerSearch,
|
||||
EntityFilter,
|
||||
EntityList
|
||||
EntityList,
|
||||
DateTimeRange
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -200,13 +221,6 @@ export default {
|
||||
entityIpNew: '-',
|
||||
entityIpActive: '-',
|
||||
|
||||
// pageObj: {
|
||||
// pageNo: 1,
|
||||
// // 是否重置pageNo,在执行新搜索时是true
|
||||
// resetPageNo: true,
|
||||
// pageSize: defaultPageSize,
|
||||
// total: 0
|
||||
// },
|
||||
newFilterData: [
|
||||
{
|
||||
icon: 'cn-icon cn-icon-registration-country',
|
||||
@@ -284,12 +298,13 @@ export default {
|
||||
initFlag: true, // 初始化标志,避免初始化时pageSize和pageNo会调用搜索
|
||||
timer: null, // 初始化标志的延时器,需要销毁
|
||||
summaryCount: {
|
||||
total: 0,
|
||||
totalCount: 0,
|
||||
domainCount: 0,
|
||||
ipCount: 0,
|
||||
appCount: 0
|
||||
},
|
||||
loadingCount: false // 实体基数统计的loading
|
||||
loadingCount: false, // 实体基数统计的loading
|
||||
keywordList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -302,6 +317,13 @@ export default {
|
||||
},
|
||||
reload (s, e, v) {
|
||||
this.dateTimeRangeChange(s, e, v)
|
||||
const { startTime, endTime } = getNowTime(this.timeFilter.dateRangeValue)
|
||||
const newParam = {
|
||||
range: this.timeFilter.dateRangeValue,
|
||||
startTime: getSecond(startTime),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
this.reloadUrl(newParam)
|
||||
},
|
||||
// methods
|
||||
dateTimeRangeChange (s, e, v) {
|
||||
@@ -361,6 +383,7 @@ export default {
|
||||
this.q = ''
|
||||
this.metaList = []
|
||||
}
|
||||
this.getKeyword(param.keywordList)
|
||||
|
||||
// 参数q,避免切换页码时,地址栏参数q为空
|
||||
let urlQ = ''
|
||||
@@ -368,6 +391,8 @@ export default {
|
||||
urlQ = encodeURI(param.str)
|
||||
} else if (this.q) {
|
||||
urlQ = encodeURI(this.q)
|
||||
} else if (!this.q) {
|
||||
this.isHideRelatedEntities = false
|
||||
}
|
||||
|
||||
// 在非列表模式下选择tag模式,在地址栏输入内容时将mode添加到地址栏
|
||||
@@ -389,7 +414,7 @@ export default {
|
||||
if (!this.showList) {
|
||||
// 首页进入搜索时重载页面,视觉上进入列表页面
|
||||
this.$router.push({
|
||||
path: '/entityExplorer',
|
||||
path: '/entity',
|
||||
query: {
|
||||
listMode: this.listMode,
|
||||
q: urlQ,
|
||||
@@ -417,19 +442,21 @@ export default {
|
||||
},
|
||||
pageSize (val) {
|
||||
this.pageObj.pageSize = val
|
||||
const keywordList = this.getKeywordListByMetaList(this.metaList)
|
||||
if (this.initFlag) {
|
||||
if (val !== 20) {
|
||||
this.search({ metaList: this.metaList, q: this.q })
|
||||
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
|
||||
}
|
||||
} else {
|
||||
this.search({ metaList: this.metaList, q: this.q })
|
||||
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
|
||||
}
|
||||
},
|
||||
pageNo (val) {
|
||||
if (!this.initFlag) {
|
||||
this.pageObj.pageNo = val
|
||||
this.pageObj.resetPageNo = false
|
||||
this.search({ metaList: this.metaList, q: this.q })
|
||||
const keywordList = this.getKeywordListByMetaList(this.metaList)
|
||||
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList })
|
||||
}
|
||||
},
|
||||
// 点击上一页箭头
|
||||
@@ -489,8 +516,8 @@ export default {
|
||||
/** 新版查询filter数据 */
|
||||
queryFilterNew (params) {
|
||||
const queryParams = {
|
||||
// startTime: getSecond(params.startTime),
|
||||
// endTime: getSecond(params.endTime),
|
||||
startTime: getSecond(params.startTime),
|
||||
endTime: getSecond(params.endTime),
|
||||
resource: params.q || ''
|
||||
}
|
||||
this.loadingLeft = true
|
||||
@@ -548,16 +575,16 @@ export default {
|
||||
const queryParams = {
|
||||
pageSize: params.pageSize,
|
||||
pageNo: params.pageNo,
|
||||
// startTime: getSecond(params.startTime),
|
||||
// endTime: getSecond(params.endTime),
|
||||
resource: params.q || ''
|
||||
startTime: getSecond(params.startTime),
|
||||
endTime: getSecond(params.endTime),
|
||||
resource: params.q || '',
|
||||
hideRelated: this.isHideRelatedEntities
|
||||
}
|
||||
axios.get(api.entity.entityList.list, { params: queryParams }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.listData = []
|
||||
this.$nextTick(() => {
|
||||
this.listData = response.data.data.list
|
||||
this.pageObj.total = response.data.data.total
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response.data.message)
|
||||
@@ -570,19 +597,21 @@ export default {
|
||||
queryCount (params) {
|
||||
this.loadingCount = true
|
||||
const queryParams = {
|
||||
// startTime: getSecond(params.startTime),
|
||||
// endTime: getSecond(params.endTime),
|
||||
resource: params.q || ''
|
||||
startTime: getSecond(params.startTime),
|
||||
endTime: getSecond(params.endTime),
|
||||
resource: params.q || '',
|
||||
hideRelated: this.isHideRelatedEntities
|
||||
}
|
||||
axios.get(api.entity.entityList.summaryCount, { params: queryParams }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.summaryCount = response.data.data
|
||||
this.pageObj.total = response.data.data.totalCount
|
||||
} else {
|
||||
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
this.summaryCount = { totalCount: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
}
|
||||
}).catch(e => {
|
||||
console.log(e)
|
||||
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
console.error(e)
|
||||
this.summaryCount = { totalCount: 0, domainCount: 0, ipCount: 0, appCount: 0 }
|
||||
}).finally(() => {
|
||||
this.loadingCount = false
|
||||
})
|
||||
@@ -675,11 +704,13 @@ export default {
|
||||
}
|
||||
}
|
||||
const parser = new Parser(columnList)
|
||||
const metaList = parser.parseStr(_.cloneDeep(str)).metaList
|
||||
const keywordList = this.getKeywordListByMetaList(metaList)
|
||||
const keyInfo = parser.comparedEntityKey(parser.handleEntityTypeByStr(str))
|
||||
if (keyInfo.isKey) {
|
||||
const errorList = parser.validateStr(keyInfo.key)
|
||||
if (_.isEmpty(errorList)) {
|
||||
this.search({ ...parser.parseStr(keyInfo.key), str: str })
|
||||
this.search({ ...parser.parseStr(keyInfo.key), str: str, keywordList: keywordList })
|
||||
} else {
|
||||
this.$message.error(handleErrorTip(errorList[0]))
|
||||
}
|
||||
@@ -727,10 +758,55 @@ export default {
|
||||
}).catch((e) => {
|
||||
}).finally(() => {
|
||||
})
|
||||
},
|
||||
getKeywordListByMetaList (metaList) {
|
||||
if (metaList) {
|
||||
const keywordList = []
|
||||
metaList.forEach(item => {
|
||||
if (item.column && item.column.type === columnType.fullText) {
|
||||
keywordList.push({ type: item.column.type, value: item.column.label })
|
||||
} else if (item.column && item.column.type === columnType.string) {
|
||||
keywordList.push({ type: item.column.type, value: item.value.value })
|
||||
}
|
||||
})
|
||||
return keywordList
|
||||
}
|
||||
},
|
||||
getKeyword (list) {
|
||||
if (list) {
|
||||
const metaList = JSON.parse(JSON.stringify(list))
|
||||
const keyList = []
|
||||
metaList.forEach(item => {
|
||||
if (item.value) {
|
||||
keyList.push({ type: item.type, value: this.getKeyValue(item.value) })
|
||||
}
|
||||
})
|
||||
this.keywordList = keyList
|
||||
} else {
|
||||
this.keywordList = []
|
||||
}
|
||||
},
|
||||
getKeyValue (str) {
|
||||
if (str[0] === "'" && str[str.length - 1] === "'") {
|
||||
str = str.substring(1, str.length)
|
||||
str = str.substring(0, str.length - 1)
|
||||
}
|
||||
if (str[0] === '%') {
|
||||
str = str.substring(1, str.length)
|
||||
}
|
||||
if (str[str.length - 1] === '%') {
|
||||
str = str.substring(0, str.length - 1)
|
||||
}
|
||||
return str
|
||||
},
|
||||
hideRelatedEntities (e) {
|
||||
this.isHideRelatedEntities = e
|
||||
this.reloadUrl({ hideRelated: e })
|
||||
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
|
||||
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getEntityIndexData()
|
||||
let { q, listMode } = this.$route.query
|
||||
|
||||
// 如果地址栏有listMode,即列表页,并非首页,则开始搜索
|
||||
@@ -741,7 +817,13 @@ export default {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
// %位置不为0,即内容包含非英文时
|
||||
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
let str1 = ''
|
||||
if (q) {
|
||||
str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
}
|
||||
if (q && q.indexOf('+') > -1) {
|
||||
q = q.replace('+', '')
|
||||
}
|
||||
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
@@ -751,6 +833,10 @@ export default {
|
||||
this.$store.commit('resetScoreBase')
|
||||
this.queryScoreBase()
|
||||
}
|
||||
|
||||
if (!this.showList) {
|
||||
this.getEntityIndexData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
timeFilter () {
|
||||
@@ -763,11 +849,11 @@ export default {
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
// 优先级:url > config.js > 默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.entity.list || 60)
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
} else {
|
||||
@@ -781,9 +867,11 @@ export default {
|
||||
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize,
|
||||
total: 0
|
||||
})
|
||||
const isHideRelatedEntities = ref(query.hideRelated ? JSON.parse(query.hideRelated) : false) // 隐藏相关实体,默认false不隐藏
|
||||
return {
|
||||
timeFilter,
|
||||
pageObj
|
||||
pageObj,
|
||||
isHideRelatedEntities
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
|
||||
@@ -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,9 +320,21 @@ export default {
|
||||
methods: {
|
||||
getMillisecond,
|
||||
dateFormatByAppearance,
|
||||
getQueryParams () {
|
||||
return {
|
||||
resource: this.entity.entityValue
|
||||
getQueryParams (dateRangeValue) {
|
||||
if (dateRangeValue) {
|
||||
// range取 config.js 中配置的值
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
return {
|
||||
startTime: getSecond(startTime),
|
||||
endTime: getSecond(endTime),
|
||||
resource: this.entity.entityValue
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime),
|
||||
resource: this.entity.entityValue
|
||||
}
|
||||
}
|
||||
},
|
||||
getPerformanceQueryParams () {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
<div class="overview__content">
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
|
||||
<div class="row__content">{{$_.get(entityData, 'category.categoryName', '-') || '-'}}</div>
|
||||
<div class="row__content" v-high-light="keywordList">{{ $_.get(entityData, 'category.categoryName', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('entities.domainDetail.categoryGroup')}}</div>
|
||||
<div class="row__content">{{$_.get(entityData, 'category.categoryGroup', '-') || '-'}}</div>
|
||||
<div class="row__content" v-high-light="keywordList">{{ $_.get(entityData, 'category.categoryGroup', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('entities.reputationLevel')}}</div>
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('entities.org')}}</div>
|
||||
<div class="row__content">{{$_.get(entityData, 'whois.registrantOrg', '-') || '-'}}</div>
|
||||
<div class="row__content" v-high-light="keywordList">{{ $_.get(entityData, 'whois.registrantOrg', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('entities.icpCompanyName')}}</div>
|
||||
@@ -97,7 +97,7 @@
|
||||
<loading :loading="loadingRelationshipOne" size="small" style="left: 1rem;"></loading>
|
||||
</div>
|
||||
|
||||
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
|
||||
<div class="data-item high-light-block" v-high-light="keywordList" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
|
||||
{{item.value}}
|
||||
</div>
|
||||
<div v-if="relationshipDataOne.length===0 && !loadingRelationshipOne">-</div>
|
||||
@@ -106,7 +106,7 @@
|
||||
<div class="data-item show-more-related" id="related-app-more" @click.stop="showMoreApp" style="position: relative">...</div>
|
||||
<div v-if="isShowMoreApp" class="app-popover_block" id="showRelatedApp">
|
||||
<div class="popover-content" v-for="(item, index) in relationshipDataOne" :key="index">
|
||||
<span v-if="!item.show">{{item.value}}</span>
|
||||
<span v-if="!item.show" class="high-light-block" v-high-light="keywordList">{{item.value}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,7 +119,7 @@
|
||||
<div v-if="loadingRelationshipTwo" style="position: relative;width: 450px;">
|
||||
<loading :loading="loadingRelationshipTwo" size="small" style="left: 1rem;"></loading>
|
||||
</div>
|
||||
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
|
||||
<div class="data-item high-light-block" v-high-light="keywordList" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
|
||||
{{item.value}}
|
||||
</div>
|
||||
<div v-if="relationshipDataTwo.length===0 && !loadingRelationshipTwo">-</div>
|
||||
@@ -128,7 +128,7 @@
|
||||
<div class="data-item show-more-related" id="related-domain-more" @click.stop="showMoreDomain" style="position: relative">...</div>
|
||||
<div v-if="isShowMoreDomain" class="domain-popover_block" id="showRelatedDomain">
|
||||
<div v-for="(item, index) in relationshipDataTwo" :key="index">
|
||||
<span v-if="!item.show">{{item.value}}</span>
|
||||
<span v-if="!item.show" class="high-light-block" v-high-light="keywordList">{{item.value}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,7 +149,7 @@
|
||||
<div class="row__content">{{entityData.performanceNum}}</div>
|
||||
</div>
|
||||
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
|
||||
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
|
||||
<div class="row__label row__label--width130">{{performance.startTime ? dateFormatByAppearance(Number(performance.startTime)) : '-'}}</div>
|
||||
<div class="row__content row__content--width90">
|
||||
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
|
||||
</div>
|
||||
@@ -176,7 +176,7 @@
|
||||
</div>
|
||||
|
||||
<div class="overview__row overview__row--small-font" v-for="(security, i) in entityData.securityList" :key="i">
|
||||
<div class="row__label row__label--width130">{{dateFormatByAppearance(getMillisecond(security.startTime)) || '-'}}</div>
|
||||
<div class="row__label row__label--width130">{{security.startTime ? dateFormatByAppearance(Number(security.startTime)) : '-'}}</div>
|
||||
<div class="row__content row__content--width90">
|
||||
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
|
||||
</div>
|
||||
@@ -219,7 +219,7 @@ import Chart from '@/views/charts/Chart'
|
||||
import _ from 'lodash'
|
||||
import axios from 'axios'
|
||||
import relatedServer from '@/mixins/relatedServer'
|
||||
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
|
||||
import { dateFormatByAppearance, getMillisecond, getSecond, getNowTime } from '@/utils/date-util'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -229,6 +229,9 @@ export default {
|
||||
Loading,
|
||||
Chart
|
||||
},
|
||||
props: {
|
||||
keywordList: Array
|
||||
},
|
||||
mixins: [entityDetailMixin, relatedServer],
|
||||
data () {
|
||||
return {
|
||||
@@ -321,16 +324,21 @@ export default {
|
||||
methods: {
|
||||
getMillisecond,
|
||||
dateFormatByAppearance,
|
||||
getQueryParams () {
|
||||
return {
|
||||
resource: this.entity.entityValue
|
||||
}
|
||||
},
|
||||
getQueryParamsWithTime () {
|
||||
return {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime),
|
||||
domain: this.entity.entityValue
|
||||
getQueryParams (dateRangeValue) {
|
||||
if (dateRangeValue) {
|
||||
// range取 config.js 中配置的值
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
return {
|
||||
startTime: getSecond(startTime),
|
||||
endTime: getSecond(endTime),
|
||||
resource: this.entity.entityValue
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime),
|
||||
resource: this.entity.entityValue
|
||||
}
|
||||
}
|
||||
},
|
||||
getPerformanceQueryParams () {
|
||||
@@ -352,7 +360,7 @@ export default {
|
||||
})
|
||||
},
|
||||
getBasicProperties () {
|
||||
axios.get(this.basicProperties, { params: this.getQueryParamsWithTime() }).then(response => {
|
||||
axios.get(this.basicProperties, { params: this.getQueryParams() }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.entityData = {
|
||||
...this.entityData,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user