CN-980 feat: 实体详情开发准备

This commit is contained in:
chenjinsong
2023-04-25 16:04:20 +08:00
parent c7214d3b89
commit 21b3cea26a
15 changed files with 264 additions and 119 deletions

View File

@@ -22,7 +22,7 @@
.edit-appearance-base__footer { .edit-appearance-base__footer {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: start; justify-content: flex-start;
height: 100px; height: 100px;
margin-top: 3px; margin-top: 3px;
padding-left:200px; padding-left:200px;
@@ -59,4 +59,4 @@
cursor: default; cursor: default;
} }
} }
} }

View File

@@ -123,7 +123,7 @@
align-items: center; align-items: center;
} }
.el-table__header tr th:nth-of-type(1) .cell { .el-table__header tr th:nth-of-type(1) .cell {
justify-content: start; justify-content: flex-start;
} }
.score-cell { .score-cell {
display: flex !important; display: flex !important;

View File

@@ -1,3 +1,14 @@
.panel-box2.panel-box2--entity-detail {
height: 100%;
.chart-list {
height: 100%;
&>.vue-grid-layout {
margin-top: 0;
}
}
}
.panel-box2 { .panel-box2 {
height: calc(100% - 20px); height: calc(100% - 20px);
.panel__header { .panel__header {

View File

@@ -1,3 +1,4 @@
/*
.entity-detail.cn-home { .entity-detail.cn-home {
.panel-chart { .panel-chart {
width: 100%; width: 100%;
@@ -105,3 +106,4 @@
} }
} }
} }
*/

View File

@@ -58,9 +58,12 @@ export const panelTypeAndRouteMapping = {
networkOverviewDrillDown: 13, networkOverviewDrillDown: 13,
networkAppPerformance: 2, networkAppPerformance: 2,
dnsServiceInsights: 3, dnsServiceInsights: 3,
ipEntityDetail: 4, /* ipEntityDetail: 4,
domainEntityDetail: 5, domainEntityDetail: 5,
appEntityDetail: 6, appEntityDetail: 6,*/
ipEntityDetail: 21,
domainEntityDetail: 22,
appEntityDetail: 23,
cryptocurrency: 7, cryptocurrency: 7,
ipDrillDownTest: 8, ipDrillDownTest: 8,
linkMonitor: 14, linkMonitor: 14,

View File

@@ -160,6 +160,24 @@
:chart="chart" :chart="chart"
@toggleLoading="toggleLoading" @toggleLoading="toggleLoading"
></dns-traffic-line> ></dns-traffic-line>
<entity-detail-basic-info
v-else-if="chart.type === typeMapping.entityDetail.basicInfo"
:chart="chart"
:entity="entity"
@toggleLoading="toggleLoading"
></entity-detail-basic-info>
<entity-detail-line
v-else-if="chart.type === typeMapping.entityDetail.line"
:chart="chart"
:entity="entity"
@toggleLoading="toggleLoading"
></entity-detail-line>
<entity-detail-tabs-chart
v-else-if="chart.type === typeMapping.entityDetail.tabsChart"
:chart="chart"
:entity="entity"
@toggleLoading="toggleLoading"
></entity-detail-tabs-chart>
</div> </div>
</template> </template>
@@ -190,6 +208,9 @@ import DnsActiveMaliciousDomain from '@/views/charts2/charts/dnsInsight/DnsActiv
import DnsEventChart from '@/views/charts2/charts/dnsInsight/DnsEventChart' import DnsEventChart from '@/views/charts2/charts/dnsInsight/DnsEventChart'
import DnsRecentEvents from '@/views/charts2/charts/dnsInsight/DnsRecentEvents' import DnsRecentEvents from '@/views/charts2/charts/dnsInsight/DnsRecentEvents'
import DnsTrafficLine from '@/views/charts2/charts/dnsInsight/DnsTrafficLine' import DnsTrafficLine from '@/views/charts2/charts/dnsInsight/DnsTrafficLine'
import EntityDetailBasicInfo from '@/views/charts2/charts/entityDetail/EntityDetailBasicInfo'
import EntityDetailLine from '@/views/charts2/charts/entityDetail/EntityDetailLine'
import EntityDetailTabsChart from '@/views/charts2/charts/entityDetail/EntityDetailTabsChart'
import { getNowTime } from '@/utils/date-util' import { getNowTime } from '@/utils/date-util'
import { ref } from 'vue' import { ref } from 'vue'
@@ -222,13 +243,17 @@ export default {
DnsActiveMaliciousDomain, DnsActiveMaliciousDomain,
DnsEventChart, DnsEventChart,
DnsRecentEvents, DnsRecentEvents,
DnsTrafficLine DnsTrafficLine,
EntityDetailBasicInfo,
EntityDetailLine,
EntityDetailTabsChart
}, },
props: { props: {
chart: Object, chart: Object,
metric: String, metric: String,
timeFilter: Object, timeFilter: Object,
extraParams: Object extraParams: Object,
entity: Object
}, },
data () { data () {
return { return {

View File

@@ -24,6 +24,7 @@
:metric="metric" :metric="metric"
:extra-params="extraParams" :extra-params="extraParams"
:id="item.id" :id="item.id"
:entity="entity"
ref="chartGrid" ref="chartGrid"
@npmTabChange="npmTabChange" @npmTabChange="npmTabChange"
:chart="item" :chart="item"

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="panel-box2"> <div class="panel-box2" :class="{'panel-box2--entity-detail': entity.entityType}">
<div class="panel__header"> <div class="panel__header" v-if="!entity.entityType">
<div class="panel__title">{{panelName?panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}} <div class="panel__title">{{panelName?panelName:(panel.i18n ? $t(panel.i18n) : panel.name)}}
<div v-if="showScore" class="score"> <div v-if="showScore" class="score">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div> <div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>

View File

@@ -1,7 +1,8 @@
export default { export default {
props: { props: {
chart: Object, chart: Object,
timeFilter: Object timeFilter: Object,
entity: Object
}, },
data () { data () {
return { return {

View File

@@ -31,5 +31,10 @@ export const typeMapping = {
dnsEventChart: 711, dnsEventChart: 711,
dnsRecentEvents: 605, dnsRecentEvents: 605,
dnsActiveMaliciousDomain: 604 dnsActiveMaliciousDomain: 604
},
entityDetail: {
basicInfo: 712,
line: 107,
tabsChart: 713
} }
} }

View File

@@ -0,0 +1,12 @@
<template>
<div style="border: 1px solid #ff5500; height: 100%;">{{entity}}</div>
</template>
<script>
import chartMixin from '@/views/charts2/chart-mixin'
export default {
name: 'EntityDetailBasicInfo',
mixins: [chartMixin]
}
</script>

View File

@@ -0,0 +1,16 @@
<template>
<div style="border: 1px solid #ff5500; height: 100%;">{{entity}}</div>
</template>
<script>
import chartMixin from '@/views/charts2/chart-mixin'
export default {
name: 'EntityDetailLine',
mixins: [chartMixin],
mounted () {
this.toggleLoading(false)
}
}
</script>

View File

@@ -0,0 +1,12 @@
<template>
<div style="border: 1px solid #ff5500; height: 100%;">{{entity}}</div>
</template>
<script>
import chartMixin from '@/views/charts2/chart-mixin'
export default {
name: 'EntityDetailTabsChart',
mixins: [chartMixin]
}
</script>

View File

@@ -1,68 +1,46 @@
<template> <template>
<div class="cn-home entity-detail"> <div class="entity-detail" style="height: 100%;">
<div class="entity-detail__header"> <panel
<div class="cn-entity__icon"><i :class="iconClass"></i></div> :entity="entityData"
<div class="cn-entity__name">{{entityData.ip || entityData.domain || entityData.appName }}</div> ></panel>
</div>
<main class="cn-body entity-detail__body">
<div class="entity-detail__menu">
<template v-for="(anchor,index) in anchorPoints" :key="anchor.id">
<div class="menu-item" :class="{'menu-item--active':anchor.isActive}" @click="jumpToAnchor(index,anchor)">
<span>{{anchor.label}}</span>
</div>
</template>
</div>
<div class="entity-detail__content" @scroll="scroll" id="detailWrapper">
<cn-panel
ref="cnPanel"
:entity="entityData"
:is-entity-detail="true"
@chartLoaded="chartLoaded"
></cn-panel>
</div>
</main>
</div> </div>
</template> </template>
<script> <script>
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import Panel from '@/views/charts/Panel' import Panel from '@/views/charts2/Panel'
import { panelTypeAndRouteMapping } from '@/utils/constants'
export default { export default {
name: 'EntityDetail', name: 'EntityDetail',
components: { components: {
cnPanel: Panel Panel
}, },
data () { data () {
return { return {
anchorPoints: [], // { id, label, top, height }
top: 0,
scrollHeight: 0,
clientHeight: 0,
currentAnchor: 0
} }
}, },
setup (props, ctx) { setup (props) {
const { query } = useRoute() const { query } = useRoute()
let panelType let panelType
const entityData = { entityType: query.entityType } const entityData = { entityType: query.entityType }
switch (query.entityType) { switch (query.entityType) {
case 'ip': { case 'ip': {
panelType = 4 panelType = panelTypeAndRouteMapping.ipEntityDetail
entityData.ip = query.name entityData.ip = query.name
break break
} }
case 'domain': { case 'domain': {
panelType = 5 panelType = panelTypeAndRouteMapping.domainEntityDetail
entityData.domain = query.name entityData.domain = query.name
break break
} }
case 'app': { case 'app': {
panelType = 6 panelType = panelTypeAndRouteMapping.appEntityDetail
entityData.appName = query.name entityData.appName = query.name
break break
} }
default: { default: {
panelType = 4 panelType = panelTypeAndRouteMapping.ipEntityDetail
break break
} }
} }
@@ -72,83 +50,8 @@ export default {
} }
}, },
methods: { methods: {
chartLoaded (chartList) {
this.chartList = chartList
this.anchorPoints = []
let anchorPoints = []
const panelDom = document.querySelector('#detailWrapper')
this.scrollHeight = panelDom.scrollHeight
this.clientHeight = panelDom.clientHeight
chartList.forEach((chart, index) => {
if (chart.params.anchorPoint) {
const dom = document.querySelector(`[id='${chart.params.anchorPoint}']`)
dom && anchorPoints.push({
id: chart.params.anchorPoint,
isActive: index === 0,
label: chart.i18n ? this.$t(chart.i18n) : chart.name,
top: dom.offsetTop + 10/* ,
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 (e) {
this.top = (e.target.scrollTop + 10) || 0
},
jumpToAnchor (index, anchor) {
const anchorEle = document.getElementById(anchor.id)
this.anchorPoints.forEach((anchor, i) => {
if (index === i) {
anchor.isActive = true
} else {
anchor.isActive = false
}
})
if (anchorEle) {
anchorEle.scrollIntoView()
document.documentElement.scrollTop = document.documentElement.scrollTop - 30
}
},
resize () {
this.chartLoaded(this.chartList)
}
}, },
mounted () { mounted () {
this.debounceFunc = this.$_.debounce(this.resize, 400)
window.addEventListener('resize', this.debounceFunc)
},
beforeUnmount () {
window.removeEventListener('resize', this.debounceFunc)
},
computed: {
iconClass () {
let className
switch (this.entityData.entityType) {
case ('ip'): {
className = 'cn-icon cn-icon-ip2'
break
}
case ('domain'): {
className = 'cn-icon cn-icon-domain2'
break
}
case ('app'): {
className = 'cn-icon cn-icon-app2'
break
}
default: break
}
return className
}
} }
} }
</script> </script>

View File

@@ -0,0 +1,154 @@
<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.ip || entityData.domain || entityData.appName }}</div>
</div>
<main class="cn-body entity-detail__body">
<div class="entity-detail__menu">
<template v-for="(anchor,index) in anchorPoints" :key="anchor.id">
<div class="menu-item" :class="{'menu-item--active':anchor.isActive}" @click="jumpToAnchor(index,anchor)">
<span>{{anchor.label}}</span>
</div>
</template>
</div>
<div class="entity-detail__content" @scroll="scroll" id="detailWrapper">
<cn-panel
ref="cnPanel"
:entity="entityData"
:is-entity-detail="true"
@chartLoaded="chartLoaded"
></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,
scrollHeight: 0,
clientHeight: 0,
currentAnchor: 0
}
},
setup (props, ctx) {
const { query } = useRoute()
let panelType
const entityData = { entityType: query.entityType }
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.chartList = chartList
this.anchorPoints = []
let anchorPoints = []
const panelDom = document.querySelector('#detailWrapper')
this.scrollHeight = panelDom.scrollHeight
this.clientHeight = panelDom.clientHeight
chartList.forEach((chart, index) => {
if (chart.params.anchorPoint) {
const dom = document.querySelector(`[id='${chart.params.anchorPoint}']`)
dom && anchorPoints.push({
id: chart.params.anchorPoint,
isActive: index === 0,
label: chart.i18n ? this.$t(chart.i18n) : chart.name,
top: dom.offsetTop + 10/* ,
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 (e) {
this.top = (e.target.scrollTop + 10) || 0
},
jumpToAnchor (index, anchor) {
const anchorEle = document.getElementById(anchor.id)
this.anchorPoints.forEach((anchor, i) => {
if (index === i) {
anchor.isActive = true
} else {
anchor.isActive = false
}
})
if (anchorEle) {
anchorEle.scrollIntoView()
document.documentElement.scrollTop = document.documentElement.scrollTop - 30
}
},
resize () {
this.chartLoaded(this.chartList)
}
},
mounted () {
this.debounceFunc = this.$_.debounce(this.resize, 400)
window.addEventListener('resize', this.debounceFunc)
},
beforeUnmount () {
window.removeEventListener('resize', this.debounceFunc)
},
computed: {
iconClass () {
let className
switch (this.entityData.entityType) {
case ('ip'): {
className = 'cn-icon cn-icon-ip2'
break
}
case ('domain'): {
className = 'cn-icon cn-icon-domain2'
break
}
case ('app'): {
className = 'cn-icon cn-icon-app2'
break
}
default: break
}
return className
}
}
}
</script>