diff --git a/src/components/charts/chart-options.js b/src/components/charts/chart-options.js
index 56051358..de2f5f60 100644
--- a/src/components/charts/chart-options.js
+++ b/src/components/charts/chart-options.js
@@ -305,7 +305,7 @@ const relationShip = {
}
]
}
-const sankeyShip = {
+const sankey = {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
@@ -345,13 +345,39 @@ const sankeyShip = {
}
]
}
+const ipOpenPortBar = {
+ xAxis: {
+ type: 'category',
+ axisTick: { show: false },
+ axisLine: { show: false }
+ },
+ grid: {
+ top: 30,
+ left: 60,
+ right: 50,
+ bottom: 50
+ },
+ yAxis: {
+ type: 'value',
+ show: false
+ },
+ series: [{
+ barWidth: 38,
+ data: [],
+ type: 'bar',
+ label: { show: true, position: 'top' },
+ barCategoryGap: '10%'
+ }]
+}
const typeOptionMappings = [
{ value: 11, option: line }, // 常规折线图
{ value: 12, option: lineWithStatistics }, // 带统计表格的折线图
{ value: 13, option: lineStack }, // 折线堆叠图
+ { value: 22, option: ipOpenPortBar }, // ip详情--开放端口的柱状图
{ value: 31, option: pieWithTable }, // 常规折线图
+ { value: 33, option: ipOpenPortBar }, // ip详情--域名
{ value: 42, option: relationShip }, // 关系图
- { value: 43, option: sankeyShip }, // 桑基图
+ { value: 43, option: sankey }, // 桑基图
{ value: 52, option: singleValueLine }
]
const typeCategory = {
diff --git a/src/components/entities/EntityList.vue b/src/components/entities/EntityList.vue
index fa69f8af..467d1979 100644
--- a/src/components/entities/EntityList.vue
+++ b/src/components/entities/EntityList.vue
@@ -35,7 +35,7 @@
{{d.asn || '-'}}
{{$t('overall.detail')}}>
+ @click="entityDetail({ip: d.ip, type: 4})">{{$t('overall.detail')}}>
@@ -50,7 +50,7 @@
{{$t('entities.credit')}}:
{{d.reputationScore || '-'}}
- {{$t('overall.detail')}}>
+ {{$t('overall.detail')}}>
@@ -65,7 +65,7 @@
{{$t('entities.subcategory')}}:
{{d.appSubategory || '-'}}
- {{$t('overall.detail')}}>
+ {{$t('overall.detail')}}>
diff --git a/src/utils/tools.js b/src/utils/tools.js
index 6300b357..b34f8bf1 100644
--- a/src/utils/tools.js
+++ b/src/utils/tools.js
@@ -350,7 +350,7 @@ export const noData = {
updated (el, binding) {
if (el) {
if (binding.value) {
- el.innerHTML = 'No data
'
+ el.innerHTML = 'No data
'
}
}
}
diff --git a/src/views/charts/Chart.vue b/src/views/charts/Chart.vue
index 49e243e7..16e31a62 100644
--- a/src/views/charts/Chart.vue
+++ b/src/views/charts/Chart.vue
@@ -49,16 +49,16 @@
- {{detailData.asn}}
- {{detailData.asSubnet}}
- {{detailData.isp}}
- {{detailData.dnsPTR}}
+ {{detailData ? detailData.asn : '-'}}
+ {{detailData.asSubnet || '-'}}
+ {{detailData.isp || '-'}}
+ {{detailData.dnsPTR || '-'}}
@@ -104,7 +104,52 @@
-
+
+
+
+
+
+
+
+
{{data.port || '-'}}
+
{{data.protocol || '-'}}
+
{{data.banner || '-'}}
+
{{data.utime || '-'}}
+
+
+
+
+
{{$t('overall.protocolsStatistics')}}
+
+
+
+
+
+
+
+
{{$t('overall.domain')}}
+
+
+
+
+
{{$t('entities.byType')}}
+
+
+
+
{{$t('entities.byCredit')}}
+
+
+
+
+
+
@@ -329,7 +374,7 @@ export default {
},
activeTab: '',
groupData: '', // group类型的查询数据,用于传递给子chart,子chart通过params.dataKey取值
- detailData: {}, // 详情类型图表的数据
+ detailData: '', // 详情类型图表的数据
statisticsData: [],
orderPieTable: chartPieTableTopOptions[0].value,
selectPieChartName: '',
@@ -357,8 +402,12 @@ export default {
}
// TODO 优化:缓存地图,重新查询时只更改数据,不再次初始化
} else if (this.isEcharts) {
- const dom = document.getElementById(`chart${this.chartInfo.id}`)
- !this.myChart && (this.myChart = echarts.init(dom))
+ if (this.isIpHostedDomain) {
+
+ } else {
+ const dom = document.getElementById(`chart${this.chartInfo.id}`)
+ !this.myChart && (this.myChart = echarts.init(dom))
+ }
this.chartOption = this.$_.cloneDeep(getOption(this.chart.type))
if (chartParams) {
if (this.isEchartsWithTable) {
@@ -369,6 +418,10 @@ export default {
this.initRelationShip(chartParams)
} else if (this.isSankey) {
this.initSankey(chartParams)
+ } else if (this.isIpOpenPort) {
+ this.initIpOpenPort(chartParams)
+ } else if (this.isIpHostedDomain) {
+ this.initIpHostedDomain(chartParams)
} else {
this.initECharts(chartParams)
}
@@ -549,139 +602,140 @@ export default {
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200 && !this.$_.isEmpty(response.data.result)) {
const data = response.data.result
- data.forEach(r => {
- const serverCountryCapital = r.serverId && getCapitalGeo(r.serverId)
- const clientCountryCapital = r.clientId && getCapitalGeo(r.clientId)
- serverCountryCapital && (r.serverLongitude = serverCountryCapital.capitalLongitude)
- serverCountryCapital && (r.serverLatitude = serverCountryCapital.capitalLatitude)
- clientCountryCapital && (r.clientLongitude = clientCountryCapital.capitalLongitude)
- clientCountryCapital && (r.clientLatitude = clientCountryCapital.capitalLatitude)
- })
- if (this.isMapLine) {
- const lineSeries = this.myChart.series.push(new am4Maps.MapLineSeries())
- const lineTemplate = lineSeries.mapLines.template
- lineTemplate.stroke = am4Core.color('#A258EC')
- lineTemplate.line.nonScalingStroke = true
- lineTemplate.line.strokeDasharray = '4 3'
- lineTemplate.nonScalingStroke = true
- lineTemplate.arrow.nonScaling = true
- lineTemplate.arrow.width = 4
- lineTemplate.arrow.height = 6
- lineSeries.data = [
- {
- multiGeoLine: data.map(d => {
- return [
- {
- latitude: parseFloat(d.serverLatitude),
- longitude: parseFloat(d.serverLongitude)
- },
- {
- latitude: parseFloat(d.clientLatitude),
- longitude: parseFloat(d.clientLongitude)
- }
- ]
- })
- }
- ]
- const imageSeries = this.myChart.series.push(new am4Maps.MapImageSeries())
- imageSeries.dataFields.value = 'sessions'
- const imageSeriesTemplate = imageSeries.mapImages.template
- const circle = imageSeriesTemplate.createChild(am4Core.Circle)
-
- circle.fillOpacity = 0.7
- circle.nonScaling = true
- circle.tooltipText = '{title}'
- const radiusHeat = imageSeries.heatRules.push({
- target: circle,
- property: 'radius',
- min: 8,
- max: 30
- })
- const colorHeat = imageSeries.heatRules.push({
- target: circle,
- property: 'fill',
- min: am4Core.color('#D2A8FF'),
- max: am4Core.color('#A258EC')
- })
- imageSeriesTemplate.propertyFields.latitude = 'latitude'
- imageSeriesTemplate.propertyFields.longitude = 'longitude'
-
- const pointData = []
- data.forEach(d => {
- pointData.push({
- ...d,
- latitude: parseFloat(d.serverLatitude),
- longitude: parseFloat(d.serverLongitude),
- title: this.getTitle(d)
- })
- pointData.push({
- ...d,
- latitude: parseFloat(d.clientLatitude),
- longitude: parseFloat(d.clientLongitude),
- title: this.getTitle(d)
- })
- })
- imageSeries.data = pointData
- } else if (this.isMapBlock) {
- const sumData = []
- data.forEach(r => {
- const hit = sumData.find(s => s.id === r.serverId)
- if (hit) {
- hit.value += Number(r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions) || 0
- } else {
- sumData.push({
- id: r.serverId,
- value: Number(r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions) || 0
- })
- }
- })
- const seriesData = sumData.map(r => {
- return {
- ...r,
- title: this.getTitle2(r, chartParams.valueColumn)
- }
- })
- polygonSeries.data = [...seriesData]
- const sorted = seriesData.sort((a, b) => b.value - a.value)
- const allZero = this.$_.isEmpty(sorted) || Number(sorted[0].value) === 0 // 数据全为0的情况,legend只显示1个颜色
-
- polygonSeries.heatRules.push({
- property: 'fill',
- target: polygonSeries.mapPolygons.template,
- min: am4Core.color('#EABA2B'),
- max: allZero ? am4Core.color('#EABA2B') : am4Core.color('#D95D41')
- })
-
- const heatLegend = this.myChart.createChild(HeatLegend)
- heatLegend.markerContainer.height = 6
- heatLegend.series = polygonSeries
- heatLegend.align = 'left'
- heatLegend.markerCount = allZero ? 1 : 3
- heatLegend.minValue = 0
- heatLegend.fontSize = 12
- heatLegend.maxValue = allZero ? 1 : Number(sorted[0].value)
- heatLegend.width = allZero ? am4Core.percent(10) : am4Core.percent(25)
- heatLegend.marginLeft = 15
- heatLegend.valign = 'bottom'
-
- const minRange = heatLegend.valueAxis.axisRanges.create()
- minRange.value = heatLegend.minValue
- minRange.label.text = minRange.value === 0 ? 0 : unitConvert(heatLegend.minValue, chartParams.unitType).join(' ')
- const maxRange = heatLegend.valueAxis.axisRanges.create()
- maxRange.value = heatLegend.maxValue
- maxRange.label.text = maxRange.value === 0 ? 0 : unitConvert(heatLegend.maxValue, chartParams.unitType).join(' ')
-
- heatLegend.valueAxis.renderer.labels.template.adapter.add('text', function (labelText) {
- return ''
- })
-
- const polygonTemplate = polygonSeries.mapPolygons.template
- polygonTemplate.tooltipText = '{name}{title}'
- polygonTemplate.nonScalingStroke = true
- polygonTemplate.strokeWidth = 0.5
- } else if (this.isIpBasicInfo) {
+ if (this.isIpBasicInfo) {
this.detailData = data
- console.info(1)
+ } else {
+ data.forEach(r => {
+ const serverCountryCapital = r.serverId && getCapitalGeo(r.serverId)
+ const clientCountryCapital = r.clientId && getCapitalGeo(r.clientId)
+ serverCountryCapital && (r.serverLongitude = serverCountryCapital.capitalLongitude)
+ serverCountryCapital && (r.serverLatitude = serverCountryCapital.capitalLatitude)
+ clientCountryCapital && (r.clientLongitude = clientCountryCapital.capitalLongitude)
+ clientCountryCapital && (r.clientLatitude = clientCountryCapital.capitalLatitude)
+ })
+ if (this.isMapLine) {
+ const lineSeries = this.myChart.series.push(new am4Maps.MapLineSeries())
+ const lineTemplate = lineSeries.mapLines.template
+ lineTemplate.stroke = am4Core.color('#A258EC')
+ lineTemplate.line.nonScalingStroke = true
+ lineTemplate.line.strokeDasharray = '4 3'
+ lineTemplate.nonScalingStroke = true
+ lineTemplate.arrow.nonScaling = true
+ lineTemplate.arrow.width = 4
+ lineTemplate.arrow.height = 6
+ lineSeries.data = [
+ {
+ multiGeoLine: data.map(d => {
+ return [
+ {
+ latitude: parseFloat(d.serverLatitude),
+ longitude: parseFloat(d.serverLongitude)
+ },
+ {
+ latitude: parseFloat(d.clientLatitude),
+ longitude: parseFloat(d.clientLongitude)
+ }
+ ]
+ })
+ }
+ ]
+ const imageSeries = this.myChart.series.push(new am4Maps.MapImageSeries())
+ imageSeries.dataFields.value = 'sessions'
+ const imageSeriesTemplate = imageSeries.mapImages.template
+ const circle = imageSeriesTemplate.createChild(am4Core.Circle)
+
+ circle.fillOpacity = 0.7
+ circle.nonScaling = true
+ circle.tooltipText = '{title}'
+ const radiusHeat = imageSeries.heatRules.push({
+ target: circle,
+ property: 'radius',
+ min: 8,
+ max: 30
+ })
+ const colorHeat = imageSeries.heatRules.push({
+ target: circle,
+ property: 'fill',
+ min: am4Core.color('#D2A8FF'),
+ max: am4Core.color('#A258EC')
+ })
+ imageSeriesTemplate.propertyFields.latitude = 'latitude'
+ imageSeriesTemplate.propertyFields.longitude = 'longitude'
+
+ const pointData = []
+ data.forEach(d => {
+ pointData.push({
+ ...d,
+ latitude: parseFloat(d.serverLatitude),
+ longitude: parseFloat(d.serverLongitude),
+ title: this.getTitle(d)
+ })
+ pointData.push({
+ ...d,
+ latitude: parseFloat(d.clientLatitude),
+ longitude: parseFloat(d.clientLongitude),
+ title: this.getTitle(d)
+ })
+ })
+ imageSeries.data = pointData
+ } else if (this.isMapBlock) {
+ const sumData = []
+ data.forEach(r => {
+ const hit = sumData.find(s => s.id === r.serverId)
+ if (hit) {
+ hit.value += Number(r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions) || 0
+ } else {
+ sumData.push({
+ id: r.serverId,
+ value: Number(r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions) || 0
+ })
+ }
+ })
+ const seriesData = sumData.map(r => {
+ return {
+ ...r,
+ title: this.getTitle2(r, chartParams.valueColumn)
+ }
+ })
+ polygonSeries.data = [...seriesData]
+ const sorted = seriesData.sort((a, b) => b.value - a.value)
+ const allZero = this.$_.isEmpty(sorted) || Number(sorted[0].value) === 0 // 数据全为0的情况,legend只显示1个颜色
+
+ polygonSeries.heatRules.push({
+ property: 'fill',
+ target: polygonSeries.mapPolygons.template,
+ min: am4Core.color('#EABA2B'),
+ max: allZero ? am4Core.color('#EABA2B') : am4Core.color('#D95D41')
+ })
+
+ const heatLegend = this.myChart.createChild(HeatLegend)
+ heatLegend.markerContainer.height = 6
+ heatLegend.series = polygonSeries
+ heatLegend.align = 'left'
+ heatLegend.markerCount = allZero ? 1 : 3
+ heatLegend.minValue = 0
+ heatLegend.fontSize = 12
+ heatLegend.maxValue = allZero ? 1 : Number(sorted[0].value)
+ heatLegend.width = allZero ? am4Core.percent(10) : am4Core.percent(25)
+ heatLegend.marginLeft = 15
+ heatLegend.valign = 'bottom'
+
+ const minRange = heatLegend.valueAxis.axisRanges.create()
+ minRange.value = heatLegend.minValue
+ minRange.label.text = minRange.value === 0 ? 0 : unitConvert(heatLegend.minValue, chartParams.unitType).join(' ')
+ const maxRange = heatLegend.valueAxis.axisRanges.create()
+ maxRange.value = heatLegend.maxValue
+ maxRange.label.text = maxRange.value === 0 ? 0 : unitConvert(heatLegend.maxValue, chartParams.unitType).join(' ')
+
+ heatLegend.valueAxis.renderer.labels.template.adapter.add('text', function (labelText) {
+ return ''
+ })
+
+ const polygonTemplate = polygonSeries.mapPolygons.template
+ polygonTemplate.tooltipText = '{name}{title}'
+ polygonTemplate.nonScalingStroke = true
+ polygonTemplate.strokeWidth = 0.5
+ }
}
} else if (response.code !== 200) {
this.isError = true
@@ -884,6 +938,110 @@ export default {
},
initSankey (chartParams) {
+ },
+ initIpOpenPort (chartParams) {
+ get(replaceUrlPlaceholder(chartParams.url, { ip: this.entity.ip })).then(response => {
+ if (response.code === 200) {
+ if (this.$_.isEmpty(response.data.result)) {
+ this.noData = true
+ } else {
+ this.noData = false
+ // this.detailData = response.data.result
+ this.detailData = [
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Http/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTP',
+ port: '80'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Http/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTP',
+ port: '81'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: ApacheServer: ApacheServer: ApacheServer: ApacheServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ },
+ {
+ utime: '2021-9-17 13:53:37',
+ banner: 'Https/1.1 200 OK\r\nDate:2021-9-17 13:53:03\r\nServer: Apache',
+ protocol: 'HTTPS',
+ port: '82'
+ }
+ ]
+ const protocols = []
+ this.detailData.forEach((d, i) => {
+ const index = protocols.findIndex(p => p.name === d.protocol.toUpperCase())
+ if (index === -1) {
+ protocols.push({ name: d.protocol.toUpperCase(), value: 1, itemStyle: { color: getChartColor(i) } })
+ } else {
+ protocols[index].value++
+ }
+ })
+ this.chartOption.series[0].data = protocols
+ this.chartOption.xAxis.data = protocols.map(p => p.name)
+ this.myChart.setOption(this.chartOption)
+ }
+ }
+ }).finally(() => {
+ setTimeout(() => {
+ this.loading = false
+ this.$nextTick(() => {
+ this.echartsResize()
+ })
+ }, 250)
+ })
+ },
+ initIpHostedDomain (chartParams) {
+ get(replaceUrlPlaceholder(chartParams.url, { ip: this.entity.ip })).then(response => {
+ if (response.code === 200) {
+ if (this.$_.isEmpty(response.data.result)) {
+ this.noData = true
+ } else {
+ this.noData = false
+ // this.detailData = response.data.result
+ this.detailData = ['www.abcdefghijklmnopqrstuvwxyz.com', 'qq.com', 'baidu.com', 'alimama.com', 'google.com']
+ }
+ }
+ }).finally(() => {
+ setTimeout(() => {
+ this.loading = false
+ }, 250)
+ })
},
initEchartsWithStatistics (chartParams) {
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
@@ -1090,14 +1248,16 @@ export default {
},
location () {
let location = ''
- if (this.detailData.country) {
- location = this.detailData.country
- if (this.detailData.region) {
- location += ', '
- location += this.detailData.region
+ if (this.detailData) {
+ if (this.detailData.country) {
+ location = this.detailData.country
+ if (this.detailData.region) {
+ location += ', '
+ location += this.detailData.region
+ }
+ } else if (this.detailData.region) {
+ location = this.detailData.region
}
- } else if (this.detailData.region) {
- location = this.detailData.region
}
return location
},
@@ -1180,3 +1340,99 @@ export default {
}
}
+