Merge branch 'dev' of https://git.mesalab.cn/cyber-narrator/cn-ui into dev
This commit is contained in:
16
public/images/flag/Unknown.svg
Normal file
16
public/images/flag/Unknown.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="18px" height="12px" viewBox="0 0 18 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>未知</title>
|
||||
<g id="🥇Entity" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Entity_Explore【domain展开】" transform="translate(-180.000000, -216.000000)">
|
||||
<g id="未知" transform="translate(180.000000, 216.000000)">
|
||||
<rect id="矩形" fill="#EFF2F5" x="0" y="0" width="18" height="12"></rect>
|
||||
<g transform="translate(2.000000, 3.000000)" fill="#96A2B0" fill-rule="nonzero">
|
||||
<polygon id="路径" points="1.513 2.957 1.035 2 0.991 2 1.13 2.957 1.13 6.131 0 6.131 0 0 0.869 0 3.174 3.243 3.635 4.174 3.679 4.174 3.539 3.243 3.539 0.043 4.67 0.043 4.67 6.174 3.8 6.174"></polygon>
|
||||
<path d="M12.261,4.783 L10.391,4.783 L9.93,6.131 L8.739,6.131 L10.93,0 L11.808,0 L14,6.131 L12.748,6.131 L12.261,4.783 Z M10.696,3.827 L12,3.827 L11.548,2.47 L11.365,1.513 L11.321,1.513 L11.138,2.478 L10.696,3.827 Z" id="形状"></path>
|
||||
<polygon id="路径" points="8.101 0 5.367 6.174 6.396 6.174 9.123 0"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -89,3 +89,9 @@
|
||||
|
||||
@import "views/entityExplorer/graphRightListBlock";
|
||||
@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 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
background-color: white;
|
||||
overflow: auto;
|
||||
//.detection-filter-case {
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// width: 280px;
|
||||
// padding: 10px;
|
||||
// margin-right: 10px;
|
||||
// background-color: white;
|
||||
// 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 {
|
||||
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;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.filter__header {
|
||||
display: flex;
|
||||
flex: 0 0 32px;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
color: #666;
|
||||
//background-color: #F3F7FA;
|
||||
cursor: pointer;
|
||||
.filter-content-checkbox {
|
||||
line-height: 16px;
|
||||
margin-bottom: 10px;
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
padding-left: 6px;
|
||||
.el-checkbox__inner {
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
text-align: center !important;
|
||||
line-height: 16px !important;
|
||||
}
|
||||
i {
|
||||
font-size: 12px;
|
||||
transition: all linear .1s;
|
||||
transform: rotate(0) translate(0, 2px);
|
||||
|
||||
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
||||
border-color: #38ACD2;
|
||||
background: #38ACD2;
|
||||
border-radius: 2px;
|
||||
}
|
||||
i.arrow-rotate {
|
||||
transform: rotate(90deg) translate(2px, 3px);
|
||||
.el-checkbox__input.is-indeterminate .el-checkbox__inner:before {
|
||||
background: #FFFFFF;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,12 +88,17 @@
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
}
|
||||
.explorer-container {
|
||||
.explorer-container, .explorer-container-new {
|
||||
display: flex;
|
||||
overflow: visible; /*overflow: hidden;*/
|
||||
height: calc(100% - 120px);
|
||||
position: relative;
|
||||
}
|
||||
.explorer-container-new {
|
||||
height: calc(100% - 62px);
|
||||
flex-direction: column;
|
||||
width: calc(100% - 340px);
|
||||
}
|
||||
.explorer-foot {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -56,11 +56,6 @@
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
.filter-country-flag {
|
||||
width: 18px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.filter__body-item-left-index {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@@ -99,3 +94,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-country-flag {
|
||||
width: 18px;
|
||||
height: 12px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
}
|
||||
.row__content {
|
||||
display: flex;
|
||||
color: #3976CB;
|
||||
color: #046ECA;
|
||||
word-wrap: break-word;
|
||||
max-width: 30%;
|
||||
|
||||
|
||||
@@ -107,8 +107,8 @@ $font-size: 12px;
|
||||
}
|
||||
|
||||
.graph-list-country-flag {
|
||||
width: 16px;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
height: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
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,29 +118,68 @@ export default {
|
||||
if (this.listUrl) {
|
||||
listUrl = this.listUrl
|
||||
}
|
||||
get(listUrl, this.searchLabel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.tableData = response.data.list
|
||||
this.pageObj.total = response.data.total
|
||||
if (!this.tableData || this.tableData.length === 0) {
|
||||
this.isNoData = true
|
||||
} else {
|
||||
this.isNoData = false
|
||||
// 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'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error(response)
|
||||
this.isNoData = true
|
||||
if (response.message) {
|
||||
this.$message.error(response.message)
|
||||
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 {
|
||||
this.$message.error(this.$t('tip.somethingWentWrong'))
|
||||
obj.status = 0
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
}).catch(() => {
|
||||
this.isNoData = true
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
|
||||
this.tableData = list
|
||||
this.pageObj.total = list.length
|
||||
this.loading = false
|
||||
} else {
|
||||
get(listUrl, this.searchLabel).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.tableData = response.data.list
|
||||
this.pageObj.total = response.data.total
|
||||
if (!this.tableData || this.tableData.length === 0) {
|
||||
this.isNoData = true
|
||||
} else {
|
||||
this.isNoData = false
|
||||
}
|
||||
} else {
|
||||
console.error(response)
|
||||
this.isNoData = true
|
||||
if (response.message) {
|
||||
this.$message.error(response.message)
|
||||
} else {
|
||||
this.$message.error(this.$t('tip.somethingWentWrong'))
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
this.isNoData = true
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
del (row) {
|
||||
this.$confirm(this.$t('tip.confirmDelete'), {
|
||||
@@ -396,7 +435,7 @@ export default {
|
||||
tableData: {
|
||||
deep: true,
|
||||
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)
|
||||
}
|
||||
// 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 './dns'
|
||||
import './entity'
|
||||
import './detection'
|
||||
|
||||
@@ -96,6 +96,18 @@ const routes = [
|
||||
name: 'Chart',
|
||||
path: '/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',
|
||||
httpErrorMetric: '/interface/detection/performance/detail/overview/metric/httpError',
|
||||
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
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -23,6 +23,7 @@ axios.interceptors.request.use(config => {
|
||||
}
|
||||
}
|
||||
}
|
||||
config.params.q = q
|
||||
}
|
||||
|
||||
config.cancelToken = cancelToken
|
||||
|
||||
@@ -1300,3 +1300,17 @@ export function numberWithCommas (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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,10 @@ export function getUnitType (column) {
|
||||
|
||||
/* 单位转换,返回转换后的[value, unit],type=time时若value<1ms,返回<1ms,type=percent时若value<0.01%,返回<0.01% */
|
||||
export function valueToRangeValue (value, unitType) {
|
||||
const values = unitConvert(Number(value), unitType)
|
||||
const values = unitConvert(value, unitType)
|
||||
if (values[0] === '-') {
|
||||
return values
|
||||
}
|
||||
if (values[0] || values[0] === 0) {
|
||||
switch (unitType) {
|
||||
case unitTypes.time: {
|
||||
@@ -168,6 +171,12 @@ export function valueToRangeValue (value, unitType) {
|
||||
}
|
||||
break
|
||||
}
|
||||
case unitTypes.number: {
|
||||
if (values[0] < 0.01) {
|
||||
return ['<0.01', '']
|
||||
}
|
||||
break
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +485,7 @@ export function stackedLineTooltipFormatter (params) {
|
||||
str += '<div class="cn-chart-tooltip">'
|
||||
params.forEach((item, i) => {
|
||||
str += `<span class="cn-chart-tooltip-value cn-chart-tooltip__color">
|
||||
${unitConvert(item.data[1], item.value[2]).join(' ')}
|
||||
${valueToRangeValue(item.data[1], item.value[2]).join(' ')}
|
||||
</span>`
|
||||
})
|
||||
str += '</div>'
|
||||
@@ -510,7 +510,7 @@ export function appStackedLineTooltipFormatter (params) {
|
||||
</span>`
|
||||
str += '</span>'
|
||||
str += `<span class="cn-chart-tooltip-value cn-chart-tooltip__color">
|
||||
${unitConvert(item.data[1], item.value[2]).join(' ')}
|
||||
${valueToRangeValue(item.data[1], item.value[2]).join(' ')}
|
||||
</span>`
|
||||
str += '</div>'
|
||||
})
|
||||
|
||||
@@ -364,6 +364,8 @@ export default {
|
||||
let url = ''
|
||||
if (this.queryCondition.indexOf(' OR ') > -1) {
|
||||
condition = this.queryCondition.split(/["|'](.*?)["|']/)
|
||||
} else if (this.queryCondition.indexOf('+OR+') > -1) {
|
||||
condition = this.queryCondition.replace(/\+/g, ' ').split(/["|'](.*?)["|']/)
|
||||
} else {
|
||||
condition = this.queryCondition
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
</div>
|
||||
<div class="line-value-unit" :test-id="`tabContent${index}`">
|
||||
<span class="line-value-unit-number">
|
||||
{{ getTabUnit(item.analysis.avg) }}
|
||||
{{ valueToRangeValue(item.analysis.avg, unitTypes.number)[0] }}
|
||||
</span>
|
||||
<span class="line-value-unit-number2">
|
||||
<span>{{ unitConvert(item.analysis.avg, unitTypes.number)[1] }}</span>
|
||||
<span>{{ valueToRangeValue(item.analysis.avg, unitTypes.number)[1] }}</span>
|
||||
<span v-if="item.unitType">{{ item.unitType }}</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -88,7 +88,7 @@
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import * as echarts from 'echarts'
|
||||
import { stackedLineChartOption } from '@/views/charts2/charts/options/echartOption'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
|
||||
@@ -153,6 +153,7 @@ export default {
|
||||
tabsTemplate: dataForNetworkOverviewLine.tabsTemplate,
|
||||
tabs: [],
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
unitTypes,
|
||||
chartDateObject: [],
|
||||
timer: null,
|
||||
@@ -627,14 +628,6 @@ export default {
|
||||
} else {
|
||||
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
|
||||
}
|
||||
},
|
||||
// 对lineTab进行转换
|
||||
getTabUnit (avg) {
|
||||
let number = unitConvert(avg, unitTypes.number)[0]
|
||||
if (number !== '-' && JSON.parse(number) > 0 && JSON.parse(number) < 1) {
|
||||
number = '<1'
|
||||
}
|
||||
return number
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -664,6 +657,7 @@ export default {
|
||||
}
|
||||
// 检测时发现该方法占用较大内存,且未被释放
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
myChart = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
<div class="tabs-name">{{$t(item.name)}}</div>
|
||||
</div>
|
||||
<div class="line-value-unit" :test-id="`tabContent${index}`">
|
||||
<span class="line-value-unit-number">{{unitConvert(item.analysis.avg, unitTypes.number)[0]}}</span>
|
||||
<span class="line-value-unit-number">{{valueToRangeValue(item.analysis.avg, unitTypes.number)[0]}}</span>
|
||||
<span class="line-value-unit-number2">
|
||||
<span>{{unitConvert(item.analysis.avg, unitTypes.number)[1]}}</span>
|
||||
<span>{{valueToRangeValue(item.analysis.avg, unitTypes.number)[1]}}</span>
|
||||
<span v-if="item.unitType">{{item.unitType}}</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -67,7 +67,7 @@ import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { chartColor3, chartColor4, unitTypes } from '@/utils/constants'
|
||||
import { getLineType, overwriteUrl, urlParamsHandler } from '@/utils/tools'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
@@ -107,7 +107,7 @@ export default {
|
||||
return {
|
||||
options: dataForLinkTrafficLine.options,
|
||||
tabs: dataForLinkTrafficLine.tabs,
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
unitTypes,
|
||||
chartDateObject: [],
|
||||
timer: null,
|
||||
@@ -457,7 +457,7 @@ export default {
|
||||
echarts.dispose(this.myChart)
|
||||
}
|
||||
this.chartOption = null
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<div class="app-card__body">
|
||||
<div class="app-card__body-content">
|
||||
<div class="app-card__body-content-value">
|
||||
<div class="app-card__body-content-number" :test-id="`rate${index}`">{{unitConvert(app.rate, unitTypes.number).join(' ')}}</div>
|
||||
<div class="app-card__body-content-number" :test-id="`rate${index}`">{{valueToRangeValue(app.rate, unitTypes.number).join(' ')}}</div>
|
||||
<div class="app-card__body-content-percent red" v-if="app.value > 0" :test-id="`percent${index}`">
|
||||
<span v-if="app.value <= 5">
|
||||
+{{unitConvert(app.value, unitTypes.percent).join('')}}
|
||||
@@ -39,8 +39,8 @@
|
||||
</div>
|
||||
<div class="app-card__body-previous">
|
||||
<div>Total</div>
|
||||
<div v-if="metric === 'Bits/s'" :test-id="`total${index}`">{{unitConvert(app.total, unitTypes.byte).join(' ')}}</div>
|
||||
<div v-else :test-id="`total${index}`">{{unitConvert(app.total, unitTypes.number).join(' ')}}</div>
|
||||
<div v-if="metric === 'Bits/s'" :test-id="`total${index}`">{{valueToRangeValue(app.total, unitTypes.byte).join(' ')}}</div>
|
||||
<div v-else :test-id="`total${index}`">{{valueToRangeValue(app.total, unitTypes.number).join(' ')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart__drawing" v-show="!isNoData" :id="`chart-${app.name}-${app.type}`"></div>
|
||||
@@ -120,7 +120,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { storageKey, unitTypes, networkTable, operationType, curTabState } from '@/utils/constants'
|
||||
import * as echarts from 'echarts'
|
||||
import { appListChartOption } from '@/views/charts2/charts/options/echartOption'
|
||||
@@ -149,6 +149,8 @@ export default {
|
||||
appData: [],
|
||||
// 假数据
|
||||
appTempData: [],
|
||||
valueToRangeValue,
|
||||
unitConvert,
|
||||
unitTypes,
|
||||
timer: null,
|
||||
showAddApp: false,
|
||||
@@ -207,7 +209,6 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
clickOutSide () {
|
||||
this.appData.forEach(t => {
|
||||
t.moreOptions = false
|
||||
@@ -732,6 +733,7 @@ export default {
|
||||
echarts.dispose(this.myChart)
|
||||
}
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
<div class="tabs-name" :test-id="`tabTitle${index}`">{{ $t(item.name) }}</div>
|
||||
</div>
|
||||
<div class="line-value-unit" :test-id="`tabContent${index}`">
|
||||
<span class="line-value-unit-number">{{ unitConvert(item.analysis.avg, unitTypes.number)[0] }}</span>
|
||||
<span class="line-value-unit-number">{{ valueToRangeValue(item.analysis.avg, unitTypes.number)[0] }}</span>
|
||||
<span class="line-value-unit-number2">
|
||||
<span>{{ unitConvert(item.analysis.avg, unitTypes.number)[1] }}</span>
|
||||
<span>{{ valueToRangeValue(item.analysis.avg, unitTypes.number)[1] }}</span>
|
||||
<span v-if="item.unitType">{{ item.unitType }}</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
import { stackedLineChartOption } from '@/views/charts2/charts/options/echartOption'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { unitTypes, chartColor3, chartColor4 } from '@/utils/constants.js'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
|
||||
@@ -107,7 +107,7 @@ export default {
|
||||
options2: dataForNetworkOverviewLine.options2,
|
||||
tabsTemplate: dataForNetworkOverviewLine.tabsTemplate,
|
||||
tabs: [],
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
unitTypes,
|
||||
chartDateObject: [],
|
||||
timer: null,
|
||||
@@ -254,7 +254,7 @@ export default {
|
||||
symbol: 'none',
|
||||
label: {
|
||||
formatter (params) {
|
||||
const arr = unitConvert(params.value, unitTypes.number).join('')
|
||||
const arr = valueToRangeValue(params.value, unitTypes.number).join('')
|
||||
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
|
||||
},
|
||||
position: 'insideStartTop',
|
||||
@@ -576,7 +576,7 @@ export default {
|
||||
echarts.dispose(this.myChart)
|
||||
}
|
||||
// 检测时发现该方法占用较大内存,且未被释放
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
myChart = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@
|
||||
<div class="data-total" >
|
||||
<div class="data-value" :test-id="`data-value-${item.prop}${scope.row.index}`">
|
||||
<template v-if="showUnit && item.unit">
|
||||
{{scope.row[item.prop]?((scope.row[item.prop][0]||scope.row[item.prop][0]===0)? unitConvert(scope.row[item.prop][0], item.unit).join(' ') : '-'):'' }}
|
||||
{{scope.row[item.prop]?((scope.row[item.prop][0]||scope.row[item.prop][0]===0)? valueToRangeValue(scope.row[item.prop][0], item.unit).join(' ') : '-'):'' }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{scope.row[item.prop]?((scope.row[item.prop][0]||scope.row[item.prop][0]===0)? unitConvert(scope.row[item.prop][0], unitTypes.number).join(' ') : '-'):'' }}
|
||||
{{scope.row[item.prop]?((scope.row[item.prop][0]||scope.row[item.prop][0]===0)? valueToRangeValue(scope.row[item.prop][0], unitTypes.number).join(' ') : '-'):'' }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="data-trend" :test-id="`data-trend-all-${item.prop}${scope.row.index}`">
|
||||
@@ -116,10 +116,10 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="showUnit && item.unit">
|
||||
{{(scope.row[item.prop] || scope.row[item.prop]===0) ? unitConvert(scope.row[item.prop], item.unit).join(' ') : '-'}}
|
||||
{{(scope.row[item.prop] || scope.row[item.prop]===0) ? valueToRangeValue(scope.row[item.prop], item.unit).join(' ') : '-'}}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{(scope.row[item.prop] || scope.row[item.prop]===0)? unitConvert(scope.row[item.prop], unitTypes.number).join(' ') : '-'}}
|
||||
{{(scope.row[item.prop] || scope.row[item.prop]===0)? valueToRangeValue(scope.row[item.prop], unitTypes.number).join(' ') : '-'}}
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
@@ -213,7 +213,7 @@ import {
|
||||
commonErrorTip
|
||||
} from '@/utils/constants'
|
||||
import { get } from '@/utils/http'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { getChainRatio, computeScore, urlParamsHandler, overwriteUrl, readDrilldownTableConfigByUser, combineDrilldownTableWithUserConfig, getDnsMapData, handleSpecialValue, getConfigVersion } from '@/utils/tools'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
@@ -231,6 +231,7 @@ export default {
|
||||
orderBy: 'totalBytes',
|
||||
tab: 'ip',
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
unitTypes,
|
||||
networkTable,
|
||||
isNoData: false,
|
||||
@@ -1993,7 +1994,8 @@ export default {
|
||||
},
|
||||
beforeUnmount () {
|
||||
// 以下元素,检测到内存并未释放
|
||||
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
},
|
||||
unmounted () {
|
||||
this.isNoData = false
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<span class="data-total-category-value" @click="drillDownData(scope.row.i18n)">{{$t(scope.row.i18n)}}</span>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'total'">
|
||||
<div class="data-total-value">{{unitConvert(scope.row.totalPacketsRate, unitTypes.bps).join(' ')}}</div>
|
||||
<div class="data-total-value">{{valueToRangeValue(scope.row.totalPacketsRate, unitTypes.bps).join(' ')}}</div>
|
||||
<div class="data-trend">
|
||||
<div v-if="scope.row.bytesRateChainRatio > 0" class="data-total-trend data-total-trend-red">
|
||||
<i class="cn-icon-rise1 cn-icon"></i>
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'outbound'">
|
||||
<div class="data-total-value">{{unitConvert(scope.row.outboundPacketsRate, unitTypes.bps).join(' ')}}</div>
|
||||
<div class="data-total-value">{{valueToRangeValue(scope.row.outboundPacketsRate, unitTypes.bps).join(' ')}}</div>
|
||||
<div class="data-trend">
|
||||
<div v-if="scope.row.outboundBytesRateChainRatio > 0" class="data-total-trend data-total-trend-red">
|
||||
<i class="cn-icon-rise1 cn-icon"></i>
|
||||
@@ -82,7 +82,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'inbound'">
|
||||
<div class="data-total-value" :test-id="`inbound-packet-${scope.row.appSubcategory}`">{{unitConvert(scope.row.inboundPacketsRate, unitTypes.bps).join(' ')}}</div>
|
||||
<div class="data-total-value" :test-id="`inbound-packet-${scope.row.appSubcategory}`">{{valueToRangeValue(scope.row.inboundPacketsRate, unitTypes.bps).join(' ')}}</div>
|
||||
<div class="data-trend">
|
||||
<div v-if="scope.row.inboundBytesRateChainRatio > 0" class="data-total-trend data-total-trend-red">
|
||||
<i class="cn-icon-rise1 cn-icon"></i>
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
<script>
|
||||
import { unitTypes, npmCategoryInfoMapping, networkTable, operationType, npmCategoryToAppCategoryMap, curTabState } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { api } from '@/utils/api'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import { computeScore, getChainRatio, getUserDrilldownTableConfig, overwriteUrl, urlParamsHandler } from '@/utils/tools'
|
||||
@@ -149,6 +149,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
unitTypes,
|
||||
colorPatchData: [
|
||||
{ letter: '' },
|
||||
@@ -379,6 +380,7 @@ export default {
|
||||
},
|
||||
beforeUnmount () {
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -118,7 +118,7 @@ export default {
|
||||
res2.forEach((r, i) => {
|
||||
if (r.code === 200) {
|
||||
mapData.forEach(t => {
|
||||
t[keyPre[i] + 'Score'] = r.data.result.find(d => d.country === t.country)
|
||||
t[keyPre[i] + 'Score'] = r.data.result.find(d => d.country === t.country && d.province === t.province)
|
||||
})
|
||||
} else {
|
||||
this.showError = true
|
||||
|
||||
@@ -50,7 +50,7 @@ import { shallowRef } from 'vue'
|
||||
import * as am4Core from '@amcharts/amcharts4/core'
|
||||
import * as am4Maps from '@amcharts/amcharts4/maps'
|
||||
import { computeScore, getGeoData } from '@/utils/tools'
|
||||
import { storageKey, unitTypes, countryNameIdMapping } from '@/utils/constants'
|
||||
import { storageKey, unitTypes } from '@/utils/constants'
|
||||
import locationOptions from '@/views/charts2/charts/locationOptions'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
@@ -58,7 +58,6 @@ import { api } from '@/utils/api'
|
||||
import axios from 'axios'
|
||||
import { get } from '@/utils/http'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
// import { Rectangle3D } from '@amcharts/amcharts4/.internal/core/elements/3d/Rectangle3D'
|
||||
import ChartError from '@/components/common/Error'
|
||||
export default {
|
||||
name: 'NpmMap',
|
||||
@@ -141,7 +140,7 @@ export default {
|
||||
res2.forEach((r, i) => {
|
||||
if (r.code === 200) {
|
||||
mapData.forEach(t => {
|
||||
const find = r.data.result.find(d => d.country === t.country)
|
||||
const find = r.data.result.find(d => d.country === t.country && t.province === d.province)
|
||||
t[keyPre[i] + 'Score'] = find
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
<div class="single-value__content" >
|
||||
<div class="single-value__content-number" v-if="index ===0 || index ===1 || index ===2" :test-id="`singleValueContent${index}`">
|
||||
{{ unitConvert(npm.Avg, unitTypes.time).join(' ') }}
|
||||
{{ valueToRangeValue(npm.Avg, unitTypes.time).join(' ') }}
|
||||
</div>
|
||||
<div class="single-value__content-number" v-else :test-id="`singleValueContent${index}`">
|
||||
{{unitConvert(npm.Avg, unitTypes.percent).join(' ')}}
|
||||
{{valueToRangeValue(npm.Avg, unitTypes.percent).join(' ')}}
|
||||
</div>
|
||||
<div v-if="npm.value > 0" class="single-value__content-trend single-value__content-trend-red" >
|
||||
<i class="cn-icon-rise1 cn-icon" :test-id="`singleValueTrendIcon${index}`"></i>
|
||||
@@ -36,17 +36,17 @@
|
||||
<div class="single-value__circle">
|
||||
<div class="single-value__circle-p95" :test-id="`singleValueP95${index}`">
|
||||
<span v-if="index ===0 || index ===1 || index ===2">
|
||||
P95:{{ unitConvert(npm.P95, unitTypes.time).join(' ') }}</span>
|
||||
P95:{{ valueToRangeValue(npm.P95, unitTypes.time).join(' ') }}</span>
|
||||
<span v-else>
|
||||
P95:{{ unitConvert(npm.P95, unitTypes.percent).join(' ') }}
|
||||
P95:{{ valueToRangeValue(npm.P95, unitTypes.percent).join(' ') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="single-value__circle-p99" :test-id="`singleValueP99${index}`">
|
||||
<span v-if="index ===0 || index ===1 || index ===2">
|
||||
P99:{{ unitConvert(npm.P99, unitTypes.time).join(' ') }}
|
||||
P99:{{ valueToRangeValue(npm.P99, unitTypes.time).join(' ') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
P99:{{ unitConvert(npm.P99, unitTypes.percent).join(' ') }}
|
||||
P99:{{ valueToRangeValue(npm.P99, unitTypes.percent).join(' ') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,7 +63,7 @@ import { getChainRatio, getQueryByFlag2, getQueryByType } from '@/utils/tools'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import { unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { dataForNpmNetworkQuantity } from '@/utils/static-data'
|
||||
export default {
|
||||
@@ -88,6 +88,7 @@ export default {
|
||||
return {
|
||||
unitTypes,
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
npmNetworkName: dataForNpmNetworkQuantity.npmNetworkName,
|
||||
npmNetworkCycleData: [],
|
||||
npmNetworkLastCycleData: [],
|
||||
@@ -114,6 +115,8 @@ export default {
|
||||
let url = ''
|
||||
if (this.queryCondition && this.queryCondition.indexOf(' OR ') > -1) {
|
||||
condition = this.queryCondition.split(/["|'](.*?)["|']/)
|
||||
} else if (this.queryCondition.indexOf('+OR+') > -1) {
|
||||
condition = this.queryCondition.replace(/\+/g, ' ').split(/["|'](.*?)["|']/)
|
||||
} else {
|
||||
condition = this.queryCondition
|
||||
}
|
||||
@@ -347,6 +350,7 @@ export default {
|
||||
clearTimeout(this.timer1)
|
||||
clearTimeout(this.timer2)
|
||||
this.unitConvert = null
|
||||
this.valueToRangeValue = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -116,6 +116,8 @@ export default {
|
||||
|
||||
if (this.queryCondition.indexOf(' OR ') > -1) {
|
||||
condition = this.queryCondition.split(/["|'](.*?)["|']/)
|
||||
} else if (this.queryCondition.indexOf('+OR+') > -1) {
|
||||
condition = this.queryCondition.replace(/\+/g, ' ').split(/["|'](.*?)["|']/)
|
||||
} else {
|
||||
condition = this.queryCondition
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
chartColor6,
|
||||
unitTypes
|
||||
} from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { axisFormatter } from '@/views/charts/charts/tools'
|
||||
import { xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
|
||||
|
||||
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'
|
||||
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>
|
||||
@@ -25,7 +25,7 @@
|
||||
:time-filter="timeFilter"
|
||||
@filter="filter"
|
||||
></entity-filter>
|
||||
<div class="explorer-container" style="height: calc(100% - 62px);flex-direction: column;width: 100%;">
|
||||
<div class="explorer-container explorer-container-new">
|
||||
<explorer-search
|
||||
ref="search"
|
||||
:class="{'explorer-search--show-list': showList}"
|
||||
@@ -662,9 +662,9 @@ export default {
|
||||
if (item.code === 200 && item.data.list) {
|
||||
this.newFilterData[index].data = []
|
||||
item.data.list.forEach(item => {
|
||||
const obj = { label: item.value, flag: '011-china', topColumn: 'Country', value: item.uniqueEntities }
|
||||
const obj = { label: item.value, topColumn: 'Country', value: item.uniqueEntities }
|
||||
if (index === 0) {
|
||||
obj.flag = item.uniqueEntities // 接口字段名称为'China',目前svg名称为'011-china',后续再指定方案调整
|
||||
obj.flag = item.value // 接口字段名称为'China',svg名称为'CN',通过countryNameIdMapping进行转换
|
||||
}
|
||||
if (index === 1) {
|
||||
obj.topColumn = 'City'
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
|
||||
<div class="filter__body-item" v-for="(data, i) in item.data" :key="i" @click="filter(data.label, data)">
|
||||
<div class="filter__body-item-left">
|
||||
<!--当前无更好方案匹配国旗,后续解决-->
|
||||
<!--<div v-if="data.flag">-->
|
||||
<!-- <img :src="require(`../../../public/images/flag/${data.flag}.svg`)" class="filter-country-flag"/>-->
|
||||
<!--</div>-->
|
||||
<div class="filter__body-item-left-index">{{ i+1 }}</div>
|
||||
<div v-if="data.flag">
|
||||
<img v-if="data.flag===countryNameIdMapping.Unknown || !countryNameIdMapping[data.flag]" src="../../../public/images/flag/Unknown.svg" class="filter-country-flag">
|
||||
<img v-else :src="require(`../../../public/images/flag/${countryNameIdMapping[data.flag]}.png`)" class="filter-country-flag"/>
|
||||
</div>
|
||||
<div v-else class="filter__body-item-left-index">{{ i+1 }}</div>
|
||||
<div class="filter__body-item-left-label">
|
||||
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
|
||||
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">{{ data.label }}</span>
|
||||
@@ -42,6 +42,7 @@
|
||||
<script>
|
||||
import Loading from '@/components/common/Loading'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { countryNameIdMapping } from '@/utils/constants'
|
||||
export default {
|
||||
name: 'EntityFilter',
|
||||
components: { ChartNoData, Loading },
|
||||
@@ -65,7 +66,8 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
disabledLabel: true
|
||||
disabledLabel: true,
|
||||
countryNameIdMapping
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
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>
|
||||
@@ -39,8 +39,11 @@
|
||||
<div class="graph-list-item-block">
|
||||
<div class="graph-list-item padding-b-4">
|
||||
<div class="graph-list-item-label">{{ $t('overall.location') }}:</div>
|
||||
<div class="graph-list-item-value graph-list-item-value1" style="display: flex;align-items: center;">
|
||||
<!-- <img :src="require(`../../../../public/images/flag/${item.flag}.svg`)" class="graph-list-country-flag"/>-->
|
||||
<div class="graph-list-item-value graph-list-item-value1" style="display: flex;align-items: center;line-height: 14px;">
|
||||
<div style="line-height: 10px">
|
||||
<img v-if="getCountry(item.detail)===countryNameIdMapping.Unknown || !countryNameIdMapping[getCountry(item.detail)]" src="../../../../public/images/flag/Unknown.svg" class="graph-list-country-flag">
|
||||
<img v-else :src="require(`../../../../public/images/flag/${countryNameIdMapping[getCountry(item.detail)]}.png`)" class="graph-list-country-flag" >
|
||||
</div>
|
||||
<span>{{ $_.get(item.detail, 'location.city', '-') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,7 +108,7 @@
|
||||
|
||||
<script>
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { riskLevelMapping } from '@/utils/constants'
|
||||
import { riskLevelMapping, countryNameIdMapping } from '@/utils/constants'
|
||||
import { scrollToTop } from '@/utils/tools'
|
||||
import _ from 'lodash'
|
||||
|
||||
@@ -119,6 +122,11 @@ export default {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
countryNameIdMapping
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Loading
|
||||
},
|
||||
@@ -181,6 +189,9 @@ export default {
|
||||
expandList () {
|
||||
// 继续拓展ip列表,传递信息,调用接口
|
||||
this.$emit('expandList', _.get(this.node, 'id'))
|
||||
},
|
||||
getCountry (detail) {
|
||||
return this.$_.get(detail, 'location.country') || 'Unknown'
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<span class="row-item-label">{{ $t('entities.sentThroughput') }} : </span>
|
||||
<span class="row-item-value">
|
||||
{{
|
||||
unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !=='- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'
|
||||
valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') !=='- ' ? valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'
|
||||
}}
|
||||
</span>
|
||||
<!-- 曲线-->
|
||||
@@ -110,7 +110,7 @@
|
||||
<i class="cn-icon cn-icon-fall"></i>
|
||||
<span class="row-item-label">{{ $t('entities.receivedThroughput') }} : </span>
|
||||
<span class="row-item-value">
|
||||
{{ unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-' }}
|
||||
{{ valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-' }}
|
||||
</span>
|
||||
<div class="item-box-loading">
|
||||
<loading :loading="loading" size="small"></loading>
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
|
||||
<div class="row__content">
|
||||
{{unitConvert(entityData.max, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
{{valueToRangeValue(entityData.max, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
|
||||
<div class="row__content">
|
||||
{{unitConvert(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
{{valueToRangeValue(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
@@ -45,7 +45,7 @@
|
||||
<div class="row__contents">
|
||||
<div class="row__content">
|
||||
<div class="row__charts-msg">{{$t('overall.sent')}}:
|
||||
{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
{{valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
@@ -54,7 +54,7 @@
|
||||
</div>
|
||||
<div class="row__content row__content-accept">
|
||||
<div class="row__charts-msg">{{$t('overall.received')}}:
|
||||
{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
{{valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
@@ -212,7 +212,7 @@
|
||||
import { api } from '@/utils/api'
|
||||
import entityDetailMixin from './entityDetailMixin'
|
||||
import { unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import Chart from '@/views/charts/Chart'
|
||||
import _ from 'lodash'
|
||||
import { get } from '@/utils/http'
|
||||
@@ -372,7 +372,7 @@ export default {
|
||||
entityCopy: {
|
||||
..._.cloneDeep(props.entity)
|
||||
},
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
entityData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
|
||||
<div class="row__content">
|
||||
{{unitConvert(entityData.max, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
{{valueToRangeValue(entityData.max, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
|
||||
<div class="row__label row__label--width130">{{$t('overall.average')}}</div>
|
||||
<div class="row__content">
|
||||
{{unitConvert(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
{{valueToRangeValue(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
@@ -53,7 +53,7 @@
|
||||
<div class="row__contents">
|
||||
<div class="row__content">
|
||||
<div class="row__charts-msg">{{$t('overall.sent')}}:
|
||||
{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
{{valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
<div class="row__content row__content-accept">
|
||||
<div class="row__charts-msg">{{$t('overall.received')}}:
|
||||
{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
{{valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
@@ -216,7 +216,7 @@
|
||||
import { api } from '@/utils/api'
|
||||
import entityDetailMixin from './entityDetailMixin'
|
||||
import { unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import Chart from '@/views/charts/Chart'
|
||||
import _ from 'lodash'
|
||||
import { get } from '@/utils/http'
|
||||
@@ -397,7 +397,7 @@ export default {
|
||||
},
|
||||
entityCopy,
|
||||
unitTypes,
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
entityData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,16 @@
|
||||
<div class="overview__content">
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.location')}}</div>
|
||||
<div class="row__content">{{ipLocationRegion(entity.location)}}</div>
|
||||
<div class="row__content">
|
||||
<div v-if="entity.location" style="display: flex">
|
||||
<div v-if="entity.location.country">
|
||||
<img v-if="entity.location.country===countryNameIdMapping.Unknown || !countryNameIdMapping[entity.location.country]" src="../../../../../public/images/flag/Unknown.svg" class="filter-country-flag">
|
||||
<img v-else :src="require(`../../../../../public/images/flag/${countryNameIdMapping[entity.location.country]}.png`)" class="filter-country-flag" >
|
||||
</div>
|
||||
{{ipLocationRegion(entity.location)}}
|
||||
</div>
|
||||
<div v-else>-</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">ASN</div>
|
||||
@@ -44,7 +53,7 @@
|
||||
<div class="row__label row__label--width130">{{$t('overall.dnsServerInfo.queryRate')}}</div>
|
||||
<div class="row__contents">
|
||||
<div class="row__content">
|
||||
<div class="row__charts-msg">{{unitConvert(entityData.queryRate, unitTypes.byte).join(' ')}}ps</div>
|
||||
<div class="row__charts-msg">{{valueToRangeValue(entityData.queryRate, unitTypes.byte).join(' ')}}ps</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
<loading :loading="!loadingDns && loading" size="small"></loading>
|
||||
@@ -62,21 +71,21 @@
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
|
||||
<div class="row__content">
|
||||
{{unitConvert(entityData.max, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
{{valueToRangeValue(entityData.max, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
|
||||
<div class="row__label row__label--width130">{{$t('overall.average')}}</div>
|
||||
<div class="row__content">
|
||||
{{unitConvert(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
{{valueToRangeValue(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
|
||||
<div class="row__contents">
|
||||
<div class="row__content">
|
||||
<div class="row__charts-msg">{{$t('overall.sent')}}:
|
||||
{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
<div class="row__charts-msg">{{$t('overall.sent')}}:
|
||||
{{valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
@@ -84,8 +93,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row__content row__content-accept">
|
||||
<div class="row__charts-msg">{{$t('overall.received')}}:
|
||||
{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
<div class="row__charts-msg">{{$t('overall.received')}}:
|
||||
{{valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? valueToRangeValue(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
|
||||
</div>
|
||||
<!-- 曲线-->
|
||||
<div class="row__content-loading">
|
||||
@@ -101,7 +110,7 @@
|
||||
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
|
||||
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
|
||||
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
|
||||
Score:{{score}}
|
||||
Score: {{score}}
|
||||
</div>
|
||||
|
||||
<loading :loading="loadingNetworkQuality" size="small" style="left: 1rem;width: 50%;"></loading>
|
||||
@@ -240,7 +249,7 @@
|
||||
import entityDetailMixin from './entityDetailMixin'
|
||||
import { api } from '@/utils/api'
|
||||
import { unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import Chart from '@/views/charts/Chart'
|
||||
import _ from 'lodash'
|
||||
import { get } from '@/utils/http'
|
||||
@@ -248,6 +257,7 @@ import relatedServer from '@/mixins/relatedServer'
|
||||
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import axios from 'axios'
|
||||
import { countryNameIdMapping } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'Ip',
|
||||
@@ -345,12 +355,16 @@ export default {
|
||||
loadingAlert: false,
|
||||
loadingSecurityEvents: false,
|
||||
loadingMap: false,
|
||||
openPort: '-'
|
||||
openPort: '-',
|
||||
countryNameIdMapping
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ipLocationRegion () {
|
||||
return function (entityData) {
|
||||
if (!entityData) {
|
||||
return '-'
|
||||
}
|
||||
let result = ''
|
||||
if (entityData.country) {
|
||||
result += `${entityData.country},`
|
||||
@@ -465,7 +479,7 @@ export default {
|
||||
},
|
||||
entityCopy,
|
||||
unitTypes,
|
||||
unitConvert
|
||||
valueToRangeValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { get } from '@/utils/http'
|
||||
import * as echarts from 'echarts'
|
||||
import { entityListLineOption } from '@/views/charts/charts/chart-options'
|
||||
import { riskLevelMapping, unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { shallowRef, markRaw } from 'vue'
|
||||
import { metricOption } from '@/views/detections/options/detectionOptions'
|
||||
import { sortBy, reverseSortBy, computeScore } from '@/utils/tools'
|
||||
@@ -498,8 +497,7 @@ export default {
|
||||
},
|
||||
setup () {
|
||||
return {
|
||||
unitTypes,
|
||||
unitConvert
|
||||
unitTypes
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as echarts from 'echarts'
|
||||
import { entityListLineOption } from '@/views/charts/charts/chart-options'
|
||||
import { riskLevelMapping, unitTypes } from '@/utils/constants'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { shallowRef } from 'vue'
|
||||
|
||||
export default {
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
trafficUrl: '',
|
||||
chartOption: null,
|
||||
unitTypes,
|
||||
unitConvert,
|
||||
valueToRangeValue,
|
||||
echartsArray: []
|
||||
}
|
||||
},
|
||||
|
||||
@@ -88,9 +88,9 @@ describe('views/charts2/charts/entityDetail/EntityDetailLine.vue测试', () => {
|
||||
expect(titleNode0.text()).toEqual('network.total')
|
||||
expect(titleNode1.text()).toEqual('network.outbound')
|
||||
expect(titleNode2.text()).toEqual('network.other')
|
||||
expect(textNode0.text()).toEqual('0bps')
|
||||
expect(textNode0.text()).toEqual('<0.01bps')
|
||||
expect(textNode1.text()).toEqual('95.23Ebps')
|
||||
expect(textNode2.text()).toEqual('<1bps')
|
||||
expect(textNode2.text()).toEqual('0.01bps')
|
||||
resolve()
|
||||
}, 200))
|
||||
})
|
||||
|
||||
@@ -165,9 +165,9 @@ describe('views/charts2/charts/linkMonitor/LinkTrafficLine.vue测试', () => {
|
||||
const textNode2 = await wrapper.get('[test-id="tabContent2"]')
|
||||
|
||||
await new Promise(resolve => setTimeout(() => {
|
||||
expect(textNode0.text()).toEqual('0bps')
|
||||
expect(textNode1.text()).toEqual('0bps')
|
||||
expect(textNode2.text()).toEqual('0bps')
|
||||
expect(textNode0.text()).toEqual('<0.01bps')
|
||||
expect(textNode1.text()).toEqual('<0.01bps')
|
||||
expect(textNode2.text()).toEqual('<0.01bps')
|
||||
resolve()
|
||||
}, 200))
|
||||
})
|
||||
|
||||
@@ -420,7 +420,7 @@ describe('views/charts2/charts/networkOverview/NetworkOverviewApps.vue测试', (
|
||||
expect(testrate0.text()).toEqual('31.33 G')
|
||||
expect(testrate1.text()).toEqual('84.59 T')
|
||||
expect(testrate2.text()).toEqual('500.75 K')
|
||||
expect(testrate3.text()).toEqual('0')
|
||||
expect(testrate3.text()).toEqual('<0.01')
|
||||
expect(testrate4.text()).toEqual('-')
|
||||
|
||||
const testPercent0 = wrapper.get('[test-id="percent0"]')
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('views/charts2/charts/networkOverview/NetworkOverviewLine.vue测试', (
|
||||
expect(titleNode0.text()).toEqual('network.total')
|
||||
expect(titleNode1.text()).toEqual('network.outbound')
|
||||
expect(titleNode2.text()).toEqual('network.other')
|
||||
expect(textNode0.text()).toEqual('0bps')
|
||||
expect(textNode0.text()).toEqual('<0.01bps')
|
||||
expect(textNode1.text()).toEqual('95.23Ebps')
|
||||
expect(textNode2.text()).toEqual('0.01bps')
|
||||
resolve()
|
||||
|
||||
@@ -169,12 +169,12 @@ describe('views/charts2/charts/npm/NpmAppCategoryScore.vue测试', () => {
|
||||
const textNode4 = wrapper.find('[test-id="inbound-packet-multimedia-streaming"]')
|
||||
const textNode5 = wrapper.find('[test-id="inbound-packet-social-networking"]')
|
||||
|
||||
expect(textNode0.text()).toBe('0 bps')
|
||||
expect(textNode1.text()).toBe('0 bps')
|
||||
expect(textNode2.text()).toBe('0 bps')
|
||||
expect(textNode3.text()).toBe('0 bps')
|
||||
expect(textNode4.text()).toBe('0 bps')
|
||||
expect(textNode5.text()).toBe('0 bps')
|
||||
expect(textNode0.text()).toBe('<0.01 bps')
|
||||
expect(textNode1.text()).toBe('<0.01 bps')
|
||||
expect(textNode2.text()).toBe('<0.01 bps')
|
||||
expect(textNode3.text()).toBe('<0.01 bps')
|
||||
expect(textNode4.text()).toBe('<0.01 bps')
|
||||
expect(textNode5.text()).toBe('<0.01 bps')
|
||||
resolve()
|
||||
}, 200))
|
||||
})
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('views/charts2/charts/npm/NpmNetworkQuantity.vue测试', () => {
|
||||
|
||||
expect(p95Node0.text()).toEqual('P95:445 ms')
|
||||
expect(p95Node1.text()).toEqual('P95:2.95 s')
|
||||
expect(p95Node2.text()).toEqual('P95:0 ms')
|
||||
expect(p95Node2.text()).toEqual('P95:<1 ms')
|
||||
expect(p95Node3.text()).toEqual('P95:3.46 %')
|
||||
expect(p95Node4.text()).toEqual('P95:10.47 %')
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('views/charts2/charts/npm/NpmNetworkQuantity.vue测试', () => {
|
||||
|
||||
expect(p99Node0.text()).toEqual('P99:601 ms')
|
||||
expect(p99Node1.text()).toEqual('P99:4.62 s')
|
||||
expect(p99Node2.text()).toEqual('P99:0 ms')
|
||||
expect(p99Node2.text()).toEqual('P99:<1 ms')
|
||||
expect(p99Node3.text()).toEqual('P99:13.72 %')
|
||||
expect(p99Node4.text()).toEqual('P99:18.30 %')
|
||||
resolve()
|
||||
@@ -195,7 +195,7 @@ describe('views/charts2/charts/npm/NpmNetworkQuantity.vue测试', () => {
|
||||
|
||||
expect(p95Node0.text()).toEqual('P95:160 ms')
|
||||
expect(p95Node1.text()).toEqual('P95:266 ms')
|
||||
expect(p95Node2.text()).toEqual('P95:0 ms')
|
||||
expect(p95Node2.text()).toEqual('P95:<1 ms')
|
||||
expect(p95Node3.text()).toEqual('P95:2.23 %')
|
||||
expect(p95Node4.text()).toEqual('P95:6.60 %')
|
||||
|
||||
@@ -207,7 +207,7 @@ describe('views/charts2/charts/npm/NpmNetworkQuantity.vue测试', () => {
|
||||
|
||||
expect(p99Node0.text()).toEqual('P99:789 ms')
|
||||
expect(p99Node1.text()).toEqual('P99:2.09 s')
|
||||
expect(p99Node2.text()).toEqual('P99:0 ms')
|
||||
expect(p99Node2.text()).toEqual('P99:<1 ms')
|
||||
expect(p99Node3.text()).toEqual('P99:21.87 %')
|
||||
expect(p99Node4.text()).toEqual('P99:9.26 %')
|
||||
resolve()
|
||||
@@ -341,7 +341,7 @@ describe('views/charts2/charts/npm/NpmNetworkQuantity.vue测试', () => {
|
||||
const p95Node3 = wrapper.get('[test-id="singleValueP953"]')
|
||||
const p95Node4 = wrapper.get('[test-id="singleValueP954"]')
|
||||
|
||||
expect(p95Node0.text()).toEqual('P95:0 ms')
|
||||
expect(p95Node0.text()).toEqual('P95:<1 ms')
|
||||
expect(p95Node1.text()).toEqual('P95:4.92 m')
|
||||
expect(p95Node2.text()).toEqual('P95:10.00 s')
|
||||
expect(p95Node3.text()).toEqual('P95:3.46 %')
|
||||
@@ -353,7 +353,7 @@ describe('views/charts2/charts/npm/NpmNetworkQuantity.vue测试', () => {
|
||||
const p99Node3 = wrapper.get('[test-id="singleValueP993"]')
|
||||
const p99Node4 = wrapper.get('[test-id="singleValueP994"]')
|
||||
|
||||
expect(p99Node0.text()).toEqual('P99:0 ms')
|
||||
expect(p99Node0.text()).toEqual('P99:<1 ms')
|
||||
expect(p99Node1.text()).toEqual('P99:7.70 m')
|
||||
expect(p99Node2.text()).toEqual('P99:10.00 s')
|
||||
expect(p99Node3.text()).toEqual('P99:13.72 %')
|
||||
|
||||
Reference in New Issue
Block a user