diff --git a/src/assets/css/components/views/detections/detections.scss b/src/assets/css/components/views/detections/detections.scss
index e536d61a..bcf81f8a 100644
--- a/src/assets/css/components/views/detections/detections.scss
+++ b/src/assets/css/components/views/detections/detections.scss
@@ -15,5 +15,44 @@
margin-bottom: 10px;
width: 100%;
background-color: white;
+
+ .chart-header {
+ display: flex;
+ justify-content:space-between;
+ align-items:center;
+ padding: 10px 20px 10px 0px;
+ height: 40px;
+
+ font-size: 14px;
+ color: $--color-text-primary;
+ transition: all 0.2s;
+ border-bottom: 1px solid #E7EAED;
+
+ .chart-header__title {
+ max-width: calc(100% - 100px);
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+ }
+
+ .chart-content {
+ height:calc(100% - 40px);
+ }
+ .statistics__severity {
+ width: 33%;
+ margin-left:15px;
+ margin-right:15px;
+ }
+ .statistics__category {
+ width: 34%;
+ margin-left:15px;
+ margin-right:15px;
+ }
+ .statistics__active-attack {
+ width: 33%;
+ margin-left:15px;
+ margin-right:15px;
+ }
}
}
diff --git a/src/utils/api.js b/src/utils/api.js
index 75160ba7..15e2e317 100644
--- a/src/utils/api.js
+++ b/src/utils/api.js
@@ -72,7 +72,12 @@ export const api = {
entityIpDetailAlert: '/interface/entity/detail/overview/ip/alert',
entityIpDetailSecurity: '/interface/entity/detail/overview/ip/security',
entityIpRelatedServerDomain: '/interface/entity/detail/overview/ip/relatedDomain',
- entityIpRelatedServerApp: '/interface/entity/detail/overview/ip/relatedApp'
+ entityIpRelatedServerApp: '/interface/entity/detail/overview/ip/relatedApp',
+ //detection
+ detectionEventSeverity:'/interface/detection/filter/severity',
+ detectionAttackType:'/interface/detection/filter/attackType',
+ detectionOffenderIp:'/interface/detection/filter/offenderIp',
+ detectionSeverity:'/interface/detection/filter/severity',
}
/* panel */
diff --git a/src/views/detections/Index.vue b/src/views/detections/Index.vue
index c0554727..a1822e71 100644
--- a/src/views/detections/Index.vue
+++ b/src/views/detections/Index.vue
@@ -14,8 +14,8 @@
>
-
-
+
+
-
+
{
+ response = {
+ "code": 200,
+ "success": true,
+ "msg": "OK",
+ "data": {
+ "resultType": "table",
+ "result": [
+ {
+ "eventSeverity": "Critical",
+ "count": 1048
+ },{
+ "eventSeverity": "High",
+ "count": 735
+ },{
+ "eventSeverity": "Medium",
+ "count": 580
+ },{
+ "eventSeverity": "Low",
+ "count": 484
+ },{
+ "eventSeverity": "Info",
+ "count": 300
+ }
+ ]
+ }
+ }
+ if (response.code === 200) {
+ this.eventSeverityData = [
+ ['time', '2021/11/12 12:00', '2021/11/13 12:00', '2021/11/14 12:00', '2021/11/15 12:00','2021/11/16 12:00','2021/11/17 12:00','2021/11/18 12:00','2021/11/19 12:00','2021/11/20 12:00','2021/11/21 12:00'],
+ ['Critical', 5, 0, 2, 0,0,3,3,0,4,2],
+ ['High', 5, 7, 3,0, 0,3,3,20,2,2],
+ ['Medium', 10, 8, 20, 8,0,5,5,5,5,20],
+ ['Low', 18, 6, 5, 0,0,2,20,2,2,2],
+ ['Info', 16, 10, 4, 0,7,0,8,5,15,1]
+ ]
+ }
+ }).finally(() => {
+ this.$nextTick(() => {
+ this.eventSeverityOption.dataset.source = this.eventSeverityData
+ this.eventSeverityOption && detectionChart.setOption(this.eventSeverityOption);
+ })
+ })
+ },
+ initSeverityPerData(){
+ const chartDom = document.getElementById('detectionSeverityPer');
+ const detectionChart = echarts.init(chartDom);
+ this.severityPerOption = this.$_.cloneDeep(pieForSeverity)
+
+ const queryParams = {
+ ...this.timeFilter,
+ q:this.q,
+ }
+ get(api.detectionSeverity, queryParams).then(response => {
+ response = {
+ "code": 200,
+ "success": true,
+ "msg": "OK",
+ "data": {
+ "resultType": "table",
+ "result": [
+ {
+ "eventSeverity": "Critical",
+ "count": 1048
+ },{
+ "eventSeverity": "High",
+ "count": 735
+ },{
+ "eventSeverity": "Medium",
+ "count": 580
+ },{
+ "eventSeverity": "Low",
+ "count": 484
+ },{
+ "eventSeverity": "Info",
+ "count": 300
+ }
+ ]
+ }
+ }
+ if (response.code === 200) {
+ this.severityPerData = response.data.result.map(d => {
+ return { value: d.count, name: d.eventSeverity,itemStyle:{color:getSeverityColor(d.eventSeverity)}}
+ })
+ }
+ }).finally(() => {
+ this.$nextTick(() => {
+ this.severityPerOption.series[0].data = this.severityPerData
+ this.severityPerOption && detectionChart.setOption(this.severityPerOption);
+ })
+ })
+ },
+ initCategoryPerData(){
+ const chartDom = document.getElementById('detectionCategoryPer');
+ const detectionChart = echarts.init(chartDom);
+ this.categoryPerOption = this.$_.cloneDeep(pieForSeverity)
+
+ const queryParams = {
+ ...this.timeFilter,
+ q:this.q,
+ }
+ get(api.detectionAttackType, queryParams).then(response => {
+ response = {
+ "code": 200,
+ "success": true,
+ "msg": "OK",
+ "data": {
+ "resultType": "table",
+ "result": [
+ {
+ "attackType": "Command and control",
+ "count": 1048
+ },{
+ "attackType": "Payload_delivery",
+ "count": 735
+ },{
+ "attackType": "Cryptomining",
+ "count": 580
+ },{
+ "attackType": "phishing",
+ "count": 484
+ },{
+ "attackType": "dga",
+ "count": 300
+ },{
+ "attackType": "ddos",
+ "count": 50
+ },
+ ]
+ }
+ }
+ if (response.code === 200) {
+ this.categoryPerData = response.data.result.map(d => {
+ return { value: d.count, name: d.attackType,itemStyle:{color:getAttackColor(d.attackType)}}
+ })
+ }
+ }).finally(() => {
+ this.$nextTick(() => {
+ this.categoryPerOption.series[0].data = this.categoryPerData
+ this.categoryPerOption && detectionChart.setOption(this.categoryPerOption);
+ })
+ })
+
+ },
+ initActiveAttackData(){
+ const chartDom = document.getElementById('detectionActiveAttacker');
+ const detectionChart = echarts.init(chartDom);
+ this.activeAttackOption = this.$_.cloneDeep(activeAttackBar)
+
+ const queryParams = {
+ ...this.timeFilter,
+ q:this.q,
+ }
+ get(api.detectionOffenderIp, queryParams).then(response => {
+ response = {
+ "code": 200,
+ "success": true,
+ "msg": "OK",
+ "data": {
+ "resultType": "table",
+ "result": [
+ {
+ "offenderIp": "192.168.12.21",
+ "count": 99999
+ },{
+ "offenderIp": "192.168.22.21",
+ "count": 88888
+ },{
+ "offenderIp": "192.168.32.21",
+ "count": 77777
+ },{
+ "offenderIp": "192.168.42.21",
+ "count": 66666
+ },{
+ "offenderIp": "192.168.52.21",
+ "count": 55555
+ }
+ ]
+ }
+ }
+ if (response.code === 200) {
+ this.activeAttackData = response.data.result.map(d => {
+ return [d.count,d.offenderIp]
+ })
+ }
+ }).finally(() => {
+ this.$nextTick(() => {
+ this.activeAttackOption.series[0].data = this.activeAttackData.reverse()
+ this.activeAttackOption && detectionChart.setOption(this.activeAttackOption);
+ })
+ })
+ },
timeRefreshChange () {
if (!this.$refs.dateTimeRange.isCustom) {
const value = this.timeFilter.dateRangeValue
@@ -226,6 +464,12 @@ export default {
})
}
},
+ mounted () {
+ this.initEventSeverityData()
+ this.initSeverityPerData()
+ this.initCategoryPerData()
+ this.initActiveAttackData()
+ },
watch: {
timeFilter (n) {
this.search(this.metaList, this.q)
@@ -236,7 +480,23 @@ export default {
const { startTime, endTime } = getNowTime(dateRangeValue)
const timeFilter = ref({ startTime, endTime, dateRangeValue })
return {
- timeFilter
+ timeFilter,
+ severityPerChart: {
+ params: {
+ url: '/interface/entity/detail/ip/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&ip={{ip}}',
+ unitType: 'number'
+ },
+ id: 'detectionSeverityPer',
+ type: 32
+ },
+ categoryPerChart: {
+ params: {
+ url: '/interface/entity/detail/ip/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&ip={{ip}}',
+ unitType: 'number'
+ },
+ id: 'detectionCategoryPer',
+ type: 32
+ }
}
}
}
diff --git a/src/views/detections/options/detectionOptions.js b/src/views/detections/options/detectionOptions.js
new file mode 100644
index 00000000..cb18368e
--- /dev/null
+++ b/src/views/detections/options/detectionOptions.js
@@ -0,0 +1,249 @@
+import {
+ chartColor,
+ tooLongFormatter
+} from '@/views/charts/charts/tools'
+import unitConvert from '@/utils/unit-convert'
+import _ from 'lodash'
+
+const activeAttackColorMappings = [
+ { value: 'Command and control', color: '#51a9ee' },
+ { value: 'Payload_delivery', color: '#49bcf2' },
+ { value: 'Cryptomining', color: '#4ad7eb' },
+ { value: 'phishing', color: '#4cd4c8' },
+ { value: 'dga', color: '#7acc7e' },
+ { value: 'ddos', color: '#a7db69' }
+]
+
+export function getAttackColor (type) {
+ const mapping = activeAttackColorMappings.find(m => m.value === type)
+ return mapping && mapping.color ? _.cloneDeep(mapping.color) : null
+}
+
+const severityColorMappings = [
+ { value: 'Critical', color: '#d84c4c' },
+ { value: 'High', color: '#ff9a79' },
+ { value: 'Medium', color: '#ffb65a' },
+ { value: 'Low', color: '#ffdd4a' },
+ { value: 'Info', color: '#d7c668' }
+]
+
+export function getSeverityColor (type) {
+ const mapping = severityColorMappings.find(m => m.value === type)
+ return mapping && mapping.color ? _.cloneDeep(mapping.color) : null
+}
+
+export const multipleBarOption = {
+ legend: {
+ icon: 'circle',
+ top: 10,
+ right:10,
+ itemWidth: 10, // 设置宽度
+ itemHeight: 10, // 设置高度
+ },
+ tooltip: {},
+ dataset: {
+ source: [
+ ]
+ },
+ xAxis: {
+ type: 'category',
+ axisTick: { show: false },
+ axisLine: {
+ show: true ,
+ lineStyle :{
+ color:'#eaedef'
+ }
+ },
+ axisLabel:{
+ color:'#737373',
+ interval: 0
+ },
+ splitLine:{
+ show:false
+ },
+ },
+ yAxis: {
+ axisTick: { show: false },
+ axisLine: {
+ show: true,
+ lineStyle :{
+ color:'#eaedef'
+ }
+ },
+ axisLabel:{
+ color:'#737373'
+ },
+ splitLine:{
+ show:false
+ },
+ interval:10
+ },
+ grid: {
+ top: 40,
+ left: 25,
+ right: 25,
+ bottom: 20,
+ containLabel: true
+ },
+ series: [
+ {
+ name: 'Critical',
+ type: 'bar',
+ barWidth: 15,
+ seriesLayoutBy: 'row',
+ itemStyle: {
+ color: '#d84c4c'
+ }
+ },
+ {
+ name: 'High',
+ type: 'bar',
+ barWidth: 15,
+ seriesLayoutBy: 'row',
+ itemStyle: {
+ color: '#ff9a79'
+ }
+ },
+ {
+ name: 'Medium',
+ type: 'bar',
+ barWidth: 15,
+ seriesLayoutBy: 'row',
+ itemStyle: {
+ color: '#ffb65a'
+ }
+ },
+ {
+ name: 'Low',
+ type: 'bar',
+ barWidth: 15,
+ seriesLayoutBy: 'row',
+ itemStyle: {
+ color: '#ffdd4a'
+ }
+ },
+ {
+ name: 'Info',
+ type: 'bar',
+ barWidth: 15,
+ seriesLayoutBy: 'row',
+ itemStyle: {
+ color: '#d7c668'
+ }
+ },
+ ]
+};
+export const pieForSeverity = {
+ tooltip: {
+ appendToBody: true
+ },
+ color: chartColor,
+ animation: false,
+ legend: {
+ orient: 'vertical',
+ type: 'plain',
+ left: '60%',
+ top: 'middle',
+ icon: 'circle',
+ itemWidth: 10, // 设置宽度
+ itemHeight: 10, // 设置高度
+ itemGap: 10,
+ formatter: tooLongFormatter,
+ tooltip: {
+ show: true
+ }
+ },
+ series: [
+ {
+ type: 'pie',
+ selectedMode: 'single',
+ radius: ['42%', '65%'],
+ center: ['30%', '50%'],
+ data: [],
+ label: {
+ show:false
+ },
+ labelLine:{
+ show:false
+ },
+ tooltip: {
+ formatter: function (param, index, callback) {
+ return `${param.name}: ${param.value}`
+ }
+ },
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowOffsetX: 0,
+ shadowColor: 'rgba(0, 0, 0, 0.5)'
+ }
+ }
+ }
+ ]
+}
+export const activeAttackBar = {
+ tooltip: {
+ appendToBody: true,
+ trigger: 'item',
+ textStyle: {
+ width: '20px',
+ overflow: 'truncate'
+ },
+ formatter: function (param, index, callback) {
+ return `${param.name}: ${unitConvert(param.value[0], 'number').join(' ')}`
+ },
+ show: true,
+ className: 'nz-chart-tooltip',
+ extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
+ },
+ xAxis: {
+ axisTick: { show: false },
+ axisLine: { show: false },
+ axisLabel:{
+ show:false
+ },
+ splitLine:{
+ show:false
+ },
+ },
+ grid: {
+ top: 20,
+ left: 10,
+ right: 25,
+ bottom: 20,
+ containLabel: true
+ },
+ yAxis: {
+ type: 'category',
+ axisTick: { show: false },
+ axisLine: { show: false },
+ axisLabel:{
+ show:true
+ },
+ splitLine:{
+ show:false
+ },
+ },
+ series: [{
+ barWidth: 5,
+ itemStyle: {
+ normal: {
+ color: function(params) {
+ const colorList = ['#d1bd50','#c9d150','#fddd52','#ffb65a','#fe845d'];
+ return colorList[params.dataIndex]
+ }
+ }
+ },
+ data: [],
+ type: 'bar',
+ label: {
+ show: true,
+ position: 'right',
+ valueAnimation: true,
+ formatter: function (param, index, callback) {
+ return `${unitConvert(param.value[0], 'number').join(' ')}`
+ },
+ },
+ barCategoryGap: '10%'
+ }]
+}
\ No newline at end of file