CN-1173: 检测功能UI开发
This commit is contained in:
@@ -89,3 +89,9 @@
|
|||||||
|
|
||||||
@import "views/entityExplorer/graphRightListBlock";
|
@import "views/entityExplorer/graphRightListBlock";
|
||||||
@import "views/entityExplorer/graphRightDetailBlock";
|
@import "views/entityExplorer/graphRightDetailBlock";
|
||||||
|
|
||||||
|
@import "views/detections/detection-tools";
|
||||||
|
@import "views/detections/detection-drawer";
|
||||||
|
@import "views/detections/detection-table";
|
||||||
|
@import "views/detections/detection-create/detection-form";
|
||||||
|
@import "views/detections/detection-create/detection-form-setting";
|
||||||
|
|||||||
@@ -0,0 +1,515 @@
|
|||||||
|
.form-setting__block {
|
||||||
|
width: 620px;
|
||||||
|
|
||||||
|
.el-form-item__label {
|
||||||
|
height: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-mode {
|
||||||
|
width: 300px;
|
||||||
|
height: 125px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid rgba(226, 229, 236, 1);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 20px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.block-mode-left {
|
||||||
|
line-height: 16px;
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
.block-mode-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-mode-right {
|
||||||
|
width: 240px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.block-mode-title {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-mode-content {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #717171;
|
||||||
|
line-height: 15px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-mode-btn, .block-mode-btn-active {
|
||||||
|
width: 240px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
text-align: center;
|
||||||
|
background: #F5F6F7;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-mode-btn-active {
|
||||||
|
background: #7E9F54;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-title {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-title1 {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-setting__select {
|
||||||
|
width: 620px !important;
|
||||||
|
|
||||||
|
.el-input--mini .el-input__inner {
|
||||||
|
height: 24px !important;
|
||||||
|
line-height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-setting__input {
|
||||||
|
&.el-input {
|
||||||
|
height: 24px !important;
|
||||||
|
line-height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-setting__textarea {
|
||||||
|
.el-textarea__inner {
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-switch__label, .form-setting__block .el-switch__label {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-dimension {
|
||||||
|
width: 620px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
border: 1px solid #e2e5ec;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.block-dimension-tag {
|
||||||
|
width: auto;
|
||||||
|
height: 19px;
|
||||||
|
line-height: 19px;
|
||||||
|
background: #E5E8EB;
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0.6;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input--mini .el-input__inner {
|
||||||
|
height: 24px !important;
|
||||||
|
line-height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-setting__block-key {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.block-key {
|
||||||
|
height: 28px;
|
||||||
|
background: #F5F8FA;
|
||||||
|
border: 1px solid rgba(222, 222, 222, 1);
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(51, 51, 51, 0.02);
|
||||||
|
border-radius: 2px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-left: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #575757;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-setting__btn, .form-setting__btn1 {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
height: 30px !important;
|
||||||
|
min-height: 30px !important;
|
||||||
|
line-height: 30px !important;
|
||||||
|
background: #38ACD2;
|
||||||
|
border: 1px solid rgba(46, 136, 166, 1);
|
||||||
|
border-radius: 2px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-setting__btn1 {
|
||||||
|
.el-button {
|
||||||
|
padding: 0 11px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-drawer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
border-color: #DEDEDE !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition-filter-block {
|
||||||
|
border: 1px #E2E5EC solid;
|
||||||
|
min-height: 196px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 12px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.definition-filter-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
.filter-item__select {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item__input {
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input--mini .el-input__inner {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 12px !important;
|
||||||
|
font-variant-caps: all-small-caps;
|
||||||
|
color: #575757;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-block-footer {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 12px;
|
||||||
|
font-variant-caps: all-small-caps;
|
||||||
|
color: #575757;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-filter-add {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
background: #F5F8FA;
|
||||||
|
border: 1px solid rgba(222, 222, 222, 1);
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(51, 51, 51, 0.02);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #575757;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-top-key {
|
||||||
|
width: 396px;
|
||||||
|
|
||||||
|
.el-drawer__body {
|
||||||
|
border: 1px #E2E5EC solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-overlay {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer, .rtl {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-table {
|
||||||
|
.el-table th > .cell, .el-table .cell {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table th {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0 0 0 12px;
|
||||||
|
color: #353636;
|
||||||
|
background: #F9F9F9;
|
||||||
|
height: 30px;
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__body td {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 0 0 0 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
height: 28px;
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table::before {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.definition-condition-block {
|
||||||
|
border: 1px #E2E5EC solid;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.condition-title {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
height: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition__select {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
padding-left: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition__select__icon {
|
||||||
|
width: 4px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-metric {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.condition-metric-item1 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.metric-item1__text {
|
||||||
|
height: 24px;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 12px;
|
||||||
|
font-variant-caps: all-small-caps;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-item1-close {
|
||||||
|
color: #E26154;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-item1-close-disable {
|
||||||
|
color: #ecb2ad;
|
||||||
|
margin-left: 12px;
|
||||||
|
cursor: no-drop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-metric-item2 {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.metric-item2__select {
|
||||||
|
margin-right: 12px;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-item2__input {
|
||||||
|
width: 112px;
|
||||||
|
margin-right: 12px;
|
||||||
|
height: 24px !important;
|
||||||
|
margin-top: -1px !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
width: 112px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-divider--horizontal {
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition__select {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
padding-left: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition__select__icon {
|
||||||
|
width: 4px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-add {
|
||||||
|
width: 100%;
|
||||||
|
height: 24px;
|
||||||
|
background: #F5F8FA;
|
||||||
|
border: 1px solid rgba(222, 222, 222, 1);
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(51, 51, 51, 0.02);
|
||||||
|
border-radius: 2px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
font-variant-caps: all-small-caps;
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-divider {
|
||||||
|
border: 1px #ECECEC dashed;
|
||||||
|
|
||||||
|
.el-divider__text {
|
||||||
|
padding: 0 4px;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #BEBEBE;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-r-8 {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-b-20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-b-10 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-t-18 {
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
.detection-form {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.detection-form-header {
|
||||||
|
font-family: NotoSansHans-Black;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 900;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-form-content {
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
|
||||||
|
.detection-form-collapse {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 1200px;
|
||||||
|
|
||||||
|
.el-collapse-item__header {
|
||||||
|
width: 1200px !important;
|
||||||
|
height: 56px !important;
|
||||||
|
background: #FFFFFF !important;
|
||||||
|
border-left: 1px solid #EFF2F5;
|
||||||
|
border-right: 1px solid #EFF2F5;
|
||||||
|
//box-shadow: 0 2px 4px 0 rgba(51,51,51,0.02) !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: inherit;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-collapse-item__wrap {
|
||||||
|
border: 1px solid #EFF2F5;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-collapse-header {
|
||||||
|
margin-left: 26px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.form-collapse-header-no, .form-collapse-header-no-active {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: 0.5s all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-collapse-header-no {
|
||||||
|
background: #E2E5EC;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-collapse-header-no-active {
|
||||||
|
background: #38ACD2;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-collapse-header-title {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-collapse-content {
|
||||||
|
padding: 0 20px 0 46px;
|
||||||
|
|
||||||
|
.trigger-block {
|
||||||
|
width: 620px;
|
||||||
|
height: 90px;
|
||||||
|
padding: 16px 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px #E2E5EC solid;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
.trigger-block-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input--mini .el-input__inner {
|
||||||
|
width: 112px;
|
||||||
|
margin: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select .el-input .el-select__caret {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-trigger__select {
|
||||||
|
margin-left: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item__content {
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input--mini, .el-input--mini .el-input__inner {
|
||||||
|
height: 24px !important;
|
||||||
|
line-height: 24px !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-collapse-item__content {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
.detection-drawer {
|
||||||
|
padding: 20px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
.el-drawer {
|
||||||
|
width: 440px !important;
|
||||||
|
}
|
||||||
|
.el-collapse-item__content {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-overlay {
|
||||||
|
top: 100px !important;
|
||||||
|
background-color: rgba(0, 0, 0, 0.16) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-drawer-title, .basic-function-value, basic-description-value {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #717171;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 14px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-basic {
|
||||||
|
.drawer-basic-header {
|
||||||
|
font-family: NotoSansHans-Black;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 900;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.drawer-basic-id {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-basic-function, .drawer-basic-description {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-drawer-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-description-value {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.basic-function-value {
|
||||||
|
color: #046ECA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-drawer-collapse {
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid rgba(226, 229, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
//box-shadow: 0 1px 0 0 rgba(226, 229, 236, 1);
|
||||||
|
|
||||||
|
.el-collapse-item__header {
|
||||||
|
height: 32px !important;
|
||||||
|
background-color: #F7F7F7 !important;
|
||||||
|
padding-left: 12px !important;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-collapse-content {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
|
||||||
|
.detection__icon {
|
||||||
|
width: 4px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-collapse-trigger {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +1,165 @@
|
|||||||
.detection-filter-case {
|
//.detection-filter-case {
|
||||||
display: flex;
|
// display: flex;
|
||||||
flex-direction: column;
|
// flex-direction: column;
|
||||||
width: 280px;
|
// width: 280px;
|
||||||
padding: 10px;
|
// padding: 10px;
|
||||||
margin-right: 10px;
|
// margin-right: 10px;
|
||||||
background-color: white;
|
// background-color: white;
|
||||||
overflow: auto;
|
// overflow: auto;
|
||||||
|
//
|
||||||
|
// .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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// .new-detection-filter-title {
|
||||||
|
// display: flex;
|
||||||
|
// flex: 0 0 32px;
|
||||||
|
// align-items: center;
|
||||||
|
// padding-left: 27px;
|
||||||
|
// background-color: #EFF2F5;
|
||||||
|
// cursor: pointer;
|
||||||
|
// font-size: 14px;
|
||||||
|
// color: #353636;
|
||||||
|
// font-weight: 600;
|
||||||
|
// margin: -10px;
|
||||||
|
// margin-bottom: 10px;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
.detection-filter-title {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
background: #F7F7F7;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-shadow: 0 1px 0 0 rgba(226, 229, 236, 1);
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
.detection-filter-content {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
.detection-filter {
|
.filter-content-title {
|
||||||
display: flex;
|
font-family: NotoSansHans-Medium;
|
||||||
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;
|
font-size: 14px;
|
||||||
padding-left: 6px;
|
line-height: 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
i {
|
.filter-content-content {
|
||||||
font-size: 12px;
|
display: flex;
|
||||||
transition: all linear .1s;
|
flex-direction: column;
|
||||||
transform: rotate(0) translate(0, 2px);
|
|
||||||
}
|
.filter-content-checkbox {
|
||||||
i.arrow-rotate {
|
line-height: 16px;
|
||||||
transform: rotate(90deg) translate(2px, 3px);
|
margin-bottom: 10px;
|
||||||
}
|
font-family: NotoSansSChineseRegular;
|
||||||
.new-detection-filter-header-title {
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #353636;
|
color: #353636;
|
||||||
font-weight: 600;
|
font-weight: 400;
|
||||||
}
|
|
||||||
.new-detection-filter-icon {
|
.el-checkbox__inner {
|
||||||
margin-left: 8px;
|
width: 16px !important;
|
||||||
margin-bottom: 2px;
|
height: 16px !important;
|
||||||
font-weight: bold !important;
|
text-align: center !important;
|
||||||
}
|
line-height: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter__body {
|
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
||||||
padding: 5px 0 0 15px;
|
border-color: #38ACD2;
|
||||||
|
background: #38ACD2;
|
||||||
.el-checkbox-group {
|
border-radius: 2px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.el-checkbox {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px 0;
|
|
||||||
margin-right: 5px;
|
|
||||||
.el-checkbox__label {
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
.el-checkbox__input.is-indeterminate .el-checkbox__inner:before {
|
||||||
.filter__checkbox-label {
|
background: #FFFFFF;
|
||||||
display: flex;
|
border-radius: 1px;
|
||||||
justify-content: space-between;
|
}
|
||||||
align-items: center;
|
.el-checkbox__input.is-checked {
|
||||||
|
.el-checkbox__inner {
|
||||||
.severity-color-block {
|
border-color: #38ACD2;
|
||||||
width: 4px;
|
background: #38ACD2;
|
||||||
height: 15px;
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.el-checkbox__input.is-focus {
|
||||||
|
.el-checkbox__inner {
|
||||||
|
border-color: #38ACD2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:last-of-type {
|
.el-checkbox__label {
|
||||||
padding-bottom: 0;
|
font-family: NotoSansSChineseRegular;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.new-detection-filter-title {
|
|
||||||
display: flex;
|
|
||||||
flex: 0 0 32px;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 27px;
|
|
||||||
background-color: #EFF2F5;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #353636;
|
color: #353636;
|
||||||
font-weight: 600;
|
font-weight: 400;
|
||||||
margin: -10px;
|
}
|
||||||
margin-bottom: 10px;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
.detection-table {
|
||||||
|
.el-table th > .cell, .el-table .cell {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
.el-table--enable-row-transition .el-table__body td, .el-table--border th {
|
||||||
|
border-left: 0 !important;
|
||||||
|
border-right: 0 !important;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #353636;
|
||||||
|
}
|
||||||
|
.el-table--border th {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__header-wrapper {
|
||||||
|
height: 32px !important;
|
||||||
|
line-height: 32px !important;
|
||||||
|
border-bottom: 1px solid #EBEEF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__body td {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__row {
|
||||||
|
height: 32px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-tag-blue, .detection-tag-red, .detection-tag-gray, .detection-tag-status0, .detection-tag-status1 {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 1px 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-tag-blue {
|
||||||
|
background: rgba(56, 172, 210, 0.10);
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(51, 51, 51, 0.02);
|
||||||
|
color: #046ECA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-tag-red {
|
||||||
|
background: rgba(226, 97, 84, 0.12);
|
||||||
|
color: #E26154;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-tag-gray {
|
||||||
|
background: rgba(113, 113, 113, 0.12);
|
||||||
|
color: #717171;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-tag-status0 {
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
background: rgba(113, 113, 113, 0.12);
|
||||||
|
color: #717171;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-tag-status1 {
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
background: rgba(126, 159, 84, 0.12);
|
||||||
|
color: #7E9F54;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-table-library {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #046ECA;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
154
src/assets/css/components/views/detections/detection-tools.scss
Normal file
154
src/assets/css/components/views/detections/detection-tools.scss
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
.top-tools__left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.top-tool-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
border: 1px solid #DEDEDE;
|
||||||
|
outline: none;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: #F9F9F9;
|
||||||
|
transition: background-color linear .1s;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
//font-family: $fontFamily !important;
|
||||||
|
i {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #575757;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.66;
|
||||||
|
|
||||||
|
i {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn:hover:not(.cn-btn-disabled) {
|
||||||
|
border: 1px solid #DEDEDE;
|
||||||
|
background-color: #EBF1F4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn:focus:not(.cn-btn-disabled), .top-tool-btn.is-focus {
|
||||||
|
background-color: #E0E7EA;
|
||||||
|
border: 1px solid #DEDEDE;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #575757;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--delete.top-tool-btn:focus:not(.cn-btn-disabled) {
|
||||||
|
background-color: #EBF1F4;
|
||||||
|
border-color: #FFC4B9;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #F0745A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--create {
|
||||||
|
background-color: #38ACD2 !important;
|
||||||
|
border-color: #2E88A6 !important;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--create:hover {
|
||||||
|
background-color: #57B8D9 !important;
|
||||||
|
border-color: #2E88A6 !important;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--create:focus {
|
||||||
|
background-color: #31A5CD !important;
|
||||||
|
border-color: #2E88A6 !important;
|
||||||
|
color: #FFFFFF !important;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #FFFFFF !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--create:disabled {
|
||||||
|
opacity: 0.66;
|
||||||
|
background-color: #38ACD2 !important;
|
||||||
|
border-color: #2E88A6 !important;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-search {
|
||||||
|
display: flex;
|
||||||
|
width: 242px; //calc(100% - 256px);
|
||||||
|
.el-input--small {
|
||||||
|
line-height: 27px;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 2px 0 0 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn {
|
||||||
|
border-left: none;
|
||||||
|
border-radius: 0 2px 2px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--search:hover {
|
||||||
|
border-left: none !important;
|
||||||
|
border-radius: 0 2px 2px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--search:focus {
|
||||||
|
border-left: none !important;
|
||||||
|
border-radius: 0 2px 2px 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-search {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.el-input--small {
|
||||||
|
line-height: 27px;
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 2px 0 0 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn {
|
||||||
|
border-left: none;
|
||||||
|
border-radius: 0 2px 2px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--search:hover {
|
||||||
|
border-left: none !important;
|
||||||
|
border-radius: 0 2px 2px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-tool-btn--search:focus {
|
||||||
|
border-left: none !important;
|
||||||
|
border-radius: 0 2px 2px 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
194
src/components/table/detection/GeneralSettings.vue
Normal file
194
src/components/table/detection/GeneralSettings.vue
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="form-setting__block margin-b-20" style="display: flex">
|
||||||
|
<div class="block-mode">
|
||||||
|
<!--todo 图标没有,后期换-->
|
||||||
|
<div class="block-mode-left">
|
||||||
|
<i class="cn-icon cn-icon-setting2 block-mode-icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-mode-right">
|
||||||
|
<div class="block-mode-title">Indicator Match</div>
|
||||||
|
<div class="block-mode-content">
|
||||||
|
Use indicators from intelligencesources to detect matchingevents and alerts.
|
||||||
|
</div>
|
||||||
|
<div :class="settingObj.ruleType===detectionRuleType.indicator?'block-mode-btn-active':'block-mode-btn'"
|
||||||
|
@click="selectMode(detectionRuleType.indicator)">select
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-mode">
|
||||||
|
<!--todo 图标没有,后期换-->
|
||||||
|
<div class="block-mode-left">
|
||||||
|
<i class="cn-icon cn-icon-setting2 block-mode-icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block-mode-right">
|
||||||
|
<div class="block-mode-title">Threshold</div>
|
||||||
|
<div class="block-mode-content">
|
||||||
|
Aggregate query results to detect when number of matches exceeds threshold.
|
||||||
|
</div>
|
||||||
|
<div :class="settingObj.ruleType===detectionRuleType.threshold?'block-mode-btn-active':'block-mode-btn'"
|
||||||
|
@click="selectMode(detectionRuleType.threshold)">select
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--category-->
|
||||||
|
<el-form ref="form" :model="settingObj" label-position="top" :rules="rules">
|
||||||
|
<el-form-item label="Category" prop="category" class="form-setting__block margin-b-20">
|
||||||
|
<el-select v-model="settingObj.category" class="form-setting__select" placeholder=" " size="mini" @change="changeEditFlag">
|
||||||
|
<el-option
|
||||||
|
v-for="item in categoryList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!--type-->
|
||||||
|
<el-form-item label="Type" prop="eventType" class="form-setting__block margin-b-20">
|
||||||
|
<el-select v-model="settingObj.eventType" placeholder=" " size="mini" class="form-setting__select" @change="changeEditFlag">
|
||||||
|
<el-option
|
||||||
|
v-for="item in typeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!--name-->
|
||||||
|
<el-form-item label="Name" prop="name" class="form-setting__block margin-b-20">
|
||||||
|
<el-input
|
||||||
|
maxlength="64"
|
||||||
|
show-word-limit
|
||||||
|
placeholder=""
|
||||||
|
v-model="settingObj.name"
|
||||||
|
@input="changeEditFlag"
|
||||||
|
class="form-setting__input" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!--Description-->
|
||||||
|
<div class="form-setting__block margin-b-20">
|
||||||
|
<div class="block-title">Description</div>
|
||||||
|
<el-input
|
||||||
|
maxlength="255"
|
||||||
|
show-word-limit
|
||||||
|
v-model="settingObj.description"
|
||||||
|
type="textarea"
|
||||||
|
resize='none'
|
||||||
|
@input="changeEditFlag"
|
||||||
|
class="form-setting__textarea" />
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="form-setting__block margin-b-20">
|
||||||
|
<div class="block-title">Policy Status</div>
|
||||||
|
<el-switch
|
||||||
|
v-model="settingObj.status"
|
||||||
|
@change="changeEditFlag"
|
||||||
|
active-color="#38ACD2"
|
||||||
|
inactive-color="#C0CEDB"
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="0"
|
||||||
|
:active-text="switchStatus(settingObj.status)"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-setting__btn">
|
||||||
|
<el-button @click="onContinue">Continue</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { detectionRuleType } from '@/utils/constants'
|
||||||
|
import { switchStatus } from '@/utils/tools'
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GeneralSettings',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
detectionRuleType,
|
||||||
|
categoryList: [],
|
||||||
|
typeList: [],
|
||||||
|
settingObj: {
|
||||||
|
ruleType: detectionRuleType.threshold,
|
||||||
|
category: '',
|
||||||
|
eventType: '',
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
status: 1,
|
||||||
|
editFlag: false, // 编辑标识,如果保存之后继续编辑且不再点击保存,置为true,则不会将编辑内容提交到最终form
|
||||||
|
saveFlag: false // 是否点击保存标识
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
category: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
eventType: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.initData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
switchStatus,
|
||||||
|
initData () {
|
||||||
|
get(api.detection.statistics, { pageSize: -1 }).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.categoryList = response.data.categoryList || []
|
||||||
|
this.typeList = response.data.typeList || []
|
||||||
|
} else {
|
||||||
|
console.error(response)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectMode (ruleType) {
|
||||||
|
this.settingObj.ruleType = ruleType
|
||||||
|
this.changeEditFlag()
|
||||||
|
},
|
||||||
|
changeEditFlag () {
|
||||||
|
this.settingObj.editFlag = true
|
||||||
|
},
|
||||||
|
/** 点击继续,进行第二步 */
|
||||||
|
onContinue () {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.settingObj.editFlag = false
|
||||||
|
this.settingObj.saveFlag = true
|
||||||
|
this.$emit('setSettingForm', this.settingObj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
234
src/components/table/detection/HistoryTopKeys.vue
Normal file
234
src/components/table/detection/HistoryTopKeys.vue
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
<template>
|
||||||
|
<div class="history-top-key" v-if="myDrawer">
|
||||||
|
<el-drawer v-model="myDrawer" :with-header="false">
|
||||||
|
<div class="key-header">
|
||||||
|
<div>History Top Keys</div>
|
||||||
|
<i class="cn-icon cn-icon-close" @click="closeDrawer"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="key-search">
|
||||||
|
<el-input v-model="searchKey" @keyup.enter="onSearch" size="mini" placeholder="Search for">
|
||||||
|
<template #prefix>
|
||||||
|
<!--todo 该图标名称错误,已在iconfont修改,后续记得改过来-->
|
||||||
|
<i class="cn-icon cn-icon-serach key-search-icon"></i>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<i class="cn-icon cn-icon-refresh1 key-refresh" @click="onRefresh"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="key-total">
|
||||||
|
Total: <span class="key-total-number">{{ tableTotal }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="key-table">
|
||||||
|
<loading :loading="loading"></loading>
|
||||||
|
|
||||||
|
<el-table :data="tableData" style="width: 100%" @row-click="rowClick">
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, index) in tableTitle"
|
||||||
|
:key="`col-${index}`"
|
||||||
|
:fixed="item.fixed"
|
||||||
|
:label="item.label"
|
||||||
|
:min-width="`${item.minWidth}`"
|
||||||
|
:prop="item.prop"
|
||||||
|
:sort-orders="['ascending', 'descending']"
|
||||||
|
:width="`${item.width}`"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" :column="item">
|
||||||
|
<template v-if="item.prop === 'metric'">
|
||||||
|
<span>{{unitConvert(scope.row.metric, unitTypes.byte).join(' ')}}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.prop === 'last'">
|
||||||
|
<span>{{dateFormatByAppearance(scope.row[item.prop])}}</span>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ scope.row[item.prop] || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<template v-slot:empty >
|
||||||
|
<chart-error v-if="showError" :content="errorMsg" style="line-height: 0" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import unitConvert from '@/utils/unit-convert'
|
||||||
|
import { unitTypes } from '@/utils/constants'
|
||||||
|
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import Loading from '@/components/common/Loading'
|
||||||
|
import ChartError from '@/components/common/Error'
|
||||||
|
export default {
|
||||||
|
name: 'HistoryTopKeys',
|
||||||
|
props: {
|
||||||
|
showDrawer: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ChartError,
|
||||||
|
Loading
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
myDrawer: false,
|
||||||
|
searchKey: '',
|
||||||
|
unitTypes,
|
||||||
|
loading: false,
|
||||||
|
tableTotal: 0,
|
||||||
|
tableData: [],
|
||||||
|
tableTitle: [
|
||||||
|
{
|
||||||
|
// label: this.$t('knowledge.status'),
|
||||||
|
label: 'Keys',
|
||||||
|
prop: 'keys',
|
||||||
|
show: true,
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Last Seen',
|
||||||
|
prop: 'last',
|
||||||
|
show: true,
|
||||||
|
minWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Primary metric',
|
||||||
|
prop: 'metric',
|
||||||
|
show: true,
|
||||||
|
minWidth: 120
|
||||||
|
}
|
||||||
|
],
|
||||||
|
showError: false,
|
||||||
|
errorMsg: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.myDrawer = this.showDrawer
|
||||||
|
this.getTopKeysData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
unitConvert,
|
||||||
|
dateFormatByAppearance,
|
||||||
|
getTopKeysData (data) {
|
||||||
|
this.loading = true
|
||||||
|
const params = {}
|
||||||
|
if (data) {
|
||||||
|
// todo 具体入参按文档要求
|
||||||
|
params.param = data
|
||||||
|
}
|
||||||
|
|
||||||
|
get(api.detection.create.topKeys, params).then(res => {
|
||||||
|
this.tableTotal = 0
|
||||||
|
this.tableData = []
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.tableTotal = res.data.total
|
||||||
|
this.tableData = res.data.list
|
||||||
|
} else {
|
||||||
|
this.httpError(res)
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
this.httpError(err)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 关闭topKeys弹窗 */
|
||||||
|
closeDrawer () {
|
||||||
|
this.myDrawer = false
|
||||||
|
this.$emit('closeDrawer', false)
|
||||||
|
},
|
||||||
|
/** 单击topKeys弹窗某一项 */
|
||||||
|
rowClick (data) {
|
||||||
|
this.$emit('keyRowClick', data)
|
||||||
|
},
|
||||||
|
onRefresh () {
|
||||||
|
this.getTopKeysData()
|
||||||
|
},
|
||||||
|
onSearch () {
|
||||||
|
this.getTopKeysData(this.searchKey)
|
||||||
|
},
|
||||||
|
httpError (e) {
|
||||||
|
this.showError = true
|
||||||
|
this.errorMsg = this.errorMsgHandler(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.history-top-key {
|
||||||
|
width: 396px;
|
||||||
|
height: 520px;
|
||||||
|
|
||||||
|
.el-drawer__body {
|
||||||
|
border: 1px #E2E5EC solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.key-header {
|
||||||
|
height: 41px;
|
||||||
|
background: #F7F7F7;
|
||||||
|
box-shadow: 0 1px 0 0 rgba(226,229,236,1);
|
||||||
|
border-radius: 2px 2px 0 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #575757;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-search {
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.key-search-icon {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-refresh {
|
||||||
|
margin: 0 10px;
|
||||||
|
color: #38ACD2;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-total {
|
||||||
|
margin: 10px 12px;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 21px;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
.key-total-number {
|
||||||
|
color: #666666;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
488
src/components/table/detection/RuleDefinition.vue
Normal file
488
src/components/table/detection/RuleDefinition.vue
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="mySettingObj.ruleType===detectionRuleType.threshold" style="display: flex;justify-content: space-between;">
|
||||||
|
<div>
|
||||||
|
<el-form ref="form" :model="thresholdRuleObj" label-position="top" :rules="rules">
|
||||||
|
<!--source-->
|
||||||
|
<el-form-item label="Source" prop="dataSource" class="form-setting__block margin-b-20">
|
||||||
|
<el-select v-model="thresholdRuleObj.dataSource" placeholder=" " size="mini" class="form-setting__select">
|
||||||
|
<el-option
|
||||||
|
v-for="item in sourceList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!--Dimensions-->
|
||||||
|
<div class="form-setting__block margin-b-20">
|
||||||
|
<div class="block-title">Dimensions</div>
|
||||||
|
<div class="block-dimension">
|
||||||
|
<div class="block-dimension-tag" v-for="(ite, ind) in dimensionList" :key="ind">{{ ite.label }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-setting__block form-setting__block-key">
|
||||||
|
<div>Key Selection</div>
|
||||||
|
<div class="block-key">
|
||||||
|
<!--todo 图标暂无,需要更换-->
|
||||||
|
<i class="cn-icon cn-icon-shijianjihua"></i>
|
||||||
|
<span @click="showDrawer=true">History Top Keys</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--Filters模块-->
|
||||||
|
<div class="form-setting__block margin-b-20">
|
||||||
|
<div class="block-title1">{{ $t('detections.filters') }}</div>
|
||||||
|
<div class="definition-filter-block" v-if="showFilter">
|
||||||
|
<div class="definition-filter-item" v-for="(item, index) in thresholdRuleObj.filterList" :key="index">
|
||||||
|
<el-select class="filter-item__select margin-r-8" v-model="item.filter" placeholder=" " size="mini">
|
||||||
|
<el-option
|
||||||
|
v-for="item in selectList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input class="filter-item__input margin-r-8" size="mini" disabled placeholder="equal"></el-input>
|
||||||
|
<el-input
|
||||||
|
class="filter-item__input margin-r-8"
|
||||||
|
size="mini"
|
||||||
|
oninput="value=value.replace(/[^\d]/g,'')"
|
||||||
|
v-model="item.value"></el-input>
|
||||||
|
<i class="cn-icon cn-icon-close" @click="delFilterItem(index)"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="height: 10px;"></div>
|
||||||
|
<div class="filter-block-footer">
|
||||||
|
<i class="cn-icon cn-icon-add" @click="addFilter"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="block-filter-add" @click="addFilter">+</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--Condition模块-->
|
||||||
|
<div class="form-setting__block margin-b-20">
|
||||||
|
<div class="block-title">Condition</div>
|
||||||
|
|
||||||
|
<el-form ref="form2" :model="thresholdRuleObj" label-position="top">
|
||||||
|
<div class="definition-condition-block" v-for="(item, index) in thresholdRuleObj.conditionData" :key="index">
|
||||||
|
<el-form-item :label="$t('detection.level')" :prop="`conditionData.${index}.level`" :rules="rules.level">
|
||||||
|
<el-select class="condition__select margin-b-20" v-model="item.level" placeholder=" " size="mini">
|
||||||
|
<template #prefix>
|
||||||
|
<div
|
||||||
|
class="condition__select__icon"
|
||||||
|
:style="{background: eventSeverityColor[item.level]}"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-option
|
||||||
|
v-for="item in levelList"
|
||||||
|
:key="item.label"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<div class="condition-metric" v-for="(data, i) in item.list" :key="i">
|
||||||
|
<div class="condition-metric-item1">
|
||||||
|
<div class="metric-item1__text">
|
||||||
|
<span style="margin-right: 9px;">If</span>
|
||||||
|
<el-form-item :prop="`conditionData.${index}.list.${i}.metric`" :rules="rules.metric">
|
||||||
|
<el-select v-model="data.metric" placeholder=" " size="mini">
|
||||||
|
<el-option
|
||||||
|
v-for="item in metricList"
|
||||||
|
:key="item.label"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<span style="margin-left: 9px;">of keys</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<i class="cn-icon cn-icon-add" @click="addConditionItem(index)"></i>
|
||||||
|
<i
|
||||||
|
class="cn-icon cn-icon-close"
|
||||||
|
:class="delConditionDisabled ? 'metric-item1-close-disable' : 'metric-item1-close'"
|
||||||
|
@click="delConditionItem(index, i)">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="condition-metric-item2">
|
||||||
|
<div style="height: 24px;line-height: 24px;display: flex;">
|
||||||
|
<el-form-item :prop="`conditionData.${index}.list.${i}.condition`" :rules="rules.condition">
|
||||||
|
<el-select class="metric-item2__select" v-model="data.condition" placeholder=" " size="mini">
|
||||||
|
<el-option
|
||||||
|
v-for="item in conditionList"
|
||||||
|
:key="item.label"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :prop="`conditionData.${index}.list.${i}.value`" :rules="rules.value">
|
||||||
|
<!--todo 应当添加长度限制-->
|
||||||
|
<el-input
|
||||||
|
class="metric-item2__input"
|
||||||
|
v-model.number="data.value"
|
||||||
|
oninput="value=value.replace(/[^\d]/g,'')"
|
||||||
|
size="mini"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>{{ data.metric }}</span>
|
||||||
|
</div>
|
||||||
|
<el-divider v-if="item.list.length - 1 > i" class="condition-divider">and</el-divider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="condition-add" @click="addCondition">
|
||||||
|
<i class="cn-icon cn-icon-add"></i>Add Condition
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--History Top Keys弹窗-->
|
||||||
|
<div class="key-drawer">
|
||||||
|
<history-top-keys
|
||||||
|
v-if="showDrawer"
|
||||||
|
:showDrawer="showDrawer"
|
||||||
|
@closeDrawer="onCloseDrawer"
|
||||||
|
@keyRowClick="getRowClick"
|
||||||
|
></history-top-keys>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="mySettingObj.ruleType===detectionRuleType.indicator">
|
||||||
|
<el-form ref="form" :model="indicatorRuleObj" label-position="top" :rules="rules">
|
||||||
|
<!--Source-->
|
||||||
|
<el-form-item label="Source" prop="dataSource" class="form-setting__block margin-b-20">
|
||||||
|
<el-select v-model="indicatorRuleObj.dataSource" class="form-setting__select" placeholder=" " size="mini">
|
||||||
|
<el-option
|
||||||
|
v-for="item in sourceList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!--Library-->
|
||||||
|
<el-form-item label="Library" prop="knowledgeId" class="form-setting__block margin-b-20">
|
||||||
|
<el-select v-model="indicatorRuleObj.knowledgeId" class="form-setting__select" placeholder=" " size="mini">
|
||||||
|
<el-option
|
||||||
|
v-for="item in libraryList"
|
||||||
|
:key="item.knowledgeId"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.knowledgeId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<!--Level-->
|
||||||
|
<el-form-item :label="$t('detection.level')" prop="level" class="form-setting__block">
|
||||||
|
<el-select v-model="indicatorRuleObj.level" class="condition__select form-setting__select" placeholder=" " size="mini">
|
||||||
|
<template #prefix>
|
||||||
|
<div
|
||||||
|
class="condition__select__icon"
|
||||||
|
:style="{background: eventSeverityColor[indicatorRuleObj.level]}"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-option
|
||||||
|
v-for="item in levelList"
|
||||||
|
:key="item.label"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-setting__btn">
|
||||||
|
<el-button @click="onContinue">Continue</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import HistoryTopKeys from '@/components/table/detection/HistoryTopKeys'
|
||||||
|
import { eventSeverityColor, detectionRuleType } from '@/utils/constants'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RuleDefinition',
|
||||||
|
props: {
|
||||||
|
settingObj: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
HistoryTopKeys
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
settingObj: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler (newVal, oldVal) {
|
||||||
|
if (!newVal.editFlag && newVal.saveFlag) {
|
||||||
|
this.mySettingObj = JSON.parse(JSON.stringify(newVal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
eventSeverityColor,
|
||||||
|
detectionRuleType,
|
||||||
|
mySettingObj: {
|
||||||
|
ruleType: detectionRuleType.threshold
|
||||||
|
},
|
||||||
|
dimensionList: [], // Dimensions数据
|
||||||
|
// ruleType为Indicator时表单数据
|
||||||
|
indicatorRuleObj: {
|
||||||
|
dataSource: '',
|
||||||
|
knowledgeId: '',
|
||||||
|
level: ''
|
||||||
|
},
|
||||||
|
// ruleType为Threshold时表单数据
|
||||||
|
thresholdRuleObj: {
|
||||||
|
dataSource: '',
|
||||||
|
dimensionKeys: '',
|
||||||
|
filterList: [],
|
||||||
|
filters: '', // filter提交到接口的数据,即filterList转化为字符串
|
||||||
|
conditionData: [
|
||||||
|
{
|
||||||
|
level: '',
|
||||||
|
list: [
|
||||||
|
{ metric: '', condition: '', value: '' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
conditions: {} // filter提交到接口的数据,即filterList转化为字符串
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
dataSource: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
level: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
metric: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
condition: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
knowledgeId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sourceList: [], // source下拉列表数据
|
||||||
|
// rule的policy创建信息
|
||||||
|
showDrawer: false, // 显示History Top Keys抽屉弹框
|
||||||
|
showFilter: false, // 显示filter筛选框,点击add显示
|
||||||
|
selectList: [], // filter的第一个下拉列表
|
||||||
|
levelList: [], // condition的level下拉列表
|
||||||
|
metricList: [],
|
||||||
|
conditionList: [],
|
||||||
|
libraryList: [],
|
||||||
|
delConditionDisabled: true, // condition删除标识,true表示禁止删除,即只有一个condition时
|
||||||
|
unitObj: {
|
||||||
|
than: '>',
|
||||||
|
less: '<',
|
||||||
|
equal: '='
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.initData()
|
||||||
|
|
||||||
|
// todo 调用接口进行赋值
|
||||||
|
this.dimensionList = [
|
||||||
|
{ label: 'Destination IP/CIDR', value: 'Destination IP/CIDR' },
|
||||||
|
{ label: 'Source Port Number', value: 'Source Port Number' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initData () {
|
||||||
|
get(api.detection.statistics, { pageSize: -1 }).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.sourceList = response.data.sourceList || []
|
||||||
|
this.levelList = response.data.levelList || []
|
||||||
|
this.conditionList = response.data.conditionList || []
|
||||||
|
this.metricList = response.data.metricList || []
|
||||||
|
this.libraryList = response.data.libraryList || []
|
||||||
|
} else {
|
||||||
|
console.error(response)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 单击History Top Keys列表某行,filter添加数据 */
|
||||||
|
getRowClick (data) {
|
||||||
|
this.addFilter(data)
|
||||||
|
},
|
||||||
|
/** 关闭History Top Keys弹框 */
|
||||||
|
onCloseDrawer () {
|
||||||
|
this.showDrawer = false
|
||||||
|
},
|
||||||
|
/** filter模块点击add按钮 */
|
||||||
|
addFilter (data) {
|
||||||
|
this.showFilter = true
|
||||||
|
if (this.selectList.length === 0) {
|
||||||
|
this.getFilterList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加数据前,先删除空白项
|
||||||
|
const delIndex = this.thresholdRuleObj.filterList.findIndex(t => t.filter === '')
|
||||||
|
if (delIndex > -1) {
|
||||||
|
this.thresholdRuleObj.filterList.splice(delIndex, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.metric) {
|
||||||
|
// 从key弹框添加数据
|
||||||
|
const obj = this.thresholdRuleObj.filterList.find(t => t.keyId === data.keyId)
|
||||||
|
if (!obj) {
|
||||||
|
this.thresholdRuleObj.filterList.push({ keyId: data.keyId, filter: this.selectList[0].label, value: data.metric })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 手动添加
|
||||||
|
this.thresholdRuleObj.filterList.push({ filter: '', value: '' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 添加condition大模块 */
|
||||||
|
addCondition () {
|
||||||
|
this.$refs.form2.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
// 如果选择了level,则禁用这一条level,不允许再选择,除非删除才能选择
|
||||||
|
this.levelList.forEach(item => {
|
||||||
|
const obj = this.thresholdRuleObj.conditionData.find(t => t.level === item.value)
|
||||||
|
if (obj) {
|
||||||
|
item.disabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.thresholdRuleObj.conditionData.push({ level: '', list: [{ metric: '', condition: '', value: '' }] })
|
||||||
|
this.delConditionDisabled = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 添加condition小模块 */
|
||||||
|
addConditionItem (index) {
|
||||||
|
this.$refs.form2.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.thresholdRuleObj.conditionData[index].list.push({ metric: '', condition: '', value: '' })
|
||||||
|
this.delConditionDisabled = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除condition,小模块只剩一个时点击删除,则删除大模块
|
||||||
|
* 只有一个大模块时,只剩一个小模块则不可删除
|
||||||
|
* */
|
||||||
|
delConditionItem (index, i) {
|
||||||
|
if (!this.delConditionDisabled) {
|
||||||
|
const dataLen = this.thresholdRuleObj.conditionData.length
|
||||||
|
const listLen = this.thresholdRuleObj.conditionData[index].list.length
|
||||||
|
if (dataLen === 1) {
|
||||||
|
if (listLen >= 2) {
|
||||||
|
this.thresholdRuleObj.conditionData[index].list.splice(i, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.thresholdRuleObj.conditionData[index].list.length === 1) {
|
||||||
|
this.delConditionDisabled = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (listLen === 1) {
|
||||||
|
this.thresholdRuleObj.conditionData.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
this.thresholdRuleObj.conditionData[index].list.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 获取filter模块下拉列表 */
|
||||||
|
getFilterList () {
|
||||||
|
// todo 请求接口
|
||||||
|
this.selectList = [
|
||||||
|
{ value: 'Destination As Number', label: 'Destination As Number' },
|
||||||
|
{ value: 'Destination As String', label: 'Destination As String' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/** 删除filter某一项 */
|
||||||
|
delFilterItem (i) {
|
||||||
|
this.thresholdRuleObj.filterList.splice(i, 1)
|
||||||
|
},
|
||||||
|
/** 点击继续,展开第三步 */
|
||||||
|
onContinue () {
|
||||||
|
this.$refs.form.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (this.mySettingObj.ruleType === detectionRuleType.indicator) {
|
||||||
|
// 第一步模式选择Indicator Match
|
||||||
|
this.$emit('setRuleObj', this.indicatorRuleObj)
|
||||||
|
} else {
|
||||||
|
// 第一步模式选择Threshold
|
||||||
|
this.$refs.form2.validate(valid2 => {
|
||||||
|
if (valid2) {
|
||||||
|
this.getConditions()
|
||||||
|
this.$emit('setRuleObj', this.thresholdRuleObj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 将condition的数组转换为入参需要的字符串 */
|
||||||
|
getConditions () {
|
||||||
|
const conditionData = this.thresholdRuleObj.conditionData
|
||||||
|
const obj = {}
|
||||||
|
|
||||||
|
conditionData.forEach(item => {
|
||||||
|
obj[item.level] = ''
|
||||||
|
let str = ''
|
||||||
|
item.list.forEach(t => {
|
||||||
|
str = str + t.metric + ' ' + this.unitObj[t.condition] + ' ' + t.value + ' && '
|
||||||
|
})
|
||||||
|
str = str.substring(0, str.length - 4)
|
||||||
|
obj[item.level] = str
|
||||||
|
})
|
||||||
|
this.thresholdRuleObj.conditions = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -118,6 +118,43 @@ export default {
|
|||||||
if (this.listUrl) {
|
if (this.listUrl) {
|
||||||
listUrl = this.listUrl
|
listUrl = this.listUrl
|
||||||
}
|
}
|
||||||
|
// todo 此段是为了避免mock没开启,打开detection界面报错提示,后续再开发detection时删除
|
||||||
|
if (listUrl === api.detection.list) {
|
||||||
|
const list = []
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const obj = {
|
||||||
|
ruleId: 100000 + i,
|
||||||
|
ruleType: 'indicator_match',
|
||||||
|
status: 1,
|
||||||
|
name: 'name123',
|
||||||
|
category: 'Security Event',
|
||||||
|
eventType: 'C&C',
|
||||||
|
description: 'Built-in darkweb IoC',
|
||||||
|
ruleConfig: {
|
||||||
|
knowledge: {
|
||||||
|
name: 'VPN Server IP',
|
||||||
|
category: 'user_defined'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
obj.ruleType = 'threshold'
|
||||||
|
obj.ruleConfig = {
|
||||||
|
dimensions: 'Destination IP/CIDR'
|
||||||
|
}
|
||||||
|
obj.description = 'abuse.ch is providing community driven threat intelligence on \n' +
|
||||||
|
'cyber threats. It is the home of a couple of projects that are \n' +
|
||||||
|
'helping internet service providers and network operators protect …'
|
||||||
|
} else {
|
||||||
|
obj.status = 0
|
||||||
|
}
|
||||||
|
list.push(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tableData = list
|
||||||
|
this.pageObj.total = list.length
|
||||||
|
this.loading = false
|
||||||
|
} else {
|
||||||
get(listUrl, this.searchLabel).then(response => {
|
get(listUrl, this.searchLabel).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
this.tableData = response.data.list
|
this.tableData = response.data.list
|
||||||
@@ -140,7 +177,9 @@ export default {
|
|||||||
this.isNoData = true
|
this.isNoData = true
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.toggleLoading(false)
|
this.toggleLoading(false)
|
||||||
|
this.loading = false
|
||||||
})
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
del (row) {
|
del (row) {
|
||||||
this.$confirm(this.$t('tip.confirmDelete'), {
|
this.$confirm(this.$t('tip.confirmDelete'), {
|
||||||
@@ -396,7 +435,7 @@ export default {
|
|||||||
tableData: {
|
tableData: {
|
||||||
deep: true,
|
deep: true,
|
||||||
handler (n) {
|
handler (n) {
|
||||||
if (n.length === 0 && this.pageObj.pageNo > 1) {
|
if (n && n.length === 0 && this.pageObj.pageNo > 1) {
|
||||||
this.pageNo(this.pageObj.pageNo - 1)
|
this.pageNo(this.pageObj.pageNo - 1)
|
||||||
}
|
}
|
||||||
// TODO 不是删除时回到顶部
|
// TODO 不是删除时回到顶部
|
||||||
|
|||||||
167
src/mock/detection.js
Normal file
167
src/mock/detection.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import Mock from 'mockjs'
|
||||||
|
|
||||||
|
const urlAndVersion = BASE_CONFIG.baseUrl + BASE_CONFIG.apiVersion
|
||||||
|
const openMock = true
|
||||||
|
if (openMock) {
|
||||||
|
Mock.mock(new RegExp(urlAndVersion + '/rule/detection/list.*'), 'get', function (requestObj) {
|
||||||
|
const list = []
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const obj = {
|
||||||
|
ruleId: 100000 + i,
|
||||||
|
ruleType: 'indicator_match',
|
||||||
|
status: 1,
|
||||||
|
name: 'name123',
|
||||||
|
category: 'Security Event',
|
||||||
|
eventType: 'C&C',
|
||||||
|
description: 'Built-in darkweb IoC',
|
||||||
|
ruleConfig: {
|
||||||
|
knowledge: {
|
||||||
|
name: 'VPN Server IP',
|
||||||
|
category: 'user_defined'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
obj.ruleType = 'threshold'
|
||||||
|
obj.ruleConfig = {
|
||||||
|
dimensions: 'Destination IP/CIDR'
|
||||||
|
}
|
||||||
|
obj.description = 'abuse.ch is providing community driven threat intelligence on \n' +
|
||||||
|
'cyber threats. It is the home of a couple of projects that are \n' +
|
||||||
|
'helping internet service providers and network operators protect …'
|
||||||
|
} else {
|
||||||
|
obj.status = 0
|
||||||
|
}
|
||||||
|
list.push(obj)
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
total: list.length,
|
||||||
|
pageSize: 20,
|
||||||
|
pageNo: 1,
|
||||||
|
list: list
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
msg: 'success',
|
||||||
|
code: 200,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Mock.mock(new RegExp(urlAndVersion + '/detection/statistics.*'), 'get', function (requestObj) {
|
||||||
|
const data = {
|
||||||
|
statusList: [
|
||||||
|
{ status: 1 },
|
||||||
|
{ status: 0 }
|
||||||
|
],
|
||||||
|
categoryList: [
|
||||||
|
{ value: 'security', label: 'Security Event' },
|
||||||
|
{ value: 'performance', label: 'Performance Event' },
|
||||||
|
{ value: 'regulatory_risk', label: 'Regulatory Risk Event' }
|
||||||
|
],
|
||||||
|
typeList: [
|
||||||
|
{ value: 'c&c', label: 'C&C' },
|
||||||
|
{ value: 'ddos', label: 'DDos' },
|
||||||
|
{ value: 'lateral_movement', label: 'Lateral movement' },
|
||||||
|
{ value: 'brute_force', label: 'Brute force' }
|
||||||
|
],
|
||||||
|
sourceList: [
|
||||||
|
{ value: 'ip_metric', label: 'IP metric' },
|
||||||
|
{ value: 'performance_event', label: 'performance event' }
|
||||||
|
],
|
||||||
|
levelList: [
|
||||||
|
{ value: 'critical', label: 'Critical' },
|
||||||
|
{ value: 'high', label: 'High' },
|
||||||
|
{ value: 'medium', label: 'Medium' },
|
||||||
|
{ value: 'low', label: 'Low' },
|
||||||
|
{ value: 'info', label: 'Info' }
|
||||||
|
],
|
||||||
|
metricList: [
|
||||||
|
{ value: 'tcp_lostlen_ratio', label: 'Bits/second' },
|
||||||
|
{ value: 's2c_byte_retrans_ratio', label: 'Packets/second' },
|
||||||
|
{ value: 's2c_byte_retrans_ratio1', label: 'Sessions/second' }
|
||||||
|
],
|
||||||
|
conditionList: [
|
||||||
|
{ value: 'than', label: 'Greater Than' },
|
||||||
|
{ value: 'less', label: 'Greater Less' },
|
||||||
|
{ value: 'equal', label: 'Greater Equal' }
|
||||||
|
],
|
||||||
|
libraryList: [
|
||||||
|
{ value: 'library name2', knowledgeId: '101', label: 'Library name' },
|
||||||
|
{ value: 'library name1', knowledgeId: '102', label: 'Library name1' },
|
||||||
|
{ value: 'library name2', knowledgeId: '103', label: 'Library name2' }
|
||||||
|
],
|
||||||
|
intervalList: [
|
||||||
|
{ value: 'minutes', label: 'minutes' },
|
||||||
|
{ value: 'hours', label: 'hours' },
|
||||||
|
{ value: 'days', label: 'days' },
|
||||||
|
{ value: 'weeks', label: 'weeks' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
msg: 'success',
|
||||||
|
code: 200,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Mock.mock(new RegExp(urlAndVersion + '/detection/topKeys.*'), 'get', function (requestObj) {
|
||||||
|
const list = [
|
||||||
|
{ keyId: '10000001', keys: '192.168.40.54', last: 1690266188, metric: 181440000000 },
|
||||||
|
{ keyId: '10000002', keys: '192.168.40.55', last: 1690266188, metric: 161440000000 },
|
||||||
|
{ keyId: '10000003', keys: '192.168.40.56', last: 1690266188, metric: 181440000000 },
|
||||||
|
{ keyId: '10000004', keys: '192.168.40.57', last: 1690266188, metric: 171440000000 },
|
||||||
|
{ keyId: '10000005', keys: '192.168.40.58', last: 1690266188, metric: 171440000000 },
|
||||||
|
{ keyId: '10000006', keys: '192.168.40.59', last: 1690266188, metric: 187440000000 },
|
||||||
|
{ keyId: '10000007', keys: '192.168.40.60', last: 1690266188, metric: 181440000000 }
|
||||||
|
]
|
||||||
|
const data = {
|
||||||
|
list: list,
|
||||||
|
total: list.length
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
msg: 'success',
|
||||||
|
code: 200,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Mock.mock(new RegExp(urlAndVersion + '/rule/detection.*'), 'get', function (requestObj) {
|
||||||
|
const ruleId = getLastValue(requestObj.url)
|
||||||
|
const data = {
|
||||||
|
name: 'name123',
|
||||||
|
category: 'Security Event',
|
||||||
|
ruleType: 'indicator_match',
|
||||||
|
eventType: 'C&C',
|
||||||
|
description: 'Built-in darkweb IoC',
|
||||||
|
status: 1,
|
||||||
|
ruleConfig: {
|
||||||
|
dataSource: 'VPN Server IP',
|
||||||
|
knowledgeId: 10,
|
||||||
|
level: 10
|
||||||
|
},
|
||||||
|
trigger: {
|
||||||
|
atLeast: 1,
|
||||||
|
interval: 'PT5M',
|
||||||
|
resetInterval: 'PT10M'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleId % 2 === 0) {
|
||||||
|
data.ruleType = 'threshold'
|
||||||
|
data.status = 1
|
||||||
|
} else {
|
||||||
|
data.status = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
msg: 'success',
|
||||||
|
code: 200,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLastValue = (url) => {
|
||||||
|
const index = url.lastIndexOf('/')
|
||||||
|
return url.substring(index + 1)
|
||||||
|
}
|
||||||
@@ -2,3 +2,4 @@ import './npm'
|
|||||||
import './linkMonitor'
|
import './linkMonitor'
|
||||||
import './dns'
|
import './dns'
|
||||||
import './entity'
|
import './entity'
|
||||||
|
import './detection'
|
||||||
|
|||||||
@@ -96,6 +96,18 @@ const routes = [
|
|||||||
name: 'Chart',
|
name: 'Chart',
|
||||||
path: '/chart',
|
path: '/chart',
|
||||||
component: () => import('@/views/administration/Chart')
|
component: () => import('@/views/administration/Chart')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/detectionsNew',
|
||||||
|
component: () => import('@/views/detectionsNew/Index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/detection/policies',
|
||||||
|
component: () => import('@/views/detectionsNew/Index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/detection/policies/create',
|
||||||
|
component: () => import('@/views/detectionsNew/DetectionForm')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,16 @@ export const api = {
|
|||||||
dnsErrorMetric: '/interface/detection/performance/detail/overview/metric/dnsError',
|
dnsErrorMetric: '/interface/detection/performance/detail/overview/metric/dnsError',
|
||||||
httpErrorMetric: '/interface/detection/performance/detail/overview/metric/httpError',
|
httpErrorMetric: '/interface/detection/performance/detail/overview/metric/httpError',
|
||||||
highDnsResponseTimeMetric: '/interface/detection/performance/detail/overview/metric/highDnsResponseTime'
|
highDnsResponseTimeMetric: '/interface/detection/performance/detail/overview/metric/highDnsResponseTime'
|
||||||
|
},
|
||||||
|
list: apiVersion + '/rule/detection/list', // 检测规则列表
|
||||||
|
detail: apiVersion + '/rule/detection', // 检测规则详情
|
||||||
|
delete: apiVersion + '/rule', // 检测规则删除
|
||||||
|
// 获取单位列表,如source、type、metric等
|
||||||
|
statistics: apiVersion + '/detection/statistics',
|
||||||
|
// 规则新建模块
|
||||||
|
create: {
|
||||||
|
topKeys: apiVersion + '/detection/topKeys', // topKeys列表
|
||||||
|
create: apiVersion + '/rule/detection/create' // todo 规则新建编辑,此api为模拟,后续需要修改
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Dashboard
|
// Dashboard
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1300,3 +1300,17 @@ export function numberWithCommas (num) {
|
|||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态编码0,1转化为Disabled,Enabled
|
||||||
|
* @param status
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function switchStatus (status) {
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
return 'Disabled'
|
||||||
|
case 1:
|
||||||
|
return 'Enabled'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
173
src/views/detectionsNew/DetectionDrawer.vue
Normal file
173
src/views/detectionsNew/DetectionDrawer.vue
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
<template>
|
||||||
|
<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="`detection-tag-status${detailData.status}`">
|
||||||
|
{{ switchStatus(detailData.status) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Name</div>
|
||||||
|
<div class="basic-function-value">{{ detailData.name }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Type</div>
|
||||||
|
<div class="basic-function-value">{{ detailData.eventType }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-description">
|
||||||
|
<div class="detection-drawer-title">Description</div>
|
||||||
|
<div class="basic-description-value">{{ detailData.description }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detection-drawer-collapse">
|
||||||
|
<el-collapse v-model="activeRule">
|
||||||
|
<el-collapse-item title="Rule Definitcm" name="rule">
|
||||||
|
<div class="drawer-collapse-content">
|
||||||
|
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Source</div>
|
||||||
|
<div class="basic-function-value">{{ detailData.category }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="detailData.ruleType==='indicator_match'">
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Library</div>
|
||||||
|
<span class="basic-function-value">{{ detailData.library }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Level</div>
|
||||||
|
<div class="detection-drawer-title">
|
||||||
|
<div class="detection__icon" :style="`background-color: ${eventSeverityColor['critical']}`"></div>
|
||||||
|
<div class="basic-function-value">Critical</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Dimensions</div>
|
||||||
|
<span class="detection-tag-blue">{{ detailData.dimensions }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Filters</div>
|
||||||
|
<span class="detection-tag-blue">source</span>
|
||||||
|
<span style="margin: 0 6px;">equal</span><span>19890</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-function" v-for="item in severityList" :key="item.severity"
|
||||||
|
style="padding-bottom: 0">
|
||||||
|
<div class="detection-drawer-title">
|
||||||
|
<div class="detection__icon" :style="`background-color: ${eventSeverityColor[item.severity]}`"></div>
|
||||||
|
<div>{{ toUpperCaseByString(item.severity) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detection-drawer-title">Conditions</div>
|
||||||
|
<div>
|
||||||
|
<div class="detection-tag-gray margin-r-10">> 60 Kpackets/s</div>
|
||||||
|
<div class="detection-tag-gray">> 50 Unique Src IPs</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detection-drawer-collapse" style="margin: 20px 0">
|
||||||
|
<el-collapse v-model="activeTrigger">
|
||||||
|
<el-collapse-item title="Trigger" name="trigger">
|
||||||
|
<div class="drawer-collapse-content">
|
||||||
|
<div class="drawer-collapse-trigger">
|
||||||
|
Triggered when conditions occur at least
|
||||||
|
<span style="color: #046ECA" v-if="detailData.trigger">
|
||||||
|
{{ detailData.trigger.atLeast }} time
|
||||||
|
</span> in
|
||||||
|
<span style="color: #046ECA" v-if="detailData.trigger">
|
||||||
|
<!--todo 此处返回的是PT5M,具体时间处理根据后续字段来看-->
|
||||||
|
{{ getNumberFromStr(detailData.trigger.interval) }} minutes
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-basic-function">
|
||||||
|
<div class="detection-drawer-title">Evaluation Frequency</div>
|
||||||
|
<div class="basic-function-value" v-if="detailData.trigger">{{ getNumberFromStr(detailData.trigger.resetInterval) }} minutes</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { switchStatus, toUpperCaseByString } from '@/utils/tools'
|
||||||
|
import { eventSeverityColor } from '@/utils/constants'
|
||||||
|
import axios from '_axios@0.21.4@axios'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DetectionDrawer',
|
||||||
|
props: {
|
||||||
|
drawerInfo: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
activeRule: 'rule',
|
||||||
|
activeTrigger: 'trigger',
|
||||||
|
detailData: {},
|
||||||
|
eventSeverityColor,
|
||||||
|
severityList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
drawerInfo: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
if (n) {
|
||||||
|
this.getDetailData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
switchStatus,
|
||||||
|
toUpperCaseByString,
|
||||||
|
getDetailData () {
|
||||||
|
this.severityList = [
|
||||||
|
{
|
||||||
|
severity: 'critical',
|
||||||
|
list: ['> 60 Kpackets/s', '> 50 Unique Src IPs']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
severity: 'high',
|
||||||
|
list: ['> 20 Kpackets/s', '> 50 Unique Src IPs']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
axios.get(`${api.detection.detail}/${this.drawerInfo.ruleId}`).then(res => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
this.detailData = res.data.data
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getNumberFromStr (str) {
|
||||||
|
return str.match(/\d+(\.\d+)?/g)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
||||||
215
src/views/detectionsNew/DetectionFilter.vue
Normal file
215
src/views/detectionsNew/DetectionFilter.vue
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="detection-filter-title">
|
||||||
|
{{ $t('detections.filters') }}
|
||||||
|
</div>
|
||||||
|
<div class="detection-filter-content">
|
||||||
|
<div>
|
||||||
|
<div class="filter-content-title">{{ $t('overall.status') }}</div>
|
||||||
|
<div class="filter-content-content">
|
||||||
|
<el-checkbox-group v-model="checkStatus" @change="onChangeCategory" style="display: flex;flex-direction: column">
|
||||||
|
<el-checkbox v-for="item in statusList" :key="item.label" class="filter-content-checkbox" :label="item.status">
|
||||||
|
<div>{{ item.label }}</div>
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="filter-content-title">{{ $t('overall.category') }}</div>
|
||||||
|
<div class="filter-content-content">
|
||||||
|
<el-checkbox-group v-model="checkCategory" @change="onChangeCategory">
|
||||||
|
<el-checkbox v-for="item in categoryList" :key="item.value" class="filter-content-checkbox" :label="item.value">
|
||||||
|
{{ item.label }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="filter-content-title">{{ $t('overall.type') }}</div>
|
||||||
|
<div class="filter-content-content">
|
||||||
|
<el-checkbox-group v-model="checkType" @change="onChangeCategory" style="display: flex;flex-direction: column">
|
||||||
|
<el-checkbox v-for="item in typeList" :key="item.value" class="filter-content-checkbox" :label="item.value">
|
||||||
|
{{ item.label }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DetectionFilter',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
statusList: [],
|
||||||
|
categoryList: [],
|
||||||
|
typeList: [],
|
||||||
|
checkStatus: [],
|
||||||
|
checkCategory: [],
|
||||||
|
checkType: [],
|
||||||
|
url: api.detection.statistics
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
// 开发时删除---start
|
||||||
|
this.statusList = [
|
||||||
|
{ status: 1, label: 'Enabled' },
|
||||||
|
{ status: 0, label: 'Disabled' }
|
||||||
|
]
|
||||||
|
this.checkStatus = [0, 1]
|
||||||
|
this.categoryList = [
|
||||||
|
{ value: 'security', label: 'Security Event' },
|
||||||
|
{ value: 'performance', label: 'Performance Event' },
|
||||||
|
{ value: 'regulatory_risk', label: 'Regulatory Risk Event' }
|
||||||
|
]
|
||||||
|
this.checkCategory = ['security', 'performance', 'regulatory_risk']
|
||||||
|
this.typeList = [
|
||||||
|
{ value: 'c&c', label: 'C&C' },
|
||||||
|
{ value: 'ddos', label: 'DDos' },
|
||||||
|
{ value: 'lateral_movement', label: 'Lateral movement' },
|
||||||
|
{ value: 'brute_force', label: 'Brute force' }
|
||||||
|
]
|
||||||
|
this.checkType = ['c&c', 'ddos', 'lateral_movement', 'brute_force']
|
||||||
|
// 开发时删除---end
|
||||||
|
|
||||||
|
// todo 暂时禁用,后续再开发时解禁
|
||||||
|
// this.getFilterData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getFilterData (params) {
|
||||||
|
let searchParams = { pageSize: -1 }
|
||||||
|
if (params) {
|
||||||
|
searchParams = { ...searchParams, ...params }
|
||||||
|
}
|
||||||
|
get(this.url, searchParams).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
if (response.data.statusList) {
|
||||||
|
this.statusList = []
|
||||||
|
response.data.statusList.forEach(item => {
|
||||||
|
this.statusList.push({ status: item.status, label: this.switchStatus(item.status) })
|
||||||
|
this.checkStatus.push(item.status)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.categoryList = response.data.categoryList
|
||||||
|
if (response.data.categoryList) {
|
||||||
|
response.data.categoryList.forEach(item => {
|
||||||
|
this.checkCategory.push(item.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.typeList = response.data.typeList
|
||||||
|
if (response.data.typeList) {
|
||||||
|
response.data.typeList.forEach(item => {
|
||||||
|
this.checkType.push(item.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(response)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
// this.initTypeData()
|
||||||
|
// this.initStatusData()
|
||||||
|
// const self = this
|
||||||
|
// this.$nextTick(() => {
|
||||||
|
// if (self.$refs.knowledgeTreeTypeFilter) {
|
||||||
|
// self.$refs.knowledgeTreeTypeFilter.setCheckedKeys(this.defaultCheckedCategory)
|
||||||
|
// }
|
||||||
|
// if (self.$refs.knowledgeTreeStatusFilter) {
|
||||||
|
// self.$refs.knowledgeTreeStatusFilter.setCheckedKeys(this.defaultCheckedStatus)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onChangeCategory (data) {
|
||||||
|
// todo 暂时禁用,后续再开发时解禁
|
||||||
|
// 根据选择的值,构造不同入参,传给列表页,调用查询列表接口
|
||||||
|
},
|
||||||
|
switchStatus (status) {
|
||||||
|
switch (status) {
|
||||||
|
case 0: return 'Disabled'
|
||||||
|
case 1: return 'Enabled'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.detection-filter-title {
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
background: #F7F7F7;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-shadow: 0 1px 0 0 rgba(226, 229, 236, 1);
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
.detection-filter-content {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.filter-content-title {
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.filter-content-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.filter-content-checkbox {
|
||||||
|
line-height: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
.el-checkbox__inner {
|
||||||
|
width: 16px !important;
|
||||||
|
height: 16px !important;
|
||||||
|
text-align: center !important;
|
||||||
|
line-height: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
||||||
|
border-color: #38ACD2;
|
||||||
|
background: #38ACD2;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.el-checkbox__input.is-indeterminate .el-checkbox__inner:before {
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-radius: 1px;
|
||||||
|
}
|
||||||
|
.el-checkbox__input.is-checked {
|
||||||
|
.el-checkbox__inner {
|
||||||
|
border-color: #38ACD2;
|
||||||
|
background: #38ACD2;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-checkbox__input.is-focus {
|
||||||
|
.el-checkbox__inner {
|
||||||
|
border-color: #38ACD2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-checkbox__label {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
251
src/views/detectionsNew/DetectionForm.vue
Normal file
251
src/views/detectionsNew/DetectionForm.vue
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
<template>
|
||||||
|
<div class="detection-form">
|
||||||
|
|
||||||
|
<div class="detection-form-header">
|
||||||
|
Create Alert Policies
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--第一步:General Settings-->
|
||||||
|
<div class="detection-form-content">
|
||||||
|
<div class="detection-form-collapse">
|
||||||
|
<el-collapse v-model="activeNames">
|
||||||
|
<el-collapse-item name="1">
|
||||||
|
<template #title>
|
||||||
|
<div class="form-collapse-header">
|
||||||
|
<div :class="activeNames[0]==='1' ? 'form-collapse-header-no-active' : 'form-collapse-header-no'">1</div>
|
||||||
|
<div class="form-collapse-header-title">General Settings</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="form-collapse-content">
|
||||||
|
<general-settings ref="form" @setSettingForm="getFormSetting" />
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--第二步:Rule Definition-->
|
||||||
|
<div class="detection-form-collapse">
|
||||||
|
<el-collapse v-model="activeNames" style="position: relative;">
|
||||||
|
<el-collapse-item name="2">
|
||||||
|
<template #title>
|
||||||
|
<div class="form-collapse-header">
|
||||||
|
<div :class="activeNames[0]==='2' ? 'form-collapse-header-no-active' : 'form-collapse-header-no'">2</div>
|
||||||
|
<div class="form-collapse-header-title">Rule Definition</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="form-collapse-content">
|
||||||
|
<rule-definition :settingObj="settingObj" @setRuleObj="getRuleObj" />
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--第三步:Trigger-->
|
||||||
|
<div class="detection-form-collapse">
|
||||||
|
<el-collapse v-model="activeNames">
|
||||||
|
<el-collapse-item name="3">
|
||||||
|
<template #title>
|
||||||
|
<div class="form-collapse-header">
|
||||||
|
<div :class="activeNames[0]==='3' ? 'form-collapse-header-no-active' : 'form-collapse-header-no'">3</div>
|
||||||
|
<div class="form-collapse-header-title">Trigger</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="form-collapse-content margin-t-18">
|
||||||
|
<el-form 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-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>
|
||||||
|
|
||||||
|
<el-form-item prop="intervalVal">
|
||||||
|
<el-select v-model="triggerObj.intervalVal" class="form-trigger__select" placeholder=" " size="mini">
|
||||||
|
<el-option
|
||||||
|
v-for="item in intervalList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="trigger-block-item">
|
||||||
|
<div>With the counter resetting after no activity for</div>
|
||||||
|
<el-form-item prop="minute">
|
||||||
|
<el-input size="mini" v-model="triggerObj.minute" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<div>minutes</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="form-setting__btn1">
|
||||||
|
<el-button @click="createPolicy">Create & enable rule</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import GeneralSettings from '@/components/table/detection/GeneralSettings'
|
||||||
|
import RuleDefinition from '@/components/table/detection/RuleDefinition'
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DetectionForm',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
activeNames: ['1'],
|
||||||
|
settingObj: {}, // General Settings,即第一步的form表单信息
|
||||||
|
ruleObj: {}, // 第二步的form表单信息
|
||||||
|
// 第三步的form表单信息
|
||||||
|
triggerObj: {
|
||||||
|
atLeast: '',
|
||||||
|
interval: '',
|
||||||
|
intervalVal: '',
|
||||||
|
minute: '',
|
||||||
|
finishFlag: false
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
atLeast: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
interval: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
intervalVal: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
minute: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: this.$t('validate.required'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
intervalList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
GeneralSettings,
|
||||||
|
RuleDefinition
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getStatistics()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 获取下拉列表数据 */
|
||||||
|
getStatistics () {
|
||||||
|
get(api.detection.statistics, { pageSize: -1 }).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
this.intervalList = response.data.intervalList || []
|
||||||
|
} else {
|
||||||
|
console.error(response)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 获取General Settings折叠板form数据 */
|
||||||
|
getFormSetting (data) {
|
||||||
|
this.handleActiveNames('1', this.activeNames)
|
||||||
|
this.settingObj = JSON.parse(JSON.stringify(data))
|
||||||
|
},
|
||||||
|
/** 获取Rule Definition折叠板form数据 */
|
||||||
|
getRuleObj (data) {
|
||||||
|
this.handleActiveNames('2', this.activeNames)
|
||||||
|
this.ruleObj = JSON.parse(JSON.stringify(data))
|
||||||
|
},
|
||||||
|
/** 自动展开收起折叠板 */
|
||||||
|
handleActiveNames (name, arr) {
|
||||||
|
const list = arr
|
||||||
|
list.splice(list.indexOf(name), 1)
|
||||||
|
this.activeNames = []
|
||||||
|
list.forEach(t => {
|
||||||
|
this.activeNames.push(t)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 创建policy */
|
||||||
|
createPolicy () {
|
||||||
|
const self = this
|
||||||
|
const settingLen = Object.keys(this.settingObj).length
|
||||||
|
const ruleLen = Object.keys(this.ruleObj).length
|
||||||
|
|
||||||
|
if (settingLen > 0 && ruleLen > 0) {
|
||||||
|
this.$refs.form3.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
// 最终提交form
|
||||||
|
// const formObj = { ...this.settingObj, ...this.ruleObj, ...this.triggerObj }
|
||||||
|
this.$message({
|
||||||
|
duration: 2000,
|
||||||
|
type: 'success',
|
||||||
|
message: this.$t('tip.saveSuccess')
|
||||||
|
})
|
||||||
|
|
||||||
|
// axios.post('api', formObj).then(response => {
|
||||||
|
// if (response.data.code === 200) {
|
||||||
|
// this.$message({
|
||||||
|
// duration: 2000,
|
||||||
|
// type: 'success',
|
||||||
|
// message: this.$t('tip.saveSuccess')
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// this.$router.push({
|
||||||
|
// path: '/detectionNew',
|
||||||
|
// query: {
|
||||||
|
// t: +new Date()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// this.$message.error(this.errorMsgHandler(response))
|
||||||
|
// }
|
||||||
|
// }).catch(e => {
|
||||||
|
// console.error(e)
|
||||||
|
// this.$message.error(this.errorMsgHandler(e))
|
||||||
|
// }).finally(() => {
|
||||||
|
// //
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (settingLen === 0) {
|
||||||
|
this.handleFormError('1')
|
||||||
|
} else if (ruleLen === 0) {
|
||||||
|
this.handleFormError('2')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleFormError (name) {
|
||||||
|
const list = this.activeNames
|
||||||
|
list.push(name)
|
||||||
|
this.activeNames = []
|
||||||
|
list.forEach(t => {
|
||||||
|
this.activeNames.push(t)
|
||||||
|
})
|
||||||
|
this.$message.error('请确保信息填写完整')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
175
src/views/detectionsNew/DetectionTable.vue
Normal file
175
src/views/detectionsNew/DetectionTable.vue
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<template>
|
||||||
|
<el-table
|
||||||
|
id="detectionTable"
|
||||||
|
class="detection-table"
|
||||||
|
ref="dataTable"
|
||||||
|
:data="tableData"
|
||||||
|
height="100%"
|
||||||
|
border
|
||||||
|
empty-text=" "
|
||||||
|
@header-dragend="dragend"
|
||||||
|
@sort-change="tableDataSort"
|
||||||
|
@selection-change="selectionChange"
|
||||||
|
@row-dblclick="rowDoubleClick"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
:resizable="false"
|
||||||
|
align="center"
|
||||||
|
type="selection"
|
||||||
|
:selectable="selectable"
|
||||||
|
width="55">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, index) in customTableTitles"
|
||||||
|
:key="`col-${index}`"
|
||||||
|
:fixed="item.fixed"
|
||||||
|
:label="item.label"
|
||||||
|
:min-width="`${item.minWidth}`"
|
||||||
|
:prop="item.prop"
|
||||||
|
:resizable="true"
|
||||||
|
:sort-orders="['ascending', 'descending']"
|
||||||
|
:sortable="item.sortable"
|
||||||
|
:width="`${item.width}`"
|
||||||
|
class="data-column"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span class="data-column__span">{{ item.label }}</span>
|
||||||
|
<div class="col-resize-area"></div>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" :column="item">
|
||||||
|
<template v-if="item.prop === 'name'">
|
||||||
|
<template v-if="scope.row.i18n">
|
||||||
|
<span :title="scope.row[item.prop]">{{ $t(scope.row.i18n) }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="scope.row.name">
|
||||||
|
<span :title="scope.row[item.prop]">{{ scope.row.name }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span>-</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.prop === 'status'">
|
||||||
|
<div :class="`detection-tag-status${scope.row[item.prop]}`">
|
||||||
|
{{ switchStatus(scope.row[item.prop]) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.prop === 'description'">
|
||||||
|
<div style="padding-right: 20px">{{ scope.row[item.prop] }}</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.prop === 'dimensions' && scope.row[item.prop]">
|
||||||
|
<span class="detection-tag-blue">{{ scope.row[item.prop] }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.prop === 'library' && scope.row[item.prop]">
|
||||||
|
<span class="detection-table-library">{{ scope.row[item.prop] }}</span>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ scope.row[item.prop] || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-slot:empty>
|
||||||
|
<div class="table-no-data" v-if="isNoData">
|
||||||
|
<div class="table-no-data__title">{{ $t('npm.noData') }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import table from '@/mixins/table'
|
||||||
|
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||||
|
import { switchStatus } from '@/utils/tools'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DetectionTable',
|
||||||
|
props: {
|
||||||
|
isNoData: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [table],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tableTitle: [
|
||||||
|
{
|
||||||
|
label: this.$t('knowledge.status'),
|
||||||
|
prop: 'status',
|
||||||
|
show: true,
|
||||||
|
minWidth: 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ID',
|
||||||
|
prop: 'ruleId',
|
||||||
|
show: true,
|
||||||
|
minWidth: 65
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('config.roles.name'),
|
||||||
|
prop: 'name',
|
||||||
|
minWidth: 90,
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('overall.category'),
|
||||||
|
prop: 'category',
|
||||||
|
minWidth: 140,
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('overall.type'),
|
||||||
|
prop: 'eventType',
|
||||||
|
minWidth: 145,
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('overall.remark'),
|
||||||
|
prop: 'description',
|
||||||
|
minWidth: 205,
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// label: this.$t('config.user.createTime'),
|
||||||
|
label: 'Dimensions',
|
||||||
|
prop: 'dimensions',
|
||||||
|
minWidth: 204,
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// label: this.$t('config.user.createTime'),
|
||||||
|
label: 'Library',
|
||||||
|
prop: 'library',
|
||||||
|
minWidth: 204,
|
||||||
|
show: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
tableData: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
if (n) {
|
||||||
|
n.forEach(t => {
|
||||||
|
if (t.ruleType === 'indicator_match') {
|
||||||
|
t.library = t.ruleConfig.knowledge.name
|
||||||
|
} else if (t.ruleType === 'threshold') {
|
||||||
|
t.dimensions = t.ruleConfig.dimensions
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
dateFormatByAppearance,
|
||||||
|
switchStatus,
|
||||||
|
rowDoubleClick (data) {
|
||||||
|
this.$emit('rowDoubleClick', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
77
src/views/detectionsNew/DetectionTools.vue
Normal file
77
src/views/detectionsNew/DetectionTools.vue
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="top-tools__left">
|
||||||
|
<button
|
||||||
|
id="knowledge-base-add"
|
||||||
|
:title="$t('knowledgeBase.createKnowledgeBase')"
|
||||||
|
class="top-tool-btn margin-r-10 top-tool-btn--create"
|
||||||
|
@click="onCreate"
|
||||||
|
style="width:72px;">
|
||||||
|
<i class="cn-icon-xinjian cn-icon"></i>
|
||||||
|
<span>{{ $t('overall.create') }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <button-->
|
||||||
|
<!-- id="knowledge-base-edit"-->
|
||||||
|
<!-- :title="$t('knowledgeBase.editKnowledgeBase')"-->
|
||||||
|
<!-- class="top-tool-btn margin-r-10"-->
|
||||||
|
<!-- style="width:72px;">-->
|
||||||
|
<!-- <i class="cn-icon-edit cn-icon" ></i>-->
|
||||||
|
<!-- <span>{{$t('overall.edit')}}</span>-->
|
||||||
|
<!-- </button>-->
|
||||||
|
|
||||||
|
<button
|
||||||
|
:disabled="disableDelete"
|
||||||
|
id="knowledge-base-delete"
|
||||||
|
:title="$t('knowledgeBase.deleteKnowledgeBase')"
|
||||||
|
class="top-tool-btn margin-r-10"
|
||||||
|
@click="onDelete"
|
||||||
|
style="width:72px;">
|
||||||
|
<i class="cn-icon-delete cn-icon"></i>
|
||||||
|
<span>{{ $t('overall.delete') }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="top-tool-search margin-l-10">
|
||||||
|
<el-input v-model="keyWord" size="small" style="height: 28px;" @keyup.enter="onSearch"></el-input>
|
||||||
|
<button
|
||||||
|
class="top-tool-btn top-tool-btn--search"
|
||||||
|
style="border-radius: 0 2px 2px 0 !important;"
|
||||||
|
@click="onSearch">
|
||||||
|
<i class="el-icon-search"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'DetectionTools',
|
||||||
|
props: {
|
||||||
|
disableDelete: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
keyWord: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSearch () {
|
||||||
|
this.$emit('search', this.keyWord)
|
||||||
|
},
|
||||||
|
onCreate () {
|
||||||
|
this.$emit('create')
|
||||||
|
},
|
||||||
|
onDelete (data) {
|
||||||
|
this.$emit('delete', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
301
src/views/detectionsNew/Index.vue
Normal file
301
src/views/detectionsNew/Index.vue
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
<template>
|
||||||
|
<div class="detection">
|
||||||
|
<div class="detection-title">
|
||||||
|
<span>{{ $t('overall.detections') }}</span>
|
||||||
|
<span class="detection-title-label">
|
||||||
|
60 Polices created(200 max) | 32 polices enabled(100 max)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detection-content">
|
||||||
|
<div class="detection-filter">
|
||||||
|
<detection-filter></detection-filter>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detection-block">
|
||||||
|
<detection-tools
|
||||||
|
@delete="toDelete"
|
||||||
|
@create="onCreate"
|
||||||
|
@search="onSearch"
|
||||||
|
:disableDelete="disableDelete"/>
|
||||||
|
|
||||||
|
<div class="detection-table" style="position: relative">
|
||||||
|
<loading :loading="loading"></loading>
|
||||||
|
<detection-table
|
||||||
|
ref="dataTable"
|
||||||
|
height="100%"
|
||||||
|
:api="url"
|
||||||
|
:isNoData="isNoData"
|
||||||
|
:custom-table-title="tools.customTableTitle"
|
||||||
|
:table-data="tableData"
|
||||||
|
:is-selected-status="isSelectedStatus"
|
||||||
|
:all-count="18"
|
||||||
|
@selectionChange="selectionChange"
|
||||||
|
@reload="reloadRowList"
|
||||||
|
@toggleLoading="toggleLoading"
|
||||||
|
@rowDoubleClick="onRowDoubleClick"
|
||||||
|
></detection-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="knowledge-pagination">
|
||||||
|
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="showConfirmDialog"
|
||||||
|
:title="$t('overall.hint')"
|
||||||
|
width="480px"
|
||||||
|
custom-class="del-model-hint"
|
||||||
|
:before-close="handleClose">
|
||||||
|
<div class="dialog-message">{{ $t('knowledge.deleteDataHint') }}</div>
|
||||||
|
<el-table v-model="delItemList"
|
||||||
|
ref="delDataTable"
|
||||||
|
:data="batchDeleteObjs"
|
||||||
|
@selection-change="secondSelectionChange"
|
||||||
|
height="156px"
|
||||||
|
width="100%"
|
||||||
|
class="dialog-table"
|
||||||
|
cell-style="padding:4px 0px;font-size: 12px;color: #353636;font-weight: 400;"
|
||||||
|
header-cell-style="padding:4px 0px;background: #F5F8FA;font-size: 12px;color: #353636;font-weight: 500;">
|
||||||
|
<el-table-column :resizable="false" align="center" type="selection" width="50"></el-table-column>
|
||||||
|
<el-table-column property="ruleId" label="ID" width="70"></el-table-column>
|
||||||
|
<el-table-column property="name" label="Name"></el-table-column>
|
||||||
|
<el-table-column property="category" label="Category" width="100" :formatter="categoryFormat"></el-table-column>
|
||||||
|
<el-table-column property="function" label="Function" width="110" :formatter="sourceFormat"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="showConfirmDialog = false">{{ $t('overall.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="submit">{{ $t('tip.confirm') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<div class="detection-drawer">
|
||||||
|
<el-drawer v-model="showDrawer" :with-header="false">
|
||||||
|
<detection-drawer :drawer-info="drawerInfo"></detection-drawer>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DetectionFilter from '@/views/detectionsNew/DetectionFilter'
|
||||||
|
import DetectionTools from '@/views/detectionsNew/DetectionTools'
|
||||||
|
import DetectionTable from '@/views/detectionsNew/DetectionTable'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import dataListMixin from '@/mixins/data-list'
|
||||||
|
import DetectionDrawer from '@/views/detectionsNew/DetectionDrawer'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Index',
|
||||||
|
components: {
|
||||||
|
DetectionFilter,
|
||||||
|
DetectionTools,
|
||||||
|
DetectionTable,
|
||||||
|
DetectionDrawer
|
||||||
|
},
|
||||||
|
mixins: [dataListMixin],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
// url: api.detection.list,
|
||||||
|
url: api.knowledgeBase,
|
||||||
|
listUrl: api.detection.list,
|
||||||
|
tableId: 'detectionTable',
|
||||||
|
isNoData: false,
|
||||||
|
tableData: [],
|
||||||
|
isSelectedStatus: false,
|
||||||
|
batchDeleteObjs: [], // 待删除列表
|
||||||
|
secondBatchDeleteObjs: [],
|
||||||
|
disableDelete: true,
|
||||||
|
showConfirmDialog: false,
|
||||||
|
delItemList: [],
|
||||||
|
showDrawer: false,
|
||||||
|
drawerInfo: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSearch () {
|
||||||
|
// todo 暂时禁用,后续再开发时解禁
|
||||||
|
// const params = {
|
||||||
|
// ...this.filterParams,
|
||||||
|
// name: this.keyWord
|
||||||
|
// }
|
||||||
|
// this.clearList()
|
||||||
|
// this.search(params)
|
||||||
|
// this.$refs.knowledgeFilter.reloadFilter(this.checkedCategoryIds, this.checkedStatusIds)
|
||||||
|
},
|
||||||
|
toDelete (data) {
|
||||||
|
// todo 暂时禁用,后续再开发时解禁
|
||||||
|
// if (data && data.ruleId) {
|
||||||
|
// this.secondBatchDeleteObjs = []
|
||||||
|
// this.batchDeleteObjs = []
|
||||||
|
// this.secondBatchDeleteObjs.push(data)
|
||||||
|
// this.batchDeleteObjs.push(data)
|
||||||
|
// }
|
||||||
|
// this.showDelDialog()
|
||||||
|
},
|
||||||
|
onCreate () {
|
||||||
|
// todo 暂时禁用,后续再开发时解禁
|
||||||
|
// this.$router.push({
|
||||||
|
// path: '/detection/policies/create',
|
||||||
|
// query: {
|
||||||
|
// t: +new Date()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
selectionChange (objs) {
|
||||||
|
this.batchDeleteObjs = []
|
||||||
|
objs.forEach(obj => {
|
||||||
|
const delObj = this.batchDeleteObjs.find(item => item.ruleId === obj.ruleId)
|
||||||
|
if (delObj === undefined) {
|
||||||
|
this.batchDeleteObjs.push(obj)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.disableEdit = this.batchDeleteObjs.length !== 1
|
||||||
|
this.disableDelete = this.batchDeleteObjs.length < 1
|
||||||
|
},
|
||||||
|
reloadRowList () {
|
||||||
|
this.getTableData()
|
||||||
|
},
|
||||||
|
toggleLoading () {
|
||||||
|
},
|
||||||
|
showDelDialog () {
|
||||||
|
this.showConfirmDialog = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.batchDeleteObjs.forEach((item) => {
|
||||||
|
this.$refs.delDataTable.toggleRowSelection(item, true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleClose () {
|
||||||
|
this.showConfirmDialog = false
|
||||||
|
},
|
||||||
|
secondSelectionChange (objs) {
|
||||||
|
this.secondBatchDeleteObjs = objs
|
||||||
|
},
|
||||||
|
categoryFormat (row, column) {
|
||||||
|
// const category = row.category
|
||||||
|
// const t = knowledgeBaseCategory.find(t => t.value === category)
|
||||||
|
// return t ? t.name : category
|
||||||
|
},
|
||||||
|
sourceFormat (row, column) {
|
||||||
|
// const source = row.source
|
||||||
|
// const t = knowledgeBaseSource.find(t => t.value === source)
|
||||||
|
// return t ? t.name : source
|
||||||
|
},
|
||||||
|
submit () {
|
||||||
|
this.delBatchDetection()
|
||||||
|
this.showConfirmDialog = false
|
||||||
|
},
|
||||||
|
delBatchDetection () {
|
||||||
|
const ids = []
|
||||||
|
if (this.secondBatchDeleteObjs && this.secondBatchDeleteObjs.length > 0) {
|
||||||
|
this.secondBatchDeleteObjs.forEach(item => {
|
||||||
|
ids.push(item.ruleId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (ids.length === 0) {
|
||||||
|
this.$alert(this.$t('tip.pleaseSelect'), {
|
||||||
|
confirmButtonText: this.$t('tip.yes'),
|
||||||
|
type: 'warning'
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// todo 调用接口删除
|
||||||
|
// this.toggleLoading(true)
|
||||||
|
// axios.delete(api.detection.delete + '?ruleIds=' + ids).then(response => {
|
||||||
|
// if (response.data.code === 200) {
|
||||||
|
// this.delFlag = true
|
||||||
|
// this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||||
|
// this.secondBatchDeleteObjs.forEach((item) => {
|
||||||
|
// this.$refs.delDataTable.toggleRowSelection(item, false)
|
||||||
|
// })
|
||||||
|
// this.$refs.knowledgeFilter.reloadFilter()
|
||||||
|
// this.secondBatchDeleteObjs = []
|
||||||
|
// this.batchDeleteObjs = []
|
||||||
|
// delete this.searchLabel.category
|
||||||
|
// delete this.searchLabel.source
|
||||||
|
// this.getTableData()
|
||||||
|
// } else {
|
||||||
|
// this.$message.error(response.data.message)
|
||||||
|
// }
|
||||||
|
// }).finally(() => {
|
||||||
|
// this.toggleLoading(false)
|
||||||
|
// if (this.isSelectedStatus != undefined) {
|
||||||
|
// this.isSelectedStatus = false
|
||||||
|
// this.disableDelete = true
|
||||||
|
// this.secondBatchDeleteObjs = []
|
||||||
|
// this.batchDeleteObjs = []
|
||||||
|
// this.showConfirmDialog = false
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRowDoubleClick (data) {
|
||||||
|
// todo 暂时禁用,后续再开发时解禁
|
||||||
|
// this.showDrawer = true
|
||||||
|
// this.drawerInfo = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.detection {
|
||||||
|
padding: 20px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.detection-title {
|
||||||
|
font-family: NotoSansHans-Black;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 900;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.detection-title-label {
|
||||||
|
font-family: NotoSansSChineseRegular;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #717171;
|
||||||
|
letter-spacing: 0;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-content {
|
||||||
|
margin-top: 15px;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 96px);
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.detection-filter {
|
||||||
|
width: 320px;
|
||||||
|
height: calc(100% + 34px);
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 1px solid rgba(226, 229, 236, 1);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detection-block {
|
||||||
|
width: calc(100% - 340px);
|
||||||
|
|
||||||
|
.detection-table {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 44px);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
150
src/views/entityExplorer/EntityFilterOld.vue
Normal file
150
src/views/entityExplorer/EntityFilterOld.vue
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
<template >
|
||||||
|
<div class="entity-filter-case">
|
||||||
|
<div class="filter-case__header">{{$t('entities.filter')}}</div>
|
||||||
|
<div
|
||||||
|
class="entity-filter"
|
||||||
|
v-for="(filters, index) in filterData"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="filter__header">{{filters.title}}</div>
|
||||||
|
<div class="filter__body">
|
||||||
|
|
||||||
|
<div class="filter__row" v-for="(item, i) in filters.data" :key="i">
|
||||||
|
<el-popover popper-class="filter__row-popover" placement="right-start" :width="440" v-model:visible="item.showTopTen">
|
||||||
|
<template #reference>
|
||||||
|
<div class="filter__row-popover" @click="showTopDialog(i, item, filters)">
|
||||||
|
<div class="row__label">
|
||||||
|
<i :class="item.icon"></i>
|
||||||
|
<span>{{item.label}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="row__value">
|
||||||
|
<loading :loading="loadingLeft" size="small"></loading>
|
||||||
|
<span>{{item.value}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<entity-top
|
||||||
|
ref="entityTopTenPop"
|
||||||
|
:loading="loading"
|
||||||
|
:popover-data="popoverData"
|
||||||
|
:item-data="itemData"
|
||||||
|
:total-count="totalCount"
|
||||||
|
:top-column="item.topColumn"
|
||||||
|
@filter="filter"
|
||||||
|
></entity-top>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import EntityTop from '@/views/entityExplorer/EntityTop'
|
||||||
|
import { get } from '@/utils/http'
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import Loading from '@/components/common/Loading'
|
||||||
|
import { getSecond } from '@/utils/date-util'
|
||||||
|
export default {
|
||||||
|
name: 'EntityFilter',
|
||||||
|
components: {
|
||||||
|
Loading,
|
||||||
|
EntityTop
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
filterData: Array,
|
||||||
|
q: String,
|
||||||
|
timeFilter: Object,
|
||||||
|
loadingLeft: Boolean
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
topList: 'list',
|
||||||
|
topData: [],
|
||||||
|
entityTopTenData: [],
|
||||||
|
currentColumn: {},
|
||||||
|
totalCount: 0,
|
||||||
|
loading: false,
|
||||||
|
popoverData: [],
|
||||||
|
itemData: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
currentColumn (n, o) {
|
||||||
|
if (n.column === 'dnsServerOrgCount') {
|
||||||
|
this.totalCount = this.filterData[3].orgTotalCount
|
||||||
|
} else if (n.column === 'dnsServerSoftwareCount') {
|
||||||
|
this.totalCount = this.filterData[3].softwareTotalCount
|
||||||
|
} else if (n.column === 'dnsServerOsCount') {
|
||||||
|
this.totalCount = this.filterData[3].osTotalCount
|
||||||
|
} else if (n.column === 'categoryDistinctCount' && n.type === 'app') {
|
||||||
|
this.totalCount = this.filterData[1].totalCount
|
||||||
|
} else {
|
||||||
|
let count = 0
|
||||||
|
this.filterData.forEach(f => {
|
||||||
|
const filter = f.data.some(d => d.column === n.column)
|
||||||
|
if (filter) {
|
||||||
|
count = f.totalCount
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.totalCount = count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showTopDialog (i, item, filter) {
|
||||||
|
if (this.currentColumn.column === item.column && item.showTopTen) {
|
||||||
|
item.showTopTen = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.filterData.forEach(f => {
|
||||||
|
f.data.forEach(ff => {
|
||||||
|
ff.showTopTen = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
item.showTopTen = true
|
||||||
|
this.currentColumn = {
|
||||||
|
column: item.column,
|
||||||
|
type: filter.type
|
||||||
|
}
|
||||||
|
const queryParams = {
|
||||||
|
q: this.q,
|
||||||
|
entityType: filter.type,
|
||||||
|
column: item.topColumn,
|
||||||
|
top: 10,
|
||||||
|
startTime: getSecond(this.timeFilter.startTime),
|
||||||
|
endTime: getSecond(this.timeFilter.endTime)
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
this.popoverData = []
|
||||||
|
this.itemData = {}
|
||||||
|
get(api.filterTop, queryParams).then(response => {
|
||||||
|
if (response.code === 200) {
|
||||||
|
if (this.currentColumn.column === item.column) {
|
||||||
|
if (filter.type === 'dns') {
|
||||||
|
this.popoverData = response.data.result.filter(f => {
|
||||||
|
return f.count > 0
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.popoverData = response.data.result
|
||||||
|
}
|
||||||
|
this.itemData = item
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.popoverData = []
|
||||||
|
this.itemData = item
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
}).catch(e => {
|
||||||
|
this.popoverData = []
|
||||||
|
this.itemData = item
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
filter (name, topData) {
|
||||||
|
this.showTopDialog('', topData)
|
||||||
|
this.$emit('filter', name, topData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user