CN-240 feat: 实体详情
This commit is contained in:
140
src/views/entityExplorer/EntityDetail.vue
Normal file
140
src/views/entityExplorer/EntityDetail.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="cn-home entity-detail">
|
||||
<div class="entity-detail__header">
|
||||
<div class="cn-entity__icon"><i :class="iconClass"></i></div>
|
||||
<div class="cn-entity__name">{{entityData.name}}</div>
|
||||
</div>
|
||||
<main class="cn-body entity-detail__body">
|
||||
<div class="entity-detail__menu">
|
||||
<template v-for="anchor in anchorPoints" :key="anchor.id">
|
||||
<div class="menu-item" :class="{'menu-item--active': menuIsActive(anchor)}" @click="jumpToAnchor(anchor)">
|
||||
<span>{{anchor.label}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="entity-detail__content">
|
||||
<cn-panel
|
||||
ref="cnPanel"
|
||||
:entity="entityData"
|
||||
:is-entity-detail="true"
|
||||
@chartLoaded="chartLoaded"
|
||||
@scroll="scroll"
|
||||
></cn-panel>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useRoute } from 'vue-router'
|
||||
import Panel from '@/views/charts/Panel'
|
||||
export default {
|
||||
name: 'EntityDetail',
|
||||
components: {
|
||||
cnPanel: Panel
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
anchorPoints: [], // { id, label, top, height }
|
||||
top: 0
|
||||
}
|
||||
},
|
||||
setup (props, ctx) {
|
||||
const { query } = useRoute()
|
||||
let panelType
|
||||
const entityData = {}
|
||||
switch (query.entityType) {
|
||||
case 'ip': {
|
||||
panelType = 4
|
||||
entityData.ip = query.name
|
||||
break
|
||||
}
|
||||
case 'domain': {
|
||||
panelType = 5
|
||||
entityData.domain = query.name
|
||||
break
|
||||
}
|
||||
case 'app': {
|
||||
panelType = 6
|
||||
entityData.appName = query.name
|
||||
break
|
||||
}
|
||||
default: {
|
||||
panelType = 4
|
||||
break
|
||||
}
|
||||
}
|
||||
entityData.type = panelType
|
||||
return {
|
||||
entityData
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
chartLoaded (chartList) {
|
||||
this.anchorPoints = []
|
||||
let anchorPoints = []
|
||||
chartList.forEach(chart => {
|
||||
if (chart.params.anchorPoint) {
|
||||
const dom = document.querySelector(`#${chart.params.anchorPoint}`)
|
||||
anchorPoints.push({
|
||||
id: chart.params.anchorPoint,
|
||||
label: chart.i18n ? this.$t(chart.i18n) : chart.name,
|
||||
top: dom.offsetTop/* ,
|
||||
height: document.querySelector(`#${chart.params.anchorPoint}}`).scrollHeight */
|
||||
})
|
||||
}
|
||||
})
|
||||
// 从小到大排序
|
||||
anchorPoints = anchorPoints.sort((a, b) => {
|
||||
return a.top - b.top
|
||||
})
|
||||
if (!this.$_.isEmpty(anchorPoints)) {
|
||||
anchorPoints[0].top = 0
|
||||
}
|
||||
this.anchorPoints = anchorPoints
|
||||
},
|
||||
scroll ({ top }) {
|
||||
this.top = top || 0
|
||||
},
|
||||
jumpToAnchor (anchor) {
|
||||
this.top = anchor.top
|
||||
this.$refs.cnPanel.jumpToTop(anchor.top)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iconClass () {
|
||||
let className
|
||||
switch (this.entityData.entityType) {
|
||||
case ('ip'): {
|
||||
className = 'cn-icon cn-icon-ip'
|
||||
break
|
||||
}
|
||||
case ('domain'): {
|
||||
className = 'cn-icon cn-icon-domain'
|
||||
break
|
||||
}
|
||||
case ('app'): {
|
||||
className = 'cn-icon cn-icon-app'
|
||||
break
|
||||
}
|
||||
default: break
|
||||
}
|
||||
return className
|
||||
},
|
||||
menuIsActive () {
|
||||
return function (anchor) {
|
||||
return this.currentAnchor ? this.currentAnchor.id === anchor.id : false
|
||||
}
|
||||
},
|
||||
currentAnchor () {
|
||||
let currentAnchor = null
|
||||
this.anchorPoints.forEach(anchor => {
|
||||
if (anchor.top <= this.top) {
|
||||
currentAnchor = anchor
|
||||
}
|
||||
})
|
||||
return currentAnchor
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -150,28 +150,28 @@ export default {
|
||||
data: [
|
||||
{
|
||||
label: this.$t('overall.country'),
|
||||
column: 'country_distinct_count',
|
||||
topColumn: 'ip_location_country', // top弹框查询字段
|
||||
column: 'countryDistinctCount',
|
||||
topColumn: 'ipLocationCountry', // top弹框查询字段
|
||||
icon: entityFilterType.ip[0].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('overall.province'),
|
||||
column: 'province_distinct_count',
|
||||
topColumn: 'ip_location_province', // top弹框查询字段
|
||||
column: 'provinceDistinctCount',
|
||||
topColumn: 'ipLocationProvince', // top弹框查询字段
|
||||
icon: entityFilterType.ip[1].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('overall.city'),
|
||||
column: 'city_distinct_count',
|
||||
column: 'cityDistinctCount',
|
||||
topColumn: 'ip_location_city', // top弹框查询字段
|
||||
icon: entityFilterType.ip[2].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('entities.asn'),
|
||||
column: 'asn_distinct_count',
|
||||
column: 'asnDistinctCount',
|
||||
topColumn: 'ip_asn', // top弹框查询字段
|
||||
icon: entityFilterType.ip[3].icon,
|
||||
value: 0
|
||||
@@ -185,21 +185,21 @@ export default {
|
||||
data: [
|
||||
{
|
||||
label: this.$t('entities.category'),
|
||||
column: 'category_distinct_count',
|
||||
column: 'categoryDistinctCount',
|
||||
topColumn: 'app_category', // top弹框查询字段
|
||||
icon: entityFilterType.app[0].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('entities.subcategory'),
|
||||
column: 'subcategory_distinct_count',
|
||||
column: 'subcategoryDistinctCount',
|
||||
topColumn: 'app_subcategory', // top弹框查询字段
|
||||
icon: entityFilterType.app[1].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('entities.risk'),
|
||||
column: 'risk_distinct_count',
|
||||
column: 'riskDistinctCount',
|
||||
topColumn: 'app_risk', // top弹框查询字段
|
||||
icon: entityFilterType.app[2].icon,
|
||||
value: 0
|
||||
@@ -213,22 +213,22 @@ export default {
|
||||
data: [
|
||||
{
|
||||
label: this.$t('entities.domainDetail.categoryGroup'),
|
||||
column: 'category_group_distinct_count',
|
||||
column: 'categoryGroupDistinctCount',
|
||||
topColumn: 'domain_category_group', // top弹框查询字段
|
||||
icon: entityFilterType.domain[0].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('entities.category'),
|
||||
column: 'category_distinct_count',
|
||||
column: 'categoryDistinctCount',
|
||||
topColumn: 'domain_category', // top弹框查询字段
|
||||
icon: entityFilterType.domain[1].icon,
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: this.$t('entities.reputationLevel'),
|
||||
column: 'reputation_level_distinct_count',
|
||||
topColumn: 'domain_reputation_level', // top弹框查询字段
|
||||
column: 'reputationLevelDistinctCount',
|
||||
topColumn: 'domainReputationLevel', // top弹框查询字段
|
||||
icon: entityFilterType.domain[2].icon,
|
||||
value: 0
|
||||
}
|
||||
|
||||
@@ -84,7 +84,11 @@
|
||||
<span>{{entityData.securityCount || '-'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="show-detail" :style="{visibility: !isCollapse ? 'visible' : 'hidden'}">{{$t('overall.detail')}}>></div>
|
||||
<div
|
||||
class="show-detail"
|
||||
:style="{visibility: !isCollapse ? 'visible' : 'hidden'}"
|
||||
@click="showDetail"
|
||||
>{{$t('overall.detail')}}>></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,6 +131,16 @@ export default {
|
||||
/* 设为折叠状态 */
|
||||
collapse () {
|
||||
this.isCollapse = true
|
||||
},
|
||||
showDetail () {
|
||||
const { href } = this.$router.resolve({
|
||||
path: '/entityDetail',
|
||||
query: {
|
||||
entityType: this.entityData.entityType,
|
||||
name: this.entityData.ipAddr || this.entityData.domainName || this.entityData.appName
|
||||
}
|
||||
})
|
||||
window.open(href, '_blank')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
let name
|
||||
switch (this.entityData.entityType) {
|
||||
case ('ip'): {
|
||||
name = this.entity.ip
|
||||
name = this.entity.ipAddr
|
||||
break
|
||||
}
|
||||
case ('domain'): {
|
||||
@@ -85,7 +85,7 @@ export default {
|
||||
startTime: Math.floor(now.getTime() / 1000 - 3600),
|
||||
endTime: Math.floor(now.getTime() / 1000)
|
||||
},
|
||||
ip: this.entityData.ip
|
||||
ip: this.entityData.ipAddr
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user