Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e26d2b40ab | ||
|
|
6c1f1ed71b | ||
|
|
b4b72fc1e1 | ||
|
|
b3d4bbd440 | ||
|
|
6c90ebe7bc | ||
|
|
ef3994e14a | ||
|
|
abc1d07e6c | ||
|
|
38af43b322 | ||
|
|
90ed543a84 | ||
|
|
c66a6b7f59 | ||
|
|
a0af36b7c0 | ||
|
|
f95dc60cd3 | ||
|
|
7d64868ce0 | ||
|
|
62a7a629e5 | ||
|
|
e3c6c21545 | ||
|
|
3b2eaf3851 | ||
|
|
ee05f47f2d | ||
|
|
07dd16f2c2 | ||
|
|
9103c454c2 | ||
|
|
e5a7c2abfa | ||
|
|
e288e20fd7 | ||
|
|
ec746f0fc7 | ||
|
|
4ca33e9ede | ||
|
|
b8105a4565 | ||
|
|
29157ae7d3 | ||
|
|
9c0ce493ab | ||
|
|
4662061fc5 | ||
|
|
0eb0346abc | ||
|
|
c8926177f7 | ||
|
|
5aa5d77511 | ||
|
|
38368a6cb8 | ||
|
|
051aeadbca | ||
|
|
8c2119e773 | ||
|
|
cb70fb0236 | ||
|
|
853fa79d4c | ||
|
|
2f919d1774 | ||
|
|
360fffde68 | ||
|
|
bb2a7676e6 | ||
|
|
de698f0a71 | ||
|
|
1c1d354a6c | ||
|
|
8126618e54 | ||
|
|
5a8796688a | ||
|
|
be36b2604b | ||
|
|
330a4b0d3b | ||
|
|
1410f890f3 | ||
|
|
78105475fb | ||
|
|
02a9f35070 | ||
|
|
c89397da97 | ||
|
|
36c3db5dee | ||
|
|
366bb8f17f | ||
|
|
1c00f568fa | ||
|
|
93be846064 | ||
|
|
ffb822d65d | ||
|
|
f8e9d36c8a | ||
|
|
241665cd80 | ||
|
|
ee57ca4c6c | ||
|
|
2bb6c54cab | ||
|
|
65827d27e4 | ||
|
|
98948ff179 | ||
|
|
74dc057090 | ||
|
|
e8959bab27 | ||
|
|
a7ce777e10 | ||
|
|
d7d450222b | ||
|
|
3da4b4b20a | ||
|
|
a0d2160b43 | ||
|
|
b94dba9ed3 | ||
|
|
696b03ad40 | ||
|
|
f199442c60 | ||
|
|
889903bb46 | ||
|
|
e12d76e2ad | ||
|
|
29df6ec60e | ||
|
|
20857e1203 | ||
|
|
8d4924a421 | ||
|
|
41d2f48766 | ||
|
|
f151415de6 | ||
|
|
73dec68e23 | ||
|
|
60e821fb16 | ||
|
|
4cb4aba707 | ||
|
|
ba4893dae1 | ||
|
|
19c42021db | ||
|
|
d6c9bd0ee2 | ||
|
|
63fd8b268f | ||
|
|
bd1f755612 | ||
|
|
dd4f5e1fba | ||
|
|
ed1d994d5e | ||
|
|
815af776aa | ||
|
|
a4da1dbfac | ||
|
|
9a3bf1a4af | ||
|
|
6a196ba3f0 | ||
|
|
7b8ca90436 | ||
|
|
90827fd706 | ||
|
|
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,20 @@
|
||||
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'
|
||||
}
|
||||
// 默认时间过滤条件,单位分钟. 0表示请求接口时不传时间参数
|
||||
const DEFAULT_TIME_FILTER_RANGE = {
|
||||
dashboard: 60,
|
||||
entity: {
|
||||
list: 60,
|
||||
trafficLine: 60,
|
||||
informationAggregation: 0,
|
||||
relatedEntity: 60 * 24 * 7,
|
||||
openPort: 60 * 24 * 7,
|
||||
securityEvent: 60 * 24 * 7,
|
||||
performanceEvent: 60 * 24 * 7,
|
||||
behaviorPattern: 60 * 24 * 7
|
||||
},
|
||||
detection: 60
|
||||
}
|
||||
|
||||
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
@@ -54,7 +54,8 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
username: '',
|
||||
pin: ''
|
||||
pin: '',
|
||||
language: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -75,6 +76,9 @@ export default {
|
||||
if (!_.isEmpty(res.data.data.user.lang)) {
|
||||
localStorage.setItem(storageKey.language, res.data.data.user.lang)
|
||||
}
|
||||
if (!localStorage.getItem(storageKey.language)) {
|
||||
localStorage.setItem(storageKey.language, this.language)
|
||||
}
|
||||
if (!_.isEmpty(res.data.data.user.theme)) {
|
||||
localStorage.setItem(storageKey.theme, res.data.data.user.theme)
|
||||
}
|
||||
@@ -107,6 +111,7 @@ export default {
|
||||
})
|
||||
},
|
||||
appearanceOut (data) {
|
||||
this.language = data.lang || defaultLang
|
||||
if (_.isEmpty(localStorage.getItem(storageKey.language))) {
|
||||
localStorage.setItem(storageKey.language, data.lang || defaultLang)
|
||||
}
|
||||
|
||||
@@ -3,84 +3,92 @@
|
||||
height: 100%;
|
||||
|
||||
.search__suffixes {
|
||||
height: 38px;
|
||||
|
||||
&.entity-explorer-home {
|
||||
margin-right: 1px;
|
||||
color: #3976CB;
|
||||
|
||||
&.search__suffixes--text-mode, &.search__suffixes--tag-mode {
|
||||
.search__suffix:last-of-type {
|
||||
width: unset;
|
||||
height: unset;
|
||||
margin-right: 12px;
|
||||
background-color: transparent;
|
||||
|
||||
.el-icon-search {
|
||||
color: #3976CB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.search__suffixes--text-mode, &.search__suffixes--tag-mode {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
align-items: center;
|
||||
top: 1px;
|
||||
right: 0;
|
||||
|
||||
.search__suffix {
|
||||
// margin-left: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
border-radius: 0 2px 2px 0;
|
||||
|
||||
.cn-icon-search-advance, .cn-icon-search-normal, .cn-icon-filter {
|
||||
color: #A6AAAE;
|
||||
font-size: 18px;
|
||||
}
|
||||
.el-icon-search {
|
||||
color: #3976CB;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
width: 41px;
|
||||
height: 38px;
|
||||
line-height: 39px;
|
||||
background: #38ACD2;
|
||||
|
||||
.el-icon-search {
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.search__suffix-close {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin-top: -9px;
|
||||
|
||||
.el-icon-error {
|
||||
font-size: 17px;
|
||||
color: #C4C4C4;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.entity-explorer-search {
|
||||
color: #3976CB;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.margin-r-12 {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.new-search__suffix {
|
||||
width: 41px;
|
||||
height: 41px;
|
||||
line-height: 41px;
|
||||
background: #38ACD2;
|
||||
text-align: center;
|
||||
margin-top: -10px;
|
||||
margin-right: -10px;
|
||||
|
||||
.el-icon-search {
|
||||
color: #fff !important;
|
||||
margin-top: 9px !important;
|
||||
}
|
||||
}
|
||||
.my-popper-class .el-popper__arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.search__suffixes--tag-mode__block {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
/*.search-tip--error {
|
||||
font-size: 14px;
|
||||
color: #F56C6C;
|
||||
}*/
|
||||
}
|
||||
.advanced-search--show-list .CodeMirror, .advanced-search--show-list .tag-search {
|
||||
border: none;
|
||||
.detections {
|
||||
.tag-search, .CodeMirror {
|
||||
border: 1px solid #E2E5EC;
|
||||
}
|
||||
}
|
||||
.tag-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
overflow: auto hidden;
|
||||
border: 1px solid #CECECE;
|
||||
border-radius: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 80px;
|
||||
background-color: white;
|
||||
border: 1px solid #DEDEDE;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
@@ -104,6 +112,7 @@
|
||||
border-radius: 1px;
|
||||
cursor: pointer;
|
||||
transition: all linear .1s;
|
||||
margin-right: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: white;
|
||||
@@ -167,6 +176,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.entity__search .tag-search {
|
||||
padding-left: 65px;
|
||||
}
|
||||
|
||||
.el-popover.my-popper-class {
|
||||
width: auto !important;
|
||||
|
||||
@@ -13,11 +13,16 @@
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
.entity__search {
|
||||
.CodeMirror {
|
||||
padding-left: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
/* PADDING */
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding: 11px 5px; /* Vertical padding around content */
|
||||
padding: 9px 5px; /* Vertical padding around content */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
&>div {
|
||||
height: 100%;
|
||||
}
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.cn-header {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
padding: 0 20px 20px;
|
||||
padding: 20px;
|
||||
|
||||
.panel__title {
|
||||
font-size: 24px;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +194,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.form-setting__btn, .form-setting__btn1 {
|
||||
.form-setting__btn, .form-setting__btn1, .policy-form__footer__btn {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.el-button {
|
||||
width: 80px !important;
|
||||
height: 30px !important;
|
||||
min-height: 30px !important;
|
||||
line-height: 30px !important;
|
||||
@@ -208,9 +213,25 @@
|
||||
font-weight: 500;
|
||||
padding: 0 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-setting__btn1 {
|
||||
.btn1 {
|
||||
margin-right: 10px;
|
||||
.el-button {
|
||||
background: #F5F6F7;
|
||||
border: 1px solid rgba(215,215,215,1);
|
||||
color: #353636;
|
||||
}
|
||||
}
|
||||
}
|
||||
.policy-form__footer__btn {
|
||||
justify-content: center;
|
||||
margin-top: 8px;
|
||||
|
||||
.btn1 {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
.form-setting__btn1, .policy-form__footer__btn {
|
||||
.el-button {
|
||||
padding: 0 11px !important;
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
}
|
||||
|
||||
.detection-form-content {
|
||||
height: 100%;
|
||||
height: calc(100% - 92px);
|
||||
overflow: scroll;
|
||||
padding-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.detection-form-collapse {
|
||||
margin-top: 20px;
|
||||
@@ -101,6 +101,12 @@
|
||||
justify-content: flex-start;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
|
||||
.policy-form-item {
|
||||
.el-form-item__error {
|
||||
width: 260px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--mini .el-input__inner {
|
||||
@@ -124,6 +130,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.policy-form-trigger {
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input--mini, .el-input--mini .el-input__inner {
|
||||
@@ -137,4 +149,13 @@
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.policy-form__footer {
|
||||
width: calc(100% + 40px);
|
||||
height: 60px;
|
||||
margin-left: -20px;
|
||||
box-shadow: 0 -1px 4px 0 rgba(0,0,0,0.10);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -34,6 +34,15 @@
|
||||
|
||||
.drawer-basic-id {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +67,10 @@
|
||||
color: #046ECA;
|
||||
}
|
||||
|
||||
.drawer-trigger-minutes {
|
||||
color: #353636;
|
||||
}
|
||||
|
||||
.detection-drawer-collapse {
|
||||
background: #FFFFFF;
|
||||
border: 1px solid rgba(226, 229, 236, 1);
|
||||
|
||||
@@ -2,98 +2,124 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 280px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
background-color: white;
|
||||
margin-right: 12px;
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
border: 1px solid rgba(226, 229, 236, 1) !important;
|
||||
border-radius: 4px !important;
|
||||
|
||||
.detection-filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.filter__header {
|
||||
display: flex;
|
||||
flex: 0 0 32px;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
color: #666;
|
||||
//background-color: #F3F7FA;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
i {
|
||||
font-size: 12px;
|
||||
transition: all linear .1s;
|
||||
transform: rotate(0) translate(0, 2px);
|
||||
}
|
||||
i.arrow-rotate {
|
||||
transform: rotate(90deg) translate(2px, 3px);
|
||||
}
|
||||
.new-detection-filter-header-title {
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 600;
|
||||
}
|
||||
.new-detection-filter-icon {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 2px;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
}
|
||||
|
||||
.filter__body {
|
||||
padding: 5px 0 0 15px;
|
||||
|
||||
.el-checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
margin-right: 5px;
|
||||
.el-checkbox__label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.filter__checkbox-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.severity-color-block {
|
||||
width: 4px;
|
||||
height: 15px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.filter-case__header {
|
||||
padding-left: 8px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
background: #F7F7F7;
|
||||
box-shadow: 0 1px 0 0 rgba(226,229,236,1);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.new-detection-filter-title {
|
||||
display: flex;
|
||||
flex: 0 0 32px;
|
||||
align-items: center;
|
||||
padding-left: 27px;
|
||||
background-color: #EFF2F5;
|
||||
cursor: pointer;
|
||||
|
||||
.filter__header {
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
margin: 0 20px;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 600;
|
||||
margin: -10px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.filter__body {
|
||||
width: calc(100% - 30px);
|
||||
margin: 0 10px 0 20px;
|
||||
max-height: 265px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
|
||||
.filter__body-item {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
.filter__body-item-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
.filter__body-item-left-index {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
text-align: center;
|
||||
background: #EFF2F5;
|
||||
border-radius: 2px;
|
||||
margin-right: 6px;
|
||||
font-family: NotoSansHans-Black;
|
||||
font-size: 9px;
|
||||
color: #96A2B0;
|
||||
font-weight: 900;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.filter__body-item-left-label {
|
||||
max-width: 180px;
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.filter__body-item-right {
|
||||
flex-shrink: 0;
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 12px;
|
||||
color: #717171;
|
||||
font-weight: 400;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-country-flag {
|
||||
width: 18px;
|
||||
height: 12px;
|
||||
margin-right: 6px;
|
||||
border: 1px solid #E8E8E8;
|
||||
}
|
||||
|
||||
.filter-show-more, .filter-no-show-more {
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
margin-left: 20px;
|
||||
color: #046ECA;
|
||||
user-select: none; // 禁止文本选中
|
||||
font-size: 12px;
|
||||
}
|
||||
.filter-no-show-more {
|
||||
cursor: not-allowed;
|
||||
color: rgba(16, 16, 16, 0.3);
|
||||
}
|
||||
|
||||
.filter-hr {
|
||||
width: calc(100% - 40px);
|
||||
margin-left: 20px;
|
||||
margin-top: 6px;
|
||||
height: 1px;
|
||||
background: #EFF2F5;
|
||||
//background: #000;
|
||||
}
|
||||
|
||||
.new-detection-filter-title {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
|
||||
@@ -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);
|
||||
@@ -145,7 +149,7 @@
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.basic-info__item {
|
||||
.basic-info__item, .basic-info__item1 {
|
||||
padding-right: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -168,6 +172,11 @@
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
.basic-info__item1 {
|
||||
span: {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-detail {
|
||||
@@ -262,3 +271,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,6 +203,7 @@
|
||||
color: #046ECA;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 500;
|
||||
height: 36px;
|
||||
}
|
||||
.timeline__start-time {
|
||||
font-size: 12px;
|
||||
@@ -215,13 +233,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;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.explorer-top-tools, .explorer-detection-top-tools {
|
||||
.explorer-top-tools, .explorer-detection-top-tools, .explorer-entity-top-tools {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
@@ -46,7 +46,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.explorer-detection-top-tools {
|
||||
.explorer-entity-top-tools {
|
||||
width: 100%;
|
||||
}
|
||||
.explorer-detection-top-tools, .explorer-entity-top-tools {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
@@ -87,6 +90,13 @@
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
.entity-hide-entity {
|
||||
margin-left: 20px;
|
||||
.el-checkbox__label {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.explorer-container, .explorer-container-new {
|
||||
display: flex;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 320px;
|
||||
margin-right: 20px;
|
||||
margin-right: 12px;
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
border: 1px solid rgba(226, 229, 236, 1) !important;
|
||||
|
||||
@@ -349,6 +349,25 @@
|
||||
.data-score-green {
|
||||
background: #749F4D;
|
||||
}
|
||||
.score-dot {
|
||||
display: inline-block;
|
||||
margin-bottom: 2px;
|
||||
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;
|
||||
|
||||
@@ -79,11 +79,22 @@
|
||||
.cn-entity__header-title {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.cn-entity__header-tag {
|
||||
|
||||
.entity-related-entity {
|
||||
font-size: 12px;
|
||||
color: #717171;
|
||||
cursor: pointer;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.entity-row-tag {
|
||||
display: flex;
|
||||
margin-left: 6px;
|
||||
margin-top: 1px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.cn-entity__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -98,7 +109,7 @@
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.basic-info__item {
|
||||
.basic-info__item, .basic-info__item1 {
|
||||
padding-right: 30px;
|
||||
|
||||
.item__box {
|
||||
@@ -161,6 +172,17 @@
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
.basic-info__item1 {
|
||||
span: {
|
||||
color: #666;
|
||||
}
|
||||
span:first-of-type {
|
||||
color: #666;
|
||||
}
|
||||
.row-item-label {
|
||||
color: #999 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.row-item-label {
|
||||
font-family: NotoSansSChineseRegular;
|
||||
@@ -174,6 +196,26 @@
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
|
||||
.score-dot {
|
||||
display: inline-block;
|
||||
margin-bottom: 2px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
padding: 0 20px;
|
||||
|
||||
&.explorer-search__input-case--question-mark-in-line {
|
||||
flex-direction: row;
|
||||
//flex-direction: row;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
|
||||
.explorer-search__input {
|
||||
@@ -34,9 +35,8 @@
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.explorer-search__input__border {
|
||||
border: 1px #DEDEDE solid;
|
||||
height: 43px;
|
||||
.explorer-search__input--border .CodeMirror {
|
||||
border: 1px solid #DEDEDE;
|
||||
}
|
||||
}
|
||||
.search-symbol-inline {
|
||||
@@ -53,14 +53,14 @@
|
||||
max-width: 1000px;
|
||||
height: 40px;
|
||||
}
|
||||
.explorer-search__foot {
|
||||
.explorer-search__foot,.explorer-search__foot-list {
|
||||
display: flex;
|
||||
padding-top: 18px;
|
||||
padding-top: 9px;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
font-weight: bold;
|
||||
justify-content: flex-start;
|
||||
//font-weight: bold;
|
||||
|
||||
.foot__item {
|
||||
display: flex;
|
||||
@@ -87,14 +87,15 @@
|
||||
|
||||
.search__history {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
display: flex;
|
||||
padding: 10px 0 0 0;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
z-index: 2;
|
||||
top: 47px;
|
||||
border: 1px solid rgba(206,206,206,0.20);
|
||||
//top: 47px;
|
||||
border: 1px solid rgba(226,229,236,1);
|
||||
border-radius: 2px;
|
||||
background-color: white;
|
||||
|
||||
@@ -137,8 +138,32 @@
|
||||
color: #66b1ff;
|
||||
}
|
||||
}
|
||||
|
||||
.history__items-new {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
.el-table th,.el-table td {
|
||||
padding: 6px 0;
|
||||
border: none !important;
|
||||
}
|
||||
.el-table {
|
||||
font-family: NotoSansSChineseRegular;
|
||||
font-size: 14px;
|
||||
color: #575757;
|
||||
font-weight: 400;
|
||||
thead {
|
||||
font-family: NotoSansHans-Medium;
|
||||
font-size: 14px;
|
||||
color: #353636;
|
||||
font-weight: 500;
|
||||
}
|
||||
.cell {
|
||||
padding-left: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.clear-all {
|
||||
padding-left: 30px;
|
||||
padding-left: 18px;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
height: 35px;
|
||||
@@ -152,6 +177,71 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.explorer-search__foot-list {
|
||||
max-width: 756px;
|
||||
position: absolute;
|
||||
left: 196px;
|
||||
top: 44px;
|
||||
.search__history {
|
||||
top: 6px;
|
||||
max-width: 756px;
|
||||
margin-left: -196px;
|
||||
.history__items-new {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
.el-table th,.el-table td {
|
||||
padding: 4px 0;
|
||||
border: none !important;
|
||||
}
|
||||
.el-table {
|
||||
font-size: 12px;
|
||||
thead {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.el-table--scrollable-x .el-table__body-wrapper {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.highlight__text {
|
||||
background: #FEECC2;
|
||||
padding: 0 3px;
|
||||
}
|
||||
.highlight__block {
|
||||
background: #FEECC2;
|
||||
}
|
||||
.explorer-search__foot-list .explorer-search__block {
|
||||
margin-left: -196px;
|
||||
top: -44px;
|
||||
}
|
||||
.explorer-search__foot .explorer-search__block {
|
||||
margin-left: 0;
|
||||
top: -40px;
|
||||
}
|
||||
.explorer-search__block {
|
||||
position: absolute;
|
||||
padding-left: 15px;
|
||||
height: 39px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
|
||||
i {
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.search-dividing-line {
|
||||
width: 1px;
|
||||
height: 26px;
|
||||
background: #DEDEDE;
|
||||
margin-left: 15px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
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=1699411209748') format('woff2'),
|
||||
url('iconfont.woff?t=1699411209748') format('woff'),
|
||||
url('iconfont.ttf?t=1699411209748') format('truetype');
|
||||
}
|
||||
|
||||
.cn-icon {
|
||||
@@ -13,6 +13,22 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.cn-icon-related:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
.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.
@@ -17,7 +17,7 @@
|
||||
size="mini"
|
||||
v-model="meta.column.label"
|
||||
ref="columnSelect"
|
||||
:placeholder="meta.column.label || ''"
|
||||
:placeholder="meta.column.label || ' '"
|
||||
@blur="columnBlur(meta, index)"
|
||||
@change="(value) => selectColumn(value, meta)"
|
||||
>
|
||||
@@ -90,8 +90,8 @@
|
||||
</template>
|
||||
</div>
|
||||
<div class="tag-search__add" @click="addCondition">{{$t('entities.advancedSearch.add')}}</div>
|
||||
<div class="search__suffixes search__suffixes--tag-mode">
|
||||
<div class="search__suffix" style="margin-right: 12px">
|
||||
<div class="search__suffixes search__suffixes--tag-mode search__suffixes--tag-mode__block" :class="showList ? '' : 'entity-explorer-home'">
|
||||
<span class="search__suffix">
|
||||
<el-popover
|
||||
popper-class="my-popper-class"
|
||||
placement="top"
|
||||
@@ -102,13 +102,13 @@
|
||||
<i class="cn-icon cn-icon-search-normal" @click="changeMode"></i>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div v-show="metaList.length>0" class="search__suffix-close" @click="cleanMetaList">
|
||||
</span>
|
||||
<span v-show="metaList.length>0" class="search__suffix search__suffix-close" @click="cleanMetaList">
|
||||
<i class="el-icon-error"></i>
|
||||
</div>
|
||||
<div class="search__suffix" :class="showList ? 'new-search__suffix' : 'entity-explorer-search'" @click="search">
|
||||
</span>
|
||||
<span class="search__suffix" @click="search">
|
||||
<i class="el-icon-search"></i>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -202,6 +202,7 @@ export default {
|
||||
if (this.isCustomized(value)) {
|
||||
meta.column.type = columnType.fullText
|
||||
meta.column.label = value
|
||||
meta.column.isFullText = true
|
||||
meta.resetOperator()
|
||||
meta.resetValue()
|
||||
} else {
|
||||
@@ -248,6 +249,7 @@ export default {
|
||||
if (meta.column && meta.column.type === 'fullText') {
|
||||
meta.operator.value = '='
|
||||
meta.column.show = false
|
||||
meta.column.isFullText = true
|
||||
meta.operator.show = false
|
||||
const label = JSON.parse(JSON.stringify(meta.column.label))
|
||||
meta.column.label = parser.getEntityTypeByValue(meta.column.label)
|
||||
@@ -461,13 +463,21 @@ export default {
|
||||
if (this.metaList.length > 0) {
|
||||
const parser = new Parser(this.columnList)
|
||||
const errorList = parser.validateMeta(this.metaList)
|
||||
const keywordList = []
|
||||
this.metaList.forEach(item => {
|
||||
if (item.column && item.column.isFullText) {
|
||||
keywordList.push({ type: 'fullText', value: item.value.value })
|
||||
} else if (item.column && !item.column.isFullText) {
|
||||
keywordList.push({ type: item.column.type, value: item.value.value })
|
||||
}
|
||||
})
|
||||
if (_.isEmpty(errorList)) {
|
||||
const strObj = parser.handleMetaListToStr(this.metaList)
|
||||
const str = strObj.str ? strObj.str : strObj
|
||||
const str2 = strObj.str2 ? strObj.str2 : strObj
|
||||
// str为将metaList转成字符串的值,str2为地址栏展示的值
|
||||
const key = parser.handleEntityTypeByStr(str)
|
||||
this.$emit('search', { ...parser.parseStr(key), str: str2 })
|
||||
this.$emit('search', { ...parser.parseStr(key), str: str2, keywordList: keywordList })
|
||||
} else {
|
||||
this.$message.error(handleErrorTip(errorList[0]))
|
||||
}
|
||||
@@ -555,14 +565,17 @@ export default {
|
||||
const column = this.columnList.find(c => {
|
||||
return c.label === param.column
|
||||
})
|
||||
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
|
||||
// 不是在首位,则删除时顺带删除前一个index(and或or),否则顺带删除后一个index
|
||||
if (metaIndex > 0) {
|
||||
this.metaList.splice(metaIndex - 1, 2)
|
||||
} else if (this.metaList.length === 1) {
|
||||
this.metaList.splice(metaIndex, 1)
|
||||
} else {
|
||||
this.metaList.splice(metaIndex, 2)
|
||||
const obj = this.metaList.find(d => d.column && d.column.label === param.column && d.value && (d.value.value === `'${param.value[0]}'` || d.value.value === param.value[0]))
|
||||
if (obj) {
|
||||
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
|
||||
// 不是在首位,则删除时顺带删除前一个index(and或or),否则顺带删除后一个index
|
||||
if (metaIndex > 0) {
|
||||
this.metaList.splice(metaIndex - 1, 2)
|
||||
} else if (this.metaList.length === 1) {
|
||||
this.metaList.splice(metaIndex, 1)
|
||||
} else {
|
||||
this.metaList.splice(metaIndex, 2)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -622,8 +635,16 @@ export default {
|
||||
let { q } = this.$route.query
|
||||
if (q && !this.convertMetaList) {
|
||||
const parser = new Parser(this.columnList)
|
||||
if (q.indexOf('+') > -1) {
|
||||
q = q.replace('+', '')
|
||||
}
|
||||
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
|
||||
q = decodeURI(q)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
@@ -645,6 +666,7 @@ export default {
|
||||
if (item.column && item.column.type === 'fullText') {
|
||||
item.operator.value = '='
|
||||
item.column.show = false
|
||||
item.column.isFullText = true
|
||||
item.operator.show = false
|
||||
const label = JSON.parse(JSON.stringify(item.column.label))
|
||||
item.column.label = parser.getEntityTypeByValue(item.column.label)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<textarea
|
||||
style="text-indent: 65px;"
|
||||
cols="40"
|
||||
ref="textSearch"
|
||||
></textarea>
|
||||
<div class="search__suffixes search__suffixes--text-mode">
|
||||
<div class="search__suffix">
|
||||
<div class="search__suffixes search__suffixes--text-mode" :class="showList ? '' : 'entity-explorer-home'" style="padding-left: 1px">
|
||||
<span class="search__suffix">
|
||||
<el-popover
|
||||
popper-class="my-popper-class"
|
||||
placement="top"
|
||||
@@ -11,16 +13,16 @@
|
||||
:content="$t('entity.switchToAdvancedSearch')"
|
||||
>
|
||||
<template #reference>
|
||||
<i class="cn-icon cn-icon-filter margin-r-12" @click="changeMode"></i>
|
||||
<i class="cn-icon cn-icon-filter" @click="changeMode"></i>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div v-show="isCloseIcon" class="search__suffix-close" @click="cleanParams">
|
||||
</span>
|
||||
<span v-show="isCloseIcon" class="search__suffix search__suffix-close" @click="cleanParams">
|
||||
<i class="el-icon-error"></i>
|
||||
</div>
|
||||
<div class="search__suffix" :class="showList ? 'new-search__suffix' : 'entity-explorer-search'" @click="search">
|
||||
</span>
|
||||
<span class="search__suffix" @click="search">
|
||||
<i class="el-icon-search"></i>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,7 +70,7 @@ export default {
|
||||
mode: {
|
||||
name: 'sql'
|
||||
},
|
||||
placeholder: 'Enter...',
|
||||
placeholder: '',
|
||||
lineNumbers: false
|
||||
})
|
||||
this.codeMirror.setOption('extraKeys', {
|
||||
@@ -97,10 +99,19 @@ export default {
|
||||
if (str) {
|
||||
const parser = new Parser(this.columnList)
|
||||
const keyInfo = parser.comparedEntityKey(parser.handleEntityTypeByStr(str))
|
||||
const metaList = parser.parseStr(_.cloneDeep(str)).metaList
|
||||
const keywordList = []
|
||||
metaList.forEach(item => {
|
||||
if (item.column && item.column.type === columnType.fullText) {
|
||||
keywordList.push({ type: item.column.type, value: item.column.label })
|
||||
} else if (item.column && item.column.type === columnType.string) {
|
||||
keywordList.push({ type: item.column.type, value: item.value.value })
|
||||
}
|
||||
})
|
||||
if (keyInfo.isKey) {
|
||||
const errorList = parser.validateStr(keyInfo.key)
|
||||
if (_.isEmpty(errorList)) {
|
||||
this.$emit('search', { ...parser.parseStr(keyInfo.key), str: str })
|
||||
this.$emit('search', { ...parser.parseStr(keyInfo.key), str: str, keywordList: keywordList })
|
||||
} else {
|
||||
this.$message.error(handleErrorTip(errorList[0]))
|
||||
}
|
||||
@@ -223,8 +234,16 @@ export default {
|
||||
toRaw(this.codeMirror).setValue(this.str)
|
||||
}
|
||||
if (q) {
|
||||
if (q.indexOf('+') > -1) {
|
||||
q = q.replace('+', '')
|
||||
}
|
||||
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
|
||||
q = decodeURI(q)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
@@ -1399,8 +1414,8 @@ export default class Parser {
|
||||
} else if (i.toLowerCase() === 'tag') {
|
||||
lastObj[i] = `has(${i},${commonObj[i]})`
|
||||
} else {
|
||||
// 单独存在的,直接保留
|
||||
lastObj[i] = `${i} = '${commonObj[i]}'`
|
||||
// 单独存在的,直接保留 todo 后续观察当初添加单引号动机和问题
|
||||
lastObj[i] = `${i} = ${commonObj[i]}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
<template>
|
||||
<div class="pagination" >
|
||||
<el-pagination
|
||||
ref="page"
|
||||
@size-change="size"
|
||||
@prev-click="prev"
|
||||
@next-click="next"
|
||||
@current-change="current"
|
||||
:current-page="pageObj.pageNo"
|
||||
:page-sizes="pageSizes?pageSizes:[20, 50, 100]"
|
||||
:page-size="Number(pageObj.pageSize)"
|
||||
:layout="layout"
|
||||
:total="pageObj.total"
|
||||
v-bind="bind"
|
||||
>
|
||||
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
|
||||
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
|
||||
:popper-class="popClass" @visible-change="popperVisible">
|
||||
<el-option v-for="(item, index) in pageSizes" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
<el-config-provider :locale="locale">
|
||||
<el-pagination
|
||||
ref="page"
|
||||
@size-change="size"
|
||||
@prev-click="prev"
|
||||
@next-click="next"
|
||||
@current-change="current"
|
||||
:current-page="pageObj.pageNo"
|
||||
:page-sizes="pageSizes?pageSizes:[20, 50, 100]"
|
||||
:page-size="Number(pageObj.pageSize)"
|
||||
:layout="layout"
|
||||
:total="pageObj.total"
|
||||
v-bind="bind"
|
||||
>
|
||||
<el-select v-model="pageSize" :placeholder="pageSize+$t('pageSize')" size="mini"
|
||||
:popper-append-to-body="appendToBody" class="pagination-size-select" @change="size"
|
||||
:popper-class="popClass" @visible-change="popperVisible">
|
||||
<el-option v-for="(item, index) in pageSizes" :key="index" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
|
||||
</el-pagination>
|
||||
</el-pagination>
|
||||
</el-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defaultPageSize, storageKey } from '@/utils/constants'
|
||||
|
||||
import { defaultPageSize, storageKey, ZH, EN } from '@/utils/constants'
|
||||
import { urlParamsHandler, overwriteUrl } from '@/utils/tools'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { parseInt } from 'lodash'
|
||||
import ElConfigProvider from 'element-plus'
|
||||
import cn from 'element-plus/lib/locale/lang/zh-cn'
|
||||
import en from 'element-plus/lib/locale/lang/en'
|
||||
|
||||
export default {
|
||||
name: 'pagination',
|
||||
@@ -60,9 +64,15 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const pageSize = ref(defaultPageSize)
|
||||
const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1))
|
||||
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
|
||||
let locale = en
|
||||
if (language === ZH) {
|
||||
locale = cn
|
||||
}
|
||||
return {
|
||||
pageSize,
|
||||
currentPageNo
|
||||
currentPageNo,
|
||||
locale
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
@@ -18,28 +18,30 @@
|
||||
<div v-if="dropdownFlag" class="date-range-panel">
|
||||
<el-row class="date-range-panel-top" style="position: relative">
|
||||
<el-col :span="16" class="date-range-panel-content date-range-panel-content-left">
|
||||
<div class="date-range-title" style="padding-left: 0">Absolute time range</div>
|
||||
<el-date-picker
|
||||
v-model="newDateValue"
|
||||
ref="newDatePicker"
|
||||
popper-class="my-date-picker"
|
||||
style="position: absolute;top: -53px;left: -536px;"
|
||||
:clearable="false"
|
||||
:default-time="defaultTime"
|
||||
:unlink-panels="true"
|
||||
type="datetimerange"
|
||||
@change="timeArrChange"
|
||||
/>
|
||||
<div class="content-title">From</div>
|
||||
<div class="date-range-title" style="padding-left: 0">{{$t('dateTime.absoluteTimeRange')}}</div>
|
||||
<el-config-provider :locale="locale">
|
||||
<el-date-picker
|
||||
v-model="newDateValue"
|
||||
ref="newDatePicker"
|
||||
popper-class="my-date-picker"
|
||||
style="position: absolute;top: -53px;left: -536px;"
|
||||
:clearable="false"
|
||||
:default-time="defaultTime"
|
||||
:unlink-panels="true"
|
||||
type="datetimerange"
|
||||
@change="timeArrChange"
|
||||
/>
|
||||
</el-config-provider>
|
||||
<div class="content-title">{{$t('dateTime.from')}}</div>
|
||||
<div @click="myDatePickerShow" tabindex="1" class="content-input">
|
||||
{{ dateFormatByAppearance(getMillisecond(myStartTime)) }}
|
||||
</div>
|
||||
<div class="content-title">To</div>
|
||||
<div class="content-title">{{$t('dateTime.to')}}</div>
|
||||
<div @click="myDatePickerShow" tabindex="2" class="content-input">
|
||||
{{ dateFormatByAppearance(getMillisecond(myEndTime)) }}
|
||||
</div>
|
||||
|
||||
<div class="date-range-title" style="padding-left: 0">Recently used absolute ranges</div>
|
||||
<div class="date-range-title" style="padding-left: 0">{{$t('dateTime.recentlyUsedRanges')}}</div>
|
||||
<div class="date-range-history">
|
||||
<div v-for="(item, index) in rangeHistoryArr" :key="index" class="date-range-history-item"
|
||||
@click="historyChange(item)">
|
||||
@@ -53,7 +55,7 @@
|
||||
:span="8"
|
||||
class="date-range-panel-content date-range-panel-content-right"
|
||||
style="border-left: 1px solid rgba(0,0,0,0.09);">
|
||||
<div class="date-range-title">Relatime time ranges</div>
|
||||
<div class="date-range-title">{{$t('dateTime.relativeTimeRanges')}}</div>
|
||||
<ul class="date-range-item">
|
||||
<li v-for="item in dateRangeArr"
|
||||
@click="quickChange(item.value)"
|
||||
@@ -79,9 +81,12 @@
|
||||
|
||||
<script>
|
||||
import { ref, computed, watch, reactive } from 'vue'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { EN, storageKey, ZH } from '@/utils/constants'
|
||||
import { getMillisecond, millTimestampDiffFromTz, timestampToList } from '@/utils/date-util'
|
||||
import { useStore } from 'vuex'
|
||||
import ElConfigProvider from 'element-plus'
|
||||
import cn from 'element-plus/lib/locale/lang/zh-cn'
|
||||
import en from 'element-plus/lib/locale/lang/en'
|
||||
|
||||
export default {
|
||||
name: 'DateTimeRange',
|
||||
@@ -105,6 +110,31 @@ export default {
|
||||
}
|
||||
},
|
||||
emits: ['change'],
|
||||
data () {
|
||||
return {
|
||||
dateRangeArr: [
|
||||
{ value: 5, name: this.$t('dateTime.last5Mins') }, // 'last 5 mins'
|
||||
{ value: 15, name: this.$t('dateTime.last15Mins') },
|
||||
{ value: 30, name: this.$t('dateTime.last30Mins') },
|
||||
{ value: 60, name: this.$t('dateTime.last1Hour') }, // dateTime.last1Hour
|
||||
{ value: 180, name: this.$t('dateTime.last3Hours') },
|
||||
{ value: 360, name: this.$t('dateTime.last6Hours') },
|
||||
{ value: 720, name: this.$t('dateTime.last12Hours') },
|
||||
{ value: 1440, name: this.$t('dateTime.last1Day') }, // dateTime.last2Days
|
||||
{ value: 2880, name: this.$t('dateTime.last2Days') }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showDetail () {
|
||||
let str = ''
|
||||
if (this.dateRangeValue !== -1) {
|
||||
const rangeItem = this.dateRangeArr.find(item => item.value === this.dateRangeValue)
|
||||
str = rangeItem ? rangeItem.name : this.dateRangeArr[0].name
|
||||
}
|
||||
return str
|
||||
}
|
||||
},
|
||||
setup (props, ctx) {
|
||||
// data
|
||||
const store = useStore()
|
||||
@@ -124,24 +154,12 @@ export default {
|
||||
const rangeHistory = ref(localStorage.getItem(storageKey.dataRangeHistory) ? JSON.parse(localStorage.getItem(storageKey.dataRangeHistory)) : [])
|
||||
const dateRangeValue = props.dateRange ? ref(props.dateRange) : ref(60)
|
||||
const isCustom = ref(dateRangeValue.value === -1)
|
||||
const dateRangeArr = [
|
||||
{ value: 5, name: 'last 5 mins' },
|
||||
{ value: 15, name: 'last 15 mins' },
|
||||
{ value: 30, name: 'last 30 mins' },
|
||||
{ value: 60, name: 'last 1 hour' },
|
||||
{ value: 180, name: 'last 3 hours' },
|
||||
{ value: 360, name: 'last 6 hours' },
|
||||
{ value: 720, name: 'last 12 hours' },
|
||||
{ value: 1440, name: 'last 1 day' },
|
||||
{ value: 2880, name: 'last 2 days' }
|
||||
]
|
||||
const dropdownFlag = ref(false)
|
||||
// 默认日历选择时间,即开始时间YYYY-MM-DD 00:00:00,结束时间YYYY-MM-DD 59:59:59
|
||||
const defaultTime = ref([
|
||||
new Date(2023, 1, 1, 0, 0, 0),
|
||||
new Date(2023, 1, 2, 23, 59, 59)
|
||||
])
|
||||
|
||||
// computed
|
||||
const utcStr = computed(() => {
|
||||
let str = 'UTC '
|
||||
@@ -159,13 +177,6 @@ export default {
|
||||
str += ':00 '
|
||||
return str
|
||||
})
|
||||
const showDetail = computed(() => {
|
||||
let str = ''
|
||||
if (dateRangeValue.value !== -1) {
|
||||
str = dateRangeArr.find(item => item.value === dateRangeValue.value).name
|
||||
}
|
||||
return str
|
||||
})
|
||||
const rangeHistoryArr = rangeHistory
|
||||
|
||||
// refs
|
||||
@@ -254,12 +265,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({
|
||||
@@ -287,6 +300,12 @@ export default {
|
||||
})
|
||||
}
|
||||
}
|
||||
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
|
||||
let locale = en
|
||||
if (language === ZH) {
|
||||
locale = cn
|
||||
}
|
||||
|
||||
return {
|
||||
myStartTime,
|
||||
myEndTime,
|
||||
@@ -295,13 +314,11 @@ export default {
|
||||
utcStr,
|
||||
rangeEchartsData,
|
||||
address,
|
||||
dateRangeArr,
|
||||
defaultTime,
|
||||
dateRangeValue,
|
||||
isCustom,
|
||||
newDateValue,
|
||||
newDatePicker,
|
||||
showDetail,
|
||||
rangeHistory,
|
||||
rangeHistoryArr,
|
||||
getMillisecond,
|
||||
@@ -311,7 +328,8 @@ export default {
|
||||
timeArrChange,
|
||||
returnValue,
|
||||
quickChange,
|
||||
historyChange
|
||||
historyChange,
|
||||
locale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { storageKey, EN } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'TopToolMoreOptions',
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
if (this.paramsType) {
|
||||
form.append('type', this.paramsType)
|
||||
}
|
||||
form.append('language', localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : 'en')
|
||||
form.append('language', localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : EN)
|
||||
axios.post(this.importUrl, form, { 'Content-Type': 'multipart/form-data' }).then(response => {
|
||||
if (response.status === 200 && response.data.msg === 'success') {
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.importSuccess') })
|
||||
@@ -128,7 +128,7 @@ export default {
|
||||
this.importFile = null
|
||||
},
|
||||
downloadTemplate () {
|
||||
const language = localStorage.getItem(storageKey.language) || 'en' // 初始未选择默认 en 英文
|
||||
const language = localStorage.getItem(storageKey.language) || EN // 初始未选择默认 en 英文
|
||||
const fileName = this.exportFileName + '-' + this.$t('overall.template') + '-' + this.getTimeString() + '.json'
|
||||
|
||||
let url = null
|
||||
@@ -159,7 +159,7 @@ export default {
|
||||
})
|
||||
}
|
||||
params.pageSize = -1
|
||||
params.language = localStorage.getItem(storageKey.language) || 'en'
|
||||
params.language = localStorage.getItem(storageKey.language) || EN
|
||||
|
||||
this.export(this.exportUrl, params, this.exportFileName + '-' + this.getTimeString() + '.json')
|
||||
this.closeDialog()
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<div id="header-to-english" :style="language === 'en'?'color:#0091ff':''" @click="changeLocal('en')">
|
||||
<div id="header-to-english" :style="language === EN?'color:#0091ff':''" @click="changeLocal(EN)">
|
||||
English
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<div id="header-to-chinese" :style="language === 'cn'?'color:#0091ff':''" @click="changeLocal('cn')">
|
||||
<div id="header-to-chinese" :style="language === ZH?'color:#0091ff':''" @click="changeLocal(ZH)">
|
||||
中文
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
@@ -42,11 +42,11 @@
|
||||
</div>
|
||||
<div class="cn-header__nav">
|
||||
<i class="cn-icon cn-icon-a-NetworkAnalytics"></i>
|
||||
<el-breadcrumb class="header__left-breadcrumb" separator=">">
|
||||
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`" :title="item.value"
|
||||
<el-breadcrumb class="header__left-breadcrumb" separator=">" v-if="route.startsWith('/panel')">
|
||||
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`" :title="index===3?item.value:''"
|
||||
v-for="(item,index) in breadcrumb" :key="item.value">
|
||||
<template v-if="index===3">
|
||||
<div class="header__left-breadcrumb-item-select">
|
||||
<template v-if="index===3" >
|
||||
<div class="header__left-breadcrumb-item-select" >
|
||||
<el-popover placement="bottom-start"
|
||||
ref="breadcrumbPopover"
|
||||
:show-arrow="false"
|
||||
@@ -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里的数据控制 -->
|
||||
@@ -113,6 +113,13 @@
|
||||
</template>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
<el-breadcrumb class="header__left-breadcrumb" separator=">" v-else>
|
||||
<el-breadcrumb-item class="header__left-breadcrumb-item" :id="`breadcrumb${item.value}`"
|
||||
v-for="(item,index) in breadcrumb" :key="item.value">
|
||||
<span v-if="item.clickable" class="route-menu" @click="jumpOther(item.route,index)">{{ item.value }}</span>
|
||||
<span v-else>{{ item.value }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
<!-- 菜单 -->
|
||||
@@ -196,7 +203,9 @@ import {
|
||||
networkTable,
|
||||
operationType,
|
||||
storageKey,
|
||||
wholeScreenRouterMapping
|
||||
wholeScreenRouterMapping,
|
||||
ZH,
|
||||
EN
|
||||
} from '@/utils/constants'
|
||||
import { api } from '@/utils/api'
|
||||
import { ref } from 'vue'
|
||||
@@ -234,7 +243,7 @@ export default {
|
||||
return {
|
||||
username: localStorage.getItem(storageKey.username),
|
||||
nickName: localStorage.getItem(storageKey.nickName),
|
||||
language: localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : 'en',
|
||||
language: localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : EN,
|
||||
showChangePin: false,
|
||||
from: '', // entity类型
|
||||
changePassForm: {
|
||||
@@ -296,7 +305,9 @@ export default {
|
||||
curTabState: curTabState,
|
||||
urlChangeParams: {},
|
||||
wholeScreenRouterMapping,
|
||||
logo: 'images/logo-header.svg'
|
||||
logo: 'images/logo-header.svg',
|
||||
ZH,
|
||||
EN
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -304,7 +315,7 @@ export default {
|
||||
return this.$store.getters.menuList.find(menu => menu.code === 'networkAnalytics')
|
||||
},
|
||||
otherMenu () {
|
||||
return this.$store.getters.menuList.filter(menu => ['networkAnalytics', 'chart', 'I18N', 'entityDetail', 'temp', 'entityGraph', 'detectionPolicy'].indexOf(menu.code) === -1)
|
||||
return this.$store.getters.menuList.filter(menu => ['networkAnalytics', 'I18N', 'entityDetail', 'entityGraph', 'detectionPolicy'].indexOf(menu.code) === -1)
|
||||
|
||||
/* function excludeButton (menu) {
|
||||
for (let i = 0; i < menu.length; i++) {
|
||||
@@ -322,15 +333,17 @@ export default {
|
||||
breadcrumb () {
|
||||
const breadcrumb = []
|
||||
this.generateBreadcrumb(breadcrumb, this.$store.getters.menuList)
|
||||
// 写死一级和二级菜单是否可以点击跳转
|
||||
if (breadcrumb[0]) {
|
||||
if (['knowledgeBase'].indexOf(breadcrumb[0].code) > -1) {
|
||||
breadcrumb[0].clickable = true
|
||||
}
|
||||
if (breadcrumb[1]) {
|
||||
if (breadcrumb[1].route && breadcrumb[1].route.indexOf('/panel/') === 0) {
|
||||
breadcrumb[1].clickable = true
|
||||
}
|
||||
if (breadcrumb) {
|
||||
// panel菜单是否可以点击跳转:一级菜单不可点击,二级菜单可以点击
|
||||
if (breadcrumb[0] && breadcrumb[1] && breadcrumb[1].route &&
|
||||
breadcrumb[1].route.indexOf('/panel/') === 0) {
|
||||
breadcrumb[1].clickable = true
|
||||
} else { // 除panel外的菜单是否可以点击跳转:除了新增、编辑,其它均可点击
|
||||
breadcrumb.forEach(item => {
|
||||
if (item.value !== 'Create' && item.value !== 'Edit') {
|
||||
item.clickable = true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +379,7 @@ export default {
|
||||
},
|
||||
async breadcrumb (n) {
|
||||
this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
if (this.dnsQtypeMapData.size === 0) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
}
|
||||
@@ -384,7 +397,7 @@ export default {
|
||||
async mounted () {
|
||||
this.from = Object.keys(this.entityType)[0]
|
||||
// 是否需要dns的qtype和rcode的数据字典
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
if (this.dnsQtypeMapData.size === 0) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
}
|
||||
@@ -392,7 +405,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()
|
||||
}
|
||||
@@ -405,10 +418,10 @@ export default {
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : 60
|
||||
const chartTimeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
chartTimeFilter.value.startTime = startTime
|
||||
chartTimeFilter.value.endTime = endTime
|
||||
} else {
|
||||
@@ -423,55 +436,6 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
generateBreadcrumb (breadcrumb, menus) {
|
||||
if (this.route === '/entityDetail') {
|
||||
const entityMenu = menus.find(m => m.route === '/entityExplorer')
|
||||
const entityDetailMenu = menus.find(m => m.route === '/entityDetail')
|
||||
breadcrumb.push({
|
||||
code: entityMenu.code,
|
||||
value: entityMenu.i18n ? this.$t(entityMenu.i18n) : entityMenu.name,
|
||||
route: entityMenu.route,
|
||||
type: entityMenu.type
|
||||
})
|
||||
breadcrumb.push({
|
||||
code: entityDetailMenu.code,
|
||||
value: entityDetailMenu.i18n ? this.$t(entityDetailMenu.i18n) : entityDetailMenu.name,
|
||||
route: entityDetailMenu.route,
|
||||
type: entityDetailMenu.type
|
||||
})
|
||||
return true
|
||||
} else if (this.route === '/entityGraph') {
|
||||
const entityMenu = menus.find(m => m.route === '/entityExplorer')
|
||||
const entityGraphMenu = menus.find(m => m.route === '/entityGraph')
|
||||
breadcrumb.push({
|
||||
code: entityMenu.code,
|
||||
value: entityMenu.i18n ? this.$t(entityMenu.i18n) : entityMenu.name,
|
||||
route: entityMenu.route,
|
||||
type: entityMenu.type
|
||||
})
|
||||
breadcrumb.push({
|
||||
code: entityGraphMenu.code,
|
||||
value: entityGraphMenu.i18n ? this.$t(entityGraphMenu.i18n) : entityGraphMenu.name,
|
||||
route: entityGraphMenu.route,
|
||||
type: entityGraphMenu.type
|
||||
})
|
||||
return true
|
||||
} 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) {
|
||||
breadcrumb.unshift({
|
||||
@@ -523,7 +487,7 @@ export default {
|
||||
},
|
||||
getCurTabByLabel (label) {
|
||||
let curTab = null
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
|
||||
if (curTableInCode && curTableInCode.tabList) {
|
||||
curTab = curTableInCode.tabList.find(item => item.label === label)
|
||||
@@ -536,7 +500,7 @@ export default {
|
||||
const currentValue = document.getElementById('breadcrumbValue') ? document.getElementById('breadcrumbValue').innerText : ''
|
||||
const columnName = this.getUrlParam(this.curTabState.thirdMenu, '')
|
||||
let type = 'ip'
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const curTableInCode = networkTable[tableType] ? networkTable[tableType] : networkTable.networkOverview
|
||||
if (curTableInCode && curTableInCode.tabList) {
|
||||
const curTab = curTableInCode.tabList.find(item => item.label === columnName)
|
||||
@@ -560,7 +524,7 @@ export default {
|
||||
axios.get(curTableInCode.url.drilldownList, { params }).then(async response => {
|
||||
if (response.status === 200) {
|
||||
this.breadcrumbColumnValueListShow = response.data.data.result
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
if (this.dnsQtypeMapData.size === 0) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
}
|
||||
@@ -691,7 +655,7 @@ export default {
|
||||
},
|
||||
async handleCurDrilldownTableConfig (thirdMenu) {
|
||||
// const userId = localStorage.getItem(storageKey.userId)
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
|
||||
const drillDownTableConfigs = await combineDrilldownTableWithUserConfig()
|
||||
const currentTableConfig = drillDownTableConfigs.find(config => config.route === tableType)
|
||||
@@ -712,7 +676,26 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
// 仅处理除panel外的相关路径的导航
|
||||
async jumpOther (route, index) {
|
||||
route = route.replace('redirect:', '')
|
||||
this.showMenu = false
|
||||
if (route === this.route && index > 0) { // 当前只有一级菜单时,点击不进行刷新,重新跳转
|
||||
this.refresh()
|
||||
return
|
||||
}
|
||||
if (route) {
|
||||
this.$router.push({
|
||||
path: route,
|
||||
query: {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
// 仅处理panel相关路径的导航
|
||||
async jump (route, columnName, columnValue, opeType) {
|
||||
route = route.replace('redirect:', '')
|
||||
if (route === '/panel/linkMonitor' && opeType === 3) {
|
||||
return true
|
||||
}
|
||||
@@ -732,7 +715,7 @@ export default {
|
||||
this.$store.commit('setNetworkOverviewTabList', [])
|
||||
}
|
||||
// 清空网络概况的特殊面包屑
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
|
||||
const curTab = await getDefaultCurTab(tableType, metric, columnName)
|
||||
this.$store.getters.menuList.forEach(menu => {
|
||||
@@ -744,11 +727,6 @@ export default {
|
||||
child.columnName = columnName
|
||||
this.urlChangeParams[this.curTabState.thirdMenu] = columnName
|
||||
this.urlChangeParams[this.curTabState.fourthMenu] = columnValue
|
||||
// const tabObjGroup = networkOverviewTabList.filter(item => item.label == columnName)
|
||||
// let curTab = this.getCurTabByLabel()
|
||||
// const type = curTab ? curTab.prop : ''
|
||||
// this.curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
|
||||
// this.urlChangeParams[this.curTabState.dimensionType] = type
|
||||
this.urlChangeParams[this.curTabState.panelName] = columnValue
|
||||
} else if (columnName) { // 点击的为列名
|
||||
child.columnValue = ''
|
||||
@@ -786,6 +764,7 @@ export default {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
return
|
||||
} else if (opeType === 3) {
|
||||
this.$router.push({
|
||||
query: {
|
||||
@@ -794,6 +773,7 @@ export default {
|
||||
t: +new Date()
|
||||
}
|
||||
})
|
||||
return
|
||||
} else if (opeType !== 4) {
|
||||
this.$router.push({
|
||||
query: {
|
||||
@@ -803,6 +783,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() }">
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
<button type="button" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new option-btn" style="margin-left: 0px;" @click="expandAllOrNone" :class="{'btn-active':expandAllFlag}">展开/收缩</button>
|
||||
<button type="button" class="cn-btn cn-btn-size-small-new cn-btn-style-light-new option-btn" @click="selectAllOrNone" :class="{'btn-active':selectAllFlag}"><span ><i class="cn-icon cn-icon-delete"></i></span></button>
|
||||
</div>-->
|
||||
<el-tree :data="menus" :default-expand-all="expandAllFlag" :props="{label:labelFormatter}" @check-change="selectChange" class="tree-border" node-key="id" ref="menuTree" show-checkbox id="role-box-input-menus">
|
||||
<el-checkbox v-model="isCheckAll" :label="$t('overall.all')" @change="checkAll"></el-checkbox>
|
||||
<el-tree :data="menus" :default-expand-all="expandAllFlag" check-strictly="true" :props="{label:labelFormatter}" @check-change="selectChange" class="tree-border" node-key="id" ref="menuTree" show-checkbox id="role-box-input-menus">
|
||||
<template #default="{ data }">
|
||||
<span>
|
||||
<i v-if="data.type === '1'" class="el-icon-menu"></i>
|
||||
@@ -90,7 +91,8 @@ export default {
|
||||
menus: [],
|
||||
selectedIds: [],
|
||||
selectAllFlag: false,
|
||||
expandAllFlag: true
|
||||
expandAllFlag: true,
|
||||
isCheckAll: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -150,9 +152,59 @@ export default {
|
||||
labelFormatter: function (data, node) {
|
||||
return data && data.i18n ? this.$t(data.i18n) : data.name
|
||||
},
|
||||
selectChange: function (data, isCheck, childIsCheck) {
|
||||
getChildNodes (menu) {
|
||||
let nodeGroup = []
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
nodeGroup = menu.children
|
||||
const _this = this
|
||||
menu.children.forEach(node => {
|
||||
const childNodes = _this.getChildNodes(node)
|
||||
if (childNodes && childNodes.length > 0) {
|
||||
nodeGroup = nodeGroup.concat(childNodes)
|
||||
}
|
||||
})
|
||||
}
|
||||
return nodeGroup
|
||||
},
|
||||
checkAll () {
|
||||
if (this.$refs.menuTree) {
|
||||
this.editRole.menuIds = this.$refs.menuTree.getCheckedKeys(true)
|
||||
if (this.isCheckAll) {
|
||||
let nodeGroup = this.menus
|
||||
const _this = this
|
||||
this.menus.forEach(menu => {
|
||||
const childNodes = _this.getChildNodes(menu)
|
||||
if (childNodes && childNodes.length > 0) {
|
||||
nodeGroup = nodeGroup.concat(childNodes)
|
||||
}
|
||||
})
|
||||
this.$refs.menuTree.setCheckedNodes(nodeGroup)
|
||||
} else {
|
||||
this.$refs.menuTree.setCheckedNodes([])
|
||||
}
|
||||
}
|
||||
},
|
||||
checkParentNode(node) {
|
||||
if(node && this.$refs.menuTree.getNode(node)){
|
||||
let parent = this.$refs.menuTree.getNode(node).parent
|
||||
let parentNode = parent.data
|
||||
if(parentNode && parentNode.id && parentNode.id !== 0 ){
|
||||
this.$refs.menuTree.setChecked(parentNode,true,false)
|
||||
this.checkParentNode(parentNode)
|
||||
}
|
||||
}
|
||||
},
|
||||
selectChange: function (data, isCheck, childIsCheck) {
|
||||
if(isCheck) {//如果是选中节点,则同步选中所有的父辈节点(有全选和半选两种状态)
|
||||
this.checkParentNode(data)
|
||||
} else {//如果是取消节点,则同步取消选中所有子节点
|
||||
if(data.children && data.children.length > 0) {
|
||||
data.children.forEach(node => {
|
||||
this.$refs.menuTree.setChecked(node, false, true)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.$refs.menuTree) {
|
||||
this.editRole.menuIds = this.$refs.menuTree.getCheckedKeys(false)
|
||||
}
|
||||
},
|
||||
selectAllOrNone: function () {
|
||||
|
||||
@@ -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
|
||||
@@ -125,16 +125,16 @@ export default {
|
||||
mixins: [rightBoxMixin],
|
||||
data () {
|
||||
const validatePin = (rule, value, callback) => { // 确认密码
|
||||
if (value.length < 5) {
|
||||
if (value && value.length < 5) {
|
||||
callback(new Error(this.$t('validate.atLeastFive')))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateConfirmPin = (rule, value, callback) => { // 确认密码的二次校验
|
||||
if (value === '' && this.editObject.pin) {
|
||||
if (_.isEmpty(value) && !_.isEmpty(this.editObject.pin)) {//密码有内容,确认密码没内容
|
||||
callback(new Error(this.$t('config.user.confirmPin')))
|
||||
} else if (value !== this.editObject.pin) {
|
||||
} else if (!_.isEmpty(value) && value !== this.editObject.pin) {//密码有内容,确认密码也有内容,内容不一致
|
||||
callback(new Error(this.$t('config.user.confirmPinErr')))
|
||||
} else {
|
||||
callback()
|
||||
@@ -207,7 +207,7 @@ export default {
|
||||
],
|
||||
pinChange: [
|
||||
{ validator: validateConfirmPin, trigger: 'blur' },
|
||||
{ pattern: /^[a-zA-Z0-9]{5,64}$/, message: this.$t('validate.atLeastFive') }
|
||||
{ validator: validatePin, trigger: 'blur' }
|
||||
],
|
||||
roleIds: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'blur' }
|
||||
|
||||
@@ -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>
|
||||
@@ -52,13 +52,17 @@
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'status'">
|
||||
<el-switch
|
||||
v-if="scope.row.id"
|
||||
v-if="scope.row.id && hasPermission('editUser')"
|
||||
v-model="scope.row.status"
|
||||
active-value="1"
|
||||
:disabled="(scope.row.username === loginName) || (scope.row.username==='admin' && scope.row.id===1) || scope.row.buildIn === 1"
|
||||
inactive-value="0"
|
||||
@change="()=>{statusChange(scope.row)}">
|
||||
</el-switch>
|
||||
<template v-else>
|
||||
<span v-if="scope.row.status === '1'">{{$t('detection.create.enabled')}}</span>
|
||||
<span v-else>{{$t('detection.create.disabled')}}</span>
|
||||
</template>
|
||||
</template>
|
||||
<span v-else>{{scope.row[item.prop] || '-'}}</span>
|
||||
</template>
|
||||
|
||||
@@ -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===ZH" style="color: rgba(0,0,0,0)">0</div>
|
||||
</div>
|
||||
<div :class="settingObj.ruleType===detectionRuleType.indicator?'block-mode-btn-active':'block-mode-btn'"
|
||||
@click="selectMode(detectionRuleType.indicator)">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, ZH, EN } 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,22 @@ export default {
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
language: EN,
|
||||
ZH
|
||||
}
|
||||
},
|
||||
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 +184,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 +194,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 +215,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<div class="reference-tag__group">
|
||||
<span class="reference-tag" v-for="(refer, index) in scope.row[item.prop].slice(0,2)" >{{refer}}</span>
|
||||
</div>
|
||||
<div class="reference-more">+{{scope.row[item.prop].length - 2}} more</div>
|
||||
<div class="reference-more">+{{scope.row[item.prop].length - 2}} {{$t('overall.more')}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="reference-tag__tip">
|
||||
@@ -70,9 +70,11 @@
|
||||
</el-popover>
|
||||
</templage>
|
||||
<template v-else>
|
||||
<template v-for="(refer, index) in scope.row[item.prop]">
|
||||
<div class="type-tag">{{refer}}</div>
|
||||
</template>
|
||||
<div class="reference-tag__show">
|
||||
<div class="reference-tag__group">
|
||||
<span class="reference-tag" v-for="(refer, index) in scope.row[item.prop]" >{{refer}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'opTime' || item.prop === 'ctime'">
|
||||
@@ -96,6 +98,7 @@
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'status'">
|
||||
<el-switch v-model="scope.row.status"
|
||||
v-if="hasPermission('editUserDefinedLibrary')"
|
||||
active-color="#38ACD2"
|
||||
inactive-color="#C0CEDB"
|
||||
:active-value="1"
|
||||
@@ -103,6 +106,15 @@
|
||||
@change="changeStatus($event,scope.row.knowledgeId)"
|
||||
>
|
||||
</el-switch>
|
||||
<template v-else>
|
||||
<span v-if="scope.row.status === 1">{{$t('detection.create.enabled')}}</span>
|
||||
<span v-else>{{$t('detection.create.disabled')}}</span>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="item.prop === 'color'">
|
||||
<div class="knowledge-color">
|
||||
<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>
|
||||
@@ -151,6 +163,11 @@ export default {
|
||||
}, {
|
||||
label: this.$t('knowledge.reference'),
|
||||
prop: 'reference',
|
||||
width: 190,
|
||||
show: true
|
||||
}, {
|
||||
label: this.$t('overall.color'),
|
||||
prop: 'color',
|
||||
width: 180,
|
||||
show: true
|
||||
}, {
|
||||
@@ -189,6 +206,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 +255,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,38 +3,30 @@
|
||||
<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"
|
||||
v-if="hasPermission('editBuiltInKnowledgeBase')"
|
||||
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"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
|
||||
</div>
|
||||
<div class="card-desc" :title="data.desc">{{data.desc ? data.desc : '—'}}</div>
|
||||
<div class="card-desc" :title="$t(data.desc)">{{$t(data.desc) || '—'}}</div>
|
||||
</div>
|
||||
<div class="card-operate__footer">
|
||||
<button v-if="data.showUpdate"
|
||||
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')"
|
||||
class="top-tool-btn--update"
|
||||
@click="jumpToUpdatePage(data,true)">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
@@ -53,12 +45,12 @@
|
||||
<img :src="data.iconUrl"/>
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<div class="card-title-name" :title="data.label">{{data.label}}</div>
|
||||
<div class="card-title-name" :title="$t(data.label)">{{$t(data.label)}}</div>
|
||||
</div>
|
||||
<div class="card-desc" :title="data.desc ? data.desc:'—'">{{data.desc ? data.desc : '—'}}</div>
|
||||
<div class="card-desc" :title="data.desc ? $t(data.desc) : '—'">{{data.desc ? $t(data.desc) : '—'}}</div>
|
||||
</div>
|
||||
<div class="card-operate__footer">
|
||||
<button v-if="data.showUpdate" :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
<button v-if="data.showUpdate && hasPermission('editBuiltInKnowledgeBase')" :title="$t('overall.update')" class="top-tool-btn--update"
|
||||
@click="jumpToUpdatePage(data,false)">
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
@@ -75,7 +67,9 @@
|
||||
|
||||
<div class="center-dialog">
|
||||
<el-dialog v-model="showUpdateDialog"
|
||||
:destroy-on-close="true"
|
||||
:custom-class="showAddUpdateDialog ? 'update-knowledge update-knowledge--upload' : 'update-knowledge'"
|
||||
:before-close="beforeClose"
|
||||
:after-close="handleClose">
|
||||
<div class="knowledge-update__top" >
|
||||
<div class="update-left__icon">
|
||||
@@ -84,60 +78,144 @@
|
||||
<div class="update-right">
|
||||
<div class="knowledge-enable">
|
||||
<div class="update-title">
|
||||
<div class="card-title-name" :title="updateKnowledge.label">{{updateKnowledge.label}}</div>
|
||||
<div class="card-title-name" :title="$t(updateKnowledge.label)">{{$t(updateKnowledge.label)}}</div>
|
||||
</div>
|
||||
<el-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' && hasPermission('editBuiltInKnowledgeBase')"
|
||||
>
|
||||
<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 class="knowledge-desc" :title="updateKnowledge.desc ? $t(updateKnowledge.desc) : '-'">{{updateKnowledge.desc ? $t(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>
|
||||
<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">
|
||||
@click="uploadRecord"><!--:disabled="hasUpdatingRecord"-->
|
||||
<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"
|
||||
@click="uploadRecord"><!-- :disabled="hasUpdatingRecord" -->
|
||||
<i class="cn-icon-update-knowledge-base cn-icon"></i>
|
||||
<span>{{$t('overall.update')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="{height: updateKnowledge.source === 'cn_psiphon3_ip' && 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="updateKnowledge.source !== 'cn_psiphon3_ip' || 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">
|
||||
@@ -179,7 +257,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="showAddUpdateDialog = false">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button @click="cancle">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submitConfirm">{{ $t('tip.confirm') }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -187,6 +265,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 +275,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="cancleSwitch">{{ $t('overall.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="switchLearning">{{$t('tip.confirm')}}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import table from '@/mixins/table'
|
||||
import Loading from '@/components/common/Loading'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo } from '@/utils/constants'
|
||||
import { ref } from 'vue'
|
||||
import { getSecond, getMillisecond, xAxisTimeFormatter, xAxisTimeRich } from '@/utils/date-util'
|
||||
import { knowledgeCategoryValue, unitTypes, storageKey, builtInKnowledgeBaseBasicInfo, knowledgeCardUpdateRecordType } from '@/utils/constants'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { api } from '@/utils/api'
|
||||
import { detectionTooltipFormatter } from '@/views/charts/charts/tools'
|
||||
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 +319,8 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Loading
|
||||
Loading,
|
||||
ChartNoData
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -237,7 +336,48 @@ export default {
|
||||
updateHistoryList: [],
|
||||
updateObject: {},
|
||||
currentVersion: 0,
|
||||
uploadLoading: false
|
||||
uploadLoading: false,
|
||||
psiphon3Loading: false,
|
||||
updateLogLoading: false,
|
||||
showConfirmSwitch: false,
|
||||
// timer: null,
|
||||
switchKnowledgeId: '',
|
||||
activeTab: 'updateRecord',
|
||||
isNoDataForPsiphon3: false,
|
||||
showErrorForPsiphon3: false,
|
||||
errorMsgForPsiphon3: '',
|
||||
leftOffset: 0,
|
||||
tabType: 'total',
|
||||
mousemoveCursor: '',
|
||||
selectTime: 1440,
|
||||
// hasUpdatingRecord: false,
|
||||
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 () {
|
||||
@@ -252,13 +392,177 @@ export default {
|
||||
uploadErrorTip,
|
||||
fileTypeLimit: '.csv',
|
||||
fileList: ref([]),
|
||||
uploadFileSizeLimit: 100 * 1024 * 1024
|
||||
uploadFileSizeLimit: 1024 * 1024 * 1024,
|
||||
myChart: shallowRef(null),
|
||||
chartOption: shallowRef(null)
|
||||
}
|
||||
},
|
||||
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',
|
||||
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 endTime = window.$dayJs.tz().valueOf()
|
||||
const params = {
|
||||
startTime: getSecond(endTime - this.selectTime * 60 * 1000),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
const url = api.knowledgeBaseTimedistribution.replace('{{knowledgeId}}', this.updateKnowledge.knowledgeId).replace('{{type}}', this.tabType)
|
||||
axios.get(url, { params: params }).then(response => {
|
||||
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]
|
||||
})
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
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 () {
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
activeChange (item) { // isClick:代表是通过点击操作来的
|
||||
if (item) {
|
||||
this.tabType = item.class
|
||||
}
|
||||
this.legendSelectChange(item)
|
||||
if (this.updateKnowledge.source === 'cn_psiphon3_ip') {
|
||||
this.init()
|
||||
}
|
||||
},
|
||||
mouseenterTab (item) {
|
||||
if (this.isNoDataForPsiphon3) return
|
||||
this.mousemoveCursor = item.class
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
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 = []
|
||||
@@ -283,17 +587,9 @@ export default {
|
||||
uploadSuccess (response) {
|
||||
this.uploadLoading = false
|
||||
this.uploaded = true
|
||||
/* this.uploaded = response.code === 200
|
||||
if (response.code === 200) { */
|
||||
this.$message.success(this.$t('tip.success'))
|
||||
this.showAddUpdateDialog = false
|
||||
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
|
||||
})
|
||||
/* } else {
|
||||
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
|
||||
} */
|
||||
this.getCurTabData()
|
||||
},
|
||||
beforeUpload (file) {
|
||||
this.uploadLoading = true
|
||||
@@ -305,6 +601,9 @@ export default {
|
||||
submit () {
|
||||
this.$refs.knowledgeUpload.submit()
|
||||
},
|
||||
cancle () {
|
||||
this.showAddUpdateDialog = false
|
||||
},
|
||||
clickCard (data, event) {
|
||||
if (data.isSelected) { // 原来为选中,当前点击后未选中
|
||||
const index = this.checkList.indexOf(data)
|
||||
@@ -325,6 +624,13 @@ export default {
|
||||
data.isSelected = val
|
||||
this.$emit('checkboxStatusChange', val, data)
|
||||
},
|
||||
beforeClose (done) {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
done()
|
||||
},
|
||||
handleClose () {
|
||||
this.showUpdateDialog = false
|
||||
this.showAddUpdateDialog = false
|
||||
@@ -340,14 +646,19 @@ 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()
|
||||
if (this.activeTab === knowledgeCardUpdateRecordType.intelligenceLearning) {
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveBar()
|
||||
})
|
||||
}
|
||||
},
|
||||
uploadRecord () {
|
||||
this.showAddUpdateDialog = true
|
||||
@@ -355,6 +666,52 @@ 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 === knowledgeCardUpdateRecordType.updateRecord) {
|
||||
params = {
|
||||
...params,
|
||||
opUser: -1
|
||||
}
|
||||
} else if (this.activeTab === knowledgeCardUpdateRecordType.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
|
||||
}
|
||||
/*
|
||||
this.hasUpdatingRecord = false
|
||||
this.updateHistoryList.forEach(item => {
|
||||
if (item.isUpdating) { // if(item.isUpdating){//????????
|
||||
this.hasUpdatingRecord = true
|
||||
}
|
||||
})
|
||||
|
||||
*/
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
}).finally(() => {
|
||||
this.updateLogLoading = false
|
||||
})
|
||||
},
|
||||
// 切换tab
|
||||
handleClick (tab) {
|
||||
this.getCurTabData()
|
||||
if (tab.index === '1') {
|
||||
this.timeChange()
|
||||
}
|
||||
},
|
||||
clearSelect () {
|
||||
this.$nextTick(() => {
|
||||
this.checkList = []
|
||||
@@ -366,18 +723,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 +743,50 @@ export default {
|
||||
dataType: dataType
|
||||
}
|
||||
})
|
||||
},
|
||||
confirmSwitchLearning (id) {
|
||||
this.showConfirmSwitch = true
|
||||
this.switchKnowledgeId = id
|
||||
return false
|
||||
},
|
||||
cancleSwitch () {
|
||||
this.showConfirmSwitch = false
|
||||
},
|
||||
switchLearning () {
|
||||
const hint = this.aiTaggingList.find(d => d.knowledgeId === this.switchKnowledgeId)
|
||||
const toStatus = hint.status === 0 ? 1 : 0
|
||||
const url = toStatus === 0 ? api.knowledgeBaseLearningStop : api.knowledgeBaseLearningStart
|
||||
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.timeChange()
|
||||
},
|
||||
/*
|
||||
hasUpdatingRecord (n) {
|
||||
if (n) { // update record页存在“正在更新”的记录时,每20秒自动请求一次接口
|
||||
this.timer = setTimeout(() => {
|
||||
this.getCurTabData()
|
||||
}, 20000)
|
||||
} else { // 直到出现新的记录,出现新记录后(失败或者成功),取消定时请求接口,右上角"update"按钮恢复可用。"正在更新"和"失败”都会有对应的强调样式。
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
},
|
||||
*/
|
||||
tableData: {
|
||||
handler (n) {
|
||||
if (this.tableData && this.tableData.length > 0) {
|
||||
@@ -420,15 +810,33 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
activeTab (n) {
|
||||
if (n === 'updateRecord') {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
}
|
||||
},
|
||||
showAddUpdateDialog: {
|
||||
handler (n) {
|
||||
if (!n) {
|
||||
this.fileList = []
|
||||
this.timeChange()
|
||||
} 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 +848,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 +871,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.confirmEnablePsiphon3') + '?'
|
||||
} else if (find.status === 1) {
|
||||
tip = this.$t('tip.confirmDisablePsiphon3') + '?'
|
||||
}
|
||||
}
|
||||
}
|
||||
return tip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createI18n } from 'vue-i18n/index'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { storageKey, EN } from '@/utils/constants'
|
||||
import { getI18n } from '@/utils/api'
|
||||
import store from '@/store'
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: localStorage.getItem(storageKey.language) || 'en'
|
||||
locale: localStorage.getItem(storageKey.language) || EN
|
||||
})
|
||||
export async function loadI18n () {
|
||||
if (!store.state.i18n) {
|
||||
|
||||
@@ -5,9 +5,8 @@ import router from '@/router'
|
||||
import store from '@/store'
|
||||
import App from '@/App.vue'
|
||||
import '@/utils/http.js'
|
||||
import { hasPermission } from '@/permission'
|
||||
import commonMixin from '@/mixins/common'
|
||||
import { cancelWithChange, noData } from '@/utils/tools'
|
||||
import { cancelWithChange, noData, myHighLight } from '@/utils/tools'
|
||||
import { ClickOutside } from 'element-plus/lib/directives'
|
||||
import i18n from '@/i18n'
|
||||
// import '@/mock/index.js'
|
||||
@@ -37,10 +36,10 @@ app.use(i18n)
|
||||
app.use(hljsVuePlugin)
|
||||
app.use(VueGridLayout)
|
||||
|
||||
app.directive('has', hasPermission) // 注册指令
|
||||
app.directive('ele-click-outside', ClickOutside)
|
||||
app.directive('cancel', cancelWithChange)
|
||||
app.directive('no-data', noData)
|
||||
app.directive('high-light', myHighLight)
|
||||
app.config.globalProperties.$_ = _
|
||||
|
||||
app.mixin(commonMixin)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hasButton } from '@/permission'
|
||||
import { hasPermission } from '@/permission'
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { commonErrorTip } from '@/utils/constants'
|
||||
export default {
|
||||
@@ -28,9 +28,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hasButton (code) {
|
||||
return hasButton(this.$store.getters.buttonList, code)
|
||||
},
|
||||
hasPermission,
|
||||
errorMsgHandler (axiosError) {
|
||||
if (axiosError.response) {
|
||||
if (axiosError.response.data) {
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
// 请求数据 relationshipUrlOne => 路由 refOne => ref
|
||||
getRelatedServerDataOne (relationshipUrlOne, refOne) {
|
||||
this.loadingRelationshipOne = true
|
||||
axios.get(relationshipUrlOne, { params: this.getQueryParams() }).then(response => {
|
||||
axios.get(relationshipUrlOne, { params: this.getQueryParams(DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity) }).then(response => {
|
||||
if (response.status === 200) {
|
||||
const relationshipDataOne = []
|
||||
if (response.data.data.result.length > 0) {
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
},
|
||||
getRelatedServerDataTwo (relationshipUrlTow, refTow) {
|
||||
this.loadingRelationshipTwo = true
|
||||
axios.get(relationshipUrlTow, { params: this.getQueryParams() }).then(response => {
|
||||
axios.get(relationshipUrlTow, { params: this.getQueryParams(DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity) }).then(response => {
|
||||
if (response.status === 200) {
|
||||
const relationshipDataTwo = []
|
||||
if (response.data.data.result.length > 0) {
|
||||
@@ -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 ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { chartTableOrderOptionsMapping, storageKey, knowledgeCategoryValue } from '@/utils/constants'
|
||||
import { chartTableOrderOptionsMapping, storageKey, knowledgeCategoryValue, ZH } from '@/utils/constants'
|
||||
import { getWidthByLanguage } from '@/utils/tools'
|
||||
import { api } from '@/utils/api'
|
||||
import axios from 'axios'
|
||||
@@ -58,7 +58,7 @@ export default {
|
||||
const language = localStorage.getItem(storageKey.language)
|
||||
// 文字所占宽度,一个英文字母占7px,中文16px
|
||||
let num = getWidthByLanguage(language) || 7
|
||||
if (language !== 'cn') {
|
||||
if (language !== ZH) {
|
||||
num = num + 1 // 最后一位加空格
|
||||
}
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -32,14 +32,19 @@ router.beforeEach(async (to, from, next) => {
|
||||
store.commit('setMenuList', menuList)
|
||||
store.commit('setButtonList', buttonList)
|
||||
store.commit('setRoleList', roleList)
|
||||
}
|
||||
if (to.path) {
|
||||
next()
|
||||
/* if (hasMenu(store.getters.menuList, to.path)) {
|
||||
const homeRoute = {
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('@/components/layout/Home'),
|
||||
children: []
|
||||
}
|
||||
handleRoutes(menuList, homeRoute.children)
|
||||
router.addRoute(homeRoute)
|
||||
next({ ...to, replace: true })
|
||||
} else {
|
||||
if (to.path) {
|
||||
next()
|
||||
} else {
|
||||
ElMessage.error('No access') // TODO 国际化
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -60,14 +65,14 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
// menuList中是否包含route权限
|
||||
export function hasMenu (menuList, route) {
|
||||
// menuList中是否包含code
|
||||
export function hasMenu (menuList, code) {
|
||||
return menuList.some(menu => {
|
||||
if (menu.route === route) {
|
||||
if (menu.code === code) {
|
||||
return true
|
||||
} else {
|
||||
if (menu.children) {
|
||||
if (hasMenu(menu.children, route)) {
|
||||
if (hasMenu(menu.children, code)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -96,36 +101,9 @@ export function hasButton (buttonList, code) {
|
||||
return buttonList.some(button => button === code)
|
||||
}
|
||||
|
||||
// 用法 v-has="code" | v-has="[code...]" 任意匹配一个 | v-has:all="[code...]" 全匹配
|
||||
export const hasPermission = {
|
||||
beforeMount (el, binding) {
|
||||
// 节点权限处理
|
||||
const buttonCode = binding.value
|
||||
const arg = binding.arg
|
||||
if (buttonCode) {
|
||||
if (buttonCode instanceof Array) {
|
||||
let has = true
|
||||
if (arg && arg === 'all') { // 全匹配
|
||||
buttonCode.forEach(button => {
|
||||
if (has) {
|
||||
has = hasButton(store.getters.buttonList, button)
|
||||
}
|
||||
})
|
||||
} else { // 任意匹配
|
||||
has = buttonCode.some(button => {
|
||||
return hasButton(store.getters.buttonList, button)
|
||||
})
|
||||
}
|
||||
if (!has) {
|
||||
el.parentNode.removeChild(el)
|
||||
}
|
||||
} else { // 单个匹配
|
||||
if (!hasButton(store.getters.buttonList, buttonCode)) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 根据code从menuList和buttonList中判断是否有权限
|
||||
export function hasPermission (code) {
|
||||
return hasButton(store.getters.buttonList, code) || hasMenu(store.getters.menuList, code)
|
||||
}
|
||||
|
||||
// 根据orderNum排序
|
||||
@@ -160,3 +138,100 @@ export function getWelcomeMenu (menu) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function handleComponent (code) {
|
||||
switch (code) {
|
||||
case 'networkOverview':
|
||||
case 'networkAppPerformance':
|
||||
case 'dnsServiceInsights':
|
||||
case 'linkMonitor':
|
||||
return () => import('@/views/charts2/Panel')
|
||||
case 'entity':
|
||||
return () => import('@/views/entityExplorer/EntityExplorer')
|
||||
case 'entityDetail':
|
||||
return () => import('@/views/entityExplorer/EntityDetail')
|
||||
case 'entityGraph':
|
||||
return () => import('@/views/entityExplorer/EntityGraph')
|
||||
case 'securityEvents':
|
||||
case 'performanceEvents':
|
||||
return () => import('@/views/detections/Index')
|
||||
case 'detectionPolicy':
|
||||
return () => import('@/views/detections/detectionPolicies/Index')
|
||||
case 'createDetectionPolicy':
|
||||
case 'editDetectionPolicy':
|
||||
return () => import('@/views/detections/detectionPolicies/PolicyForm')
|
||||
case 'report':
|
||||
return () => import('@/views/report/Report')
|
||||
case 'knowledgeBase':
|
||||
return () => import('@/views/setting/KnowledgeBase')
|
||||
case 'userDefinedLibrary':
|
||||
return () => import('@/views/setting/KnowledgeBaseUserDefinedList')
|
||||
case 'createUserDefinedLibrary':
|
||||
case 'editUserDefinedLibrary':
|
||||
return () => import('@/views/setting/KnowledgeBaseForm')
|
||||
case 'administration':
|
||||
return () => import('@/views/administration/Index')
|
||||
case 'user':
|
||||
return () => import('@/views/administration/User')
|
||||
case 'role':
|
||||
return () => import('@/views/administration/Roles')
|
||||
case 'operationLog':
|
||||
return () => import('@/views/administration/OperationLog')
|
||||
case 'appearance':
|
||||
return () => import('@/views/administration/Appearance')
|
||||
case 'I18N':
|
||||
return () => import('@/views/administration/I18n')
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function handleRoutes (menus, routes) {
|
||||
menus.forEach(menu => {
|
||||
if (menu.route === '' && (!menu.children || menu.children.length < 0)) {
|
||||
return false
|
||||
}
|
||||
// administration的路由使用了嵌套,其他的是平铺
|
||||
if (menu.pid === 0 && menu.code === 'administration') {
|
||||
const path = menu.route.replace('redirect:', '')
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
const route = {
|
||||
name: menu.name,
|
||||
path,
|
||||
redirect: menu.children[0].route,
|
||||
component: handleComponent(menu.code),
|
||||
children: []
|
||||
}
|
||||
menu.children.forEach(c => {
|
||||
route.children.push({
|
||||
name: c.name,
|
||||
path: c.route,
|
||||
component: handleComponent(c.code)
|
||||
})
|
||||
})
|
||||
routes.push(route)
|
||||
}
|
||||
} else {
|
||||
if (menu.route && menu.route.startsWith('redirect:')) {
|
||||
const path = menu.route.replace('redirect:', '')
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
routes.push({
|
||||
name: menu.name,
|
||||
path,
|
||||
redirect: menu.children[0].route
|
||||
})
|
||||
handleRoutes(menu.children, routes)
|
||||
}
|
||||
} else {
|
||||
routes.push({
|
||||
name: menu.code,
|
||||
path: menu.route,
|
||||
component: handleComponent(menu.code)
|
||||
})
|
||||
}
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
handleRoutes(menu.children, routes)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,111 +5,6 @@ const routes = [
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('@/Login')
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('@/components/layout/Home'),
|
||||
children: [
|
||||
{
|
||||
name: 'panel',
|
||||
path: '/panel/:typeName',
|
||||
component: () => import('@/views/charts2/Panel')
|
||||
},
|
||||
{
|
||||
path: '/report/builtIn',
|
||||
component: () => import('@/views/report/Report')
|
||||
},
|
||||
{
|
||||
path: '/entityExplorer',
|
||||
component: () => import('@/views/entityExplorer/EntityExplorer')
|
||||
},
|
||||
{
|
||||
path: '/entityDetail',
|
||||
component: () => import('@/views/entityExplorer/EntityDetail')
|
||||
},
|
||||
{
|
||||
path: '/entityGraph',
|
||||
component: () => import('@/views/entityExplorer/EntityGraph')
|
||||
},
|
||||
{
|
||||
path: '/detection',
|
||||
redirect: '/detection/securityEvent'
|
||||
},
|
||||
{
|
||||
path: '/detection/:typeName',
|
||||
component: () => import('@/views/detections/Index')
|
||||
},
|
||||
{
|
||||
path: '/businessLog/viewer',
|
||||
component: () => import('@/views/businessLog/Viewer')
|
||||
},
|
||||
{
|
||||
path: '/knowledgeBase',
|
||||
component: () => import('@/views/setting/KnowledgeBase')
|
||||
},
|
||||
{
|
||||
path: '/knowledgeBase/userDefinedLibrary',
|
||||
component: () => import('@/views/setting/KnowledgeBase')
|
||||
},
|
||||
{
|
||||
path: '/knowledgeBase/userDefinedLibrary/create',
|
||||
component: () => import('@/views/setting/KnowledgeBaseForm')
|
||||
},
|
||||
{
|
||||
path: '/knowledgeBase/userDefinedLibrary/edit',
|
||||
component: () => import('@/views/setting/KnowledgeBaseForm')
|
||||
},
|
||||
{
|
||||
name: 'Administration',
|
||||
path: '/administration',
|
||||
redirect: '/administration/user',
|
||||
component: () => import('@/views/administration/Index'),
|
||||
children: [
|
||||
{
|
||||
name: 'User',
|
||||
path: '/administration/user',
|
||||
component: () => import('@/views/administration/User')
|
||||
},
|
||||
{
|
||||
name: 'Role',
|
||||
path: '/administration/role',
|
||||
component: () => import('@/views/administration/Roles')
|
||||
},
|
||||
{
|
||||
name: 'OperationLog',
|
||||
path: '/administration/operationLog',
|
||||
component: () => import('@/views/administration/OperationLog')
|
||||
},
|
||||
{
|
||||
name: 'Appearance',
|
||||
path: '/administration/appearance',
|
||||
component: () => import('@/views/administration/Appearance')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'I18n',
|
||||
path: '/i18n',
|
||||
component: () => import('@/views/administration/I18n')
|
||||
},
|
||||
{
|
||||
name: 'Chart',
|
||||
path: '/chart',
|
||||
component: () => import('@/views/administration/Chart')
|
||||
},
|
||||
{
|
||||
path: '/detectionsNew',
|
||||
component: () => import('@/views/detectionsNew/Index')
|
||||
},
|
||||
{
|
||||
path: '/detection/policies',
|
||||
component: () => import('@/views/detectionsNew/Index')
|
||||
},
|
||||
{
|
||||
path: '/detection/policies/create',
|
||||
component: () => import('@/views/detectionsNew/DetectionForm')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import router from '@/router'
|
||||
import { getWelcomeMenu, sortByOrderNum } from '@/permission'
|
||||
import { getWelcomeMenu, sortByOrderNum, handleRoutes } from '@/permission'
|
||||
import { ElMessage } from 'element-plus' // dependent on utc plugin
|
||||
import { dbDrilldownTableConfig, storageKey } from '@/utils/constants'
|
||||
import { getConfigVersion } from '@/utils/tools'
|
||||
@@ -64,7 +64,14 @@ const user = {
|
||||
store.commit('setMenuList', menuList)
|
||||
store.commit('setButtonList', res2.data.buttons)
|
||||
store.commit('setRoleList', res2.data.roles)
|
||||
|
||||
const homeRoute = {
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('@/components/layout/Home'),
|
||||
children: []
|
||||
}
|
||||
handleRoutes(menuList, homeRoute.children)
|
||||
router.addRoute(homeRoute)
|
||||
if (res.loginSuccessPath) {
|
||||
let tempArr = res.loginSuccessPath.split('?')
|
||||
const path = tempArr[0]
|
||||
@@ -118,7 +125,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
@@ -43,7 +43,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 +121,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 +137,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' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -80,15 +80,15 @@ export const dataForNetworkOverviewLine = {
|
||||
options2: [
|
||||
{
|
||||
value: 'Average',
|
||||
label: 'Average'
|
||||
label: 'overall.average'
|
||||
},
|
||||
{
|
||||
value: '95th Percentile',
|
||||
label: '95th Percentile'
|
||||
label: ['overall.percentileNumber', { number: 95 }]
|
||||
},
|
||||
{
|
||||
value: 'Maximum',
|
||||
label: 'Maximum'
|
||||
label: 'overall.maximum'
|
||||
}
|
||||
],
|
||||
tabsTemplate: [
|
||||
@@ -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' },
|
||||
@@ -200,15 +200,15 @@ export const dataForDnsTrafficLine = {
|
||||
options2: [
|
||||
{
|
||||
value: 'Average',
|
||||
label: 'Average'
|
||||
label: 'overall.average'
|
||||
},
|
||||
{
|
||||
value: '95th Percentile',
|
||||
label: '95th Percentile'
|
||||
label: ['overall.percentileNumber', { number: 95 }]
|
||||
},
|
||||
{
|
||||
value: 'Maximum',
|
||||
label: 'Maximum'
|
||||
label: 'overall.maximum'
|
||||
}
|
||||
],
|
||||
tabs: [
|
||||
@@ -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 = [
|
||||
{
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import i18n from '@/i18n'
|
||||
import _ from 'lodash'
|
||||
import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, networkTable, dbDrilldownTableConfig } from '@/utils/constants'
|
||||
import { storageKey, iso36112, topDomain, echartsFontSize, dbGeoDataTableName, networkTable, dbDrilldownTableConfig, ZH, EN } from '@/utils/constants'
|
||||
import { getIso36112JsonData, getDictList } from '@/utils/api'
|
||||
import { format } from 'echarts'
|
||||
import router from '@/router'
|
||||
import store from '@/store'
|
||||
import indexedDBUtils from '@/indexedDB'
|
||||
import { columnType } from '@/components/advancedSearch/meta/meta'
|
||||
|
||||
export const tableSort = {
|
||||
// 是否需要排序
|
||||
@@ -784,7 +785,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 +800,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)
|
||||
})
|
||||
@@ -1260,10 +1249,10 @@ export function getQueryByFlag2 (type, condition) {
|
||||
*/
|
||||
export function getWidthByLanguage (language) {
|
||||
switch (language) {
|
||||
case 'en': {
|
||||
case EN: {
|
||||
return 7
|
||||
}
|
||||
case 'cn': {
|
||||
case ZH: {
|
||||
return 16
|
||||
}
|
||||
}
|
||||
@@ -1318,12 +1307,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 +1346,104 @@ 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};`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段高亮
|
||||
* 用法: <span v-high-light=[{type: 'string', value: '搜索'}, {type: 'fullText', value: '高亮'}]>搜索关键字高亮<span>
|
||||
* 其中type为fullText的为模糊搜索
|
||||
* @type {{updated(*, *): (*|undefined)}}
|
||||
*/
|
||||
export const myHighLight = {
|
||||
updated (el, binding) {
|
||||
if (el && binding.value) {
|
||||
const { value } = binding
|
||||
if (value && value.length > 0) {
|
||||
const text = _.cloneDeep(el.innerHTML)
|
||||
const regex = new RegExp(value.map(item => `${item.value}`).join('|'), 'g')
|
||||
if (el.getElementsByClassName('highlight__text').length === 0) {
|
||||
const newText = text.replace(regex, (match) => {
|
||||
// 将value中的value提取出来对比,string即精准搜索,fullText模糊搜索
|
||||
for (const item of value) {
|
||||
if ((item.type === columnType.string && item.value === el.innerHTML) || el.className.indexOf('high-location') > -1) {
|
||||
if (el.className.indexOf('high-light-block') > -1) {
|
||||
return `<span class="highlight__block">${match}</span>`
|
||||
} else {
|
||||
return `<span class="highlight__text">${match}</span>`
|
||||
}
|
||||
} else if (item.type === columnType.fullText && item.value === match) {
|
||||
if (el.className.indexOf('high-light-block') > -1) {
|
||||
return `<span class="highlight__block">${match}</span>`
|
||||
} else {
|
||||
return `<span class="highlight__text">${match}</span>`
|
||||
}
|
||||
}
|
||||
}
|
||||
return match
|
||||
})
|
||||
if (newText && newText !== '-') {
|
||||
// 此处不用el.className.indexOf('high-light-block')判断,是因为block可能会有多个,有一个满足所有的都会渲染
|
||||
if (newText.indexOf('highlight__block') > -1) {
|
||||
el.style.cssText = el.style.cssText + 'background: #FEECC2;'
|
||||
// 此处是相关app、相关ip、相关domain弹窗获取不到dom的草错
|
||||
let dom
|
||||
if (document.getElementById('showRelatedApp')) {
|
||||
dom = document.getElementById('showRelatedApp')
|
||||
} else if (document.getElementById('showRelatedDomain')) {
|
||||
dom = document.getElementById('showRelatedDomain')
|
||||
}
|
||||
|
||||
if (dom) {
|
||||
const itemDom = dom.getElementsByClassName('high-light-block')
|
||||
if (itemDom) {
|
||||
for (let i = 0; i < itemDom.length; i++) {
|
||||
if (itemDom[i].innerHTML === el.innerHTML) {
|
||||
itemDom[i].style.cssText = itemDom[i].style.cssText + 'background: #FEECC2;'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
el.innerHTML = newText
|
||||
}
|
||||
} else {
|
||||
return newText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const changeTimeByDate = (date) => {
|
||||
if (date) {
|
||||
const nowDate = Date.now()
|
||||
const oldDate = isNaN(date) ? Date.parse(date) : date
|
||||
const diff = Math.floor((nowDate - oldDate) / 1000)
|
||||
if (diff < 60) {
|
||||
return diff + i18n.global.t('entity.search.secondsAgo')
|
||||
} else if (diff >= 60 && diff < 3600) {
|
||||
const minutes = Math.floor(diff / 60)
|
||||
return minutes === 1 ? `${minutes} ${i18n.global.t('entity.search.minuteAgo')}` : `${minutes} ${i18n.global.t('entity.search.minutesAgo')}`
|
||||
} else if (diff >= 3600 && diff < 86400) {
|
||||
const hours = Math.floor(diff / 3600)
|
||||
return hours === 1 ? `${hours} ${i18n.global.t('entity.search.hourAgo')}` : `${hours} ${i18n.global.t('entity.search.hoursAgo')}`
|
||||
} else {
|
||||
return date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="edit-appearance-base__footer">
|
||||
<button style="position: relative;" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save">
|
||||
<button style="position: relative;" :class="{'footer__btn--disabled': blockOperation.save}" :disabled="blockOperation.save" class="footer__btn" @click="save"
|
||||
v-if="hasPermission('editAppearence')"
|
||||
>
|
||||
<loading size="small" :loading="blockOperation.save"></loading>
|
||||
<span>{{$t('overall.save')}}</span>
|
||||
</button>
|
||||
@@ -58,7 +60,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { api } from '@/utils/api'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { storageKey, ZH, EN } from '@/utils/constants'
|
||||
import axios from 'axios'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
@@ -97,12 +99,12 @@ export default {
|
||||
{
|
||||
id: 1,
|
||||
label: 'English',
|
||||
value: 'en'
|
||||
value: EN
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '中文',
|
||||
value: 'zh'
|
||||
value: ZH
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -11,16 +11,19 @@
|
||||
>
|
||||
<template v-slot:top-tool-left>
|
||||
<button id="roles-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
|
||||
v-if="hasPermission('createRole')"
|
||||
@click="add">
|
||||
<i class="cn-icon-xinjian cn-icon"></i>
|
||||
<span>{{$t('overall.create')}}</span>
|
||||
</button>
|
||||
<button id="roles-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
|
||||
v-if="hasPermission('editRole')"
|
||||
@click="edit">
|
||||
<i class="cn-icon-edit cn-icon"></i>
|
||||
<span>{{$t('overall.edit')}}</span>
|
||||
</button>
|
||||
<button id="roles-delete" class="top-tool-btn margin-r-10" :disabled="disableDelete"
|
||||
v-if="hasPermission('deleteRole')"
|
||||
@click="delBatch">
|
||||
<i class="cn-icon-delete cn-icon"></i>
|
||||
<span>{{$t('overall.delete')}}</span>
|
||||
|
||||
@@ -11,16 +11,19 @@
|
||||
>
|
||||
<template #top-tool-left>
|
||||
<button id="account-add" class="top-tool-btn margin-r-10 top-tool-btn--create"
|
||||
v-if="hasPermission('createUser')"
|
||||
@click="add">
|
||||
<i class="cn-icon-xinjian cn-icon"></i>
|
||||
<span>{{$t('overall.create')}}</span>
|
||||
</button>
|
||||
<button id="account-edit" class="top-tool-btn margin-r-10" :disabled="disableEdit"
|
||||
v-if="hasPermission('editUser')"
|
||||
@click="editSelectRecord">
|
||||
<i class="cn-icon-edit cn-icon"></i>
|
||||
<span>{{$t('overall.edit')}}</span>
|
||||
</button>
|
||||
<button id="account-delete" class="top-tool-btn margin-r-10" :disabled="disableDelete"
|
||||
v-if="hasPermission('deleteUser')"
|
||||
@click="delBatch">
|
||||
<i class="cn-icon-delete cn-icon"></i>
|
||||
<span>{{$t('overall.delete')}}</span>
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
v-if="panel.params && panel.params.wholeScreenScroll"
|
||||
id="wholeScreenBox"
|
||||
>
|
||||
<dns-screen v-if="currentPath === wholeScreenRouterMapping.dns"
|
||||
<!-- <dns-screen v-if="currentPath === wholeScreenRouterMapping.dns"
|
||||
:copy-data-list="chartList"
|
||||
ref="dnsScreen"
|
||||
:time-filter="timeFilter"
|
||||
:entity="entity">
|
||||
</dns-screen>
|
||||
</dns-screen>-->
|
||||
</div>
|
||||
<div id="panelList" v-if="!isEntityDetail" class="panel-list">
|
||||
<div id="cn-panel" class="cn-panel2">
|
||||
@@ -49,7 +49,7 @@
|
||||
<script>
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ref } from 'vue'
|
||||
import DnsScreen from '@/views/charts/wholeScreenScroll/DnsScreen'
|
||||
// import DnsScreen from '@/views/charts/wholeScreenScroll/DnsScreen'
|
||||
import { panelTypeAndRouteMapping, wholeScreenRouterMapping } from '@/utils/constants'
|
||||
import { api, getPanelList, getChartList } from '@/utils/api'
|
||||
import { getNowTime } from '@/utils/date-util'
|
||||
@@ -66,9 +66,9 @@ export default {
|
||||
isEntityDetail: Boolean,
|
||||
typeName: String
|
||||
},
|
||||
components: {
|
||||
/* components: {
|
||||
DnsScreen
|
||||
},
|
||||
}, */
|
||||
data () {
|
||||
return {
|
||||
chartList: [], // 普通panel的chart
|
||||
@@ -104,8 +104,8 @@ export default {
|
||||
setup (props, ctx) {
|
||||
const panel = ref({})
|
||||
let panelType = 1 // 取得panel的type
|
||||
const { params } = useRoute()
|
||||
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName]
|
||||
const { path } = useRoute()
|
||||
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[path.replace('/panel/', '')]
|
||||
|
||||
// date
|
||||
const dateRangeValue = 60
|
||||
|
||||
@@ -338,7 +338,10 @@ export function axisFormatter (params) {
|
||||
return str
|
||||
}
|
||||
export function tooLongFormatter (name) {
|
||||
return format.truncateText(name, 110, '12px')
|
||||
return format.truncateText(name, 160, '12px')
|
||||
}
|
||||
export function tooLongFormatterFor2Columns (name) {
|
||||
return format.truncateText(name, 100, '12px')
|
||||
}
|
||||
export function timeHorizontalFormatter (params) {
|
||||
let str = '<div>'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="panel-box2" :class="{'panel-box2--entity-detail': entity && entity.entityType}">
|
||||
<div class="panel__header" v-if="!entity">
|
||||
<div class="panel__title">{{panelName?panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}}
|
||||
<div class="panel__title">{{panelName ? panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}}
|
||||
<div v-if="showScore" class="score">
|
||||
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
|
||||
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
|
||||
@@ -24,14 +24,14 @@
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="metric"
|
||||
placeholder=""
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
v-if="showMetric"
|
||||
:popper-append-to-body="false"
|
||||
@change="metricChange"
|
||||
>
|
||||
<template #prefix>
|
||||
<span class="select-prefix">Metric:</span>
|
||||
<span class="select-prefix">{{$t('detections.metric')}}:</span>
|
||||
</template>
|
||||
<el-option v-for="item in metricOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
@@ -104,7 +104,9 @@ export default {
|
||||
dnsRcodeMapData: [],
|
||||
dnsQtypeMapData: [],
|
||||
score: null,
|
||||
curTabState: curTabState
|
||||
curTabState: curTabState,
|
||||
performanceData: {},
|
||||
scoreDataState: false // 评分数据是否加载完成
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -114,29 +116,39 @@ 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 () {
|
||||
// this.panelName = this.$store.getters.getPanelName
|
||||
const pName = this.$route.query.panelName ? this.$t(this.$route.query.panelName) : ''
|
||||
const curTabProp = this.$route.query.dimensionType ? this.$route.query.dimensionType : null
|
||||
if (this.$route.params.typeName === fromRoute.dnsServiceInsights) {
|
||||
if (this.$route.path.replace('/panel/', '') === fromRoute.dnsServiceInsights) {
|
||||
this.dnsQtypeMapData = await getDnsMapData('dnsQtype')
|
||||
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
|
||||
this.$store.commit('setDnsQtypeMapData', this.dnsQtypeMapData)
|
||||
@@ -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) {
|
||||
@@ -231,12 +249,11 @@ export default {
|
||||
|
||||
const panel = ref({})
|
||||
let panelType = 1 // 取得panel的type
|
||||
let { params, query, path } = useRoute()
|
||||
let { query, path } = useRoute()
|
||||
|
||||
// 获取路由跳转过的历史状态,赋值给当前界面,起到保留状态的作用,如浏览器的回退前进等
|
||||
const routerObj = store.getters.getRouterHistoryList.find(item => item.t === query.t)
|
||||
if (routerObj) {
|
||||
params = routerObj.params
|
||||
query = routerObj.query
|
||||
path = routerObj.path
|
||||
|
||||
@@ -274,19 +291,19 @@ export default {
|
||||
} else if (thirdPanel) {
|
||||
panelType = Number(thirdPanel)
|
||||
} else {
|
||||
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[params.typeName]
|
||||
panelType = props.entity ? props.entity.type : panelTypeAndRouteMapping[path.replace('/panel/', '')]
|
||||
}
|
||||
|
||||
// 获取url携带的range、startTime、endTime
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
// 优先级:url > config.js > 默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.dashboard || 60)
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = getSecond(startTime)
|
||||
timeFilter.value.endTime = getSecond(endTime)
|
||||
// 如果没有时间参数,就将参数写入url
|
||||
@@ -396,6 +413,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,30 +498,36 @@ 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
|
||||
})
|
||||
}
|
||||
},
|
||||
jumpEntityDetail () {
|
||||
const { href } = this.$router.resolve({
|
||||
path: '/entityDetail',
|
||||
path: '/entity/detail',
|
||||
query: {
|
||||
entityType: this.entityType,
|
||||
entityName: this.entityValue
|
||||
}
|
||||
})
|
||||
window.open(href, '_blank')
|
||||
},
|
||||
handleScoreData () {
|
||||
this.score = computeScore(this.performanceData, this.$store.getters.getScoreBase)
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { entityDetailRelatedEntitiesShowSize } from '@/utils/constants'
|
||||
import { entityDetailRelatedEntitiesShowSize, entityDetailTabsName } from '@/utils/constants'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -28,6 +29,59 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
getParamsByTabType (tabType) {
|
||||
let params = {
|
||||
resource: this.entity.entityName
|
||||
}
|
||||
let dataRangeValue = 60 * 24 * 7
|
||||
switch (tabType) {
|
||||
case entityDetailTabsName.relatedEntity:
|
||||
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity
|
||||
break
|
||||
case entityDetailTabsName.performanceEvent:
|
||||
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.performanceEvent
|
||||
break
|
||||
case entityDetailTabsName.securityEvent:
|
||||
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.securityEvent
|
||||
break
|
||||
case entityDetailTabsName.openPort:
|
||||
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.openPort
|
||||
break
|
||||
case entityDetailTabsName.informationAggregation:
|
||||
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.informationAggregation
|
||||
break
|
||||
case entityDetailTabsName.behaviorPattern:
|
||||
dataRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.behaviorPattern
|
||||
break
|
||||
default:
|
||||
dataRangeValue = 60 * 24 * 7
|
||||
}
|
||||
|
||||
if (dataRangeValue !== 0) {
|
||||
const endTime = window.$dayJs.tz().valueOf()
|
||||
const startTime = endTime - dataRangeValue * 60 * 1000
|
||||
params = {
|
||||
...params,
|
||||
startTime: getSecond(startTime),
|
||||
endTime: getSecond(endTime)
|
||||
}
|
||||
}
|
||||
return params
|
||||
},
|
||||
getParams () {
|
||||
const range = this.timeFilter.dateRangeValue
|
||||
let params = {
|
||||
resource: this.entity.entityName
|
||||
}
|
||||
if (range !== 0) {
|
||||
params = {
|
||||
...params,
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
}
|
||||
return params
|
||||
},
|
||||
handleShowDataNum (showListInfo, allList) {
|
||||
if (allList.length <= entityDetailRelatedEntitiesShowSize) {
|
||||
showListInfo.num = allList.length
|
||||
|
||||
@@ -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,19 +41,22 @@
|
||||
<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>
|
||||
<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"
|
||||
@change="referenceSelectChange"
|
||||
>
|
||||
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
<el-option :key="options2[0].value" :label="$t(options2[0].label)" :value="options2[0].value"></el-option>
|
||||
<el-option :key="options2[1].value" :label="$t(options2[1].label[0], options2[1].label[1])" :value="options2[1].value"></el-option>
|
||||
<el-option :key="options2[2].value" :label="$t(options2[2].label)" :value="options2[2].value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,6 +178,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)
|
||||
@@ -250,6 +257,14 @@ export default {
|
||||
label: {
|
||||
formatter (params) {
|
||||
const arr = unitConvert(params.value, unitTypes.number).join('')
|
||||
const referIndex = _this.options2.findIndex(o => o.value === _this.lineRefer)
|
||||
if (referIndex > -1) {
|
||||
if (referIndex === 1) {
|
||||
return _this.$t(_this.options2[1].label[0], _this.options2[1].label[1]) + '(' + arr + echartsData[0].unitType + ')'
|
||||
} else {
|
||||
return _this.$t(_this.options2[referIndex].label) + '(' + arr + echartsData[0].unitType + ')'
|
||||
}
|
||||
}
|
||||
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
|
||||
},
|
||||
position: 'insideStartTop',
|
||||
@@ -445,6 +460,7 @@ export default {
|
||||
this.legendSelectChange(e, 0)
|
||||
})
|
||||
this.tabs = tabs
|
||||
this.lineRefer = 'Average'
|
||||
this.echartsInit(this.tabs, true)
|
||||
} else {
|
||||
const unit = 'bps'
|
||||
@@ -464,7 +480,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 +490,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 +510,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')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="entity-detail-basic-info">
|
||||
<chart-error v-if="showError" :content="errorMsg"/>
|
||||
<div class="entity-type">{{entityType[entity.entityType]}}</div>
|
||||
<div class="entity-type">{{entityTypeName}}</div>
|
||||
<div class="entity-basic-info">
|
||||
<div class="entity-basic-info__name">
|
||||
<span id="entityName">{{entity.entityName}}</span>
|
||||
@@ -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'
|
||||
@@ -83,17 +90,34 @@ export default {
|
||||
hideTagArea: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tagValueHandler (k, k2, value) {
|
||||
if (k === 'psiphon3Ip') {
|
||||
if (k2 === 'type') {
|
||||
const find = psiphon3IpType.find(t => t.value === value)
|
||||
if (find) {
|
||||
return find.name
|
||||
}
|
||||
computed: {
|
||||
entityTypeName () {
|
||||
const type = this.entity.entityType
|
||||
let entityTypeName = '-'
|
||||
switch (type) {
|
||||
case ('ip'): {
|
||||
entityTypeName = 'IP'
|
||||
break
|
||||
}
|
||||
case ('domain'): {
|
||||
entityTypeName = this.$t('overall.domain')
|
||||
break
|
||||
}
|
||||
case ('app'): {
|
||||
entityTypeName = 'APP'
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
return value
|
||||
return entityTypeName
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTagColor,
|
||||
tagValueHandler (value) {
|
||||
const find = tagValueLabelMapping.find(t => t.value === value)
|
||||
return find ? find.name : value
|
||||
},
|
||||
getData () {
|
||||
this.toggleLoading(true)
|
||||
@@ -114,13 +138,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(() => {
|
||||
@@ -258,7 +282,7 @@ export default {
|
||||
icon: 'cn-icon cn-icon-graph',
|
||||
label: i18n.global.t('entities.graph'),
|
||||
url: resolvePath({
|
||||
path: '/entityGraph',
|
||||
path: '/entity/graph',
|
||||
query: {
|
||||
entityType: props.entity.entityType,
|
||||
entityName: props.entity.entityName
|
||||
|
||||
@@ -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"
|
||||
@@ -123,12 +124,12 @@ export default {
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
// 优先级:url > config.js > 默认值。
|
||||
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.entity.trafficLine || 60)
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const { startTime, endTime } = getNowTime(60)
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
} else {
|
||||
@@ -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
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
<span :style="{color: tab.name === activeTab ? '#046ECA' : '#717171'}">{{ tab.tag }}</span>
|
||||
</el-tag>
|
||||
</template>
|
||||
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
|
||||
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></domain-name-resolution>
|
||||
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
|
||||
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></open-port>
|
||||
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
|
||||
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></domain-name-resolution>
|
||||
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" @checkTag="setTag" />
|
||||
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
|
||||
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag" />
|
||||
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></open-port>
|
||||
<behavior-pattern v-else-if="tab.name === entityDetailTabsName.behaviorPattern && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></behavior-pattern>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -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,
|
||||
@@ -54,12 +58,7 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
timer: null,
|
||||
// 最近一天的时间
|
||||
oneDayTimeFilter: {
|
||||
startTime: window.$dayJs.tz().valueOf() - 1440 * 60 * 1000,
|
||||
endTime: window.$dayJs.tz().valueOf()
|
||||
}
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -90,6 +89,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()
|
||||
@@ -107,22 +109,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entity.entityName
|
||||
// startTime: getSecond(this.oneDayTimeFilter.startTime),
|
||||
// endTime: getSecond(this.oneDayTimeFilter.endTime)
|
||||
}
|
||||
|
||||
const url = this.getUrlByEntityType(this.entity.entityType)
|
||||
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: params })
|
||||
const openPort = axios.get(url, { params: params })
|
||||
// const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
|
||||
// const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
|
||||
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: this.getParamsByTabType(entityDetailTabsName.informationAggregation) })
|
||||
const openPort = axios.get(url, { params: this.getParamsByTabType(entityDetailTabsName.openPort) })
|
||||
const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.securityEvent) })
|
||||
const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: this.getParamsByTabType(entityDetailTabsName.performanceEvent) })
|
||||
|
||||
Promise.all([informationAggregation, openPort]).then(response => {
|
||||
if (response[0].status === 200) {
|
||||
Promise.allSettled([informationAggregation, openPort, security, performance]).then(response => {
|
||||
const informationAggregationResponse = response[0].value
|
||||
if (informationAggregationResponse.status === 200) {
|
||||
const list = []
|
||||
response[0].data.data.result.forEach(r => {
|
||||
informationAggregationResponse.data.data.result.forEach(r => {
|
||||
Object.keys(r).forEach(k => {
|
||||
const aggregation = {
|
||||
createTime: r[k].createTime,
|
||||
@@ -136,7 +133,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 })
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -145,39 +142,42 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.initSetTag(entityDetailTabsName.informationAggregation, list.length)
|
||||
}
|
||||
if (response[1].status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.openPort, response[1].data.data.result.length)
|
||||
const openPortResponse = response[1].value
|
||||
if (openPortResponse.status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.openPort, openPortResponse.data.data.result.length)
|
||||
}
|
||||
// if (response[2].status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.securityEvent, response[2].data.data.result.length)
|
||||
const securityResponse = response[2].value
|
||||
if (securityResponse.status === 200) {
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, securityResponse.data.data.result.length)
|
||||
}
|
||||
// let performanceResponse = response[3].value
|
||||
// if (performanceResponse.status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.performanceEvent, performanceResponse.data.data.result.length)
|
||||
// }
|
||||
// if (response[3].status === 200) {
|
||||
// this.initSetTag(entityDetailTabsName.performanceEvent, response[3].data.data.result.length)
|
||||
// }
|
||||
this.initSetTag(entityDetailTabsName.securityEvent, 0)
|
||||
this.initSetTag(entityDetailTabsName.performanceEvent, 0)
|
||||
})
|
||||
|
||||
const relatedEntityParams = this.getParamsByTabType(entityDetailTabsName.relatedEntity)
|
||||
// 域名解析
|
||||
if (this.entity.entityType === 'app') {
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: relatedEntityParams })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: relatedEntityParams })
|
||||
this.promiseData(ipsOfApp, domainsOfApp)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'ip') {
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
|
||||
this.promiseData(appsOfIp, domainsOfIp)
|
||||
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: relatedEntityParams })
|
||||
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: relatedEntityParams })
|
||||
const behaviorPattern = axios.get(api.entity.behaviorPattern, { params: relatedEntityParams })
|
||||
this.promiseData(appsOfIp, domainsOfIp, behaviorPattern)
|
||||
}
|
||||
|
||||
if (this.entity.entityType === 'domain') {
|
||||
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: params })
|
||||
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: params })
|
||||
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: params })
|
||||
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: relatedEntityParams })
|
||||
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: relatedEntityParams })
|
||||
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: relatedEntityParams })
|
||||
this.promiseData(appsOfDomain, ipsOfDomain, fqdnsOfDomain)
|
||||
}
|
||||
},
|
||||
@@ -191,6 +191,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 +263,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 () {
|
||||
|
||||
190
src/views/charts2/charts/entityDetail/tabs/BehaviorPattern.vue
Normal file
190
src/views/charts2/charts/entityDetail/tabs/BehaviorPattern.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<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" >{{ valueToRangeValue(data.value, unitTypes.number).join('')}}</div>
|
||||
<div class="legend-percent">{{ valueToRangeValue(data.percent, unitTypes.percent).join('') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="entityIpRoseType" class="behavior-pattern-chart"></div>
|
||||
<div class="chart-bottom-dot__right"></div>
|
||||
<div class="chart-bottom-dot__left"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import * as echarts from 'echarts'
|
||||
import { pieChartOption4 } from '@/views/charts2/charts/options/echartOption'
|
||||
import { shallowRef, ref } from 'vue'
|
||||
import { entityDetailTabsName, chartColorForBehaviorPattern, unitTypes } from '@/utils/constants'
|
||||
import { valueToRangeValue } 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
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.behaviorPattern
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName,
|
||||
myChart: shallowRef(null),
|
||||
chartColorForBehaviorPattern,
|
||||
unitTypes,
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.initData()
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
},
|
||||
methods: {
|
||||
valueToRangeValue,
|
||||
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 = this.getParams()
|
||||
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 = []
|
||||
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 && key !== 'total') {
|
||||
this.tableData.push({
|
||||
name: key,
|
||||
value: value,
|
||||
percent: dataObject.total ? value / dataObject.total : '-'
|
||||
})
|
||||
}
|
||||
})
|
||||
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>
|
||||
@@ -70,15 +70,15 @@
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { getNowTime } from '@/utils/date-util'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { entityDetailTabsName } from '@/utils/constants'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'DomainNameResolution',
|
||||
mixins: [chartMixin],
|
||||
props: {
|
||||
},
|
||||
components: {
|
||||
chartNoData
|
||||
},
|
||||
@@ -105,16 +105,24 @@ export default {
|
||||
errorMsg2: ''
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.relatedEntity
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initData()
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entity.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
const params = this.getParams()
|
||||
if (this.entity.entityType === 'app') {
|
||||
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
|
||||
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
|
||||
|
||||
@@ -71,9 +71,10 @@
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { entityDetailTabsName, entityDetailTags, tagValueLabelMapping } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'InformationAggregation',
|
||||
@@ -83,6 +84,18 @@ export default {
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.informationAggregation
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mixins: [chartMixin],
|
||||
components: { chartNoData },
|
||||
methods: {
|
||||
@@ -95,7 +108,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
|
||||
}
|
||||
@@ -108,7 +121,12 @@ export default {
|
||||
this.showError = false
|
||||
this.toggleLoading(true)
|
||||
this.informationAggregationList = []
|
||||
axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`).then(response => {
|
||||
const params = this.getParams()
|
||||
let timeStr = ''
|
||||
if (params.startTime && params.endTime) {
|
||||
timeStr = '&startTime=' + params.startTime + '&endTime=' + params.endTime
|
||||
}
|
||||
axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1${timeStr}`).then(response => {
|
||||
const res = response.data
|
||||
if (response.status === 200) {
|
||||
// this.isNoData = res.data.result.length === 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="type-data">
|
||||
<div class="type-title">
|
||||
<span class="title-mark"></span>
|
||||
<span class="type-title-word">{{ $t('entities.tab.currentDevelopmentPortsAndServices') }}</span>({{ openPortList.length }})
|
||||
<span class="type-title-word">{{ $t('entities.tab.currentOpenPortsAndServices') }}</span>({{ openPortList.length }})
|
||||
</div>
|
||||
<div class="type-content">
|
||||
<div v-for="(openPort, index) in openPortList.slice(0,showOpenPortListInfo.num)" :key="index" class="data-item">
|
||||
@@ -27,6 +27,8 @@ import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { api } from '@/utils/api'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { entityDetailTabsName } from '@/utils/constants'
|
||||
import { ref } from 'vue'
|
||||
import { getNowTime } from '@/utils/date-util'
|
||||
|
||||
export default {
|
||||
name: 'OpenPort',
|
||||
@@ -50,16 +52,21 @@ export default {
|
||||
mounted () {
|
||||
this.initData()
|
||||
},
|
||||
setup () {
|
||||
setup (props) {
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.openPort
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entity.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
const url = this.getUrlByEntityType(this.entity.entityType)
|
||||
axios.get(url, { params: params }).then(response => {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span :test-id="`severity${index}`">{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
|
||||
<span :test-id="`severity${index}`">{{ changeSecurity(item.eventSeverity) }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
@@ -50,8 +50,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
@@ -60,6 +60,7 @@ import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import ChartError from '@/components/common/Error'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'PerformanceEvent',
|
||||
@@ -77,33 +78,36 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.performanceEvent
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName
|
||||
entityName,
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// this.initData()
|
||||
this.initData()
|
||||
/*
|
||||
this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
}, 200) */
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
axios.get(`${api.entity.performance}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
@@ -130,6 +134,18 @@ export default {
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
|
||||
},
|
||||
changeSecurity (value) {
|
||||
if (value) {
|
||||
const obj = securityLevel.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
:key="item.eventId">
|
||||
<div class="cn-detection--list">
|
||||
<div class="cn-detection__case entity-detail-security">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__icon"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header">
|
||||
<span
|
||||
@@ -17,7 +17,7 @@
|
||||
class="detection-event-severity-color-block"
|
||||
:style="`background-color: ${eventSeverityColor[item.eventSeverity]}`">
|
||||
</span>
|
||||
<span class="detection-event-severity-block">{{ toUpperCaseByString(item.securityType) || '-' }}</span>
|
||||
<span class="detection-event-severity-block">{{ item.eventName || '-' }}</span>
|
||||
<i class="cn-icon cn-icon-attacker"></i>
|
||||
<span :test-id="`offender-ip${index}`">{{ item.offenderIp || '-' }}</span>
|
||||
<div class="domain">{{ item.offenderDomain }}</div>
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span>{{ toUpperCaseByString(item.eventSeverity) || '-' }}</span>
|
||||
<span>{{ changeSecurity(item.eventSeverity) }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.eventType">
|
||||
<i class="cn-icon cn-icon-event-type"></i>
|
||||
@@ -76,8 +76,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { dateFormatByAppearance } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
|
||||
import { dateFormatByAppearance, getNowTime } from '@/utils/date-util'
|
||||
import { eventSeverityColor, entityDetailTabsName, securityLevel } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
@@ -85,6 +85,7 @@ import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { toUpperCaseByString } from '@/utils/tools'
|
||||
import chartNoData from '@/views/charts/charts/ChartNoData'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'SecurityEvent',
|
||||
@@ -102,33 +103,36 @@ export default {
|
||||
const { query } = useRoute()
|
||||
const entityType = query.entityType
|
||||
const entityName = query.entityName
|
||||
// range取 config.js 中配置的值
|
||||
const dateRangeValue = DEFAULT_TIME_FILTER_RANGE.entity.securityEvent
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
|
||||
return {
|
||||
entityType,
|
||||
entityName
|
||||
entityName,
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// this.initData()
|
||||
this.initData()
|
||||
/*
|
||||
this.isNoData = true
|
||||
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
|
||||
this.toggleLoading(true)
|
||||
const timer = setTimeout(() => {
|
||||
this.toggleLoading(false)
|
||||
clearInterval(timer)
|
||||
}, 200)
|
||||
}, 200) */
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
toUpperCaseByString,
|
||||
dateFormatByAppearance,
|
||||
initData () {
|
||||
const params = {
|
||||
resource: this.entityName
|
||||
// startTime: getSecond(this.timeFilter.startTime),
|
||||
// endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
const params = this.getParams()
|
||||
this.toggleLoading(true)
|
||||
axios.get(`${api.entity.security}/${this.entityType}`, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
@@ -155,6 +159,18 @@ export default {
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
},
|
||||
changeSecurity (value) {
|
||||
if (value) {
|
||||
const obj = securityLevel.find(d => d.value === value)
|
||||
let label = value
|
||||
if (obj) {
|
||||
label = this.$t(obj.label)
|
||||
}
|
||||
return label
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
import { getSecond } from '@/utils/date-util'
|
||||
import { api } from '@/utils/api'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import { storageKey, ZH } from '@/utils/constants'
|
||||
import PopoverContent from './LinkDirectionGrid/PopoverContent'
|
||||
import { computeScore } from '@/utils/tools'
|
||||
import axios from 'axios'
|
||||
@@ -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
|
||||
@@ -137,11 +156,9 @@ export default {
|
||||
})
|
||||
|
||||
// 一行如果无数据,则删除该行,默认10*10矩阵
|
||||
const rowXIndex = 0
|
||||
this.handleXRowNoData(linkGridData, rowXIndex)
|
||||
this.handleXRowNoData(linkGridData, 0)
|
||||
// 一列如果无数据,则删除该列,默认10*10矩阵
|
||||
const rowYIndex = 0
|
||||
this.handleYRowNoData(linkGridData, rowYIndex)
|
||||
this.handleYRowNoData(linkGridData, 0)
|
||||
this.isLinkNoData = linkGridData.length === 0
|
||||
this.linkGridData = linkGridData
|
||||
}
|
||||
@@ -159,9 +176,9 @@ export default {
|
||||
// 接口数据乱序,根据入方向排序,再根据同个入方向下的出方向进行排序
|
||||
nextLinkData.sort((a, b) => {
|
||||
if (a.inLinkDirection !== b.inLinkDirection) {
|
||||
return a.inLinkDirection.localeCompare(b.inLinkDirection, 'zh')
|
||||
return a.inLinkDirection.localeCompare(b.inLinkDirection, ZH)
|
||||
}
|
||||
return a.outLinkDirection.localeCompare(b.outLinkDirection, 'zh')
|
||||
return a.outLinkDirection.localeCompare(b.outLinkDirection, ZH)
|
||||
})
|
||||
|
||||
this.isNextNoData = nextLinkData.length === 0
|
||||
@@ -210,9 +227,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
|
||||
@@ -238,11 +255,9 @@ export default {
|
||||
})
|
||||
|
||||
// 一行如果无数据,则删除该行,默认3*3矩阵
|
||||
const rowXIndex = 0
|
||||
this.handleXRowNoData(nextGridData, rowXIndex)
|
||||
this.handleXRowNoData(nextGridData, 0)
|
||||
// 一列如果无数据,则删除该列,默认3*3矩阵
|
||||
const rowYIndex = 0
|
||||
this.handleYRowNoData(nextGridData, rowYIndex)
|
||||
this.handleYRowNoData(nextGridData, 0)
|
||||
|
||||
this.isNextNoData = nextGridData.length === 0
|
||||
this.nextGridData = nextGridData
|
||||
@@ -254,6 +269,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.scoreDataState = true
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
},
|
||||
@@ -282,6 +298,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
|
||||
@@ -320,7 +353,7 @@ export default {
|
||||
* @param index
|
||||
*/
|
||||
handleXRowNoData (data, index) {
|
||||
if (data) {
|
||||
if (data && data.length > 0) {
|
||||
const item = data[index]
|
||||
let tempList = []
|
||||
if (item) {
|
||||
@@ -343,7 +376,7 @@ export default {
|
||||
*/
|
||||
handleYRowNoData (data, index) {
|
||||
const rowList = []
|
||||
if (data) {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach(item => {
|
||||
if (item.out[index]) {
|
||||
if (item.out[index].noData) {
|
||||
|
||||
@@ -37,11 +37,12 @@
|
||||
</div>
|
||||
<div class="line-select line-header-right">
|
||||
<div class="line-select-metric">
|
||||
<span>{{$t('network.metric')}}:</span>
|
||||
<span>{{$t('detections.metric')}}:</span>
|
||||
<div class="line-select__operation">
|
||||
<el-select
|
||||
size="mini"
|
||||
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'
|
||||
}
|
||||
]
|
||||
@@ -326,7 +326,7 @@ export default {
|
||||
if (tabType) {
|
||||
const oldCurTab = this.getUrlParam(this.curTabState.networkOverviewBeforeTab, '')
|
||||
const curTable = networkTable.networkOverview
|
||||
const tableType = this.$route.params ? this.$route.params.typeName : 'networkOverview'
|
||||
const tableType = this.$route.path.replace('/panel/', '') || 'networkOverview'
|
||||
const metric = this.getUrlParam(this.curTabState.tableMetric, 'Bits/s')
|
||||
const list = await getUserDrilldownTableConfig(tableType, metric)
|
||||
const tabGroup = list.filter(item => item.label === tabType)
|
||||
@@ -493,11 +493,9 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
if (val) {
|
||||
if (!show) { // 非滚动滚动条操作,直接覆盖之前的数据
|
||||
this.providerOptions = res.data.list
|
||||
} else if (!val && !show) {
|
||||
this.providerOptions = res.data.list
|
||||
} else {
|
||||
} else { // 滚动条操作,则将新数据和之前的数据组合
|
||||
this.providerOptions.push(...res.data.list)
|
||||
this.appListData([], this.providerOptions)
|
||||
}
|
||||
@@ -519,11 +517,9 @@ export default {
|
||||
}
|
||||
})
|
||||
})
|
||||
if (val) {
|
||||
if (!show) { // 非滚动滚动条操作,直接覆盖之前的数据
|
||||
this.appOptions = res.data.list
|
||||
} else if (!val && !show) {
|
||||
this.appOptions = res.data.list
|
||||
} else {
|
||||
} else { // 滚动条操作,则将新数据和之前的数据组合
|
||||
this.appOptions.push(...res.data.list)
|
||||
this.appListData(this.appOptions, [])
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
startTime: this.timeFilter && this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : '',
|
||||
endTime: this.timeFilter && this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : ''
|
||||
}
|
||||
/*this.toggleLoading(true)
|
||||
/* this.toggleLoading(true)
|
||||
axios.get(api.netWorkOverview.ddosEventAnalysis, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
if (response.status === 200) {
|
||||
@@ -85,7 +85,7 @@ export default {
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})*/
|
||||
}) */
|
||||
this.toggleLoading(false)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -32,17 +32,20 @@
|
||||
</div>
|
||||
<div class="line-select line-header-right">
|
||||
<div class="line-select-reference-line">
|
||||
<span>{{ $t('network.referenceLine') }}:</span>
|
||||
<span>{{ $t('network.referenceLine') }} : </span>
|
||||
<div class="line-select__operation">
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="lineRefer"
|
||||
:disabled="!lineTab"
|
||||
placeholder=" "
|
||||
popper-class="common-select"
|
||||
:popper-append-to-body="false"
|
||||
@change="referenceSelectChange"
|
||||
>
|
||||
<el-option v-for="item in options2" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
<el-option :key="options2[0].value" :label="$t(options2[0].label)" :value="options2[0].value"></el-option>
|
||||
<el-option :key="options2[1].value" :label="$t(options2[1].label[0], options2[1].label[1])" :value="options2[1].value"></el-option>
|
||||
<el-option :key="options2[2].value" :label="$t(options2[2].label)" :value="options2[2].value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -192,6 +195,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)
|
||||
@@ -270,6 +276,14 @@ export default {
|
||||
label: {
|
||||
formatter (params) {
|
||||
const arr = valueToRangeValue(params.value, unitTypes.number).join('')
|
||||
const referIndex = _this.options2.findIndex(o => o.value === _this.lineRefer)
|
||||
if (referIndex > -1) {
|
||||
if (referIndex === 1) {
|
||||
return _this.$t(_this.options2[1].label[0], _this.options2[1].label[1]) + '(' + arr + echartsData[0].unitType + ')'
|
||||
} else {
|
||||
return _this.$t(_this.options2[referIndex].label) + '(' + arr + echartsData[0].unitType + ')'
|
||||
}
|
||||
}
|
||||
return _this.lineRefer + '(' + arr + echartsData[0].unitType + ')'
|
||||
},
|
||||
position: 'insideStartTop',
|
||||
@@ -464,8 +478,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 +491,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 +526,7 @@ export default {
|
||||
})
|
||||
this.tabs = tabs
|
||||
this.$nextTick(() => {
|
||||
this.lineRefer = 'Average'
|
||||
this.echartsInit(this.tabs, true)
|
||||
})
|
||||
} else {
|
||||
@@ -512,7 +544,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 +554,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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user