Compare commits
122 Commits
23.09.1
...
dev-23.10-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77e5037a19 | ||
|
|
82035a20f5 | ||
|
|
a121857093 | ||
|
|
0152c46d05 | ||
|
|
64f376e22a | ||
|
|
5b89fca77c | ||
|
|
d1f5997b88 | ||
|
|
88002a8fc4 | ||
|
|
556a9b03b4 | ||
|
|
ca5c81d8be | ||
|
|
02779c26d1 | ||
|
|
20692705e9 | ||
|
|
bc3bc7eaf3 | ||
|
|
d05ae06af6 | ||
|
|
ca3d8766ba | ||
|
|
00e73adadb | ||
|
|
4ede5768e4 | ||
|
|
541692f50f | ||
|
|
89294c98e1 | ||
|
|
8fd283c1e7 | ||
|
|
90b90fdd3c | ||
|
|
72ee214877 | ||
|
|
99f97b76a7 | ||
|
|
b0260ea41a | ||
|
|
a7d6ffb4b4 | ||
|
|
dbc68077ca | ||
|
|
186e115adf | ||
|
|
6c5233a760 | ||
|
|
2df67c1972 | ||
|
|
cb3b66bdf7 | ||
|
|
6ba0ca0a82 | ||
|
|
00df0414c2 | ||
|
|
20a4bb22ef | ||
|
|
00bb6f8c5f | ||
|
|
e61c664802 | ||
|
|
a4dff135d1 | ||
|
|
0bd87db92f | ||
|
|
407d9ec667 | ||
|
|
e7637dd866 | ||
|
|
59c2b26d6c | ||
|
|
8b5b36a621 | ||
|
|
7762658864 | ||
|
|
eb25dc6aa7 | ||
|
|
1fff8f49c9 | ||
|
|
47d8212abe | ||
|
|
94940d745c | ||
|
|
030af380fd | ||
|
|
e9f21038b5 | ||
|
|
e355e427fb | ||
|
|
6dea1ee890 | ||
|
|
1fae281b8b | ||
|
|
60a527c765 | ||
|
|
3daa875a25 | ||
|
|
b4fcbd260b | ||
|
|
871781ab70 | ||
|
|
9bb9967353 | ||
|
|
327ae1956d | ||
|
|
4d4d197a80 | ||
|
|
46f1a880cd | ||
|
|
d1c9eba029 | ||
|
|
643ada9172 | ||
|
|
d0aa64fb9c | ||
|
|
5b7dd6da90 | ||
|
|
97d793471c | ||
|
|
6a2d5a0efe | ||
|
|
b90810470e | ||
|
|
d8865b74d7 | ||
|
|
012d873b71 | ||
|
|
fefca1588f | ||
|
|
f6af25e5e6 | ||
|
|
c5c7b58720 | ||
|
|
40d43acb6c | ||
|
|
7d34b8388c | ||
|
|
d2feedb319 | ||
|
|
0f52bd5362 | ||
|
|
48b3e2aebd | ||
|
|
9208a4fff2 | ||
|
|
19747cf326 | ||
|
|
c2de6c853c | ||
|
|
b0a7d44c42 | ||
|
|
3b641e1878 | ||
|
|
0a9d5591d5 | ||
|
|
0e82bebaac | ||
|
|
85db8cd745 | ||
|
|
c960c60790 | ||
|
|
18b62f1e3d | ||
|
|
11540d3c34 | ||
|
|
5fbab86a80 | ||
|
|
ed9a2c4e1f | ||
|
|
b94bc23a86 | ||
|
|
328f226a86 | ||
|
|
660950a73c | ||
|
|
8f9308a04e | ||
|
|
133c9a23c0 | ||
|
|
bc4a6c2a9e | ||
|
|
5798c0c5a7 | ||
|
|
15ac00d548 | ||
|
|
95217b84a5 | ||
|
|
24e549a444 | ||
|
|
e16d351efd | ||
|
|
ccec756baa | ||
|
|
a47e04fae2 | ||
|
|
8cc6edbdd4 | ||
|
|
afa86464d9 | ||
|
|
16e63ab905 | ||
|
|
6f72b2258f | ||
|
|
5a4301e3aa | ||
|
|
96c380001c | ||
|
|
40365decf6 | ||
|
|
ac9c16d464 | ||
|
|
7abe436127 | ||
|
|
d85e2d347f | ||
|
|
cc61cbc05c | ||
|
|
7cc2439dd2 | ||
|
|
8ba062054c | ||
|
|
cdb1d4baec | ||
|
|
1886f8214f | ||
|
|
aaaecfd9a1 | ||
|
|
9ab00df342 | ||
|
|
33293aaaa3 | ||
|
|
5cb105784a | ||
|
|
4e0e49585e |
@@ -39,9 +39,9 @@ build_project:
|
||||
stage: build_project
|
||||
script:
|
||||
- echo "npm install ..."
|
||||
- cnpm install --save-dev --unsafe-perm
|
||||
- npm install --save-dev --unsafe-perm
|
||||
- echo "npm run build"
|
||||
- cnpm run build
|
||||
- npm run build
|
||||
artifacts:
|
||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
|
||||
when: on_success
|
||||
|
||||
27006
package-lock.json
generated
27006
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
68
package.json
68
package.json
@@ -10,63 +10,64 @@
|
||||
"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amcharts/amcharts4": "^4.10.24",
|
||||
"@amcharts/amcharts4": "~4.10.0",
|
||||
"@amcharts/amcharts4-geodata": "^4.1.20",
|
||||
"@antv/g6": "^4.8.17",
|
||||
"axios": "^0.21.1",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"babel-plugin-lodash": "~3.3.0",
|
||||
"codemirror": "^5.65.1",
|
||||
"core-js": "^3.6.5",
|
||||
"core-js": "~3.31.0",
|
||||
"dayjs": "^1.10.5",
|
||||
"dexie": "^3.2.2",
|
||||
"dexie": "~3.2.0",
|
||||
"echarts": "^5.1.1",
|
||||
"element-plus": "^1.0.2-beta.44",
|
||||
"element-plus": "~1.0.2-beta.71",
|
||||
"lib-flexible": "^0.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"node-sass": "^4.14.1",
|
||||
"postcss-plugin-px2rem": "^0.8.1",
|
||||
"node-sass": "~4.14.0",
|
||||
"postcss-plugin-px2rem": "~0.8.1",
|
||||
"postcss-px2rem-exclude": "0.0.6",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sass-resources-loader": "^2.2.1",
|
||||
"sass-loader": "~8.0.2",
|
||||
"sass-resources-loader": "~2.2.5",
|
||||
"tiny-emitter": "^2.1.0",
|
||||
"vue": "^3.0.0",
|
||||
"vue": "~3.3.0",
|
||||
"vue-grid-layout": "^3.0.0-beta1",
|
||||
"vue-i18n": "^9.1.6",
|
||||
"vue-router": "^4.0.8",
|
||||
"vuex": "^4.0.1"
|
||||
"vue-i18n": "~9.2.0",
|
||||
"vue-router": "~4.2.0",
|
||||
"vuex": "~4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.12.1",
|
||||
"@babel/core": "^7.11.4",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/plugin-proposal-private-methods": "^7.12.1",
|
||||
"@babel/plugin-transform-runtime": "^7.12.1",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.12.1",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@babel/preset-typescript": "^7.10.4",
|
||||
"@babel/cli": "~7.22.0",
|
||||
"@babel/core": "~7.22.5",
|
||||
"@babel/plugin-proposal-class-properties": "~7.18.0",
|
||||
"@babel/plugin-proposal-private-methods": "~7.18.0",
|
||||
"@babel/plugin-transform-runtime": "~7.22.0",
|
||||
"@babel/plugin-proposal-private-property-in-object": "~7.21.0",
|
||||
"@babel/preset-env": "~7.22.0",
|
||||
"@babel/preset-typescript": "~7.22.0",
|
||||
"@commitlint/cli": "^9.1.2",
|
||||
"@commitlint/config-conventional": "^9.1.2",
|
||||
"@highlightjs/vue-plugin": "^2.0.1",
|
||||
"@rollup/plugin-commonjs": "^15.1.0",
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
"@rollup/plugin-typescript": "^6.0.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@testing-library/vue": "^6.4.2",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@testing-library/jest-dom": "~5.16.0",
|
||||
"@testing-library/user-event": "~14.4.0",
|
||||
"@testing-library/vue": "~6.6.0",
|
||||
"@types/jest": "~26.0.0",
|
||||
"@types/lodash": "^4.14.161",
|
||||
"@typescript-eslint/eslint-plugin": "^3.10.1",
|
||||
"@typescript-eslint/parser": "^3.10.1",
|
||||
"@vue/babel-plugin-jsx": "^1.0.0",
|
||||
"@vue/babel-preset-app": "^5.0.8",
|
||||
"@vue/babel-plugin-jsx": "~1.1.0",
|
||||
"@vue/babel-preset-app": "~5.0.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"@vue/component-compiler-utils": "^3.2.0",
|
||||
"@vue/test-utils": "^2.2.7",
|
||||
"@vue/compiler-sfc": "~3.3.0",
|
||||
"@vue/component-compiler-utils": "~3.3.0",
|
||||
"@vue/test-utils": "~2.4.0",
|
||||
"@vue/server-renderer": "~3.3.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.0.0",
|
||||
"compression-webpack-plugin": "^8.0.1",
|
||||
@@ -77,11 +78,10 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"eslint-plugin-vue": "^7.7.0",
|
||||
"jest": "^26.0.0",
|
||||
"ts-jest": "^26.4.4",
|
||||
"jest": "~26.6.0",
|
||||
"ts-jest": "~26.5.0",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"vue-jest": "^5.0.0-alpha.10",
|
||||
"vue3-ace-editor": "^2.0.2"
|
||||
"vue-jest": "^5.0.0-alpha.10"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const BASE_CONFIG = {
|
||||
baseUrl: 'http://192.168.44.54:8091/',
|
||||
version: '23.06',
|
||||
baseUrl: 'http://192.168.44.54:8090/',
|
||||
version: '23.10',
|
||||
apiVersion: 'v1'
|
||||
}
|
||||
|
||||
1
public/geojson/frenchPolynesiaLow.json
Normal file
1
public/geojson/frenchPolynesiaLow.json
Normal file
File diff suppressed because one or more lines are too long
1
public/geojson/lesothoLow.json
Normal file
1
public/geojson/lesothoLow.json
Normal file
File diff suppressed because one or more lines are too long
1
public/geojson/liberiaLow.json
Normal file
1
public/geojson/liberiaLow.json
Normal file
File diff suppressed because one or more lines are too long
11
public/geojson/mauritiusLow.json
Normal file
11
public/geojson/mauritiusLow.json
Normal file
File diff suppressed because one or more lines are too long
BIN
public/images/knowledge-base-logo/cyber-ghost.png
Normal file
BIN
public/images/knowledge-base-logo/cyber-ghost.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
public/images/knowledge-base-logo/hospot-vpn.png
Normal file
BIN
public/images/knowledge-base-logo/hospot-vpn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -83,6 +83,71 @@
|
||||
|
||||
.entity-detail-event-block {
|
||||
width: calc(100% - 2px);
|
||||
.behavior-pattern {
|
||||
height:100% ;
|
||||
position: relative;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
.behavior-pattern-legend {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding:10px 18px 10px 18px;
|
||||
width:400px;
|
||||
.behavior-pattern-legend__item {
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
font-size: 12px;
|
||||
color: #353636;
|
||||
line-height: 12px;
|
||||
margin-bottom:11px;
|
||||
.legend-icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin: 3px 8px 0 0;
|
||||
border-radius: 1px;;
|
||||
}
|
||||
.legend-name {
|
||||
width:180px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.legend-value{
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
margin-left:20px;
|
||||
width:90px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.legend-percent {
|
||||
margin-left:20px;
|
||||
width:70px;
|
||||
justify-content: left;
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
.behavior-pattern-chart{
|
||||
height: calc(100% - 50px);
|
||||
width:calc(100% - 400px);
|
||||
position: relative
|
||||
}
|
||||
.chart-bottom-dot__left {
|
||||
position: absolute;
|
||||
height: 0;
|
||||
width: 195px;
|
||||
border-bottom: 1px dashed #d3d3d3;
|
||||
top: 319px;left: 575px;
|
||||
}
|
||||
.chart-bottom-dot__right {
|
||||
position: absolute;
|
||||
height: 0;
|
||||
width: 195px;
|
||||
border-bottom: 1px dashed #d3d3d3;
|
||||
top: 319px;left: 830px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.entity-detail-event-error {
|
||||
|
||||
@@ -128,24 +128,25 @@ $blue: #046ECA;
|
||||
}
|
||||
}
|
||||
}
|
||||
.link-statistical-dimension {
|
||||
.row-dot {
|
||||
margin-top: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.row-dot {
|
||||
margin-top: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.green-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #749F4D;
|
||||
}
|
||||
|
||||
.green-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #749F4D;
|
||||
}
|
||||
|
||||
.red-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #E26154;
|
||||
.red-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #E26154;
|
||||
}
|
||||
}
|
||||
|
||||
.item-popover-up, .item-popover-down {
|
||||
|
||||
@@ -31,8 +31,12 @@
|
||||
line-height: 16px;
|
||||
margin-right: 10px;
|
||||
|
||||
.block-mode-icon1 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.block-mode-icon {
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +212,15 @@
|
||||
font-weight: 500;
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
.btn1 {
|
||||
margin-right: 10px;
|
||||
.el-button {
|
||||
background: #F5F6F7;
|
||||
border: 1px solid rgba(215,215,215,1);
|
||||
color: #353636;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-setting__btn1 {
|
||||
|
||||
@@ -101,6 +101,12 @@
|
||||
justify-content: flex-start;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
|
||||
.policy-form-item {
|
||||
.el-form-item__error {
|
||||
width: 260px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--mini .el-input__inner {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
background-color: rgba(0, 0, 0, 0.16) !important;
|
||||
}
|
||||
|
||||
.detection-drawer-title, .basic-function-value, basic-description-value {
|
||||
.detection-drawer-title, .basic-function-value, basic-description-value, .drawer-trigger-minutes {
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 14px;
|
||||
color: #717171;
|
||||
@@ -58,6 +58,10 @@
|
||||
color: #046ECA;
|
||||
}
|
||||
|
||||
.drawer-trigger-minutes {
|
||||
color: #353636;
|
||||
}
|
||||
|
||||
.detection-drawer-collapse {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid rgba(226, 229, 236, 1);
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
width: 5px;
|
||||
height: 20px;
|
||||
border-radius: 12px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.cn-detection__row {
|
||||
@@ -88,6 +89,7 @@
|
||||
margin-left: 12px;
|
||||
font-size: xx-small;
|
||||
font-weight: bold;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.circle {
|
||||
@@ -95,7 +97,7 @@
|
||||
height: 10px;
|
||||
border: 2px solid #da5656;
|
||||
border-radius: 10px;
|
||||
margin-top: 4px;
|
||||
margin-top: 1px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
@@ -119,10 +121,12 @@
|
||||
margin-right: 12px;
|
||||
}
|
||||
.detection-event-severity-block {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
color: #046EC9;
|
||||
font-weight: 500;
|
||||
padding: 2px 10px;
|
||||
padding: 0 10px;
|
||||
background: rgba(56,172,210,0.10);
|
||||
border: 1px solid #ADC7DB;
|
||||
box-shadow: 0 2px 4px 0 rgba(51,51,51,0.02);
|
||||
@@ -262,3 +266,8 @@
|
||||
color: #FFD82D !important;
|
||||
}
|
||||
}
|
||||
|
||||
.detection-list-icon {
|
||||
margin-top: 1px;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
@@ -60,12 +60,14 @@
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.row__content {
|
||||
.row__content, .row__content1 {
|
||||
display: flex;
|
||||
//color: #3976CB;
|
||||
color: #046ECA;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.row__content--link {
|
||||
font-style: italic;
|
||||
@@ -87,6 +89,21 @@
|
||||
font-style: italic;
|
||||
color: #1890FF;
|
||||
}
|
||||
|
||||
.row__content__icon {
|
||||
font-size: 14px;
|
||||
margin-right: 5px;
|
||||
color: #1890FF;
|
||||
}
|
||||
|
||||
.row__content__svg {
|
||||
font-size: 14px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
.row__content1 {
|
||||
display: block;
|
||||
padding-right: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,13 +232,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.row__tag {
|
||||
.row__tag, .row__tag__level {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
//color: white;
|
||||
}
|
||||
.row__tag__level {
|
||||
height: 16px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.performance-event-remark {
|
||||
font-family: NotoSansSChineseRegular;
|
||||
|
||||
@@ -61,22 +61,19 @@
|
||||
|
||||
.detection-tag-status0 {
|
||||
font-weight: 500;
|
||||
font-family: NotoSansHans-Medium;
|
||||
background: rgba(113, 113, 113, 0.12);
|
||||
color: #717171;
|
||||
padding: 0 12px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.detection-tag-status1 {
|
||||
font-weight: 500;
|
||||
font-family: NotoSansHans-Medium;
|
||||
background: rgba(126, 159, 84, 0.12);
|
||||
color: #7E9F54;
|
||||
padding: 0 8px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.detection-table-library {
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 12px;
|
||||
color: #046ECA;
|
||||
font-weight: 400;
|
||||
|
||||
@@ -4,6 +4,18 @@
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.detections {
|
||||
.entity__pagination{
|
||||
bottom: 9px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
.detections__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.detection__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -349,6 +349,24 @@
|
||||
.data-score-green {
|
||||
background: #749F4D;
|
||||
}
|
||||
.score-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: #CBCBCB;
|
||||
margin-right: 4px;
|
||||
|
||||
&.score-dot--red {
|
||||
background-color: #E26154;
|
||||
}
|
||||
&.score-dot--yellow {
|
||||
background-color: #E5A219;
|
||||
}
|
||||
&.score-dot--green {
|
||||
background-color: #749F4D;
|
||||
}
|
||||
}
|
||||
height:24px;
|
||||
font-size: 14px;
|
||||
color: #046ECA;
|
||||
|
||||
@@ -174,6 +174,25 @@
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
.score-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: #CBCBCB;
|
||||
margin-right: 4px;
|
||||
|
||||
&.score-dot--red {
|
||||
background-color: #E26154;
|
||||
}
|
||||
&.score-dot--yellow {
|
||||
background-color: #E5A219;
|
||||
}
|
||||
&.score-dot--green {
|
||||
background-color: #749F4D;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "cn-icon"; /* Project id 2614877 */
|
||||
src: url('iconfont.woff2?t=1693386443164') format('woff2'),
|
||||
url('iconfont.woff?t=1693386443164') format('woff'),
|
||||
url('iconfont.ttf?t=1693386443164') format('truetype');
|
||||
src: url('iconfont.woff2?t=1698229141457') format('woff2'),
|
||||
url('iconfont.woff?t=1698229141457') format('woff'),
|
||||
url('iconfont.ttf?t=1698229141457') format('truetype');
|
||||
}
|
||||
|
||||
.cn-icon {
|
||||
@@ -13,6 +13,18 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.cn-icon-indicator-match:before {
|
||||
content: "\e80c";
|
||||
}
|
||||
|
||||
.cn-icon-threshold:before {
|
||||
content: "\e80d";
|
||||
}
|
||||
|
||||
.cn-icon-behavior:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
.cn-icon-category-group:before {
|
||||
content: "\e6c7";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -624,6 +624,11 @@ export default {
|
||||
const parser = new Parser(this.columnList)
|
||||
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
|
||||
q = decodeURI(q)
|
||||
} else {
|
||||
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
if (q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
}
|
||||
this.metaList = parser.parseStr(q).metaList
|
||||
}
|
||||
|
||||
@@ -225,6 +225,11 @@ export default {
|
||||
if (q) {
|
||||
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
|
||||
q = decodeURI(q)
|
||||
} else {
|
||||
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
if (q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
}
|
||||
// 为避免地址栏任意输入导致全查询的q带QUERY,解析时不识别导致的语法错误
|
||||
// 如地址栏输入116.178.222.171,此时的q很长,刷新界面时需要把q里的116.178.222.171拿出来进行搜索
|
||||
|
||||
@@ -6,9 +6,10 @@ import { ElMessage } from 'element-plus'
|
||||
import i18n from '@/i18n'
|
||||
|
||||
const strReg = {
|
||||
all: /^[\da-zA-Z\s.'><!=-_(),%]$/,
|
||||
// 需要不限制语言,正则过滤中英日俄语出错实现语言都通过。留个记录观察,后续校验
|
||||
all: /^[\da-zA-Z\u4E00-\u9FA5\u3040-\u309F\u0800-\u4e00\u0400-\u04FF\u2000-\u206F\s.'><!=-_(),%]$/,
|
||||
key: /^(?![\d])[\da-zA-Z\s.'-_]$/,
|
||||
value: /^[\da-zA-Z\s.'-_%]$/
|
||||
value: /^[\da-zA-Z\u4E00-\u9FA5\u3040-\u309F\u0800-\u4e00\u0400-\u04FF\u2000-\u206F\s.'-_%]$/
|
||||
}
|
||||
const operatorList = ['=', ' in ', ' IN ', ' like ', ' LIKE ', 'HAS(', 'has(']
|
||||
|
||||
@@ -527,7 +528,9 @@ export default class Parser {
|
||||
}
|
||||
// 在单引号里,又在括号里,则遇到逗号、右括号就报错
|
||||
if (isInBracket && isInApostrophe) {
|
||||
if ([',', ')'].indexOf(strArr[j]) > -1) {
|
||||
// if ([',', ')'].indexOf(strArr[j]) > -1) {
|
||||
// 目前有ip in ('1.1.1.1,2.2.2.2')该情况,后续待验证
|
||||
if ([')'].indexOf(strArr[j]) > -1) {
|
||||
errorList.push(new ParserError(j, errorTypes.syntaxError, errorDesc.syntaxError.unclosedApostrophe))
|
||||
break
|
||||
}
|
||||
@@ -709,6 +712,13 @@ export default class Parser {
|
||||
meta.column.label = token.value
|
||||
meta.column.type = columnType.fullText
|
||||
}
|
||||
} else if (nextToken.type === types.rightBracket) {
|
||||
// 此处操作为ip='1,2' and has(tag,'222')中 ,'222')的情况
|
||||
if (prevToken) {
|
||||
if (prevToken.type === types.comma && token.type === types.commonStr && nextToken.type === types.rightBracket) {
|
||||
meta.value.value = token.value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorList.push(new ParserError(token.end, errorTypes.syntaxError, errorDesc.syntaxError.unexpectedString))
|
||||
break
|
||||
@@ -1163,7 +1173,9 @@ export default class Parser {
|
||||
const arr = []
|
||||
// 如果出现this.columnList中的字段,如IP\Domain\App\Country等,则不进行模糊搜索,将str返回出去
|
||||
this.columnList.forEach(item => {
|
||||
arr.push(item.label.toLowerCase())
|
||||
// todo 取消了大小写校验,后续观察是否出现问题
|
||||
// arr.push(item.label.toLowerCase())
|
||||
arr.push(item.label)
|
||||
})
|
||||
|
||||
// 因为手动输入时可能会输入and,所以将操作符的AND转换为and,统一处理
|
||||
@@ -1173,7 +1185,9 @@ export default class Parser {
|
||||
newStr = newStr.replace(new RegExp(arr[i], 'g'), arr[i])
|
||||
}
|
||||
// 检查str字段在arr中是否出现,true为出现过
|
||||
const result = arr.some(item => newStr.toLowerCase().includes(item))
|
||||
// todo 取消了大小写校验,后续观察是否出现问题
|
||||
// const result = arr.some(item => newStr.toLowerCase().includes(item))
|
||||
const result = arr.some(item => newStr.includes(item))
|
||||
if (newStr.indexOf(' and ') > -1) {
|
||||
// 将单引号包裹的and拿出来放到数组tempList里,原来的单引号包裹内容用temp即'it is test keyword{键值}'代替
|
||||
// 再将字符串用and转换为数组,遍历数组,发现值为temp的,获取键值,根据键值获取tempList的值组合起来,
|
||||
@@ -1184,7 +1198,7 @@ export default class Parser {
|
||||
|
||||
// 将单引号包裹的and内容集合起来
|
||||
while ((match = regex.exec(newStr)) !== null) {
|
||||
if (match[1].includes('and')) {
|
||||
if (match[1].includes(' and ')) {
|
||||
tempList.push(match[1])
|
||||
}
|
||||
}
|
||||
@@ -1224,7 +1238,8 @@ export default class Parser {
|
||||
// 不区分大小写,用this.columnList里的label
|
||||
arr.forEach(item => {
|
||||
if (str.toLowerCase().indexOf(item.toLowerCase()) > -1) {
|
||||
str = str.replace(new RegExp(item, 'gi'), item)
|
||||
// todo 记录一下此处取消了转小写转换,后续搜索验证
|
||||
// str = str.replace(new RegExp(item, 'gi'), item)
|
||||
if (!operatorList.some(ite => str.includes(ite)) && str.toLowerCase() !== item.toLowerCase()) {
|
||||
str = this.checkFormatByStr(str)
|
||||
}
|
||||
|
||||
@@ -254,12 +254,14 @@ export default {
|
||||
const returnValue = () => {
|
||||
store.commit('setTimeFilter', { startTime: myStartTime.value, endTime: myEndTime.value, range: dateRangeValue.value })
|
||||
cancelHttp()
|
||||
const obj = rangeHistory.value.find(d => d.start === myStartTime.value && d.end === myEndTime.value)
|
||||
if (!obj) {
|
||||
rangeHistory.value.unshift({
|
||||
start: myStartTime.value,
|
||||
end: myEndTime.value
|
||||
})
|
||||
if (rangeHistory.value[0]) {
|
||||
const d = rangeHistory.value[0]
|
||||
if (d.start !== myStartTime.value || d.end !== myEndTime.value) {
|
||||
rangeHistory.value.unshift({
|
||||
start: myStartTime.value,
|
||||
end: myEndTime.value
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!rangeHistory.value[0]) {
|
||||
rangeHistory.value.unshift({
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="index===2">
|
||||
<span v-if="route===wholeScreenRouterMapping.dns">{{ $t(item.value) }}</span>
|
||||
<span v-if="route===wholeScreenRouterMapping.dns || !route.startsWith('/panel')">{{ $t(item.value) }}</span>
|
||||
<span v-else class="route-menu" @click="jump(route,item.value,'',3)">{{ $t(item.value) }}</span>
|
||||
</template>
|
||||
<!-- index=0和index=1的点击跳转由breadcrumb里的数据控制 -->
|
||||
@@ -392,7 +392,7 @@ export default {
|
||||
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
|
||||
}
|
||||
}
|
||||
let path = this.$route.path;
|
||||
const path = this.$route.path
|
||||
if (path.indexOf('panel') > -1 && path.indexOf('linkMonitor') === -1) {
|
||||
await this.initDropdownList()
|
||||
}
|
||||
@@ -455,22 +455,6 @@ export default {
|
||||
type: entityGraphMenu.type
|
||||
})
|
||||
return true
|
||||
} else if (this.route === '/detectionsNew') {
|
||||
const detectionMenu = menus.find(m => m.route === '/detection')
|
||||
const policyMenu = menus.find(m => m.route === '/detectionsNew')
|
||||
breadcrumb.push({
|
||||
code: detectionMenu.code,
|
||||
value: detectionMenu.i18n ? this.$t(detectionMenu.i18n) : detectionMenu.name,
|
||||
route: detectionMenu.route,
|
||||
type: detectionMenu.type
|
||||
})
|
||||
breadcrumb.push({
|
||||
code: policyMenu.code,
|
||||
value: policyMenu.i18n ? this.$t(policyMenu.i18n) : policyMenu.name,
|
||||
route: policyMenu.route,
|
||||
type: policyMenu.type
|
||||
})
|
||||
return true
|
||||
}
|
||||
const menu = menus.find(m => m.route === this.route)
|
||||
if (menu) {
|
||||
@@ -786,6 +770,7 @@ export default {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
return
|
||||
} else if (opeType === 3) {
|
||||
this.$router.push({
|
||||
query: {
|
||||
@@ -794,6 +779,7 @@ export default {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
return
|
||||
} else if (opeType !== 4) {
|
||||
this.$router.push({
|
||||
query: {
|
||||
@@ -803,6 +789,7 @@ export default {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
if (route === this.route) {
|
||||
this.refresh()
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
<template>
|
||||
<div class="right-box right-box-user">
|
||||
<div class="right-box__header">
|
||||
<div class="header__title">{{editObject.id ? $t('config.chart.edit') : $t('config.chart.add')}}</div>
|
||||
<div class="header__operation">
|
||||
<span v-cancel="{object: editObject, func: esc}"><i class="cn-icon cn-icon-close"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-box__container">
|
||||
<div class="container__form">
|
||||
<el-form ref="chartForm" :model="editObject" :rules="editObject.id ? rules2 : rules" label-position="top" label-width="120px">
|
||||
<!--name-->
|
||||
<el-form-item :label="$t('overall.name')" prop="name">
|
||||
<el-input id="chart-input-name" v-model="editObject.name" :disabled="editObject.name==='admin' && editObject.id === 1"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--i18n-->
|
||||
<el-form-item :label="$t('config.chart.i18n')" prop="i18n">
|
||||
<el-input id="chart-input-i18n" v-model="editObject.i18n"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--type-->
|
||||
<el-form-item :label="$t('config.chart.type')" prop="type">
|
||||
<el-select id="chart-type"
|
||||
v-model="editObject.type"
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small"
|
||||
@change="()=>{ this.$forceUpdate() }">
|
||||
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<span style="float: left">{{ item.label }}</span><span style="float: right;color: #909399;font-size: 13px;">{{ item.remark }}</span>
|
||||
</el-option>
|
||||
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--panel-->
|
||||
<el-form-item :label="$t('config.chart.panel')" prop="panelId">
|
||||
<el-select id="chart-input-panelId"
|
||||
v-model="editObject.panelId"
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
:disabled="editObject.id?true:false"
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small"
|
||||
@change="getChartData">
|
||||
|
||||
<el-option :key="panelTypeAndRouteMapping.trafficSummary" :label="$t('trafficSummary.trafficSummary')" :value="panelTypeAndRouteMapping.trafficSummary"></el-option>
|
||||
<el-option :key="panelTypeAndRouteMapping.networkAppPerformance" :label="$t('networkAppPerformance.networkAppPerformance')" :value="panelTypeAndRouteMapping.networkAppPerformance"></el-option>
|
||||
<el-option :key="panelTypeAndRouteMapping.cryptocurrency" :label="$t('overall.cryptocurrency')" :value="panelTypeAndRouteMapping.cryptocurrency"></el-option>
|
||||
<el-option :key="panelTypeAndRouteMapping.dnsServiceInsights" :label="$t('dnsServiceInsights.dnsServiceInsights')" :value="panelTypeAndRouteMapping.dnsServiceInsights"></el-option>
|
||||
<el-option :key="panelTypeAndRouteMapping.ipEntityDetail" :label="$t('entities.ipEntityDetail')" :value="panelTypeAndRouteMapping.ipEntityDetail"></el-option>
|
||||
<el-option :key="panelTypeAndRouteMapping.domainEntityDetail" :label="$t('entities.domainEntityDetail')" :value="panelTypeAndRouteMapping.domainEntityDetail"></el-option>
|
||||
<el-option :key="panelTypeAndRouteMapping.appEntityDetail" :label="$t('entities.appEntityDetail')" :value="panelTypeAndRouteMapping.appEntityDetail"></el-option>
|
||||
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--pid-->
|
||||
<el-form-item :label="$t('config.chart.pid')" prop="pid">
|
||||
<el-select id="chart-pid"
|
||||
v-model="editObject.pid"
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
:disabled="editObject.id?true:false"
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small"
|
||||
@change="()=>{ this.$forceUpdate() }">
|
||||
|
||||
<template v-for="chart in chartData" :key="chart.id">
|
||||
<el-option :label="chart.name" :value="chart.id"></el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--x-->
|
||||
<el-form-item :label="$t('config.chart.x')" prop="x">
|
||||
<el-input id="chart-input-x" v-model="editObject.x"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--y-->
|
||||
<el-form-item :label="$t('config.chart.y')" prop="y">
|
||||
<el-input id="chart-input-y" v-model="editObject.y"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--w-->
|
||||
<el-form-item :label="$t('config.chart.w')" prop="w">
|
||||
<el-input id="chart-input-x" v-model="editObject.w"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--h-->
|
||||
<el-form-item :label="$t('config.chart.h')" prop="h">
|
||||
<el-input id="chart-input-y" v-model="editObject.h"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!--params-->
|
||||
<el-form-item :label="$t('config.chart.params')" prop="params">
|
||||
<v-ace-editor
|
||||
v-model:value="editObject.params"
|
||||
lang="json"
|
||||
theme="chrome"
|
||||
style="height: 300px" />
|
||||
</el-form-item>
|
||||
<!--remark-->
|
||||
<el-form-item :label="$t('config.chart.remark')">
|
||||
<el-input maxlength="1024" show-word-limit :rows="2" size='mini' type="textarea" v-model="editObject.remark" id="chart-box-remark"/>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-box__footer">
|
||||
<button id="chart-edit-cancel" v-cancel="{object: editObject, func: esc}" class="footer__btn footer__btn--light">
|
||||
<span>{{$t('overall.cancel')}}</span>
|
||||
</button>
|
||||
<button id="chart-edit-save" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
|
||||
<span>{{$t('overall.save')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import rightBoxMixin from '@/mixins/right-box'
|
||||
import axios from 'axios'
|
||||
import { panelTypeAndRouteMapping, storageKey } from '@/utils/constants'
|
||||
import { api } from '@/utils/api'
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
import 'ace-builds/src-noconflict/mode-javascript'
|
||||
import 'ace-builds/src-noconflict/mode-json'
|
||||
import 'ace-builds/src-noconflict/theme-chrome'
|
||||
|
||||
export default {
|
||||
name: 'ChartBox',
|
||||
mixins: [rightBoxMixin],
|
||||
components: {
|
||||
VAceEditor
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
url: api.chart,
|
||||
loginName: localStorage.getItem(storageKey.username),
|
||||
panelTypeAndRouteMapping: panelTypeAndRouteMapping,
|
||||
rules: { // 表单校验规则
|
||||
name: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
panel_id: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
x: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
y: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
w: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
h: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
rules2: { // 表单校验规则
|
||||
name: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
panel_id: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
x: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
y: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
w: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
],
|
||||
h: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
chartData: [],
|
||||
options: [
|
||||
{
|
||||
value: 1,
|
||||
label: 'map-1',
|
||||
remark: '流量流向地图-连线'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: 'map-2',
|
||||
remark: '地图-色块'
|
||||
}, {
|
||||
value: 3,
|
||||
label: 'map-3',
|
||||
remark: '地图-点'
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: 'map-4',
|
||||
remark: '地图-点'
|
||||
}, {
|
||||
value: 11,
|
||||
label: 'line-1',
|
||||
remark: '折线图-常规'
|
||||
},
|
||||
{
|
||||
value: 61,
|
||||
label: 'table-1',
|
||||
remark: '表格'
|
||||
}, {
|
||||
value: 62,
|
||||
label: 'table-2',
|
||||
remark: 'DNS记录'
|
||||
}, {
|
||||
value: 63,
|
||||
label: 'table-3',
|
||||
remark: '挖矿活跃ip'
|
||||
}, {
|
||||
value: 31,
|
||||
label: 'pie-1',
|
||||
remark: '饼图-带联动表格'
|
||||
}, {
|
||||
value: 51,
|
||||
label: 'singleValue-1',
|
||||
remark: '单值图'
|
||||
},
|
||||
{
|
||||
value: 91,
|
||||
label: 'tab-container',
|
||||
remark: 'tab标签(容器)'
|
||||
}, {
|
||||
value: 92,
|
||||
label: 'tab-item',
|
||||
remark: 'tab标签(标签页)'
|
||||
},
|
||||
{
|
||||
value: 93,
|
||||
label: 'title',
|
||||
remark: '标题'
|
||||
}, {
|
||||
value: 94,
|
||||
label: 'group',
|
||||
remark: '组'
|
||||
},
|
||||
{
|
||||
value: 12,
|
||||
label: 'line-2',
|
||||
remark: '折线图-带统计'
|
||||
}, {
|
||||
value: 52,
|
||||
label: 'singleValue-2',
|
||||
remark: '详情单值图'
|
||||
},
|
||||
{
|
||||
value: 53,
|
||||
label: 'singleValue-3',
|
||||
remark: '单值图'
|
||||
}, {
|
||||
value: 13,
|
||||
label: 'line-3',
|
||||
remark: '折线图-堆叠面积'
|
||||
},
|
||||
{
|
||||
value: 21,
|
||||
label: 'bar-1',
|
||||
remark: '柱状图'
|
||||
}, {
|
||||
value: 22,
|
||||
label: 'bar-2',
|
||||
remark: '开放端口'
|
||||
}, {
|
||||
value: 23,
|
||||
label: 'bar-3',
|
||||
remark: '挖矿事件统计(time类型柱状图)'
|
||||
}, {
|
||||
value: 24,
|
||||
label: 'bar-4',
|
||||
remark: '矿机所属单位(category类型柱状图)'
|
||||
},
|
||||
{
|
||||
value: 32,
|
||||
label: 'pie-2',
|
||||
remark: '饼图-常规'
|
||||
}, {
|
||||
value: 33,
|
||||
label: 'pie-3',
|
||||
remark: '托管域名'
|
||||
},
|
||||
{
|
||||
value: 34,
|
||||
label: 'pie-4',
|
||||
remark: '相关域名'
|
||||
}, {
|
||||
value: 42,
|
||||
label: 'relation-2',
|
||||
remark: '关系图谱'
|
||||
},
|
||||
{
|
||||
value: 43,
|
||||
label: 'relation-3',
|
||||
remark: '访问链路图'
|
||||
},
|
||||
{
|
||||
value: 82,
|
||||
label: 'base-2',
|
||||
remark: 'APP基本信息'
|
||||
}, {
|
||||
value: 83,
|
||||
label: 'list-1',
|
||||
remark: 'Whois'
|
||||
},
|
||||
{
|
||||
value: 84,
|
||||
label: 'list-2',
|
||||
remark: 'DNS记录'
|
||||
},
|
||||
{
|
||||
value: 85,
|
||||
label: 'list-3',
|
||||
remark: '近期挖矿事件'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
},
|
||||
mounted () {
|
||||
if (this.editObject.id) {
|
||||
this.getChartData(this.editObject.panelId)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'editObject.panelId': function (newValue, oldValue) {
|
||||
this.editObject.pid = ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isCurrentUser (username) {
|
||||
return localStorage.getItem(storageKey.username) === username
|
||||
},
|
||||
/* 密码失去焦点 检验确认密码 */
|
||||
pinBlur () {
|
||||
if (this.editObject.pin && this.editObject.pinChange) {
|
||||
this.$refs.chartForm.validateField('pinChange')
|
||||
}
|
||||
},
|
||||
save () {
|
||||
if (this.blockOperation.save) { return }
|
||||
this.blockOperation.save = true
|
||||
|
||||
this.$refs.chartForm.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.editObject.id) {
|
||||
axios.put(this.url, this.editObject).then(res => {
|
||||
this.blockOperation.save = false
|
||||
if (res.status === 200) {
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
|
||||
this.esc(true)
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
axios.post(this.url, this.editObject).then(res => {
|
||||
this.blockOperation.save = false
|
||||
if (res.status === 200) {
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
|
||||
this.esc(true)
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.blockOperation.save = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
async getChartData (value) {
|
||||
await axios.get(api.chart, { params: { panelId: value } }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.chartData = response.data.data.list
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,117 +0,0 @@
|
||||
<template>
|
||||
<div class="right-box">
|
||||
<div class="right-box__header">
|
||||
<div class="header__title">{{editObject.id ? $t('overall.edit') : $t('overall.new')}}</div>
|
||||
<div class="header__operation">
|
||||
<span v-cancel="{object: editObject, func: esc}"><i class="cn-icon cn-icon-close"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-box__container">
|
||||
<div class="container__form">
|
||||
<el-form ref="form" :model="editObject" :rules="rules" label-position="top" label-width="120px">
|
||||
<!--name-->
|
||||
<el-form-item :label="$t('overall.name')" prop="name">
|
||||
<el-input id="proxy-name" v-model="editObject.name"
|
||||
maxlength="64" placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--path-->
|
||||
<el-form-item :label="$t('overall.path')" prop="path">
|
||||
<el-input id="proxy-path" v-model="editObject.path"
|
||||
placeholder="" show-word-limit size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--method-->
|
||||
<el-form-item :label="$t('overall.method')" prop="method">
|
||||
<el-select id="proxy-method"
|
||||
v-model="editObject.method"
|
||||
placeholder=" "
|
||||
size="small"
|
||||
class="right-box__select"
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
>
|
||||
<el-option value="get"></el-option>
|
||||
<el-option value="post"></el-option>
|
||||
<el-option value="put"></el-option>
|
||||
<el-option value="fetch"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--version-->
|
||||
<el-form-item :label="$t('overall.version')" prop="version">
|
||||
<el-input id="proxy-version" v-model="editObject.version" placeholder="" size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--target-->
|
||||
<el-form-item :label="$t('galaxyProxy.targetUrl')" prop="targetUrl">
|
||||
<el-input id="proxy-targetUrl" v-model="editObject.targetUrl" placeholder="" size="small" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!--target param-->
|
||||
<el-form-item :label="$t('galaxyProxy.targetParam')" prop="targetParam">
|
||||
<!-- <prism-editor class="my-editor" v-model="editObject.targetParam" :highlight="jsonHl" line-numbers></prism-editor>-->
|
||||
<v-ace-editor
|
||||
v-model:value="editObject.targetParam"
|
||||
lang="json"
|
||||
theme="chrome"
|
||||
style="height: 300px" />
|
||||
</el-form-item>
|
||||
<!--target header-->
|
||||
<el-form-item :label="$t('galaxyProxy.targetHeader')" prop="targetHeader">
|
||||
<v-ace-editor
|
||||
v-model:value="editObject.targetHeader"
|
||||
lang="json"
|
||||
theme="chrome"
|
||||
style="height: 300px" />
|
||||
</el-form-item>
|
||||
<!--pre handle-->
|
||||
<el-form-item label="Pre handle" prop="preHandle">
|
||||
<v-ace-editor
|
||||
v-model:value="editObject.preHandle"
|
||||
lang="javascript"
|
||||
theme="chrome"
|
||||
style="height: 300px" />
|
||||
</el-form-item>
|
||||
<!--post handle-->
|
||||
<el-form-item label="Post handle" prop="postHandle">
|
||||
<v-ace-editor
|
||||
v-model:value="editObject.postHandle"
|
||||
lang="javascript"
|
||||
theme="chrome"
|
||||
style="height: 300px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-box__footer">
|
||||
<button id="asset-edit-cancel" v-cancel="{object: editObject, func: esc}" class="footer__btn footer__btn--light">
|
||||
<span>{{$t('overall.cancel')}}</span>
|
||||
</button>
|
||||
<button id="asset-edit-save" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
|
||||
<span>{{$t('overall.save')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import rightBoxMixin from '@/mixins/right-box'
|
||||
import { api } from '@/utils/api'
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
import 'ace-builds/src-noconflict/mode-javascript'
|
||||
import 'ace-builds/src-noconflict/mode-json'
|
||||
import 'ace-builds/src-noconflict/theme-chrome'
|
||||
|
||||
export default {
|
||||
name: 'GalaxyProxyBox',
|
||||
mixins: [rightBoxMixin],
|
||||
components: {
|
||||
VAceEditor
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
url: api.galaxyProxy,
|
||||
rules: { // 表单校验规则
|
||||
name: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -26,7 +26,7 @@
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small"
|
||||
@change="()=>{ this.$forceUpdate() }">
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small"
|
||||
@change="()=>{ this.$forceUpdate() }">
|
||||
@@ -61,7 +61,7 @@
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small">
|
||||
<template v-for="lang in langData" :key="lang.value">
|
||||
@@ -70,20 +70,20 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--theme-->
|
||||
<el-form-item :label="$t('config.user.theme')" prop="i18n">
|
||||
<!-- <el-form-item :label="$t('config.user.theme')" prop="i18n">
|
||||
<el-select id="account-input-roleIds"
|
||||
v-model="editObject.theme"
|
||||
class="right-box__select"
|
||||
clearable
|
||||
collapse-tags
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="right-box-select-dropdown prevent-clickoutside"
|
||||
size="small">
|
||||
<template v-for="theme in themeData" :key="theme.value">
|
||||
<el-option :label="theme.label" :value="theme.value"></el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>-->
|
||||
<!--enable-->
|
||||
<el-form-item :label="$t('config.user.enable')">
|
||||
<el-switch
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="show"
|
||||
:top="top"
|
||||
:modal="modal"
|
||||
:custom-class="customClass"
|
||||
:show-close="showClose"
|
||||
:width="width"
|
||||
@close="closeDebug"
|
||||
@open="openDebug"
|
||||
destroy-on-close
|
||||
>
|
||||
|
||||
<div class="debug-wrapper">
|
||||
<el-select v-model="name" filterable placeholder="Select" class="item item-1" allow-create
|
||||
@change="changPath">
|
||||
<template v-for="(proxy,index) in galaxyProxyData" :key="proxy.id">
|
||||
<el-option :label="proxy.name" :value="index"></el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
<div class="item item-2 sub-grid-send">
|
||||
<el-select v-model="method" filterable placeholder="Select" style="width:100px;" >
|
||||
<el-option
|
||||
v-for="item in methodOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-input v-model="fullPathWithParams" placeholder="Please input" readonly/>
|
||||
<el-button @click="send">{{$t('galaxyProxy.debug.send')}}</el-button>
|
||||
</div>
|
||||
|
||||
<div class="item item-3 sub-grid-params">
|
||||
<div class="sub-grid-params-add">
|
||||
<span style="padding-left: 15px;">{{$t('galaxyProxy.debug.requestParams')}}</span>
|
||||
<el-button size="mini" @click="handleAddDetails"><i class="cn-icon-add cn-icon"></i></el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="paramsTableData"
|
||||
width="100%"
|
||||
@selection-change="handleDetailSelectionChange"
|
||||
:row-class-name="rowClassName"
|
||||
empty-text=""
|
||||
ref="tb">
|
||||
|
||||
<el-table-column type="selection" align="center" label="" width="30px" style="border-left:0px;" />
|
||||
|
||||
<el-table-column prop="key" align="center" width="80px" >
|
||||
<template #header>
|
||||
<div class="table-operation-title">{{$t('galaxyProxy.debug.key')}}</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input @change="paramsChange(scope.row)" v-model="scope.row.key"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="value" align="center" >
|
||||
<template #header>
|
||||
<div class="table-operation-title">{{$t('galaxyProxy.debug.value')}}</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input @change="paramsChange(scope.row)" v-model="scope.row.value"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" style="border-right:0px;" width="70px">
|
||||
<template #header>
|
||||
<div class="table-operation-title">{{$t('overall.option')}}</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div @click="deleteRow(scope.$index)" class="debug__params-delete">{{$t('overall.delete')}}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-slot:empty>
|
||||
<div style="height:0px;"></div>
|
||||
</template>
|
||||
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
<div class="item item-4">
|
||||
<div class="request-header" >{{$t('galaxyProxy.debug.request.header')}}</div>
|
||||
<div class="request-header-content" ><pre v-html="proxyRequestHeader" style="height: 98%;width: 98%;margin-left: -15px;margin-top: -10px;"></pre></div>
|
||||
<div class="response-header">{{$t('galaxyProxy.debug.response.header')}}</div>
|
||||
<div class="response-body">{{$t('galaxyProxy.debug.response.body')}}</div>
|
||||
<div class="response-header-content"><pre v-html="proxyResponseHeader" style="height: 98%;width: 98%;margin-left: -15px;margin-top: -10px;"></pre></div>
|
||||
<div class="response-body-content color-pre__style">
|
||||
<v-ace-editor
|
||||
v-model:value="proxyResponseBody"
|
||||
lang="json"
|
||||
readonly="true"
|
||||
theme="chrome"
|
||||
style="height: 100%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/utils/api'
|
||||
import { getForDebug, postForDebug } from '@/utils/http'
|
||||
import axios from 'axios'
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
import 'ace-builds/src-noconflict/mode-javascript'
|
||||
import 'ace-builds/src-noconflict/mode-json'
|
||||
import 'ace-builds/src-noconflict/theme-chrome'
|
||||
|
||||
export default {
|
||||
name: 'GalaxyProxyDebug',
|
||||
components: {
|
||||
VAceEditor
|
||||
},
|
||||
props: {
|
||||
showDebug: Boolean,
|
||||
top: {
|
||||
type: String,
|
||||
default: '5vh'
|
||||
},
|
||||
modal: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
customClass: {
|
||||
type: String,
|
||||
default: 'proxy-debug__dialog'
|
||||
},
|
||||
showClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
width: { // 高度需要通过customClass自行定义
|
||||
type: [String, Number],
|
||||
default: '90vw'
|
||||
},
|
||||
curGalaxyProxy: Object // 实体,{ name: '', path: '', icon: icon-class }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
show: false,
|
||||
galaxyProxyData: [],
|
||||
name: '',
|
||||
paramsTableData: [
|
||||
|
||||
],
|
||||
// 选中的数据
|
||||
checkedDetail: {},
|
||||
checkedRowData: [],
|
||||
methodOptions: [
|
||||
{
|
||||
value: 'GET',
|
||||
label: 'GET'
|
||||
},
|
||||
{
|
||||
value: 'POST',
|
||||
label: 'POST'
|
||||
}
|
||||
],
|
||||
method: 'GET',
|
||||
path: '', // 不带参数的路径,用于请求URL
|
||||
fullPathWithParams: '', // 带参数的完整路径,用于界面显示
|
||||
proxyResponseBody: '',
|
||||
proxyRequestHeader: '',
|
||||
proxyResponseHeader: ''
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleAddDetails () {
|
||||
if (this.paramsTableData == undefined) {
|
||||
this.paramsTableData = new Array()
|
||||
}
|
||||
const obj = {}
|
||||
obj.key = ''
|
||||
obj.value = ''
|
||||
|
||||
this.paramsTableData.push(obj)
|
||||
},
|
||||
|
||||
// 删除当前行
|
||||
deleteRow (index) {
|
||||
this.paramsTableData.splice(index, 1)
|
||||
this.updateUrlParams()
|
||||
},
|
||||
|
||||
rowClassName ({ row, rowIndex }) {
|
||||
// 把每一行的索引放进row
|
||||
row.index = rowIndex
|
||||
},
|
||||
|
||||
// 参数发送改变时
|
||||
paramsChange (row) {
|
||||
this.fullPathWithParams = this.path
|
||||
this.checkedDetail = {}
|
||||
this.checkedRowData.forEach(t => {
|
||||
if (t.key != '') {
|
||||
this.checkedDetail[t.key] = t.value
|
||||
}
|
||||
})
|
||||
if (this.fullPathWithParams != '') {
|
||||
this.updateUrlParams()
|
||||
}
|
||||
},
|
||||
|
||||
updateUrlParams () {
|
||||
this.fullPathWithParams = this.path
|
||||
let params = ''
|
||||
for (const key in this.checkedDetail) {
|
||||
if (key != '') {
|
||||
params = params + key + '=' + this.checkedDetail[key] + '&'
|
||||
}
|
||||
}
|
||||
if (params.endsWith('&')) {
|
||||
params = params.substring(0, params.length - 1)
|
||||
}
|
||||
if (params != '') {
|
||||
this.fullPathWithParams = this.fullPathWithParams + '?' + params
|
||||
}
|
||||
},
|
||||
|
||||
// 单选框选中数据
|
||||
handleDetailSelectionChange (selection) {
|
||||
this.checkedDetail = {}
|
||||
this.checkedRowData = selection
|
||||
selection.forEach((item, index) => {
|
||||
this.checkedDetail[item.key] = item.value
|
||||
})
|
||||
this.updateUrlParams()
|
||||
},
|
||||
|
||||
async getGalaxyProxyData (value) {
|
||||
await axios.get(api.galaxyProxy + '?pageSize=-1').then(response => {
|
||||
if (response.status === 200) {
|
||||
this.galaxyProxyData = response.data.data.list
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
syntaxUrl (json) {
|
||||
if (typeof json != 'string') {
|
||||
json = JSON.stringify(json, undefined, 2)
|
||||
}
|
||||
return json = json.replace(/,/g, '&').replace(/{/g, '').replace(/}/g, '').replace(/"/g, '').replace(/:/g, '=').replace(/ /g, '')
|
||||
},
|
||||
|
||||
changPath (index) {
|
||||
// 修改input的值
|
||||
this.path = axios.defaults.baseURL + 'interface' + this.galaxyProxyData[index].path
|
||||
this.updateUrlParams()
|
||||
this.method = this.galaxyProxyData[index].method.toUpperCase()
|
||||
},
|
||||
|
||||
send () {
|
||||
// this.path = "galaxy/setting?pageNo=1&pageSize=1"
|
||||
// 注意有GET与POST两种方式
|
||||
if (this.method.toUpperCase() == 'GET') {
|
||||
getForDebug(this.path, this.checkedDetail).then(response => {
|
||||
this.proxyResponseBody = this.syntaxJson(JSON.parse(JSON.stringify(response.data)))
|
||||
this.proxyResponseHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.headers)))
|
||||
this.proxyRequestHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.config.headers)))
|
||||
this.proxyResponseHeader = this.proxyResponseHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
|
||||
this.proxyRequestHeader = this.proxyRequestHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
|
||||
})
|
||||
} else if (this.method.toUpperCase() == 'POST') {
|
||||
postForDebug(this.path, this.checkedDetail).then(response => {
|
||||
if (response) {
|
||||
this.proxyResponseBody = this.syntaxJson(JSON.parse(JSON.stringify(response.data)))
|
||||
this.proxyResponseHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.headers)))
|
||||
this.proxyRequestHeader = this.syntaxJson(JSON.parse(JSON.stringify(response.config.headers)))
|
||||
|
||||
this.proxyResponseHeader = this.proxyResponseHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
|
||||
this.proxyRequestHeader = this.proxyRequestHeader.replace(/{/g, '').replace(/}/g, '').replace(/"/g, '')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
// 格式化json
|
||||
syntaxJson (json) {
|
||||
if (typeof json != 'string') {
|
||||
json = JSON.stringify(json, undefined, 2)
|
||||
}
|
||||
return json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
},
|
||||
closeDebug () {
|
||||
this.paramsTableData = []// JSON.parse(JSON.stringify(paramsTableData))
|
||||
this.proxyResponseBody = ''
|
||||
this.proxyResponseHeader = ''
|
||||
this.proxyRequestHeader = ''
|
||||
this.name = ''
|
||||
this.path = ''
|
||||
this.fullPathWithParams = ''
|
||||
for (const key in this.curGalaxyProxy) {
|
||||
delete this.curGalaxyProxy[key]
|
||||
}
|
||||
},
|
||||
openDebug () {
|
||||
if (this.curGalaxyProxy.path) {
|
||||
this.name = this.curGalaxyProxy.name
|
||||
this.path = axios.defaults.baseURL + 'interface' + this.curGalaxyProxy.path
|
||||
this.fullPathWithParams = this.path
|
||||
this.method = this.curGalaxyProxy.method.toUpperCase()
|
||||
} else {
|
||||
this.name = ''
|
||||
this.path = ''
|
||||
this.method = 'GET'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 获取代理名称及路径
|
||||
this.getGalaxyProxyData()
|
||||
},
|
||||
watch: {
|
||||
showDebug (n, o) {
|
||||
this.show = n
|
||||
},
|
||||
show (n, o) {
|
||||
this.$emit('update:show-debug', n)
|
||||
},
|
||||
path (n, o) {
|
||||
this.updateUrlParams()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -4,56 +4,60 @@
|
||||
<div class="block-mode">
|
||||
<!--todo 图标没有,后期换-->
|
||||
<div class="block-mode-left">
|
||||
<i class="cn-icon cn-icon-setting2 block-mode-icon"></i>
|
||||
<i class="cn-icon cn-icon-indicator-match block-mode-icon1"></i>
|
||||
</div>
|
||||
|
||||
<div class="block-mode-right">
|
||||
<div class="block-mode-title">Indicator Match</div>
|
||||
<div class="block-mode-right" style="position:relative;">
|
||||
<div class="block-mode-title">{{ $t('detection.policy.indicatorMatch') }}</div>
|
||||
<div class="block-mode-content">
|
||||
Use indicators from intelligencesources to detect matchingevents and alerts.
|
||||
{{ $t('detection.policy.indicatorMatchIntroduce') }}
|
||||
<div v-if="language==='cn'" style="color: rgba(0,0,0,0)">0</div>
|
||||
</div>
|
||||
<div :class="settingObj.ruleType===detectionRuleType.indicator?'block-mode-btn-active':'block-mode-btn'"
|
||||
@click="selectMode(detectionRuleType.indicator)">select
|
||||
@click="selectMode(detectionRuleType.indicator)">{{ $t('overall.select') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block-mode">
|
||||
<!--todo 图标没有,后期换-->
|
||||
<div class="block-mode" style="cursor: no-drop;">
|
||||
<div class="block-mode-left">
|
||||
<i class="cn-icon cn-icon-setting2 block-mode-icon"></i>
|
||||
<i class="cn-icon cn-icon-threshold block-mode-icon"></i>
|
||||
</div>
|
||||
|
||||
<div class="block-mode-right">
|
||||
<div class="block-mode-title">Threshold</div>
|
||||
<div class="block-mode-title">{{ $t('detection.policy.threshold') }}</div>
|
||||
<div class="block-mode-content">
|
||||
Aggregate query results to detect when number of matches exceeds threshold.
|
||||
{{ $t('detection.policy.thresholdIntroduce') }}
|
||||
</div>
|
||||
<div :class="settingObj.ruleType===detectionRuleType.threshold?'block-mode-btn-active':'block-mode-btn'"
|
||||
@click="selectMode(detectionRuleType.threshold)">select
|
||||
<!--todo 当前版本暂时不可选择-->
|
||||
<div style="cursor: no-drop;" :class="settingObj.ruleType===detectionRuleType.threshold?'block-mode-btn-active':'block-mode-btn'">
|
||||
{{ $t('overall.select') }}
|
||||
</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-form-item :label="$t('overall.category')" prop="category" class="form-setting__block margin-b-20">
|
||||
<el-select :disabled="settingObj.ruleId" 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"
|
||||
:label="$t(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-form-item :label="$t('overall.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"
|
||||
v-for="item in eventTypeList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
@@ -62,32 +66,32 @@
|
||||
</el-form-item>
|
||||
|
||||
<!--name-->
|
||||
<el-form-item label="Name" prop="name" class="form-setting__block margin-b-20">
|
||||
<el-form-item :label="$t('overall.name')" prop="name" class="form-setting__block margin-b-20">
|
||||
<el-input
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
placeholder=""
|
||||
v-model="settingObj.name"
|
||||
@input="changeEditFlag"
|
||||
@blur="changeEditFlag"
|
||||
class="form-setting__input" />
|
||||
</el-form-item>
|
||||
|
||||
<!--Description-->
|
||||
<div class="form-setting__block margin-b-20">
|
||||
<div class="block-title">Description</div>
|
||||
<div class="block-title">{{ $t('config.dataSet.description') }}</div>
|
||||
<el-input
|
||||
maxlength="255"
|
||||
show-word-limit
|
||||
v-model="settingObj.description"
|
||||
type="textarea"
|
||||
resize='none'
|
||||
@input="changeEditFlag"
|
||||
@blur="changeEditFlag"
|
||||
class="form-setting__textarea" />
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="form-setting__block margin-b-20">
|
||||
<div class="block-title">Policy Status</div>
|
||||
<div class="block-title">{{ $t('detection.create.policyStatus') }}</div>
|
||||
<el-switch
|
||||
v-model="settingObj.status"
|
||||
@change="changeEditFlag"
|
||||
@@ -95,30 +99,36 @@
|
||||
inactive-color="#C0CEDB"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:active-text="switchStatus(settingObj.status)"/>
|
||||
:active-text="$t(switchStatus(settingObj.status))"/>
|
||||
</div>
|
||||
|
||||
<div class="form-setting__btn">
|
||||
<el-button @click="onContinue">Continue</el-button>
|
||||
<el-button @click="onContinue">{{ $t('detection.create.continue') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { detectionRuleType } from '@/utils/constants'
|
||||
import { detectionRuleType, storageKey, detectionUnitList } from '@/utils/constants'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'GeneralSettings',
|
||||
props: {
|
||||
editObj: {
|
||||
type: Object
|
||||
},
|
||||
isComplete: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
detectionRuleType,
|
||||
categoryList: [],
|
||||
typeList: [],
|
||||
eventTypeList: [],
|
||||
settingObj: {
|
||||
ruleType: detectionRuleType.threshold,
|
||||
ruleType: detectionRuleType.indicator,
|
||||
category: '',
|
||||
eventType: '',
|
||||
name: '',
|
||||
@@ -149,6 +159,21 @@ export default {
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
language: 'en'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
editObj (newVal) {
|
||||
if (newVal.ruleId) {
|
||||
this.settingObj = JSON.parse(JSON.stringify({ ...newVal }))
|
||||
this.settingObj.editFlag = false
|
||||
this.settingObj.saveFlag = true
|
||||
}
|
||||
},
|
||||
isComplete (newVal) {
|
||||
if (!newVal) {
|
||||
this.onContinue()
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -158,15 +183,9 @@ export default {
|
||||
methods: {
|
||||
switchStatus,
|
||||
initData () {
|
||||
axios.get(api.detection.statistics, { params: { pageSize: -1 } }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.categoryList = response.data.data.categoryList || []
|
||||
this.typeList = response.data.data.typeList || []
|
||||
} else {
|
||||
console.error(response.data)
|
||||
}
|
||||
}).finally(() => {
|
||||
})
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
this.categoryList = detectionUnitList.categoryList
|
||||
this.eventTypeList = detectionUnitList.eventTypeList
|
||||
},
|
||||
selectMode (ruleType) {
|
||||
this.settingObj.ruleType = ruleType
|
||||
@@ -174,9 +193,16 @@ export default {
|
||||
},
|
||||
changeEditFlag () {
|
||||
this.settingObj.editFlag = true
|
||||
if (this.settingObj.ruleType && this.settingObj.category && this.settingObj.eventType && this.settingObj.name) {
|
||||
this.settingObj.settingNoContinue = true
|
||||
this.onContinue()
|
||||
}
|
||||
},
|
||||
/** 点击继续,进行第二步 */
|
||||
onContinue () {
|
||||
onContinue (e) {
|
||||
if (e) {
|
||||
delete this.settingObj.settingNoContinue
|
||||
}
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
this.settingObj.editFlag = false
|
||||
@@ -188,7 +214,3 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
<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>
|
||||
<i class="cn-icon cn-icon-search key-search-icon"></i>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
@@ -24,7 +23,7 @@
|
||||
<div class="key-table">
|
||||
<loading :loading="loading"></loading>
|
||||
|
||||
<el-table :data="tableData" style="width: 100%" @row-click="rowClick">
|
||||
<el-table :data="tableData" style="width: 100%" :row-class-name="tableRowClassName" @row-click="rowClick">
|
||||
<el-table-column
|
||||
v-for="(item, index) in tableTitle"
|
||||
:key="`col-${index}`"
|
||||
@@ -73,6 +72,9 @@ export default {
|
||||
showDrawer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
delKeyId: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -116,6 +118,16 @@ export default {
|
||||
this.myDrawer = this.showDrawer
|
||||
this.getTopKeysData()
|
||||
},
|
||||
watch: {
|
||||
delKeyId (newVal) {
|
||||
if (newVal) {
|
||||
const obj = this.tableData.find(d => d.keyId === newVal)
|
||||
if (obj) {
|
||||
obj.filterKey = false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
dateFormatByAppearance,
|
||||
@@ -150,6 +162,7 @@ export default {
|
||||
},
|
||||
/** 单击topKeys弹窗某一项 */
|
||||
rowClick (data) {
|
||||
data.filterKey = true
|
||||
this.$emit('keyRowClick', data)
|
||||
},
|
||||
onRefresh () {
|
||||
@@ -161,6 +174,11 @@ export default {
|
||||
httpError (e) {
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
},
|
||||
tableRowClassName (row) {
|
||||
if (row.row.filterKey) {
|
||||
return 'key-click-row'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,4 +249,8 @@ export default {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table .key-click-row {
|
||||
background: #F5F7FA !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<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-form-item :label="$t('config.user.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"
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<!--Dimensions-->
|
||||
<div class="form-setting__block margin-b-20">
|
||||
<div class="block-title">Dimensions</div>
|
||||
<div class="block-title">{{ $t('detection.create.dimensions') }}</div>
|
||||
<div class="block-dimension">
|
||||
<div class="block-dimension-tag" v-for="(ite, ind) in dimensionList" :key="ind">{{ ite.label }}</div>
|
||||
</div>
|
||||
@@ -52,7 +52,7 @@
|
||||
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>
|
||||
<i class="cn-icon cn-icon-close" @click="delFilterItem(index, item)"></i>
|
||||
</div>
|
||||
|
||||
<div style="height: 10px;"></div>
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
<!--Condition模块-->
|
||||
<div class="form-setting__block margin-b-20">
|
||||
<div class="block-title">Condition</div>
|
||||
<div class="block-title">{{ $t('detection.create.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">
|
||||
@@ -97,7 +97,7 @@
|
||||
v-for="item in metricList"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:value="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -144,7 +144,7 @@
|
||||
</el-form>
|
||||
|
||||
<div class="condition-add" @click="addCondition">
|
||||
<i class="cn-icon cn-icon-add"></i>Add Condition
|
||||
<i class="cn-icon cn-icon-add"></i>{{ $t('detection.create.addCondition') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,6 +154,7 @@
|
||||
<history-top-keys
|
||||
v-if="showDrawer"
|
||||
:showDrawer="showDrawer"
|
||||
:delKeyId="delKeyId"
|
||||
@closeDrawer="onCloseDrawer"
|
||||
@keyRowClick="getRowClick"
|
||||
></history-top-keys>
|
||||
@@ -163,24 +164,24 @@
|
||||
<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-form-item :label="$t('config.user.source')" prop="dataSource" class="form-setting__block margin-b-20">
|
||||
<el-select v-model="indicatorRuleObj.dataSource" class="form-setting__select" placeholder=" " size="mini" @change="handleParamsComplete">
|
||||
<el-option
|
||||
v-for="item in sourceList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:label="$t(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-form-item :label="$t('detection.library')" prop="knowledgeId" class="form-setting__block margin-b-20">
|
||||
<el-select v-model="indicatorRuleObj.knowledgeId" class="form-setting__select" filterable placeholder=" " size="mini" @change="handleParamsComplete">
|
||||
<el-option
|
||||
v-for="item in libraryList"
|
||||
:key="item.knowledgeId"
|
||||
:label="item.label"
|
||||
:label="item.name"
|
||||
:value="item.knowledgeId"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -188,7 +189,7 @@
|
||||
|
||||
<!--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">
|
||||
<el-select v-model="indicatorRuleObj.level" class="condition__select form-setting__select" placeholder=" " size="mini" @change="handleParamsComplete">
|
||||
<template #prefix>
|
||||
<div
|
||||
class="condition__select__icon"
|
||||
@@ -198,7 +199,7 @@
|
||||
<el-option
|
||||
v-for="item in levelList"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
:label="$t(item.label)"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -207,22 +208,29 @@
|
||||
</div>
|
||||
|
||||
<div class="form-setting__btn">
|
||||
<el-button @click="onContinue">Continue</el-button>
|
||||
<el-button @click="onContinue">{{ $t('detection.create.continue') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import HistoryTopKeys from '@/components/table/detection/HistoryTopKeys'
|
||||
import { eventSeverityColor, detectionRuleType } from '@/utils/constants'
|
||||
import { eventSeverityColor, detectionRuleType, detectionUnitList, securityLevel } from '@/utils/constants'
|
||||
import axios from 'axios'
|
||||
import _ from 'lodash'
|
||||
import { api } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'RuleDefinition',
|
||||
props: {
|
||||
settingObj: {
|
||||
type: Object
|
||||
},
|
||||
editObj: {
|
||||
type: Object
|
||||
},
|
||||
isComplete: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -232,11 +240,31 @@ export default {
|
||||
settingObj: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler (newVal, oldVal) {
|
||||
handler (newVal) {
|
||||
if (!newVal.editFlag && newVal.saveFlag) {
|
||||
this.mySettingObj = JSON.parse(JSON.stringify(newVal))
|
||||
}
|
||||
}
|
||||
},
|
||||
editObj: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler (newVal) {
|
||||
if (newVal) {
|
||||
if (newVal.ruleType === detectionRuleType.indicator) {
|
||||
this.indicatorRuleObj = this.$_.cloneDeep(newVal.ruleConfigObj)
|
||||
this.indicatorRuleObj.knowledgeId = newVal.ruleConfigObj.knowledgeBase.knowledgeId
|
||||
}
|
||||
if (newVal.ruleType === detectionRuleType.threshold) {
|
||||
this.thresholdRuleObj = this.$_.cloneDeep(newVal.ruleConfigObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
isComplete (newVal) {
|
||||
if (!newVal) {
|
||||
this.onContinue()
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@@ -244,7 +272,7 @@ export default {
|
||||
eventSeverityColor,
|
||||
detectionRuleType,
|
||||
mySettingObj: {
|
||||
ruleType: detectionRuleType.threshold
|
||||
ruleType: detectionRuleType.indicator
|
||||
},
|
||||
dimensionList: [], // Dimensions数据
|
||||
// ruleType为Indicator时表单数据
|
||||
@@ -327,7 +355,8 @@ export default {
|
||||
than: '>',
|
||||
less: '<',
|
||||
equal: '='
|
||||
}
|
||||
},
|
||||
delKeyId: '' // 删除的keyId
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -341,18 +370,31 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
axios.get(api.detection.statistics, { params: { pageSize: -1 } }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.sourceList = response.data.data.sourceList || []
|
||||
this.levelList = response.data.data.levelList || []
|
||||
this.conditionList = response.data.data.conditionList || []
|
||||
this.metricList = response.data.data.metricList || []
|
||||
this.libraryList = response.data.data.libraryList || []
|
||||
} else {
|
||||
console.error(response.data)
|
||||
}
|
||||
}).finally(() => {
|
||||
})
|
||||
this.sourceList = detectionUnitList.sourceList || []
|
||||
this.levelList = securityLevel || []
|
||||
// threshold模式还没确定,所以数据暂时静态数据,后续根据需要修改
|
||||
this.conditionList = detectionUnitList.conditionList || []
|
||||
this.metricList = detectionUnitList.metricList || []
|
||||
|
||||
if (this.mySettingObj.ruleType === this.detectionRuleType.indicator) {
|
||||
axios.get(api.knowledgeBaseList, { params: { pageSize: -1 } }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.libraryList = _.get(response, 'data.data.list', []).filter(l => l.isBuiltIn === 0)
|
||||
} else {
|
||||
console.error(response.data.message)
|
||||
this.libraryList = []
|
||||
if (response.data.message) {
|
||||
this.$message.error(response.data.message)
|
||||
} else {
|
||||
this.$message.error(this.$t('tip.somethingWentWrong'))
|
||||
}
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.libraryList = []
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
}
|
||||
},
|
||||
/** 单击History Top Keys列表某行,filter添加数据 */
|
||||
getRowClick (data) {
|
||||
@@ -364,6 +406,7 @@ export default {
|
||||
},
|
||||
/** filter模块点击add按钮 */
|
||||
addFilter (data) {
|
||||
this.delKeyId = ''
|
||||
this.showFilter = true
|
||||
if (this.selectList.length === 0) {
|
||||
this.getFilterList()
|
||||
@@ -445,11 +488,15 @@ export default {
|
||||
]
|
||||
},
|
||||
/** 删除filter某一项 */
|
||||
delFilterItem (i) {
|
||||
delFilterItem (i, item) {
|
||||
this.delKeyId = item.keyId
|
||||
this.thresholdRuleObj.filterList.splice(i, 1)
|
||||
},
|
||||
/** 点击继续,展开第三步 */
|
||||
onContinue () {
|
||||
onContinue (e) {
|
||||
if (e) {
|
||||
delete this.indicatorRuleObj.ruleNoContinue
|
||||
}
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
if (this.mySettingObj.ruleType === detectionRuleType.indicator) {
|
||||
@@ -482,6 +529,12 @@ export default {
|
||||
obj[item.level] = str
|
||||
})
|
||||
this.thresholdRuleObj.conditions = obj
|
||||
},
|
||||
handleParamsComplete () {
|
||||
if (this.indicatorRuleObj.dataSource && this.indicatorRuleObj.knowledgeId && this.indicatorRuleObj.level) {
|
||||
this.indicatorRuleObj.ruleNoContinue = true
|
||||
this.onContinue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,11 @@
|
||||
>
|
||||
</el-switch>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'color'">
|
||||
<div class="knowledge-color">
|
||||
<span class="knowledge-color__icon" :class="colorName(scope.row[item.prop])"></span> <span>{{colorText(scope.row[item.prop])}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else>{{scope.row[item.prop] || '-'}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -153,6 +158,11 @@ export default {
|
||||
prop: 'reference',
|
||||
width: 180,
|
||||
show: true
|
||||
}, {
|
||||
label: this.$t('overall.color'),
|
||||
prop: 'color',
|
||||
width: 180,
|
||||
show: true
|
||||
}, {
|
||||
label: this.$t('overall.remark'),
|
||||
prop: 'description',
|
||||
@@ -189,6 +199,23 @@ export default {
|
||||
show: true,
|
||||
width: 80
|
||||
}
|
||||
],
|
||||
knowledgeBaseColor: [
|
||||
{
|
||||
label: this.$t('knowledge.info'),
|
||||
value: 'rgb(119,131,145)',
|
||||
name: 'info'
|
||||
},
|
||||
{
|
||||
label: this.$t('knowledge.benign'),
|
||||
value: 'rgb(116,159,77)',
|
||||
name: 'benign'
|
||||
},
|
||||
{
|
||||
label: this.$t('knowledge.malicious'),
|
||||
value: 'rgb(226,97,84)',
|
||||
name: 'malicious'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -221,6 +248,20 @@ export default {
|
||||
const t = knowledgeBaseSource.find(t => t.value === type)
|
||||
return t ? t.name : ''
|
||||
}
|
||||
},
|
||||
colorText () {
|
||||
const vm = this
|
||||
return function (color) {
|
||||
const t = vm.knowledgeBaseColor.find(t => t.value === color)
|
||||
return t ? t.label : vm.knowledgeBaseColor[0].label
|
||||
}
|
||||
},
|
||||
colorName () {
|
||||
const vm = this
|
||||
return function (color) {
|
||||
const t = vm.knowledgeBaseColor.find(t => t.value === color)
|
||||
return t ? t.name : vm.knowledgeBaseColor[0].name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,30 +3,21 @@
|
||||
<div class="card-type-title" v-if="aiTaggingList.length > 0">{{$t('knowledgeBase.intelligenceLearning')}}</div>
|
||||
<el-checkbox-group v-model="checkList" >
|
||||
<div class="card-box" v-for="data in aiTaggingList" :key="data.knowledgeId">
|
||||
<div @click="isSelectedStatus && data.isBuiltIn !== 1 && clickCard(data,$event)" @mouseenter="mouseenter(data)" @mouseleave="mouseleave(data)" class="card-item" :class="data.isSelected ? 'card-selected' : ''">
|
||||
<div @click="isSelectedStatus && data.isBuiltIn !== 1 && clickCard(data,$event)" @mouseenter="mouseenter(data)" @mouseleave="mouseleave(data)" class="card-item" :class="data.isSelected ? 'card-selected' : ''">
|
||||
<div class="card-content">
|
||||
<div class="card-operate">
|
||||
<el-tooltip
|
||||
effect="light"
|
||||
trigger="hover"
|
||||
:content="$t('tip.notAvailable')"
|
||||
placement="right"
|
||||
popper-class="panel-tooltip"
|
||||
<el-switch v-model="data.status"
|
||||
class="card-enable"
|
||||
active-color="#38ACD2"
|
||||
inactive-color="#C0CEDB"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:before-change="(knowledgeId) => confirmSwitchLearning(data.knowledgeId)"
|
||||
>
|
||||
<el-switch v-model="data.status"
|
||||
class="card-enable"
|
||||
active-color="#38ACD2"
|
||||
inactive-color="#C0CEDB"
|
||||
:disabled="true"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="changeStatus($event,data.knowledgeId)"
|
||||
>
|
||||
</el-switch>
|
||||
</el-tooltip>
|
||||
</el-switch>
|
||||
</div>
|
||||
<div class="card-icon">
|
||||
<img :src="data.iconUrl"/>
|
||||
<img :src="data.iconUrl" style="max-height: 50px;"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
@@ -75,7 +66,9 @@
|
||||
|
||||
<div class="center-dialog">
|
||||
<el-dialog v-model="showUpdateDialog"
|
||||
:destroy-on-close="true"
|
||||
:custom-class="showAddUpdateDialog ? 'update-knowledge update-knowledge--upload' : 'update-knowledge'"
|
||||
:before-close="beforeClose"
|
||||
:after-close="handleClose">
|
||||
<div class="knowledge-update__top" >
|
||||
<div class="update-left__icon">
|
||||
@@ -86,32 +79,47 @@
|
||||
<div class="update-title">
|
||||
<div class="card-title-name" :title="updateKnowledge.label">{{updateKnowledge.label}}</div>
|
||||
</div>
|
||||
<el-tooltip
|
||||
effect="light"
|
||||
trigger="hover"
|
||||
v-if="showEnable"
|
||||
:content="$t('tip.notAvailable')"
|
||||
placement="right"
|
||||
popper-class="panel-tooltip"
|
||||
<el-switch v-model="updateKnowledge.status"
|
||||
active-color="#38ACD2"
|
||||
inactive-color="#C0CEDB"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
:before-change="(knowledgeId) => confirmSwitchLearning(updateKnowledge.knowledgeId)"
|
||||
v-if="updateKnowledge.source === 'cn_psiphon3_ip'"
|
||||
>
|
||||
<el-switch v-model="updateKnowledge.status"
|
||||
active-color="#38ACD2"
|
||||
inactive-color="#C0CEDB"
|
||||
:disabled="true"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="changeStatus($event,updateKnowledge.knowledgeId)"
|
||||
>
|
||||
</el-switch>
|
||||
</el-tooltip>
|
||||
</el-switch>
|
||||
</div>
|
||||
<div class="knowledge-desc" :title="updateKnowledge.desc">{{updateKnowledge.desc ? updateKnowledge.desc : '—'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="!showAddUpdateDialog">
|
||||
<div class="knowledge-update" >
|
||||
<div class="update-title">
|
||||
<div class="card-title-name">update record</div>
|
||||
<div class="knowledge-update__tab" v-if="showEnable">
|
||||
<el-tabs v-model="activeTab"
|
||||
class="update-log-tab"
|
||||
@tab-click="handleClick"
|
||||
>
|
||||
<el-tab-pane :label="$t('knowledgeBase.updateRecord')"
|
||||
name="updateRecord"
|
||||
key="updateRecord"
|
||||
ref="knowledgeUpdateRecordTab">
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('knowledgeBase.learningEngineLogs')"
|
||||
name="intelligenceLearning"
|
||||
key="intelligenceLearning"
|
||||
ref="knowledgeIntelligenceLearningTab">
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="update-operate">
|
||||
<button :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="uploadRecord">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="knowledge-update" v-else>
|
||||
<div class="update-title" >
|
||||
<div class="card-title-name">{{$t('knowledgeBase.updateRecord')}}</div>
|
||||
</div>
|
||||
<div class="update-operate">
|
||||
<button :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@@ -121,23 +129,92 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning' ? 'calc(90vh - 190px - 200px - 50px - 42px)' : 'calc(100% - 242px)', marginTop: '42px', position: 'absolute', width: 'calc(100% - 60px)'}">
|
||||
<loading :loading="updateLogLoading"></loading>
|
||||
</div>
|
||||
<el-table ref="updateDataTable"
|
||||
border
|
||||
:data="updateHistoryList"
|
||||
@selection-change="secondSelectionChange"
|
||||
width="100%"
|
||||
class="update-dialog__table"
|
||||
:class="{
|
||||
'update-dialog__table--psiphon3': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning',
|
||||
'update-dialog__table--system-user': updateKnowledge.source === 'cn_psiphon3_ip' && activeTab !== 'intelligenceLearning'
|
||||
}"
|
||||
:header-cell-style="{background:'#f5f7fa',color:'#353636',fontWeight: '400',fontSize: '12px',borderRight: 'none',borderBottom: 'none'}"
|
||||
cell-style="padding:6px 0px;font-size: 12px;color: #353636;font-weight: 400;line-height: 20px;border-right:none;"
|
||||
header-cell-style="padding:8px 0px;font-size: 12px;color: #353636;font-weight: 500;border-right:none;">
|
||||
<el-table-column prop="opTime" label="Update time" width="150" ></el-table-column>
|
||||
<el-table-column prop="user" label="Operating user" width="150" >
|
||||
<el-table-column prop="opTime" :label="$t('entities.tab.informationAggregation.updateTime')" width="150" >
|
||||
<template #default="scope" :column="item">
|
||||
<span>{{scope.row.opTime ? dateFormatByAppearance(scope.row.opTime) : '-'}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user" :label="$t('knowledgeBase.operator')" width="150" v-if="activeTab === 'updateRecord'">
|
||||
<template #default="scope" :column="item">
|
||||
<span>{{$_.get(scope.row, 'user.name', '-')}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="commitVersion" label="Version information" width="150" ></el-table-column>
|
||||
<el-table-column prop="description" label="Description"></el-table-column>
|
||||
<el-table-column prop="commitVersion" :label="$t('overall.version')" width="150" ></el-table-column>
|
||||
<el-table-column prop="description" :label="$t('overall.remark')"></el-table-column>
|
||||
<template v-slot:empty >
|
||||
<div class="table-no-data" v-if="updateHistoryList.length === 0 && !updateLogLoading">
|
||||
<div class="table-no-data__title">{{ $t('npm.noData') }}</div>
|
||||
</div>
|
||||
<div v-else></div>
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<div class="psiphon3" v-if="updateKnowledge.source === 'cn_psiphon3_ip' && activeTab === 'intelligenceLearning'">
|
||||
<div class="psiphon3-title">{{$t('knowledgeBase.psiphon3IpCount')}}</div>
|
||||
<div class="psiphon3-bar">
|
||||
<chart-error v-if="showErrorForPsiphon3" :content="errorMsgForPsiphon3"/>
|
||||
<div class="bar-header" v-else>
|
||||
<div class="bar-header-left">
|
||||
<div class="bar-value-active" ></div>
|
||||
<div class="bar-value">
|
||||
<template v-for="(item, index) in tabs" :key="index">
|
||||
<div class="bar-value-tabs"
|
||||
:class=" {'is-active': tabType === item.class, 'mousemove-cursor': mousemoveCursor === item.class}"
|
||||
@mouseenter="mouseenterTab(item)"
|
||||
@mouseleave="mouseleaveTab(item)"
|
||||
@click="activeChange(item)"
|
||||
>
|
||||
<div class="bar-value-tabs-name">
|
||||
<div :class="item.class"></div>
|
||||
<div class="tabs-name" >{{ $t(item.name) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bar-select bar-header-right">
|
||||
<div class="bar-select-time">
|
||||
<div class="bar-select__operation">
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="selectTime"
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="timeChange"
|
||||
>
|
||||
<template #prefix>
|
||||
<div class="calendar-popover-text"><i class="cn-icon cn-icon-Data"></i></div>
|
||||
</template>
|
||||
<el-option v-for="item in dateRangeArr" :key="item.value" :label="item.name" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: calc(100% - 24px); position: relative">
|
||||
<chart-no-data v-if="isNoDataForPsiphon3 && !showErrorForPsiphon3 && !psiphon3Loading"></chart-no-data>
|
||||
<loading :loading="psiphon3Loading"></loading>
|
||||
<div class="chart-drawing" v-show="!isNoDataForPsiphon3 && !showErrorForPsiphon3" id="psiphonBarChart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="showAddUpdateDialog">
|
||||
<div class="update-knowledge-form">
|
||||
@@ -187,6 +264,7 @@
|
||||
<el-dialog v-model="showConfirmDialog"
|
||||
:title="$t('overall.tips')"
|
||||
custom-class="update-knowledge-tip"
|
||||
:width="480"
|
||||
:before-close="handleConfirmClose">
|
||||
<div class="dialog-message">{{$t('knowledge.updateTips')}}</div>
|
||||
<template #footer>
|
||||
@@ -196,18 +274,37 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="showConfirmSwitch"
|
||||
:width="330"
|
||||
custom-class="confirm-knowledge-switch"
|
||||
:title="$t('overall.hint')"
|
||||
>
|
||||
<div class="dialog-message">{{ confirmSwitchLearningTip }}</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showConfirmSwitch = false">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="switchLearning">OK</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import table from '@/mixins/table'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { getSecond, getMillisecond, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo } from '@/utils/constants'
|
||||
import { ref } from 'vue'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { api } from '@/utils/api'
|
||||
import { detectionTooltipFormatter } from '@/views/charts/charts/tools'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import axios from 'axios'
|
||||
import _ from 'lodash'
|
||||
import * as echarts from 'echarts'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
|
||||
export default {
|
||||
name: 'knowledgeBaseTableForCard',
|
||||
mixins: [table],
|
||||
@@ -221,7 +318,8 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Loading
|
||||
Loading,
|
||||
ChartNoData
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -237,12 +335,57 @@ export default {
|
||||
updateHistoryList: [],
|
||||
updateObject: {},
|
||||
currentVersion: 0,
|
||||
uploadLoading: false
|
||||
uploadLoading: false,
|
||||
psiphon3Loading: false,
|
||||
updateLogLoading: false,
|
||||
showConfirmSwitch: false,
|
||||
switchKnowledgeId: '',
|
||||
activeTab: 'updateRecord',
|
||||
isNoDataForPsiphon3: false,
|
||||
showErrorForPsiphon3: false,
|
||||
errorMsgForPsiphon3: '',
|
||||
leftOffset: 0,
|
||||
tabType: 'total',
|
||||
mousemoveCursor: '',
|
||||
selectTime: 1440,
|
||||
tabs: [
|
||||
{
|
||||
name: 'knowledgeBase.total',
|
||||
class: 'total',
|
||||
color: '#00A7AB',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: 'knowledgeBase.active',
|
||||
class: 'active',
|
||||
color: '#7FA054',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: 'knowledgeBase.new',
|
||||
class: 'new',
|
||||
color: '#98709B',
|
||||
data: []
|
||||
}
|
||||
],
|
||||
dateRangeArr: [
|
||||
{ value: 1440, name: this.$t('dateTime.last1Day') },
|
||||
{ value: 2880, name: this.$t('dateTime.last2Days') },
|
||||
{ value: 10080, name: this.$t('dateTime.last7Days') },
|
||||
{ value: 21600, name: this.$t('dateTime.last15Days') },
|
||||
{ value: 43200, name: this.$t('dateTime.last30Days') }
|
||||
]
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
// 没上传过文件的提示
|
||||
const uploadErrorTip = ref('')
|
||||
const nowMill = window.$dayJs.tz().valueOf()
|
||||
const timeFilter = ref({
|
||||
startTime: nowMill - 1000 * 60 * 60 * 24,
|
||||
endTime: nowMill,
|
||||
dateRangeValue: 1440
|
||||
})
|
||||
return {
|
||||
baseUrl: BASE_CONFIG.baseUrl,
|
||||
apiVersion: BASE_CONFIG.apiVersion,
|
||||
@@ -252,13 +395,174 @@ export default {
|
||||
uploadErrorTip,
|
||||
fileTypeLimit: '.csv',
|
||||
fileList: ref([]),
|
||||
uploadFileSizeLimit: 100 * 1024 * 1024
|
||||
uploadFileSizeLimit: 1024 * 1024 * 1024,
|
||||
myChart: shallowRef(null),
|
||||
chartOption: shallowRef(null),
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
echartsInit (echartsData) {
|
||||
const _this = this
|
||||
const curTab = this.tabs.find(item => item.class === _this.tabType)
|
||||
this.chartOption = {
|
||||
color: curTab.color,
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
formatter: (params) => {
|
||||
params.seriesName = this.$t(params.seriesName)
|
||||
params.borderColor = params.color
|
||||
return detectionTooltipFormatter(params)
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: '12%',
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: 24,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: ['1%', '3%'],
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: xAxisTimeFormatter,
|
||||
rich: xAxisTimeRich
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#ECECEC'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 20
|
||||
},
|
||||
minInterval: 1
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: curTab.name,
|
||||
data: echartsData,
|
||||
type: 'bar',
|
||||
barWidth: 26
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (!this.myChart) {
|
||||
this.myChart = echarts.init(document.getElementById('psiphonBarChart'))
|
||||
}
|
||||
this.myChart.setOption(this.chartOption)
|
||||
})
|
||||
},
|
||||
init (val, show, active, n) {
|
||||
this.psiphon3Loading = true
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
const url = api.knowledgeBaseTimedistribution.replace('{{knowledgeId}}', this.updateKnowledge.knowledgeId).replace('{{type}}', this.tabType)
|
||||
axios.get(url, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
this.isNoDataForPsiphon3 = res.data.result.length === 0
|
||||
this.showErrorForPsiphon3 = false
|
||||
if (!this.isNoDataForPsiphon3) {
|
||||
const chartsData = res.data.result.map(item => {
|
||||
return [getMillisecond(item.statTime), item.count]
|
||||
})
|
||||
this.echartsInit(chartsData)
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.psiphon3Loading = false
|
||||
})
|
||||
},
|
||||
httpError (e) {
|
||||
this.isNoDataForPsiphon3 = false
|
||||
this.showErrorForPsiphon3 = true
|
||||
this.errorMsgForPsiphon3 = this.errorMsgHandler(e)
|
||||
},
|
||||
handleActiveBar () {
|
||||
if (document.querySelector('.psiphon3-bar .bar-value-tabs.is-active')) {
|
||||
const {
|
||||
offsetLeft,
|
||||
clientWidth,
|
||||
clientLeft
|
||||
} = document.querySelector('.psiphon3-bar .bar-value-tabs.is-active')
|
||||
const activeBar = document.querySelector('.psiphon3-bar .bar-value-active')
|
||||
activeBar.style.cssText += `width: ${clientWidth}px; left: ${offsetLeft + this.leftOffset + clientLeft}px;`
|
||||
}
|
||||
},
|
||||
resize () {
|
||||
if (this.myChart) {
|
||||
this.myChart.resize()
|
||||
}
|
||||
},
|
||||
dispatchSelectAction (type, name) {
|
||||
this.myChart && this.myChart.dispatchAction({
|
||||
type: type,
|
||||
name: name
|
||||
})
|
||||
},
|
||||
legendSelectChange (item) {
|
||||
this.dispatchSelectAction('legendSelect', item.name)
|
||||
this.tabs.forEach((t) => {
|
||||
if (t.name !== item.name) {
|
||||
this.dispatchSelectAction('legendUnSelect', t.name)
|
||||
}
|
||||
})
|
||||
},
|
||||
timeChange () {
|
||||
this.timeFilter.endTime = window.$dayJs.tz().valueOf()
|
||||
this.timeFilter.startTime = this.timeFilter.endTime - this.selectTime * 60 * 1000
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
},
|
||||
activeChange (item) { // isClick:代表是通过点击操作来的
|
||||
if (item) {
|
||||
this.tabType = item.class
|
||||
}
|
||||
this.legendSelectChange(item)
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
mouseenterTab (item) {
|
||||
if (this.isNoDataForPsiphon3) return
|
||||
this.mousemoveCursor = item.class
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
},
|
||||
mouseleaveTab () {
|
||||
this.mousemoveCursor = ''
|
||||
},
|
||||
fileChange (file, fileList) {
|
||||
console.info(!_.endsWith(file.name, '.csv'))
|
||||
console.info(file.size > this.uploadFileSizeLimit)
|
||||
// 判断后缀,仅支持.csv
|
||||
if (!_.endsWith(file.name, '.csv')) {
|
||||
this.fileList = []
|
||||
@@ -287,10 +591,10 @@ export default {
|
||||
if (response.code === 200) { */
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
this.showAddUpdateDialog = false
|
||||
axios.get(api.knowledgeBaseLog + '/' + this.updateKnowledge.knowledgeId, { params: { pageSize: 999 } }).then(res => {
|
||||
this.updateHistoryList = res.data.data.list
|
||||
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
|
||||
})
|
||||
this.getCurTabData()
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
/* } else {
|
||||
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
|
||||
} */
|
||||
@@ -325,6 +629,13 @@ export default {
|
||||
data.isSelected = val
|
||||
this.$emit('checkboxStatusChange', val, data)
|
||||
},
|
||||
beforeClose (done) {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
done()
|
||||
},
|
||||
handleClose () {
|
||||
this.showUpdateDialog = false
|
||||
this.showAddUpdateDialog = false
|
||||
@@ -340,13 +651,16 @@ export default {
|
||||
this.showUpdateDialog = true
|
||||
this.showAddUpdateDialog = false
|
||||
},
|
||||
jumpToUpdatePage (data, showEnable) {
|
||||
axios.get(api.knowledgeBaseLog + '/' + data.knowledgeId, { params: { pageSize: 999 } }).then(res => {
|
||||
this.updateKnowledge = data
|
||||
this.updateHistoryList = res.data.data.list
|
||||
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
|
||||
this.showEnable = showEnable
|
||||
this.showUpdate()
|
||||
async jumpToUpdatePage (data, showEnable) {
|
||||
this.updateKnowledge = data
|
||||
this.showEnable = showEnable
|
||||
await this.getCurTabData()
|
||||
if (data.source === 'cn_psiphon3_ip') {
|
||||
await this.init()
|
||||
}
|
||||
this.showUpdate()
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
},
|
||||
uploadRecord () {
|
||||
@@ -355,6 +669,43 @@ export default {
|
||||
this.updateObject.label = this.updateKnowledge.label
|
||||
this.updateObject.description = ''
|
||||
},
|
||||
getCurTabData () { // showEnable:true 为psiphon3的知识库,false为其它知识库
|
||||
let params = {
|
||||
pageSize: -1
|
||||
}
|
||||
if (this.showEnable) {
|
||||
if (this.activeTab === 'updateRecord') {
|
||||
params = {
|
||||
...params,
|
||||
opUser: -1
|
||||
}
|
||||
} else if (this.activeTab === 'intelligenceLearning') {
|
||||
params = {
|
||||
...params,
|
||||
opUser: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
this.updateLogLoading = true
|
||||
this.updateHistoryList = []
|
||||
axios.get(api.knowledgeBaseLog + '/' + this.updateKnowledge.knowledgeId, { params: params }).then(res => {
|
||||
this.updateHistoryList = res.data.data.list
|
||||
if (this.updateHistoryList[0]) {
|
||||
this.currentVersion = this.updateHistoryList[0].commitVersion + 1
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
}).finally(() => {
|
||||
this.updateLogLoading = false
|
||||
})
|
||||
},
|
||||
// 切换tab
|
||||
handleClick (tab) {
|
||||
this.getCurTabData()
|
||||
if (tab.index === '1') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
clearSelect () {
|
||||
this.$nextTick(() => {
|
||||
this.checkList = []
|
||||
@@ -366,18 +717,10 @@ export default {
|
||||
})
|
||||
},
|
||||
mouseenter (card) {
|
||||
this.tableData.forEach(t => {
|
||||
if (t.knowledgeId === card.knowledgeId) {
|
||||
card.showUpdate = true
|
||||
}
|
||||
})
|
||||
card.showUpdate = true
|
||||
},
|
||||
mouseleave (card) {
|
||||
this.tableData.forEach(t => {
|
||||
if (t.knowledgeId === card.knowledgeId) {
|
||||
card.showUpdate = false
|
||||
}
|
||||
})
|
||||
card.showUpdate = false
|
||||
},
|
||||
del (data) {
|
||||
this.$emit('delete', data)
|
||||
@@ -394,9 +737,54 @@ export default {
|
||||
dataType: dataType
|
||||
}
|
||||
})
|
||||
},
|
||||
confirmSwitchLearning (id) {
|
||||
this.showConfirmSwitch = true
|
||||
this.switchKnowledgeId = id
|
||||
return false
|
||||
},
|
||||
switchLearning () {
|
||||
const hint = this.aiTaggingList.find(d => d.knowledgeId === this.switchKnowledgeId)
|
||||
const toStatus = hint.status === 0 ? 1 : 0
|
||||
const url = toStatus === 0 ? api.knowledgeBaseLearningStop : api.knowledgeBaseLearningStart
|
||||
if (hint.knowledgeId === 101 || hint.knowledgeId === 102) {
|
||||
hint.status = toStatus
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
this.showConfirmSwitch = false
|
||||
} else {
|
||||
axios.post(`${url}?knowledgeId=${hint.knowledgeId}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
hint.status = toStatus
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
} else {
|
||||
console.error(res.message)
|
||||
this.$message.error(this.errorMsgHandler(res))
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
}).finally(() => {
|
||||
this.showConfirmSwitch = false
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tabType (n) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
},
|
||||
timeFilter: {
|
||||
handler () {
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
tableData: {
|
||||
handler (n) {
|
||||
if (this.tableData && this.tableData.length > 0) {
|
||||
@@ -417,6 +805,48 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
// 2024-01-15 以下两个是为105环境演示准备的假数据
|
||||
const data1 = {
|
||||
knowledgeId: 101,
|
||||
name: 'CyberGhost',
|
||||
category: 'ai_tagging',
|
||||
description: 'CyberGhost is a VPN service used to unblock sites and browse privately and anonymously.',
|
||||
isBuiltIn: 1,
|
||||
isPublished: 1,
|
||||
status: 1,
|
||||
showUpdate: false
|
||||
}
|
||||
const basicInfo1 = builtInKnowledgeBaseBasicInfo.find(bi => bi.knowledgeId === data1.knowledgeId)
|
||||
this.aiTaggingList.push({
|
||||
...data1,
|
||||
...basicInfo1
|
||||
})
|
||||
const data2 = {
|
||||
knowledgeId: 102,
|
||||
name: 'HotSpotshield VPN',
|
||||
category: 'ai_tagging',
|
||||
description: 'Hotspot Shield is a public VPN service, providing\n' +
|
||||
'a secure proxy connection through an encrypted\n' +
|
||||
'channel between your device and the target\n' +
|
||||
'website, using VPN technology.',
|
||||
isBuiltIn: 1,
|
||||
isPublished: 1,
|
||||
status: 1,
|
||||
showUpdate: false
|
||||
}
|
||||
const basicInfo2 = builtInKnowledgeBaseBasicInfo.find(bi => bi.knowledgeId === data2.knowledgeId)
|
||||
this.aiTaggingList.push({
|
||||
...data2,
|
||||
...basicInfo2
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
activeTab (n) {
|
||||
if (n === 'updateRecord') {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -424,11 +854,23 @@ export default {
|
||||
handler (n) {
|
||||
if (!n) {
|
||||
this.fileList = []
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
} else {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.myChart = null
|
||||
this.chartOption = null
|
||||
window.addEventListener('resize', this.resize)
|
||||
|
||||
this.aiTaggingList = []
|
||||
this.websketchList = []
|
||||
this.tableData.forEach(item => {
|
||||
@@ -440,6 +882,22 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
beforeUnmount () {
|
||||
clearTimeout(this.timer)
|
||||
window.removeEventListener('resize', this.resize)
|
||||
const dom = document.getElementById('psiphonBarChart')
|
||||
if (dom) {
|
||||
let myChart = echarts.getInstanceByDom(document.getElementById('psiphonBarChart'))
|
||||
if (myChart) {
|
||||
echarts.dispose(myChart)
|
||||
}
|
||||
myChart = null
|
||||
}
|
||||
|
||||
if (this.myChart) {
|
||||
echarts.dispose(this.myChart)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uploadParams () {
|
||||
return {
|
||||
@@ -447,6 +905,20 @@ export default {
|
||||
action: 'overwrite',
|
||||
description: this.updateObject.description
|
||||
}
|
||||
},
|
||||
confirmSwitchLearningTip () {
|
||||
let tip = ''
|
||||
if (this.switchKnowledgeId) {
|
||||
const find = this.aiTaggingList.find(item => item.knowledgeId === this.switchKnowledgeId)
|
||||
if (find) {
|
||||
if (find.status === 0) {
|
||||
tip = this.$t('tip.confirmEnable') + '?'
|
||||
} else if (find.status === 1) {
|
||||
tip = this.$t('tip.confirmDisable') + '?'
|
||||
}
|
||||
}
|
||||
}
|
||||
return tip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,64 +110,26 @@ export default {
|
||||
if (this.listUrl) {
|
||||
listUrl = this.listUrl
|
||||
}
|
||||
// todo 此段是为了避免mock没开启,打开detection界面报错提示,后续再开发detection时删除
|
||||
if (listUrl === api.detection.list) {
|
||||
const list = []
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const obj = {
|
||||
ruleId: 100000 + i,
|
||||
ruleType: 'indicator_match',
|
||||
status: 1,
|
||||
name: 'name123',
|
||||
category: 'Security Event',
|
||||
eventType: 'C&C',
|
||||
description: 'Built-in darkweb IoC',
|
||||
ruleConfig: {
|
||||
knowledge: {
|
||||
name: 'VPN Server IP',
|
||||
category: 'user_defined'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i % 2 === 0) {
|
||||
obj.ruleType = 'threshold'
|
||||
obj.ruleConfig = {
|
||||
dimensions: 'Destination IP/CIDR'
|
||||
}
|
||||
obj.description = 'abuse.ch is providing community driven threat intelligence on \n' +
|
||||
'cyber threats. It is the home of a couple of projects that are \n' +
|
||||
'helping internet service providers and network operators protect …'
|
||||
} else {
|
||||
obj.status = 0
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
|
||||
this.tableData = list
|
||||
this.pageObj.total = list.length
|
||||
this.loading = false
|
||||
} else {
|
||||
axios.get(listUrl, { params: this.searchLabel }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.tableData = _.get(response, 'data.data.list', [])
|
||||
this.pageObj.total = _.get(response, 'data.data.total', 0)
|
||||
this.isNoData = !this.tableData || this.tableData.length === 0
|
||||
} else {
|
||||
console.error(response)
|
||||
this.isNoData = true
|
||||
if (response.data.message) {
|
||||
this.$message.error(response.data.message)
|
||||
} else {
|
||||
this.$message.error(this.$t('tip.somethingWentWrong'))
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
axios.get(listUrl, { params: this.searchLabel }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.tableData = _.get(response, 'data.data.list', [])
|
||||
this.pageObj.total = _.get(response, 'data.data.total', 0)
|
||||
this.isNoData = !this.tableData || this.tableData.length === 0
|
||||
} else {
|
||||
console.error(response)
|
||||
this.isNoData = true
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
if (response.data.message) {
|
||||
this.$message.error(response.data.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'), {
|
||||
@@ -400,10 +362,23 @@ export default {
|
||||
this.searchLabel.orderBy = orderBy
|
||||
this.getTableData()
|
||||
},
|
||||
search (params) {
|
||||
search (params, flag, list) {
|
||||
this.pageObj.pageNo = 1
|
||||
delete this.searchLabel.category
|
||||
delete this.searchLabel.source
|
||||
if (flag !== 'detection') {
|
||||
delete this.searchLabel.category
|
||||
delete this.searchLabel.source
|
||||
}
|
||||
if (list && list.length > 0) {
|
||||
if (list.indexOf('status') > -1) {
|
||||
delete this.searchLabel.status
|
||||
}
|
||||
if (list.indexOf('category') > -1) {
|
||||
delete this.searchLabel.category
|
||||
}
|
||||
if (list.indexOf('eventType') > -1) {
|
||||
delete this.searchLabel.eventType
|
||||
}
|
||||
}
|
||||
this.getTableData(params)
|
||||
},
|
||||
getTimeString () {
|
||||
|
||||
@@ -96,5 +96,41 @@ export default {
|
||||
this.relationshipShowMoreTwo = false
|
||||
this.relationshipShowMoreOne = false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scoreDot () {
|
||||
const dots = []
|
||||
if (this.score === '-') {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
dots.push({
|
||||
class: 'score-dot'
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
if (i < this.score) {
|
||||
dots.push({
|
||||
class: `score-dot ${handleClass(this.score)}`
|
||||
})
|
||||
} else {
|
||||
dots.push({
|
||||
class: 'score-dot'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return dots
|
||||
|
||||
function handleClass (score) {
|
||||
if (score <= 2) {
|
||||
return 'score-dot--red'
|
||||
} else if (score <= 4) {
|
||||
return 'score-dot--yellow'
|
||||
} else if (score <= 6) {
|
||||
return 'score-dot--green'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ if (openMock) {
|
||||
const list = []
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const obj = {
|
||||
ruleId: 100000 + i,
|
||||
ruleId: 163 + i,
|
||||
ruleType: 'indicator_match',
|
||||
status: 1,
|
||||
name: 'name123',
|
||||
@@ -50,51 +50,17 @@ if (openMock) {
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/statistics.*'), 'get', function (requestObj) {
|
||||
const data = {
|
||||
statusList: [
|
||||
{ status: 1 },
|
||||
{ status: 0 }
|
||||
{ status: 1, count: 34 },
|
||||
{ status: 0, count: 28 }
|
||||
],
|
||||
categoryList: [
|
||||
{ value: 'security', label: 'Security Event' },
|
||||
{ value: 'performance', label: 'Performance Event' },
|
||||
{ value: 'regulatory_risk', label: 'Regulatory Risk Event' }
|
||||
{ name: 'Security Event', count: 32 },
|
||||
{ name: 'Performance Event', count: 28 }
|
||||
],
|
||||
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' }
|
||||
eventTypeList: [
|
||||
{ name: 'DDos', count: 15 },
|
||||
{ name: 'Lateral movement', count: 17 },
|
||||
{ name: 'Brute force', count: 12 }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -129,28 +95,50 @@ if (openMock) {
|
||||
const ruleId = getLastValue(requestObj.url)
|
||||
const data = {
|
||||
name: 'name123',
|
||||
category: 'Security Event',
|
||||
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
|
||||
knowledgeBase: {
|
||||
knowledgeId: 10,
|
||||
name: 'cn_ioc_darkweb',
|
||||
category: 'websketch',
|
||||
source: 'cn_ioc_darkweb'
|
||||
},
|
||||
level: 'critical'
|
||||
},
|
||||
trigger: {
|
||||
ruleConfigObj: {
|
||||
dataSource: 'VPN Server IP',
|
||||
knowledgeBase: {
|
||||
knowledgeId: '101',
|
||||
name: 'cn_ioc_darkweb',
|
||||
category: 'websketch',
|
||||
source: 'cn_ioc_darkweb'
|
||||
},
|
||||
level: 'critical'
|
||||
},
|
||||
ruleTrigger: {
|
||||
atLeast: 1,
|
||||
interval: 'PT5M',
|
||||
resetInterval: 'PT10M'
|
||||
},
|
||||
ruleTriggerObj: {
|
||||
atLeast: 1,
|
||||
interval: 'PT5M',
|
||||
resetInterval: 'PT10M'
|
||||
}
|
||||
}
|
||||
data.ruleConfig = JSON.stringify(data.ruleConfig)
|
||||
data.trigger = JSON.stringify(data.trigger)
|
||||
|
||||
if (ruleId % 2 === 0) {
|
||||
data.ruleType = 'threshold'
|
||||
data.status = 1
|
||||
} else {
|
||||
data.status = 0
|
||||
} else {
|
||||
data.status = 1
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
353
src/mock/detectionList.js
Normal file
353
src/mock/detectionList.js
Normal file
@@ -0,0 +1,353 @@
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const urlAndVersion = BASE_CONFIG.baseUrl + BASE_CONFIG.apiVersion
|
||||
const openMock = true
|
||||
if (openMock) {
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/list.*'), 'get', function (requestObj) {
|
||||
const result = []
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const obj = {
|
||||
eventId: 1212,
|
||||
eventType: 'Anonymity',
|
||||
eventName: 'Tor',
|
||||
eventKey: '2,1.1.1,12.2.2.2',
|
||||
ruleId: 2,
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: 1,
|
||||
severity: 'critical',
|
||||
offenderIp: '1.1.1.1',
|
||||
victimIp: '2.2.2.2',
|
||||
domain: '*.vioee.com',
|
||||
app: 'vio',
|
||||
startTime: 1697092617,
|
||||
endTime: 1697092777,
|
||||
durationS: 30000,
|
||||
matchTimes: 1,
|
||||
status: 1,
|
||||
eventInfo: "{\'knowledge_id\': 123, \'name\': \'example_ioc_malware\', \'ioc_type\': \'domain\', \'ioc_value\': \'iocentity.com\'}",
|
||||
eventInfoObj: {
|
||||
knowledge_id: 123,
|
||||
name: 'example_ioc_malware',
|
||||
ioc_type: 'domain',
|
||||
ioc_value: 'iocentity.com'
|
||||
}
|
||||
}
|
||||
if (i % 2 === 0) {
|
||||
obj.status = 0
|
||||
}
|
||||
result.push(obj)
|
||||
}
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/severity/timedistribution.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
statTime: 1697523900,
|
||||
severity: 'critical',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
statTime: 1697524200,
|
||||
severity: 'high',
|
||||
count: 31
|
||||
},
|
||||
{
|
||||
statTime: 1697524500,
|
||||
severity: 'info',
|
||||
count: 21
|
||||
},
|
||||
{
|
||||
statTime: 1697524800,
|
||||
severity: 'low',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
statTime: 1697525100,
|
||||
severity: 'medium',
|
||||
count: 32
|
||||
},
|
||||
{
|
||||
statTime: 1697525400,
|
||||
severity: 'critical',
|
||||
count: 22
|
||||
},
|
||||
{
|
||||
statTime: 1697525700,
|
||||
severity: 'critical',
|
||||
count: 23
|
||||
},
|
||||
{
|
||||
statTime: 1697526000,
|
||||
severity: 'critical',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
statTime: 1697526300,
|
||||
severity: 'critical',
|
||||
count: 21
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/severity/statistics.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
severity: 'critical',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
severity: 'high',
|
||||
count: 31
|
||||
},
|
||||
{
|
||||
severity: 'info',
|
||||
count: 21
|
||||
},
|
||||
{
|
||||
severity: 'low',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
severity: 'medium',
|
||||
count: 32
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/event-type/statistics.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
eventType: 'Anonymity',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
eventType: 'Command and Control',
|
||||
count: 31
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/offender-ip/statistics.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
offenderIp: '221.7.1.20',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
offenderIp: '58.247.118.253',
|
||||
count: 31
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/victim-ip/statistics.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
victimIp: '21.77.1.201',
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
victimIp: '58.47.118.153',
|
||||
count: 31
|
||||
},
|
||||
{
|
||||
victimIp: '22.47.223.57',
|
||||
count: 43
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/status/statistics.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
status: 1,
|
||||
count: 25
|
||||
},
|
||||
{
|
||||
status: 0,
|
||||
count: 31
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/ip/relation/event.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
eventId: 10010,
|
||||
severity: 'high',
|
||||
eventType: 'Command and Control',
|
||||
offenderIp: '1.1.1.1',
|
||||
victimIp: '2.2.2.2',
|
||||
startTime: 1697092617
|
||||
}
|
||||
]
|
||||
|
||||
const data = {
|
||||
resultType: 'table',
|
||||
result: result
|
||||
}
|
||||
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: data
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/count.*'), 'get', function (requestObj) {
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: {
|
||||
resultType: 'single',
|
||||
result: 123
|
||||
}
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/ip.*'), 'get', function (requestObj) {
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: {
|
||||
asn: {
|
||||
id: 2,
|
||||
asn: '14061',
|
||||
organization: 'DIGITALOCEAN-ASN - DigitalOcean, LLC, US'
|
||||
},
|
||||
malware: {
|
||||
threatType: 'command and control',
|
||||
malwareName: 'IcedID',
|
||||
malwareAlias: 'BokBot,IceID',
|
||||
mitreAttackDescription: '[IcedID](https://attack.mitre.org/software/S0483) is a modular banking malware designed to steal financial information that has been observed in the wild since at least 2017. [IcedID](https://attack.mitre.org/software/S0483) has been downloaded by [Emotet](https://attack.mitre.org/software/S0367) in multiple campaigns.(Citation: IBM IcedID November 2017)(Citation: Juniper IcedID June 2020)',
|
||||
mitreAttackPlatforms: 'Windows',
|
||||
mitreAttackTechniques: '[""Asymmetric Cryptography(T1573.002)"",""Asynchronous Procedure Call(T1055.004)"",""Browser Session Hijacking(T1185)"",""Domain Account(T1087.002)"",""Ingress Tool Transfer(T1105)"",""Malicious File(T1204.002)"",""Msiexec(T1218.007)"",""Native API(T1106)"",""Obfuscated Files or Information(T1027)"",""Permission Groups Discovery(T1069)"",""Registry Run Keys / Startup Folder(T1547.001)"",""Scheduled Task(T1053.005)"",""Software Packing(T1027.002)"",""Spearphishing Attachment(T1566.001)"",""Steganography(T1027.003)"",""System Information Discovery(T1082)"",""Visual Basic(T1059.005)"",""Web Protocols(T1071.001)"",""Windows Management Instrumentation(T1047)""]',
|
||||
mitreAttackGroups: '[""TA551(G0127)""]',
|
||||
confidenceLevel: 'high',
|
||||
reference: ''
|
||||
},
|
||||
location: {
|
||||
continent: 'North America',
|
||||
country: 'United States',
|
||||
province: 'New York',
|
||||
city: '',
|
||||
lngwgs: '-74.006',
|
||||
latwgs: '40.713',
|
||||
isp: 'dba Omsoft',
|
||||
owner: 'tie net'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/domain.*'), 'get', function (requestObj) {
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: {
|
||||
malware: {
|
||||
threatType: 'command and control',
|
||||
malwareName: 'IcedID',
|
||||
malwareAlias: 'BokBot,IceID',
|
||||
mitreAttackDescription: '[IcedID](https://attack.mitre.org/software/S0483) is a modular banking malware designed to steal financial information that has been observed in the wild since at least 2017. [IcedID](https://attack.mitre.org/software/S0483) has been downloaded by [Emotet](https://attack.mitre.org/software/S0367) in multiple campaigns.(Citation: IBM IcedID November 2017)(Citation: Juniper IcedID June 2020)',
|
||||
mitreAttackPlatforms: 'Windows',
|
||||
mitreAttackTechniques: '[""Asymmetric Cryptography(T1573.002)"",""Asynchronous Procedure Call(T1055.004)"",""Browser Session Hijacking(T1185)"",""Domain Account(T1087.002)"",""Ingress Tool Transfer(T1105)"",""Malicious File(T1204.002)"",""Msiexec(T1218.007)"",""Native API(T1106)"",""Obfuscated Files or Information(T1027)"",""Permission Groups Discovery(T1069)"",""Registry Run Keys / Startup Folder(T1547.001)"",""Scheduled Task(T1053.005)"",""Software Packing(T1027.002)"",""Spearphishing Attachment(T1566.001)"",""Steganography(T1027.003)"",""System Information Discovery(T1082)"",""Visual Basic(T1059.005)"",""Web Protocols(T1071.001)"",""Windows Management Instrumentation(T1047)""]',
|
||||
mitreAttackGroups: '[""TA551(G0127)""]',
|
||||
confidenceLevel: 'high',
|
||||
reference: ''
|
||||
},
|
||||
category: {
|
||||
categoryName: '门户网站',
|
||||
categoryGroup: '互联网',
|
||||
reputationLevel: 'high'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(urlAndVersion + '/detection/security/entity/detail/app.*'), 'get', function (requestObj) {
|
||||
return {
|
||||
msg: 'OK',
|
||||
code: 200,
|
||||
data: {
|
||||
category: {
|
||||
appName: 'QQ',
|
||||
appId: 333,
|
||||
appCategory: '娱乐',
|
||||
appSubcategory: '聊天',
|
||||
appRisk: 'low',
|
||||
appDescription: '聊天社交软件',
|
||||
appLongname: 'Tencent qq',
|
||||
appTechnology: 'socket',
|
||||
appCompany: 'tencent',
|
||||
appCompanyCategory: '互联网'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -3,3 +3,4 @@ import './linkMonitor'
|
||||
import './dns'
|
||||
import './entity'
|
||||
import './detection'
|
||||
import './detectionList'
|
||||
|
||||
@@ -93,21 +93,16 @@ const routes = [
|
||||
component: () => import('@/views/administration/I18n')
|
||||
},
|
||||
{
|
||||
name: 'Chart',
|
||||
path: '/chart',
|
||||
component: () => import('@/views/administration/Chart')
|
||||
path: '/detectionPolicy',
|
||||
component: () => import('@/views/detections/detectionPolicies/Index')
|
||||
},
|
||||
{
|
||||
path: '/detectionsNew',
|
||||
component: () => import('@/views/detectionsNew/Index')
|
||||
path: '/detectionPolicy/create',
|
||||
component: () => import('@/views/detections/detectionPolicies/PolicyForm')
|
||||
},
|
||||
{
|
||||
path: '/detection/policies',
|
||||
component: () => import('@/views/detectionsNew/Index')
|
||||
},
|
||||
{
|
||||
path: '/detection/policies/create',
|
||||
component: () => import('@/views/detectionsNew/DetectionForm')
|
||||
path: '/detectionPolicy/edit',
|
||||
component: () => import('@/views/detections/detectionPolicies/PolicyForm')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -60,6 +60,29 @@ const panel = {
|
||||
routerHistoryList: [], // 路由跳转记录列表
|
||||
dnsQtypeMapData: [],
|
||||
dnsRcodeMapData: [],
|
||||
scoreBase: {
|
||||
isReady: false,
|
||||
establishLatencyMs: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
httpResponseLatency: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
sslConLatency: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
tcpLostlenPercent: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
pktRetransPercent: {
|
||||
p10: null,
|
||||
p90: null
|
||||
}
|
||||
},
|
||||
chartTabList: null // chartTabs组件的tab状态点击列表,初始化为null方便原有逻辑计算
|
||||
},
|
||||
mutations: {
|
||||
@@ -153,6 +176,56 @@ const panel = {
|
||||
setRouterHistoryList (state, list) {
|
||||
state.routerHistoryList = list
|
||||
},
|
||||
resetScoreBase (state) {
|
||||
state.scoreBase = {
|
||||
isReady: false,
|
||||
establishLatencyMs: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
httpResponseLatency: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
sslConLatency: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
tcpLostlenPercent: {
|
||||
p10: null,
|
||||
p90: null
|
||||
},
|
||||
pktRetransPercent: {
|
||||
p10: null,
|
||||
p90: null
|
||||
}
|
||||
}
|
||||
},
|
||||
setScoreBase (state, scoreBase) {
|
||||
state.scoreBase = {
|
||||
isReady: true,
|
||||
establishLatencyMs: {
|
||||
p10: scoreBase.establishLatencyMsP10,
|
||||
p90: scoreBase.establishLatencyMsP90
|
||||
},
|
||||
httpResponseLatency: {
|
||||
p10: scoreBase.httpResponseLatencyP10,
|
||||
p90: scoreBase.httpResponseLatencyP90
|
||||
},
|
||||
sslConLatency: {
|
||||
p10: scoreBase.sslConLatencyP10,
|
||||
p90: scoreBase.sslConLatencyP90
|
||||
},
|
||||
tcpLostlenPercent: {
|
||||
p10: scoreBase.tcpLostlenPercentP10,
|
||||
p90: scoreBase.tcpLostlenPercentP90
|
||||
},
|
||||
pktRetransPercent: {
|
||||
p10: scoreBase.pktRetransPercentP10,
|
||||
p90: scoreBase.pktRetransPercentP90
|
||||
}
|
||||
}
|
||||
},
|
||||
setChartTabList (state, list) {
|
||||
state.chartTabList = list
|
||||
}
|
||||
@@ -232,6 +305,12 @@ const panel = {
|
||||
},
|
||||
getChartTabList (state) {
|
||||
return state.chartTabList
|
||||
},
|
||||
scoreBaseReady (state) {
|
||||
return state.scoreBase.isReady
|
||||
},
|
||||
getScoreBase (state) {
|
||||
return state.scoreBase
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
@@ -118,7 +118,7 @@ const user = {
|
||||
localStorage.setItem(storageKey.linkInfo, res.page.list[0].cvalue)
|
||||
}
|
||||
})
|
||||
axios.get(api.config, { params: { ckey: 'schema_entity_explore' } }).then(response => {
|
||||
axios.get(api.config, { params: { ckey: 'schema_explore' } }).then(response => {
|
||||
const res = response.data
|
||||
if (response.status === 200 && res.page.list && res.page.list.length > 0) {
|
||||
localStorage.setItem(storageKey.schemaEntityExplore, res.page.list[0].cvalue)
|
||||
|
||||
@@ -38,9 +38,12 @@ export const api = {
|
||||
knowledgeBase: apiVersion + '/knowledgeBase',
|
||||
knowledgeBaseList: apiVersion + '/knowledgeBase/list',
|
||||
knowledgeBaseEnable: apiVersion + '/knowledgeBase/status',
|
||||
knowledgeBaseLearningStart: apiVersion + '/knowledgeBase/intelligence-learning/start',
|
||||
knowledgeBaseLearningStop: apiVersion + '/knowledgeBase/intelligence-learning/stop',
|
||||
knowledgeBaseStatistics: apiVersion + '/knowledgeBase/statistics',
|
||||
updateKnowledgeUrl: apiVersion + '/knowledgeBase/items/batch',
|
||||
knowledgeBaseLog: apiVersion + '/knowledgeBase/audit/log',
|
||||
knowledgeBaseTimedistribution: apiVersion + '/knowledgeBase/{{knowledgeId}}/{{type}}/timedistribution',
|
||||
|
||||
// 报告相关
|
||||
reportJob: '/report/job',
|
||||
@@ -123,7 +126,20 @@ export const api = {
|
||||
listBasic: '/interface/detection/security/list/basic',
|
||||
listCount: '/interface/detection/security/list/count',
|
||||
overviewBasic: '/interface/detection/security/detail/overview/basic',
|
||||
overviewEvent: '/interface/detection/security/detail/overview/event'
|
||||
overviewEvent: '/interface/detection/security/detail/overview/event',
|
||||
securityList: apiVersion + '/detection/security/list', // 安全事件列表
|
||||
timeDistribution: apiVersion + '/detection/security/severity/timedistribution', // 事件严重等级分布(顶部柱状图)
|
||||
severityStatistics: apiVersion + '/detection/security/severity/statistics', // 事件严重等级统计(左侧filter事件严重等级和饼图)
|
||||
statusStatistics: apiVersion + '/detection/security/status/statistics', // 事件状态统计
|
||||
eventTypeStatistics: apiVersion + '/detection/security/event-type/statistics', // 事件类型统计
|
||||
offenderIpStatistics: apiVersion + '/detection/security/offender-ip/statistics', // 攻击者IP统计
|
||||
victimIpStatistics: apiVersion + '/detection/security/victim-ip/statistics', // 受害者IP统计
|
||||
relationEvent: apiVersion + '/detection/security/ip/relation/event', // IP相关近期事件
|
||||
securityCount: apiVersion + '/detection/security/count', // 安全事件总数
|
||||
detail: apiVersion + '/detection/security/entity/detail', // 安全事件实体详情,后面得加上实体类型
|
||||
ipDetail: apiVersion + '/detection/security/entity/detail/ip', // 安全事件实体详情ip响应
|
||||
domainDetail: apiVersion + '/detection/security/entity/detail/domain', // 安全事件实体详情domain响应
|
||||
appDetail: apiVersion + '/detection/security/entity/detail/app' // 安全事件实体详情app响应
|
||||
},
|
||||
performanceEvent: {
|
||||
eventSeverityTrend: '/interface/detection/performance/filter/severityTrend',
|
||||
@@ -139,13 +155,13 @@ export const api = {
|
||||
},
|
||||
list: apiVersion + '/rule/detection/list', // 检测规则列表
|
||||
detail: apiVersion + '/rule/detection', // 检测规则详情
|
||||
delete: apiVersion + '/rule', // 检测规则删除
|
||||
delete: apiVersion + '/rule/detection', // 检测规则删除
|
||||
// 获取单位列表,如source、type、metric等
|
||||
statistics: apiVersion + '/detection/statistics',
|
||||
statistics: apiVersion + '/rule/detection/statistics',
|
||||
// 规则新建模块
|
||||
create: {
|
||||
topKeys: apiVersion + '/detection/topKeys', // topKeys列表
|
||||
create: apiVersion + '/rule/detection/create' // todo 规则新建编辑,此api为模拟,后续需要修改
|
||||
create: apiVersion + '/rule/detection'
|
||||
}
|
||||
},
|
||||
// Dashboard
|
||||
@@ -248,6 +264,7 @@ export const api = {
|
||||
throughput: apiVersion + '/entity/detail/traffic/throughput',
|
||||
security: apiVersion + '/entity/detail/event/security',
|
||||
performance: apiVersion + '/entity/detail/event/performance',
|
||||
behaviorPattern: apiVersion + '/entity/detail/behavior/ip',
|
||||
// 域名解析:ip相关app、domain
|
||||
domainNameResolutionAboutAppsOfIp: apiVersion + '/entity/detail/ip/relate/apps',
|
||||
domainNameResolutionAboutDomainsOfIp: apiVersion + '/entity/detail/ip/relate/domains',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -13,11 +13,17 @@ export function getMillisecond (time) {
|
||||
ms = window.$dayJs.tz(new Date(time)).valueOf()
|
||||
} else if (_.isNumber(time)) {
|
||||
const timeStr = _.toString(time)
|
||||
const difference = timeStr.length - 13
|
||||
/* const difference = timeStr.length - 13
|
||||
if (difference >= 0) {
|
||||
ms = window.$dayJs.tz(new Date(Number(timeStr.slice(0, 13)))).valueOf()
|
||||
} else {
|
||||
ms = window.$dayJs.tz(new Date(Math.floor(time * (10 ** (0 - difference))))).valueOf()
|
||||
} */
|
||||
// 判断9位和10位数为秒,12位和13位为毫秒。其他位数不做处理
|
||||
if (timeStr.length === 9 || timeStr.length === 10) {
|
||||
ms = window.$dayJs.tz(new Date(Number(time * 1000))).valueOf()
|
||||
} else {
|
||||
ms = window.$dayJs.tz(new Date(Number(time))).valueOf()
|
||||
}
|
||||
} else if (_.isString(time)) {
|
||||
try {
|
||||
@@ -43,7 +49,7 @@ export function rTime (date) {
|
||||
}
|
||||
// 日期转换为时间戳
|
||||
export function toTime (date) {
|
||||
return new Date(date).getTime()
|
||||
return new Date(parseFloat(date)).getTime()
|
||||
}
|
||||
// 时间格式转换
|
||||
export function dateFormat (date, format = 'YYYY-MM-DD HH:mm:ss') {
|
||||
@@ -121,9 +127,9 @@ export function xAxisTimeFormatter (value) {
|
||||
':' +
|
||||
(date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes())
|
||||
// 如果是一天的开始
|
||||
if (date.getTime() === dayStart.getTime()) {
|
||||
if (getSecond(date.getTime()) === getSecond(dayStart.getTime())) {
|
||||
return '{day|' + dateFormat(date, 'YYYY-MM-DD') + '}'
|
||||
} else if (date.getTime() === hourStart.getTime()) {
|
||||
} else if (getSecond(date.getTime()) === getSecond(hourStart.getTime())) {
|
||||
return '{hour|' + HHmm + '}'
|
||||
} else {
|
||||
return HHmm
|
||||
@@ -137,3 +143,69 @@ export const xAxisTimeRich = {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
|
||||
function switchDateTypeByStr (str) {
|
||||
switch (str) {
|
||||
// case 'Y':
|
||||
// return 'years'
|
||||
// case 'M':
|
||||
// return 'months'
|
||||
// case 'W':
|
||||
// return 'weeks'
|
||||
case 'D':
|
||||
return 'days'
|
||||
case 'H':
|
||||
return 'hours'
|
||||
case 'M':
|
||||
return 'minutes'
|
||||
case 'S':
|
||||
return 'seconds'
|
||||
}
|
||||
}
|
||||
|
||||
function switchValueByDateType (str) {
|
||||
switch (str) {
|
||||
// case 'years':
|
||||
// return 'Y'
|
||||
// case 'months':
|
||||
// return 'M'
|
||||
// case 'weeks':
|
||||
// return 'W'
|
||||
case 'days':
|
||||
return 'D'
|
||||
case 'hours':
|
||||
return 'H'
|
||||
case 'minutes':
|
||||
return 'M'
|
||||
case 'seconds':
|
||||
return 'S'
|
||||
}
|
||||
}
|
||||
|
||||
export function getTimeByDurations (str) {
|
||||
if (str && str.indexOf('P') === 0) {
|
||||
const obj = {
|
||||
type: '',
|
||||
value: ''
|
||||
}
|
||||
for (let i = 1; i < str.length; i++) {
|
||||
const item = str[i]
|
||||
// P5D表示持续5天,PT5M持续5分钟,T是位于时间分量之前的时间指示符,即T之前是年月周日,T之后是时分秒
|
||||
if (isNaN(item) && item !== 'T') {
|
||||
obj.type = switchDateTypeByStr(item)
|
||||
} else if (!isNaN(item)) {
|
||||
obj.value += item
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
export function getDurationsTimeByType (number, type) {
|
||||
let T = ''
|
||||
if (['hours', 'minutes', 'seconds'].indexOf(type) > -1) {
|
||||
T = 'T'
|
||||
}
|
||||
return `P${T}${number}${switchValueByDateType(type)}`
|
||||
}
|
||||
|
||||
@@ -15,42 +15,42 @@ _this.$t = _this.t
|
||||
export const dataForNpmTrafficLine = {
|
||||
tabs: [
|
||||
{
|
||||
name: _this.$t('network.total'),
|
||||
name: 'network.total',
|
||||
show: true,
|
||||
positioning: 0,
|
||||
data: [],
|
||||
unitType: 'number'
|
||||
},
|
||||
{
|
||||
name: _this.$t('network.inbound'),
|
||||
name: 'network.inbound',
|
||||
show: true,
|
||||
positioning: 1,
|
||||
data: [],
|
||||
unitType: 'number'
|
||||
},
|
||||
{
|
||||
name: _this.$t('network.outbound'),
|
||||
name: 'network.outbound',
|
||||
show: true,
|
||||
positioning: 2,
|
||||
data: [],
|
||||
unitType: 'number'
|
||||
},
|
||||
{
|
||||
name: _this.$t('network.internal'),
|
||||
name: 'network.internal',
|
||||
show: true,
|
||||
positioning: 3,
|
||||
data: [],
|
||||
unitType: 'number'
|
||||
},
|
||||
{
|
||||
name: _this.$t('network.through'),
|
||||
name: 'network.through',
|
||||
show: true,
|
||||
positioning: 4,
|
||||
data: [],
|
||||
unitType: 'number'
|
||||
},
|
||||
{
|
||||
name: _this.$t('network.other'),
|
||||
name: 'network.other',
|
||||
show: true,
|
||||
positioning: 5,
|
||||
data: [],
|
||||
@@ -58,21 +58,21 @@ export const dataForNpmTrafficLine = {
|
||||
}
|
||||
],
|
||||
npmQuantity: [
|
||||
{ name: _this.$t('networkAppPerformance.tcpConnectionEstablishLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 },
|
||||
{ name: _this.$t('networkAppPerformance.httpResponse'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 },
|
||||
{ name: _this.$t('networkAppPerformance.sslResponseLatency'), show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 },
|
||||
{ name: _this.$t('networkAppPerformance.packetLoss'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 },
|
||||
{ name: _this.$t('overall.packetRetrans'), show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
|
||||
{ name: 'networkAppPerformance.tcpConnectionEstablishLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 0 },
|
||||
{ name: 'networkAppPerformance.httpResponse', show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 1 },
|
||||
{ name: 'networkAppPerformance.sslResponseLatency', show: true, positioning: 0, data: [], unitType: unitTypes.time, index: 2 },
|
||||
{ name: 'networkAppPerformance.packetLoss', show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 3 },
|
||||
{ name: 'overall.packetRetrans', show: true, positioning: 0, data: [], unitType: unitTypes.percent, index: 4 }
|
||||
],
|
||||
metricOptions: [
|
||||
/* { value: 'Bits/s', label: 'Bits/s' },
|
||||
{ value: 'Packets/s', label: 'Packets/s' },
|
||||
{ value: 'Sessions/s', label: 'Sessions/s' }, */
|
||||
{ value: 'establishLatencyMs', label: _this.$t('networkAppPerformance.tcpConnectionEstablishLatency') },
|
||||
{ value: 'httpResponseLatency', label: _this.$t('networkAppPerformance.httpResponse') },
|
||||
{ value: 'sslConLatency', label: _this.$t('networkAppPerformance.sslResponseLatency') },
|
||||
{ value: 'tcpLostlenPercent', label: _this.$t('networkAppPerformance.packetLoss') },
|
||||
{ value: 'pktRetransPercent', label: _this.$t('overall.packetRetrans') }
|
||||
{ value: 'establishLatencyMs', label: 'networkAppPerformance.tcpConnectionEstablishLatency' },
|
||||
{ value: 'httpResponseLatency', label: 'networkAppPerformance.httpResponse' },
|
||||
{ value: 'sslConLatency', label: 'networkAppPerformance.sslResponseLatency' },
|
||||
{ value: 'tcpLostlenPercent', label: 'networkAppPerformance.packetLoss' },
|
||||
{ value: 'pktRetransPercent', label: 'overall.packetRetrans' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -175,9 +175,9 @@ export const dataForLinkTrafficLine = {
|
||||
|
||||
export const dataForNpmLine = {
|
||||
chartOptionLineData: [
|
||||
{ legend: _this.$t('network.total'), index: 0, invertTab: true, show: false, color: '#749F4D' },
|
||||
{ legend: _this.$t('network.inbound'), index: 1, invertTab: true, show: false, color: '#98709B' },
|
||||
{ legend: _this.$t('network.outbound'), index: 2, invertTab: true, show: false, color: '#E5A219' }
|
||||
{ legend: 'network.total', index: 0, invertTab: true, show: false, color: '#749F4D' },
|
||||
{ legend: 'network.inbound', index: 1, invertTab: true, show: false, color: '#98709B' },
|
||||
{ legend: 'network.outbound', index: 2, invertTab: true, show: false, color: '#E5A219' }
|
||||
],
|
||||
npmLineColor: [
|
||||
{ legend: '', color: '#749F4D' },
|
||||
@@ -221,27 +221,27 @@ export const dataForDnsTrafficLine = {
|
||||
export const dataForNpmEventsHeader = {
|
||||
chartData: [
|
||||
{
|
||||
eventSeverity: 'critical',
|
||||
eventSeverity: 'overall.critical',
|
||||
count: '-',
|
||||
index: 0
|
||||
},
|
||||
{
|
||||
eventSeverity: 'high',
|
||||
eventSeverity: 'overall.high',
|
||||
count: '-',
|
||||
index: 1
|
||||
},
|
||||
{
|
||||
eventSeverity: 'medium',
|
||||
eventSeverity: 'overall.medium',
|
||||
count: '-',
|
||||
index: 2
|
||||
},
|
||||
{
|
||||
eventSeverity: 'low',
|
||||
eventSeverity: 'overall.low',
|
||||
count: '-',
|
||||
index: 3
|
||||
},
|
||||
{
|
||||
eventSeverity: 'info',
|
||||
eventSeverity: 'overall.info',
|
||||
count: '-',
|
||||
index: 4
|
||||
}
|
||||
@@ -334,11 +334,91 @@ export const columnList1 = [
|
||||
}
|
||||
}
|
||||
]
|
||||
const securityEvent = [
|
||||
{
|
||||
name: 'event_type',
|
||||
type: 'string',
|
||||
label: 'event_type',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'event_name',
|
||||
type: 'string',
|
||||
label: 'event_name',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'severity',
|
||||
type: 'string',
|
||||
label: 'severity',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'offender_ip',
|
||||
type: 'string',
|
||||
label: 'offender Ip',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'victim_ip',
|
||||
type: 'string',
|
||||
label: 'victim Ip',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'domain',
|
||||
type: 'string',
|
||||
label: 'domain',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'app',
|
||||
type: 'string',
|
||||
label: 'app',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in,like'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
let schemaEntityExplore = localStorage.getItem(storageKey.schemaEntityExplore)
|
||||
schemaEntityExplore = schemaEntityExplore ? JSON.parse(schemaEntityExplore).entityMetadata.searchColumns : columnList1
|
||||
const schema = localStorage.getItem(storageKey.schemaEntityExplore)
|
||||
const schemaEntityExplore = schema ? JSON.parse(schema).entityMetadata.searchColumns : columnList1
|
||||
export const columnList = schemaEntityExplore
|
||||
|
||||
let securityEventMetadata = securityEvent
|
||||
if (schema) {
|
||||
if (JSON.parse(schema).securityEventMetadata) {
|
||||
securityEventMetadata = JSON.parse(schema).securityEventMetadata.searchColumns
|
||||
}
|
||||
}
|
||||
export const schemaDetectionSecurity = securityEventMetadata
|
||||
|
||||
export const operatorList = ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE']
|
||||
export const connectionList = [
|
||||
{
|
||||
|
||||
@@ -784,7 +784,7 @@ export function getChainRatio (current, prev) {
|
||||
}
|
||||
}
|
||||
|
||||
export function computeScore (data) {
|
||||
export function computeScore (data, scoreBase) {
|
||||
let score = 0
|
||||
let k = 0
|
||||
let totalScore = 0
|
||||
@@ -799,26 +799,14 @@ export function computeScore (data) {
|
||||
} else if (t === 'httpResponseLatency' || t === 'sslConLatency') {
|
||||
k = 0.05
|
||||
}
|
||||
if (t === 'establishLatencyMs' || t === 'httpResponseLatency' || t === 'sslConLatency') {
|
||||
if (!data[t] && data[t] !== 0) {
|
||||
score = 1
|
||||
} else if (data[t] <= 50) {
|
||||
score = 1
|
||||
} else if (data[t] > 200) {
|
||||
score = 0
|
||||
} else {
|
||||
score = (data[t] - 200) / (50 - 200)
|
||||
}
|
||||
} else if (t === 'tcpLostlenPercent' || t === 'pktRetransPercent') {
|
||||
if (!data[t] && data[t] !== 0) {
|
||||
score = 1
|
||||
} else if (data[t] <= 0.01) {
|
||||
score = 1
|
||||
} else if (data[t] > 0.05) {
|
||||
score = 0
|
||||
} else {
|
||||
score = (data[t] - 0.05) / (0.01 - 0.05)
|
||||
}
|
||||
if (!data[t] && data[t] !== 0) {
|
||||
score = 1
|
||||
} else if (data[t] <= scoreBase[t].p10) {
|
||||
score = 1
|
||||
} else if (data[t] >= scoreBase[t].p90) {
|
||||
score = 0
|
||||
} else {
|
||||
score = (data[t] - scoreBase[t].p90) / (scoreBase[t].p10 - scoreBase[t].p90)
|
||||
}
|
||||
scoreArr.push(score * k)
|
||||
})
|
||||
@@ -1318,12 +1306,13 @@ export function numberWithCommas (num) {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function switchStatus (status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'Disabled'
|
||||
case 1:
|
||||
return 'Enabled'
|
||||
switch (status + '') {
|
||||
case '0':
|
||||
return 'detection.create.disabled'
|
||||
case '1':
|
||||
return 'detection.create.enabled'
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1356,3 +1345,18 @@ export function beforeRouterPush () {
|
||||
}
|
||||
store.commit('setRouterHistoryList', historyList)
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置tag颜色,此为接口返回的颜色字段,color为rgb格式
|
||||
* @param color
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getTagColor (color) {
|
||||
if (color) {
|
||||
let backgroundColor = ''
|
||||
if (color.indexOf('rgb(') > -1) {
|
||||
backgroundColor = color.replace('rgb(', 'rgba(').replace(')', ',0.06)')
|
||||
}
|
||||
return `color: ${color};border-color: ${color};background-color: ${backgroundColor};`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
<template>
|
||||
<div style="height: 100%;">
|
||||
<cn-data-list
|
||||
ref="dataList"
|
||||
:tableId="tableId"
|
||||
v-model:custom-table-title="tools.customTableTitle"
|
||||
:api="url"
|
||||
:from="fromRoute.chart"
|
||||
:layout="['columnCustomize','elementSet','search']"
|
||||
@search="search"
|
||||
>
|
||||
<template #top-tool-left>
|
||||
<button id="chart-add"
|
||||
class="top-tool-btn margin-r-10 top-tool-btn--create"
|
||||
@click="add">
|
||||
<i class="cn-icon-xinjian cn-icon"></i>
|
||||
<span>{{$t('overall.create')}}</span>
|
||||
</button>
|
||||
<button id="chart-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
|
||||
@click="editSelectRecord">
|
||||
<i class="cn-icon-edit cn-icon"></i>
|
||||
<span>{{$t('overall.edit')}}</span>
|
||||
</button>
|
||||
<button id="chart-delete" class="top-tool-btn margin-r-10" :disabled="disableDelete"
|
||||
@click="delBatch">
|
||||
<i class="cn-icon-delete cn-icon"></i>
|
||||
<span>{{$t('overall.delete')}}</span>
|
||||
</button>
|
||||
</template>
|
||||
<template #default>
|
||||
<loading :loading="loading"></loading>
|
||||
<chart-table
|
||||
ref="dataTable"
|
||||
:api="url"
|
||||
:isNoData="isNoData"
|
||||
:custom-table-title="tools.customTableTitle"
|
||||
:height="mainTableHeight"
|
||||
:table-data="tableData"
|
||||
@delete="del"
|
||||
@edit="edit"
|
||||
@orderBy="tableDataSort"
|
||||
@reload="getTableData"
|
||||
@selectionChange="selectionChange"
|
||||
/>
|
||||
</template>
|
||||
<template #pagination>
|
||||
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
|
||||
</template>
|
||||
</cn-data-list>
|
||||
<el-drawer
|
||||
v-model="rightBox.show"
|
||||
direction="rtl"
|
||||
custom-class="common-right-box"
|
||||
:size="700"
|
||||
:with-header="false"
|
||||
destroy-on-close>
|
||||
<chart-box
|
||||
:object="object"
|
||||
@close="closeRightBox"
|
||||
/>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import cnDataList from '@/components/table/CnDataList'
|
||||
import dataListMixin from '@/mixins/data-list'
|
||||
import chartTable from '@/components/table/administration/ChartTable'
|
||||
import chartBox from '@/components/rightBox/settings/ChartBox'
|
||||
import { api } from '@/utils/api'
|
||||
|
||||
export default {
|
||||
name: 'Chart',
|
||||
mixins: [dataListMixin],
|
||||
components: {
|
||||
cnDataList,
|
||||
chartTable,
|
||||
chartBox
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
url: api.chart,
|
||||
listUrl: api.chartList,
|
||||
blankObject: { // 空白对象
|
||||
id: '',
|
||||
name: '',
|
||||
params: '',
|
||||
i18n: ''
|
||||
},
|
||||
tableId: 'chartTable'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,159 +0,0 @@
|
||||
<template>
|
||||
<div style="height: 100%;">
|
||||
<cn-data-list
|
||||
ref="dataList"
|
||||
:tableId="tableId"
|
||||
v-model:custom-table-title="tools.customTableTitle"
|
||||
:api="url"
|
||||
:from="fromRoute.galaxyProxy"
|
||||
:layout="['columnCustomize','elementSet','search']"
|
||||
@search="search"
|
||||
>
|
||||
<template v-slot:top-tool-left>
|
||||
<button id="galaxy-proxy-clear-cache" class="top-tool-btn margin-r-10" :title="$t('overall.clearCache')"
|
||||
type="button" @click="clearCache">
|
||||
<i class="cn-icon cn-icon-clear-cache"></i>
|
||||
</button>
|
||||
<button id="galaxy-proxy-add"
|
||||
class="top-tool-btn margin-r-10 top-tool-btn--create"
|
||||
@click="add">
|
||||
<i class="cn-icon-xinjian cn-icon"></i>
|
||||
<span>{{$t('overall.create')}}</span>
|
||||
</button>
|
||||
<button id="galaxy-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
|
||||
@click="editSelectRecord">
|
||||
<i class="cn-icon-edit cn-icon"></i>
|
||||
<span>{{$t('overall.edit')}}</span>
|
||||
</button>
|
||||
<button id="galaxy-delete" class="top-tool-btn margin-r-10"
|
||||
@click="delBatch">
|
||||
<i class="cn-icon-delete cn-icon"></i>
|
||||
<span>{{$t('overall.delete')}}</span>
|
||||
</button>
|
||||
<button id="galaxy-proxy-debug" class="top-tool-btn margin-r-10" :title="$t('overall.debug')"
|
||||
type="button" @click="debug(true,{})">
|
||||
<i class="cn-icon-category cn-icon"></i>
|
||||
</button>
|
||||
<top-tool-more-options
|
||||
ref="export"
|
||||
id="model"
|
||||
:params="searchLabel"
|
||||
class="top-tool-export margin-l-10 margin-r-10"
|
||||
export-file-name="galaxyProxy"
|
||||
export-url="/galaxy/setting/export"
|
||||
import-url="/galaxy/setting/import"
|
||||
@afterImport="getTableData"
|
||||
>
|
||||
<template v-slot:before>
|
||||
</template>
|
||||
</top-tool-more-options>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<loading :loading="loading"></loading>
|
||||
<galaxy-proxy-table
|
||||
ref="dataTable"
|
||||
:api="url"
|
||||
:isNoData="isNoData"
|
||||
:custom-table-title="tools.customTableTitle"
|
||||
:height="mainTableHeight"
|
||||
:table-data="tableData"
|
||||
@delete="del"
|
||||
@edit="edit"
|
||||
@orderBy="tableDataSort"
|
||||
@reload="getTableData"
|
||||
@selectionChange="selectionChange"
|
||||
@copy="copy"
|
||||
@debug="debugRow"
|
||||
></galaxy-proxy-table>
|
||||
</template>
|
||||
<!-- 分页组件 -->
|
||||
<template #pagination>
|
||||
<pagination ref="pagination" :page-obj="pageObj" :table-id="tableId" @pageNo='pageNo' @pageSize='pageSize'></pagination>
|
||||
</template>
|
||||
</cn-data-list>
|
||||
<el-drawer
|
||||
v-model="rightBox.show"
|
||||
direction="rtl"
|
||||
custom-class="common-right-box"
|
||||
:size="700"
|
||||
:with-header="false"
|
||||
destroy-on-close>
|
||||
<galaxy-proxy-box :object="object" @close="closeRightBox"></galaxy-proxy-box>
|
||||
</el-drawer>
|
||||
</div>
|
||||
<galaxy-proxy-debug
|
||||
v-model:show-debug="showDebug"
|
||||
top="5vh"
|
||||
:show-close="false"
|
||||
:curGalaxyProxy="curGalaxyProxy"></galaxy-proxy-debug>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import cnDataList from '@/components/table/CnDataList'
|
||||
import galaxyProxyBox from '@/components/rightBox/settings/GalaxyProxyBox'
|
||||
import galaxyProxyTable from '@/components/table/administration/GalaxyProxyTable'
|
||||
import dataListMixin from '@/mixins/data-list'
|
||||
import { api } from '@/utils/api'
|
||||
import axios from 'axios'
|
||||
import TopToolMoreOptions from '@/components/common/popBox/TopToolMoreOptions'
|
||||
import galaxyProxyDebug from '@/components/setting/GalaxyProxyDebug'
|
||||
|
||||
export default {
|
||||
name: 'GalaxyProxy',
|
||||
components: {
|
||||
cnDataList,
|
||||
galaxyProxyBox,
|
||||
galaxyProxyTable,
|
||||
TopToolMoreOptions,
|
||||
galaxyProxyDebug
|
||||
},
|
||||
mixins: [dataListMixin],
|
||||
data () {
|
||||
return {
|
||||
url: api.galaxyProxy,
|
||||
tableId: 'galaxySettingTable', // 需要分页的table的id,用于记录每页数量
|
||||
blankObject: { // 空白对象
|
||||
name: ''
|
||||
},
|
||||
showDebug: false,
|
||||
curGalaxyProxy: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
edit (u) {
|
||||
axios.get(`${this.url}/${u.id}`).then(response => {
|
||||
if (response.status === 200) {
|
||||
const editObject = response.data.data
|
||||
editObject.targetHeader || (editObject.targetHeader = '')
|
||||
editObject.preHandle || (editObject.preHandle = '')
|
||||
editObject.postHandle || (editObject.postHandle = '')
|
||||
editObject.targetParam || (editObject.targetParam = '')
|
||||
this.object = editObject
|
||||
this.rightBox.show = true
|
||||
}
|
||||
})
|
||||
},
|
||||
debug (isTopDebug, u) {
|
||||
if (!isTopDebug && u) {
|
||||
this.curGalaxyProxy = JSON.parse(JSON.stringify(u))
|
||||
}
|
||||
this.showDebug = true
|
||||
},
|
||||
debugRow (u) {
|
||||
this.debug(false, u)
|
||||
},
|
||||
clearCache () {
|
||||
axios.put(`${this.url}/clearCache`).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.success') })
|
||||
} else {
|
||||
this.$message.error(response.data.message)
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$message.error(this.$t('tip.unknownError'))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -58,7 +58,7 @@
|
||||
size="mini"
|
||||
v-model="table.limit"
|
||||
class="option__select select-topn"
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="option-popper"
|
||||
@change="tableLimitChange"
|
||||
>
|
||||
@@ -125,7 +125,7 @@
|
||||
size="mini"
|
||||
v-model="copyOrderPieTable"
|
||||
class="option__select select-column"
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="option-popper"
|
||||
@change="orderPieTableChange"
|
||||
>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="metric"
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
v-if="showMetric"
|
||||
:popper-append-to-body="false"
|
||||
@@ -104,7 +104,9 @@ export default {
|
||||
dnsRcodeMapData: [],
|
||||
dnsQtypeMapData: [],
|
||||
score: null,
|
||||
curTabState: curTabState
|
||||
curTabState: curTabState,
|
||||
performanceData: {},
|
||||
scoreDataState: false // 评分数据是否加载完成
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -114,22 +116,32 @@ export default {
|
||||
// 显示顶部的Metric单位选项标识
|
||||
showMetric () {
|
||||
return this.panelType === panelTypeAndRouteMapping.networkOverview || this.panelType === panelTypeAndRouteMapping.networkOverviewDrillDown
|
||||
},
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// npmThirdLevelMenuScore: {
|
||||
// deep: true,
|
||||
// immediate: true,
|
||||
// handler (n) {
|
||||
// this.score = n
|
||||
// }
|
||||
// }
|
||||
timeFilter: {
|
||||
handler () {
|
||||
if (this.$route.path === '/panel/networkAppPerformance' && (this.lineQueryCondition || this.networkOverviewBeforeTab)) {
|
||||
this.scoreCalculation()
|
||||
if (this.$route.path === '/panel/networkAppPerformance') {
|
||||
this.$store.commit('resetScoreBase')
|
||||
this.queryScoreBase()
|
||||
if (this.lineQueryCondition || this.networkOverviewBeforeTab) {
|
||||
this.scoreCalculation()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && this.scoreDataState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
},
|
||||
scoreDataState (n) {
|
||||
if (n && this.scoreBaseState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
@@ -212,8 +224,14 @@ export default {
|
||||
return chart
|
||||
})
|
||||
})
|
||||
if (this.$route.path === '/panel/networkAppPerformance' && (this.lineQueryCondition || this.networkOverviewBeforeTab)) {
|
||||
this.scoreCalculation()
|
||||
if (this.$route.path === '/panel/networkAppPerformance') {
|
||||
if (this.lineQueryCondition || this.networkOverviewBeforeTab) {
|
||||
this.scoreCalculation()
|
||||
}
|
||||
}
|
||||
if (this.$route.path === '/panel/networkAppPerformance' || this.$route.path === '/panel/linkMonitor') {
|
||||
this.$store.commit('resetScoreBase')
|
||||
this.queryScoreBase()
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
@@ -396,6 +414,44 @@ export default {
|
||||
})
|
||||
overwriteUrl(newUrl)
|
||||
},
|
||||
// 动态查询评分基准
|
||||
queryScoreBase () {
|
||||
const params = {
|
||||
startTime: this.timeFilter.startTime,
|
||||
endTime: this.timeFilter.endTime
|
||||
}
|
||||
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params })
|
||||
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params })
|
||||
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params })
|
||||
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params })
|
||||
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params })
|
||||
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
|
||||
const scoreBase = {}
|
||||
res.forEach((t, i) => {
|
||||
if (t.status === 200) {
|
||||
if (i === 0) {
|
||||
scoreBase.establishLatencyMsP10 = _.get(t.data, 'data.result.establishLatencyMsP10', null)
|
||||
scoreBase.establishLatencyMsP90 = _.get(t.data, 'data.result.establishLatencyMsP90', null)
|
||||
} else if (i === 1) {
|
||||
scoreBase.httpResponseLatencyP10 = _.get(t.data, 'data.result.httpResponseLatencyP10', null)
|
||||
scoreBase.httpResponseLatencyP90 = _.get(t.data, 'data.result.httpResponseLatencyP90', null)
|
||||
} else if (i === 2) {
|
||||
scoreBase.sslConLatencyP10 = _.get(t.data, 'data.result.sslConLatencyP10', null)
|
||||
scoreBase.sslConLatencyP90 = _.get(t.data, 'data.result.sslConLatencyP90', null)
|
||||
} else if (i === 3) {
|
||||
scoreBase.tcpLostlenPercentP10 = _.get(t.data, 'data.result.tcpLostlenPercentP10', null)
|
||||
scoreBase.tcpLostlenPercentP90 = _.get(t.data, 'data.result.tcpLostlenPercentP90', null)
|
||||
} else if (i === 4) {
|
||||
scoreBase.pktRetransPercentP10 = _.get(t.data, 'data.result.pktRetransPercentP10', null)
|
||||
scoreBase.pktRetransPercentP90 = _.get(t.data, 'data.result.pktRetransPercentP90', null)
|
||||
}
|
||||
}
|
||||
})
|
||||
this.$store.commit('setScoreBase', scoreBase)
|
||||
}).catch((e) => {
|
||||
}).finally(() => {
|
||||
})
|
||||
},
|
||||
scoreCalculation () {
|
||||
let condition = ''
|
||||
let url = ''
|
||||
@@ -443,18 +499,21 @@ export default {
|
||||
url = api.npm.overview.networkAnalysis
|
||||
}
|
||||
if ((type && condition) || type) {
|
||||
this.scoreDataState = false
|
||||
this.performanceData = {}
|
||||
params.type = params.type || type
|
||||
axios.get(url, { params }).then(res => {
|
||||
if (res.status === 200) {
|
||||
const data = {
|
||||
this.performanceData = {
|
||||
establishLatencyMs: _.get(res, 'data.data.result.establishLatencyMsAvg', null),
|
||||
httpResponseLatency: _.get(res, 'data.data.result.httpResponseLatencyAvg', null),
|
||||
sslConLatency: _.get(res, 'data.data.result.sslConLatencyAvg', null),
|
||||
tcpLostlenPercent: _.get(res, 'data.data.result.tcpLostlenPercentAvg', null),
|
||||
pktRetransPercent: _.get(res, 'data.data.result.pktRetransPercentAvg', null)
|
||||
}
|
||||
this.score = computeScore(data)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.scoreDataState = true
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -467,6 +526,9 @@ export default {
|
||||
}
|
||||
})
|
||||
window.open(href, '_blank')
|
||||
},
|
||||
handleScoreData () {
|
||||
this.score = computeScore(this.performanceData, this.$store.getters.getScoreBase)
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -27,12 +27,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="line-select line-header-right">
|
||||
<div class="line-select-metric">
|
||||
<!-- <div class="line-select-metric">
|
||||
<span>{{$t('network.metric')}}:</span>
|
||||
<div class="line-select__operation">
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="lineMetric"
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="metricSelectChange"
|
||||
@@ -40,13 +41,14 @@
|
||||
<el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
<div class="line-select-reference-line">
|
||||
<span>{{$t('network.referenceLine')}}:</span>
|
||||
<div class="line-select__operation">
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="lineRefer"
|
||||
placeholder=" "
|
||||
:disabled="!lineTab"
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@@ -174,6 +176,9 @@ export default {
|
||||
if (res.status === 200) {
|
||||
this.showError = false
|
||||
this.isNoData = res.data.data.result.length === 0
|
||||
if (!active) {
|
||||
this.tabs = _.cloneDeep(dataForDnsTrafficLine.tabs)
|
||||
}
|
||||
if (this.isNoData) {
|
||||
this.lineTab = ''
|
||||
this.tabs = _.cloneDeep(dataForDnsTrafficLine.tabs)
|
||||
@@ -445,6 +450,7 @@ export default {
|
||||
this.legendSelectChange(e, 0)
|
||||
})
|
||||
this.tabs = tabs
|
||||
this.lineRefer = 'Average'
|
||||
this.echartsInit(this.tabs, true)
|
||||
} else {
|
||||
const unit = 'bps'
|
||||
@@ -464,7 +470,7 @@ export default {
|
||||
|
||||
dnsData.forEach(e => {
|
||||
e.unitType = type
|
||||
if (parseFloat(e.analysis.avg) === 0 || isNaN(parseFloat(e.analysis.avg))) {
|
||||
if (parseFloat(e.analysis.max) === 0 || isNaN(parseFloat(e.analysis.max))) {
|
||||
e.show = false
|
||||
num += 1
|
||||
} else {
|
||||
@@ -474,13 +480,18 @@ export default {
|
||||
}
|
||||
}
|
||||
if (this.lineTab === e.class) {
|
||||
if (parseFloat(e.analysis.avg) <= 0) {
|
||||
if (parseFloat(e.analysis.max) <= 0) {
|
||||
this.lineTab = ''
|
||||
this.lineRefer = ''
|
||||
this.init()
|
||||
// this.init() // 后续多关注
|
||||
}
|
||||
}
|
||||
})
|
||||
const emptyData = dnsData.filter(d => parseFloat(d.analysis.max) === 0)
|
||||
this.isNoData = emptyData.length === dnsData.length
|
||||
if (this.isNoData) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.tabs = dnsData
|
||||
// 如果三者avg都为0时,至少保证total显示
|
||||
@@ -489,10 +500,10 @@ export default {
|
||||
let ingressAvg = 0
|
||||
let egressAvg = 0
|
||||
if (ingressObj) {
|
||||
ingressAvg = parseFloat(ingressObj.analysis.avg) || 0
|
||||
ingressAvg = parseFloat(ingressObj.analysis.max) || 0
|
||||
}
|
||||
if (egressObj) {
|
||||
egressAvg = parseFloat(egressObj.analysis.avg) || 0
|
||||
egressAvg = parseFloat(egressObj.analysis.max) || 0
|
||||
}
|
||||
if ((ingressAvg + egressAvg) === 0) {
|
||||
const totalObj = dnsData.find(d => d.name === 'network.total')
|
||||
|
||||
@@ -32,7 +32,13 @@
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="entity-tags" v-if="!hideTagArea">
|
||||
<div v-for="tag in levelTwoTags" :key="tag.value" class="entity-tag" :class="`entity-tag--level-two-${tag.type}`">{{tag.value}}</div>
|
||||
<div v-for="tag in levelTwoTags"
|
||||
:key="tag.value"
|
||||
class="entity-tag"
|
||||
:class="`entity-tag--level-two-${tag.type}`"
|
||||
:style="getTagColor(tag.color)">
|
||||
{{tag.value}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分割线-->
|
||||
<div class="dividing-line"></div>
|
||||
@@ -54,10 +60,11 @@ import {
|
||||
drillDownPanelTypeMapping,
|
||||
entityType,
|
||||
entityDetailTags,
|
||||
psiphon3IpType,
|
||||
riskLevelMapping
|
||||
tagValueLabelMapping,
|
||||
riskLevelMapping,
|
||||
entityDefaultColor
|
||||
} from '@/utils/constants'
|
||||
import { selectElementText, copySelectionText } from '@/utils/tools'
|
||||
import { selectElementText, copySelectionText, getTagColor } from '@/utils/tools'
|
||||
import { ref } from 'vue'
|
||||
import i18n from '@/i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
@@ -84,16 +91,10 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tagValueHandler (k, k2, value) {
|
||||
if (k === 'psiphon3Ip') {
|
||||
if (k2 === 'type') {
|
||||
const find = psiphon3IpType.find(t => t.value === value)
|
||||
if (find) {
|
||||
return find.name
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
getTagColor,
|
||||
tagValueHandler (value) {
|
||||
const find = tagValueLabelMapping.find(t => t.value === value)
|
||||
return find ? find.name : value
|
||||
},
|
||||
getData () {
|
||||
this.toggleLoading(true)
|
||||
@@ -114,13 +115,13 @@ export default {
|
||||
Object.keys(res.data[k]).forEach(k2 => {
|
||||
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
|
||||
if (find) {
|
||||
this.levelTwoTags.push({ key: k2, value: this.tagValueHandler(k, k2, res.data[k][k2]), type: find.type })
|
||||
this.levelTwoTags.push({ key: k2, value: this.tagValueHandler(res.data[k][k2]), type: find.type })
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
if (_.isArray(res.data.userDefinedTags)) {
|
||||
this.levelTwoTags = _.concat(this.levelTwoTags, res.data.userDefinedTags.map(tag => ({ value: tag.tagValue, type: 'normal' })))
|
||||
this.levelTwoTags = _.concat(this.levelTwoTags, res.data.userDefinedTags.map(tag => ({ value: tag.tagValue, color: tag.knowledgeBase ? tag.knowledgeBase.color : entityDefaultColor })))
|
||||
}
|
||||
this.hideTagArea = _.isEmpty(this.levelTwoTags)
|
||||
this.$nextTick(() => {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="metric"
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="metricChange"
|
||||
@@ -66,6 +66,7 @@
|
||||
size="mini"
|
||||
v-model="lineRefer"
|
||||
:disabled="!lineTab"
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="referenceSelectChange"
|
||||
@@ -226,6 +227,9 @@ export default {
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.showError = false
|
||||
if (!active) {
|
||||
this.tabs = _.cloneDeep(this.tabsTemplate)
|
||||
}
|
||||
if (this.isNoData) {
|
||||
this.lineTab = ''
|
||||
this.tabs = _.cloneDeep(this.tabsTemplate)
|
||||
@@ -497,7 +501,7 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
if (data !== undefined && data.length > 0) {
|
||||
if (data && data.length > 0) {
|
||||
newData.forEach((item) => {
|
||||
item.type = getLineType(item.type)
|
||||
if (item.type === val) {
|
||||
@@ -510,6 +514,24 @@ export default {
|
||||
})
|
||||
}
|
||||
lineData.splice(0, 1)
|
||||
// TODO 下面的逻辑是判断total曲线的尾部数据,从尾往前数0值的个数,若个数大于0,所有曲线都从尾部去掉相同数量的点,最多4个
|
||||
const totalData = lineData[0]
|
||||
if (_.get(totalData, 'values', []).length > 4) {
|
||||
let count = 0
|
||||
for (let i = totalData.values.length - 1; i >= totalData.values.length - 4; i--) {
|
||||
if (totalData.values[i].length > 1 && totalData.values[i][1] === 0) {
|
||||
count++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
lineData.forEach(l => {
|
||||
l.values.splice(l.values.length - count, count)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (val === 'Sessions/s') {
|
||||
const tabs = _.cloneDeep(this.tabsTemplate)
|
||||
lineData.forEach((d, i) => {
|
||||
@@ -527,6 +549,7 @@ export default {
|
||||
})
|
||||
this.tabs = tabs
|
||||
this.$nextTick(() => {
|
||||
this.lineRefer = 'Average'
|
||||
this.echartsInit(this.tabs, true)
|
||||
})
|
||||
} else {
|
||||
@@ -544,7 +567,7 @@ export default {
|
||||
const self = this
|
||||
tabs.forEach(e => {
|
||||
e.unitType = type
|
||||
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
|
||||
if (e.name !== 'network.total' && parseFloat(e.analysis.max) === 0) {
|
||||
e.show = false
|
||||
num += 1
|
||||
} else {
|
||||
@@ -554,13 +577,18 @@ export default {
|
||||
}
|
||||
}
|
||||
if (self.lineTab === e.class) {
|
||||
if (parseFloat(e.analysis.avg) <= 0) {
|
||||
if (parseFloat(e.analysis.max) <= 0) {
|
||||
self.lineTab = ''
|
||||
self.lineRefer = ''
|
||||
self.init()
|
||||
// self.init() // 后续多测试关注
|
||||
}
|
||||
}
|
||||
})
|
||||
const emptyData = tabs.filter(d => parseFloat(d.analysis.max) === 0)
|
||||
this.isNoData = emptyData.length === tabs.length
|
||||
if (this.isNoData) {
|
||||
return true
|
||||
}
|
||||
this.tabs = tabs
|
||||
if (num === 5) {
|
||||
tabs[0].invertTab = false
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></open-port>
|
||||
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></behavior-pattern>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -28,24 +29,27 @@
|
||||
<script>
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import i18n from '@/i18n'
|
||||
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
|
||||
import { entityDetailTabsName, entityDetailTags } from '@/utils/constants'
|
||||
import { reactive, ref } from 'vue'
|
||||
import InformationAggregation from '@/views/charts2/charts/entityDetail/tabs/InformationAggregation'
|
||||
import DomainNameResolution from '@/views/charts2/charts/entityDetail/tabs/DomainNameResolution'
|
||||
import SecurityEvent from '@/views/charts2/charts/entityDetail/tabs/SecurityEvent'
|
||||
import PerformanceEvent from '@/views/charts2/charts/entityDetail/tabs/PerformanceEvent'
|
||||
import BehaviorPattern from '@/views/charts2/charts/entityDetail/tabs/BehaviorPattern'
|
||||
import OpenPort from '@/views/charts2/charts/entityDetail/tabs/OpenPort'
|
||||
import DigitalCertificate from '@/views/charts2/charts/entityDetail/tabs/DigitalCertificate'
|
||||
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
|
||||
import { useRoute } from 'vue-router'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { tagValueLabelMapping } from '../../../../utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'EntityDetailTabs',
|
||||
mixins: [chartMixin],
|
||||
components: {
|
||||
PerformanceEvent,
|
||||
BehaviorPattern,
|
||||
SecurityEvent,
|
||||
InformationAggregation,
|
||||
DomainNameResolution,
|
||||
@@ -90,6 +94,9 @@ export default {
|
||||
if (entityType !== 'app') {
|
||||
tabs.unshift({ name: entityDetailTabsName.informationAggregation, label: i18n.global.t('entities.informationAggregation'), icon: 'cn-icon cn-icon-information-aggregation', tag: 0 })
|
||||
}
|
||||
if (entityType === 'ip') {
|
||||
tabs.push({ name: entityDetailTabsName.behaviorPattern, label: i18n.global.t('entities.behaviorPattern'), icon: 'cn-icon cn-icon-behavior', tag: 0 })
|
||||
}
|
||||
const activeTab = ref(tabs[0].name)
|
||||
|
||||
const { query } = useRoute()
|
||||
@@ -118,7 +125,6 @@ export default {
|
||||
const openPort = axios.get(url, { params: params })
|
||||
// const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
|
||||
// const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
|
||||
|
||||
Promise.all([informationAggregation, openPort]).then(response => {
|
||||
if (response[0].status === 200) {
|
||||
const list = []
|
||||
@@ -136,7 +142,7 @@ export default {
|
||||
Object.keys(r[k]).forEach(k2 => {
|
||||
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
|
||||
if (find) {
|
||||
aggregation.intelligenceContent.push({ key: k2, value: this.tagValueHandler(k, k2, r[k][k2]), type: find.type })
|
||||
aggregation.intelligenceContent.push({ key: k2, value: this.tagValueHandler(r[k][k2]), type: find.type })
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -159,6 +165,10 @@ export default {
|
||||
// }
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, 0)
|
||||
this.initSetTag(entityDetailTabsName.performanceEvent, 0)
|
||||
if (this.entity.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entity.entityName === '218.77.183.150') {
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, 3)
|
||||
this.initSetTag(entityDetailTabsName.performanceEvent, 1)
|
||||
}
|
||||
})
|
||||
|
||||
// 域名解析
|
||||
@@ -171,7 +181,8 @@ export default {
|
||||
if (this.entity.entityType === 'ip') {
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
|
||||
this.promiseData(appsOfIp, domainsOfIp)
|
||||
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: params })
|
||||
this.promiseData(appsOfIp, domainsOfIp, behaviorPattern)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'domain') {
|
||||
@@ -191,6 +202,18 @@ export default {
|
||||
const len1 = res[0].status === 200 ? res0.data.result.length : 0
|
||||
const len2 = res[1].status === 200 ? res1.data.result.length : 0
|
||||
this.initSetTag(entityDetailTabsName.relatedEntity, len1 + len2)
|
||||
const behaviorPatternData = res[2].status === 200 ? res[2].data.data.result : 0
|
||||
let behaviorPatternLen = 0
|
||||
if (behaviorPatternData && behaviorPatternData[0]) {
|
||||
const dataObject = behaviorPatternData[0]
|
||||
Object.keys(dataObject).forEach(key => {
|
||||
const value = Number(dataObject[key]) ? Number(dataObject[key]) : 0
|
||||
if (value !== 0) {
|
||||
behaviorPatternLen++
|
||||
}
|
||||
})
|
||||
}
|
||||
this.initSetTag(entityDetailTabsName.behaviorPattern, behaviorPatternLen)
|
||||
break
|
||||
}
|
||||
case 'domain': {
|
||||
@@ -251,16 +274,9 @@ export default {
|
||||
case 'app': return api.entity.openPortOfApp
|
||||
}
|
||||
},
|
||||
tagValueHandler (k, k2, value) {
|
||||
if (k === 'psiphon3Ip') {
|
||||
if (k2 === 'type') {
|
||||
const find = psiphon3IpType.find(t => t.value === value)
|
||||
if (find) {
|
||||
return find.name
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
tagValueHandler (value) {
|
||||
const find = tagValueLabelMapping.find(t => t.value === value)
|
||||
return find ? find.name : value
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
|
||||
189
src/views/charts2/charts/entityDetail/tabs/BehaviorPattern.vue
Normal file
189
src/views/charts2/charts/entityDetail/tabs/BehaviorPattern.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<chart-error v-if="showError" :content="errorMsg" class="entity-detail-event-error"></chart-error>
|
||||
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
|
||||
|
||||
<div v-if="!isNoData && !showError" class="entity-detail-event-block" style="height: 100%;width: 100%;position: relative;">
|
||||
<div class="behavior-pattern" >
|
||||
<div class="behavior-pattern-legend" >
|
||||
<div class="behavior-pattern-legend__item" v-for="(data, index) in tableData" :key="index">
|
||||
<div class="legend-icon" :style="`background:${chartColorForBehaviorPattern[index%10]};`"></div>
|
||||
<div class="legend-name">{{data.name}}</div>
|
||||
<div class="legend-value" >{{ unitConvert(data.value, unitTypes.number).join('')}}</div>
|
||||
<div class="legend-percent">{{ unitConvert(data.percent, unitTypes.percent).join('') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="entityIpRoseType" class="behavior-pattern-chart"></div>
|
||||
<div class="chart-bottom-dot__right"></div>
|
||||
<div class="chart-bottom-dot__left"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import * as echarts from 'echarts'
|
||||
import { pieChartOption4 } from '@/views/charts2/charts/options/echartOption'
|
||||
import { shallowRef } from 'vue'
|
||||
import { entityDetailTabsName, chartColorForBehaviorPattern, unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import ChartError from '@/components/common/Error'
|
||||
import { toUpperCaseByString, reverseSortBy } from '@/utils/tools'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
|
||||
export default {
|
||||
name: 'BehaviorPattern',
|
||||
components: { ChartError, ChartNoData },
|
||||
mixins: [chartMixin],
|
||||
data () {
|
||||
return {
|
||||
showError: false,
|
||||
errorMsg: '',
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName,
|
||||
myChart: shallowRef(null),
|
||||
chartColorForBehaviorPattern,
|
||||
unitTypes
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.initData()
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initEcharts () {
|
||||
this.chartOption = pieChartOption4
|
||||
this.chartOption.angleAxis.data = []
|
||||
this.chartOption.series[0].data = []
|
||||
this.tableData.forEach((item, index) => {
|
||||
this.chartOption.angleAxis.data.push(item.name)
|
||||
this.chartOption.series[0].data.push(item.value)
|
||||
})
|
||||
const len = this.tableData.length
|
||||
const endIndex = len + 1
|
||||
this.tableData.forEach((item, i) => {
|
||||
if (i !== endIndex) {
|
||||
this.chartOption.angleAxis.data.push(item.name + '2')
|
||||
this.chartOption.series[0].data.push(0)
|
||||
}
|
||||
})
|
||||
const axisLabel = this.chartOption.angleAxis.axisLabel
|
||||
this.chartOption.angleAxis.axisLabel = {
|
||||
...axisLabel,
|
||||
formatter: function (params, index) {
|
||||
if (len > 15) {
|
||||
if (index === 7) {
|
||||
return params + '\n'
|
||||
} else if (index === 8) {
|
||||
return '\n' + params
|
||||
}
|
||||
}
|
||||
if (index === endIndex) {
|
||||
return params + '\n'
|
||||
} else {
|
||||
return params
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const self = this
|
||||
this.$nextTick(() => {
|
||||
if (self.myChart) {
|
||||
self.myChart.dispose()
|
||||
}
|
||||
|
||||
const dom = document.getElementById('entityIpRoseType')
|
||||
if (dom) {
|
||||
self.myChart = echarts.init(dom)
|
||||
self.myChart.setOption(this.chartOption)
|
||||
|
||||
self.myChart.dispatchAction({
|
||||
type: 'takeGlobalCursor',
|
||||
key: 'brush',
|
||||
brushOption: {
|
||||
brushType: 'lineX',
|
||||
xAxisIndex: 'all',
|
||||
brushMode: 'single',
|
||||
throttleType: 'debounce'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
async initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
}
|
||||
this.toggleLoading(true)
|
||||
await axios.get(`${api.entity.behaviorPattern}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
const data = res.data.result
|
||||
this.tableData = []
|
||||
let sum = 0
|
||||
if (data && data[0]) {
|
||||
const dataObject = data[0]
|
||||
Object.keys(dataObject).forEach(key => {
|
||||
const value = Number(dataObject[key]) ? Number(dataObject[key]) : 0
|
||||
if (value !== 0) {
|
||||
sum = sum + value
|
||||
this.tableData.push({
|
||||
name: key,
|
||||
value: value
|
||||
})
|
||||
}
|
||||
})
|
||||
this.tableData.forEach(item => {
|
||||
item.percent = item.value / sum
|
||||
})
|
||||
this.tableData = this.tableData.sort(reverseSortBy('value'))
|
||||
this.$emit('checkTag', entityDetailTabsName.behaviorPattern, this.tableData.length)
|
||||
if (this.tableData.length === 0) {
|
||||
this.isNoData = true
|
||||
}
|
||||
this.initEcharts()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
},
|
||||
httpError (e) {
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
this.$emit('checkTag', entityDetailTabsName.behaviorPattern, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -71,7 +71,7 @@
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
|
||||
import { entityDetailTabsName, entityDetailTags, tagValueLabelMapping } from '@/utils/constants'
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
|
||||
@@ -95,7 +95,7 @@ export default {
|
||||
tagValueHandler (k, k2, value) {
|
||||
if (k === 'psiphon3Ip') {
|
||||
if (k2 === 'type') {
|
||||
const find = psiphon3IpType.find(t => t.value === value)
|
||||
const find = tagValueLabelMapping.find(t => t.value === value)
|
||||
if (find) {
|
||||
return find.name
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="cn-detection__case entity-detail-performance">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header">
|
||||
<div class="cn-detection__header" style="padding-bottom: 0">
|
||||
<span
|
||||
:test-id="`severity-color-block${index}`"
|
||||
class="detection-event-severity-color-block"
|
||||
@@ -38,6 +38,11 @@
|
||||
<span>{{ $t('overall.duration') }} : </span>
|
||||
<span :test-id="`duration-time${index}`">{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-traffic-overview"></i>
|
||||
<span>{{ $t('entity.detail.anomaly') }} : </span>
|
||||
<div id="anomalyChart" style="height: 20px; width: 100px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,15 +56,19 @@
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
|
||||
import { eventSeverityColor, entityDetailTabsName, unitTypes } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import ChartError from '@/components/common/Error'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import { reverseSortBy, sortBy, toUpperCaseByString } from '@/utils/tools'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { markRaw } from 'vue'
|
||||
import { metricOption } from '@/views/detections/options/detectionOptions'
|
||||
import * as echarts from 'echarts'
|
||||
import _ from 'lodash'
|
||||
|
||||
export default {
|
||||
name: 'PerformanceEvent',
|
||||
@@ -80,18 +89,19 @@ export default {
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName
|
||||
entityName,
|
||||
chartOption: metricOption
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// this.initData()
|
||||
this.isNoData = true
|
||||
this.initData()
|
||||
/*this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
}, 200)*/
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
@@ -105,31 +115,88 @@ export default {
|
||||
}
|
||||
|
||||
this.toggleLoading(true)
|
||||
axios.get(`${api.entity.performance}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, res.data.result.length)
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
this.eventList = res.data.result
|
||||
if (this.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entityName === '218.77.183.150') {
|
||||
setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
this.isNoData = false
|
||||
this.eventList = [
|
||||
{
|
||||
"serverIp": "1.1.1.1",
|
||||
"domain": "www.baidu.com",
|
||||
"appName": "ab",
|
||||
"eventSeverity": "critical",
|
||||
"eventType": "Http error",
|
||||
"durationMs": 840000,
|
||||
"startTime": new Date().getTime() - 1957 * 1000,
|
||||
"endTime": 2222222222
|
||||
}
|
||||
]
|
||||
this.metricList = [
|
||||
[new Date().getTime() / 1000 - 2677, 2],
|
||||
[new Date().getTime() / 1000 - 2557, 3],
|
||||
[new Date().getTime() / 1000 - 2437, 2],
|
||||
[new Date().getTime() / 1000 - 2317, 7],
|
||||
[new Date().getTime() / 1000 - 2197, 8],
|
||||
[new Date().getTime() / 1000 - 2077, 38],
|
||||
[new Date().getTime() / 1000 - 1857, 12],
|
||||
[new Date().getTime() / 1000 - 1637, 8],
|
||||
[new Date().getTime() / 1000 - 1517, 7],
|
||||
[new Date().getTime() / 1000 - 1277, 3],
|
||||
[new Date().getTime() / 1000 - 1157, 1],
|
||||
[new Date().getTime() / 1000 - 1037, 2]
|
||||
]
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 1)
|
||||
this.$nextTick(() => {
|
||||
this.initChart()
|
||||
})
|
||||
}, 200)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.isNoData = true
|
||||
this.toggleLoading(false)
|
||||
this.eventList = []
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
}, 200)
|
||||
/*axios.get(`${api.entity.performance}/${this.entityType}`, {params: params}).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, res.data.result.length)
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
this.eventList = res.data.result
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})*/
|
||||
}
|
||||
},
|
||||
httpError (e) {
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
},
|
||||
initChart () {
|
||||
this.metricChart = markRaw(echarts.init(document.getElementById('anomalyChart')))
|
||||
this.chartOptionMetric = _.cloneDeep(this.chartOption)
|
||||
this.chartOptionMetric.series[0].data = this.metricList.slice(0, 4).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
|
||||
this.chartOptionMetric.series[1].data = this.metricList.slice(3, 9).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
|
||||
this.chartOptionMetric.series[2].data = this.metricList.slice(8, 11).map(v => [Number(v[0]) * 1000, Number(v[1]), unitTypes.number])
|
||||
|
||||
this.chartOptionMetric.series.forEach(item => {
|
||||
item.name = 'Http error'
|
||||
})
|
||||
|
||||
this.chartOptionMetric && this.metricChart.setOption(this.chartOptionMetric)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
class="detection-event-severity-color-block"
|
||||
:style="`background-color: ${eventSeverityColor[item.eventSeverity]}`">
|
||||
</span>
|
||||
<span class="detection-event-severity-block">{{ toUpperCaseByString(item.securityType) || '-' }}</span>
|
||||
<span class="detection-event-severity-block">{{ item.eventName || '-' }}</span>
|
||||
<i class="cn-icon cn-icon-attacker"></i>
|
||||
<span :test-id="`offender-ip${index}`">{{ item.offenderIp || '-' }}</span>
|
||||
<div class="domain">{{ item.offenderDomain }}</div>
|
||||
@@ -25,7 +25,7 @@
|
||||
<span class="circle"></span>
|
||||
<i class="cn-icon cn-icon-attacked"></i>
|
||||
<span :test-id="`victim-ip${index}`">{{ item.victimIp || '-' }}</span>
|
||||
<div class="domain">{{ item.victimDomain }}</div>
|
||||
<div class="domain">{{ item.domain }}</div>
|
||||
</div>
|
||||
<div class="cn-detection__body">
|
||||
<div class="body__basic-info">
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
<span>{{ $t('detection.list.startTime') }} : </span>
|
||||
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
|
||||
<span>{{ dateFormatByAppearance(parseFloat(item.startTime)) || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-duration"></i>
|
||||
@@ -109,14 +109,14 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// this.initData()
|
||||
this.isNoData = true
|
||||
this.initData()
|
||||
/*this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
}, 200)*/
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
@@ -130,25 +130,101 @@ export default {
|
||||
}
|
||||
|
||||
this.toggleLoading(true)
|
||||
axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
if (this.entityName === 'hqzc.wssp.hainan.gov.cn' || this.entityName === '218.77.183.150') {
|
||||
setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
this.isNoData = false
|
||||
this.eventList = [
|
||||
{
|
||||
eventId: '1717034000326447105',
|
||||
eventType: 'Command and Control',
|
||||
eventName: 'Mirai',
|
||||
eventKey: '5,26.26.26.1,192.168.38.73',
|
||||
ruleId: '5',
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: '1',
|
||||
eventSeverity: 'critical',
|
||||
offenderIp: '119.102.149.177',
|
||||
victimIp: '218.77.183.150',
|
||||
domain: 'hqzc.wssp.hainan.gov.cn',
|
||||
app: '',
|
||||
startTime: new Date().getTime() - 3600 * 1000,
|
||||
endTime: '1698207720',
|
||||
durationMs: 1613000,
|
||||
matchTimes: '1',
|
||||
status: '1',
|
||||
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
|
||||
},
|
||||
{
|
||||
eventId: '1717034000326447105',
|
||||
eventType: 'Command and Control',
|
||||
eventName: 'Bashlite',
|
||||
eventKey: '5,26.26.26.1,192.168.38.73',
|
||||
ruleId: '5',
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: '1',
|
||||
eventSeverity: 'critical',
|
||||
offenderIp: '142.4.196.195',
|
||||
victimIp: '218.77.183.150',
|
||||
domain: 'hqzc.wssp.hainan.gov.cn',
|
||||
app: '',
|
||||
startTime: new Date().getTime() - 1600 * 1000,
|
||||
endTime: '1698207720',
|
||||
durationMs: 1285000,
|
||||
matchTimes: '1',
|
||||
status: '1',
|
||||
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
|
||||
},
|
||||
{
|
||||
eventId: '1717034000326447105',
|
||||
eventType: 'Command and Control',
|
||||
eventName: 'Mirai',
|
||||
eventKey: '5,26.26.26.1,192.168.38.73',
|
||||
ruleId: '5',
|
||||
ruleType: 'indicator_match',
|
||||
isBuiltin: '1',
|
||||
eventSeverity: 'critical',
|
||||
offenderIp: '103.119.112.54',
|
||||
victimIp: '218.77.183.150',
|
||||
domain: 'hqzc.wssp.hainan.gov.cn',
|
||||
app: '',
|
||||
startTime: new Date().getTime() - 2600 * 1000,
|
||||
endTime: '1698207720',
|
||||
durationMs: 2280000,
|
||||
matchTimes: '1',
|
||||
status: '1',
|
||||
eventInfo: '{\"knowledge_id\":\"8\",\"name\":\"built_in_ioc_darkweb\",\"ioc_type\":\"ip\",\"ioc_value\":\"26.26.26.1\"}'
|
||||
}
|
||||
]
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 3)
|
||||
}, 200)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.isNoData = true
|
||||
this.toggleLoading(false)
|
||||
this.eventList = []
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
}, 200)
|
||||
/*axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, res.data.result.length)
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
this.eventList = res.data.result
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, res.data.result.length)
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
this.eventList = res.data.result
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})*/
|
||||
}
|
||||
},
|
||||
httpError (e) {
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
|
||||
@@ -355,12 +355,12 @@ export default {
|
||||
if (out < 0.0001 && out !== 0) {
|
||||
outUsage = '< 0.01%'
|
||||
} else {
|
||||
outUsage = JSON.stringify(parseFloat((out * 100).toFixed(2)))
|
||||
outUsage = JSON.stringify(parseFloat(out * 100).toFixed(2))
|
||||
}
|
||||
if (_in < 0.0001 && _in !== 0) {
|
||||
inUsage = '< 0.01%'
|
||||
} else {
|
||||
inUsage = JSON.stringify(parseFloat((_in * 100).toFixed(2)))
|
||||
inUsage = JSON.stringify(parseFloat(_in * 100).toFixed(2))
|
||||
}
|
||||
|
||||
length = outUsage.length + inUsage.length
|
||||
|
||||
@@ -30,7 +30,8 @@ export default {
|
||||
isLinkShowError: false, // 显示左侧链路报错标识
|
||||
linkErrorMsg: '', // 左侧链路的报错信息
|
||||
isNextShowError: false, // 显示右侧下一跳报错标识
|
||||
nextErrorMsg: '' // 右侧下一跳的报错信息
|
||||
nextErrorMsg: '', // 右侧下一跳的报错信息
|
||||
scoreDataState: false // 评分数据是否加载完成
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -41,6 +42,23 @@ export default {
|
||||
handler () {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && this.scoreDataState) {
|
||||
this.handleScoreData(this.linkGridData)
|
||||
this.handleScoreData(this.nextGridData)
|
||||
}
|
||||
},
|
||||
scoreDataState (n) {
|
||||
if (n && this.scoreBaseState) {
|
||||
this.handleScoreData(this.linkGridData)
|
||||
this.handleScoreData(this.nextGridData)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -48,6 +66,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
init () {
|
||||
this.scoreDataState = false
|
||||
// 链路基本信息
|
||||
let linkInfo = localStorage.getItem(storageKey.linkInfo)
|
||||
linkInfo = JSON.parse(linkInfo)
|
||||
@@ -117,9 +136,9 @@ export default {
|
||||
d.usageMore90 = outUsage >= 0.9 || inUsage >= 0.9
|
||||
// 计算npm分数
|
||||
// 分数低于3分,赋红点
|
||||
d.score = this.localComputeScore(d)
|
||||
// d.score = this.localComputeScore(d)
|
||||
|
||||
d.scoreLow3 = d.score < 3 || d.score === '-'
|
||||
// d.scoreLow3 = d.score < 3 || d.score === '-'
|
||||
|
||||
const xAxis = inLink.linkId.split('Hundredgige').pop() - 1
|
||||
const yAxis = outLink.linkId.split('Hundredgige').pop() - 1
|
||||
@@ -169,9 +188,9 @@ export default {
|
||||
// 链路下一跳数据
|
||||
let nextGridData = []
|
||||
const nextGridTemplate = [
|
||||
{ linkId: 'Hundredgige2', nextHop: '太原', out: [] },
|
||||
{ linkId: 'Hundredgige1', nextHop: '西安', out: [] },
|
||||
{ linkId: 'Hundredgige4', nextHop: '西宁', out: [] }
|
||||
{ linkId: 'Hundredgige2', nextHop: 'City2', out: [] },
|
||||
{ linkId: 'Hundredgige1', nextHop: 'City1', out: [] },
|
||||
{ linkId: 'Hundredgige4', nextHop: 'City3', out: [] }
|
||||
]
|
||||
nextGridData = JSON.parse(JSON.stringify(nextGridTemplate))
|
||||
nextGridData.forEach(link => {
|
||||
@@ -210,9 +229,9 @@ export default {
|
||||
d.usageMore90 = outUsage >= 0.9 || inUsage >= 0.9
|
||||
// 计算npm分数
|
||||
// 分数低于3分,赋红点
|
||||
d.score = this.localComputeScore(d)
|
||||
// d.score = this.localComputeScore(d)
|
||||
|
||||
d.scoreLow3 = d.score < 3 || d.score === '-'
|
||||
// d.scoreLow3 = d.score < 3 || d.score === '-'
|
||||
|
||||
const xAxis = inLink.linkId
|
||||
const yAxis = outLink.linkId
|
||||
@@ -254,6 +273,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.scoreDataState = true
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
},
|
||||
@@ -282,6 +302,23 @@ export default {
|
||||
score = computeScore(dataScore)
|
||||
return score
|
||||
},
|
||||
handleScoreData (data) {
|
||||
data.forEach(d => {
|
||||
if (d.out) {
|
||||
d.out.forEach(t => {
|
||||
const data = {
|
||||
establishLatencyMs: t.establishLatencyMs,
|
||||
httpResponseLatency: t.httpResponseLatency,
|
||||
sslConLatency: t.sslConLatency,
|
||||
tcpLostlenPercent: t.tcpLostlenPercent,
|
||||
pktRetransPercent: t.pktRetransPercent
|
||||
}
|
||||
t.score = computeScore(data, this.$store.getters.getScoreBase)
|
||||
t.scoreLow3 = t.score < 3 || t.score === '-'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 计算popover弹窗和右侧数据模块的宽度
|
||||
* 弹窗最小宽度为360px,右侧数据最小宽度为75px,右侧数据每大一位,popover弹窗宽度增加7px
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="lineMetric"
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="metricSelectChange"
|
||||
@@ -156,7 +157,7 @@ export default {
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
if (this.queryCondition) {
|
||||
const condition = this.queryCondition.toLowerCase().split(' or ')
|
||||
const condition = this.queryCondition.split(' or ')
|
||||
if (condition.length > 1) {
|
||||
params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -1)
|
||||
params.inParam = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1)
|
||||
@@ -168,6 +169,9 @@ export default {
|
||||
if (response.status === 200) {
|
||||
this.showError = false
|
||||
this.isNoData = res.data.result.length === 0
|
||||
if (!active) {
|
||||
this.tabs = dataForLinkTrafficLine.tabs
|
||||
}
|
||||
if (this.isNoData) {
|
||||
this.lineTab = ''
|
||||
this.tabs = dataForLinkTrafficLine.tabs
|
||||
@@ -387,7 +391,7 @@ export default {
|
||||
let num = 0
|
||||
linkData.forEach(e => {
|
||||
e.unitType = type
|
||||
if (parseFloat(e.analysis.avg) === 0 || isNaN(parseFloat(e.analysis.avg))) {
|
||||
if (parseFloat(e.analysis.max) === 0 || isNaN(parseFloat(e.analysis.max))) {
|
||||
e.show = false
|
||||
num += 1
|
||||
} else {
|
||||
@@ -397,13 +401,18 @@ export default {
|
||||
}
|
||||
}
|
||||
if (this.lineTab === e.class) {
|
||||
if (parseFloat(e.analysis.avg) <= 0) {
|
||||
if (parseFloat(e.analysis.max) <= 0) {
|
||||
this.lineTab = ''
|
||||
this.lineRefer = ''
|
||||
this.init()
|
||||
// this.init() // 暂时注掉,后续观察
|
||||
}
|
||||
}
|
||||
})
|
||||
const emptyData = linkData.filter(d => parseFloat(d.analysis.max) === 0)
|
||||
this.isNoData = emptyData.length === linkData.length
|
||||
if (this.isNoData) {
|
||||
return true
|
||||
}
|
||||
this.tabs = linkData
|
||||
// 如果三者avg都为0时,至少保证total显示
|
||||
const ingressObj = linkData.find(d => d.name === 'linkMonitor.ingress')
|
||||
@@ -411,10 +420,10 @@ export default {
|
||||
let ingressAvg = 0
|
||||
let egressAvg = 0
|
||||
if (ingressObj) {
|
||||
ingressAvg = parseFloat(ingressObj.analysis.avg) || 0
|
||||
ingressAvg = parseFloat(ingressObj.analysis.max) || 0
|
||||
}
|
||||
if (egressObj) {
|
||||
egressAvg = parseFloat(egressObj.analysis.avg) || 0
|
||||
egressAvg = parseFloat(egressObj.analysis.max) || 0
|
||||
}
|
||||
if ((ingressAvg + egressAvg) === 0) {
|
||||
const totalObj = linkData.find(d => d.name === 'network.total')
|
||||
|
||||
@@ -100,7 +100,7 @@ export default {
|
||||
}
|
||||
let url = ''
|
||||
if (this.queryCondition) {
|
||||
const condition = this.queryCondition.toLowerCase().split(' or ')
|
||||
const condition = this.queryCondition.split(' or ')
|
||||
if (condition.length > 1) {
|
||||
if (n === 0) {
|
||||
params.q = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1)
|
||||
|
||||
@@ -87,7 +87,14 @@ export default {
|
||||
bandWidth: 0,
|
||||
loading: false,
|
||||
showError: false,
|
||||
errorMsg: ''
|
||||
errorMsg: '',
|
||||
scoreDataState: false,
|
||||
performanceData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -95,6 +102,16 @@ export default {
|
||||
handler (n) {
|
||||
this.linkTrafficData()
|
||||
}
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && this.scoreDataState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
},
|
||||
scoreDataState (n) {
|
||||
if (n && this.scoreBaseState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -103,8 +120,11 @@ export default {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
this.loading = true
|
||||
this.performanceData = {}
|
||||
this.scoreDataState = false
|
||||
if (this.queryCondition) {
|
||||
const condition = this.queryCondition.toLowerCase().split(' or ')
|
||||
const condition = this.queryCondition.split(' or ')
|
||||
if (condition.length > 1) {
|
||||
// params.outParam = true
|
||||
params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -1)
|
||||
@@ -142,7 +162,6 @@ export default {
|
||||
this.bandWidth = bandwidthAll
|
||||
}
|
||||
}
|
||||
this.loading = true
|
||||
axios.get(api.linkMonitor.networkAnalysis, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
if (response.status === 200) {
|
||||
@@ -152,7 +171,7 @@ export default {
|
||||
this.linkTrafficListData = {}
|
||||
this.linkTrafficListData.npmScore = '-'
|
||||
} else {
|
||||
const data = {
|
||||
this.performanceData = {
|
||||
establishLatencyMs: _.get(res.data.result[0], 'establishLatencyMs', null),
|
||||
httpResponseLatency: _.get(res.data.result[0], 'httpResponseLatency', null),
|
||||
sslConLatency: _.get(res.data.result[0], 'sslConLatency', null),
|
||||
@@ -160,19 +179,24 @@ export default {
|
||||
pktRetransPercent: _.get(res.data.result[0], 'pktRetransPercent', null)
|
||||
}
|
||||
this.linkTrafficListData = res.data.result[0]
|
||||
this.linkTrafficListData.npmScore = computeScore(data)
|
||||
// this.linkTrafficListData.npmScore = computeScore(data)
|
||||
}
|
||||
} else {
|
||||
this.showError = true
|
||||
this.errorMsg = res.message
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.showError = true
|
||||
this.errorMsg = e.message
|
||||
this.isNoData = false
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
this.scoreDataState = true
|
||||
})
|
||||
},
|
||||
handleScoreData () {
|
||||
this.linkTrafficListData.npmScore = computeScore(this.performanceData, this.$store.getters.getScoreBase)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -1,994 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
value: 'Afghanistan',
|
||||
label: 'Afghanistan'
|
||||
},
|
||||
{
|
||||
value: 'Albania',
|
||||
label: 'Albania'
|
||||
},
|
||||
{
|
||||
value: 'Algeria',
|
||||
label: 'Algeria'
|
||||
},
|
||||
{
|
||||
value: 'American Samoa',
|
||||
label: 'American Samoa'
|
||||
},
|
||||
{
|
||||
value: 'Andorra',
|
||||
label: 'Andorra'
|
||||
},
|
||||
{
|
||||
value: 'Angola',
|
||||
label: 'Angola'
|
||||
},
|
||||
{
|
||||
value: 'Anguilla',
|
||||
label: 'Anguilla'
|
||||
},
|
||||
{
|
||||
value: 'Antarctica',
|
||||
label: 'Antarctica'
|
||||
},
|
||||
{
|
||||
value: 'Antigua and Barbuda',
|
||||
label: 'Antigua and Barbuda'
|
||||
},
|
||||
{
|
||||
value: 'Argentina',
|
||||
label: 'Argentina'
|
||||
},
|
||||
{
|
||||
value: 'Armenia',
|
||||
label: 'Armenia'
|
||||
},
|
||||
{
|
||||
value: 'Aruba',
|
||||
label: 'Aruba'
|
||||
},
|
||||
{
|
||||
value: 'Australia',
|
||||
label: 'Australia'
|
||||
},
|
||||
{
|
||||
value: 'Austria',
|
||||
label: 'Austria'
|
||||
},
|
||||
{
|
||||
value: 'Azerbaijan',
|
||||
label: 'Azerbaijan'
|
||||
},
|
||||
{
|
||||
value: 'Bahamas (the)',
|
||||
label: 'Bahamas (the)'
|
||||
},
|
||||
{
|
||||
value: 'Bahrain',
|
||||
label: 'Bahrain'
|
||||
},
|
||||
{
|
||||
value: 'Bangladesh',
|
||||
label: 'Bangladesh'
|
||||
},
|
||||
{
|
||||
value: 'Barbados',
|
||||
label: 'Barbados'
|
||||
},
|
||||
{
|
||||
value: 'Belarus',
|
||||
label: 'Belarus'
|
||||
},
|
||||
{
|
||||
value: 'Belgium',
|
||||
label: 'Belgium'
|
||||
},
|
||||
{
|
||||
value: 'Belize',
|
||||
label: 'Belize'
|
||||
},
|
||||
{
|
||||
value: 'Benin',
|
||||
label: 'Benin'
|
||||
},
|
||||
{
|
||||
value: 'Bermuda',
|
||||
label: 'Bermuda'
|
||||
},
|
||||
{
|
||||
value: 'Åland Islands',
|
||||
label: 'Åland Islands'
|
||||
},
|
||||
{
|
||||
value: 'Bhutan',
|
||||
label: 'Bhutan'
|
||||
},
|
||||
{
|
||||
value: 'Bolivia (Plurinational State of)',
|
||||
label: 'Bolivia (Plurinational State of)'
|
||||
},
|
||||
{
|
||||
value: 'Bonaire, Sint Eustatius and Saba',
|
||||
label: 'Bonaire, Sint Eustatius and Saba'
|
||||
},
|
||||
{
|
||||
value: 'Bosnia and Herzegovina',
|
||||
label: 'Bosnia and Herzegovina'
|
||||
},
|
||||
{
|
||||
value: 'Botswana',
|
||||
label: 'Botswana'
|
||||
},
|
||||
{
|
||||
value: 'Bouvet Island',
|
||||
label: 'Bouvet Island'
|
||||
},
|
||||
{
|
||||
value: 'Brazil',
|
||||
label: 'Brazil'
|
||||
},
|
||||
{
|
||||
value: 'British Indian Ocean Territory (the)',
|
||||
label: 'British Indian Ocean Territory (the)'
|
||||
},
|
||||
{
|
||||
value: 'Brunei Darussalam',
|
||||
label: 'Brunei Darussalam'
|
||||
},
|
||||
{
|
||||
value: 'Bulgaria',
|
||||
label: 'Bulgaria'
|
||||
},
|
||||
{
|
||||
value: 'Burkina Faso',
|
||||
label: 'Burkina Faso'
|
||||
},
|
||||
{
|
||||
value: 'Burundi',
|
||||
label: 'Burundi'
|
||||
},
|
||||
{
|
||||
value: 'Cabo Verde',
|
||||
label: 'Cabo Verde'
|
||||
},
|
||||
{
|
||||
value: 'Cambodia',
|
||||
label: 'Cambodia'
|
||||
},
|
||||
{
|
||||
value: 'Cameroon',
|
||||
label: 'Cameroon'
|
||||
},
|
||||
{
|
||||
value: 'Canada',
|
||||
label: 'Canada'
|
||||
},
|
||||
{
|
||||
value: 'Cayman Islands (the)',
|
||||
label: 'Cayman Islands (the)'
|
||||
},
|
||||
{
|
||||
value: 'Central African Republic (the)',
|
||||
label: 'Central African Republic (the)'
|
||||
},
|
||||
{
|
||||
value: 'Chad',
|
||||
label: 'Chad'
|
||||
},
|
||||
{
|
||||
value: 'Chile',
|
||||
label: 'Chile'
|
||||
},
|
||||
{
|
||||
value: 'China',
|
||||
label: 'China'
|
||||
},
|
||||
{
|
||||
value: 'Christmas Island',
|
||||
label: 'Christmas Island'
|
||||
},
|
||||
{
|
||||
value: 'Cocos (Keeling) Islands (the)',
|
||||
label: 'Cocos (Keeling) Islands (the)'
|
||||
},
|
||||
{
|
||||
value: 'Colombia',
|
||||
label: 'Colombia'
|
||||
},
|
||||
{
|
||||
value: 'Comoros (the)',
|
||||
label: 'Comoros (the)'
|
||||
},
|
||||
{
|
||||
value: 'Congo (the Democratic Republic of the)',
|
||||
label: 'Congo (the Democratic Republic of the)'
|
||||
},
|
||||
{
|
||||
value: 'Congo (the)',
|
||||
label: 'Congo (the)'
|
||||
},
|
||||
{
|
||||
value: 'Cook Islands (the)',
|
||||
label: 'Cook Islands (the)'
|
||||
},
|
||||
{
|
||||
value: 'Costa Rica',
|
||||
label: 'Costa Rica'
|
||||
},
|
||||
{
|
||||
value: 'Croatia',
|
||||
label: 'Croatia'
|
||||
},
|
||||
{
|
||||
value: 'Cuba',
|
||||
label: 'Cuba'
|
||||
},
|
||||
{
|
||||
value: 'Curaçao',
|
||||
label: 'Curaçao'
|
||||
},
|
||||
{
|
||||
value: 'Cyprus',
|
||||
label: 'Cyprus'
|
||||
},
|
||||
{
|
||||
value: 'Czech Republic',
|
||||
label: 'Czech Republic'
|
||||
},
|
||||
{
|
||||
value: "Côte d'Ivoire",
|
||||
label: "Côte d'Ivoire"
|
||||
},
|
||||
{
|
||||
value: 'Denmark',
|
||||
label: 'Denmark'
|
||||
},
|
||||
{
|
||||
value: 'Djibouti',
|
||||
label: 'Djibouti'
|
||||
},
|
||||
{
|
||||
value: 'Dominica',
|
||||
label: 'Dominica'
|
||||
},
|
||||
{
|
||||
value: 'Dominican Republic (the)',
|
||||
label: 'Dominican Republic (the)'
|
||||
},
|
||||
{
|
||||
value: 'Ecuador',
|
||||
label: 'Ecuador'
|
||||
},
|
||||
{
|
||||
value: 'Egypt',
|
||||
label: 'Egypt'
|
||||
},
|
||||
{
|
||||
value: 'El Salvador',
|
||||
label: 'El Salvador'
|
||||
},
|
||||
{
|
||||
value: 'Equatorial Guinea',
|
||||
label: 'Equatorial Guinea'
|
||||
},
|
||||
{
|
||||
value: 'Eritrea',
|
||||
label: 'Eritrea'
|
||||
},
|
||||
{
|
||||
value: 'Estonia',
|
||||
label: 'Estonia'
|
||||
},
|
||||
{
|
||||
value: 'Eswatini',
|
||||
label: 'Eswatini'
|
||||
},
|
||||
{
|
||||
value: 'Ethiopia',
|
||||
label: 'Ethiopia'
|
||||
},
|
||||
{
|
||||
value: 'Falkland Islands (the) [Malvinas]',
|
||||
label: 'Falkland Islands (the) [Malvinas]'
|
||||
},
|
||||
{
|
||||
value: 'Faroe Islands (the)',
|
||||
label: 'Faroe Islands (the)'
|
||||
},
|
||||
{
|
||||
value: 'Fiji',
|
||||
label: 'Fiji'
|
||||
},
|
||||
{
|
||||
value: 'Finland',
|
||||
label: 'Finland'
|
||||
},
|
||||
{
|
||||
value: 'France',
|
||||
label: 'France'
|
||||
},
|
||||
{
|
||||
value: 'French Guiana',
|
||||
label: 'French Guiana'
|
||||
},
|
||||
{
|
||||
value: 'French Polynesia',
|
||||
label: 'French Polynesia'
|
||||
},
|
||||
{
|
||||
value: 'French Southern Territories (the)',
|
||||
label: 'French Southern Territories (the)'
|
||||
},
|
||||
{
|
||||
value: 'Gabon',
|
||||
label: 'Gabon'
|
||||
},
|
||||
{
|
||||
value: 'Gambia (the)',
|
||||
label: 'Gambia (the)'
|
||||
},
|
||||
{
|
||||
value: 'Georgia',
|
||||
label: 'Georgia'
|
||||
},
|
||||
{
|
||||
value: 'Germany',
|
||||
label: 'Germany'
|
||||
},
|
||||
{
|
||||
value: 'Ghana',
|
||||
label: 'Ghana'
|
||||
},
|
||||
{
|
||||
value: 'Gibraltar',
|
||||
label: 'Gibraltar'
|
||||
},
|
||||
{
|
||||
value: 'Greece',
|
||||
label: 'Greece'
|
||||
},
|
||||
{
|
||||
value: 'Greenland',
|
||||
label: 'Greenland'
|
||||
},
|
||||
{
|
||||
value: 'Grenada',
|
||||
label: 'Grenada'
|
||||
},
|
||||
{
|
||||
value: 'Guadeloupe',
|
||||
label: 'Guadeloupe'
|
||||
},
|
||||
{
|
||||
value: 'Guam',
|
||||
label: 'Guam'
|
||||
},
|
||||
{
|
||||
value: 'Guatemala',
|
||||
label: 'Guatemala'
|
||||
},
|
||||
{
|
||||
value: 'Guernsey',
|
||||
label: 'Guernsey'
|
||||
},
|
||||
{
|
||||
value: 'Guinea',
|
||||
label: 'Guinea'
|
||||
},
|
||||
{
|
||||
value: 'Guinea-Bissau',
|
||||
label: 'Guinea-Bissau'
|
||||
},
|
||||
{
|
||||
value: 'Guyana',
|
||||
label: 'Guyana'
|
||||
},
|
||||
{
|
||||
value: 'Haiti',
|
||||
label: 'Haiti'
|
||||
},
|
||||
{
|
||||
value: 'Heard Island and McDonald Islands',
|
||||
label: 'Heard Island and McDonald Islands'
|
||||
},
|
||||
{
|
||||
value: 'Holy See (the)',
|
||||
label: 'Holy See (the)'
|
||||
},
|
||||
{
|
||||
value: 'Honduras',
|
||||
label: 'Honduras'
|
||||
},
|
||||
{
|
||||
value: 'Hong Kong',
|
||||
label: 'Hong Kong'
|
||||
},
|
||||
{
|
||||
value: 'Hungary',
|
||||
label: 'Hungary'
|
||||
},
|
||||
{
|
||||
value: 'Iceland',
|
||||
label: 'Iceland'
|
||||
},
|
||||
{
|
||||
value: 'India',
|
||||
label: 'India'
|
||||
},
|
||||
{
|
||||
value: 'Indonesia',
|
||||
label: 'Indonesia'
|
||||
},
|
||||
{
|
||||
value: 'Iran (Islamic Republic of)',
|
||||
label: 'Iran (Islamic Republic of)'
|
||||
},
|
||||
{
|
||||
value: 'Iraq',
|
||||
label: 'Iraq'
|
||||
},
|
||||
{
|
||||
value: 'Ireland',
|
||||
label: 'Ireland'
|
||||
},
|
||||
{
|
||||
value: 'Isle of Man',
|
||||
label: 'Isle of Man'
|
||||
},
|
||||
{
|
||||
value: 'Israel',
|
||||
label: 'Israel'
|
||||
},
|
||||
{
|
||||
value: 'Italy',
|
||||
label: 'Italy'
|
||||
},
|
||||
{
|
||||
value: 'Jamaica',
|
||||
label: 'Jamaica'
|
||||
},
|
||||
{
|
||||
value: 'Japan',
|
||||
label: 'Japan'
|
||||
},
|
||||
{
|
||||
value: 'Jersey',
|
||||
label: 'Jersey'
|
||||
},
|
||||
{
|
||||
value: 'Jordan',
|
||||
label: 'Jordan'
|
||||
},
|
||||
{
|
||||
value: 'Kazakhstan',
|
||||
label: 'Kazakhstan'
|
||||
},
|
||||
{
|
||||
value: 'Kenya',
|
||||
label: 'Kenya'
|
||||
},
|
||||
{
|
||||
value: 'Kiribati',
|
||||
label: 'Kiribati'
|
||||
},
|
||||
{
|
||||
value: 'Korea',
|
||||
label: 'Korea'
|
||||
},
|
||||
{
|
||||
value: 'Kuwait',
|
||||
label: 'Kuwait'
|
||||
},
|
||||
{
|
||||
value: 'Kyrgyzstan',
|
||||
label: 'Kyrgyzstan'
|
||||
},
|
||||
{
|
||||
value: "Lao People's Democratic Republic (the)",
|
||||
label: "Lao People's Democratic Republic (the)"
|
||||
},
|
||||
{
|
||||
value: 'Latvia',
|
||||
label: 'Latvia'
|
||||
},
|
||||
{
|
||||
value: 'Lebanon',
|
||||
label: 'Lebanon'
|
||||
},
|
||||
{
|
||||
value: 'Lesotho',
|
||||
label: 'Lesotho'
|
||||
},
|
||||
{
|
||||
value: 'Liberia',
|
||||
label: 'Liberia'
|
||||
},
|
||||
{
|
||||
value: 'Libya',
|
||||
label: 'Libya'
|
||||
},
|
||||
{
|
||||
value: 'Liechtenstein',
|
||||
label: 'Liechtenstein'
|
||||
},
|
||||
{
|
||||
value: 'Lithuania',
|
||||
label: 'Lithuania'
|
||||
},
|
||||
{
|
||||
value: 'Luxembourg',
|
||||
label: 'Luxembourg'
|
||||
},
|
||||
{
|
||||
value: 'Macao',
|
||||
label: 'Macao'
|
||||
},
|
||||
{
|
||||
value: 'Madagascar',
|
||||
label: 'Madagascar'
|
||||
},
|
||||
{
|
||||
value: 'Malawi',
|
||||
label: 'Malawi'
|
||||
},
|
||||
{
|
||||
value: 'Malaysia',
|
||||
label: 'Malaysia'
|
||||
},
|
||||
{
|
||||
value: 'Maldives',
|
||||
label: 'Maldives'
|
||||
},
|
||||
{
|
||||
value: 'Mali',
|
||||
label: 'Mali'
|
||||
},
|
||||
{
|
||||
value: 'Malta',
|
||||
label: 'Malta'
|
||||
},
|
||||
{
|
||||
value: 'Marshall Islands (the)',
|
||||
label: 'Marshall Islands (the)'
|
||||
},
|
||||
{
|
||||
value: 'Martinique',
|
||||
label: 'Martinique'
|
||||
},
|
||||
{
|
||||
value: 'Mauritania',
|
||||
label: 'Mauritania'
|
||||
},
|
||||
{
|
||||
value: 'Mauritius',
|
||||
label: 'Mauritius'
|
||||
},
|
||||
{
|
||||
value: 'Mayotte',
|
||||
label: 'Mayotte'
|
||||
},
|
||||
{
|
||||
value: 'Mexico',
|
||||
label: 'Mexico'
|
||||
},
|
||||
{
|
||||
value: 'Micronesia (Federated States of)',
|
||||
label: 'Micronesia (Federated States of)'
|
||||
},
|
||||
{
|
||||
value: 'Moldova (the Republic of)',
|
||||
label: 'Moldova (the Republic of)'
|
||||
},
|
||||
{
|
||||
value: 'Monaco',
|
||||
label: 'Monaco'
|
||||
},
|
||||
{
|
||||
value: 'Mongolia',
|
||||
label: 'Mongolia'
|
||||
},
|
||||
{
|
||||
value: 'Montenegro',
|
||||
label: 'Montenegro'
|
||||
},
|
||||
{
|
||||
value: 'Montserrat',
|
||||
label: 'Montserrat'
|
||||
},
|
||||
{
|
||||
value: 'Morocco',
|
||||
label: 'Morocco'
|
||||
},
|
||||
{
|
||||
value: 'Mozambique',
|
||||
label: 'Mozambique'
|
||||
},
|
||||
{
|
||||
value: 'Myanmar',
|
||||
label: 'Myanmar'
|
||||
},
|
||||
{
|
||||
value: 'Namibia',
|
||||
label: 'Namibia'
|
||||
},
|
||||
{
|
||||
value: 'Nauru',
|
||||
label: 'Nauru'
|
||||
},
|
||||
{
|
||||
value: 'Nepal',
|
||||
label: 'Nepal'
|
||||
},
|
||||
{
|
||||
value: 'Netherlands',
|
||||
label: 'Netherlands'
|
||||
},
|
||||
{
|
||||
value: 'New Caledonia',
|
||||
label: 'New Caledonia'
|
||||
},
|
||||
{
|
||||
value: 'New Zealand',
|
||||
label: 'New Zealand'
|
||||
},
|
||||
{
|
||||
value: 'Nicaragua',
|
||||
label: 'Nicaragua'
|
||||
},
|
||||
{
|
||||
value: 'Niger',
|
||||
label: 'Niger'
|
||||
},
|
||||
{
|
||||
value: 'Nigeria',
|
||||
label: 'Nigeria'
|
||||
},
|
||||
{
|
||||
value: 'Niue',
|
||||
label: 'Niue'
|
||||
},
|
||||
{
|
||||
value: 'Norfolk Island',
|
||||
label: 'Norfolk Island'
|
||||
},
|
||||
{
|
||||
value: 'North Macedonia',
|
||||
label: 'North Macedonia'
|
||||
},
|
||||
{
|
||||
value: 'Northern Mariana Islands (the)',
|
||||
label: 'Northern Mariana Islands (the)'
|
||||
},
|
||||
{
|
||||
value: 'Norway',
|
||||
label: 'Norway'
|
||||
},
|
||||
{
|
||||
value: 'Oman',
|
||||
label: 'Oman'
|
||||
},
|
||||
{
|
||||
value: 'Pakistan',
|
||||
label: 'Pakistan'
|
||||
},
|
||||
{
|
||||
value: 'Palau',
|
||||
label: 'Palau'
|
||||
},
|
||||
{
|
||||
value: 'Palestine, State of',
|
||||
label: 'Palestine, State of'
|
||||
},
|
||||
{
|
||||
value: 'Panama',
|
||||
label: 'Panama'
|
||||
},
|
||||
{
|
||||
value: 'Papua New Guinea',
|
||||
label: 'Papua New Guinea'
|
||||
},
|
||||
{
|
||||
value: 'Paraguay',
|
||||
label: 'Paraguay'
|
||||
},
|
||||
{
|
||||
value: 'Peru',
|
||||
label: 'Peru'
|
||||
},
|
||||
{
|
||||
value: 'Philippines',
|
||||
label: 'Philippines'
|
||||
},
|
||||
{
|
||||
value: 'Pitcairn',
|
||||
label: 'Pitcairn'
|
||||
},
|
||||
{
|
||||
value: 'Poland',
|
||||
label: 'Poland'
|
||||
},
|
||||
{
|
||||
value: 'Portugal',
|
||||
label: 'Portugal'
|
||||
},
|
||||
{
|
||||
value: 'Puerto Rico',
|
||||
label: 'Puerto Rico'
|
||||
},
|
||||
{
|
||||
value: 'Qatar',
|
||||
label: 'Qatar'
|
||||
},
|
||||
{
|
||||
value: 'Romania',
|
||||
label: 'Romania'
|
||||
},
|
||||
{
|
||||
value: 'Russian Federation',
|
||||
label: 'Russian Federation'
|
||||
},
|
||||
{
|
||||
value: 'Rwanda',
|
||||
label: 'Rwanda'
|
||||
},
|
||||
{
|
||||
value: 'Réunion',
|
||||
label: 'Réunion'
|
||||
},
|
||||
{
|
||||
value: 'Saint Barthélemy',
|
||||
label: 'Saint Barthélemy'
|
||||
},
|
||||
{
|
||||
value: 'Saint Helena, Ascension and Tristan da Cunha',
|
||||
label: 'Saint Helena, Ascension and Tristan da Cunha'
|
||||
},
|
||||
{
|
||||
value: 'Saint Kitts and Nevis',
|
||||
label: 'Saint Kitts and Nevis'
|
||||
},
|
||||
{
|
||||
value: 'Saint Lucia',
|
||||
label: 'Saint Lucia'
|
||||
},
|
||||
{
|
||||
value: 'Saint Martin',
|
||||
label: 'Saint Martin'
|
||||
},
|
||||
{
|
||||
value: 'Saint Pierre and Miquelon',
|
||||
label: 'Saint Pierre and Miquelon'
|
||||
},
|
||||
{
|
||||
value: 'Saint Vincent and the Grenadines',
|
||||
label: 'Saint Vincent and the Grenadines'
|
||||
},
|
||||
{
|
||||
value: 'Samoa',
|
||||
label: 'Samoa'
|
||||
},
|
||||
{
|
||||
value: 'San Marino',
|
||||
label: 'San Marino'
|
||||
},
|
||||
{
|
||||
value: 'Sao Tome and Principe',
|
||||
label: 'Sao Tome and Principe'
|
||||
},
|
||||
{
|
||||
value: 'Saudi Arabia',
|
||||
label: 'Saudi Arabia'
|
||||
},
|
||||
{
|
||||
value: 'Senegal',
|
||||
label: 'Senegal'
|
||||
},
|
||||
{
|
||||
value: 'Serbia',
|
||||
label: 'Serbia'
|
||||
},
|
||||
{
|
||||
value: 'Seychelles',
|
||||
label: 'Seychelles'
|
||||
},
|
||||
{
|
||||
value: 'Sierra Leone',
|
||||
label: 'Sierra Leone'
|
||||
},
|
||||
{
|
||||
value: 'Singapore',
|
||||
label: 'Singapore'
|
||||
},
|
||||
{
|
||||
value: 'Sint Maarten',
|
||||
label: 'Sint Maarten'
|
||||
},
|
||||
{
|
||||
value: 'Slovakia',
|
||||
label: 'Slovakia'
|
||||
},
|
||||
{
|
||||
value: 'Slovenia',
|
||||
label: 'Slovenia'
|
||||
},
|
||||
{
|
||||
value: 'Solomon Islands',
|
||||
label: 'Solomon Islands'
|
||||
},
|
||||
{
|
||||
value: 'Somalia',
|
||||
label: 'Somalia'
|
||||
},
|
||||
{
|
||||
value: 'South Africa',
|
||||
label: 'South Africa'
|
||||
},
|
||||
{
|
||||
value: 'South Georgia and the South Sandwich Islands',
|
||||
label: 'South Georgia and the South Sandwich Islands'
|
||||
},
|
||||
{
|
||||
value: 'South Sudan',
|
||||
label: 'South Sudan'
|
||||
},
|
||||
{
|
||||
value: 'Spain',
|
||||
label: 'Spain'
|
||||
},
|
||||
{
|
||||
value: 'Sri Lanka',
|
||||
label: 'Sri Lanka'
|
||||
},
|
||||
{
|
||||
value: 'Sudan',
|
||||
label: 'Sudan'
|
||||
},
|
||||
{
|
||||
value: 'Suriname',
|
||||
label: 'Suriname'
|
||||
},
|
||||
{
|
||||
value: 'Svalbard and Jan Mayen',
|
||||
label: 'Svalbard and Jan Mayen'
|
||||
},
|
||||
{
|
||||
value: 'Sweden',
|
||||
label: 'Sweden'
|
||||
},
|
||||
{
|
||||
value: 'Switzerland',
|
||||
label: 'Switzerland'
|
||||
},
|
||||
{
|
||||
value: 'Syrian Arab Republic',
|
||||
label: 'Syrian Arab Republic'
|
||||
},
|
||||
{
|
||||
value: 'Taiwan',
|
||||
label: 'Taiwan'
|
||||
},
|
||||
{
|
||||
value: 'Tajikistan',
|
||||
label: 'Tajikistan'
|
||||
},
|
||||
{
|
||||
value: 'Tanzania, the United Republic of',
|
||||
label: 'Tanzania, the United Republic of'
|
||||
},
|
||||
{
|
||||
value: 'Thailand',
|
||||
label: 'Thailand'
|
||||
},
|
||||
{
|
||||
value: 'Timor-Leste',
|
||||
label: 'Timor-Leste'
|
||||
},
|
||||
{
|
||||
value: 'Togo',
|
||||
label: 'Togo'
|
||||
},
|
||||
{
|
||||
value: 'Tokelau',
|
||||
label: 'Tokelau'
|
||||
},
|
||||
{
|
||||
value: 'Tonga',
|
||||
label: 'Tonga'
|
||||
},
|
||||
{
|
||||
value: 'Trinidad and Tobago',
|
||||
label: 'Trinidad and Tobago'
|
||||
},
|
||||
{
|
||||
value: 'Tunisia',
|
||||
label: 'Tunisia'
|
||||
},
|
||||
{
|
||||
value: 'Turkey',
|
||||
label: 'Turkey'
|
||||
},
|
||||
{
|
||||
value: 'Turkmenistan',
|
||||
label: 'Turkmenistan'
|
||||
},
|
||||
{
|
||||
value: 'Turks and Caicos Islands',
|
||||
label: 'Turks and Caicos Islands'
|
||||
},
|
||||
{
|
||||
value: 'Tuvalu',
|
||||
label: 'Tuvalu'
|
||||
},
|
||||
{
|
||||
value: 'Uganda',
|
||||
label: 'Uganda'
|
||||
},
|
||||
{
|
||||
value: 'Ukraine',
|
||||
label: 'Ukraine'
|
||||
},
|
||||
{
|
||||
value: 'United Arab Emirates',
|
||||
label: 'United Arab Emirates'
|
||||
},
|
||||
{
|
||||
value: 'United Kingdom of Great Britain and Northern Ireland',
|
||||
label: 'United Kingdom of Great Britain and Northern Ireland'
|
||||
},
|
||||
{
|
||||
value: 'United States Minor Outlying Islands',
|
||||
label: 'United States Minor Outlying Islands'
|
||||
},
|
||||
{
|
||||
value: 'United States',
|
||||
label: 'United States'
|
||||
},
|
||||
{
|
||||
value: 'Uruguay',
|
||||
label: 'Uruguay'
|
||||
},
|
||||
{
|
||||
value: 'Uzbekistan',
|
||||
label: 'Uzbekistan'
|
||||
},
|
||||
{
|
||||
value: 'Vanuatu',
|
||||
label: 'Vanuatu'
|
||||
},
|
||||
{
|
||||
value: 'Venezuela (Bolivarian Republic of)',
|
||||
label: 'Venezuela (Bolivarian Republic of)'
|
||||
},
|
||||
{
|
||||
value: 'Viet Nam',
|
||||
label: 'Viet Nam'
|
||||
},
|
||||
{
|
||||
value: 'Virgin Islands (British)',
|
||||
label: 'Virgin Islands (British)'
|
||||
},
|
||||
{
|
||||
value: 'Virgin Islands (U.S.)',
|
||||
label: 'Virgin Islands (U.S.)'
|
||||
},
|
||||
{
|
||||
value: 'Wallis and Futuna',
|
||||
label: 'Wallis and Futuna'
|
||||
},
|
||||
{
|
||||
value: 'Western Sahara*',
|
||||
label: 'Western Sahara*'
|
||||
},
|
||||
{
|
||||
value: 'Yemen',
|
||||
label: 'Yemen'
|
||||
},
|
||||
{
|
||||
value: 'Zambia',
|
||||
label: 'Zambia'
|
||||
},
|
||||
{
|
||||
value: 'Zimbabwe',
|
||||
label: 'Zimbabwe'
|
||||
}
|
||||
]
|
||||
@@ -38,6 +38,7 @@
|
||||
size="mini"
|
||||
v-model="lineRefer"
|
||||
:disabled="!lineTab"
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="referenceSelectChange"
|
||||
@@ -192,6 +193,9 @@ export default {
|
||||
if (response.status === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.showError = false
|
||||
if (!active) {
|
||||
this.tabs = _.cloneDeep(this.tabsTemplate)
|
||||
}
|
||||
if (this.isNoData) {
|
||||
this.lineTab = ''
|
||||
this.tabs = _.cloneDeep(this.tabsTemplate)
|
||||
@@ -464,8 +468,7 @@ export default {
|
||||
newData.push(obj)
|
||||
})
|
||||
}
|
||||
|
||||
if (data !== undefined && data.length > 0) {
|
||||
if (data && data.length > 0) {
|
||||
newData.forEach((item) => {
|
||||
item.type = getLineType(item.type)
|
||||
if (item.type === val) {
|
||||
@@ -478,6 +481,24 @@ export default {
|
||||
})
|
||||
}
|
||||
lineData.splice(0, 1)
|
||||
// TODO 下面的逻辑是判断total曲线的尾部数据,从尾往前数0值的个数,若个数大于0,所有曲线都从尾部去掉相同数量的点,最多4个
|
||||
const totalData = lineData[0]
|
||||
if (_.get(totalData, 'values', []).length > 4) {
|
||||
let count = 0
|
||||
for (let i = totalData.values.length - 1; i >= totalData.values.length - 4; i--) {
|
||||
if (totalData.values[i].length > 1 && totalData.values[i][1] === 0) {
|
||||
count++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
lineData.forEach(l => {
|
||||
l.values.splice(l.values.length - count, count)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (val === 'Sessions/s') {
|
||||
const tabs = _.cloneDeep(this.tabsTemplate)
|
||||
lineData.forEach((d, i) => {
|
||||
@@ -495,6 +516,7 @@ export default {
|
||||
})
|
||||
this.tabs = tabs
|
||||
this.$nextTick(() => {
|
||||
this.lineRefer = 'Average'
|
||||
this.echartsInit(this.tabs, true)
|
||||
})
|
||||
} else {
|
||||
@@ -512,7 +534,7 @@ export default {
|
||||
const self = this
|
||||
tabs.forEach(e => {
|
||||
e.unitType = type
|
||||
if (e.name !== 'network.total' && parseFloat(e.analysis.avg) === 0) {
|
||||
if (e.name !== 'network.total' && parseFloat(e.analysis.max) === 0) {
|
||||
e.show = false
|
||||
num += 1
|
||||
} else {
|
||||
@@ -522,13 +544,18 @@ export default {
|
||||
}
|
||||
}
|
||||
if (self.lineTab === e.class) {
|
||||
if (parseFloat(e.analysis.avg) <= 0) {
|
||||
if (parseFloat(e.analysis.max) <= 0) {
|
||||
self.lineTab = ''
|
||||
self.lineRefer = ''
|
||||
self.init()
|
||||
// self.init()
|
||||
}
|
||||
}
|
||||
})
|
||||
const emptyData = tabs.filter(d => parseFloat(d.analysis.max) === 0)
|
||||
this.isNoData = emptyData.length === tabs.length
|
||||
if (this.isNoData) {
|
||||
return true
|
||||
}
|
||||
this.tabs = tabs
|
||||
if (num === 5) {
|
||||
tabs[0].invertTab = false
|
||||
|
||||
@@ -379,12 +379,25 @@ export default {
|
||||
timeFilter: {
|
||||
handler (n) {
|
||||
const queryParams = this.getQueryParams()
|
||||
this.changeUrlTabState()
|
||||
this.getChartData(queryParams)
|
||||
this.changeUrlTabState()
|
||||
}
|
||||
},
|
||||
metric (n) {
|
||||
this.changeMetric()
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && Object.keys(_.get(this.tableData, '[0].scoreGroup', {})).length >= 5) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
},
|
||||
tableData: {
|
||||
deep: true,
|
||||
handler (n) {
|
||||
if (Object.keys(_.get(n, '[0].scoreGroup', {})).length >= 5 && this.scoreBaseState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -399,6 +412,9 @@ export default {
|
||||
className = 'tab-table tab-table__no-bottom'
|
||||
}
|
||||
return className
|
||||
},
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
mixins: [chartMixin],
|
||||
@@ -468,12 +484,12 @@ export default {
|
||||
return excludeName.indexOf(title.name) > -1 ? false : 'custom'
|
||||
},
|
||||
searchColumnWidth (columnType) {
|
||||
let checkedGroup = this.customTableTitles.filter(item => item.checked)
|
||||
let checkedNum = checkedGroup.length
|
||||
const checkedGroup = this.customTableTitles.filter(item => item.checked)
|
||||
const checkedNum = checkedGroup.length
|
||||
if (columnType === 'dillDown') {
|
||||
if(checkedNum === 1){
|
||||
if (checkedNum === 1) {
|
||||
return 'auto'
|
||||
}else if(checkedNum > 1){
|
||||
} else if (checkedNum > 1) {
|
||||
return '217px'
|
||||
}
|
||||
}
|
||||
@@ -501,7 +517,7 @@ export default {
|
||||
name: this.dropDownValue ? this.dropDownValue : ''
|
||||
}
|
||||
let url = curTableInCode.url.drilldownList
|
||||
if(this.isDrilldown() || this.tableType === fromRoute.linkMonitor){
|
||||
if (this.isDrilldown() || this.tableType === fromRoute.linkMonitor) {
|
||||
url = curTableInCode.url.relationTabDrilldownList
|
||||
const condition = this.getQueryCondition()
|
||||
if (condition) {
|
||||
@@ -601,8 +617,8 @@ export default {
|
||||
this.curPageNum = 1
|
||||
this.initDropdownList(prop)
|
||||
},
|
||||
scrollList (prop,tabProp) {
|
||||
const obj = document.getElementById('tabSearchSelectDropdown'+tabProp)
|
||||
scrollList (prop, tabProp) {
|
||||
const obj = document.getElementById('tabSearchSelectDropdown' + tabProp)
|
||||
if ((obj.scrollTop + obj.clientHeight === obj.scrollHeight) && obj.scrollHeight !== 0) {
|
||||
this.initDropdownList(prop)
|
||||
}
|
||||
@@ -1038,12 +1054,12 @@ export default {
|
||||
} else {
|
||||
item.scoreGroup[self.columnNameGroup[tableColumn.prop]] = 0
|
||||
}
|
||||
if (Object.keys(item.scoreGroup).length >= 5) {
|
||||
/* if (Object.keys(item.scoreGroup).length >= 5) {
|
||||
item.score = computeScore(item.scoreGroup)
|
||||
if (!_.isNumber(item.score)) {
|
||||
item.score = '-'
|
||||
}
|
||||
}
|
||||
} */
|
||||
})
|
||||
} else {
|
||||
tableColumn.showError = true
|
||||
@@ -2223,6 +2239,18 @@ export default {
|
||||
this.orderBy = 'sessions'
|
||||
this.metricUnit = 'sessions'
|
||||
}
|
||||
},
|
||||
handleScoreData () {
|
||||
this.tableData.forEach(t => {
|
||||
const data = {
|
||||
establishLatencyMs: t.scoreGroup ? t.scoreGroup.establishLatencyMs : null,
|
||||
httpResponseLatency: t.scoreGroup ? t.scoreGroup.httpResponseLatency : null,
|
||||
sslConLatency: t.scoreGroup ? t.scoreGroup.sslConLatency : null,
|
||||
tcpLostlenPercent: t.scoreGroup ? t.scoreGroup.tcpLostlenPercent : null,
|
||||
pktRetransPercent: t.scoreGroup ? t.scoreGroup.pktRetransPercent : null
|
||||
}
|
||||
t.score = computeScore(data, this.$store.getters.getScoreBase)
|
||||
})
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -172,7 +172,8 @@ export default {
|
||||
curTabState: curTabState,
|
||||
urlChangeParams: {},
|
||||
showError: false,
|
||||
errorMsg: ''
|
||||
errorMsg: '',
|
||||
scoreDataState: false // 评分数据是否加载完成
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@@ -185,6 +186,21 @@ export default {
|
||||
handler () {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && this.scoreDataState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
},
|
||||
scoreDataState (n) {
|
||||
if (n && this.scoreBaseState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -197,6 +213,7 @@ export default {
|
||||
const currentTrafficRequest = axios.get(api.npm.overview.appTrafficAnalysis, { params: { ...params, cycle: 0 } })
|
||||
const lastCycleTrafficRequest = axios.get(api.npm.overview.appTrafficAnalysis, { params: { ...params, cycle: 1 } })
|
||||
this.toggleLoading(true)
|
||||
this.scoreDataState = false
|
||||
Promise.all([currentTrafficRequest, lastCycleTrafficRequest]).then(res => {
|
||||
if (res[0].status === 200 && res[1].status === 200) {
|
||||
this.showError = false
|
||||
@@ -239,6 +256,9 @@ export default {
|
||||
t[keyPre[i] + 'Score'] = r.data.data.result.find(d => d.appSubcategory === t.appSubcategory)
|
||||
})
|
||||
} else {
|
||||
tableData.forEach(t => {
|
||||
t[keyPre[i] + 'Score'] = null
|
||||
})
|
||||
this.showError = true
|
||||
msg = msg + ',' + r.data.data.message
|
||||
if (msg.indexOf(',') === 0) {
|
||||
@@ -250,7 +270,7 @@ export default {
|
||||
this.errorMsg = msg
|
||||
}
|
||||
})
|
||||
tableData.forEach(t => {
|
||||
/* tableData.forEach(t => {
|
||||
const data = {
|
||||
establishLatencyMs: t.tcpScore ? t.tcpScore.establishLatencyMs : null,
|
||||
httpResponseLatency: t.httpScore ? t.httpScore.httpResponseLatency : null,
|
||||
@@ -259,10 +279,11 @@ export default {
|
||||
pktRetransPercent: t.packetRetransScore ? t.packetRetransScore.pktRetransPercent : null
|
||||
}
|
||||
t.score = computeScore(data)
|
||||
})
|
||||
}) */
|
||||
this.tableData = tableData
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
this.scoreDataState = true
|
||||
})
|
||||
} else {
|
||||
this.tableData = []
|
||||
@@ -382,6 +403,18 @@ export default {
|
||||
overwriteUrl(newUrl)
|
||||
}
|
||||
this.urlChangeParams = {}
|
||||
},
|
||||
handleScoreData () {
|
||||
this.tableData.forEach(t => {
|
||||
const data = {
|
||||
establishLatencyMs: t.tcpScore ? t.tcpScore.establishLatencyMs : null,
|
||||
httpResponseLatency: t.httpScore ? t.httpScore.httpResponseLatency : null,
|
||||
sslConLatency: t.sslScore ? t.sslScore.sslConLatency : null,
|
||||
tcpLostlenPercent: t.tcpLostScore ? t.tcpLostScore.tcpLostlenPercent : null,
|
||||
pktRetransPercent: t.packetRetransScore ? t.packetRetransScore.pktRetransPercent : null
|
||||
}
|
||||
t.score = computeScore(data, this.$store.getters.getScoreBase)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="npm-header-body" v-for="(item, index) in chartData" :key=index>
|
||||
<div class="npm-header-body-severity">
|
||||
<div class="npm-header-body-severity-icon" :class="item.eventSeverity" :test-id="`icon${index}`"></div>
|
||||
<div class="npm-header-body-severity-value" :test-id="`severity${index}`">{{item.eventSeverity}}</div>
|
||||
<div class="npm-header-body-severity-value" :test-id="`severity${index}`">{{ $t(item.eventSeverity) }}</div>
|
||||
</div>
|
||||
<chart-error v-if="showError" tooltip :content="errorMsg" />
|
||||
<div v-else class="npm-header-body-total" :test-id="`total${index}`">{{item.count}}</div>
|
||||
@@ -45,7 +45,7 @@ export default {
|
||||
endTime: this.timeFilter && this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : '',
|
||||
type: this.type
|
||||
}
|
||||
/*this.toggleLoading(true)
|
||||
/* this.toggleLoading(true)
|
||||
axios.get(api.npm.events.list, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
if (response.status === 200) {
|
||||
@@ -69,7 +69,7 @@ export default {
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})*/
|
||||
}) */
|
||||
this.toggleLoading(false)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -43,21 +43,38 @@ export default {
|
||||
trafficDirection: 'Server',
|
||||
curTabState: curTabState,
|
||||
showError: false,
|
||||
errorMsg: ''
|
||||
errorMsg: '',
|
||||
scoreDataState: false
|
||||
}
|
||||
},
|
||||
mixins: [chartMixin],
|
||||
computed: {
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
timeFilter: {
|
||||
handler () {
|
||||
this.loadAm4ChartMap(this.polygonSeries, this.worldImageSeries)
|
||||
}
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && this.scoreDataState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
},
|
||||
scoreDataState (n) {
|
||||
if (n && this.scoreBaseState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async initMap () {
|
||||
// 初始化插件
|
||||
this.toggleLoading(true)
|
||||
this.scoreDataState = false // 重置评分数据状态
|
||||
const geoData = await getGeoData(storageKey.iso36112WorldLow)
|
||||
const chart = am4Core.create('npmDrillDownMap', am4Maps.MapChart)
|
||||
chart.geodata = geoData
|
||||
@@ -81,6 +98,7 @@ export default {
|
||||
// 清除数据
|
||||
// polygonSeries.data.splice(0)
|
||||
this.toggleLoading(true)
|
||||
this.scoreDataState = false // 重置评分数据状态
|
||||
// 清除legend
|
||||
this.myChart.children.each((s, i) => {
|
||||
if (s && s.className !== 'Container') {
|
||||
@@ -140,10 +158,11 @@ export default {
|
||||
tcpLostlenPercent: valueToRangeValue(data.tcpLostlenPercent, unitTypes.percent).join(' '),
|
||||
pktRetransPercent: valueToRangeValue(data.pktRetransPercent, unitTypes.percent).join(' ')
|
||||
}
|
||||
t.tooltip.data.score = computeScore(data)
|
||||
t.performanceData = data
|
||||
/* t.tooltip.data.score = computeScore(data)
|
||||
if (t.tooltip.data.score === '-') {
|
||||
t.tooltip.data.score = ''
|
||||
}
|
||||
} */
|
||||
t.tooltip.data.total = valueToRangeValue(t.totalBitsRate, unitTypes.bps).join(' ')
|
||||
t.tooltip.data.inbound = valueToRangeValue(t.inboundBitsRate, unitTypes.bps).join(' ')
|
||||
t.tooltip.data.outbound = valueToRangeValue(t.outboundBitsRate, unitTypes.bps).join(' ')
|
||||
@@ -161,6 +180,7 @@ export default {
|
||||
sslConLatency: this.$t('networkAppPerformance.sslResponseLatency')
|
||||
}
|
||||
})
|
||||
this.scoreDataState = true
|
||||
this.loadMarkerData(imageSeries, mapData)
|
||||
})
|
||||
} else {
|
||||
@@ -180,14 +200,10 @@ export default {
|
||||
}
|
||||
},
|
||||
loadMarkerData (imageSeries, data) {
|
||||
const _data = data.filter(d => d.tooltip.data.score || d.tooltip.data.score === 0)
|
||||
imageSeries.data = _data.map(r => ({
|
||||
imageSeries.data = data.map(r => ({
|
||||
...r,
|
||||
score: r.tooltip.data.score,
|
||||
name: r.superAdminArea || r.countryRegion,
|
||||
id: r.serverId,
|
||||
color: this.scoreColor(r.tooltip.data.score),
|
||||
border: this.scoreColor(r.tooltip.data.score)
|
||||
id: r.serverId
|
||||
}))
|
||||
},
|
||||
scoreColor (score) {
|
||||
@@ -281,6 +297,9 @@ export default {
|
||||
imageTemplate.adapter.add('longitude', function (longitude, target) {
|
||||
const polygon = polygonSeries.getPolygonById(target.dataItem.dataContext.id)
|
||||
if (polygon) {
|
||||
if (target.dataItem.dataContext.id === 'RU') {
|
||||
return 90
|
||||
}
|
||||
return polygon.visualLongitude
|
||||
}
|
||||
return longitude
|
||||
@@ -307,6 +326,22 @@ export default {
|
||||
})
|
||||
|
||||
return imageSeries
|
||||
},
|
||||
handleScoreData () {
|
||||
const imageSeries = this.location ? this.countryImageSeries : this.worldImageSeries
|
||||
imageSeries.data = imageSeries.data.map(d => {
|
||||
let score = computeScore(d.performanceData, this.$store.getters.getScoreBase)
|
||||
if (score === '-') {
|
||||
score = ''
|
||||
}
|
||||
d.tooltip.data.score = score
|
||||
return {
|
||||
...d,
|
||||
score,
|
||||
color: this.scoreColor(score),
|
||||
border: this.scoreColor(score)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -324,9 +324,15 @@ export default {
|
||||
case 5:
|
||||
return api.npm.location.packetsRetrains
|
||||
}
|
||||
},
|
||||
initI18n () {
|
||||
dataForNpmLine.chartOptionLineData.forEach(item => {
|
||||
item.legend = this.$t(item.legend)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initI18n()
|
||||
if (this.chart) {
|
||||
this.chartData = _.cloneDeep(this.chart)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
v-model="trafficDirection"
|
||||
class="map-select map-select__direction"
|
||||
popper-class="map-select-down"
|
||||
placeholder=" "
|
||||
:popper-append-to-body="false"
|
||||
>
|
||||
<el-option value="Server">Server</el-option>
|
||||
@@ -25,7 +26,7 @@
|
||||
:popper-append-to-body="false"
|
||||
>
|
||||
<template #prefix><i class="cn-icon cn-icon-location" style="color: #575757;"></i></template>
|
||||
<el-option v-for="(country, index) in locationOptions" :key="index" :value="country.value">{{country.label}}</el-option>
|
||||
<el-option v-for="country in showLocationOptions" :key="country" :value="country">{{country}}</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="map-legend">
|
||||
@@ -50,8 +51,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 { countryNameIdMapping, storageKey, unitTypes } from '@/utils/constants'
|
||||
import locationOptions from '@/views/charts2/charts/locationOptions'
|
||||
import { countryNameIdMapping, storageKey, unitTypes, iso36112 } from '@/utils/constants'
|
||||
import { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import { api } from '@/utils/api'
|
||||
@@ -65,7 +65,7 @@ export default {
|
||||
components: { ChartError },
|
||||
data () {
|
||||
return {
|
||||
locationOptions,
|
||||
showLocationOptions: [],
|
||||
myChart: null,
|
||||
polygonSeries: null,
|
||||
countrySeries: null,
|
||||
@@ -75,14 +75,21 @@ export default {
|
||||
trafficDirection: 'Server',
|
||||
location: '',
|
||||
showError: false,
|
||||
errorMsg: ''
|
||||
errorMsg: '',
|
||||
scoreDataState: false
|
||||
}
|
||||
},
|
||||
mixins: [chartMixin],
|
||||
computed: {
|
||||
scoreBaseState () {
|
||||
return this.$store.getters.scoreBaseReady
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async initMap () {
|
||||
// 初始化插件
|
||||
this.toggleLoading(true)
|
||||
this.scoreDataState = false // 重置评分数据状态
|
||||
const geoData = await getGeoData(storageKey.iso36112WorldLow)
|
||||
const chart = am4Core.create('npmMap', am4Maps.MapChart)
|
||||
chart.geodata = geoData
|
||||
@@ -101,14 +108,13 @@ export default {
|
||||
this.worldImageSeries.mapImages.template.events.on('hit', async ev => {
|
||||
this.$store.commit('setNpmLocationCountry', ev.target.dataItem.dataContext.name)
|
||||
this.location = ev.target.dataItem.dataContext.name
|
||||
const countryId = ev.target.dataItem.dataContext.id
|
||||
ev.target.isHover = false
|
||||
await this.drill(countryId)
|
||||
})
|
||||
},
|
||||
loadAm4ChartMap (polygonSeries, imageSeries) {
|
||||
try {
|
||||
this.toggleLoading(true)
|
||||
this.scoreDataState = false // 重置评分数据状态
|
||||
// 清除legend
|
||||
this.myChart.children.each((s, i) => {
|
||||
if (s && s.className !== 'Container') {
|
||||
@@ -172,10 +178,11 @@ export default {
|
||||
tcpLostlenPercent: valueToRangeValue(data.tcpLostlenPercent, unitTypes.percent).join(' '),
|
||||
pktRetransPercent: valueToRangeValue(data.pktRetransPercent, unitTypes.percent).join(' ')
|
||||
}
|
||||
t.tooltip.data.score = computeScore(data)
|
||||
t.performanceData = data
|
||||
/* t.tooltip.data.score = computeScore(data)
|
||||
if (t.tooltip.data.score === '-') {
|
||||
t.tooltip.data.score = ''
|
||||
}
|
||||
} */
|
||||
t.tooltip.data.total = valueToRangeValue(t.totalBitsRate, unitTypes.bps).join(' ')
|
||||
t.tooltip.data.inbound = valueToRangeValue(t.inboundBitsRate, unitTypes.bps).join(' ')
|
||||
t.tooltip.data.outbound = valueToRangeValue(t.outboundBitsRate, unitTypes.bps).join(' ')
|
||||
@@ -193,6 +200,7 @@ export default {
|
||||
sslConLatency: this.$t('networkAppPerformance.sslResponseLatency')
|
||||
}
|
||||
})
|
||||
this.scoreDataState = true
|
||||
this.loadMarkerData(imageSeries, mapData)
|
||||
}).catch(e => {
|
||||
this.showError = true
|
||||
@@ -219,15 +227,18 @@ export default {
|
||||
}
|
||||
},
|
||||
loadMarkerData (imageSeries, data) {
|
||||
const _data = data.filter(d => d.tooltip.data.score || d.tooltip.data.score === 0)
|
||||
imageSeries.data = _data.map(r => ({
|
||||
imageSeries.data = data.map(r => ({
|
||||
...r,
|
||||
score: r.tooltip.data.score,
|
||||
name: r.superAdminArea || r.countryRegion,
|
||||
id: r.serverId,
|
||||
color: this.scoreColor(r.tooltip.data.score),
|
||||
border: this.scoreColor(r.tooltip.data.score)
|
||||
id: r.serverId
|
||||
}))
|
||||
if (!this.location) {
|
||||
this.filterLocationOptions(data)
|
||||
}
|
||||
},
|
||||
filterLocationOptions (data) {
|
||||
const showLocationOptions = Object.keys(countryNameIdMapping).filter(c => data.some(d => d.countryRegion === c))
|
||||
this.showLocationOptions = Array.from(new Set(showLocationOptions))
|
||||
},
|
||||
scoreColor (score) {
|
||||
if (score >= 0 && score <= 2) {
|
||||
@@ -320,6 +331,9 @@ export default {
|
||||
imageTemplate.adapter.add('longitude', function (longitude, target) {
|
||||
const polygon = polygonSeries.getPolygonById(target.dataItem.dataContext.id)
|
||||
if (polygon) {
|
||||
if (target.dataItem.dataContext.id === 'RU') {
|
||||
return 90
|
||||
}
|
||||
return polygon.visualLongitude
|
||||
}
|
||||
return longitude
|
||||
@@ -352,34 +366,55 @@ export default {
|
||||
if (countryId) {
|
||||
const targetMapObject = this.polygonSeries.getPolygonById(countryId)
|
||||
targetMapObject.series.chart.zoomToMapObject(targetMapObject)
|
||||
const geoData = await getGeoData(countryId)
|
||||
if (geoData) {
|
||||
if (!this.countrySeries) {
|
||||
this.countrySeries = this.polygonSeriesFactory()
|
||||
}
|
||||
if (!this.countryImageSeries) {
|
||||
this.countryImageSeries = this.imageSeriesFactory('score', this.countrySeries)
|
||||
}
|
||||
this.countrySeries.geodata = geoData
|
||||
this.polygonSeries.hide()
|
||||
this.worldImageSeries.hide()
|
||||
this.countrySeries.show()
|
||||
this.countryImageSeries.show()
|
||||
if (iso36112[countryId]) {
|
||||
const geoData = await getGeoData(countryId)
|
||||
if (geoData) {
|
||||
if (!this.countrySeries) {
|
||||
this.countrySeries = this.polygonSeriesFactory()
|
||||
}
|
||||
if (!this.countryImageSeries) {
|
||||
this.countryImageSeries = this.imageSeriesFactory('score', this.countrySeries)
|
||||
}
|
||||
this.countrySeries.geodata = geoData
|
||||
this.polygonSeries.hide()
|
||||
this.worldImageSeries.hide()
|
||||
this.countrySeries.show()
|
||||
this.countryImageSeries.show()
|
||||
|
||||
await this.$nextTick(() => {
|
||||
this.loadAm4ChartMap(this.countrySeries, this.countryImageSeries)
|
||||
})
|
||||
} else if (!num || num < 3) {
|
||||
// 多次测试,最多2次查询不到数据
|
||||
if (!num) {
|
||||
num = 0
|
||||
await this.$nextTick(() => {
|
||||
this.loadAm4ChartMap(this.countrySeries, this.countryImageSeries)
|
||||
})
|
||||
} else if (!num || num < 3) {
|
||||
// 多次测试,最多2次查询不到数据
|
||||
if (!num) {
|
||||
num = 0
|
||||
}
|
||||
num++
|
||||
await this.drill(countryId, num)
|
||||
} else {
|
||||
this.$message.warning(this.$t('tip.noDetailMap'))
|
||||
}
|
||||
num++
|
||||
await this.drill(countryId, num)
|
||||
} else {
|
||||
this.$message.warning(this.$t('tip.noDetailMap'))
|
||||
}
|
||||
}
|
||||
},
|
||||
handleScoreData () {
|
||||
const imageSeries = this.location ? this.countryImageSeries : this.worldImageSeries
|
||||
imageSeries.data = imageSeries.data.map(d => {
|
||||
let score = computeScore(d.performanceData, this.$store.getters.getScoreBase)
|
||||
if (score === '-') {
|
||||
score = ''
|
||||
}
|
||||
d.tooltip.data.score = score
|
||||
return {
|
||||
...d,
|
||||
score,
|
||||
color: this.scoreColor(score),
|
||||
border: this.scoreColor(score)
|
||||
}
|
||||
})
|
||||
// this.myChart.invalidateData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -419,6 +454,16 @@ export default {
|
||||
this.loadAm4ChartMap(this.polygonSeries, this.worldImageSeries)
|
||||
}
|
||||
}
|
||||
},
|
||||
scoreBaseState (n) {
|
||||
if (n && this.scoreDataState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
},
|
||||
scoreDataState (n) {
|
||||
if (n && this.scoreBaseState) {
|
||||
this.handleScoreData()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="metricFilter"
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="metricChange"
|
||||
@@ -458,9 +458,21 @@ export default {
|
||||
}
|
||||
this.$store.commit('setRangeEchartsData', rangeObj)
|
||||
}
|
||||
},
|
||||
initI18n () {
|
||||
dataForNpmTrafficLine.tabs.forEach(item => {
|
||||
item.name = this.$t(item.name)
|
||||
})
|
||||
dataForNpmTrafficLine.npmQuantity.forEach(item => {
|
||||
item.name = this.$t(item.name)
|
||||
})
|
||||
dataForNpmTrafficLine.metricOptions.forEach(item => {
|
||||
item.label = this.$t(item.label)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initI18n()
|
||||
if (this.chart) {
|
||||
this.chartData = _.cloneDeep(this.chart)
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import {
|
||||
chartColor3,
|
||||
chartColor5,
|
||||
chartColor6,
|
||||
chartColorForBehaviorPattern,
|
||||
unitTypes
|
||||
} from '@/utils/constants'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import { axisFormatter } from '@/views/charts/charts/tools'
|
||||
import { xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
|
||||
@@ -156,6 +157,111 @@ export const pieChartOption3 = {
|
||||
]
|
||||
}
|
||||
|
||||
export const pieChartOption4 = {
|
||||
color: chartColorForBehaviorPattern,
|
||||
polar: {
|
||||
radius: [30, 225],
|
||||
center: ['400px', '100%']// 为了显示出来半圆底部左侧的边
|
||||
},
|
||||
radiusAxis: {
|
||||
min: 0,
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#d3d3d3',
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#d3d3d3',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
angleAxis: {
|
||||
type: 'category',
|
||||
data: [], // 'a', 'b', 'c', 'd','aa', 'ab', 'ac', 'ad','a', 'b', 'c', 'd','aa', 'ab', 'ac', 'ad'
|
||||
startAngle: 180,
|
||||
// splitNumber: 30,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
show: true,
|
||||
// alignWithLabel:true,//可以保证刻度线和标签对齐
|
||||
interval: 0, // 强制显示所有标签
|
||||
// hideOverlap:true//从 v5.2.0 开始支持
|
||||
formatter: function (params, index) {
|
||||
if (index === 7) {
|
||||
return params + '\n'
|
||||
} else if (index === 8) {
|
||||
return '\n' + params
|
||||
} else if (index === 15) {
|
||||
return params + '\n'
|
||||
} else {
|
||||
return params
|
||||
}
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
alignWithLabel: true,
|
||||
interval: 0,
|
||||
length: 5
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: ['#e2e5ec'],
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
formatter: function (item) {
|
||||
let str = '<div style="display:flex;flex-direction: row;align-items: center;">'
|
||||
str += '<div style="width: 8px;\n' +
|
||||
' height: 8px;\n' +
|
||||
' margin: 3px 8px 0 0;\n' +
|
||||
' border-radius: 1px;;background:'
|
||||
str += item.color
|
||||
str += ';"></div>'
|
||||
str += item.name + ': ' + unitConvert(item.value, unitTypes.number).join('')
|
||||
str += '</div>'
|
||||
str += '</div>'
|
||||
return str
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
type: 'bar',
|
||||
data: [], // 8,7,6,5,4,3,2,1,0,0,0,0,0,0,0,0
|
||||
coordinateSystem: 'polar',
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
itemStyle: {
|
||||
color: function (params) {
|
||||
return chartColorForBehaviorPattern[params.dataIndex]
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'middle',
|
||||
formatter: '{b}: {c}'
|
||||
}
|
||||
}],
|
||||
animation: false
|
||||
}
|
||||
|
||||
export const stackedLineChartOption = {
|
||||
color: chartColor3,
|
||||
tooltip: {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<template>
|
||||
<div class="detection-filter-case">
|
||||
<div class="no-data" v-if="isNoData">{{ $t('npm.noData') }}</div>
|
||||
|
||||
<div class="new-detection-filter-title">{{$t('detections.filters')}}</div>
|
||||
<div class="no-data" v-if="isNoData">{{ $t('npm.noData') }}</div>
|
||||
<template v-for="(filter, index) in filterData" :key="index">
|
||||
<div class="detection-filter" v-show="filter.data.length > 0">
|
||||
<div class="filter__header" @click="filter.collapse = !filter.collapse">
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<loading :loading="loading"></loading>
|
||||
<div class="detection-list__content">
|
||||
<div class="detection-list--list">
|
||||
<div class="no-data" v-if="noData">{{ $t('npm.noData') }}</div>
|
||||
<div class="no-data" v-if="myListData.length===0">{{ $t('npm.noData') }}</div>
|
||||
<div v-if="!isCollapse" @click="collapse" class="cn-detection__shadow new-cn-detection__shadow"></div>
|
||||
<detection-row
|
||||
style="margin-bottom: 10px"
|
||||
class="detection-border"
|
||||
v-for="(data, index) in listData"
|
||||
v-for="(data, index) in myListData"
|
||||
:detection="data"
|
||||
:page-type="pageType"
|
||||
:timeFilter="timeFilter"
|
||||
@@ -26,6 +26,8 @@
|
||||
<script>
|
||||
import DetectionRow from '@/views/detections/DetectionRow'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
export default {
|
||||
name: 'DetectionList',
|
||||
components: {
|
||||
@@ -49,7 +51,8 @@ export default {
|
||||
collapseIndex: 0,
|
||||
tableId: 'detectionList',
|
||||
listDataCopy: [],
|
||||
noData: false
|
||||
noData: true,
|
||||
myListData: [] // listData的克隆,避免因为修改listData里的malWareName而触发watch监听
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -60,6 +63,25 @@ export default {
|
||||
window.removeEventListener('mousewheel', this.handleScroll)
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
this.myListData = []
|
||||
this.listData.forEach((item, i) => {
|
||||
this.myListData.push(this.$_.cloneDeep(item))
|
||||
if (item.eventInfoObj && item.isBuiltin == 1) {
|
||||
axios.get(`${api.detection.securityEvent.detail}/${item.eventInfoObj.ioc_type.toLowerCase()}?resource=${item.eventInfoObj.ioc_value}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
if (item.eventType === 'Anonymity') {
|
||||
this.myListData[i].darkweb = this.$_.get(res, 'data.data.darkweb', {}) || {}
|
||||
} else if (item.eventType === 'Command and Control') {
|
||||
this.myListData[i].malware = this.$_.get(res, 'data.data.malware', {}) || {}
|
||||
}
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
switchCollapse (isCollapse, index) {
|
||||
this.isCollapse = isCollapse
|
||||
this.collapseIndex = index
|
||||
@@ -84,15 +106,18 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
listData: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler (n) {
|
||||
if (!n || n.length === 0) {
|
||||
this.timeout = setTimeout(() => {
|
||||
this.noData = true
|
||||
this.myListData = []
|
||||
}, 500)
|
||||
} else {
|
||||
clearTimeout(this.timeout)
|
||||
this.noData = false
|
||||
this.initData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="cn-detection__case">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[detection.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[detection.severity]}`"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header" v-if="pageType === detectionPageType.securityEvent">
|
||||
<span
|
||||
class="detection-event-severity-color-block"
|
||||
:style="`background-color: ${eventSeverityColor[detection.eventSeverity]}`">
|
||||
</span>
|
||||
<span class="detection-event-severity-block">{{ detection.securityType || '-' }}</span>
|
||||
<i class="cn-icon cn-icon-attacker" ></i>{{detection.offenderIp || '-'}}
|
||||
<span class="detection-event-severity-block">{{ detection.eventName || '-' }}</span>
|
||||
<i class="cn-icon cn-icon-attacker detection-list-icon" ></i>{{detection.offenderIp || '-'}}
|
||||
<div v-if="detection.domain" class="domain">{{detection.domain}}</div>
|
||||
<span class="line">-------</span>
|
||||
<span class="circle"></span>
|
||||
<i class="cn-icon cn-icon-attacked" ></i>{{detection.victimIp || '-'}}
|
||||
<i class="cn-icon cn-icon-attacked detection-list-icon" ></i>{{detection.victimIp || '-'}}
|
||||
</div>
|
||||
<div class="cn-detection__header" v-else-if="pageType === detectionPageType.performanceEvent">
|
||||
<div class="cn-entity__icon"><i :class="iconClass"></i></div>
|
||||
@@ -30,10 +30,10 @@
|
||||
<div class="cn-detection__body">
|
||||
<div class="body__basic-info">
|
||||
<div class="basic-info">
|
||||
<div class="basic-info__item" v-if="detection.eventSecurity">
|
||||
<div class="basic-info__item" v-if="detection.severity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{$t('detection.list.security')}} : </span>
|
||||
<span>{{detection.eventSecurity || '-'}}</span>
|
||||
<span>{{detection.severity || '-'}}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="detection.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
@@ -45,15 +45,15 @@
|
||||
<span>{{$t('detections.eventType')}} : </span>
|
||||
<span>{{detection.eventType || '-'}}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="detection.malwareName">
|
||||
<div class="basic-info__item" v-if="detection.malware">
|
||||
<i class="cn-icon cn-icon-trojan"></i>
|
||||
<span>{{$t('detection.list.malwareName')}} : </span>
|
||||
<span>{{detection.malwareName || '-'}}</span>
|
||||
<span>{{ $_.get(detection, 'malware.malwareName', '-') || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="detection.cryptominingPool">
|
||||
<i class="cn-icon cn-icon-mining-pool"></i>
|
||||
<span>{{$t('detection.list.cryptominingPool')}} : </span>
|
||||
<span>{{detection.cryptominingPool || '-'}}</span>
|
||||
<div class="basic-info__item" v-if="detection.darkweb">
|
||||
<i class="cn-icon cn-icon-trojan"></i>
|
||||
<span>{{$t('detection.nodeType')}} : </span>
|
||||
<span>{{ $_.get(detection, 'darkweb.nodeType', '-') || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
@@ -63,9 +63,11 @@
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-duration"></i>
|
||||
<span>{{$t('overall.duration')}} : </span>
|
||||
<span style="display: inline-block;height: 6px;width: 6px;border-radius: 50%;margin-right: 8px;"
|
||||
:style="pointColor(detection)"></span>
|
||||
<span>{{unitConvert(detection.durationMs, 'time', null, null, 0).join(' ') || '-'}}</span>
|
||||
<span>
|
||||
{{ detection.matchTimes || '-'}} {{ $t('detection.list.times') }} /
|
||||
{{unitConvert(parseInt(detection.durationS), 'time', 's', null, 0).join(' ') || '-'}}
|
||||
</span>
|
||||
<div v-if="parseInt(detection.status) === 0" class="margin-l-10 detection-row-active">{{$t('detections.active')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,7 +110,7 @@
|
||||
|
||||
<script>
|
||||
import { eventSeverityColor, detectionPageType, entityType } from '@/utils/constants'
|
||||
import { getMillisecond } from '@/utils/date-util'
|
||||
import { getMillisecond, dateFormatByAppearance } from '@/utils/date-util'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import DetectionSecurityEventOverview from '@/views/detections/overview/DetectionSecurityEventOverview'
|
||||
import DetectionPerformanceEventIpOverview from '@/views/detections/overview/DetectionPerformanceEventIpOverview'
|
||||
@@ -174,9 +176,19 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isCollapse (newVal) {
|
||||
const newQuery = this.$route.query
|
||||
if (newVal && newQuery.eventId) {
|
||||
delete newQuery.eventId
|
||||
this.reloadUrl(newQuery, 'cleanOldParams')
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
getMillisecond,
|
||||
dateFormatByAppearance,
|
||||
/* 切换折叠状态 */
|
||||
switchCollapse () {
|
||||
this.isCollapse = !this.isCollapse
|
||||
@@ -235,3 +247,17 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.detection-row-active {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
padding: 0 7px;
|
||||
background: #E9EFE1;
|
||||
border-radius: 2px;
|
||||
font-family: NotoSansHans-Medium;
|
||||
font-size: 12px;
|
||||
color: #7E9F54;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,23 +5,26 @@
|
||||
<advanced-search
|
||||
ref="search"
|
||||
:column-list="columnList[pageType]"
|
||||
:operator-list="operatorList"
|
||||
:connection-list="connectionList"
|
||||
:full-text="false"
|
||||
:default-mode="defaultMode"
|
||||
class="advanced-search--show-list"
|
||||
:full-text="true"
|
||||
:show-list="showList"
|
||||
@search="search"
|
||||
></advanced-search>
|
||||
</div>
|
||||
<div class="search-symbol-inline">
|
||||
<i class="cn-icon cn-icon-help"></i>
|
||||
</div>
|
||||
<!-- <div class="search-symbol-inline">-->
|
||||
<!-- <i class="cn-icon cn-icon-help"></i>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdvancedSearch from '@/components/advancedSearch/Index'
|
||||
import { humpToLine } from '@/utils/tools'
|
||||
import { schemaDetectionSecurity } from '@/utils/static-data'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
export default {
|
||||
name: 'DetectionSearch',
|
||||
props: {
|
||||
@@ -33,74 +36,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
columnList: {
|
||||
securityEvent: [
|
||||
{
|
||||
name: 'event_severity',
|
||||
type: 'string',
|
||||
// label: 'Event severity',
|
||||
label: 'event_severity',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'security_type',
|
||||
type: 'string',
|
||||
// label: 'Security type',
|
||||
label: 'security_type',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'victim_ip',
|
||||
type: 'string',
|
||||
// label: 'Victim IP'
|
||||
label: 'victim_ip',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'victim_location_country',
|
||||
type: 'string',
|
||||
// label: 'Victim location'
|
||||
label: 'victim_location_country',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'offender_ip',
|
||||
type: 'string',
|
||||
// label: 'Offender IP'
|
||||
label: 'offender_ip',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'offender_location_country',
|
||||
type: 'string',
|
||||
// label: 'Offender location'
|
||||
label: 'offender_location_country',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
securityEvent: schemaDetectionSecurity,
|
||||
performanceEvent: [
|
||||
{
|
||||
name: 'event_severity',
|
||||
@@ -113,17 +49,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'event_type',
|
||||
type: 'string',
|
||||
// label: 'Event type'
|
||||
label: 'event_type',
|
||||
doc: {
|
||||
constraints: {
|
||||
operator_functions: '=,in'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'app_name',
|
||||
type: 'string',
|
||||
@@ -169,10 +94,19 @@ export default {
|
||||
value: 'OR',
|
||||
label: 'OR'
|
||||
}
|
||||
]
|
||||
],
|
||||
showList: true
|
||||
}
|
||||
},
|
||||
emits: ['search'],
|
||||
setup () {
|
||||
// 根据地址栏添加mode,即text和tag模式,默认text
|
||||
const { query } = useRoute()
|
||||
const defaultMode = ref(query.mode || 'text')
|
||||
return {
|
||||
defaultMode
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* search (metaList, formatSql) {
|
||||
let sql = formatSql
|
||||
@@ -196,7 +130,7 @@ export default {
|
||||
if (params.oldValue.length === 0 && params.newValue.length === 1) {
|
||||
// 1.参数值数量从0到1,直接addParams
|
||||
const p = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: '=',
|
||||
value: params.newValue
|
||||
}
|
||||
@@ -204,7 +138,7 @@ export default {
|
||||
} else if (params.oldValue.length === 1 && params.newValue.length === 0) {
|
||||
// 2.参数值数量从1到0,直接removeParams
|
||||
const p = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: '=',
|
||||
value: params.oldValue
|
||||
}
|
||||
@@ -212,12 +146,12 @@ export default {
|
||||
} else if (params.oldValue.length === 2 && params.newValue.length === 1) {
|
||||
// 3.参数值数量从多到1,operator由'in'改为'='
|
||||
const oldParam = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: 'IN',
|
||||
value: params.oldValue
|
||||
}
|
||||
const newParam = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: '=',
|
||||
value: params.newValue
|
||||
}
|
||||
@@ -225,12 +159,12 @@ export default {
|
||||
} else if (params.oldValue.length === 1 && params.newValue.length === 2) {
|
||||
// 4.参数值数量从1到多, operator由'='改为'in'
|
||||
const oldParam = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: '=',
|
||||
value: params.oldValue
|
||||
}
|
||||
const newParam = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: 'IN',
|
||||
value: params.newValue
|
||||
}
|
||||
@@ -238,12 +172,12 @@ export default {
|
||||
} else {
|
||||
// 5.参数值数量从多到多,加1或者减1
|
||||
const oldParam = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: 'IN',
|
||||
value: params.oldValue
|
||||
}
|
||||
const newParam = {
|
||||
column: humpToLine(params.column),
|
||||
column: params.column,
|
||||
operator: 'IN',
|
||||
value: params.newValue
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="entity-explorer entity-explorer--show-list">
|
||||
<div class="entity-explorer entity-explorer--show-list detections">
|
||||
<!-- 顶部工具栏,在列表页显示 -->
|
||||
<div class="explorer-top-tools explorer-detection-top-tools">
|
||||
<div class="explorer-top-tools-title">{{$t('overall.detections')}}</div>
|
||||
@@ -33,7 +33,7 @@
|
||||
@search="search"
|
||||
></detection-search>
|
||||
<!-- 内容区 -->
|
||||
<div class="explorer-container" style="height: calc(100% - 20px);flex-direction: column">
|
||||
<div class="detections__container">
|
||||
<loading :loading="loading"></loading>
|
||||
<template v-if="isEventSeverityNoData">
|
||||
<div class="no-data detection__event-severity-bar" >{{ $t('npm.noData') }}</div>
|
||||
@@ -54,39 +54,36 @@
|
||||
<div class="detection__list-statistics detection-border">
|
||||
<div class="statistics__severity">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header__title">{{$t('detection.severity')}}</div>
|
||||
<div class="chart-header__title">{{$t('detections.severity')}}</div>
|
||||
</div>
|
||||
<template v-if="isStatisticsSeverityNoData">
|
||||
<div class="no-data chart-content" >{{ $t('npm.noData') }}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="chart-content" :id="`eventSeverityPie${pageType}`">
|
||||
</div>
|
||||
<div class="chart-content" :id="`eventSeverityPie${pageType}`"></div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
<div class="statistics__category">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header__title">{{$t('detection.categoryProportion')}}</div>
|
||||
<div class="chart-header__title">{{$t('detections.eventType')}}</div>
|
||||
</div>
|
||||
<template v-if="isStatisticsCategoryNoData">
|
||||
<div class="no-data chart-content" >{{ $t('npm.noData') }}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="chart-content" :id="`detectionCategoryPer${pageType}`">
|
||||
</div>
|
||||
<div class="chart-content" :id="`detectionCategoryPer${pageType}`"></div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="statistics__active-attack">
|
||||
<div class="chart-header">
|
||||
<div class="chart-header__title">{{pageType === detectionPageType.securityEvent ? $t('detection.activeAttacker') : $t('detections.activeEntity')}}</div>
|
||||
<div class="chart-header__title">{{pageType === detectionPageType.securityEvent ? $t('detection.activeOffender') : $t('detections.activeEntity')}}</div>
|
||||
</div>
|
||||
<template v-if="isStatisticsActiveAttackNoData">
|
||||
<div class="no-data chart-content" >{{ $t('npm.noData') }}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="chart-content" style="padding-left: 5px;" :id="`detectionActiveAttacker${pageType}`">
|
||||
</div>
|
||||
<div class="chart-content" style="padding-left: 5px;" :id="`detectionActiveAttacker${pageType}`"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,7 +121,7 @@ import DetectionFilter from '@/views/detections/DetectionFilter'
|
||||
import DetectionList from '@/views/detections/DetectionList'
|
||||
import Pagination from '@/components/common/Pagination'
|
||||
import { defaultPageSize, detectionPageType } from '@/utils/constants'
|
||||
import { getNowTime, getSecond, toTime } from '@/utils/date-util'
|
||||
import { getNowTime, getSecond, getMillisecond } from '@/utils/date-util'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import {
|
||||
@@ -134,12 +131,13 @@ import {
|
||||
multipleBarOption,
|
||||
pieForSeverity
|
||||
} from '@/views/detections/options/detectionOptions'
|
||||
import { api, getData } from '@/utils/api'
|
||||
import { api } from '@/utils/api'
|
||||
import axios from 'axios'
|
||||
import { extensionEchartY, reverseSortBy } from '@/utils/tools'
|
||||
import { urlParamsHandler, overwriteUrl, extensionEchartY, reverseSortBy } from '@/utils/tools'
|
||||
import { useRoute } from 'vue-router'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import ChartTabs from '@/components/common/ChartTabs'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
@@ -160,18 +158,18 @@ export default {
|
||||
i18n: 'entities.securityEvents',
|
||||
path: '/detection/securityEvent',
|
||||
icon: 'cn-icon cn-icon-a-SecurityEvent'
|
||||
},
|
||||
}
|
||||
// {
|
||||
// i18n: 'entities.regulatoryRiskEvents',
|
||||
// path: '/detection/securityEvent',
|
||||
// icon: 'cn-icon cn-icon-a-RegulatoryRiskEvent',
|
||||
// disable: true
|
||||
// },
|
||||
{
|
||||
i18n: 'overall.performanceEvents',
|
||||
path: '/detection/performanceEvent',
|
||||
icon: 'cn-icon cn-icon-a-PerformanceEvent'
|
||||
}
|
||||
// {
|
||||
// i18n: 'overall.performanceEvents',
|
||||
// path: '/detection/performanceEvent',
|
||||
// icon: 'cn-icon cn-icon-a-PerformanceEvent'
|
||||
// }
|
||||
],
|
||||
chartInit: [],
|
||||
pageObj: {
|
||||
@@ -185,61 +183,73 @@ export default {
|
||||
filterData: {
|
||||
securityEvent: [
|
||||
{
|
||||
title: this.$t('detections.eventSeverity'),
|
||||
column: 'eventSeverity',
|
||||
title: this.$t('overall.status'),
|
||||
column: 'status',
|
||||
topColumn: 'status',
|
||||
collapse: false,
|
||||
value: [], // value之间是or的关系
|
||||
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
|
||||
},
|
||||
{
|
||||
title: this.$t('detections.securityType'),
|
||||
column: 'securityType',
|
||||
title: this.$t('detections.severity'),
|
||||
column: 'severity',
|
||||
topColumn: 'severity',
|
||||
collapse: false,
|
||||
value: [], // value之间是or的关系
|
||||
data: [] // 从接口动态获取,本项在获得数据后需要特殊处理左边框颜色
|
||||
},
|
||||
{
|
||||
title: this.$t('detections.eventType'),
|
||||
column: 'eventType',
|
||||
topColumn: 'event_type',
|
||||
collapse: false,
|
||||
value: [],
|
||||
data: [] // 从接口动态获取
|
||||
},
|
||||
{
|
||||
title: this.$t('detections.victimIp'),
|
||||
column: 'victimIp',
|
||||
column: 'victimIP',
|
||||
topColumn: 'victim_ip',
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: true,
|
||||
showIndex: 9,
|
||||
data: [] // 从接口动态获取
|
||||
},
|
||||
{
|
||||
title: this.$t('detections.victimLocation'),
|
||||
column: 'victimLocationCountry',
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: false,
|
||||
showIndex: 9,
|
||||
data: [
|
||||
{
|
||||
label: 'China',
|
||||
value: 'china',
|
||||
count: 50
|
||||
}
|
||||
] // 从接口动态获取
|
||||
},
|
||||
// {
|
||||
// title: this.$t('detections.victimLocation'),
|
||||
// column: 'victimLocationCountry',
|
||||
// collapse: false,
|
||||
// value: [],
|
||||
// showMore: false,
|
||||
// showIndex: 9,
|
||||
// data: [
|
||||
// {
|
||||
// label: 'China',
|
||||
// value: 'china',
|
||||
// count: 50
|
||||
// }
|
||||
// ] // 从接口动态获取
|
||||
// },
|
||||
{
|
||||
title: this.$t('detections.offenderIp'),
|
||||
column: 'offenderIp',
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: false,
|
||||
showIndex: 9,
|
||||
data: [] // 从接口动态获取
|
||||
},
|
||||
{
|
||||
title: this.$t('detections.offenderLocation'),
|
||||
column: 'offenderLocationCountry',
|
||||
column: 'offenderIP',
|
||||
topColumn: 'offender_ip',
|
||||
collapse: false,
|
||||
value: [],
|
||||
showMore: false,
|
||||
showIndex: 9,
|
||||
data: [] // 从接口动态获取
|
||||
}
|
||||
// {
|
||||
// title: this.$t('detections.offenderLocation'),
|
||||
// column: 'offenderLocationCountry',
|
||||
// collapse: false,
|
||||
// value: [],
|
||||
// showMore: false,
|
||||
// showIndex: 9,
|
||||
// data: [] // 从接口动态获取
|
||||
// }
|
||||
],
|
||||
performanceEvent: [
|
||||
{
|
||||
@@ -275,24 +285,47 @@ export default {
|
||||
isStatisticsCategoryNoData: false,
|
||||
isStatisticsActiveAttackNoData: false,
|
||||
loading: false,
|
||||
oldActiveEntitySearchValue: ''
|
||||
oldActiveEntitySearchValue: '',
|
||||
initFlag: true // 初始化标识,初始化时保证mounted执行
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 初始化顶部大柱状图
|
||||
initStatusData (params) {
|
||||
axios.get(api.detection[this.pageType].statusStatistics, { params }).then(res => {
|
||||
if (res.status === 200) {
|
||||
const data = res.data.data.result
|
||||
this.filterData[this.pageType][0].data = data.map(r => {
|
||||
let label = ''
|
||||
if (r.status === '0') {
|
||||
label = this.$t('detections.active')
|
||||
} else if (r.status === '1') {
|
||||
label = this.$t('detections.ended')
|
||||
}
|
||||
return { label, value: r.status, count: r.count }
|
||||
})
|
||||
this.isCheckFilterByQ(params, 0)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][0].data = []
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
/** 初始化顶部大柱状图 */
|
||||
initEventSeverityTrendData (params) {
|
||||
this.loading = true
|
||||
/* getData(api.detection[this.pageType].eventSeverityTrend, params).then(data => {
|
||||
axios.get(api.detection[this.pageType].timeDistribution, { params }).then(res => {
|
||||
const data = res.data.data.result
|
||||
this.eventSeverityData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
const dataMap = new Map()
|
||||
data.forEach(item => {
|
||||
if (item.eventSeverity) {
|
||||
if (!dataMap.has(item.eventSeverity)) {
|
||||
const count = [[toTime(item.statTime), item.count]]
|
||||
dataMap.set(item.eventSeverity, count)
|
||||
if (item.severity) {
|
||||
if (!dataMap.has(item.severity)) {
|
||||
const count = [[getMillisecond(parseFloat(item.statTime)), item.count]]
|
||||
dataMap.set(item.severity, count)
|
||||
} else {
|
||||
dataMap.get(item.eventSeverity).push([toTime(item.statTime), item.count])
|
||||
dataMap.get(item.severity).push([getMillisecond(parseFloat(item.statTime)), item.count])
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -331,12 +364,6 @@ export default {
|
||||
serie.data = seriesData
|
||||
})
|
||||
|
||||
// eventSeverityTrendOption.xAxis.data = dataMap.get('info').map(v => rTime(v[0]))
|
||||
|
||||
eventSeverityTrendOption.xAxis = [{
|
||||
type: 'time',
|
||||
splitNumber: 8
|
||||
}]
|
||||
let detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
if (detectionChart) {
|
||||
echarts.dispose(detectionChart)
|
||||
@@ -348,32 +375,26 @@ export default {
|
||||
} else {
|
||||
// this.isEventSeverityNoData = true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
}).finally(() => {
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}) */
|
||||
const timer = setTimeout(() => {
|
||||
this.loading = false
|
||||
this.isEventSeverityNoData = true
|
||||
this.isStatisticsCategoryNoData = true
|
||||
this.isStatisticsSeverityNoData = true
|
||||
this.isStatisticsActiveAttackNoData = true
|
||||
clearTimeout(timer)
|
||||
}, 150)
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化左侧事件严重等级和小饼图
|
||||
/** 初始化左侧事件严重等级和第一个小饼图 */
|
||||
initEventSeverityData (params) {
|
||||
getData(api.detection[this.pageType].eventSeverity, params).then(data => {
|
||||
axios.get(api.detection[this.pageType].severityStatistics, { params }).then(res => {
|
||||
const data = res.data.data.result
|
||||
this.statisticsSeverityData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
this.filterData[this.pageType][0].data = data.map(r => ({ label: r.eventSeverity, value: r.eventSeverity, count: r.count }))
|
||||
this.filterData[this.pageType][1].data = data.map(r => ({ label: r.severity, value: r.severity, count: r.count }))
|
||||
this.isCheckFilterByQ(params, 1)
|
||||
const eventSeverityOption = this.$_.cloneDeep(pieForSeverity)
|
||||
eventSeverityOption.series[0].data = data.map(d => {
|
||||
return { value: d.count, name: d.eventSeverity, itemStyle: { color: getSeverityColor(d.eventSeverity) } }
|
||||
return { value: d.count, name: d.severity, itemStyle: { color: getSeverityColor(d.severity) } }
|
||||
})
|
||||
const chartDom = document.getElementById(`eventSeverityPie${this.pageType}`)
|
||||
let detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
@@ -389,19 +410,22 @@ export default {
|
||||
if (this.pageType === 'performanceEvent') {
|
||||
vm.filterData.performanceEvent[0].value = vm.triggerFilterDataValue(vm.filterData.performanceEvent[0].value, e.data.name)
|
||||
} else if (this.pageType === 'securityEvent') {
|
||||
vm.filterData.securityEvent[0].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[0].value, e.data.name)
|
||||
vm.filterData.securityEvent[1].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[1].value, e.data.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][1].data = []
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
initEventTypeData (params) {
|
||||
getData(api.detection[this.pageType].eventType, params).then(data => {
|
||||
axios.get(api.detection[this.pageType].eventType, { params }).then(res => {
|
||||
const data = res.data.data.result
|
||||
this.statisticsCategoryData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
this.filterData[this.pageType][1].data = data.map(r => ({
|
||||
this.filterData[this.pageType][2].data = data.map(r => ({
|
||||
label: r.eventType,
|
||||
value: r.eventType,
|
||||
count: r.count
|
||||
@@ -425,19 +449,24 @@ export default {
|
||||
vm.filterData.performanceEvent[1].value = vm.triggerFilterDataValue(vm.filterData.performanceEvent[1].value, e.data.name)
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][2].data = []
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
/** 第二个饼图和左侧filter的eventType */
|
||||
initSecurityTypeData (params) {
|
||||
getData(api.detection[this.pageType].securityType, params).then(data => {
|
||||
axios.get(api.detection[this.pageType].eventTypeStatistics, { params }).then(res => {
|
||||
const data = res.data.data.result
|
||||
this.statisticsCategoryData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
this.filterData[this.pageType][1].data = data.map(r => ({
|
||||
label: r.securityType,
|
||||
value: r.securityType,
|
||||
this.filterData[this.pageType][2].data = data.map(r => ({
|
||||
label: r.eventType,
|
||||
value: r.eventType,
|
||||
count: r.count
|
||||
}))
|
||||
this.isCheckFilterByQ(params, 2)
|
||||
const chartDom = document.getElementById(`detectionCategoryPer${this.pageType}`)
|
||||
let detectionChart = echarts.getInstanceByDom(chartDom)
|
||||
if (detectionChart) {
|
||||
@@ -447,22 +476,26 @@ export default {
|
||||
this.chartInit.push(shallowRef(detectionChart))
|
||||
const securityTypeOption = this.$_.cloneDeep(pieForSeverity)
|
||||
securityTypeOption.series[0].data = data.map(d => {
|
||||
return { value: d.count, name: d.securityType, itemStyle: { color: getAttackColor(d.securityType) } }
|
||||
return { value: d.count, name: d.eventType, itemStyle: { color: getAttackColor(d.eventType) } }
|
||||
})
|
||||
detectionChart.setOption(securityTypeOption)
|
||||
|
||||
const vm = this
|
||||
detectionChart.off('click')
|
||||
detectionChart.on('click', e => {
|
||||
vm.filterData.securityEvent[1].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[1].value, e.data.name)
|
||||
vm.filterData.securityEvent[2].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[2].value, e.data.name)
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][2].data = []
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
/** 横向柱状图和左侧filter的offenderIp */
|
||||
initOffenderIpData (params) {
|
||||
getData(api.detection[this.pageType].offenderIp, params).then(data => {
|
||||
axios.get(api.detection[this.pageType].offenderIpStatistics, { params }).then(res => {
|
||||
let data = res.data.data.result
|
||||
this.statisticsActiveAttackData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
this.filterData[this.pageType][4].data = data.map(r => ({
|
||||
@@ -470,6 +503,7 @@ export default {
|
||||
value: r.offenderIp,
|
||||
count: r.count
|
||||
}))
|
||||
this.isCheckFilterByQ(params, 4)
|
||||
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][4].data)
|
||||
this.filterData[this.pageType][4].showMore = showMore
|
||||
this.filterData[this.pageType][4].showIndex = showIndex
|
||||
@@ -495,43 +529,34 @@ export default {
|
||||
vm.filterData.securityEvent[4].value = vm.triggerFilterDataValue(vm.filterData.securityEvent[4].value, e.data[1])
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][4].data = []
|
||||
this.filterData[this.pageType][4].showMore = false
|
||||
this.filterData[this.pageType][4].showIndex = 9
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
|
||||
initVictimIpData (params) {
|
||||
getData(api.detection[this.pageType].victimIp, params).then(data => {
|
||||
this.filterData[this.pageType][2].data = data.map(r => ({ label: r.victimIp, value: r.victimIp, count: r.count }))
|
||||
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][2].data)
|
||||
this.filterData[this.pageType][2].showMore = showMore
|
||||
this.filterData[this.pageType][2].showIndex = showIndex
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
initVictimLocationData (params) {
|
||||
getData(api.detection[this.pageType].victimLocation, params).then(data => {
|
||||
this.filterData[this.pageType][3].data = data.map(r => ({ label: r.victimLocationCountry, value: r.victimLocationCountry, count: r.count }))
|
||||
axios.get(api.detection[this.pageType].victimIpStatistics, { params }).then(res => {
|
||||
const data = res.data.data.result
|
||||
this.filterData[this.pageType][3].data = data.map(r => ({ label: r.victimIp, value: r.victimIp, count: r.count }))
|
||||
this.isCheckFilterByQ(params, 3)
|
||||
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][3].data)
|
||||
this.filterData[this.pageType][3].showMore = showMore
|
||||
this.filterData[this.pageType][3].showIndex = showIndex
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
},
|
||||
initOffenderLocationData (params) {
|
||||
getData(api.detection[this.pageType].offenderLocation, params).then(data => {
|
||||
this.filterData[this.pageType][5].data = data.map(r => ({ label: r.offenderLocationCountry, value: r.offenderLocationCountry, count: r.count }))
|
||||
const { showMore, showIndex } = this.computeFilterPage(this.filterData[this.pageType][5].data)
|
||||
this.filterData[this.pageType][5].showMore = showMore
|
||||
this.filterData[this.pageType][5].showIndex = showIndex
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.filterData[this.pageType][3].data = []
|
||||
this.filterData[this.pageType][3].showMore = false
|
||||
this.filterData[this.pageType][3].showIndex = 9
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
})
|
||||
},
|
||||
initActiveEntity (params) {
|
||||
getData(api.detection[this.pageType].activeEntity, params).then(data => {
|
||||
axios.get(api.detection[this.pageType].activeEntity, { params }).then(res => {
|
||||
let data = res.data.data.result
|
||||
this.statisticsActiveAttackData = data
|
||||
if (!this.$_.isEmpty(data)) {
|
||||
const chartDom = document.getElementById(`detectionActiveAttacker${this.pageType}`)
|
||||
@@ -591,7 +616,7 @@ export default {
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
console.error(error)
|
||||
})
|
||||
},
|
||||
triggerFilterDataValue (array, value) {
|
||||
@@ -610,34 +635,49 @@ export default {
|
||||
showIndex: 9
|
||||
}
|
||||
},
|
||||
queryList () {
|
||||
queryList (q) {
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime),
|
||||
q: this.q,
|
||||
resource: q,
|
||||
pageSize: this.pageObj.pageSize,
|
||||
pageNo: this.pageObj.pageNo
|
||||
}
|
||||
/* axios.get(api.detection[this.pageType].listBasic, { params }).then(response => {
|
||||
axios.get(api.detection[this.pageType].securityList, { params }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.listData = response.data.data.result
|
||||
const data = response.data.data.result
|
||||
if (data.length > 0) {
|
||||
data.forEach(item => {
|
||||
item.eventInfoObj = JSON.parse(item.eventInfo)
|
||||
item.startTime = parseFloat(item.startTime)
|
||||
})
|
||||
this.listData = data
|
||||
} else {
|
||||
this.listData = []
|
||||
}
|
||||
} else {
|
||||
this.listData = []
|
||||
console.error(response.data.message)
|
||||
this.$message.error(response.data.message)
|
||||
}
|
||||
})
|
||||
getData(api.detection[this.pageType].listCount, params).then(data => {
|
||||
this.pageObj.total = data
|
||||
axios.get(api.detection[this.pageType].securityCount, { params }).then(res => {
|
||||
this.pageObj.total = parseInt(this.$_.get(res, 'data.data.result', 0))
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
}) */
|
||||
console.error(error)
|
||||
})
|
||||
},
|
||||
timeRefreshChange () {
|
||||
this.initNoData()
|
||||
if (!this.$refs.dateTimeRange.isCustom) {
|
||||
const value = this.timeFilter.dateRangeValue
|
||||
this.$refs.dateTimeRange.quickChange(value)
|
||||
// 不是自选时间
|
||||
if (this.$refs.dateTimeRange) {
|
||||
if (!this.$refs.dateTimeRange.isCustom) {
|
||||
const value = this.timeFilter.dateRangeValue
|
||||
this.$refs.dateTimeRange.quickChange(value)
|
||||
} else {
|
||||
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
|
||||
}
|
||||
} else {
|
||||
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
|
||||
}
|
||||
},
|
||||
initNoData () {
|
||||
@@ -646,9 +686,16 @@ export default {
|
||||
this.isStatisticsCategoryNoData = false
|
||||
this.isStatisticsActiveAttackNoData = false
|
||||
},
|
||||
reload (s, e, v) {
|
||||
reload (startTime, endTime, dateRangeValue) {
|
||||
this.initNoData()
|
||||
this.dateTimeRangeChange(s, e, v)
|
||||
this.dateTimeRangeChange(startTime, endTime, dateRangeValue)
|
||||
const { query } = this.$route
|
||||
const newUrl = urlParamsHandler(window.location.href, query, {
|
||||
startTime: this.timeFilter.startTime,
|
||||
endTime: this.timeFilter.endTime,
|
||||
range: dateRangeValue.value
|
||||
})
|
||||
overwriteUrl(newUrl)
|
||||
},
|
||||
// methods
|
||||
dateTimeRangeChange (s, e, v) {
|
||||
@@ -674,8 +721,26 @@ export default {
|
||||
} else {
|
||||
this.pageObj.resetPageNo = true
|
||||
}
|
||||
this.queryFilter()
|
||||
this.queryList()
|
||||
// 参数q,避免切换页码时,地址栏参数q为空
|
||||
let urlQ = ''
|
||||
if (param && param.str) {
|
||||
// urlQ = encodeURI(param.str)
|
||||
urlQ = param.str
|
||||
} else if (this.q) {
|
||||
// urlQ = encodeURI(this.q)
|
||||
urlQ = this.q
|
||||
}
|
||||
const mode = this.$route.query.mode || 'text'
|
||||
const newUrl = urlParamsHandler(window.location.href, this.$route.query, {
|
||||
startTime: this.timeFilter.startTime,
|
||||
endTime: this.timeFilter.endTime,
|
||||
range: this.timeFilter.dateRangeValue,
|
||||
q: urlQ,
|
||||
mode: mode
|
||||
})
|
||||
overwriteUrl(newUrl)
|
||||
this.queryFilter(urlQ)
|
||||
this.queryList(urlQ)
|
||||
},
|
||||
resetFilterData () {
|
||||
this.filterData.securityEvent.forEach(d => {
|
||||
@@ -685,25 +750,23 @@ export default {
|
||||
d.data = []
|
||||
})
|
||||
},
|
||||
queryFilter () {
|
||||
queryFilter (q) {
|
||||
this.resetFilterData()
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime),
|
||||
q: this.q
|
||||
resource: q
|
||||
}
|
||||
this.listData = []
|
||||
this.initStatusData(params)
|
||||
this.initEventSeverityTrendData(params)
|
||||
// this.initEventSeverityData(params)
|
||||
this.initEventSeverityData(params)
|
||||
if (this.pageType === detectionPageType.securityEvent) {
|
||||
// this.initOffenderIpData(params)
|
||||
// this.initOffenderLocationData(params)
|
||||
// this.initVictimIpData(params)
|
||||
// this.initVictimLocationData(params)
|
||||
// this.initSecurityTypeData(params)
|
||||
this.initOffenderIpData(params)
|
||||
this.initVictimIpData(params)
|
||||
this.initSecurityTypeData(params)
|
||||
} else if (this.pageType === detectionPageType.performanceEvent) {
|
||||
// this.initActiveEntity(params)
|
||||
// this.initEventTypeData(params)
|
||||
this.initActiveEntity(params)
|
||||
this.initEventTypeData(params)
|
||||
}
|
||||
},
|
||||
pageSize (val) {
|
||||
@@ -713,7 +776,11 @@ export default {
|
||||
pageNo (val) {
|
||||
this.pageObj.pageNo = val || 1
|
||||
this.pageObj.resetPageNo = false
|
||||
this.search(this.metaList, this.q)
|
||||
// 初始化时,mounted和pageNo都会调用列表接口,且pageNo先执行,
|
||||
// 初始化保证mounted执行,后续pageNo变动则不影响接口调用
|
||||
if (!this.initFlag) {
|
||||
this.search(this.metaList, this.q)
|
||||
}
|
||||
},
|
||||
// 点击上一页箭头
|
||||
prev () {
|
||||
@@ -739,16 +806,48 @@ export default {
|
||||
},
|
||||
jumpNewDetetion () {
|
||||
this.$router.push({
|
||||
path: '/detectionsNew',
|
||||
path: '/detectionPolicy',
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
},
|
||||
isCheckFilterByQ (params, index) {
|
||||
if (params.resource) {
|
||||
let obj
|
||||
if (index === 0) {
|
||||
obj = this.filterData[this.pageType][index].data.find(d => params.resource.indexOf(d.value) > -1 && params.resource.indexOf('status') > -1)
|
||||
} else {
|
||||
obj = this.filterData[this.pageType][index].data.find(d => params.resource.indexOf(d.value) > -1)
|
||||
}
|
||||
if (obj) {
|
||||
this.filterData[this.pageType][index].value = [obj.value]
|
||||
this.filterData[this.pageType][index].flag = true
|
||||
}
|
||||
} else {
|
||||
this.filterData[this.pageType][index].value = []
|
||||
this.filterData[this.pageType][index].flag = true
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.queryFilter()
|
||||
this.queryList()
|
||||
let { q } = this.$route.query
|
||||
|
||||
// 如果地址栏有listMode,即列表页,并非首页,则开始搜索
|
||||
if (q) {
|
||||
// %位置为0是输入中文时能解码,%20,25%分别是空格和%的情况
|
||||
if (q && (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1)) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
// %位置不为0,即内容包含非英文时
|
||||
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
}
|
||||
this.queryFilter(q)
|
||||
this.initFlag = false
|
||||
this.queryList(q)
|
||||
this.debounceFunc = this.$_.debounce(this.resize, 300)
|
||||
window.addEventListener('resize', this.debounceFunc)
|
||||
},
|
||||
@@ -821,37 +920,61 @@ export default {
|
||||
'filterData.securityEvent.0.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[0].column, oldValue: o, newValue: n })
|
||||
if (!this.filterData.securityEvent[0].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[0].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[0].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.1.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[1].column, oldValue: o, newValue: n })
|
||||
if (!this.filterData.securityEvent[1].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[1].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[1].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.2.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[2].column, oldValue: o, newValue: n })
|
||||
if (!this.filterData.securityEvent[2].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[2].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[2].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.3.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[3].column, oldValue: o, newValue: n })
|
||||
if (!this.filterData.securityEvent[3].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[3].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[3].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.4.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[4].column, oldValue: o, newValue: n })
|
||||
if (!this.filterData.securityEvent[4].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[4].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[4].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.securityEvent.5.value': {
|
||||
deep: true,
|
||||
handler (n, o) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[5].column, oldValue: o, newValue: n })
|
||||
if (!this.filterData.securityEvent[5].flag) {
|
||||
this.$refs.search.changeParams({ column: this.filterData.securityEvent[5].column, oldValue: o, newValue: n })
|
||||
} else {
|
||||
this.filterData.securityEvent[5].flag = false
|
||||
}
|
||||
}
|
||||
},
|
||||
'filterData.performanceEvent.0.value': {
|
||||
@@ -871,12 +994,37 @@ export default {
|
||||
window.removeEventListener('resize', this.debounceFunc)
|
||||
},
|
||||
setup () {
|
||||
const { params } = useRoute()
|
||||
const pageType = params.typeName
|
||||
const dateRangeValue = 60
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
const timeFilter = ref({ startTime, endTime, dateRangeValue })
|
||||
const store = useStore()
|
||||
let { params, query, path } = useRoute()
|
||||
// 获取路由跳转过的历史状态,赋值给当前界面,起到保留状态的作用,如浏览器的回退前进等
|
||||
const routerObj = store.getters.getRouterHistoryList.find(item => item.t === query.t)
|
||||
if (routerObj) {
|
||||
params = routerObj.params
|
||||
query = routerObj.query
|
||||
path = routerObj.path
|
||||
|
||||
// 如果当前界面之前载入过,获取状态后更新地址栏,以便后续的赋值操作
|
||||
const newUrl = urlParamsHandler(window.location.href, useRoute().query, query)
|
||||
overwriteUrl(newUrl)
|
||||
}
|
||||
const pageType = params.typeName
|
||||
// 获取url携带的range、startTime、endTime
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
timeFilter.value.startTime = getSecond(startTime)
|
||||
timeFilter.value.endTime = getSecond(endTime)
|
||||
// 如果没有时间参数,就将参数写入url
|
||||
const newUrl = urlParamsHandler(window.location.href, useRoute().query, { startTime: timeFilter.value.startTime, endTime: timeFilter.value.endTime, range: dateRangeValue })
|
||||
overwriteUrl(newUrl)
|
||||
} else {
|
||||
timeFilter.value.startTime = parseInt(startTimeParam)
|
||||
timeFilter.value.endTime = parseInt(endTimeParam)
|
||||
}
|
||||
return {
|
||||
timeFilter,
|
||||
pageType
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
<template>
|
||||
<div class="detection">
|
||||
<div class="detection-title">
|
||||
<span>{{ $t('overall.detections') }}</span>
|
||||
<span>{{ $t('overall.policies') }}</span>
|
||||
<span class="detection-title-label">
|
||||
60 polices created(200 max) | 32 polices enabled(100 max)
|
||||
{{ $t('detection.policesCreated', { total: policyTotal }) }} | {{ $t('detection.policesEnabled', { enabled: policyEnabledNum }) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="detection-content">
|
||||
<div class="detection-filter">
|
||||
<detection-filter></detection-filter>
|
||||
<detection-filter @filterParams="getFilterParams" @policyTotal="getPolicyTotal" />
|
||||
</div>
|
||||
|
||||
<div class="detection-block">
|
||||
<detection-tools
|
||||
@delete="toDelete"
|
||||
@create="onCreate"
|
||||
@edit="onEdit"
|
||||
@search="onSearch"
|
||||
:disableEdit="disableEdit"
|
||||
:disableDelete="disableDelete"/>
|
||||
|
||||
<div class="detection-table" style="position: relative">
|
||||
@@ -32,7 +34,6 @@
|
||||
:all-count="18"
|
||||
@selectionChange="selectionChange"
|
||||
@reload="reloadRowList"
|
||||
@toggleLoading="toggleLoading"
|
||||
@rowDoubleClick="onRowDoubleClick"
|
||||
></detection-table>
|
||||
</div>
|
||||
@@ -61,10 +62,8 @@
|
||||
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="ruleId" label="ID" width="150"></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">
|
||||
@@ -83,12 +82,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DetectionFilter from '@/views/detectionsNew/DetectionFilter'
|
||||
import DetectionTools from '@/views/detectionsNew/DetectionTools'
|
||||
import DetectionTable from '@/views/detectionsNew/DetectionTable'
|
||||
import DetectionFilter from '@/views/detections/detectionPolicies/PolicyFilter'
|
||||
import DetectionTools from '@/views/detections/detectionPolicies/PolicyTools'
|
||||
import DetectionTable from '@/views/detections/detectionPolicies/PolicyTable'
|
||||
import { api } from '@/utils/api'
|
||||
import dataListMixin from '@/mixins/data-list'
|
||||
import DetectionDrawer from '@/views/detectionsNew/DetectionDrawer'
|
||||
import DetectionDrawer from '@/views/detections/detectionPolicies/PolicyDrawer'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
@@ -101,8 +101,8 @@ export default {
|
||||
mixins: [dataListMixin],
|
||||
data () {
|
||||
return {
|
||||
// url: api.detection.list,
|
||||
url: api.knowledgeBase,
|
||||
url: api.detection.list,
|
||||
// url: api.knowledgeBase,
|
||||
listUrl: api.detection.list,
|
||||
tableId: 'detectionTable',
|
||||
isNoData: false,
|
||||
@@ -110,42 +110,52 @@ export default {
|
||||
isSelectedStatus: false,
|
||||
batchDeleteObjs: [], // 待删除列表
|
||||
secondBatchDeleteObjs: [],
|
||||
disableEdit: true,
|
||||
disableDelete: true,
|
||||
showConfirmDialog: false,
|
||||
delItemList: [],
|
||||
showDrawer: false,
|
||||
drawerInfo: {}
|
||||
drawerInfo: {},
|
||||
filterParams: {},
|
||||
policyTotal: 0,
|
||||
policyEnabledNum: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSearch () {
|
||||
// todo 暂时禁用,后续再开发时解禁
|
||||
// const params = {
|
||||
// ...this.filterParams,
|
||||
// name: this.keyWord
|
||||
// }
|
||||
// this.clearList()
|
||||
// this.search(params)
|
||||
// this.$refs.knowledgeFilter.reloadFilter(this.checkedCategoryIds, this.checkedStatusIds)
|
||||
onSearch (keyWord) {
|
||||
this.filterParams = {
|
||||
...this.filterParams,
|
||||
name: keyWord
|
||||
}
|
||||
this.search(this.filterParams, 'detection')
|
||||
},
|
||||
toDelete (data) {
|
||||
// todo 暂时禁用,后续再开发时解禁
|
||||
// if (data && data.ruleId) {
|
||||
// this.secondBatchDeleteObjs = []
|
||||
// this.batchDeleteObjs = []
|
||||
// this.secondBatchDeleteObjs.push(data)
|
||||
// this.batchDeleteObjs.push(data)
|
||||
// }
|
||||
// this.showDelDialog()
|
||||
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()
|
||||
// }
|
||||
// })
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy/create',
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
},
|
||||
onEdit () {
|
||||
const pageNo = this.$router.currentRoute.value.query.pageNo
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy/edit',
|
||||
query: {
|
||||
t: +new Date(),
|
||||
pageNoForTable: pageNo || 1,
|
||||
id: this.batchDeleteObjs[0].ruleId
|
||||
}
|
||||
})
|
||||
},
|
||||
selectionChange (objs) {
|
||||
this.batchDeleteObjs = []
|
||||
@@ -161,8 +171,6 @@ export default {
|
||||
reloadRowList () {
|
||||
this.getTableData()
|
||||
},
|
||||
toggleLoading () {
|
||||
},
|
||||
showDelDialog () {
|
||||
this.showConfirmDialog = true
|
||||
this.$nextTick(() => {
|
||||
@@ -177,16 +185,6 @@ export default {
|
||||
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
|
||||
@@ -205,40 +203,64 @@ export default {
|
||||
}).catch(() => {
|
||||
})
|
||||
} else {
|
||||
// todo 调用接口删除
|
||||
// this.toggleLoading(true)
|
||||
// axios.delete(api.detection.delete + '?ruleIds=' + ids).then(response => {
|
||||
// if (response.status === 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
|
||||
// }
|
||||
// })
|
||||
this.toggleLoading(true)
|
||||
axios.delete(api.detection.delete + '?ruleIds=' + ids).then(response => {
|
||||
if (response.status === 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.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) {
|
||||
this.isSelectedStatus = false
|
||||
this.disableDelete = true
|
||||
this.secondBatchDeleteObjs = []
|
||||
this.batchDeleteObjs = []
|
||||
this.showConfirmDialog = false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onRowDoubleClick (data) {
|
||||
// todo 暂时禁用,后续再开发时解禁
|
||||
// this.showDrawer = true
|
||||
// this.drawerInfo = data
|
||||
this.showDrawer = true
|
||||
this.drawerInfo = data
|
||||
},
|
||||
getFilterParams (params) {
|
||||
const delList = []
|
||||
if (params.status) {
|
||||
this.filterParams.status = params.status
|
||||
} else {
|
||||
delete this.filterParams.status
|
||||
delList.push('status')
|
||||
}
|
||||
if (params.category) {
|
||||
this.filterParams.category = params.category
|
||||
} else {
|
||||
delete this.filterParams.category
|
||||
delList.push('category')
|
||||
}
|
||||
if (params.eventType) {
|
||||
this.filterParams.eventType = params.eventType
|
||||
} else {
|
||||
delete this.filterParams.eventType
|
||||
delList.push('eventType')
|
||||
}
|
||||
|
||||
this.search(this.filterParams, 'detection', delList)
|
||||
},
|
||||
getPolicyTotal (total, enabledNum) {
|
||||
this.policyTotal = total
|
||||
this.policyEnabledNum = enabledNum
|
||||
}
|
||||
}
|
||||
}
|
||||
252
src/views/detections/detectionPolicies/PolicyDrawer.vue
Normal file
252
src/views/detections/detectionPolicies/PolicyDrawer.vue
Normal file
@@ -0,0 +1,252 @@
|
||||
<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${drawerInfo.status}`">
|
||||
{{ $t(switchStatus(drawerInfo.status)) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('overall.name') }}</div>
|
||||
<div class="basic-function-value">{{ $_.get(detailData, 'name', '-') || '-'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('overall.type') }}</div>
|
||||
<div class="basic-function-value">{{ $_.get(detailData, 'eventType', '-') || '-'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-description">
|
||||
<div class="detection-drawer-title">{{ $t('config.dataSet.description') }}</div>
|
||||
<div class="basic-description-value">{{ $_.get(detailData, 'description', '-') || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detection-drawer-collapse">
|
||||
<el-collapse v-model="activeRule">
|
||||
<el-collapse-item :title="$t('detection.ruleDefinition')" name="rule">
|
||||
<div class="drawer-collapse-content">
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('config.user.source') }}</div>
|
||||
<div class="basic-function-value">{{ changeCategory(detailData.category) }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="detailData.ruleType==='indicator_match'">
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('detection.library') }}</div>
|
||||
<span class="basic-function-value">{{ $_.get(detailData, 'ruleConfigObj.knowledgeBase.name', '-') || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('detection.level') }}</div>
|
||||
<div class="detection-drawer-title">
|
||||
<div class="detection__icon" :style="`background-color: ${eventSeverityColor[detailData.ruleConfigObj.level]}`"></div>
|
||||
<div class="basic-function-value">{{ changeSecurityLevel(detailData.ruleConfigObj) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('detection.create.dimensions') }}</div>
|
||||
<span class="detection-tag-blue">{{ detailData.dimensions }}</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('detections.filters') }}</div>
|
||||
<span class="detection-tag-blue">Source Port</span>
|
||||
<span style="margin: 0 6px;">{{ $t('detections.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">{{ $t('detections.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="$t('detection.create.trigger')" name="trigger">
|
||||
<div class="drawer-collapse-content" v-if="language==='en'">
|
||||
<div class="drawer-collapse-trigger">
|
||||
Triggered when conditions occur at least
|
||||
<span style="color: #046ECA">
|
||||
{{ atLeast }} {{ times }}
|
||||
</span> in
|
||||
<span style="color: #046ECA">
|
||||
{{ getNumberFromStr($_.get(detailData, 'ruleTriggerObj.interval', '0')) || '-' }}
|
||||
{{ $_.get(detailData, 'ruleTriggerObj.intervalVal', '-') || '-' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">{{ $t('detection.evaluationFrequency') }}</div>
|
||||
<div class="drawer-trigger-minutes">
|
||||
{{ getNumberFromStr($_.get(detailData, 'ruleTriggerObj.resetInterval', '0')) || '-' }}
|
||||
{{ $_.get(detailData, 'ruleTriggerObj.intervalVal', '-') || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-collapse-content" v-if="language==='cn'">
|
||||
<div class="drawer-collapse-trigger">
|
||||
当条件为
|
||||
<span style="color: #046ECA">
|
||||
{{ getNumberFromStr($_.get(detailData, 'ruleTriggerObj.interval', '0')) || '-' }}
|
||||
{{ changeValueToLabel(detailData.ruleTriggerObj) }}
|
||||
</span>内至少出现
|
||||
<span style="color: #046ECA">
|
||||
{{ $_.get(detailData, 'ruleTriggerObj.atLeast', '-') || '-' }} 次
|
||||
</span>时触发
|
||||
</div>
|
||||
|
||||
<div class="drawer-basic-function">
|
||||
<div class="detection-drawer-title">评估频率</div>
|
||||
<div class="drawer-trigger-minutes">
|
||||
{{ getNumberFromStr($_.get(detailData, 'ruleTriggerObj.resetInterval', '0')) || '-' }}
|
||||
{{ changeValueToLabel(detailData.ruleTriggerObj) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { switchStatus, toUpperCaseByString } from '@/utils/tools'
|
||||
import { detectionUnitList, eventSeverityColor, securityLevel, storageKey } 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: [],
|
||||
language: 'en',
|
||||
atLeast: 0,
|
||||
times: 'time'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
drawerInfo: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler (n) {
|
||||
if (n) {
|
||||
this.getDetailData()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
},
|
||||
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.status === 200) {
|
||||
this.detailData = res.data.data
|
||||
this.atLeast = this.$_.get(this.detailData, 'ruleTriggerObj.atLeast', '-')
|
||||
if (!isNaN(this.atLeast) && this.atLeast > 1) {
|
||||
this.times = 'times'
|
||||
} else {
|
||||
this.times = 'time'
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
},
|
||||
getNumberFromStr (str) {
|
||||
return str.match(/\d+(\.\d+)?/g)[0]
|
||||
},
|
||||
changeCategory (value) {
|
||||
if (value) {
|
||||
const obj = detectionUnitList.categoryList.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
},
|
||||
changeSecurityLevel (config) {
|
||||
if (config) {
|
||||
if (config.level) {
|
||||
const obj = securityLevel.find(d => d.value === config.level)
|
||||
let label = config.level
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
},
|
||||
changeValueToLabel (config) {
|
||||
if (config) {
|
||||
if (config.intervalVal) {
|
||||
const obj = detectionUnitList.intervalListCN.find(d => d.value === config.intervalVal)
|
||||
let label = config.intervalVal
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
128
src/views/detections/detectionPolicies/PolicyFilter.vue
Normal file
128
src/views/detections/detectionPolicies/PolicyFilter.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="new-detection-filter-title">
|
||||
{{ $t('detections.filters') }}
|
||||
</div>
|
||||
<div class="new-detection-filter-content">
|
||||
<div>
|
||||
<div class="new-filter-content-title">{{ $t('overall.status') }}</div>
|
||||
<div class="new-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.name" class="new-filter-content-checkbox" :label="item.status">
|
||||
<div>{{ item.name }}</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="new-filter-content-title">{{ $t('overall.category') }}</div>
|
||||
<div class="new-filter-content-content">
|
||||
<el-checkbox-group v-model="checkCategory" @change="onChangeCategory">
|
||||
<el-checkbox v-for="item in categoryList" :key="item.name" class="new-filter-content-checkbox" :label="item.name">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="new-filter-content-title">{{ $t('overall.type') }}</div>
|
||||
<div class="new-filter-content-content">
|
||||
<el-checkbox-group v-model="checkEventType" @change="onChangeCategory" style="display: flex;flex-direction: column">
|
||||
<el-checkbox v-for="item in eventTypeList" :key="item.name" class="new-filter-content-checkbox" :label="item.name">
|
||||
{{ item.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
import { detectionUnitList } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'DetectionFilter',
|
||||
data () {
|
||||
return {
|
||||
statusList: [], // 状态列表数据
|
||||
categoryList: [], // 类别列表
|
||||
eventTypeList: [], // 事件类型列表
|
||||
checkStatus: [], // checkbox选择的状态列表
|
||||
checkCategory: [], // checkbox选择的类别
|
||||
checkEventType: [], // checkbox选择的事件类型
|
||||
policyTotal: 0, // 策略总条数,与筛选条件无关
|
||||
policyEnabledNum: 0 // 策略中状态为enabled的数量
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getFilterData()
|
||||
},
|
||||
methods: {
|
||||
getFilterData () {
|
||||
axios.get(api.detection.statistics).then(response => {
|
||||
if (response.status === 200) {
|
||||
const data = response.data.data
|
||||
if (data.statusList) {
|
||||
data.statusList.forEach(item => {
|
||||
this.policyTotal += item.count
|
||||
if (item.status === 1) {
|
||||
this.policyEnabledNum = item.count
|
||||
}
|
||||
this.statusList.push({ status: item.status, name: this.$t(switchStatus(item.status)) })
|
||||
})
|
||||
this.$emit('policyTotal', this.policyTotal, this.policyEnabledNum)
|
||||
} else {
|
||||
this.statusList = []
|
||||
}
|
||||
|
||||
if (data.categoryList) {
|
||||
this.categoryList = []
|
||||
data.categoryList.forEach(item => {
|
||||
const obj = detectionUnitList.categoryList.find(d => d.value === item.name)
|
||||
if (obj) {
|
||||
this.categoryList.push({ ...item, label: this.$t(obj.label) })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.categoryList = []
|
||||
}
|
||||
|
||||
if (data.eventTypeList) {
|
||||
this.eventTypeList = data.eventTypeList
|
||||
} else {
|
||||
this.eventTypeList = []
|
||||
}
|
||||
} else {
|
||||
console.error(response.data)
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.error(e)
|
||||
this.statusList = []
|
||||
this.categoryList = []
|
||||
this.eventTypeList = []
|
||||
})
|
||||
},
|
||||
onChangeCategory () {
|
||||
const obj = {}
|
||||
if (this.checkStatus.length === 1) {
|
||||
obj.status = this.checkStatus.join(',')
|
||||
} else {
|
||||
delete obj.status
|
||||
}
|
||||
if (this.checkCategory.length > 0) {
|
||||
obj.category = this.checkCategory.join(',')
|
||||
}
|
||||
if (this.checkEventType.length > 0) {
|
||||
obj.eventType = this.checkEventType.join(',')
|
||||
}
|
||||
this.$emit('filterParams', obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
497
src/views/detections/detectionPolicies/PolicyForm.vue
Normal file
497
src/views/detections/detectionPolicies/PolicyForm.vue
Normal file
@@ -0,0 +1,497 @@
|
||||
<template>
|
||||
<div class="detection-form">
|
||||
<loading :loading="myLoading"></loading>
|
||||
|
||||
<div class="detection-form-header">
|
||||
{{ ruleId ? $t('detection.editEventPolicies') : $t('detection.createEventPolicies') }}
|
||||
</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">{{ $t('detection.create.generalSettings') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="form-collapse-content">
|
||||
<general-settings ref="form" :editObj="editObj" :isComplete="isComplete" @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">{{ $t('detection.create.ruleDefinition') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="form-collapse-content">
|
||||
<rule-definition :settingObj="settingObj" :editObj="editObj" :isComplete="isComplete" @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">{{ $t('detection.create.trigger') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="form-collapse-content margin-t-18">
|
||||
<el-form v-if="language==='en'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<div class="trigger-block-item margin-b-10">
|
||||
<div>At least</div>
|
||||
<el-form-item prop="atLeast">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
<div>times within</div>
|
||||
|
||||
<el-form-item prop="interval" class="policy-form-item">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.interval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="intervalVal">
|
||||
<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="resetInterval" class="policy-form-item">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="resetIntervalVal">
|
||||
<el-select v-model="triggerObj.resetIntervalVal" class="form-trigger__select" placeholder=" " size="mini">
|
||||
<el-option
|
||||
v-for="item in intervalList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<el-form v-if="language==='cn'" class="trigger-block margin-b-20" ref="form3" :model="triggerObj" :rules="rules">
|
||||
<div class="trigger-block-item margin-b-10">
|
||||
在
|
||||
<el-form-item prop="interval">
|
||||
<el-input size="mini" maxlength="5" 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>
|
||||
内至少发生
|
||||
<el-form-item prop="atLeast">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.atLeast" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
次
|
||||
</div>
|
||||
|
||||
<div class="trigger-block-item">
|
||||
若连续
|
||||
<el-form-item prop="resetInterval">
|
||||
<el-input size="mini" maxlength="5" v-model="triggerObj.resetInterval" oninput="value=value.replace(/[^\d]/g,'')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="resetIntervalVal">
|
||||
<el-select v-model="triggerObj.resetIntervalVal" class="form-trigger__select" placeholder=" " size="mini">
|
||||
<el-option
|
||||
v-for="item in intervalList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
不活跃将重置计数
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div class="form-setting__btn1">
|
||||
<div class="btn1">
|
||||
<el-button @click="createPolicy('')">{{ $t('overall.save') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="createPolicy('enabled')">{{ $t('overall.save') }} & {{ $t('detection.create.enablePolicy') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GeneralSettings from '@/components/table/detection/GeneralSettings'
|
||||
import RuleDefinition from '@/components/table/detection/RuleDefinition'
|
||||
import { api } from '@/utils/api'
|
||||
import axios from 'axios'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import { getDurationsTimeByType, getTimeByDurations } from '@/utils/date-util'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { storageKey, detectionUnitList } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'DetectionForm',
|
||||
data () {
|
||||
const intervalValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, value, this.triggerObj.intervalVal)
|
||||
if (!obj.flag && obj.msg) {
|
||||
callback(new Error(obj.msg))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const intervalValValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, this.triggerObj.intervalVal, value)
|
||||
if (!obj.flag && obj.msg) {
|
||||
this.$refs.form3.validateField('interval')
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const resetIntervalValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, value, this.triggerObj.resetIntervalVal)
|
||||
if (!obj.flag && obj.msg) {
|
||||
callback(new Error(obj.msg))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const resetIntervalValValidator = (rule, value, callback) => {
|
||||
const obj = this.handleIntervalByDateType(rule, this.triggerObj.resetIntervalVal, value)
|
||||
if (!obj.flag && obj.msg) {
|
||||
this.$refs.form3.validateField('resetInterval')
|
||||
callback()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
activeNames: ['1'],
|
||||
rules: {
|
||||
atLeast: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
interval: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: intervalValidator,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
intervalVal: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'change'
|
||||
},
|
||||
{
|
||||
validator: intervalValValidator,
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
resetInterval: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'blur'
|
||||
},
|
||||
{
|
||||
validator: resetIntervalValidator,
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
resetIntervalVal: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('validate.required'),
|
||||
trigger: 'change'
|
||||
},
|
||||
{
|
||||
validator: resetIntervalValValidator,
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
},
|
||||
intervalList: [],
|
||||
editObj: {},
|
||||
isComplete: true, // 参数完整标识,默认完整(照顾编辑模式),false即不完整
|
||||
language: 'en'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
GeneralSettings,
|
||||
RuleDefinition,
|
||||
Loading
|
||||
},
|
||||
mounted () {
|
||||
this.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
if (this.language === 'en') {
|
||||
this.intervalList = detectionUnitList.intervalList
|
||||
} else if (this.language === 'cn') {
|
||||
this.intervalList = detectionUnitList.intervalListCN
|
||||
}
|
||||
this.getDetailInfo()
|
||||
},
|
||||
setup () {
|
||||
const { query } = useRoute()
|
||||
const ruleId = ref(query.id || '')
|
||||
const pageNoForTable = ref(query.pageNoForTable || 1)
|
||||
const myLoading = ref(!!ruleId.value)
|
||||
// General Settings,即第一步的form表单信息
|
||||
const settingObj = ref({})
|
||||
// 第二步的form表单信息
|
||||
const ruleObj = ref({})
|
||||
// 第三步的form表单信息
|
||||
const triggerObj = ref({
|
||||
atLeast: '',
|
||||
interval: '',
|
||||
intervalVal: '',
|
||||
resetInterval: '',
|
||||
resetIntervalVal: '',
|
||||
finishFlag: false
|
||||
})
|
||||
|
||||
return {
|
||||
ruleId,
|
||||
myLoading,
|
||||
pageNoForTable,
|
||||
settingObj,
|
||||
ruleObj,
|
||||
triggerObj
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 编辑时获取详情 */
|
||||
getDetailInfo () {
|
||||
if (this.ruleId) {
|
||||
axios.get(`${api.detection.detail}/${this.ruleId}`).then(response => {
|
||||
if (response.status === 200) {
|
||||
if (!response.data.data) {
|
||||
throw new Error('No data found, id: ' + this.ruleId)
|
||||
}
|
||||
this.myLoading = false
|
||||
this.editObj = { ...response.data.data, ruleId: this.ruleId }
|
||||
this.settingObj = this.$_.cloneDeep(this.editObj)
|
||||
this.settingObj.editFlag = false
|
||||
this.settingObj.saveFlag = true
|
||||
this.ruleObj = this.$_.cloneDeep(this.editObj.ruleConfigObj)
|
||||
this.triggerObj = this.$_.cloneDeep(this.editObj.ruleTriggerObj)
|
||||
this.triggerObj.intervalVal = getTimeByDurations(this.triggerObj.interval).type
|
||||
this.triggerObj.interval = getTimeByDurations(this.triggerObj.interval).value
|
||||
this.triggerObj.resetIntervalVal = getTimeByDurations(this.triggerObj.resetInterval).type
|
||||
this.triggerObj.resetInterval = getTimeByDurations(this.triggerObj.resetInterval).value
|
||||
this.activeNames = ['1', '2', '3']
|
||||
} else {
|
||||
console.error(response.data)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
query: {
|
||||
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
/** 获取General Settings折叠板form数据 */
|
||||
getFormSetting (data) {
|
||||
this.handleActiveNames('1', this.activeNames, data.settingNoContinue)
|
||||
this.settingObj = JSON.parse(JSON.stringify(data))
|
||||
},
|
||||
/** 获取Rule Definition折叠板form数据 */
|
||||
getRuleObj (data) {
|
||||
this.handleActiveNames('2', this.activeNames, data.ruleNoContinue)
|
||||
this.ruleObj = JSON.parse(JSON.stringify(data))
|
||||
},
|
||||
/** 自动展开收起折叠板 */
|
||||
handleActiveNames (name, arr, flag) {
|
||||
if (!flag) {
|
||||
const list = arr
|
||||
list.splice(list.indexOf(name), 1)
|
||||
if (name === '1' && list.indexOf('2') < 0) {
|
||||
list.push('2')
|
||||
}
|
||||
if (name === '2' && list.indexOf('3') < 0) {
|
||||
list.push('3')
|
||||
}
|
||||
this.activeNames = []
|
||||
list.forEach(t => {
|
||||
this.activeNames.push(t)
|
||||
})
|
||||
}
|
||||
},
|
||||
/** 创建policy */
|
||||
createPolicy (flag) {
|
||||
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.$_.cloneDeep({ ...this.settingObj, ruleConfig: JSON.stringify(this.ruleObj), ruleTrigger: this.triggerObj })
|
||||
if (flag) {
|
||||
formObj.status = 1
|
||||
}
|
||||
// 将时间转为参数所需,如5分钟转为PT5M
|
||||
formObj.ruleTrigger.resetInterval = getDurationsTimeByType(formObj.ruleTrigger.resetInterval, formObj.ruleTrigger.resetIntervalVal)
|
||||
formObj.ruleTrigger.interval = getDurationsTimeByType(formObj.ruleTrigger.interval, formObj.ruleTrigger.intervalVal)
|
||||
formObj.ruleTrigger.atLeast = parseInt(formObj.ruleTrigger.atLeast)
|
||||
formObj.ruleTrigger = JSON.stringify(formObj.ruleTrigger)
|
||||
// 删除多余参数
|
||||
delete formObj.ruleConfigObj
|
||||
delete formObj.ruleTriggerObj
|
||||
delete formObj.editFlag
|
||||
delete formObj.saveFlag
|
||||
|
||||
if (!this.ruleId) {
|
||||
// post调用是新增,put是编辑
|
||||
this.myLoading = true
|
||||
axios.post(api.detection.create.create, formObj).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.$message({
|
||||
duration: 2000,
|
||||
type: 'success',
|
||||
message: this.$t('tip.saveSuccess')
|
||||
})
|
||||
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.error(response.data.message)
|
||||
this.$message.error(this.errorMsgHandler(response))
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
}).finally(() => {
|
||||
this.myLoading = false
|
||||
})
|
||||
} else {
|
||||
console.log('进来')
|
||||
this.myLoading = true
|
||||
axios.put(api.detection.create.create, formObj).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.$message({
|
||||
duration: 2000,
|
||||
type: 'success',
|
||||
message: this.$t('tip.saveSuccess')
|
||||
})
|
||||
|
||||
this.$router.push({
|
||||
path: '/detectionPolicy',
|
||||
query: {
|
||||
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.error(response.data.message)
|
||||
this.$message.error(this.errorMsgHandler(response))
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.$message.error(this.errorMsgHandler(e))
|
||||
}).finally(() => {
|
||||
this.myLoading = false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.isComplete = false
|
||||
}
|
||||
})
|
||||
} else if (settingLen === 0) {
|
||||
this.isComplete = false
|
||||
this.handleFormError('1')
|
||||
} else if (ruleLen === 0) {
|
||||
this.isComplete = false
|
||||
this.handleFormError('2')
|
||||
}
|
||||
},
|
||||
handleFormError (name) {
|
||||
const list = this.activeNames
|
||||
if (list.indexOf(name) < 0) {
|
||||
list.push(name)
|
||||
this.activeNames = []
|
||||
list.forEach(t => {
|
||||
this.activeNames.push(t)
|
||||
})
|
||||
}
|
||||
this.$message.error(this.$t('detection.create.informationFilled'))
|
||||
},
|
||||
handleIntervalByDateType (rule, value, type) {
|
||||
if (value && (type === 'hours' || type === '小时')) {
|
||||
if (parseInt(value) <= 24) {
|
||||
return { flag: true }
|
||||
} else {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeHours') }
|
||||
}
|
||||
}
|
||||
if (value && (type === 'minutes' || type === '分钟')) {
|
||||
if (parseInt(value) <= 1440) {
|
||||
return { flag: true }
|
||||
} else {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeMinutes') }
|
||||
}
|
||||
}
|
||||
if (value && (type === 'seconds' || type === '秒')) {
|
||||
if (parseInt(value) <= 86400) {
|
||||
return { flag: true }
|
||||
} else {
|
||||
return { flag: false, msg: this.$t('policy.dateTimeRangeSeconds') }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -50,9 +50,12 @@
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'status'">
|
||||
<div :class="`detection-tag-status${scope.row[item.prop]}`">
|
||||
{{ switchStatus(scope.row[item.prop]) }}
|
||||
{{ $t(switchStatus(scope.row[item.prop])) }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'category'">
|
||||
{{ changeCategory(scope.row[item.prop]) }}
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'description'">
|
||||
<div style="padding-right: 20px">{{ scope.row[item.prop] }}</div>
|
||||
</template>
|
||||
@@ -77,6 +80,8 @@
|
||||
import table from '@/mixins/table'
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { switchStatus } from '@/utils/tools'
|
||||
import _ from 'lodash'
|
||||
import { detectionUnitList } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'DetectionTable',
|
||||
@@ -127,15 +132,13 @@ export default {
|
||||
show: true
|
||||
},
|
||||
{
|
||||
// label: this.$t('config.user.createTime'),
|
||||
label: 'Dimensions',
|
||||
label: this.$t('detection.create.dimensions'),
|
||||
prop: 'dimensions',
|
||||
minWidth: 204,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
// label: this.$t('config.user.createTime'),
|
||||
label: 'Library',
|
||||
label: this.$t('detection.library'),
|
||||
prop: 'library',
|
||||
minWidth: 204,
|
||||
show: true
|
||||
@@ -151,9 +154,9 @@ export default {
|
||||
if (n) {
|
||||
n.forEach(t => {
|
||||
if (t.ruleType === 'indicator_match') {
|
||||
t.library = t.ruleConfig.knowledge.name
|
||||
t.library = _.get(t, 'ruleConfigObj.knowledgeBase.name', '-')
|
||||
} else if (t.ruleType === 'threshold') {
|
||||
t.dimensions = t.ruleConfig.dimensions
|
||||
t.dimensions = _.get(t, 'ruleConfigObj.dimensions', '-')
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -165,6 +168,16 @@ export default {
|
||||
switchStatus,
|
||||
rowDoubleClick (data) {
|
||||
this.$emit('rowDoubleClick', data)
|
||||
},
|
||||
changeCategory (value) {
|
||||
if (value) {
|
||||
const obj = detectionUnitList.categoryList.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,16 @@
|
||||
<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="disableEdit"
|
||||
id="knowledge-base-edit"
|
||||
:title="$t('knowledgeBase.editKnowledgeBase')"
|
||||
class="top-tool-btn margin-r-10"
|
||||
@click="onEdit"
|
||||
style="width:72px;">
|
||||
<i class="cn-icon-edit cn-icon" ></i>
|
||||
<span>{{$t('overall.edit')}}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
:disabled="disableDelete"
|
||||
@@ -51,6 +53,10 @@ export default {
|
||||
disableDelete: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disableEdit: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@@ -65,6 +71,9 @@ export default {
|
||||
onCreate () {
|
||||
this.$emit('create')
|
||||
},
|
||||
onEdit () {
|
||||
this.$emit('edit')
|
||||
},
|
||||
onDelete (data) {
|
||||
this.$emit('delete', data)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from '@/views/charts/charts/tools'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import _ from 'lodash'
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { dateFormatByAppearance, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
import { unitTypes } from '@/utils/constants'
|
||||
|
||||
const severitySeriesIndexMappings = [
|
||||
@@ -67,8 +67,8 @@ export const multipleBarOption = {
|
||||
source: [
|
||||
]
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
xAxis: [{
|
||||
type: 'time',
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
@@ -77,13 +77,16 @@ export const multipleBarOption = {
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: xAxisTimeFormatter,
|
||||
rich: xAxisTimeRich,
|
||||
color: '#737373',
|
||||
interval: 'auto'
|
||||
},
|
||||
splitNumber: 8,
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
}],
|
||||
yAxis: {
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
@@ -305,6 +308,10 @@ export const metricOption = {
|
||||
str += `<span class="cn-chart-tooltip-value">
|
||||
${unitConvert(item.data[1], unitTypes.time).join(' ')}
|
||||
</span>`
|
||||
} else if (item.seriesName === 'Http error') {
|
||||
str += `<span class="cn-chart-tooltip-value">
|
||||
${unitConvert(item.data[1], unitTypes.number, '', '', 0).join(' ')}
|
||||
</span>`
|
||||
} else {
|
||||
str += `<span class="cn-chart-tooltip-value">
|
||||
${unitConvert(item.data[1], unitTypes.percent, '', '', 0).join(' ')}
|
||||
|
||||
@@ -3,162 +3,243 @@
|
||||
<div class="overview__left">
|
||||
<div class="overview__title">{{ $t('overall.remark') }}</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__content">
|
||||
<template v-if="detection.securityType === 'command and control'">
|
||||
<span
|
||||
class="row__content--link"
|
||||
@click="goDetail('ip', detection.victimIp)"
|
||||
>{{ detection.victimIp }}</span>
|
||||
<span>
|
||||
{{$t('detection.commandAndControl')}}
|
||||
</span>
|
||||
<span class="row__content--link" @click="goDetail('ip', basicInfo.iocValue)">{{basicInfo.iocValue || '-'}}</span></template
|
||||
>
|
||||
<template v-else>
|
||||
<span
|
||||
class="row__content--link"
|
||||
@click="goDetail('ip', detection.victimIp)"
|
||||
>{{ detection.victimIp }}</span
|
||||
>
|
||||
<span>
|
||||
{{$t('detection.payloadAndDelivery')}}
|
||||
</span>
|
||||
<span class="row__content--link"
|
||||
@click="goDetail('ip', basicInfo.iocValue)"
|
||||
>{{
|
||||
basicInfo.iocValue
|
||||
}}</span></template>
|
||||
<div class="row__content1" v-if="detection.eventType === 'Command and Control' && detection.isBuiltin == 1">
|
||||
<span class="row__content--link">{{detection.victimIp}}</span> communicated with <span class="row__content--link">{{detection.offenderIp}}</span> that was associated with the indicator of {{detection.eventName}} activity, {{$_.get(detection, 'eventInfoObj.ioc_value', '') || ''}}.
|
||||
</div>
|
||||
<div class="row__content1" v-else-if="detection.eventType === 'Anonymity' && detection.isBuiltin == 1">
|
||||
<span class="row__content--link">{{detection.victimIp}}</span> communicated with <span class="row__content--link">{{detection.offenderIp}}</span> that was associated with the indicator of {{detection.eventName}}.
|
||||
</div>
|
||||
<div class="row__content1" v-else>
|
||||
{{basicInfo.ruleDescription || '-'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__title">Fields</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.list.startTime') }}</div>
|
||||
<div class="row__content">
|
||||
{{
|
||||
basicInfo.startTime
|
||||
? dateFormatByAppearance(basicInfo.startTime)
|
||||
: '-'
|
||||
}}
|
||||
<i class="cn-icon cn-icon-time2 row__content__icon"></i>
|
||||
{{ detection.startTime ? dateFormatByAppearance(detection.startTime) : '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.victimIp') }}</div>
|
||||
<div class="row__content">{{ basicInfo.victimIp || '-' }}</div>
|
||||
<div class="row__content">{{ detection.victimIp || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.victimLocation') }}</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.victimLocationCountry || '-' }}
|
||||
<div v-if="$_.get(basicInfo, 'victimInfo.location.country')">
|
||||
<img v-if="basicInfo.victimInfo.location.country===countryNameIdMapping.Unknown || !countryNameIdMapping[basicInfo.victimInfo.location.country]" src="../../../../public/images/flag/Unknown.svg" class="filter-country-flag">
|
||||
<img v-else :src="require(`../../../../public/images/flag/${countryNameIdMapping[basicInfo.victimInfo.location.country]}.png`)" class="filter-country-flag" >
|
||||
</div>
|
||||
{{ locationRegion(basicInfo.victimInfo) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.victimAsn') }}</div>
|
||||
<div class="row__content">{{ basicInfo.victimAsn || '-' }}</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'victimInfo.asn.asn', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.offenderIp') }}</div>
|
||||
<div class="row__content">{{ basicInfo.offenderIp || '-' }}</div>
|
||||
<div class="row__content">{{ detection.offenderIp || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.offenderLocation') }}</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.offenderLocationCountry || '-' }}
|
||||
<div v-if="$_.get(basicInfo, 'offenderInfo.location.country')">
|
||||
<img v-if="basicInfo.offenderInfo.location.country===countryNameIdMapping.Unknown || !countryNameIdMapping[basicInfo.offenderInfo.location.country]" src="../../../../public/images/flag/Unknown.svg" class="filter-country-flag">
|
||||
<img v-else :src="require(`../../../../public/images/flag/${countryNameIdMapping[basicInfo.offenderInfo.location.country]}.png`)" class="filter-country-flag" >
|
||||
</div>
|
||||
{{ locationRegion(basicInfo.offenderInfo) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.offenderAsn') }}</div>
|
||||
<div class="row__content">{{ basicInfo.offenderAsn || '-' }}</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'offenderInfo.asn.asn', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('overall.domain') }}</div>
|
||||
<div class="row__content">{{ basicInfo.domain || '-' }}</div>
|
||||
<div class="row__content">{{ detection.domain || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('entities.domainCategory') }}</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.domainCategoryName || '-' }}
|
||||
<template v-if="detection.domain">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('entities.domainCategory') }}</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'domainInfo.category.categoryName', '-') || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">
|
||||
{{ $t('entities.domainDetail.categoryGroup') }}
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('entities.domainDetail.categoryGroup') }}</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'domainInfo.category.categoryGroup', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.domainCategoryGroup || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('entities.reputationLevel') }}</div>
|
||||
<div class="row__content">
|
||||
<div
|
||||
class="row__tag"
|
||||
:style="`background-color:${eventSeverityColor[basicInfo.domainReputationLevel]}`"
|
||||
>
|
||||
{{ basicInfo.domainReputationLevel || '-' }}
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('entities.reputationLevel') }}</div>
|
||||
<div class="row__content" v-if="$_.get(basicInfo, 'domainInfo.category.reputationLevel')">
|
||||
<div
|
||||
class="row__tag row__tag__level"
|
||||
:style="`background-color:${riskLevelColor1[basicInfo.domainInfo.category.reputationLevel]}`">
|
||||
{{ reputationLevel(basicInfo.domainInfo.category.reputationLevel) || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row__content" v-else>-</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">APP</div>
|
||||
<div class="row__content">{{ basicInfo.appName || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">APP {{ $t('entities.category') }}</div>
|
||||
<div class="row__content">{{ basicInfo.appCategory || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">APP {{ $t('entities.subcategory') }}</div>
|
||||
<div class="row__content">{{ basicInfo.appSubcategory || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('overall.appRisk') }}</div>
|
||||
<div class="row__content">
|
||||
<div
|
||||
class="row__tag"
|
||||
:style="`background-color:${eventSeverityColor[basicInfo.appRisk]}`"
|
||||
>
|
||||
{{ basicInfo.appRisk || '-' }}
|
||||
</template>
|
||||
<template v-if="detection.app">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">APP</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'appInfo.category.appName', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">APP {{ $t('entities.category') }}</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'appInfo.category.appCategory', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">APP {{ $t('entities.subcategory') }}</div>
|
||||
<div class="row__content">{{ $_.get(basicInfo, 'appInfo.category.appSubcategory', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('overall.appRisk') }}</div>
|
||||
<div class="row__content" v-if="$_.get(basicInfo, 'appInfo.category.appRisk')">
|
||||
<div
|
||||
class="row__tag row__tag__level"
|
||||
:style="`background-color:${riskLevelColor[basicInfo.appInfo.category.appRisk]}`">
|
||||
{{ appRisk(basicInfo.appInfo.category.appRisk) || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row__content" v-else>-</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malware') }}</div>
|
||||
<div class="row__content">{{ basicInfo.malwareName || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareAlias') }}</div>
|
||||
<div class="row__content">{{ basicInfo.malwareAlias || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareDescription') }}</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.malwareDescription || '-' }}
|
||||
</template>
|
||||
<template v-if="detection.malware">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malware') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'malware.malwareName', '-') || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwarePlatforms') }}</div>
|
||||
<div class="row__content">{{ basicInfo.malwarePlatforms || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareTechniques') }}</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.malwareTechniques || '-' }}
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareAlias') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'malware.malwareAlias', '-') || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareGroups') }}</div>
|
||||
<div class="row__content">
|
||||
{{ basicInfo.malwareGroups || '-' }}
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareDescription') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'malware.mitreAttackDescription', '-') || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.reference') }}</div>
|
||||
<div class="row__content row__content--link">
|
||||
{{ reference || '-' }}
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwarePlatforms') }}</div>
|
||||
<div class="row__content" v-if="$_.get(detection, 'malware.mitreAttackPlatforms')">
|
||||
<svg class="icon item-popover-up row__content__svg" aria-hidden="true">
|
||||
<use xlink:href="#cn-icon-windows"></use>
|
||||
</svg>
|
||||
{{ detection.malware.mitreAttackPlatforms }}
|
||||
</div>
|
||||
<div class="row__content" v-else>-</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <template v-if="detection.securityType === 'command and control' || detection.securityType === 'payload_delivery'">
|
||||
</template>-->
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareTechniques') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'malware.mitreAttackTechniques', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.malwareGroups') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'malware.mitreAttackGroups', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detections.reference') }}</div>
|
||||
<div class="row__content row__content--link" v-if="$_.get(detection, 'malware.reference')">
|
||||
{{ detection.malware.reference }}
|
||||
</div>
|
||||
<div class="row__content">-</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="detection.darkweb">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.nodeTypeLower') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.nodeType', '-') || '-' }}</div>
|
||||
</div>
|
||||
<template v-if="$_.get(detection.darkweb, 'nodeType', '') === 'tor'">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.torFingerprint') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.torFingerprint', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.torFlags') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.torFlags', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.torVersion') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.torVersion', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">Tor ORPort</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.torOrPort', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">Tor DirPort</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.torDirPort', '-') || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="$_.get(detection.darkweb, 'nodeType', '') === 'i2p'">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">I2P Hash</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.i2pHash', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.i2pVersion') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.i2pVersion', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.i2pBandwidth') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.i2pBandwidth', '-') || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="$_.get(detection.darkweb, 'nodeType', '') === 'mtproxy'">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">MTProxy Secret</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.mtproxySecret', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.mtproxyPort') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.mtproxyPort', '-') || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="$_.get(detection.darkweb, 'nodeType', '') === 'obfs4'">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.obfs4Fingerprint') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.obfs4Fingerprint', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.obfs4Cert') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.obfs4Cert', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.obfs4IatMode') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.obfs4IatMode', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.obfs4Port') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.obfs4Port', '-') || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="$_.get(detection.darkweb, 'nodeType', '') === 'snowflake'">
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.tor.snowflakePort') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'darkweb.snowflakePort', '-') || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.libraryId') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'eventInfoObj.knowledge_id', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.libraryName') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'eventInfoObj.name', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.iocType') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'eventInfoObj.ioc_type', '-') || '-' }}</div>
|
||||
</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__label">{{ $t('detection.iocValue') }}</div>
|
||||
<div class="row__content">{{ $_.get(detection, 'eventInfoObj.ioc_value', '-') || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="overview__right">
|
||||
<div class="overview__title">{{ $t('detections.goToVictim') }}</div>
|
||||
@@ -167,7 +248,7 @@
|
||||
<span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span>
|
||||
<span
|
||||
class="row__content--link"
|
||||
@click="goDetail('ip', basicInfo.victimIp)">{{ basicInfo.victimIp }}</span>
|
||||
@click="goDetail('ip', detection.victimIp)">{{ detection.victimIp }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__title">{{ $t('detections.goToOffender') }}</div>
|
||||
@@ -176,22 +257,22 @@
|
||||
<span class="row__content--span">{{ $t('detections.viewDetailOf') }}</span>
|
||||
<span
|
||||
class="row__content--link"
|
||||
@click="goDetail('ip', basicInfo.offenderIp)"
|
||||
>{{ basicInfo.offenderIp }}</span
|
||||
@click="goDetail('ip', detection.offenderIp)"
|
||||
>{{ detection.offenderIp }}</span
|
||||
>
|
||||
<span
|
||||
class="row__content--link"
|
||||
@click="goDetail('domain', basicInfo.domain)"
|
||||
>{{ basicInfo.domain }}</span
|
||||
@click="goDetail('domain', detection.domain)"
|
||||
>{{ detection.domain }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview__title">{{ $t('detections.goToHunt') }}</div>
|
||||
<div class="overview__row">
|
||||
<div class="row__content row__content--link">
|
||||
{{ $t('detections.viewAllRelated') }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="overview__title">{{ $t('detections.goToHunt') }}</div>-->
|
||||
<!-- <div class="overview__row">-->
|
||||
<!-- <div class="row__content row__content--link">-->
|
||||
<!-- {{ $t('detections.viewAllRelated') }}-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="overview__title">
|
||||
{{ $t('detections.relatedDetections') }}
|
||||
</div>
|
||||
@@ -221,18 +302,12 @@
|
||||
<div class="timeline__severity timeline__severity--high">
|
||||
<i
|
||||
class="cn-icon cn-icon-alert-level"
|
||||
:style="`color:${eventSeverityColor[event.eventSeverity]}`"
|
||||
:style="`color:${eventSeverityColor[event.severity]}`"
|
||||
></i>
|
||||
<span>{{ event.eventSeverity }}</span>
|
||||
</div>
|
||||
<div class="timeline__security-type">
|
||||
{{ event.securityType }}
|
||||
</div>
|
||||
<div class="timeline__start-time">
|
||||
{{
|
||||
dateFormatByAppearance(event.startTime)
|
||||
}}
|
||||
<span>{{ event.severity }}</span>
|
||||
</div>
|
||||
<div class="timeline__security-type">{{ event.eventType }}</div>
|
||||
<div class="timeline__start-time">{{ dateFormatByAppearance(parseInt(event.startTime)) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-timeline__foot">
|
||||
@@ -240,7 +315,7 @@
|
||||
class="detection-ip"
|
||||
:class="{
|
||||
'detection-ip__current':
|
||||
[basicInfo.offenderIp, basicInfo.victimIp].indexOf(
|
||||
[detection.offenderIp, detection.victimIp].indexOf(
|
||||
event.offenderIp,
|
||||
) > -1,
|
||||
}"
|
||||
@@ -252,7 +327,7 @@
|
||||
class="detection-ip"
|
||||
:class="{
|
||||
'detection-ip__current':
|
||||
[basicInfo.offenderIp, basicInfo.victimIp].indexOf(
|
||||
[detection.offenderIp, detection.victimIp].indexOf(
|
||||
event.victimIp,
|
||||
) > -1,
|
||||
}"
|
||||
@@ -270,8 +345,8 @@
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { getMillisecond } from '@/utils/date-util'
|
||||
import { eventSeverityColor, unitTypes } from '@/utils/constants'
|
||||
import { getMillisecond, dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, unitTypes, countryNameIdMapping, riskLevelMapping, riskLevelColor, riskLevelColor1 } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import _ from 'lodash'
|
||||
export default {
|
||||
@@ -282,34 +357,27 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
eventSeverityColor,
|
||||
riskLevelColor,
|
||||
riskLevelColor1,
|
||||
basicInfo: {},
|
||||
events: [],
|
||||
reference: 'https://attack.mitre.org'
|
||||
reference: 'https://attack.mitre.org',
|
||||
countryNameIdMapping
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formatT0 () {
|
||||
const vm = this
|
||||
return function (event) {
|
||||
const diffSeconds = event.diffSeconds
|
||||
const diffSeconds = parseInt(event.diffSeconds)
|
||||
if (diffSeconds === 0) {
|
||||
return 'T0'
|
||||
}
|
||||
const eventStartTime = event.startTime
|
||||
const eventStartTime = parseInt(event.startTime)
|
||||
const entityStartTime = vm.detection.startTime
|
||||
|
||||
if (
|
||||
_.isNumber(diffSeconds) &&
|
||||
_.isNumber(eventStartTime) &&
|
||||
_.isNumber(entityStartTime)
|
||||
) {
|
||||
const suffix = unitConvert(
|
||||
diffSeconds,
|
||||
unitTypes.time,
|
||||
's',
|
||||
null,
|
||||
0
|
||||
).join('')
|
||||
if (_.isNumber(diffSeconds) && _.isNumber(eventStartTime) && _.isNumber(entityStartTime)) {
|
||||
const suffix = unitConvert(diffSeconds, unitTypes.time, 's', null, 0).join('')
|
||||
if (eventStartTime > entityStartTime) {
|
||||
return `T0+${suffix}`
|
||||
} else if (eventStartTime < entityStartTime) {
|
||||
@@ -318,62 +386,105 @@ export default {
|
||||
}
|
||||
return ''
|
||||
}
|
||||
},
|
||||
appRisk () {
|
||||
return function (level) {
|
||||
const m = riskLevelMapping.find(mapping => {
|
||||
return mapping.value == level
|
||||
})
|
||||
return (m && this.$t(m.label)) || level
|
||||
}
|
||||
},
|
||||
reputationLevel () {
|
||||
return function (level) {
|
||||
const m = riskLevelMapping.find(mapping => {
|
||||
return mapping.name == level
|
||||
})
|
||||
return (m && this.$t(m.label)) || level
|
||||
}
|
||||
},
|
||||
locationRegion (info) {
|
||||
return function (info) {
|
||||
if (!info || !info.location) {
|
||||
return '-'
|
||||
}
|
||||
let result = ''
|
||||
if (info.location.country) {
|
||||
result += `${info.location.country},`
|
||||
}
|
||||
if (info.location.province) {
|
||||
result += `${info.location.province},`
|
||||
}
|
||||
if (info.location.city) {
|
||||
result += `${info.location.city},`
|
||||
}
|
||||
result = result.substr(0, result.length - 1)
|
||||
if (!result) {
|
||||
result = '-'
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMillisecond,
|
||||
query () {
|
||||
Promise.all([this.queryBasic(), this.queryEvent()]).then((responses) => {
|
||||
responses[0].malwareTechniques = responses[0].malwareTechniques.length > 2 ? responses[0].malwareTechniques.replace('[', '').replace(']', '').split(',', 5).join(', ') : ''
|
||||
responses[0].malwareGroups = responses[0].malwareGroups.length > 2 ? responses[0].malwareGroups.replace('[', '').replace(']', '').split(',', 5).join(', ') : ''
|
||||
responses[0].malwarePlatforms = responses[0].malwarePlatforms.length > 1 ? responses[0].malwarePlatforms : ''
|
||||
responses[0].malwareDescription = responses[0].malwareDescription.length > 1 ? responses[0].malwareDescription : ''
|
||||
responses[0] && (this.basicInfo = responses[0])
|
||||
responses[1] &&
|
||||
(this.events = responses[1].sort(
|
||||
(e1, e2) => e1.startTime - e2.startTime
|
||||
))
|
||||
})
|
||||
},
|
||||
queryBasic () {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
axios.get(api.detection.securityEvent.overviewBasic, {
|
||||
params: {
|
||||
eventId: this.detection.eventId,
|
||||
startTime: this.detection.startTime,
|
||||
endTime: this.detection.endTime
|
||||
}
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
resolve(response.data.data.result[0])
|
||||
} else {
|
||||
reject(response.data)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
dateFormatByAppearance,
|
||||
/** 初始化实体详情 */
|
||||
initEntityDetail () {
|
||||
// 为完整填充IP信息,攻击者ip、受害者ip都进行调用;
|
||||
// 根据detection的eventInfo对象的ioc_type进行判断,若为domain,malware信息从domain详情中获取,并填充domain信息
|
||||
// 若ioc_type为ip,则调用ip接口,填充malware信息
|
||||
// 最后调用app,填充app信息。经上获取完整实体详情则最少需要调用4次接口
|
||||
if (this.detection.offenderIp) {
|
||||
axios.get(`${api.detection.securityEvent.ipDetail}?resource=${this.detection.offenderIp}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.basicInfo.offenderInfo = res.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.detection.victimIp) {
|
||||
axios.get(`${api.detection.securityEvent.ipDetail}?resource=${this.detection.victimIp}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.basicInfo.victimInfo = res.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.detection.domain) {
|
||||
axios.get(`${api.detection.securityEvent.domainDetail}?resource=${this.detection.domain}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.basicInfo.domainInfo = res.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.detection.app) {
|
||||
axios.get(`${api.detection.securityEvent.appDetail}?resource=${this.detection.app}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.basicInfo.appInfo = res.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
if (this.detection.ruleId) {
|
||||
axios.get(`${api.detection.detail}/${this.detection.ruleId}`).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.basicInfo.ruleDescription = res.data.data.description
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
queryEvent () {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
axios.get(api.detection.securityEvent.overviewEvent, {
|
||||
params: {
|
||||
startTime: this.detection.startTime,
|
||||
offenderIp: this.detection.offenderIp,
|
||||
victimIp: this.detection.victimIp
|
||||
}
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
resolve(response.data.data.result)
|
||||
} else {
|
||||
reject(response.data)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
axios.get(api.detection.securityEvent.relationEvent, {
|
||||
params: {
|
||||
// startTime: this.detection.startTime,
|
||||
unbiasedTime: this.detection.startTime,
|
||||
offenderIp: this.detection.offenderIp,
|
||||
victimIp: this.detection.victimIp,
|
||||
biasSecond: 3600
|
||||
}
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
this.events = response.data.data.result.sort((e1, e2) => e1.startTime - e2.startTime)
|
||||
} else {
|
||||
this.events = []
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -391,7 +502,18 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.query()
|
||||
this.initEntityDetail()
|
||||
this.queryEvent()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.row__label {
|
||||
width: 176px;
|
||||
}
|
||||
.row__content {
|
||||
width: calc(100% - 176px);
|
||||
padding-right: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
<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.status === 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>
|
||||
@@ -1,143 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="new-detection-filter-title">
|
||||
{{ $t('detections.filters') }}
|
||||
</div>
|
||||
<div class="new-detection-filter-content">
|
||||
<div>
|
||||
<div class="new-filter-content-title">{{ $t('overall.status') }}</div>
|
||||
<div class="new-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="new-filter-content-checkbox" :label="item.status">
|
||||
<div>{{ item.label }}</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="new-filter-content-title">{{ $t('overall.category') }}</div>
|
||||
<div class="new-filter-content-content">
|
||||
<el-checkbox-group v-model="checkCategory" @change="onChangeCategory">
|
||||
<el-checkbox v-for="item in categoryList" :key="item.value" class="new-filter-content-checkbox" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="new-filter-content-title">{{ $t('overall.type') }}</div>
|
||||
<div class="new-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="new-filter-content-checkbox" :label="item.value">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
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 }
|
||||
}
|
||||
axios.get(this.url, { params: searchParams }).then(response => {
|
||||
if (response.status === 200) {
|
||||
if (response.data.data.statusList) {
|
||||
this.statusList = []
|
||||
response.data.data.statusList.forEach(item => {
|
||||
this.statusList.push({ status: item.status, label: this.switchStatus(item.status) })
|
||||
this.checkStatus.push(item.status)
|
||||
})
|
||||
}
|
||||
|
||||
this.categoryList = response.data.data.categoryList
|
||||
if (response.data.data.categoryList) {
|
||||
response.data.data.categoryList.forEach(item => {
|
||||
this.checkCategory.push(item.value)
|
||||
})
|
||||
}
|
||||
|
||||
this.typeList = response.data.data.typeList
|
||||
if (response.data.data.typeList) {
|
||||
response.data.data.typeList.forEach(item => {
|
||||
this.checkType.push(item.value)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.error(response.data)
|
||||
}
|
||||
}).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>
|
||||
@@ -1,250 +0,0 @@
|
||||
<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 { api } from '@/utils/api'
|
||||
import axios from 'axios'
|
||||
import _ from 'lodash'
|
||||
|
||||
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 () {
|
||||
axios.get(api.detection.statistics, { pageSize: -1 }).then(response => {
|
||||
if (response.status === 200) {
|
||||
this.intervalList = _.get(response, 'data.data.intervalList', [])
|
||||
} else {
|
||||
console.error(response.data)
|
||||
}
|
||||
}).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 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.status === 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>
|
||||
@@ -17,7 +17,7 @@
|
||||
@search="search"
|
||||
></explorer-search>
|
||||
<!-- 内容区 -->
|
||||
<div v-if="showList" style="display: flex;flex-direction: row;">
|
||||
<div v-if="showList" style="display: flex;flex-direction: row;padding-bottom: 20px;">
|
||||
<entity-filter
|
||||
:filter-data="newFilterData"
|
||||
:loading-left="loadingLeft"
|
||||
@@ -71,7 +71,8 @@
|
||||
<div class="right-label">{{ $t('network.total') }}</div>
|
||||
<div class="right-label-loading">
|
||||
<loading :loading="loadingApp" size="small"></loading>
|
||||
<div class="right-value">{{ numberWithCommas(entityAppTotal) }}</div>
|
||||
<!-- <div class="right-value">{{ numberWithCommas(entityAppTotal) }}</div>-->
|
||||
<div class="right-value">837</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +102,8 @@
|
||||
<div class="right-label">{{ $t('network.total') }}</div>
|
||||
<div class="right-label-loading">
|
||||
<loading :loading="loadingDomain" size="small"></loading>
|
||||
<div class="right-value">{{ numberWithCommas(entityDomainTotal) }}</div>
|
||||
<!-- <div class="right-value">{{ numberWithCommas(entityDomainTotal) }}</div>-->
|
||||
<div class="right-value">1,032,544</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -131,7 +133,8 @@
|
||||
<div class="right-label">{{ $t('network.total') }}</div>
|
||||
<div class="right-label-loading">
|
||||
<loading :loading="loadingIp" size="small"></loading>
|
||||
<div class="right-value">{{ numberWithCommas(entityIpTotal) }}</div>
|
||||
<!-- <div class="right-value">{{ numberWithCommas(entityIpTotal) }}</div>-->
|
||||
<div class="right-value">1,900,804</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -689,6 +692,44 @@ export default {
|
||||
} else {
|
||||
this.search({ q: '', str: '', metaList: [] })
|
||||
}
|
||||
},
|
||||
queryScoreBase () {
|
||||
const { startTime, endTime } = getNowTime(60 * 24)
|
||||
const params = {
|
||||
startTime: getSecond(startTime),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
const tcp = axios.get(api.npm.overview.tcpSessionDelay, { params: params })
|
||||
const http = axios.get(api.npm.overview.httpResponseDelay, { params: params })
|
||||
const ssl = axios.get(api.npm.overview.sslConDelay, { params: params })
|
||||
const tcpPercent = axios.get(api.npm.overview.tcpLostlenPercent, { params: params })
|
||||
const packetPercent = axios.get(api.npm.overview.packetRetransPercent, { params: params })
|
||||
Promise.all([tcp, http, ssl, tcpPercent, packetPercent]).then(res => {
|
||||
const scoreBase = {}
|
||||
res.forEach((t, i) => {
|
||||
if (t.status === 200) {
|
||||
if (i === 0) {
|
||||
scoreBase.establishLatencyMsP10 = _.get(t.data, 'data.result.establishLatencyMsP10', null)
|
||||
scoreBase.establishLatencyMsP90 = _.get(t.data, 'data.result.establishLatencyMsP90', null)
|
||||
} else if (i === 1) {
|
||||
scoreBase.httpResponseLatencyP10 = _.get(t.data, 'data.result.httpResponseLatencyP10', null)
|
||||
scoreBase.httpResponseLatencyP90 = _.get(t.data, 'data.result.httpResponseLatencyP90', null)
|
||||
} else if (i === 2) {
|
||||
scoreBase.sslConLatencyP10 = _.get(t.data, 'data.result.sslConLatencyP10', null)
|
||||
scoreBase.sslConLatencyP90 = _.get(t.data, 'data.result.sslConLatencyP90', null)
|
||||
} else if (i === 3) {
|
||||
scoreBase.tcpLostlenPercentP10 = _.get(t.data, 'data.result.tcpLostlenPercentP10', null)
|
||||
scoreBase.tcpLostlenPercentP90 = _.get(t.data, 'data.result.tcpLostlenPercentP90', null)
|
||||
} else if (i === 4) {
|
||||
scoreBase.pktRetransPercentP10 = _.get(t.data, 'data.result.pktRetransPercentP10', null)
|
||||
scoreBase.pktRetransPercentP90 = _.get(t.data, 'data.result.pktRetransPercentP90', null)
|
||||
}
|
||||
}
|
||||
})
|
||||
this.$store.commit('setScoreBase', scoreBase)
|
||||
}).catch((e) => {
|
||||
}).finally(() => {
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -702,8 +743,16 @@ export default {
|
||||
if (q && (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1)) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
// %位置不为0,即内容包含非英文时
|
||||
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
|
||||
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
|
||||
q = decodeURI(q)
|
||||
}
|
||||
this.initSearch(q)
|
||||
this.listMode = listMode
|
||||
// 查询评分基准
|
||||
this.$store.commit('resetScoreBase')
|
||||
this.queryScoreBase()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div @click="showMoreFilter(item, index)"
|
||||
:class="item.showNum === item.data.length ? 'filter-no-show-more' : 'filter-show-more'">
|
||||
{{ $t('entity.showMore') }}
|
||||
{{ $t('overall.showMore') }}
|
||||
</div>
|
||||
<div class="filter-hr"></div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user