CN-64 feat: 搜索框联动
This commit is contained in:
@@ -44,13 +44,16 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
username: 'admin',
|
username: '',
|
||||||
pin: 'Nezha2021'
|
pin: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['loginSuccess']),
|
...mapActions(['loginSuccess']),
|
||||||
login () {
|
login () {
|
||||||
|
if (!this.username || !this.pin) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!this.blockOperation.query) {
|
if (!this.blockOperation.query) {
|
||||||
this.blockOperation.query = true
|
this.blockOperation.query = true
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<slot name="operations"></slot>
|
<slot name="operations"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__body">
|
<div class="cn-chart__body" v-loading="loading">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__footer">
|
<div class="cn-chart__footer">
|
||||||
@@ -20,7 +20,10 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChartMap'
|
name: 'ChartMap',
|
||||||
|
props: {
|
||||||
|
loading: Boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<slot name="operations"></slot>
|
<slot name="operations"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__body">
|
<div class="cn-chart__body" v-loading="loading">
|
||||||
<el-table
|
<el-table
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
tooltip-effect="light"
|
tooltip-effect="light"
|
||||||
@@ -54,7 +54,8 @@ export default {
|
|||||||
name: 'ChartTable',
|
name: 'ChartTable',
|
||||||
props: {
|
props: {
|
||||||
tableColumns: Array,
|
tableColumns: Array,
|
||||||
tableData: Array
|
tableData: Array,
|
||||||
|
loading: Boolean
|
||||||
},
|
},
|
||||||
setup () {
|
setup () {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
<slot name="operations"></slot>
|
<slot name="operations"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__body" :class="{'pie-with-table': isPieWithTable}">
|
<div class="cn-chart__body" :class="{'pie-with-table': isPieWithTable}" v-loading="loading">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__footer" v-if="layout.indexOf(layoutConstant.FOOTER) > -1" :class="{'pie-with-table': isPieWithTable}">
|
<div class="cn-chart__footer" v-if="layout.indexOf(layoutConstant.FOOTER) > -1" :class="{'pie-with-table': isPieWithTable}" v-loading="loading">
|
||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,7 +23,8 @@ export default {
|
|||||||
name: 'EchartsFrame',
|
name: 'EchartsFrame',
|
||||||
props: {
|
props: {
|
||||||
layout: Array,
|
layout: Array,
|
||||||
chartInfo: Object
|
chartInfo: Object,
|
||||||
|
loading: Boolean
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,32 +4,54 @@
|
|||||||
<div class="filter__body">
|
<div class="filter__body">
|
||||||
<el-collapse v-model="active" class="filter__collapse">
|
<el-collapse v-model="active" class="filter__collapse">
|
||||||
<el-collapse-item
|
<el-collapse-item
|
||||||
v-for="(f, i) in filterData"
|
name="0"
|
||||||
:key="i"
|
|
||||||
:name="`${i}`"
|
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="collapse-header"><i :class="f.icon" class="collapse-header__icon"></i><span>{{f.title}}</span></div>
|
<div class="collapse-header"><i :class="topFilterData.icon" class="collapse-header__icon"></i><span>{{topFilterData.title}}</span></div>
|
||||||
</template>
|
</template>
|
||||||
<el-tree
|
<el-tree
|
||||||
:data="f.data"
|
:data="topFilterData.data"
|
||||||
:load="(node, resolve) => loadFilter(node, resolve, f)"
|
|
||||||
:node-key="f.key"
|
|
||||||
:props="{ isLeaf: 'leaf' }"
|
:props="{ isLeaf: 'leaf' }"
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
:lazy="i === 0"
|
:load="loadLevel2"
|
||||||
:ref="`tree-${i}`"
|
lazy
|
||||||
|
node-key="name"
|
||||||
|
ref="tree-0"
|
||||||
highlight-current
|
highlight-current
|
||||||
@node-click="(data, node, component) => nodeClick(data, node, component, i)"
|
@node-click="(data, node, component) => nodeClick(data, node, component, 0)"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<div class="filter-item">
|
<div class="filter-item">
|
||||||
<div :title="node.level === 1 ? data[f.key] : (node.level === 2 ? data[f.childrenKey] : '')">{{node.level === 1 ? data[f.key] : (node.level === 2 ? data[f.childrenKey] : '')}}</div>
|
<div>{{data.name}}</div>
|
||||||
<span>{{data.count}}</span>
|
<span>{{data.count}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
<div class="filter__more" :class="{'filter__more--disabled': f.hasnotMore}" @click="showMore(f.key, f.hasnotMore)">{{$t('overall.showMore')}}</div>
|
<div class="filter__more" :class="{'filter__more--disabled': topFilterData.hasnotMore}" @click="showMore(topFilterData)">{{$t('overall.showMore')}}</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item
|
||||||
|
name="1"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div class="collapse-header"><i :class="bottomFilterData.icon" class="collapse-header__icon"></i><span>{{bottomFilterData.title}}</span></div>
|
||||||
|
</template>
|
||||||
|
<el-tree
|
||||||
|
:data="bottomFilterData.data"
|
||||||
|
:props="{ isLeaf: 'leaf' }"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
node-key="name"
|
||||||
|
ref="tree-1"
|
||||||
|
highlight-current
|
||||||
|
@node-click="(data, node, component) => nodeClick(data, node, component, 1)"
|
||||||
|
>
|
||||||
|
<template #default="{ node, data }">
|
||||||
|
<div class="filter-item">
|
||||||
|
<div>{{data.name}}</div>
|
||||||
|
<span>{{data.count}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
<div class="filter__more" :class="{'filter__more--disabled': bottomFilterData.hasnotMore}" @click="showMore(bottomFilterData)">{{$t('overall.showMore')}}</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +62,8 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'LeftFilter',
|
name: 'LeftFilter',
|
||||||
props: {
|
props: {
|
||||||
filterData: Array
|
topFilterData: Object,
|
||||||
|
bottomFilterData: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -50,8 +73,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async loadLevel2 (node, resolve) {
|
||||||
|
resolve(await this.$parent.loadLevel2FilterData(node, this.topFilterData.column))
|
||||||
|
},
|
||||||
nodeClick (data, node, component, index) {
|
nodeClick (data, node, component, index) {
|
||||||
const currentData = index === 0 ? this.currentDataTop : this.currentDataBottom
|
const currentData = index === 0 ? this.currentDataTop : this.currentDataBottom
|
||||||
|
const column = index === 0 ? this.topFilterData.column : this.bottomFilterData.column
|
||||||
if (this.dataEqual(currentData, data)) {
|
if (this.dataEqual(currentData, data)) {
|
||||||
node.isCurrent = false
|
node.isCurrent = false
|
||||||
}
|
}
|
||||||
@@ -60,15 +87,14 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.currentDataBottom = this.$_.cloneDeep(data)
|
this.currentDataBottom = this.$_.cloneDeep(data)
|
||||||
}
|
}
|
||||||
this.$emit('select', data, node, index)
|
this.$emit('select', data, node, index, column)
|
||||||
},
|
},
|
||||||
showMore (key, hasnotMore) {
|
showMore (filterData) {
|
||||||
if (!hasnotMore) {
|
if (!filterData.hasnotMore) {
|
||||||
this.$emit('showMore', key)
|
this.$emit('showMore', filterData.column)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
loadFilter (node, resolve, f) {
|
loadFilter (node, resolve, f) {
|
||||||
this.$emit('loadFilter', node, resolve, f.filterType, f.childrenKey, f.key)
|
|
||||||
},
|
},
|
||||||
dataEqual (obj1, obj2) {
|
dataEqual (obj1, obj2) {
|
||||||
if (!obj1 || !obj2) {
|
if (!obj1 || !obj2) {
|
||||||
@@ -82,15 +108,6 @@ export default {
|
|||||||
})
|
})
|
||||||
return equal
|
return equal
|
||||||
}
|
}
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
filterData: {
|
|
||||||
deep: true,
|
|
||||||
immediate: true,
|
|
||||||
handler (n) {
|
|
||||||
this.active = ['0', '1']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -155,6 +172,7 @@ export default {
|
|||||||
}
|
}
|
||||||
.el-tree-node__content {
|
.el-tree-node__content {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.filter-item {
|
.filter-item {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import { get, post } from '@/utils/http'
|
import { get, post } from '@/utils/http'
|
||||||
import { sortByOrderNum } from '@/permission'
|
import { sortByOrderNum } from '@/permission'
|
||||||
import {storageKey} from "@/utils/constants";
|
import { storageKey } from '@/utils/constants'
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
// 系统相关
|
// 系统相关
|
||||||
@@ -15,12 +15,9 @@ export const api = {
|
|||||||
// 业务
|
// 业务
|
||||||
panel: '/visual/panel',
|
panel: '/visual/panel',
|
||||||
chart: '/visual/chart',
|
chart: '/visual/chart',
|
||||||
entityIpFilter: '/interface/entity/ip/filter',
|
entityList: '/interface/entity/list',
|
||||||
entityDomainFilter: '/interface/entity/domain/filter',
|
entityCount: '/interface/entity/total',
|
||||||
entityAppFilter: '/interface/entity/app/filter',
|
entityFilter: '/interface/entity/filter'
|
||||||
entityIpList: '/interface/entity/ip/list',
|
|
||||||
entityDomainList: '/interface/entity/domain/list',
|
|
||||||
entityAppList: '/interface/entity/app/list'
|
|
||||||
}
|
}
|
||||||
/* panel */
|
/* panel */
|
||||||
export async function getPanelList (params) {
|
export async function getPanelList (params) {
|
||||||
@@ -36,50 +33,17 @@ export async function getChartList (params) {
|
|||||||
export async function getChart (id) {
|
export async function getChart (id) {
|
||||||
return await getData(`${api.chart}/${id}`)
|
return await getData(`${api.chart}/${id}`)
|
||||||
}
|
}
|
||||||
|
/* entity列表 */
|
||||||
|
export async function getEntityList (params) {
|
||||||
|
return await getData(api.entityList, params, true)
|
||||||
|
}
|
||||||
|
/* entity总数 */
|
||||||
|
export async function getEntityCount (params) {
|
||||||
|
return await getData(api.entityCount, params)
|
||||||
|
}
|
||||||
/* ip类型entity过滤器数据 */
|
/* ip类型entity过滤器数据 */
|
||||||
export async function getEntityIpFilterList (params) {
|
export async function getEntityFilter (params) {
|
||||||
return await getData(api.entityIpFilter, params, true)
|
return await getData(api.entityFilter, params, true)
|
||||||
}
|
|
||||||
/* domain类型entity过滤器数据 */
|
|
||||||
export async function getEntityDomainFilterList (params) {
|
|
||||||
return await getData(api.entityDomainFilter, params, true)
|
|
||||||
}
|
|
||||||
/* app类型entity过滤器数据 */
|
|
||||||
export async function getEntityAppFilterList (params) {
|
|
||||||
return await getData(api.entityAppFilter, params, true)
|
|
||||||
}
|
|
||||||
/* ip类型entity列表 */
|
|
||||||
export async function getEntityIpList (params) {
|
|
||||||
const request = new Promise(resolve => {
|
|
||||||
get(api.entityIpList, params).then(response => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
resolve(response)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return await request
|
|
||||||
}
|
|
||||||
/* domain类型entity列表 */
|
|
||||||
export async function getEntityDomainList (params) {
|
|
||||||
const request = new Promise(resolve => {
|
|
||||||
get(api.entityDomainList, params).then(response => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
resolve(response)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return await request
|
|
||||||
}
|
|
||||||
/* app类型entity列表 */
|
|
||||||
export async function getEntityAppList (params) {
|
|
||||||
const request = new Promise(resolve => {
|
|
||||||
get(api.entityAppList, params).then(response => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
resolve(response)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return await request
|
|
||||||
}
|
}
|
||||||
/* 字典 */
|
/* 字典 */
|
||||||
export async function getDictList (params) {
|
export async function getDictList (params) {
|
||||||
@@ -87,12 +51,20 @@ export async function getDictList (params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getData (url, params = {}, isQueryList) {
|
export async function getData (url, params = {}, isQueryList) {
|
||||||
const request = new Promise(resolve => {
|
const request = new Promise((resolve, reject) => {
|
||||||
get(url, params).then(response => {
|
try {
|
||||||
if (response.code === 200) {
|
get(url, params).then(response => {
|
||||||
resolve(isQueryList ? response.data.list || response.data.result : response.data || response.data.result)
|
if (response.code === 200) {
|
||||||
}
|
resolve(isQueryList ? response.data.list || response.data.result : response.data.result || response.data)
|
||||||
})
|
} else {
|
||||||
|
reject(response)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
}).catch(response => {
|
||||||
|
console.error(response)
|
||||||
})
|
})
|
||||||
return await request
|
return await request
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,22 @@ export const entityType = {
|
|||||||
app: 'APP'
|
app: 'APP'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const entityTypeMappingKey = {
|
export const entityFilterType = {
|
||||||
ip: ['country', 'region']
|
ip: {
|
||||||
|
country: 'locate_country',
|
||||||
|
region: 'locate_region',
|
||||||
|
asn: 'asn'
|
||||||
|
},
|
||||||
|
domain: {
|
||||||
|
categoryGroup: 'category_group',
|
||||||
|
categoryName: 'category_name',
|
||||||
|
reputationLevel: 'reputation_level'
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
appCategory: 'app_category',
|
||||||
|
appSubcategory: 'app_subcategory',
|
||||||
|
appRisk: 'app_risk'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const unitTypes = {
|
export const unitTypes = {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
<chart-map
|
<chart-map
|
||||||
v-else-if="isMap"
|
v-else-if="isMap"
|
||||||
:style="computePosition"
|
:style="computePosition"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
|
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
|
||||||
<template #operations>
|
<template #operations>
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
:layout="layout"
|
:layout="layout"
|
||||||
:style="computePosition"
|
:style="computePosition"
|
||||||
:chartInfo="chartInfo"
|
:chartInfo="chartInfo"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #title v-if="layout.indexOf(layoutConstant.HEADER) > -1">
|
<template #title v-if="layout.indexOf(layoutConstant.HEADER) > -1">
|
||||||
{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}
|
{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}
|
||||||
@@ -51,12 +53,12 @@
|
|||||||
<template #operations v-if="layout.indexOf(layoutConstant.HEADER) > -1">
|
<template #operations v-if="layout.indexOf(layoutConstant.HEADER) > -1">
|
||||||
<div class="header__operation header__operation--echarts" v-if="chart.type === 31">
|
<div class="header__operation header__operation--echarts" v-if="chart.type === 31">
|
||||||
<el-select
|
<el-select
|
||||||
size="mini"
|
size="mini"
|
||||||
v-model="orderPieTable"
|
v-model="orderPieTable"
|
||||||
class="option__select select-column"
|
class="option__select select-column"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
popper-class="option-popper"
|
popper-class="option-popper"
|
||||||
@change="orderPieTableChange"
|
@change="orderPieTableChange"
|
||||||
>
|
>
|
||||||
<el-option v-for="item in chartPieTableTopOptions" :key="item.value" :value="item.value"> {{item.name}}</el-option>
|
<el-option v-for="item in chartPieTableTopOptions" :key="item.value" :value="item.value"> {{item.name}}</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -82,9 +84,8 @@
|
|||||||
:type="chartInfo.type"
|
:type="chartInfo.type"
|
||||||
:style="computePosition"
|
:style="computePosition"
|
||||||
:icon="singleValue.icon"
|
:icon="singleValue.icon"
|
||||||
:father="father"
|
v-loading="loading"
|
||||||
>
|
>
|
||||||
<div v-for="(item, index) in singleValue" :key="index"> {{item.result}}</div>
|
|
||||||
<template #title><span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span></template>
|
<template #title><span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span></template>
|
||||||
<template #data>
|
<template #data>
|
||||||
<span>{{handleSingleValue[0]}}</span>
|
<span>{{handleSingleValue[0]}}</span>
|
||||||
@@ -107,6 +108,7 @@
|
|||||||
:table-columns="table.tableColumns"
|
:table-columns="table.tableColumns"
|
||||||
:table-data="table.currentPageData"
|
:table-data="table.currentPageData"
|
||||||
:style="computePosition"
|
:style="computePosition"
|
||||||
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
|
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
|
||||||
<template #operations>
|
<template #operations>
|
||||||
@@ -224,7 +226,6 @@ export default {
|
|||||||
currentPageData: [] // table当前页的数据
|
currentPageData: [] // table当前页的数据
|
||||||
},
|
},
|
||||||
pieTableData: [],
|
pieTableData: [],
|
||||||
father: [],
|
|
||||||
singleValue: {
|
singleValue: {
|
||||||
value: '-',
|
value: '-',
|
||||||
icon: ''
|
icon: ''
|
||||||
@@ -234,11 +235,13 @@ export default {
|
|||||||
orderPieTable: chartPieTableTopOptions[0].value,
|
orderPieTable: chartPieTableTopOptions[0].value,
|
||||||
selectPieChartName: '',
|
selectPieChartName: '',
|
||||||
allSelectPieChartName: [],
|
allSelectPieChartName: [],
|
||||||
chartOption: null
|
chartOption: null,
|
||||||
|
loading: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initChart () {
|
initChart () {
|
||||||
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
const chartParams = this.chartInfo.params
|
const chartParams = this.chartInfo.params
|
||||||
if (this.isMap) {
|
if (this.isMap) {
|
||||||
@@ -271,44 +274,46 @@ export default {
|
|||||||
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
this.singleValue.value = response.data.result
|
this.singleValue.value = response.data.result
|
||||||
this.father = response.data.result
|
|
||||||
}
|
}
|
||||||
})
|
if (this.isSingleValueWithEcharts) { // 带曲线的单值图
|
||||||
if (this.isSingleValueWithEcharts) { // 带曲线的单值图
|
const dom = document.getElementById(`chart${this.chartInfo.id}`)
|
||||||
const dom = document.getElementById(`chart${this.chartInfo.id}`)
|
!this.myChart && (this.myChart = echarts.init(dom))
|
||||||
!this.myChart && (this.myChart = echarts.init(dom))
|
this.chartOption = this.$_.cloneDeep(getOption(this.chart.type))
|
||||||
this.chartOption = this.$_.cloneDeep(getOption(this.chart.type))
|
const seriesTemplate = this.chartOption.series[0]
|
||||||
const seriesTemplate = this.chartOption.series[0]
|
|
||||||
|
|
||||||
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000) }
|
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000) }
|
||||||
get(replaceUrlPlaceholder(chartParams.urlLine, queryParams)).then(response => {
|
get(replaceUrlPlaceholder(chartParams.urlLine, queryParams)).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
response.data.result = [
|
response.data.result = [
|
||||||
{
|
{
|
||||||
legend: "session_rate",
|
legend: "session_rate",
|
||||||
values:[
|
values:[
|
||||||
["1625122200","2"],["1625122500","2"],["1625122800","1"],["1625123100","1"],["1625123400","2"],["1625123700","2"],["1625124000","2"],["1625124300","3"],["1625124600","3"],["1625124900","3"]
|
["1625122200","2"],["1625122500","2"],["1625122800","1"],["1625123100","1"],["1625123400","2"],["1625123700","2"],["1625124000","2"],["1625124300","3"],["1625124600","3"],["1625124900","3"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
this.chartOption.series = response.data.result.map((r, i) => {
|
this.chartOption.series = response.data.result.map((r, i) => {
|
||||||
return {
|
return {
|
||||||
...seriesTemplate,
|
...seriesTemplate,
|
||||||
name: r.legend,
|
name: r.legend,
|
||||||
data: r.values.map(v => [Number(v[0]) * 1000, Number(v[1]), chartParams.unitType]),
|
data: r.values.map(v => [Number(v[0]) * 1000, Number(v[1]), chartParams.unitType]),
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color: getChartColor[i]
|
color: getChartColor[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
this.myChart.setOption(this.chartOption)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.myChart.resize()
|
||||||
})
|
})
|
||||||
}
|
setTimeout(() => { this.loading = false }, 250)
|
||||||
this.myChart.setOption(this.chartOption)
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.myChart.resize()
|
|
||||||
})
|
})
|
||||||
})
|
} else {
|
||||||
}
|
this.loading = false
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if (this.isTabs) {
|
} else if (this.isTabs) {
|
||||||
if (!this.$_.isEmpty(this.chartInfo.children)) {
|
if (!this.$_.isEmpty(this.chartInfo.children)) {
|
||||||
@@ -466,6 +471,7 @@ export default {
|
|||||||
polygonTemplate.strokeWidth = 0.5
|
polygonTemplate.strokeWidth = 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setTimeout(() => { this.loading = false }, 250)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
pageJump (val) {
|
pageJump (val) {
|
||||||
@@ -527,6 +533,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.myChart.resize()
|
this.myChart.resize()
|
||||||
})
|
})
|
||||||
|
setTimeout(() => { this.loading = false }, 250)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
initEchartsWithStatistics (chartParams) {
|
initEchartsWithStatistics (chartParams) {
|
||||||
@@ -555,6 +562,7 @@ export default {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.myChart.resize()
|
this.myChart.resize()
|
||||||
})
|
})
|
||||||
|
setTimeout(() => { this.loading = false }, 250)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
initEchartsWithPieTable (chartParams) {
|
initEchartsWithPieTable (chartParams) {
|
||||||
@@ -589,7 +597,10 @@ export default {
|
|||||||
if (response2.code === 200) {
|
if (response2.code === 200) {
|
||||||
this.pieTableData = response2.data.result
|
this.pieTableData = response2.data.result
|
||||||
}
|
}
|
||||||
|
this.loading = false
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
setTimeout(() => { this.loading = false }, 250)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -652,6 +663,7 @@ export default {
|
|||||||
this.table.tableColumns = this.getTableTitle(response.data.result)
|
this.table.tableColumns = this.getTableTitle(response.data.result)
|
||||||
this.table.currentPageData = this.getTargetPageData(1, this.table.pageSize, this.table.tableData)
|
this.table.currentPageData = this.getTargetPageData(1, this.table.pageSize, this.table.tableData)
|
||||||
}
|
}
|
||||||
|
this.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
<div class="outer-box" v-if="!showDetail">
|
<div class="outer-box" v-if="!showDetail">
|
||||||
<el-select
|
<el-select
|
||||||
size="small"
|
size="small"
|
||||||
v-model="filterType"
|
v-model="from"
|
||||||
style="position: fixed; top: 9px; width: 130px; left:260px;"
|
style="position: fixed; top: 9px; width: 130px; left:260px;"
|
||||||
>
|
>
|
||||||
<el-option v-for="(value, key) in entityType" :key="key" :label="value" :value="key"></el-option>
|
<el-option v-for="(value, key) in entityType" :key="key" :label="value" :value="key"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<div class="cn-entities">
|
<div class="cn-entities">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchContent"
|
v-model="searchContentTemp"
|
||||||
style="width: 100%; grid-area: 1 / 1 / 1 / 3;"
|
style="width: 100%; grid-area: 1 / 1 / 1 / 3;"
|
||||||
type="text"
|
type="text"
|
||||||
|
@keyup.enter="enter"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<span style="padding-left: 4px"><i class="el-icon-search"></i></span>
|
<span style="padding-left: 4px"><i class="el-icon-search"></i></span>
|
||||||
@@ -19,7 +20,8 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
<!-- 筛选区域 -->
|
<!-- 筛选区域 -->
|
||||||
<left-filter
|
<left-filter
|
||||||
:filter-data="filterData"
|
:top-filter-data="topFilterData"
|
||||||
|
:bottom-filter-data="bottomFilterData"
|
||||||
@select="select"
|
@select="select"
|
||||||
@showMore="showMore"
|
@showMore="showMore"
|
||||||
@loadFilter="loadFilter"
|
@loadFilter="loadFilter"
|
||||||
@@ -28,7 +30,7 @@
|
|||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<entity-list
|
<entity-list
|
||||||
:list-data="listData"
|
:list-data="listData"
|
||||||
:entity-type="filterType"
|
:entity-type="from"
|
||||||
:page-obj="pageObjRight"
|
:page-obj="pageObjRight"
|
||||||
@showDetail="entityDetail"
|
@showDetail="entityDetail"
|
||||||
@pageSize="pageSizeRight"
|
@pageSize="pageSizeRight"
|
||||||
@@ -41,18 +43,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { entityType } from '@/utils/constants'
|
import { entityType, entityFilterType } from '@/utils/constants'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import LeftFilter from '@/components/entities/LeftFilter'
|
import LeftFilter from '@/components/entities/LeftFilter'
|
||||||
import EntityList from '@/components/entities/EntityList'
|
import EntityList from '@/components/entities/EntityList'
|
||||||
import { getEntityIpFilterList, getEntityDomainFilterList, getEntityAppFilterList, getEntityIpList, getEntityDomainList, getEntityAppList } from '@/utils/api'
|
import { getEntityFilter, getEntityList, getEntityCount } from '@/utils/api'
|
||||||
import Panel from '@/views/charts/Panel'
|
import Panel from '@/views/charts/Panel'
|
||||||
export default {
|
export default {
|
||||||
name: 'EntityExplorer',
|
name: 'EntityExplorer',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
searchContent: '',
|
searchContentTemp: '', // 搜索框内的文本内容,按回车键后为searchContent赋值
|
||||||
filterData: [],
|
searchContent: '', // 查询语句
|
||||||
|
searchParams: null, // 搜索参数,格式为[{ name: xxx, value: xxx }, ...]
|
||||||
|
filterObj: {}, // 被选中的左侧过滤条件
|
||||||
|
topFilterData: {}, // 左侧上方的过滤列表数据
|
||||||
|
bottomFilterData: {}, // 左侧下方的过滤列表数据
|
||||||
|
listData: [], // 右侧实体列表数据
|
||||||
pageObjLeftTop: {
|
pageObjLeftTop: {
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@@ -68,7 +75,6 @@ export default {
|
|||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
total: 0
|
total: 0
|
||||||
},
|
},
|
||||||
listData: [],
|
|
||||||
showDetail: false,
|
showDetail: false,
|
||||||
typeName: '',
|
typeName: '',
|
||||||
currentEntity: {},
|
currentEntity: {},
|
||||||
@@ -81,175 +87,56 @@ export default {
|
|||||||
'cn-panel': Panel
|
'cn-panel': Panel
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async showMore (key) {
|
enter () {
|
||||||
|
if (!this.searchContentTemp) {
|
||||||
|
this.reset()
|
||||||
|
} else {
|
||||||
|
this.searchContent = this.searchContentTemp
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async showMore (column) {
|
||||||
let index = 0
|
let index = 0
|
||||||
switch (key) {
|
switch (column) {
|
||||||
case 'country':
|
case entityFilterType.ip.country:
|
||||||
case 'categoryGroup':
|
case entityFilterType.domain.categoryGroup:
|
||||||
case 'appCategory': {
|
case entityFilterType.app.appCategory: {
|
||||||
this.pageObjLeftTop.pageNo++
|
this.pageObjLeftTop.pageNo++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'asn':
|
case entityFilterType.ip.asn:
|
||||||
case 'reputationLevel':
|
case entityFilterType.domain.reputationLevel:
|
||||||
case 'appRisk': {
|
case entityFilterType.app.appRisk: {
|
||||||
this.pageObjLeftBottom.pageNo++
|
|
||||||
index = 1
|
index = 1
|
||||||
|
this.pageObjLeftBottom.pageNo++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
const data = await this.loadFilterData(this.filterType, key)
|
const { topFilterData, bottomFilterData } = await this.queryFilterData({ column, q: this.searchContent, from: this.from })
|
||||||
this.filterData[index].data = this.$_.concat(this.filterData[index].data, data)
|
if (index === 1) {
|
||||||
this.filterData[index].hasnotMore = this.$_.isEmpty(data) || data.length < 10
|
this.bottomFilterData = {
|
||||||
},
|
...this.bottomFilterData,
|
||||||
async loadFilterData (filterType, key) {
|
data: this.$_.concat(this.bottomFilterData.data, bottomFilterData),
|
||||||
let data
|
hasnotMore: bottomFilterData.length < 10
|
||||||
let params = { type: key }
|
|
||||||
switch (key) {
|
|
||||||
case 'country':
|
|
||||||
case 'categoryGroup':
|
|
||||||
case 'appCategory': {
|
|
||||||
params = { ...params, ...this.pageObjLeftTop }
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
case 'asn':
|
|
||||||
case 'reputationLevel':
|
|
||||||
case 'appRisk': {
|
|
||||||
params = { ...params, ...this.pageObjLeftBottom }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
switch (filterType) {
|
|
||||||
case 'ip': {
|
|
||||||
data = await getEntityIpFilterList(params)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'domain': {
|
|
||||||
data = await getEntityDomainFilterList(params)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'app': {
|
|
||||||
data = await getEntityAppFilterList(params)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
handleData (n) {
|
|
||||||
this.listData = []
|
|
||||||
this.filterData = []
|
|
||||||
const requests = []
|
|
||||||
const data = []
|
|
||||||
switch (n) {
|
|
||||||
case 'ip': {
|
|
||||||
requests.push(this.loadFilterData(n, 'country'))
|
|
||||||
requests.push(this.loadFilterData(n, 'asn'))
|
|
||||||
data.push({ filterType: n, title: this.$t('entities.countryOrRegion'), icon: 'cn-icon cn-icon-country', key: 'country', childrenKey: 'region', type: 'country', childrenType: 'region' })
|
|
||||||
data.push({ filterType: n, title: this.$t('entities.asn'), icon: 'cn-icon cn-icon-cloud', key: 'asn', type: 'asn' })
|
|
||||||
getEntityIpList({ ...this.pageObjRight }).then(res => {
|
|
||||||
this.listData = res.data.result
|
|
||||||
this.pageObjRight = { ...this.pageObjRight, total: res.statistics.result_size }
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'domain': {
|
|
||||||
requests.push(this.loadFilterData(n, 'categoryGroup'))
|
|
||||||
requests.push(this.loadFilterData(n, 'reputationLevel'))
|
|
||||||
data.push({ filterType: n, title: this.$t('entities.groupAndName'), icon: 'cn-icon cn-icon-category', key: 'categoryGroup', childrenKey: 'categoryName' })
|
|
||||||
data.push({ filterType: n, title: this.$t('entities.creditLevel'), icon: 'cn-icon cn-icon-risk', key: 'reputationLevel' })
|
|
||||||
getEntityDomainList({ ...this.pageObjRight }).then(res => {
|
|
||||||
this.listData = res.data.result
|
|
||||||
this.pageObjRight = { ...this.pageObjRight, total: res.statistics.result_size }
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'app': {
|
|
||||||
requests.push(this.loadFilterData(n, 'appCategory'))
|
|
||||||
requests.push(this.loadFilterData(n, 'appRisk'))
|
|
||||||
data.push({ filterType: n, title: this.$t('entities.categoryAndSub'), icon: 'cn-icon cn-icon-category', key: 'appCategory', childrenKey: 'appSubcategory' })
|
|
||||||
data.push({ filterType: n, title: this.$t('entities.riskLevel'), icon: 'cn-icon cn-icon-risk', key: 'appRisk' })
|
|
||||||
getEntityAppList({ ...this.pageObjRight }).then(res => {
|
|
||||||
this.listData = res.data.result
|
|
||||||
this.pageObjRight = { ...this.pageObjRight, total: res.statistics.result_size }
|
|
||||||
})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
Promise.all(requests).then(responses => {
|
|
||||||
data.forEach((d, i) => {
|
|
||||||
d.data = responses[i]
|
|
||||||
d.hasnotMore = this.$_.isEmpty(responses[i]) || responses[i].length < 10
|
|
||||||
})
|
|
||||||
this.filterData = data
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async select (data, node, index) {
|
|
||||||
const pageObjRight = { ...this.pageObjRight, ...data }
|
|
||||||
this.$_.forIn(data, (value, key) => {
|
|
||||||
if (value === this.pageObjRight[key]) {
|
|
||||||
delete pageObjRight[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.pageObjRight = JSON.parse(JSON.stringify(pageObjRight))
|
|
||||||
const res = await loadList(node, this.filterType, this.pageObjRight)
|
|
||||||
this.listData = res.data.result
|
|
||||||
},
|
|
||||||
getEntityData (param) {
|
|
||||||
|
|
||||||
},
|
|
||||||
pageSizeRight (val) {
|
|
||||||
this.pageObjRight.pageSize = val
|
|
||||||
this.handleData(this.filterType)
|
|
||||||
},
|
|
||||||
pageNoRight (val) {
|
|
||||||
this.pageObjRight.pageNo = val
|
|
||||||
this.handleData(this.filterType)
|
|
||||||
},
|
|
||||||
loadFilter (node, resolve, filterType, key, parentKey) {
|
|
||||||
if (node.level === 0) {
|
|
||||||
resolve(node.data)
|
|
||||||
} else {
|
} else {
|
||||||
const param = { type: key }
|
this.topFilterData = {
|
||||||
param[parentKey] = (node.data)[parentKey]
|
...this.topFilterData,
|
||||||
let req = null
|
data: this.$_.concat(this.topFilterData.data, topFilterData),
|
||||||
switch (filterType) {
|
hasnotMore: topFilterData.length < 10
|
||||||
case 'ip': {
|
|
||||||
req = getEntityIpFilterList(param)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'domain': {
|
|
||||||
req = getEntityDomainFilterList(param)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'app': {
|
|
||||||
req = getEntityAppFilterList(param)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
if (req !== null) {
|
|
||||||
req.then(res => {
|
|
||||||
res = res.map(r => {
|
|
||||||
return { ...r, leaf: true }
|
|
||||||
})
|
|
||||||
resolve(res)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
entityDetail (entity, tabs) {
|
async search () {
|
||||||
this.typeName = `${this.filterType.toLowerCase()}EntityDetail`
|
const params = { from: this.from, q: this.searchContent }
|
||||||
this.currentEntity = entity
|
this.listData = await getEntityList({ ...this.pageObjRight, ...params })
|
||||||
this.panelTabs = tabs
|
this.pageObjRight.total = await getEntityCount(params)
|
||||||
this.showDetail = true
|
const { topFilterData, bottomFilterData } = await this.queryFilterData(params)
|
||||||
}
|
this.topFilterData = topFilterData
|
||||||
},
|
this.bottomFilterData = bottomFilterData
|
||||||
watch: {
|
},
|
||||||
filterType (n) {
|
/* 重置条件 */
|
||||||
|
reset () {
|
||||||
this.pageObjLeftTop = {
|
this.pageObjLeftTop = {
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10
|
pageSize: 10
|
||||||
@@ -262,61 +149,306 @@ export default {
|
|||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 50
|
pageSize: 50
|
||||||
}
|
}
|
||||||
this.handleData(n)
|
this.searchParams = null
|
||||||
|
this.searchParams = []
|
||||||
|
},
|
||||||
|
async queryFilterData (params) {
|
||||||
|
let topFilterParams = { ...params, ...this.pageObjLeftTop }
|
||||||
|
let bottomFilterParams = { ...params, ...this.pageObjLeftBottom }
|
||||||
|
const keys = Object.keys(this.filterObj)
|
||||||
|
let topLevel = 1 // 左上过滤条件默认是tree的第一层
|
||||||
|
if (!this.$_.isEmpty(keys)) {
|
||||||
|
let hasTopColumn = false
|
||||||
|
let hasBottomColumn = false
|
||||||
|
keys.forEach(key => {
|
||||||
|
switch (key) {
|
||||||
|
case entityFilterType.ip.country:
|
||||||
|
case entityFilterType.domain.categoryGroup:
|
||||||
|
case entityFilterType.app.appCategory: {
|
||||||
|
hasTopColumn = true
|
||||||
|
topFilterParams = { ...topFilterParams, column: key }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case entityFilterType.ip.region:
|
||||||
|
case entityFilterType.domain.categoryName:
|
||||||
|
case entityFilterType.app.appSubcategory: {
|
||||||
|
topLevel = 2
|
||||||
|
hasTopColumn = true
|
||||||
|
topFilterParams = { ...topFilterParams, column: key }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case entityFilterType.ip.asn:
|
||||||
|
case entityFilterType.domain.reputationLevel:
|
||||||
|
case entityFilterType.app.appRisk: {
|
||||||
|
hasBottomColumn = true
|
||||||
|
bottomFilterParams = { ...bottomFilterParams, column: key }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!hasTopColumn) {
|
||||||
|
topFilterParams = { ...topFilterParams, column: this.getDefaultTopColumn(this.from) }
|
||||||
|
}
|
||||||
|
if (!hasBottomColumn) {
|
||||||
|
bottomFilterParams = { ...bottomFilterParams, column: this.getDefaultBottomColumn(this.from) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
topFilterParams = { ...topFilterParams, column: this.getDefaultTopColumn(this.from) }
|
||||||
|
bottomFilterParams = { ...bottomFilterParams, column: this.getDefaultBottomColumn(this.from) }
|
||||||
|
}
|
||||||
|
const topFilterListData = await getEntityFilter(topFilterParams) || []
|
||||||
|
const bottomFilterListData = await getEntityFilter(bottomFilterParams) || []
|
||||||
|
let topFilterData = { data: topFilterListData, hasnotMore: topFilterListData.length < 10, column: topFilterParams.column }
|
||||||
|
let bottomFilterData = { data: bottomFilterListData, hasnotMore: bottomFilterListData.length < 10, column: bottomFilterParams.column }
|
||||||
|
switch (this.from) {
|
||||||
|
case 'ip': {
|
||||||
|
topFilterData = { ...topFilterData, icon: 'cn-icon cn-icon-country', title: this.$t('entities.countryOrRegion'), leaf: topLevel === 2 }
|
||||||
|
bottomFilterData = { ...bottomFilterData, icon: 'cn-icon cn-icon-cloud', title: this.$t('entities.asn') }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'domain': {
|
||||||
|
topFilterData = { ...topFilterData, icon: 'cn-icon cn-icon-category', title: this.$t('entities.groupAndName'), leaf: topLevel === 2 }
|
||||||
|
bottomFilterData = { ...bottomFilterData, icon: 'cn-icon cn-icon-risk', title: this.$t('entities.creditLevel') }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'app': {
|
||||||
|
topFilterData = { ...topFilterData, icon: 'cn-icon cn-icon-category', title: this.$t('entities.categoryAndSub'), leaf: topLevel === 2 }
|
||||||
|
bottomFilterData = { ...bottomFilterData, icon: 'cn-icon cn-icon-risk', title: this.$t('entities.riskLevel') }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
return { topFilterData, bottomFilterData }
|
||||||
|
},
|
||||||
|
getDefaultTopColumn (from) {
|
||||||
|
let column = ''
|
||||||
|
switch (from) {
|
||||||
|
case 'ip': {
|
||||||
|
column = entityFilterType.ip.country
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'domain': {
|
||||||
|
column = entityFilterType.domain.categoryGroup
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'app': {
|
||||||
|
column = entityFilterType.app.appCategory
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
return column
|
||||||
|
},
|
||||||
|
getDefaultBottomColumn (from) {
|
||||||
|
let column = ''
|
||||||
|
switch (from) {
|
||||||
|
case 'ip': {
|
||||||
|
column = entityFilterType.ip.asn
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'domain': {
|
||||||
|
column = entityFilterType.domain.reputationLevel
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'app': {
|
||||||
|
column = entityFilterType.app.appRisk
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
return column
|
||||||
|
},
|
||||||
|
async select (data, node, index, column) {
|
||||||
|
this.filterObj[column] = data.name
|
||||||
|
},
|
||||||
|
async loadLevel2FilterData (node, parentColumn) {
|
||||||
|
let column
|
||||||
|
switch (parentColumn) {
|
||||||
|
case entityFilterType.ip.country: {
|
||||||
|
column = entityFilterType.ip.region
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case entityFilterType.domain.categoryGroup: {
|
||||||
|
column = entityFilterType.domain.categoryName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case entityFilterType.app.appCategory: {
|
||||||
|
column = entityFilterType.app.appSubcategory
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
q: this.searchContent,
|
||||||
|
from: this.from,
|
||||||
|
pageSize: -1,
|
||||||
|
pageNo: 1,
|
||||||
|
column
|
||||||
|
}
|
||||||
|
return await getEntityFilter(params)
|
||||||
|
},
|
||||||
|
pageSizeRight (val) {
|
||||||
|
this.pageObjRight.pageSize = val
|
||||||
|
},
|
||||||
|
pageNoRight (val) {
|
||||||
|
this.pageObjRight.pageNo = val
|
||||||
|
},
|
||||||
|
loadFilter (node, resolve, filterType, key, parentKey) {
|
||||||
|
if (node.level === 0) {
|
||||||
|
resolve(node.data)
|
||||||
|
} else {
|
||||||
|
const param = { column: key, from: filterType, q: this.searchContent }
|
||||||
|
const req = getEntityFilter(param)
|
||||||
|
if (req) {
|
||||||
|
req.then(res => {
|
||||||
|
res = res.map(r => {
|
||||||
|
return { ...r, leaf: true }
|
||||||
|
})
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
entityDetail (entity, tabs) {
|
||||||
|
this.typeName = `${this.from.toLowerCase()}EntityDetail`
|
||||||
|
this.currentEntity = entity
|
||||||
|
this.panelTabs = tabs
|
||||||
|
this.showDetail = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/* entity类型切换时,分页、搜索信息重置 */
|
||||||
|
from (n) {
|
||||||
|
this.reset()
|
||||||
|
},
|
||||||
|
/* 搜索框searchContent、左侧过滤条件filterObj互相联动
|
||||||
|
* 监听filterObj改动,将改动同步给searchParams
|
||||||
|
* 监听searchContent改动,将改动同步给searchParams
|
||||||
|
* 监听searchParams,将改动同步给filterObj、searchContent之一,并重新请求接口,获取左侧过滤条件和右侧entity列表
|
||||||
|
* */
|
||||||
|
filterObj: {
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
if (n) {
|
||||||
|
const searchParams = this.$_.cloneDeep(this.searchParams)
|
||||||
|
let change = false
|
||||||
|
Object.keys(n).forEach(key => {
|
||||||
|
let containKey = false
|
||||||
|
searchParams.forEach(item => {
|
||||||
|
if (item.name === key) {
|
||||||
|
containKey = true
|
||||||
|
if (item.value !== n[key]) {
|
||||||
|
change = true
|
||||||
|
item.value !== n[key] && (item.value = n[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!containKey) {
|
||||||
|
change = true
|
||||||
|
const name = key
|
||||||
|
const value = n[key]
|
||||||
|
const param = { name, value }
|
||||||
|
searchParams.push(param)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (change) {
|
||||||
|
this.searchParams = searchParams
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchContent (n) {
|
||||||
|
if (n) {
|
||||||
|
const paramsArr = n.split(/\s[aA][nN][dD]\s/)
|
||||||
|
const paramsObj = {}
|
||||||
|
let change = false
|
||||||
|
paramsArr.forEach(string => {
|
||||||
|
const param = string.split('=')
|
||||||
|
if (param.length > 1) {
|
||||||
|
let value = param[1].trim()
|
||||||
|
const valueArr = value.split('"')
|
||||||
|
if (valueArr.length > 2) {
|
||||||
|
value = valueArr[1].trim()
|
||||||
|
}
|
||||||
|
paramsObj[param[0].trim()] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const searchParams = this.$_.cloneDeep(this.searchParams)
|
||||||
|
Object.keys(paramsObj).forEach(key => {
|
||||||
|
let containKey = false
|
||||||
|
searchParams.forEach(item => {
|
||||||
|
if (item.name === key) {
|
||||||
|
containKey = true
|
||||||
|
if (item.value !== paramsObj[key]) {
|
||||||
|
change = true
|
||||||
|
item.value = paramsObj[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!containKey) {
|
||||||
|
change = true
|
||||||
|
searchParams.push({ name: key, value: paramsObj[key] })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (change) {
|
||||||
|
this.searchParams = searchParams
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
this.searchContentTemp !== n && (this.searchContentTemp = n)
|
||||||
|
},
|
||||||
|
searchParams: {
|
||||||
|
deep: true,
|
||||||
|
handler (n) {
|
||||||
|
if (n) {
|
||||||
|
let fromInput = false // input内容改变导致的变化
|
||||||
|
let fromFilter = false // 左侧filter过滤条件改变导致的变化
|
||||||
|
const filterKeys = Object.keys(this.filterObj)
|
||||||
|
if (n.length === 0) {
|
||||||
|
this.searchContentTemp = ''
|
||||||
|
this.searchContent = ''
|
||||||
|
this.filterObj = {}
|
||||||
|
} else if (filterKeys.length !== n.length) {
|
||||||
|
fromInput = true
|
||||||
|
} else {
|
||||||
|
fromFilter = true
|
||||||
|
}
|
||||||
|
if (fromInput) { // 是input导致的改变,则同步给filter
|
||||||
|
const filterObj = {}
|
||||||
|
n.forEach(item => {
|
||||||
|
filterObj[item.name] = item.value
|
||||||
|
})
|
||||||
|
this.filterObj = filterObj
|
||||||
|
}
|
||||||
|
if (fromFilter) { // 是filter导致的改变,则同步给input
|
||||||
|
let searchContent = ''
|
||||||
|
n.forEach(item => {
|
||||||
|
if (searchContent) {
|
||||||
|
searchContent += ' AND '
|
||||||
|
}
|
||||||
|
searchContent += `${item.name}="${item.value}"`
|
||||||
|
})
|
||||||
|
this.searchContent = searchContent
|
||||||
|
}
|
||||||
|
// 请求接口,获取左侧过滤条件和右侧entity列表
|
||||||
|
this.search()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted () {
|
async mounted () {
|
||||||
const country = await this.loadFilterData(this.filterType, 'country')
|
this.searchParams = []
|
||||||
const asn = await this.loadFilterData(this.filterType, 'asn')
|
|
||||||
const res = await getEntityIpList({ ...this.pageObjRight })
|
|
||||||
this.listData = res.data.result
|
|
||||||
this.pageObjRight = { ...this.pageObjRight, total: res.statistics.result_size }
|
|
||||||
this.filterData.push({
|
|
||||||
title: this.$t('entities.countryOrRegion'),
|
|
||||||
key: 'country',
|
|
||||||
childrenKey: 'region',
|
|
||||||
icon: 'cn-icon cn-icon-country',
|
|
||||||
data: country,
|
|
||||||
hasnotMore: this.$_.isEmpty(country) || country.length < 10,
|
|
||||||
filterType: this.filterType
|
|
||||||
})
|
|
||||||
this.filterData.push({
|
|
||||||
title: this.$t('entities.asn'),
|
|
||||||
key: 'asn',
|
|
||||||
data: asn,
|
|
||||||
icon: 'cn-icon cn-icon-cloud',
|
|
||||||
hasnotMore: this.$_.isEmpty(asn) || asn.length < 10,
|
|
||||||
filterType: this.filterType
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
setup () {
|
setup () {
|
||||||
const filterType = ref(Object.keys(entityType)[0])
|
const from = ref(Object.keys(entityType)[0])
|
||||||
return {
|
return {
|
||||||
entityType,
|
entityType, // 所有entity类型,用于header下拉框选择
|
||||||
filterType
|
from // 当前entity类型,ip/domain/app
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const loadList = async (node, filterType, pageObj) => {
|
|
||||||
let res
|
|
||||||
const param = { ...pageObj }
|
|
||||||
switch (filterType) {
|
|
||||||
case 'ip': {
|
|
||||||
res = await getEntityIpList(param)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'domain': {
|
|
||||||
res = await getEntityDomainList(param)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'app': {
|
|
||||||
res = await getEntityAppList(param)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
Reference in New Issue
Block a user