Compare commits

...

26 Commits

Author SHA1 Message Date
chenjinsong
cdd3557bbe CN-1388 fix: 调整实体列表等待交互和接口请求顺序等
1.调整接口请求顺序为list > 左侧筛选 > 顶部统计;
2.优化左侧筛选loading交互;
3.将默认每页条数改为10,可选条数由20,30,50改为10,20,50
2023-10-14 15:22:38 +08:00
chenjinsong
b44a99f354 CN-1372 fix: 曲线图步长优化后,恢复默认时间范围为1小时;修复短时间范围时横坐标显示不友好的问题 2023-10-12 17:05:05 +08:00
chenjinsong
76c0c081e5 CN-1373 fix: 去掉链路网格图整行或整列全没数据时会隐藏当前行列的逻辑 2023-10-11 18:03:14 +08:00
chenjinsong
6ac9b71b0e CN-1373 fix: 修复下一跳下钻后参数大小写变更的问题 2023-10-11 16:07:49 +08:00
chenjinsong
41f161e301 CN-1373 fix: 适配链路配置逻辑变更 2023-10-11 14:45:03 +08:00
chenjinsong
e926163da4 CN-1373 fix: 适配链路配置逻辑变更 2023-10-11 14:38:42 +08:00
chenjinsong
7b7c2456e2 fix: 删除部分无用路由 2023-10-10 15:36:57 +08:00
chenjinsong
4fb8ab2996 fix: 删除部分无用组件 2023-10-10 15:36:21 +08:00
chenjinsong
be6a1e9fb6 Merge remote-tracking branch 'origin/dev-23.09' into dev-23.09 2023-10-10 15:25:02 +08:00
chenjinsong
faae781f42 fix: 修复依赖问题 2023-10-10 15:24:53 +08:00
唐浩
216c6eaab2 Update .gitlab-ci.yml file 2023-10-10 06:45:25 +00:00
chenjinsong
f939d344fa fix: 修复依赖问题 2023-10-10 14:24:21 +08:00
chenjinsong
7b93068d75 feat: 适配Poc的变更:默认时间范围改为1小时、网络质量评分阈值变更 2023-10-10 10:57:35 +08:00
陈劲松
5dedc6d12d Merge branch 'cherry-pick-8cc6edbd' into 'dev-23.09'
fix: 实体搜索允许通过语言添加俄语

See merge request cyber-narrator/cn-ui!46
2023-10-10 02:50:55 +00:00
刘洪洪
d0cad48f6a fix: 实体搜索允许通过语言添加俄语
(cherry picked from commit 8cc6edbdd4)
2023-10-10 02:50:46 +00:00
陈劲松
1fa8f157ed Merge branch 'cherry-pick-6f72b225' into 'dev-23.09'
fix: 修复实体搜索内容包含非英文时会弹窗提示非法字符串的问题

See merge request cyber-narrator/cn-ui!45
2023-10-10 02:50:16 +00:00
刘洪洪
9a3e4e07ab fix: 修复实体搜索内容包含非英文时会弹窗提示非法字符串的问题
(cherry picked from commit 6f72b2258f)
2023-10-10 02:50:09 +00:00
陈劲松
817d593c78 Merge branch 'cherry-pick-cc61cbc0' into 'dev-23.09'
fix: 修复networkoverview line去掉尾部4个0值的代码导致测试用例不通过的问题

See merge request cyber-narrator/cn-ui!44
2023-10-07 02:39:59 +00:00
chenjinsong
ed8be53798 fix: 修复networkoverview line去掉尾部4个0值的代码导致测试用例不通过的问题
(cherry picked from commit cc61cbc05c)
2023-10-07 02:39:52 +00:00
陈劲松
d14f34ec9f Merge branch 'cherry-pick-7cc2439d' into 'dev-23.09'
fix: 去除无效打印

See merge request cyber-narrator/cn-ui!43
2023-10-07 02:39:33 +00:00
刘洪洪
6cb27b72af fix: 去除无效打印
(cherry picked from commit 7cc2439dd2)
2023-10-07 02:39:24 +00:00
陈劲松
4abe0ce74c Merge branch 'cherry-pick-8ba06205' into 'dev-23.09'
CN-1363 fix: 修复单引号内不能包含逗号的问题,以及语句中引号内包含逗号后添加连接has函数的逻辑

See merge request cyber-narrator/cn-ui!42
2023-10-07 02:38:35 +00:00
刘洪洪
272fd2a5d2 CN-1363 fix: 修复单引号内不能包含逗号的问题,以及语句中引号内包含逗号后添加连接has函数的逻辑
(cherry picked from commit 8ba062054c)
2023-10-07 02:38:15 +00:00
chenjinsong
fc56a1fc0c fix: networkoverview的曲线图去掉尾部最多4个0值点 2023-09-28 13:23:28 +08:00
chenjinsong
ccd2b3d08b fix: 修复实体列表流量速率显示为横杠的问题 2023-09-28 10:01:13 +08:00
chenjinsong
60bbb8b165 fix: 修复实体列表实体属性为空串时,不显示'-'的问题 2023-09-27 19:53:09 +08:00
30 changed files with 6128 additions and 22518 deletions

View File

@@ -39,9 +39,9 @@ build_project:
stage: build_project stage: build_project
script: script:
- echo "npm install ..." - echo "npm install ..."
- cnpm install --save-dev --unsafe-perm - npm install --save-dev --unsafe-perm
- echo "npm run build" - echo "npm run build"
- cnpm run build - npm run build
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME" name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
when: on_success when: on_success

27006
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,63 +10,64 @@
"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build" "analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
}, },
"dependencies": { "dependencies": {
"@amcharts/amcharts4": "^4.10.24", "@amcharts/amcharts4": "~4.10.0",
"@amcharts/amcharts4-geodata": "^4.1.20", "@amcharts/amcharts4-geodata": "^4.1.20",
"@antv/g6": "^4.8.17", "@antv/g6": "^4.8.17",
"axios": "^0.21.1", "axios": "^0.21.1",
"babel-plugin-lodash": "^3.3.4", "babel-plugin-lodash": "~3.3.0",
"codemirror": "^5.65.1", "codemirror": "^5.65.1",
"core-js": "^3.6.5", "core-js": "~3.31.0",
"dayjs": "^1.10.5", "dayjs": "^1.10.5",
"dexie": "^3.2.2", "dexie": "~3.2.0",
"echarts": "^5.1.1", "echarts": "^5.1.1",
"element-plus": "^1.0.2-beta.44", "element-plus": "~1.0.2-beta.71",
"lib-flexible": "^0.3.2", "lib-flexible": "^0.3.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"moment-timezone": "^0.5.33", "moment-timezone": "^0.5.33",
"node-sass": "^4.14.1", "node-sass": "~4.14.0",
"postcss-plugin-px2rem": "^0.8.1", "postcss-plugin-px2rem": "~0.8.1",
"postcss-px2rem-exclude": "0.0.6", "postcss-px2rem-exclude": "0.0.6",
"sass-loader": "^8.0.2", "sass-loader": "~8.0.2",
"sass-resources-loader": "^2.2.1", "sass-resources-loader": "~2.2.5",
"tiny-emitter": "^2.1.0", "tiny-emitter": "^2.1.0",
"vue": "^3.0.0", "vue": "~3.3.0",
"vue-grid-layout": "^3.0.0-beta1", "vue-grid-layout": "^3.0.0-beta1",
"vue-i18n": "^9.1.6", "vue-i18n": "~9.2.0",
"vue-router": "^4.0.8", "vue-router": "~4.2.0",
"vuex": "^4.0.1" "vuex": "~4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.1", "@babel/cli": "~7.22.0",
"@babel/core": "^7.11.4", "@babel/core": "~7.22.5",
"@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-class-properties": "~7.18.0",
"@babel/plugin-proposal-private-methods": "^7.12.1", "@babel/plugin-proposal-private-methods": "~7.18.0",
"@babel/plugin-transform-runtime": "^7.12.1", "@babel/plugin-transform-runtime": "~7.22.0",
"@babel/plugin-proposal-private-property-in-object": "^7.12.1", "@babel/plugin-proposal-private-property-in-object": "~7.21.0",
"@babel/preset-env": "^7.11.5", "@babel/preset-env": "~7.22.0",
"@babel/preset-typescript": "^7.10.4", "@babel/preset-typescript": "~7.22.0",
"@commitlint/cli": "^9.1.2", "@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.2", "@commitlint/config-conventional": "^9.1.2",
"@highlightjs/vue-plugin": "^2.0.1", "@highlightjs/vue-plugin": "^2.0.1",
"@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^6.0.0", "@rollup/plugin-typescript": "^6.0.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "~5.16.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "~14.4.0",
"@testing-library/vue": "^6.4.2", "@testing-library/vue": "~6.6.0",
"@types/jest": "^26.0.24", "@types/jest": "~26.0.0",
"@types/lodash": "^4.14.161", "@types/lodash": "^4.14.161",
"@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1", "@typescript-eslint/parser": "^3.10.1",
"@vue/babel-plugin-jsx": "^1.0.0", "@vue/babel-plugin-jsx": "~1.1.0",
"@vue/babel-preset-app": "^5.0.8", "@vue/babel-preset-app": "~5.0.0",
"@vue/cli-plugin-babel": "~4.5.0", "@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0", "@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0", "@vue/compiler-sfc": "~3.3.0",
"@vue/component-compiler-utils": "^3.2.0", "@vue/component-compiler-utils": "~3.3.0",
"@vue/test-utils": "^2.2.7", "@vue/test-utils": "~2.4.0",
"@vue/server-renderer": "~3.3.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^26.0.0", "babel-jest": "^26.0.0",
"compression-webpack-plugin": "^8.0.1", "compression-webpack-plugin": "^8.0.1",
@@ -77,11 +78,10 @@
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1", "eslint-plugin-promise": "^4.3.1",
"eslint-plugin-vue": "^7.7.0", "eslint-plugin-vue": "^7.7.0",
"jest": "^26.0.0", "jest": "~26.6.0",
"ts-jest": "^26.4.4", "ts-jest": "~26.5.0",
"uglifyjs-webpack-plugin": "^2.2.0", "uglifyjs-webpack-plugin": "^2.2.0",
"vue-jest": "^5.0.0-alpha.10", "vue-jest": "^5.0.0-alpha.10"
"vue3-ace-editor": "^2.0.2"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",

View File

@@ -32,7 +32,7 @@
width: 50px; width: 50px;
height: 100%; height: 100%;
margin-right: 8px; margin-right: 8px;
transform: rotate(-45deg) translate(-5px,-15px); transform: translate(0, -15px);
color: #353636; color: #353636;
font-size: 12px; font-size: 12px;
} }

View File

@@ -32,6 +32,7 @@
width: calc(100% - 30px); width: calc(100% - 30px);
margin: 0 10px 0 20px; margin: 0 10px 0 20px;
max-height: 265px; max-height: 265px;
min-height: 40px;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;

View File

@@ -21,7 +21,7 @@
.entity-list--list { .entity-list--list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: auto;
/*overflow: visible;/*overflow: auto;*/ /*overflow: visible;/*overflow: auto;*/
.cn-entity__shadow { .cn-entity__shadow {

View File

@@ -624,6 +624,11 @@ export default {
const parser = new Parser(this.columnList) const parser = new Parser(this.columnList)
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) { if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
q = decodeURI(q) 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 this.metaList = parser.parseStr(q).metaList
} }

View File

@@ -225,6 +225,11 @@ export default {
if (q) { if (q) {
if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) { if (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1) {
q = decodeURI(q) 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解析时不识别导致的语法错误 // 为避免地址栏任意输入导致全查询的q带QUERY解析时不识别导致的语法错误
// 如地址栏输入116.178.222.171此时的q很长刷新界面时需要把q里的116.178.222.171拿出来进行搜索 // 如地址栏输入116.178.222.171此时的q很长刷新界面时需要把q里的116.178.222.171拿出来进行搜索

View File

@@ -6,9 +6,10 @@ import { ElMessage } from 'element-plus'
import i18n from '@/i18n' import i18n from '@/i18n'
const strReg = { 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.'-_]$/, 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('] const operatorList = ['=', ' in ', ' IN ', ' like ', ' LIKE ', 'HAS(', 'has(']
@@ -527,7 +528,9 @@ export default class Parser {
} }
// 在单引号里,又在括号里,则遇到逗号、右括号就报错 // 在单引号里,又在括号里,则遇到逗号、右括号就报错
if (isInBracket && isInApostrophe) { 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)) errorList.push(new ParserError(j, errorTypes.syntaxError, errorDesc.syntaxError.unclosedApostrophe))
break break
} }
@@ -709,6 +712,13 @@ export default class Parser {
meta.column.label = token.value meta.column.label = token.value
meta.column.type = columnType.fullText 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 { } else {
errorList.push(new ParserError(token.end, errorTypes.syntaxError, errorDesc.syntaxError.unexpectedString)) errorList.push(new ParserError(token.end, errorTypes.syntaxError, errorDesc.syntaxError.unexpectedString))
break break

View File

@@ -58,13 +58,24 @@ export default {
*/ */
setup (props) { setup (props) {
const { query } = useRoute() const { query } = useRoute()
const pageSize = ref(defaultPageSize) // pageSize取值顺序1.url2.缓存3.pageObj4.默认值
const urlPageSize = parseInt(query.pageSize)
const cachePageSize = parseInt(localStorage.getItem(storageKey.pageSize + '-' + localStorage.getItem(storageKey.username) + '-' + props.tableId))
const pageObjPageSize = props.pageObj.pageSize
const pageSize = ref(urlPageSize || cachePageSize || pageObjPageSize || defaultPageSize)
const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1)) const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1))
return { return {
pageSize, pageSize,
currentPageNo currentPageNo
} }
}, },
mounted () {
if (this.postPageSizes && this.postPageSizes.length > 0) {
this.resetPageSizes()
}
this.currentPageNo = parseInt(this.currentPageNo)
this.current(this.currentPageNo)
},
data () { data () {
return { return {
// pageSize: defaultPageSize, // pageSize: defaultPageSize,
@@ -147,6 +158,7 @@ export default {
size (val) { size (val) {
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
// this.pageObj.pageNo = 1 // this.pageObj.pageNo = 1
this.$emit('scrollbarToTop')
this.$emit('pageSize', val) this.$emit('pageSize', val)
this.backgroundColor() this.backgroundColor()
@@ -163,11 +175,12 @@ export default {
wrap.scrollTop = 0 wrap.scrollTop = 0
} }
}) })
this.$emit('scrollbarToTop')
}) })
}, },
resetPageSizes: function () { resetPageSizes: function () {
if (this.postPageSizes) { if (this.postPageSizes) {
this.pageSizes = this.postPageSizes.map((item) => { this.pageSizes = this.postPageSizes.map(item => {
return { return {
label: item + this.$t('pageSize'), label: item + this.$t('pageSize'),
value: item value: item
@@ -183,20 +196,6 @@ export default {
// } // }
} }
}, },
mounted () {
if (this.postPageSizes && this.postPageSizes.length > 0) {
this.pageSize = this.postPageSizes[0]
this.resetPageSizes()
} else {
const pageSize = localStorage.getItem(storageKey.pageSize + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId)
if (pageSize != 'undefined' && pageSize != null) {
this.pageSize = parseInt(pageSize)
}
}
this.currentPageNo = parseInt(this.currentPageNo)
this.current(this.currentPageNo)
},
watch: { watch: {
postPageSizes: { postPageSizes: {
immediate: true, immediate: true,

View File

@@ -804,10 +804,10 @@ export default {
} }
}) })
} }
if (route === this.route) { /* if (route === this.route) {
this.refresh() this.refresh()
return return
} } */
if (route) { if (route) {
this.$router.push({ this.$router.push({
path: route, path: route,

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -92,11 +92,6 @@ const routes = [
path: '/i18n', path: '/i18n',
component: () => import('@/views/administration/I18n') component: () => import('@/views/administration/I18n')
}, },
{
name: 'Chart',
path: '/chart',
component: () => import('@/views/administration/Chart')
},
{ {
path: '/detectionsNew', path: '/detectionsNew',
component: () => import('@/views/detectionsNew/Index') component: () => import('@/views/detectionsNew/Index')

View File

@@ -802,22 +802,22 @@ export function computeScore (data) {
if (t === 'establishLatencyMs' || t === 'httpResponseLatency' || t === 'sslConLatency') { if (t === 'establishLatencyMs' || t === 'httpResponseLatency' || t === 'sslConLatency') {
if (!data[t] && data[t] !== 0) { if (!data[t] && data[t] !== 0) {
score = 1 score = 1
} else if (data[t] <= 50) { } else if (data[t] <= 100) {
score = 1 score = 1
} else if (data[t] > 200) { } else if (data[t] > 1000) {
score = 0 score = 0
} else { } else {
score = (data[t] - 200) / (50 - 200) score = (data[t] - 1000) / (100 - 1000)
} }
} else if (t === 'tcpLostlenPercent' || t === 'pktRetransPercent') { } else if (t === 'tcpLostlenPercent' || t === 'pktRetransPercent') {
if (!data[t] && data[t] !== 0) { if (!data[t] && data[t] !== 0) {
score = 1 score = 1
} else if (data[t] <= 0.01) { } else if (data[t] <= 0.001) {
score = 1 score = 1
} else if (data[t] > 0.05) { } else if (data[t] > 0.05) {
score = 0 score = 0
} else { } else {
score = (data[t] - 0.05) / (0.01 - 0.05) score = (data[t] - 0.05) / (0.001 - 0.05)
} }
} }
scoreArr.push(score * k) scoreArr.push(score * k)

View File

@@ -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>

View File

@@ -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>

View File

@@ -122,6 +122,7 @@ import { drillDownPanelTypeMapping, storageKey, unitTypes } from '@/utils/consta
import { getSecond } from '@/utils/date-util' import { getSecond } from '@/utils/date-util'
import ChartError from '@/components/common/Error' import ChartError from '@/components/common/Error'
import axios from 'axios' import axios from 'axios'
// import { analysis, nextHopAnalysis } from './test-data'
export default { export default {
name: 'LinkBlock', name: 'LinkBlock',
@@ -192,6 +193,8 @@ export default {
const res = [] const res = []
res[0] = response[0].data res[0] = response[0].data
res[1] = response[1].data res[1] = response[1].data
/*res[0] = analysis.data
res[1] = nextHopAnalysis.data*/
if (response[0].status === 200) { if (response[0].status === 200) {
this.showError1 = false this.showError1 = false
@@ -211,6 +214,9 @@ export default {
hit.outBandwidth = info.bandwidth hit.outBandwidth = info.bandwidth
} else if (info.direction === 'in') { } else if (info.direction === 'in') {
hit.inBandwidth = info.bandwidth hit.inBandwidth = info.bandwidth
} else if (info.direction === '2') {
hit.outBandwidth = info.bandwidth
hit.inBandwidth = info.bandwidth
} }
} else { } else {
const hit = { const hit = {
@@ -222,6 +228,9 @@ export default {
hit.outBandwidth = info.bandwidth hit.outBandwidth = info.bandwidth
} else if (info.direction === 'in') { } else if (info.direction === 'in') {
hit.inBandwidth = info.bandwidth hit.inBandwidth = info.bandwidth
} else if (info.direction === '2') {
hit.outBandwidth = info.bandwidth
hit.inBandwidth = info.bandwidth
} }
data.push(hit) data.push(hit)
} }
@@ -235,6 +244,9 @@ export default {
item.outLinkId = info.originalLinkId item.outLinkId = info.originalLinkId
} else if (info.direction === 'in') { } else if (info.direction === 'in') {
item.inLinkId = info.originalLinkId item.inLinkId = info.originalLinkId
} else if (info.direction === '2') {
item.outLinkId = info.originalLinkId
item.inLinkId = info.originalLinkId
} }
}) })
}) })
@@ -355,12 +367,12 @@ export default {
if (out < 0.0001 && out !== 0) { if (out < 0.0001 && out !== 0) {
outUsage = '< 0.01%' outUsage = '< 0.01%'
} else { } else {
outUsage = JSON.stringify(parseFloat((out * 100).toFixed(2))) outUsage = JSON.stringify(parseFloat(out * 100).toFixed(2))
} }
if (_in < 0.0001 && _in !== 0) { if (_in < 0.0001 && _in !== 0) {
inUsage = '< 0.01%' inUsage = '< 0.01%'
} else { } else {
inUsage = JSON.stringify(parseFloat((_in * 100).toFixed(2))) inUsage = JSON.stringify(parseFloat(_in * 100).toFixed(2))
} }
length = outUsage.length + inUsage.length length = outUsage.length + inUsage.length

View File

@@ -17,6 +17,7 @@ import PopoverContent from './LinkDirectionGrid/PopoverContent'
import { computeScore } from '@/utils/tools' import { computeScore } from '@/utils/tools'
import axios from 'axios' import axios from 'axios'
import _ from 'lodash' import _ from 'lodash'
// import { bigramAnalysis, bigramNextHopAnalysis } from './test-data'
export default { export default {
name: 'LinkDirectionGrid', name: 'LinkDirectionGrid',
@@ -74,6 +75,8 @@ export default {
const res = [] const res = []
res[0] = response[0].data res[0] = response[0].data
res[1] = response[1].data res[1] = response[1].data
/*res[0] = bigramAnalysis.data
res[1] = bigramNextHopAnalysis.data*/
if (response[0].status === 200) { if (response[0].status === 200) {
this.isLinkShowError = false this.isLinkShowError = false
// 链路流量数据 // 链路流量数据
@@ -101,6 +104,12 @@ export default {
} }
}) })
linkGridData.push({ linkId: link.linkId, out: outList }) linkGridData.push({ linkId: link.linkId, out: outList })
} else if (link.direction === '2') {
const outList = []
linkInfo.forEach(link1 => {
outList.push({ linkId: link1.linkId, noData: true })
})
linkGridData.push({ linkId: link.linkId, out: outList })
} }
}) })
@@ -121,8 +130,11 @@ export default {
d.scoreLow3 = d.score < 3 || d.score === '-' d.scoreLow3 = d.score < 3 || d.score === '-'
const xAxis = inLink.linkId.split('Hundredgige').pop() - 1 /*const xAxis = inLink.linkId.split('Hundredgige').pop() - 1
const yAxis = outLink.linkId.split('Hundredgige').pop() - 1 const yAxis = outLink.linkId.split('Hundredgige').pop() - 1*/
const xAxis = inLink.linkId.split('UFONE-RWP-GI-LANPHY-100G-').pop() - 1
const yAxis = outLink.linkId.split('UFONE-RWP-GI-LANPHY-100G-').pop() - 1
linkGridData[xAxis].out[yAxis] = { linkGridData[xAxis].out[yAxis] = {
noData: false, noData: false,
linkId: outLink.linkId, linkId: outLink.linkId,
@@ -137,11 +149,11 @@ export default {
}) })
// 一行如果无数据则删除该行默认10*10矩阵 // 一行如果无数据则删除该行默认10*10矩阵
const rowXIndex = 0 // const rowXIndex = 0
this.handleXRowNoData(linkGridData, rowXIndex) // this.handleXRowNoData(linkGridData, rowXIndex)
// 一列如果无数据则删除该列默认10*10矩阵 // 一列如果无数据则删除该列默认10*10矩阵
const rowYIndex = 0 // const rowYIndex = 0
this.handleYRowNoData(linkGridData, rowYIndex) // this.handleYRowNoData(linkGridData, rowYIndex)
this.isLinkNoData = linkGridData.length === 0 this.isLinkNoData = linkGridData.length === 0
this.linkGridData = linkGridData this.linkGridData = linkGridData
} }
@@ -166,9 +178,30 @@ export default {
this.isNextNoData = nextLinkData.length === 0 this.isNextNoData = nextLinkData.length === 0
if (!this.isNextNoData) { if (!this.isNextNoData) {
const inLinkDirections = []
const outLinkDirections = []
nextLinkData.forEach(l => {
if (inLinkDirections.indexOf(l.inLinkDirection) === -1) {
inLinkDirections.push(l.inLinkDirection)
}
if (outLinkDirections.indexOf(l.outLinkDirection) === -1) {
outLinkDirections.push(l.outLinkDirection)
}
})
inLinkDirections.sort()
outLinkDirections.sort()
// 链路下一跳数据 // 链路下一跳数据
let nextGridData = [] const nextGridData = inLinkDirections.map(inLink => ({ nextHop: inLink }))
const nextGridTemplate = [ nextGridData.forEach((link, index) => {
link.out = outLinkDirections.map((outLink, index1) => {
return {
coordinate: `${index}-${index1}`,
noData: true,
nextHop: outLink
}
})
})
/*const nextGridTemplate = [
{ linkId: 'Hundredgige2', nextHop: '太原', out: [] }, { linkId: 'Hundredgige2', nextHop: '太原', out: [] },
{ linkId: 'Hundredgige1', nextHop: '西安', out: [] }, { linkId: 'Hundredgige1', nextHop: '西安', out: [] },
{ linkId: 'Hundredgige4', nextHop: '西宁', out: [] } { linkId: 'Hundredgige4', nextHop: '西宁', out: [] }
@@ -181,11 +214,11 @@ export default {
link1.coordinate = `${link.linkId}-${link1.linkId}` link1.coordinate = `${link.linkId}-${link1.linkId}`
delete link1.out delete link1.out
}) })
}) })*/
nextLinkData.forEach(d => { nextLinkData.forEach(d => {
const inLink = linkInfo.find(l => l.nextHop === d.inLinkDirection && l.direction === 'in') const inLink = linkInfo.find(l => l.nextHop === d.inLinkDirection && (l.direction === 'in' || l.direction === '2'))
const outLink = linkInfo.find(l => l.nextHop === d.outLinkDirection && l.direction === 'out') const outLink = linkInfo.find(l => l.nextHop === d.outLinkDirection && (l.direction === 'out' || l.direction === '2'))
if (inLink && outLink) { if (inLink && outLink) {
// const data = nextGridData.find(g => g.linkId === inLink.linkId) // const data = nextGridData.find(g => g.linkId === inLink.linkId)
@@ -193,10 +226,10 @@ export default {
let outBandwidth = 0 let outBandwidth = 0
let inBandwidth = 0 let inBandwidth = 0
linkInfo.forEach((item) => { linkInfo.forEach((item) => {
if (item.nextHop === d.outLinkDirection && item.direction === 'out') { if (item.nextHop === d.outLinkDirection && (item.direction === 'out' || item.direction === '2')) {
outBandwidth += item.bandwidth outBandwidth += item.bandwidth
} }
if (item.nextHop === d.inLinkDirection && item.direction === 'in') { if (item.nextHop === d.inLinkDirection && (item.direction === 'in' || item.direction === '2')) {
inBandwidth += item.bandwidth inBandwidth += item.bandwidth
} }
}) })
@@ -214,15 +247,15 @@ export default {
d.scoreLow3 = d.score < 3 || d.score === '-' d.scoreLow3 = d.score < 3 || d.score === '-'
const xAxis = inLink.linkId const xAxis = inLinkDirections.indexOf(d.inLinkDirection)
const yAxis = outLink.linkId const yAxis = outLinkDirections.indexOf(d.outLinkDirection)
nextGridData.forEach((link, index) => { nextGridData.forEach((link, index) => {
link.out.forEach((link1, index1) => { link.out.forEach((link1, index1) => {
if (link1.coordinate === (xAxis + '-' + yAxis)) { if (link1.coordinate === (xAxis + '-' + yAxis)) {
nextGridData[index].out[index1] = { nextGridData[index].out[index1] = {
coordinate: link1.coordinate, coordinate: link1.coordinate,
noData: false, noData: false,
linkId: outLink.linkId, // linkId: outLink.linkId,
nextHop: outLink.nextHop, nextHop: outLink.nextHop,
outUsage: outUsage, outUsage: outUsage,
inUsage: inUsage, inUsage: inUsage,

View File

@@ -156,7 +156,7 @@ export default {
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
if (this.queryCondition) { if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ') const condition = this.queryCondition.split(' or ')
if (condition.length > 1) { if (condition.length > 1) {
params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -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) params.inParam = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1)
@@ -387,7 +387,7 @@ export default {
let num = 0 let num = 0
linkData.forEach(e => { linkData.forEach(e => {
e.unitType = type 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 e.show = false
num += 1 num += 1
} else { } else {
@@ -397,24 +397,24 @@ export default {
} }
} }
if (this.lineTab === e.class) { if (this.lineTab === e.class) {
if (parseFloat(e.analysis.avg) <= 0) { if (parseFloat(e.analysis.max) <= 0) {
this.lineTab = '' this.lineTab = ''
this.lineRefer = '' this.lineRefer = ''
this.init() // this.init()
} }
} }
}) })
this.tabs = linkData this.tabs = linkData
// 如果三者avg都为0时至少保证total显示 // 如果三者max都为0时至少保证total显示
const ingressObj = linkData.find(d => d.name === 'linkMonitor.ingress') const ingressObj = linkData.find(d => d.name === 'linkMonitor.ingress')
const egressObj = linkData.find(d => d.name === 'linkMonitor.egress') const egressObj = linkData.find(d => d.name === 'linkMonitor.egress')
let ingressAvg = 0 let ingressAvg = 0
let egressAvg = 0 let egressAvg = 0
if (ingressObj) { if (ingressObj) {
ingressAvg = parseFloat(ingressObj.analysis.avg) || 0 ingressAvg = parseFloat(ingressObj.analysis.max) || 0
} }
if (egressObj) { if (egressObj) {
egressAvg = parseFloat(egressObj.analysis.avg) || 0 egressAvg = parseFloat(egressObj.analysis.max) || 0
} }
if ((ingressAvg + egressAvg) === 0) { if ((ingressAvg + egressAvg) === 0) {
const totalObj = linkData.find(d => d.name === 'network.total') const totalObj = linkData.find(d => d.name === 'network.total')

View File

@@ -100,7 +100,7 @@ export default {
} }
let url = '' let url = ''
if (this.queryCondition) { if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ') const condition = this.queryCondition.split(' or ')
if (condition.length > 1) { if (condition.length > 1) {
if (n === 0) { if (n === 0) {
params.q = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1) params.q = condition.find(c => c.indexOf('common_in_link_id') > -1 || c.indexOf('in_link_direction') > -1)

View File

@@ -104,7 +104,7 @@ export default {
endTime: getSecond(this.timeFilter.endTime) endTime: getSecond(this.timeFilter.endTime)
} }
if (this.queryCondition) { if (this.queryCondition) {
const condition = this.queryCondition.toLowerCase().split(' or ') const condition = this.queryCondition.split(' or ')
if (condition.length > 1) { if (condition.length > 1) {
// params.outParam = true // params.outParam = true
params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -1) params.outParam = condition.find(c => c.indexOf('common_out_link_id') > -1 || c.indexOf('out_link_direction') > -1)

View File

@@ -464,8 +464,7 @@ export default {
newData.push(obj) newData.push(obj)
}) })
} }
if (data && data.length > 0) {
if (data !== undefined && data.length > 0) {
newData.forEach((item) => { newData.forEach((item) => {
item.type = getLineType(item.type) item.type = getLineType(item.type)
if (item.type === val) { if (item.type === val) {
@@ -478,6 +477,24 @@ export default {
}) })
} }
lineData.splice(0, 1) 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') { if (val === 'Sessions/s') {
const tabs = _.cloneDeep(this.tabsTemplate) const tabs = _.cloneDeep(this.tabsTemplate)
lineData.forEach((d, i) => { lineData.forEach((d, i) => {

View File

@@ -185,6 +185,7 @@ export const stackedLineChartOption = {
{ {
type: 'time', type: 'time',
splitNumber: 8, splitNumber: 8,
minInterval: 60000,
axisLabel: { axisLabel: {
formatter: xAxisTimeFormatter, formatter: xAxisTimeFormatter,
rich: xAxisTimeRich rich: xAxisTimeRich

View File

@@ -185,7 +185,6 @@ export default {
}, },
data () { data () {
return { return {
showList: false,
listMode: 'list', // entity列表的模式list|block listMode: 'list', // entity列表的模式list|block
entityAppTotal: '-', entityAppTotal: '-',
@@ -213,28 +212,36 @@ export default {
title: this.$t('entity.topCountries'), title: this.$t('entity.topCountries'),
topColumn: 'ip.country', topColumn: 'ip.country',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-city', icon: 'cn-icon cn-icon-city',
title: this.$t('entity.topCities'), title: this.$t('entity.topCities'),
topColumn: 'ip.city', topColumn: 'ip.city',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-as', icon: 'cn-icon cn-icon-as',
title: this.$t('entity.topASNs'), title: this.$t('entity.topASNs'),
topColumn: 'ip.asn', topColumn: 'ip.asn',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-operator', icon: 'cn-icon cn-icon-operator',
title: this.$t('entity.topISPs'), title: this.$t('entity.topISPs'),
topColumn: 'ip.isp', topColumn: 'ip.isp',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-open-port', icon: 'cn-icon cn-icon-open-port',
@@ -242,28 +249,36 @@ export default {
topColumn: 'ip.port', topColumn: 'ip.port',
topColumn1: 'ip.protocol', topColumn1: 'ip.protocol',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-FQDN', icon: 'cn-icon cn-icon-FQDN',
title: this.$t('entity.topFQDNCategories'), title: this.$t('entity.topFQDNCategories'),
topColumn: 'domain.category', topColumn: 'domain.category',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-category2', icon: 'cn-icon cn-icon-category2',
title: this.$t('entity.topAppCategories'), title: this.$t('entity.topAppCategories'),
topColumn: 'app.category', topColumn: 'app.category',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
}, },
{ {
icon: 'cn-icon cn-icon-tag1', icon: 'cn-icon cn-icon-tag1',
title: this.$t('entity.topTags'), title: this.$t('entity.topTags'),
topColumn: 'tag', topColumn: 'tag',
totalCount: 0, totalCount: 0,
data: [] data: [],
loading: false,
firstLoad: true
} }
], ],
listData: [], listData: [],
@@ -396,16 +411,17 @@ export default {
mode: mode, mode: mode,
range: this.timeFilter.dateRangeValue, range: this.timeFilter.dateRangeValue,
pageNo: this.pageObj.pageNo, pageNo: this.pageObj.pageNo,
pageSize: this.pageObj.pageSize pageSize: this.pageObj.pageSize,
showList: true
} }
}) })
this.showList = true // this.showList = true
// 跳转页面,则不执行搜索功能 // 跳转页面,则不执行搜索功能
return true return true
} }
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter }) this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter }) this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
// 延时一秒避免初始化时pageSize为20pageNo为1也会调用“搜索”的情况 // 延时一秒避免初始化时pageSize为20pageNo为1也会调用“搜索”的情况
@@ -489,8 +505,8 @@ export default {
/** 新版查询filter数据 */ /** 新版查询filter数据 */
queryFilterNew (params) { queryFilterNew (params) {
const queryParams = { const queryParams = {
// startTime: getSecond(params.startTime), startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime), endTime: getSecond(params.endTime),
resource: params.q || '' resource: params.q || ''
} }
this.loadingLeft = true this.loadingLeft = true
@@ -502,8 +518,48 @@ export default {
const aggDomain = axios.get(api.entity.entityList.aggDomain, { params: queryParams }) const aggDomain = axios.get(api.entity.entityList.aggDomain, { params: queryParams })
const aggAppCategory = axios.get(api.entity.entityList.aggAppCategory, { params: queryParams }) const aggAppCategory = axios.get(api.entity.entityList.aggAppCategory, { params: queryParams })
const aggTag = axios.get(api.entity.entityList.aggTag, { params: queryParams }) const aggTag = axios.get(api.entity.entityList.aggTag, { params: queryParams })
const requests = [aggCountry, aggCity, aggIPAsn, aggIPIsp, aggPort, aggDomain, aggAppCategory, aggTag]
Promise.all([aggCountry, aggCity, aggIPAsn, aggIPIsp, aggPort, aggDomain, aggAppCategory, aggTag]).then(response => { requests.forEach((req, index) => {
this.newFilterData[index].loading = true
req.then(response => {
if (response.status === 200 && response.data.data.list) {
if (response.data.data.list.length >= 5) {
this.newFilterData[index].showNum = 5
} else {
this.newFilterData[index].showNum = response.data.data.list.length
}
this.newFilterData[index].data = []
response.data.data.list.forEach((item, i) => {
let obj = {
label: item.value,
topColumn: this.newFilterData[index].topColumn,
value: item.uniqueEntities,
showNum: 5
}
if (index === 0) {
obj.flag = item.value // 接口字段名称为'China'svg名称为'CN'通过countryNameIdMapping进行转换
}
if (index === 4) {
obj = {
topColumn: this.newFilterData[index].topColumn,
topColumn1: this.newFilterData[index].topColumn1,
port: item.port,
l7Protocol: item.l7Protocol,
value: item.uniqueEntities,
showNum: 5
}
}
this.newFilterData[index].data.push(obj)
})
}
}).catch(e => {
this.$message.error(e.response.data.message)
}).finally(() => {
this.newFilterData[index].loading = false
this.newFilterData[index].firstLoad = false
})
})
/*Promise.all([aggCountry, aggCity, aggIPAsn, aggIPIsp, aggPort, aggDomain, aggAppCategory, aggTag]).then(response => {
response.forEach((item1, index) => { response.forEach((item1, index) => {
if (item1.status === 200 && item1.data.data.list) { if (item1.status === 200 && item1.data.data.list) {
if (item1.data.data.list.length >= 5) { if (item1.data.data.list.length >= 5) {
@@ -540,7 +596,7 @@ export default {
this.$message.error(e.response.data.message) this.$message.error(e.response.data.message)
}).finally(() => { }).finally(() => {
this.loadingLeft = false this.loadingLeft = false
}) })*/
}, },
/** 实体列表查询 */ /** 实体列表查询 */
queryList (params) { queryList (params) {
@@ -548,8 +604,8 @@ export default {
const queryParams = { const queryParams = {
pageSize: params.pageSize, pageSize: params.pageSize,
pageNo: params.pageNo, pageNo: params.pageNo,
// startTime: getSecond(params.startTime), startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime), endTime: getSecond(params.endTime),
resource: params.q || '' resource: params.q || ''
} }
axios.get(api.entity.entityList.list, { params: queryParams }).then(response => { axios.get(api.entity.entityList.list, { params: queryParams }).then(response => {
@@ -570,8 +626,8 @@ export default {
queryCount (params) { queryCount (params) {
this.loadingCount = true this.loadingCount = true
const queryParams = { const queryParams = {
// startTime: getSecond(params.startTime), startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime), endTime: getSecond(params.endTime),
resource: params.q || '' resource: params.q || ''
} }
axios.get(api.entity.entityList.summaryCount, { params: queryParams }).then(response => { axios.get(api.entity.entityList.summaryCount, { params: queryParams }).then(response => {
@@ -692,18 +748,25 @@ export default {
} }
}, },
mounted () { mounted () {
this.getEntityIndexData() if (!this.showList) {
let { q, listMode } = this.$route.query this.getEntityIndexData()
} else {
let { q, listMode } = this.$route.query
// 如果地址栏有listMode即列表页并非首页则开始搜索 // 如果地址栏有listMode即列表页并非首页则开始搜索
if (listMode) { if (listMode) {
this.showList = true // %位置为0是输入中文时能解码%2025%分别是空格和%的情况
// %位置为0是输入中文时能解码%2025%分别是空格和%的情况 if (q && (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1)) {
if (q && (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1)) { q = decodeURI(q)
q = decodeURI(q) }
// %位置不为0即内容包含非英文时
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
q = decodeURI(q)
}
this.initSearch(q)
this.listMode = listMode
} }
this.initSearch(q)
this.listMode = listMode
} }
}, },
watch: { watch: {
@@ -717,11 +780,12 @@ export default {
const rangeParam = query.range const rangeParam = query.range
const startTimeParam = query.startTime const startTimeParam = query.startTime
const endTimeParam = query.endTime const endTimeParam = query.endTime
const showList = ref(Boolean(query.showList))
// 若url携带了使用携带的值否则使用默认值。 // 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60 const dateRangeValue = rangeParam ? parseInt(query.range) : 60 * 24
const timeFilter = ref({ dateRangeValue }) const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) { if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(60) const { startTime, endTime } = getNowTime(60 * 24)
timeFilter.value.startTime = startTime timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime timeFilter.value.endTime = endTime
} else { } else {
@@ -732,12 +796,13 @@ export default {
pageNo: query.pageNo ? parseInt(query.pageNo) : 1, pageNo: query.pageNo ? parseInt(query.pageNo) : 1,
// 是否重置pageNo在执行新搜索时是true // 是否重置pageNo在执行新搜索时是true
resetPageNo: true, resetPageNo: true,
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize, pageSize: query.pageSize ? parseInt(query.pageSize) : 10, // TODO 23-10-14 默认暂时改为10
total: 0 total: 0
}) })
return { return {
timeFilter, timeFilter,
pageObj pageObj,
showList
} }
}, },
beforeUnmount () { beforeUnmount () {

View File

@@ -2,51 +2,49 @@
<div class="entity-filter-case" style="position: relative"> <div class="entity-filter-case" style="position: relative">
<div class="filter-case__header">{{ $t('entities.filter1') }}</div> <div class="filter-case__header">{{ $t('entities.filter1') }}</div>
<div v-if="filterDataLength>0"> <div v-if="filterDataLength > 0">
<div class="entity-filter" v-for="(item, index) in myFilterData" :key="index"> <div class="entity-filter" v-for="(item, index) in myFilterData" :key="index">
<div v-if="item.data.length>0"> <div class="filter__header">
<div class="filter__header"> <i :class="item.icon"></i>
<i :class="item.icon"></i> {{ item.title }}
{{ item.title }}
</div>
<div class="filter__body" style="position: relative">
<loading :loading="loadingLeft" style="top: -5px;"></loading>
<div class="filter__body-item"
v-for="(data, i) in item.data.slice(0, item.showNum)"
:key="i"
@click="filter(data.label, data)">
<div class="filter__body-item-left">
<div v-if="data.flag">
<img v-if="data.flag===countryNameIdMapping.Unknown || !countryNameIdMapping[data.flag]" src="../../../public/images/flag/Unknown.svg" class="filter-country-flag">
<img v-else :src="require(`../../../public/images/flag/${countryNameIdMapping[data.flag]}.png`)" class="filter-country-flag"/>
</div>
<div v-else class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">
<span v-if="item.topColumn==='ip.port'">
{{ data.port }}/{{ data.l7Protocol }}
</span>
<span v-else>{{ data.label }}</span>
</span>
</el-tooltip>
</div>
</div>
<div class="filter__body-item-right">{{ data.value }}</div>
</div>
</div>
<div @click="showMoreFilter(item, index)"
:class="item.showNum === item.data.length ? 'filter-no-show-more' : 'filter-show-more'">
{{ $t('entity.showMore') }}
</div>
<div class="filter-hr"></div>
</div> </div>
<div class="filter__body" style="position: relative">
<loading :loading="item.loading" style="top: -5px;"></loading>
<div class="filter__body-item"
v-for="(data, i) in item.data.slice(0, item.showNum)"
:key="i"
@click="filter(data.label, data)">
<div class="filter__body-item-left">
<div v-if="data.flag">
<img v-if="data.flag===countryNameIdMapping.Unknown || !countryNameIdMapping[data.flag]" src="../../../public/images/flag/Unknown.svg" class="filter-country-flag">
<img v-else :src="require(`../../../public/images/flag/${countryNameIdMapping[data.flag]}.png`)" class="filter-country-flag"/>
</div>
<div v-else class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">
<span v-if="item.topColumn==='ip.port'">
{{ data.port }}/{{ data.l7Protocol }}
</span>
<span v-else>{{ data.label }}</span>
</span>
</el-tooltip>
</div>
</div>
<div class="filter__body-item-right">{{ data.value }}</div>
</div>
</div>
<div @click="showMoreFilter(item, index)"
:class="item.showNum >= item.data.length || item.data.length <= 5 ? 'filter-no-show-more' : 'filter-show-more'">
{{ $t('entity.showMore') }}
</div>
<div class="filter-hr"></div>
</div> </div>
</div> </div>
<loading v-else-if="isFirstLoad" :loading="isFirstLoad"></loading>
<chart-no-data v-else style="padding-top: 40px"></chart-no-data> <chart-no-data v-else style="padding-top: 40px"></chart-no-data>
</div> </div>
</template> </template>
@@ -54,6 +52,7 @@
import Loading from '@/components/common/Loading' import Loading from '@/components/common/Loading'
import ChartNoData from '@/views/charts/charts/ChartNoData' import ChartNoData from '@/views/charts/charts/ChartNoData'
import { countryNameIdMapping } from '@/utils/constants' import { countryNameIdMapping } from '@/utils/constants'
import _ from 'lodash'
export default { export default {
name: 'EntityFilter', name: 'EntityFilter',
components: { ChartNoData, Loading }, components: { ChartNoData, Loading },
@@ -73,6 +72,9 @@ export default {
}) })
return length return length
},
isFirstLoad () {
return this.myFilterData.some(d => d.firstLoad)
} }
}, },
data () { data () {
@@ -85,6 +87,14 @@ export default {
mounted () { mounted () {
this.myFilterData = this.filterData this.myFilterData = this.filterData
}, },
watch: {
filterData: {
deep: true,
handler (n) {
this.myFilterData = _.cloneDeep(n)
}
}
},
methods: { methods: {
/** /**
* 判断文字是否溢出超出则鼠标移入tooltip显示否则鼠标移入不显示 * 判断文字是否溢出超出则鼠标移入tooltip显示否则鼠标移入不显示
@@ -103,17 +113,14 @@ export default {
this.$emit('filter', name, data) this.$emit('filter', name, data)
}, },
showMoreFilter (item, index) { showMoreFilter (item, index) {
if ((item.data.length - item.showNum) >= 5) { this.myFilterData[index].showNum = item.data.length
/*if ((item.data.length - item.showNum) >= 5) {
item.shouNum += 5 item.shouNum += 5
this.myFilterData[index].showNum += 5 this.myFilterData[index].showNum += 5
} else { } else {
this.myFilterData[index].showNum += (item.data.length - item.showNum) this.myFilterData[index].showNum += (item.data.length - item.showNum)
} }*/
} }
} }
} }
</script> </script>
<style lang="scss" scoped>
</style>

View File

@@ -38,11 +38,13 @@
<Pagination <Pagination
ref="pagination" ref="pagination"
:page-obj="pageObj" :page-obj="pageObj"
:post-page-sizes="[10, 20, 50]"
@pageNo='pageNo' @pageNo='pageNo'
@pageSize='pageSize' @pageSize='pageSize'
@size-change="pageSize" @size-change="pageSize"
@prev-click="prev" @prev-click="prev"
@next-click="next" @next-click="next"
@scrollbarToTop="scrollbarToTop"
> >
</Pagination> </Pagination>
</div> </div>
@@ -124,6 +126,10 @@ export default {
const container = document.getElementById('cnContainer') const container = document.getElementById('cnContainer')
container.scrollTop += e.deltaY / 2 container.scrollTop += e.deltaY / 2
} }
},
scrollbarToTop () {
const container = document.getElementById('cnContainer')
container.scrollTop = 0
} }
}, },
mounted () { mounted () {

View File

@@ -26,7 +26,7 @@
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-country"></i> <i class="cn-icon cn-icon-country"></i>
<span class="row-item-label">{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.location ? entityData.location.country : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'location.country', '-') || '-' }}</span>
</div> </div>
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-position"></i> <i class="cn-icon cn-icon-position"></i>
@@ -36,36 +36,36 @@
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-cloud"></i> <i class="cn-icon cn-icon-cloud"></i>
<span class="row-item-label">{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.asn ? entityData.asn.asn : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'asn.asn', '-') || '-' }}</span>
</div> </div>
</template> </template>
<template v-else-if="entityData.entityType === 'domain'"> <template v-else-if="entityData.entityType === 'domain'">
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-category-group"></i> <i class="cn-icon cn-icon-category-group"></i>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.categoryGroup : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'category.categoryGroup', '-') || '-' }}</span>
</div> </div>
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i> <i class="cn-icon cn-icon-sub-category"></i>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.categoryName : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'category.categoryName', '-') || '-' }}</span>
</div> </div>
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-credit-rating"></i> <i class="cn-icon cn-icon-credit-rating"></i>
<span class="row-item-label">{{ $t('entities.reputationLevel') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('entities.reputationLevel') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.reputationLevel : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'category.reputationLevel', '-') || '-' }}</span>
</div> </div>
</template> </template>
<template v-else-if="entityData.entityType === 'app'"> <template v-else-if="entityData.entityType === 'app'">
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-category2"></i> <i class="cn-icon cn-icon-category2"></i>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.appCategory : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'category.appCategory', '-') || '-' }}</span>
</div> </div>
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i> <i class="cn-icon cn-icon-sub-category"></i>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span> <span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.appSubcategory : '-' }}</span> <span class="row-item-value">{{ $_.get(entityData, 'category.appSubcategory', '-') || '-' }}</span>
</div> </div>
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-credit-rating"></i> <i class="cn-icon cn-icon-credit-rating"></i>
@@ -258,7 +258,7 @@ export default {
} }
axios.get(`${url}?resource=${this.entity.entityValue}`).then(response => { axios.get(`${url}?resource=${this.entity.entityValue}`).then(response => {
this.$nextTick(() => { this.$nextTick(() => {
this.entityData = { ...response.data.data, ...this.entity } this.entityData = { ...this.entityData, ...response.data.data, ...this.entity }
}) })
}) })
}, },

View File

@@ -1,4 +1,5 @@
const path = require('path') const path = require('path')
const webpack = require('webpack')
function resolve (dir) { function resolve (dir) {
return path.join(__dirname, dir) return path.join(__dirname, dir)
} }
@@ -15,6 +16,9 @@ module.exports = {
chainWebpack: (config) => { chainWebpack: (config) => {
config.resolve.alias // 路径别名 config.resolve.alias // 路径别名
.set('@', resolve('./src')) .set('@', resolve('./src'))
config.plugin('webpack.DefinePlugin').use(new webpack.DefinePlugin({
__INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false)
}))
}, },
lintOnSave: true, lintOnSave: true,
devServer: { devServer: {