CN-988: 实体详情--安全事件和性能事件tab开发
This commit is contained in:
@@ -107,6 +107,9 @@
|
||||
letter-spacing: 0;
|
||||
line-height: 14px;
|
||||
margin-left: 5px;
|
||||
font-style: italic;
|
||||
padding: 0 2px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.detection-event-severity-color-block {
|
||||
width: 5px;
|
||||
@@ -143,12 +146,12 @@
|
||||
flex-wrap: wrap;
|
||||
|
||||
.basic-info__item {
|
||||
padding-right: 40px;
|
||||
padding-right: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
padding-right: 6px;
|
||||
padding-right: 5px;
|
||||
color: #8FA1BE;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
flex-wrap: wrap;
|
||||
|
||||
.basic-info__item {
|
||||
padding-right: 40px;
|
||||
padding-right: 30px;
|
||||
|
||||
.item__box {
|
||||
display: flex;
|
||||
@@ -105,7 +105,7 @@
|
||||
}
|
||||
|
||||
i {
|
||||
padding-right: 6px;
|
||||
padding-right: 5px;
|
||||
color: #8FA1BE;
|
||||
font-size: 12px;
|
||||
height: 13px;
|
||||
|
||||
@@ -66,6 +66,84 @@ if (openMock) {
|
||||
}
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'interface/entityDetail/securityEvent.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
eventId: '1298414830886991872',
|
||||
securityType: 'command and control',
|
||||
domain: null,
|
||||
offenderIp: '213.186.33.5',
|
||||
victimIp: '116.178.217.92',
|
||||
offenderDomain: 'baidu.com',
|
||||
victimDomain: 'mi.com',
|
||||
eventSeverity: 'Critical',
|
||||
malwareName: 'NetWire RC',
|
||||
cryptominingPool: null,
|
||||
startTime: 1683186600,
|
||||
durationMs: 300000,
|
||||
endTime: 1683186900
|
||||
},
|
||||
{
|
||||
eventId: '1298414830886991873',
|
||||
securityType: 'command and control',
|
||||
domain: null,
|
||||
offenderIp: '213.186.33.5',
|
||||
victimIp: '116.178.217.93',
|
||||
offenderDomain: 'baidu.com',
|
||||
victimDomain: 'mi.com',
|
||||
eventSeverity: 'Low',
|
||||
malwareName: 'NetWire RC',
|
||||
cryptominingPool: null,
|
||||
startTime: 1683186600,
|
||||
durationMs: 300000,
|
||||
endTime: 1683186900
|
||||
}
|
||||
]
|
||||
|
||||
return {
|
||||
msg: 'success',
|
||||
code: 200,
|
||||
data: {
|
||||
result: result
|
||||
}
|
||||
}
|
||||
})
|
||||
Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'interface/entityDetail/performanceEvent.*'), 'get', function (requestObj) {
|
||||
const result = [
|
||||
{
|
||||
eventId: '1308078720390412288',
|
||||
entityType: 'ip',
|
||||
serverIp: '116.178.78.180',
|
||||
domain: null,
|
||||
appName: null,
|
||||
eventSeverity: 'Critical',
|
||||
eventType: 'Http error',
|
||||
startTime: 1683250500,
|
||||
durationMs: 900000,
|
||||
endTime: 1683251400
|
||||
},
|
||||
{
|
||||
eventId: '1308078720390412289',
|
||||
entityType: 'ip',
|
||||
serverIp: '116.178.78.180',
|
||||
domain: null,
|
||||
appName: null,
|
||||
eventSeverity: 'Info',
|
||||
eventType: 'Http error',
|
||||
startTime: 1683250500,
|
||||
durationMs: 900000,
|
||||
endTime: 1683251400
|
||||
}
|
||||
]
|
||||
|
||||
return {
|
||||
msg: 'success',
|
||||
code: 200,
|
||||
data: {
|
||||
result: result
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getQuery = (url) => {
|
||||
|
||||
@@ -234,7 +234,9 @@ export const api = {
|
||||
drilldownTrafficAnalysis: '/interface/dns/overview/drilldown/trafficAnalysis'
|
||||
},
|
||||
entity: {
|
||||
totalTrafficAnalysis: 'interface/entityDetail/totalTrafficAnalysis'
|
||||
totalTrafficAnalysis: 'interface/entityDetail/totalTrafficAnalysis',
|
||||
securityEvent: 'interface/entityDetail/securityEvent',
|
||||
performanceEvent: 'interface/entityDetail/performanceEvent'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +165,13 @@ export const eventSeverityColor = {
|
||||
low: '#FFD82D',
|
||||
info: '#D1BD50'
|
||||
}
|
||||
export const eventSeverityColor1 = {
|
||||
Critical: '#D84C4C',
|
||||
High: '#FE845D',
|
||||
Medium: '#FFB65A',
|
||||
Low: '#FFD82D',
|
||||
Info: '#D1BD50'
|
||||
}
|
||||
export const securityType = {
|
||||
commandAndControl: 'common and control',
|
||||
payloadDelivery: 'payload delivery',
|
||||
|
||||
160
src/views/charts2/charts/entityDetail/tabs/PerformanceEvent.vue
Normal file
160
src/views/charts2/charts/entityDetail/tabs/PerformanceEvent.vue
Normal file
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
|
||||
<div
|
||||
class="detection-border"
|
||||
v-for="item in eventList"
|
||||
:key="item.eventId"
|
||||
style="margin-bottom: 10px;">
|
||||
<div class="cn-detection--list">
|
||||
<div class="cn-detection__case" style="height: 46px">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor1[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header">
|
||||
<span
|
||||
class="detection-event-severity-color-block"
|
||||
:style="`background-color: ${eventSeverityColor1[item.eventSeverity]}`">
|
||||
</span>
|
||||
<span class="detection-event-severity-block" style="margin-right: 30px">
|
||||
{{ item.eventType || '-' }}
|
||||
</span>
|
||||
<div class="cn-detection__body">
|
||||
<div class="body__basic-info">
|
||||
<div class="basic-info">
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span>{{ item.eventSeverity || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
<span>{{ $t('detection.list.startTime') }} : </span>
|
||||
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-Duration"></i>
|
||||
<span>{{ $t('overall.duration') }} : </span>
|
||||
<span>{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMillisecond, getNowTime, getSecond } from '@/utils/date-util'
|
||||
import { eventSeverityColor1 } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from '_axios@0.21.4@axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
|
||||
export default {
|
||||
name: 'PerformanceEvent',
|
||||
mixins: [chartMixin],
|
||||
data () {
|
||||
return {
|
||||
eventList: [],
|
||||
showError: false,
|
||||
eventSeverityColor1
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
const { query } = useRoute()
|
||||
// 获取url携带的range、startTime、endTime
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const {
|
||||
startTime,
|
||||
endTime
|
||||
} = getNowTime(60)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
} else {
|
||||
timeFilter.value.startTime = parseInt(startTimeParam)
|
||||
timeFilter.value.endTime = parseInt(endTimeParam)
|
||||
}
|
||||
|
||||
return {
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initData()
|
||||
},
|
||||
computed: {
|
||||
pointColor () {
|
||||
return function (item) {
|
||||
let color = '#8FA1BE'
|
||||
if (item.startTime && item.endTime) {
|
||||
if (getMillisecond(item.endTime) - getMillisecond(item.startTime) < 5 * 60 * 1000) {
|
||||
color = '#D84C4C'
|
||||
}
|
||||
}
|
||||
return { backgroundColor: color }
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
initData () {
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
this.toggleLoading(true)
|
||||
axios.get(api.entity.performanceEvent, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (res.code === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
this.eventList = res.data.result
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
},
|
||||
httpError (e) {
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detection-border {
|
||||
border: 1px solid #E2E5EC;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.basic-info__item-duration {
|
||||
display: inline-block;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
184
src/views/charts2/charts/entityDetail/tabs/SecurityEvent.vue
Normal file
184
src/views/charts2/charts/entityDetail/tabs/SecurityEvent.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
|
||||
<div
|
||||
class="detection-border"
|
||||
v-for="item in eventList"
|
||||
:key="item.eventId"
|
||||
style="margin-bottom: 10px;">
|
||||
<div class="cn-detection--list">
|
||||
<div class="cn-detection__case" style="height: 70px">
|
||||
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor1[item.eventSecurity]}`"></div>
|
||||
<div class="cn-detection__row">
|
||||
<div class="cn-detection__header">
|
||||
<span
|
||||
class="detection-event-severity-color-block"
|
||||
:style="`background-color: ${eventSeverityColor1[item.eventSeverity]}`">
|
||||
</span>
|
||||
<span class="detection-event-severity-block">{{ item.securityType || '-' }}</span>
|
||||
<i class="cn-icon cn-icon-attacker"></i>{{ item.offenderIp || '-' }}
|
||||
<div class="domain">{{ item.offenderDomain }}</div>
|
||||
<span class="line">-------</span>
|
||||
<span class="circle"></span>
|
||||
<i class="cn-icon cn-icon-attacked"></i>{{ item.victimIp || '-' }}
|
||||
<div class="domain">{{ item.victimDomain }}</div>
|
||||
</div>
|
||||
<div class="cn-detection__body">
|
||||
<div class="body__basic-info">
|
||||
<div class="basic-info">
|
||||
<div class="basic-info__item" v-if="item.eventSecurity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('detection.list.eventSecurity') }} : </span>
|
||||
<span>{{ item.eventSecurity || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.eventSeverity">
|
||||
<i class="cn-icon cn-icon-severity-level"></i>
|
||||
<span>{{ $t('network.severity') }} : </span>
|
||||
<span>{{ item.eventSeverity || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.eventType">
|
||||
<i class="cn-icon cn-icon-event-type"></i>
|
||||
<span>{{ $t('detections.eventType') }} : </span>
|
||||
<span>{{ item.eventType || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.malwareName">
|
||||
<i class="cn-icon cn-icon-trojan"></i>
|
||||
<span>{{ $t('detection.list.malwareName') }} : </span>
|
||||
<span>{{ item.malwareName || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item" v-if="item.cryptominingPool">
|
||||
<i class="cn-icon cn-icon-mining-pool"></i>
|
||||
<span>{{ $t('detection.list.cryptominingPool') }} : </span>
|
||||
<span>{{ item.cryptominingPool || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
<span>{{ $t('detection.list.startTime') }} : </span>
|
||||
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-Duration"></i>
|
||||
<span>{{ $t('overall.duration') }} : </span>
|
||||
<span>{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMillisecond, getNowTime, getSecond } from '@/utils/date-util'
|
||||
import { eventSeverityColor1 } from '@/utils/constants'
|
||||
import unitConvert from '@/utils/unit-convert'
|
||||
import axios from '_axios@0.21.4@axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import chartMixin from '@/views/charts2/chart-mixin'
|
||||
|
||||
export default {
|
||||
name: 'SecurityEvent',
|
||||
mixins: [chartMixin],
|
||||
data () {
|
||||
return {
|
||||
eventList: [],
|
||||
showError: false,
|
||||
eventSeverityColor1
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
const { query } = useRoute()
|
||||
// 获取url携带的range、startTime、endTime
|
||||
const rangeParam = query.range
|
||||
const startTimeParam = query.startTime
|
||||
const endTimeParam = query.endTime
|
||||
// 若url携带了,使用携带的值,否则使用默认值。
|
||||
|
||||
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
|
||||
const timeFilter = ref({ dateRangeValue })
|
||||
if (!startTimeParam || !endTimeParam) {
|
||||
const {
|
||||
startTime,
|
||||
endTime
|
||||
} = getNowTime(60)
|
||||
timeFilter.value.startTime = startTime
|
||||
timeFilter.value.endTime = endTime
|
||||
} else {
|
||||
timeFilter.value.startTime = parseInt(startTimeParam)
|
||||
timeFilter.value.endTime = parseInt(endTimeParam)
|
||||
}
|
||||
|
||||
return {
|
||||
timeFilter
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initData()
|
||||
},
|
||||
computed: {
|
||||
pointColor () {
|
||||
return function (item) {
|
||||
let color = '#8FA1BE'
|
||||
if (item.startTime && item.endTime) {
|
||||
if (getMillisecond(item.endTime) - getMillisecond(item.startTime) < 5 * 60 * 1000) {
|
||||
color = '#D84C4C'
|
||||
}
|
||||
}
|
||||
return { backgroundColor: color }
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unitConvert,
|
||||
initData () {
|
||||
const params = {
|
||||
startTime: getSecond(this.timeFilter.startTime),
|
||||
endTime: getSecond(this.timeFilter.endTime)
|
||||
}
|
||||
|
||||
this.toggleLoading(true)
|
||||
axios.get(api.entity.securityEvent, { params: params }).then(response => {
|
||||
const res = response.data
|
||||
|
||||
if (res.code === 200) {
|
||||
this.isNoData = res.data.result.length === 0
|
||||
this.showError = false
|
||||
if (!this.isNoData) {
|
||||
this.eventList = res.data.result
|
||||
}
|
||||
} else {
|
||||
this.httpError(res)
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.httpError(e)
|
||||
}).finally(() => {
|
||||
this.toggleLoading(false)
|
||||
})
|
||||
},
|
||||
httpError (e) {
|
||||
this.isNoData = false
|
||||
this.showError = true
|
||||
this.errorMsg = this.errorMsgHandler(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detection-border {
|
||||
border: 1px solid #E2E5EC;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.basic-info__item-duration {
|
||||
display: inline-block;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -61,7 +61,7 @@
|
||||
<span>{{dateFormatByAppearance(detection.startTime) || '-'}}</span>
|
||||
</div>
|
||||
<div class="basic-info__item">
|
||||
<i class="cn-icon cn-icon-time2"></i>
|
||||
<i class="cn-icon cn-icon-Duration"></i>
|
||||
<span>{{$t('overall.duration')}} : </span>
|
||||
<span style="display: inline-block;height: 6px;width: 6px;border-radius: 50%;margin-right: 8px;"
|
||||
:style="pointColor(detection)"></span>
|
||||
|
||||
Reference in New Issue
Block a user