CN-240 feat: 实体列表、详情细节完善

This commit is contained in:
chenjinsong
2022-01-05 23:56:27 +08:00
parent db27d6213f
commit 6e9b208818
24 changed files with 403 additions and 174 deletions

View File

@@ -7,6 +7,20 @@
justify-content: center;
transition: all linear .2s;
.entity__loading {
position: absolute;
height: 100%;
width: 100%;
z-index: 1;
i {
position: absolute;
left: calc(50% - 15px);
top: calc(50% - 15px);
font-size: 30px;
color: #aaa;
}
}
&.entity-explorer--show-list {
background-color: unset;
justify-content: flex-start;

View File

@@ -4,6 +4,22 @@
flex-direction: column;
padding: 0 30px;
.overview-map {
position: absolute;
right: 50px;
top: 0;
width: 550px;
height: 350px;
.cn-chart__map {
height: 100%;
width: 100%;
.chart-drawing {
height: 100%;
width: 100%;
}
}
}
.overview-item {
display: flex;
flex-direction: column;
@@ -84,7 +100,8 @@
}
}
.row__charts-msg {
width:105px;
width: auto;
padding-right: 20px;
}
.row__charts {
height: 20px;

View File

@@ -1,6 +1,7 @@
.entity-list {
width: calc(100% - 290px);
flex: 1;
position: relative;
.entity-list__content {
height: 100%;
@@ -29,6 +30,7 @@
width: 100vw;
left: 0;
top: 0;
z-index: 1;
background-color: rgba(0, 0, 0, .2);
}
}

View File

@@ -10,7 +10,7 @@
<div class="search__suffix">
<i class="cn-icon cn-icon-search-advance"></i>
</div>
<div class="search__suffix" @click="search">
<div class="search__suffix" @click="enter">
<i class="el-icon-search"></i>
</div>
</div>
@@ -18,7 +18,7 @@
<div class="search__suffix">
<i class="cn-icon cn-icon-search-advance"></i>
</div>
<div class="search__suffix" @click="search">
<div class="search__suffix" @click="enter">
<i class="el-icon-search"></i>
</div>
</div>
@@ -28,6 +28,7 @@
</template>
<script>
import { objToStr, strToObj } from '@/utils/tools'
export default {
name: 'AdvancedSearch',
props: {
@@ -54,31 +55,10 @@ export default {
addParams (params) {
if (params) {
const newParams = { ...this.searchParams, ...params }
this.searchContentTemp = this.objToStr(newParams)
this.searchContentTemp = objToStr(newParams)
this.enter()
}
},
strToObj (n) {
const paramsArr = n.split(/\s[aA][nN][dD]\s/)
const paramsObj = {}
paramsArr.forEach(string => {
const param = string.split('=')
if (param.length > 1) {
let value = param[1].trim()
const valueArr = value.split('"')
if (valueArr.length > 2) {
value = valueArr[1].trim()
}
paramsObj[param[0].trim()] = value
}
})
return paramsObj
},
objToStr (obj) {
return Object.keys(obj).map(k => {
return `${k}="${obj[k]}"`
}).join(' AND ')
},
enter () {
if (!this.searchContentTemp) {
this.reset()
@@ -90,7 +70,7 @@ export default {
watch: {
searchContent (n) {
if (n) {
this.searchParams = this.strToObj(n)
this.searchParams = strToObj(n)
} else {
this.reset()
}

View File

@@ -2,10 +2,10 @@
<div class="cn-chart cn-chart__map">
<div class="cn-chart__header chart-header-position" >
<slot name="chartErrorInfo"></slot>
<div class="header__title" >
<div class="header__title" v-if="!hideHeader">
<slot name="title"></slot>
</div>
<div class="header__operations">
<div class="header__operations" v-if="!hideHeader">
<slot name="operations"></slot>
</div>
</div>
@@ -26,7 +26,8 @@
export default {
name: 'ChartMap',
props: {
loading: Boolean
loading: Boolean,
hideHeader: Boolean
},
data () {
return {

View File

@@ -15,7 +15,7 @@
<slot name="title"></slot>
</div>
</div>
<div class="single-value__content single-value__content--with-chart" v-if="type === 52">
<div class="single-value__content single-value__content--with-chart" v-if="type === 52 || type === 55">
<div class="content__title">
<slot name="title"></slot>
</div>
@@ -72,6 +72,7 @@ export default {
c = 'cn-chart__single-value--icon-left'
break
}
case 55:
case 52: {
c = 'cn-chart__single-value--chart'
break

View File

@@ -652,6 +652,10 @@ export function isSingleValue (type) {
export function isSingleValueWithEcharts (type) {
return type === 52
}
/* 带折线图的单值 */
export function isSingleValueWithEchartsTemp (type) {
return type === 55
}
/* 带Table的饼图 */
export function isEchartsWithTable (type) {
return type === 31

View File

@@ -226,7 +226,7 @@ export default {
if (!this.$_.isEmpty(n) && !this.$_.isEqual(n, o)) {
this.entityList = []
setTimeout(() => {
const now = new Date()
const now = window.$dayJs.tz().valueOf()
const queryParams = { startTime: Math.floor(now.getTime() / 1000 - 3600), endTime: Math.floor(now.getTime() / 1000) }
switch (this.from) {
case ('ip'): {

View File

@@ -9,6 +9,9 @@
</div>
<div class="filter-top-box" >
<div class="filter-top-body">
<div class="entity__loading" v-show="loading">
<i class="el-icon-loading"></i>
</div>
<div class="filter-top-type">{{item.value}}{{$t('overall.operator')}}{{item.label}}</div>
<el-table
:data="entityTopTenData"
@@ -44,6 +47,7 @@
<script>
export default {
props: {
loading: Boolean
},
data () {
return {

View File

@@ -19,7 +19,8 @@ export default {
delete: false,
refresh: false,
query: false
}
},
timeout: null
}
},
methods: {
@@ -51,6 +52,9 @@ export default {
},
utcTimeToLocalhost (str) { // 系统设置的时区 到 utc 0
return str
},
parseMsDate (ms, format = 'YYYY-MM-DD HH:mm:ss') {
return this.dayJs.tz(parseFloat(ms)).format(format)
}
}
}

View File

@@ -443,7 +443,29 @@ export function lineToSpace (name) {
export function humpToLine (name) {
return name.replace(/([A-Z])/g, '_$1').toLowerCase()
}
// 搜索功能:对象转字符串
export function objToStr (obj) {
return Object.keys(obj).map(k => {
return `${k}='${obj[k]}'`
}).join(' AND ')
}
// 搜索功能:字符串转对象
export function strToObj (n) {
const paramsArr = n.split(/\s[aA][nN][dD]\s/)
const paramsObj = {}
paramsArr.forEach(string => {
const param = string.split('=')
if (param.length > 1) {
let value = param[1].trim()
const valueArr = value.split(/[\"\']/g)
if (valueArr.length > 2) {
value = valueArr[1].trim()
}
paramsObj[param[0].trim()] = value
}
})
return paramsObj
}
// 加载geo数据
export function loadGeoData (key) {
const keys = []

View File

@@ -37,6 +37,7 @@
v-else-if="isMap"
:style="computePosition"
:loading="loading"
:hide-header="hideHeader"
>
<template #chartErrorInfo>
<chart-error
@@ -58,11 +59,11 @@
<template #default>
<template v-if="isIpBasicInfo">
<el-descriptions :column="1">
<el-descriptions-item label="ASN:">{{detailData ? detailData.asn : '-'}}</el-descriptions-item>
<el-descriptions-item label="AS Org:">{{detailData ? detailData.asnOrg : '-'}}</el-descriptions-item>
<el-descriptions-item :label="$t('entities.asSubnet') + ':'">{{detailData.asnSubnet || '-'}}</el-descriptions-item>
<el-descriptions-item label="ASN:">{{detailData.asn || '-'}}</el-descriptions-item>
<el-descriptions-item label="AS Org:">{{detailData.asOrganization || '-'}}</el-descriptions-item>
<el-descriptions-item :label="$t('entities.asSubnet') + ':'">{{detailData.asSubnet || '-'}}</el-descriptions-item>
<el-descriptions-item label="ISP:">{{detailData.isp || '-'}}</el-descriptions-item>
<el-descriptions-item label="DNS PTR:">{{detailData.dnsPTR || '-'}}</el-descriptions-item>
<el-descriptions-item label="DNS PTR:">{{detailData.dnsPtr || '-'}}</el-descriptions-item>
</el-descriptions>
<div class="chart-location">
<el-descriptions :column="1">
@@ -434,7 +435,7 @@
<div class="domain-detail-list">
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.sponsor')}}</div>
<div class="domain-detail-list__content">{{detailData.sponsor || '-'}}</div>
<div class="domain-detail-list__content">{{detailData.registrar || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.org')}}</div>
@@ -442,19 +443,19 @@
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">Email</div>
<div class="domain-detail-list__content">{{detailData.email || '-'}}</div>
<div class="domain-detail-list__content">{{detailData.postcode || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('overall.country')}}</div>
<div class="domain-detail-list__content">{{detailData.orgCountry || '-'}}</div>
<div class="domain-detail-list__content">{{detailData.country || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.creationDate')}}</div>
<div class="domain-detail-list__content">{{detailData.creationDate || '-'}}</div>
<div class="domain-detail-list__content">{{detailData.createTime ? parseMsDate(detailData.createTime) : '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.expirationDate')}}</div>
<div class="domain-detail-list__content">{{detailData.expirationDate || '-'}}</div>
<div class="domain-detail-list__content">{{detailData.expirationTime ? parseMsDate(detailData.expirationTime) : '-'}}</div>
</div>
</div>
</div>
@@ -627,6 +628,7 @@ import {
isMapLine,
isMapBlock,
isSingleValueWithEcharts,
isSingleValueWithEchartsTemp,
isRelationShip,
isTabs,
isGroup,
@@ -672,6 +674,7 @@ export default {
timeFilter: Object,
parentData: Object,
fromBlock: Boolean,
hideHeader: Boolean,
entity: {
type: Object,
default: () => {}
@@ -871,6 +874,7 @@ export default {
})
this.myChart.setOption(this.chartOption)
this.singleValue.value = result[0].values[result[0].values.length - 1][1]
/* const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
const dom = document.getElementById(`chart${this.chartInfo.id}`)
!this.myChart && (this.myChart = echarts.init(dom))
@@ -969,7 +973,7 @@ export default {
setTimeout(() => { this.loading = false }, 250)
})
} else if (this.isAppBasicInfo) {
const queryParams = { app: this.entity.app }
const queryParams = { appName: this.entity.appName }
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
this.detailData = response.data.result
@@ -1135,6 +1139,7 @@ export default {
}
})
console.info(this.entity)
this.showMapBackButton = !!country
const chartParams = this.chartInfo.params
const queryParams = { ...this.queryTimeRange, country: country || '', region: '', ...this.entity } // 统计数据的查询参数
@@ -1321,16 +1326,13 @@ export default {
},
// 获取最新时间
getChartCurrentTimeRange (callback) {
console.info(this.isGroup)
if (this.isGroup) {
this.$emit('getChartCurrentTimeRange', ({ startTime, endTime }) => {
console.info(startTime, endTime)
callback({ startTime, endTime })
})
} else {
const myEndTime = window.$dayJs.tz().valueOf()
const myStartTime = myEndTime - this.chartTimeFilter.dateRangeValue * 60 * 1000
console.info(myStartTime, myEndTime)
callback({ startTime: myStartTime, endTime: myEndTime })
}
},
@@ -1820,7 +1822,7 @@ export default {
]
this.myChart.setOption(this.chartOption)
this.myChart2.setOption(this.chartOption)
get(replaceUrlPlaceholder(chartParams.url, { app: this.entity.app })).then(response => {
get(replaceUrlPlaceholder(chartParams.url, { appName: this.entity.appName })).then(response => {
if (response.code === 200) {
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
@@ -2476,12 +2478,22 @@ export default {
if (this.detailData) {
if (this.detailData.country) {
location = this.detailData.country
if (this.detailData.region) {
if (this.detailData.province) {
location += ', '
location += this.detailData.region
location += this.detailData.province
if (this.detailData.city) {
location += ', '
location += this.detailData.city
}
} else if (this.detailData.region) {
location = this.detailData.region
}
} else if (this.detailData.province) {
location = this.detailData.province
if (this.detailData.city) {
location += ', '
location += this.detailData.city
}
} else if (this.detailData.city) {
location = this.detailData.city
}
}
return location
@@ -2505,6 +2517,8 @@ export default {
}
},
mounted () {
console.info(this.chart)
console.info(this.entity)
this.initChart()
this.throttle = this.$_.throttle(this.echartsResize, 500)
window.addEventListener('resize', this.throttle)
@@ -2565,6 +2579,7 @@ export default {
isEchartsWithStatistics: isEchartsWithStatistics(props.chart.type),
isSingleValue: isSingleValue(props.chart.type),
isSingleValueWithEcharts: isSingleValueWithEcharts(props.chart.type),
isSingleValueWithEchartsTemp: isSingleValueWithEchartsTemp(props.chart.type),
isRelationShip: isRelationShip(props.chart.type),
isTable: isTable(props.chart.type),
isActiveIpTable: isActiveIpTable(props.chart.type),

View File

@@ -36,7 +36,9 @@ export default {
data () {
return {
anchorPoints: [], // { id, label, top, height }
top: 0
top: 0,
scrollHeight: 0,
clientHeight: 0
}
},
setup (props, ctx) {
@@ -73,6 +75,9 @@ export default {
chartLoaded (chartList) {
this.anchorPoints = []
let anchorPoints = []
const panelDom = document.querySelector('#cn-panel')
this.scrollHeight = panelDom.scrollHeight
this.clientHeight = panelDom.clientHeight
chartList.forEach(chart => {
if (chart.params.anchorPoint) {
const dom = document.querySelector(`#${chart.params.anchorPoint}`)
@@ -128,11 +133,15 @@ export default {
},
currentAnchor () {
let currentAnchor = null
if (this.top + this.clientHeight === this.scrollHeight) {
currentAnchor = this.anchorPoints[this.anchorPoints.length - 1]
} else {
this.anchorPoints.forEach(anchor => {
if (anchor.top <= this.top) {
currentAnchor = anchor
}
})
}
return currentAnchor
}
}

View File

@@ -33,6 +33,7 @@
:time-filter="timeFilter"
@pageSize="pageSize"
@pageNo="pageNo"
:loading="listLoading"
></entity-list>
</div>
<div class="explorer-foot" v-else>
@@ -145,7 +146,6 @@ export default {
pageSize: defaultPageSize,
total: 0
},
timeFilter: {},
filterData: [
{
type: 'ip',
@@ -155,28 +155,28 @@ export default {
{
label: this.$t('overall.country'),
column: 'countryDistinctCount',
topColumn: 'ipLocationCountry', // top弹框查询字段
topColumn: 'ip_location_country', // top弹框查询字段
icon: entityFilterType.ip[0].icon,
value: 0
},
{
label: this.$t('overall.province'),
column: 'provinceDistinctCount',
topColumn: 'ipLocationProvince', // top弹框查询字段
topColumn: 'ip_location_province', // top弹框查询字段
icon: entityFilterType.ip[1].icon,
value: 0
},
{
label: this.$t('overall.city'),
column: 'cityDistinctCount',
topColumn: 'ipLocationCity', // top弹框查询字段
topColumn: 'ip_location_city', // top弹框查询字段
icon: entityFilterType.ip[2].icon,
value: 0
},
{
label: this.$t('entities.asn'),
column: 'asnDistinctCount',
topColumn: 'ipAsn', // top弹框查询字段
topColumn: 'ip_asn', // top弹框查询字段
icon: entityFilterType.ip[3].icon,
value: 0
}
@@ -190,21 +190,21 @@ export default {
{
label: this.$t('entities.category'),
column: 'categoryDistinctCount',
topColumn: 'appCategory', // top弹框查询字段
topColumn: 'app_category', // top弹框查询字段
icon: entityFilterType.app[0].icon,
value: 0
},
{
label: this.$t('entities.subcategory'),
column: 'subcategoryDistinctCount',
topColumn: 'appSubcategory', // top弹框查询字段
topColumn: 'app_subcategory', // top弹框查询字段
icon: entityFilterType.app[1].icon,
value: 0
},
{
label: this.$t('entities.risk'),
column: 'riskDistinctCount',
topColumn: 'appRisk', // top弹框查询字段
topColumn: 'app_risk', // top弹框查询字段
icon: entityFilterType.app[2].icon,
value: 0
}
@@ -218,29 +218,30 @@ export default {
{
label: this.$t('entities.domainDetail.categoryGroup'),
column: 'categoryGroupDistinctCount',
topColumn: 'domainCategoryGroup', // top弹框查询字段
topColumn: 'domain_category_group', // top弹框查询字段
icon: entityFilterType.domain[0].icon,
value: 0
},
{
label: this.$t('entities.category'),
column: 'categoryDistinctCount',
topColumn: 'domainCategory', // top弹框查询字段
topColumn: 'domain_category', // top弹框查询字段
icon: entityFilterType.domain[1].icon,
value: 0
},
{
label: this.$t('entities.reputationLevel'),
column: 'reputationLevelDistinctCount',
topColumn: 'domainReputationLevel', // top弹框查询字段
topColumn: 'domain_reputation_level', // top弹框查询字段
icon: entityFilterType.domain[2].icon,
value: 0
}
]
}
],
listData: []
/* listData: JSON.parse(`[
listData: [],
listLoading: false/*,
listData: JSON.parse(`[
{
"entityType": "app",
"appName": "360cn",
@@ -261,7 +262,7 @@ export default {
"ipAsn": "",
"ipLocationCountry": "China",
"entityType": "ip",
"ipLocation_province": "Other",
"ipLocationProvince": "Other",
"ipAddr": "116.178.30.96",
"ipLocationCity": "Other"
},
@@ -490,6 +491,10 @@ export default {
})
},
queryList (params) {
this.timeout = setTimeout(() => {
this.listLoading = true
}, 500)
this.listData = []
const queryParams = {
...params,
startTime: parseInt(params.startTime / 1000),
@@ -499,6 +504,9 @@ export default {
if (response.code === 200) {
this.listData = response.data.result
}
}).finally(() => {
clearTimeout(this.timeout)
this.listLoading = false
})
},
@@ -516,11 +524,16 @@ export default {
},
handleQ (params) {
return Object.keys(params).map(param => {
return `${param}="${params[param]}"`
return `${param}='${params[param]}'`
}).join(' AND ')
},
getEntityIndexData () {
const now = window.$dayJs.tz().valueOf()
const timeFilter = {
startTime: parseInt(now / 1000 - 3600),
endTime: parseInt(now / 1000)
}
// Total
get(api.entityTotal, { entityType: 'app' }).then(response => {
if (response.code === 200) {
@@ -538,33 +551,33 @@ export default {
}
})
// New
get(api.entityNew, { entityType: 'app' }).then(response => {
get(api.entityNew, { entityType: 'app', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityAppNew = response.data.result
}
})
get(api.entityNew, { entityType: 'domain' }).then(response => {
get(api.entityNew, { entityType: 'domain', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityDomainNew = response.data.result
}
})
get(api.entityNew, { entityType: 'ip' }).then(response => {
get(api.entityNew, { entityType: 'ip', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityIpNew = response.data.result
}
})
// Active
get(api.entityActive, { entityType: 'app' }).then(response => {
get(api.entityActive, { entityType: 'app', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityAppActive = response.data.result
}
})
get(api.entityActive, { entityType: 'domain' }).then(response => {
get(api.entityActive, { entityType: 'domain', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityDomainActive = response.data.result
}
})
get(api.entityActive, { entityType: 'ip' }).then(response => {
get(api.entityActive, { entityType: 'ip', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityIpActive = response.data.result
}

View File

@@ -22,6 +22,7 @@
<entity-top
v-show="showTopTen"
ref="entityTopTenPop"
:loading="loading"
@filter="filter"
@close="showTopTen = false"
></entity-top>
@@ -35,7 +36,7 @@
import EntityTop from '@/components/entities/EntityTop'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import { objToStr } from '@/utils/tools'
export default {
name: 'EntityFilter',
components: {
@@ -51,7 +52,8 @@ export default {
topList: 'list',
topData: [],
showTopTen: false, // 自定义列弹框是否显示
entityTopTenData: []
entityTopTenData: [],
loading: false
}
},
methods: {
@@ -60,8 +62,18 @@ export default {
const width = this.$refs['entityTopTen' + i].offsetWidth
const offsetLeft = this.$refs['entityTopTen' + i].offsetLeft
const leftVal = offsetLeft + width
get(api.filterTop, { entityType: type, q: this.searchParams, column: item.topColumn, top: 10, ...this.timeFilter }).then(response => {
const queryParams = {
q: objToStr(this.searchParams),
entityType: type,
column: item.topColumn,
top: 10,
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000)
}
this.timeout = setTimeout(() => {
this.loading = true
}, 500)
get(api.filterTop, queryParams).then(response => {
if (response.code === 200) {
this.$refs.entityTopTenPop.initEntityTop(leftVal, item, response.data.result, filter.totalCount)
} else {
@@ -70,6 +82,8 @@ export default {
}).catch(e => {
this.$refs.entityTopTenPop.initEntityTop(leftVal, item, [], filter.totalCount)
}).finally(() => {
clearTimeout(this.timeout)
this.loading = false
this.showTopTen = true
})
},

View File

@@ -58,7 +58,7 @@
<div class="body__statics">
<div class="entity-statics-down"><i class="cn-icon cn-icon-fall entity-statics-icon"></i>{{entityData.bytesReceivedRate || 0}} bps</div>
<div class="entity-statics-up" ><i class="cn-icon cn-icon-rise"></i>{{entityData.bytesSentRate || 0}} bps</div>
<div class="body__detail" @click="entityDetail({ip: entityData.ipAddr, type: 4})">{{$t('overall.detail')}}></div>
<div class="body__detail" @click="showDetail">{{$t('overall.detail')}}></div>
</div>
</template>
<template v-else-if="entityData.entityType === 'domain'">
@@ -89,7 +89,7 @@
<div class="body__statics">
<div class="entity-statics-down"><i class="cn-icon cn-icon-fall entity-statics-icon"></i>{{entityData.bytesReceivedRate || 0}} bps</div>
<div class="entity-statics-up" ><i class="cn-icon cn-icon-rise"></i>{{entityData.bytesSentRate || 0}} bps</div>
<div class="body__detail" @click="entityDetail({domain: entityData.domainName, type: 5})">{{$t('overall.detail')}}></div>
<div class="body__detail" @click="showDetail">{{$t('overall.detail')}}></div>
</div>
</template>
<template v-else-if="entityData.entityType === 'app'">
@@ -103,7 +103,7 @@
</div>
<div class="body__row">
<span class="body__row-label"><i class="cn-icon cn-icon-sub-category"></i>{{$t('entities.subcategory')}}</span>
<div class="body__row-value" :title="entityData.appSubategory">{{entityData.appSubategory || '-'}}</div>
<div class="body__row-value" :title="entityData.appSubcategory">{{entityData.appSubcategory || '-'}}</div>
</div>
<div class="body__row">
<span class="body__row-label"><i class="cn-icon cn-icon-entity-alert"></i>{{$t('entities.recentAlert')}}</span>
@@ -118,9 +118,9 @@
<div class="body__drawing" :id="`entityListChart${entityData.appName}`"></div>
</div>
<div class="body__statics">
<div class="entity-statics-down"><i class="cn-icon cn-icon-fall entity-statics-icon"></i>{{entityData.bytesReceivedRate || 0}} bps</div>
<div class="entity-statics-up" ><i class="cn-icon cn-icon-rise"></i>{{entityData.bytesSentRate || 0}} bps</div>
<div class="body__detail" @click="entityDetail({app: entityData.appName, type: 6})">{{$t('overall.detail')}}></div>
<div class="entity-statics-down"><i class="cn-icon cn-icon-fall entity-statics-icon"></i>{{entityData.bytesReceivedRate ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}</div>
<div class="entity-statics-up" ><i class="cn-icon cn-icon-rise"></i>{{entityData.bytesSentRate ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}</div>
<div class="body__detail" @click="showDetail">{{$t('overall.detail')}}></div>
</div>
</template>
</div>
@@ -129,8 +129,16 @@
<script>
import entityListMixin from './entityListMixin'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
export default {
name: 'Card',
mixins: [entityListMixin]
mixins: [entityListMixin],
setup () {
return {
unitTypes,
unitConvert
}
}
}
</script>

View File

@@ -66,12 +66,12 @@
<div class="basic-info__item">
<i class="cn-icon cn-icon-rise"></i>
<span>{{$t('entities.sentThroughput')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{entityData.bytesSentRate || '-'}}</span>
<span>{{entityData.bytesSentRate ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-fall"></i>
<span>{{$t('entities.receivedThroughput')}}&nbsp;:&nbsp;&nbsp;</span>
<span>{{entityData.bytesReceivedRate || '-'}}</span>
<span>{{entityData.bytesReceivedRate ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-entity-alert"></i>
@@ -97,6 +97,7 @@
<el-divider></el-divider>
<detail-overview
:entity="entityData"
:time-filter="timeFilter"
></detail-overview>
</div>
</el-collapse-transition>
@@ -107,6 +108,8 @@
<script>
import entityListMixin from './entityListMixin'
import DetailOverview from '@/views/entityExplorer/entityList/detailOverview/DetailOverview'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
export default {
name: 'Row',
props: {
@@ -122,6 +125,12 @@ export default {
isCollapse: true // 是否是折叠状态
}
},
setup () {
return {
unitConvert,
unitTypes
}
},
methods: {
/* 切换折叠状态 */
switchCollapse () {
@@ -131,16 +140,6 @@ 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')
}
}
}

View File

@@ -25,22 +25,22 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
<div class="row__content">{{entityData.max}} Kb/s</div>
<div class="row__content">{{unitConvert(entityData.max, unitTypes.byte).join(' ')}}/s</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
<div class="row__content">{{entityData.avg}} Kb/s</div>
<div class="row__content">{{unitConvert(entityData.avg, unitTypes.byte).join(' ')}}/s</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
<div class="row__contents">
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.sent')}}{{entityData.bytesSentRate}}bps</div>
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__charts" :id="`entityDetailSend${entityData.appName}`" ></div>
</div>
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.received')}}{{entityData.bytesReceivedRate}}bps</div>
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__charts" :id="`entityDetailReceived${entityData.appName}`" ></div>
</div>
@@ -78,7 +78,7 @@
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font">
<div class="overview__row overview__row--small-font" v-if="entityData.alertList && entityData.alertList.length > 3">
<div class="show-more">{{$t('overall.showMore')}}>></div>
</div>
</div>
@@ -98,23 +98,35 @@
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font">
<div class="overview__row overview__row--small-font" v-if="entityData.securityList && entityData.securityList.length > 3">
<div class="show-more">{{$t('overall.showMore')}}>></div>
</div>
</div>
</div>
<div class="overview-map">
<chart
:chart="chart"
:entity="entityCopy"
:hide-header="true"
@getCurrentTimeRange="getCurrentTimeRange"
></chart>
</div>
</template>
<script>
import { api } from '@/utils/api'
import entityDetailMixin from './entityDetailMixin'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import Chart from '@/views/charts/Chart'
import _ from "lodash";
export default {
name: 'App',
props: {
// entity: Object
},
mixins: [entityDetailMixin],
components: {
Chart
},
data () {
return {
// entityData: {}
@@ -125,16 +137,12 @@ export default {
securityUrl: api.entityAppDetailSecurity,
listMode: 'list'
}
},
mounted () {
},
methods: {
getQueryParams () {
const now = new Date()
const queryParams = {
startTime: Math.floor(now.getTime() / 1000 - 3600),
endTime: Math.floor(now.getTime() / 1000),
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000),
appName: this.entityData.appName
}
return queryParams
@@ -143,7 +151,23 @@ export default {
this.entityData.domainCount = result.domainCount
this.entityData.ipCount = result.ipCount
}
},
setup (props) {
return {
chart: {
params: {
url: '/interface/entity/detail/app/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&appName={{appName}}',
unitType: 'number',
valueColumn: 'sessions'
},
type: 2
},
unitTypes,
entityCopy: {
..._.cloneDeep(props.entity)
},
unitConvert
}
}
}
</script>

View File

@@ -1,13 +1,13 @@
<template>
<div class="entity-detail-overview">
<template v-if="entity.entityType === 'ip'">
<ip-overview :entity="entity"></ip-overview>
<ip-overview :entity="entity" :time-filter="timeFilter"></ip-overview>
</template>
<template v-else-if="entity.entityType === 'domain'">
<domain-overview :entity="entity"></domain-overview>
<domain-overview :entity="entity" :time-filter="timeFilter"></domain-overview>
</template>
<template v-else-if="entity.entityType === 'app'">
<app-overview :entity="entity"></app-overview>
<app-overview :entity="entity" :time-filter="timeFilter"></app-overview>
</template>
</div>
</template>
@@ -21,7 +21,8 @@ export default {
/* 详情概览 */
name: 'DetailOverview',
props: {
entity: Object
entity: Object,
timeFilter: Object
},
components: {
'domain-overview': Domain,

View File

@@ -33,22 +33,22 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
<div class="row__content">{{entityData.max}} Kb/s</div>
<div class="row__content">{{unitConvert(entityData.max, unitTypes.byte).join(' ')}}/s</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
<div class="row__content">{{entityData.avg}} Kb/s</div>
<div class="row__content">{{unitConvert(entityData.avg, unitTypes.byte).join(' ')}}/s</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
<div class="row__contents">
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.sent')}}{{entityData.bytesSentRate}}bps</div>
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__charts" :id="`entityDetailSend${entityData.domainName}`" ></div>
</div>
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.received')}}{{entityData.bytesReceivedRate}}bps</div>
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__charts" :id="`entityDetailReceived${entityData.domainName}`" ></div>
</div>
@@ -86,7 +86,7 @@
<span>{{$t('entities.avgRoundTripTime')}}</span>
</template>
<template #data>
<span>{{entityData.establishLatency?entityData.establishLatency+'ms':'-'}}</span>
<span>{{entityData.establishLatency ? unitConvert(entityData.establishLatency, unitTypes.time).join(' ') : '-'}}</span>
</template>
</single-value>
<single-value
@@ -100,7 +100,7 @@
<span>{{$t('entities.httpResponseLatency')}}</span>
</template>
<template #data>
<span>{{entityData.httpResponseLatency?entityData.httpResponseLatency+'ms':'-'}}</span>
<span>{{entityData.httpResponseLatency ? unitConvert(entityData.httpResponseLatency, unitTypes.time).join(' ') : '-'}}</span>
</template>
</single-value>
<single-value
@@ -114,7 +114,7 @@
<span>{{$t('entities.sslConLatency')}}</span>
</template>
<template #data>
<span>{{entityData.sslConLatency?entityData.sslConLatency+'ms':'-'}}</span>
<span>{{entityData.sslConLatency ? unitConvert(entityData.sslConLatency, unitTypes.time).join(' ') : '-'}}</span>
</template>
</single-value>
<single-value
@@ -128,7 +128,7 @@
<span>{{$t('entities.sequenceGapLossPercent')}}</span>
</template>
<template #data>
<span>{{entityData.sequenceGapLossPercent?entityData.sequenceGapLossPercent+'%':'-'}}</span>
<span>{{entityData.sequenceGapLossPercent ? unitConvert(entityData.sequenceGapLossPercent, unitTypes.percent).join(' ') : '-'}}</span>
</template>
</single-value>
<single-value
@@ -142,7 +142,7 @@
<span>{{$t('entities.pktRetransPercent')}}</span>
</template>
<template #data>
<span>{{entityData.pktRetransPercent?entityData.pktRetransPercent+'%':'-'}}</span>
<span>{{entityData.pktRetransPercent ? unitConvert(entityData.pktRetransPercent, unitTypes.percent).join(' ') : '-'}}</span>
</template>
</single-value>
</div>
@@ -154,11 +154,11 @@
<div class="overview__tags">
<div class="overview__tag">
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent?entityData.linkOutPercent+'%':'-'}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') + '%' : '-'}}</span>
</div>
<div class="overview__tag">
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent?entityData.linkInPercent+'%':'-'}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') + '%' : '-'}}</span>
</div>
</div>
</div>
@@ -178,7 +178,7 @@
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font">
<div class="overview__row overview__row--small-font" v-if="entityData.alertList && entityData.alertList.length > 3">
<div class="show-more">{{$t('overall.showMore')}}>></div>
</div>
</div>
@@ -198,25 +198,34 @@
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font">
<div class="overview__row overview__row--small-font" v-if="entityData.securityList && entityData.securityList.length > 3">
<div class="show-more">{{$t('overall.showMore')}}>></div>
</div>
</div>
</div>
<div class="overview-map">
<chart
:chart="chart"
:entity="entityCopy"
:hide-header="true"
@getCurrentTimeRange="getCurrentTimeRange"
></chart>
</div>
</template>
<script>
import SingleValue from '@/components/charts/ChartSingleValue'
import { api } from '@/utils/api'
import entityDetailMixin from './entityDetailMixin'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import Chart from '@/views/charts/Chart'
import _ from "lodash";
export default {
name: 'Domain',
components: {
SingleValue
},
props: {
entity: Object
SingleValue,
Chart
},
mixins: [entityDetailMixin],
data () {
@@ -235,10 +244,9 @@ export default {
},
methods: {
getQueryParams () {
const now = new Date()
const queryParams = {
startTime: Math.floor(now.getTime() / 1000 - 3600),
endTime: Math.floor(now.getTime() / 1000),
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000),
domain: this.entityData.domainName
}
return queryParams
@@ -247,6 +255,25 @@ export default {
this.entityData.appCount = result.appCount
this.entityData.ipCount = result.ipCount
}
},
setup (props) {
const entityCopy = {
..._.cloneDeep(props.entity),
domain: props.entity.domainName
}
return {
chart: {
params: {
url: '/interface/entity/detail/domain/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&domain={{domain}}',
unitType: 'number',
valueColumn: 'sessions'
},
type: 2
},
entityCopy,
unitTypes,
unitConvert
}
}
}
</script>

View File

@@ -17,22 +17,22 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
<div class="row__content">{{entityData.max}} Kb/s</div>
<div class="row__content">{{unitConvert(entityData.max, unitTypes.byte).join(' ')}}/s</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
<div class="row__content">{{entityData.avg}} Kb/s</div>
<div class="row__content">{{unitConvert(entityData.avg, unitTypes.byte).join(' ')}}/s</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
<div class="row__contents">
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.sent')}}{{entityData.bytesSentRate}}bps</div>
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__charts" :id="`entityDetailSend${entityData.ipAddr}`"></div>
</div>
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.received')}}{{entityData.bytesReceivedRate}}bps</div>
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__charts" :id="`entityDetailReceived${entityData.ipAddr}`"></div>
</div>
@@ -70,7 +70,7 @@
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font">
<div class="overview__row overview__row--small-font" v-if="entityData.alertList && entityData.alertList.length > 3">
<div class="show-more">{{$t('overall.showMore')}}>></div>
</div>
</div>
@@ -90,20 +90,35 @@
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font">
<div class="overview__row overview__row--small-font" v-if="entityData.securityList && entityData.securityList.length > 3">
<div class="show-more">{{$t('overall.showMore')}}>></div>
</div>
</div>
</div>
<div class="overview-map">
<chart
:chart="chart"
:entity="entityCopy"
:hide-header="true"
@getCurrentTimeRange="getCurrentTimeRange"
></chart>
</div>
</template>
<script>
import entityDetailMixin from './entityDetailMixin'
import { api } from '@/utils/api'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import Chart from '@/views/charts/Chart'
import _ from "lodash";
export default {
name: 'Ip',
mixins: [entityDetailMixin],
components: {
Chart
},
data () {
return {
// entityData: {}
@@ -117,10 +132,9 @@ export default {
},
methods: {
getQueryParams () {
const now = new Date()
const queryParams = {
startTime: Math.floor(now.getTime() / 1000 - 3600),
endTime: Math.floor(now.getTime() / 1000),
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000),
ip: this.entityData.ipAddr
}
return queryParams
@@ -129,6 +143,25 @@ export default {
this.entityData.appCount = result.appCount
this.entityData.domainCount = result.domainCount
}
},
setup (props) {
const entityCopy = {
..._.cloneDeep(props.entity),
ip: props.entity.ipAddr
}
return {
chart: {
params: {
url: '/interface/entity/detail/ip/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&ip={{ip}}',
unitType: 'number',
valueColumn: 'sessions'
},
type: 2
},
entityCopy,
unitTypes,
unitConvert
}
}
}
</script>

View File

@@ -3,10 +3,12 @@ import { get } from '@/utils/http'
import * as echarts from 'echarts'
import { entityListLineOption } from '@/components/charts/chart-options'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
export default {
props: {
entity: Object
entity: Object,
timeFilter: Object
},
data () {
return {
@@ -46,11 +48,16 @@ export default {
}
},
methods: {
getCurrentTimeRange (callback) {
const myEndTime = window.$dayJs.tz().valueOf()
const myStartTime = myEndTime - this.timeFilter.dateRangeValue * 60 * 1000
callback({ startTime: myStartTime, endTime: myEndTime })
},
queryEntityDetailTraffic () {
this.sentChart = echarts.init(document.getElementById(`entityDetailSend${this.entityName}`))
this.receivedChart = echarts.init(document.getElementById(`entityDetailReceived${this.entityName}`))
get(this.trafficUrl, this.getQueryParams()).then(response => {
if (response.code === 200) {
if (response.code === 200 && response.data.result && response.data.result.length > 0) {
response.data.result.forEach(t => {
if (t.legend === 'bytesRate') {
this.entityData.max = t.aggregation.max
@@ -101,15 +108,17 @@ export default {
}
}
})
}
this.sentChart.setOption(this.chartOptionSent)
this.receivedChart.setOption(this.chartOptionReceived)
}
}).finally(() => {
setTimeout(() => {
try {
this.$nextTick(() => {
this.sentChart && this.sentChart.resize()
this.receivedChart && this.receivedChart.resize()
})
} catch (e) {}
}, 250)
})
},
@@ -192,7 +201,9 @@ export default {
},
setup () {
return {
chartOption: entityListLineOption
chartOption: entityListLineOption,
unitTypes,
unitConvert
}
},
mounted () {

View File

@@ -77,7 +77,7 @@ export default {
},
queryParams () {
let params
const now = new Date()
const now = window.$dayJs.tz().valueOf()
switch (this.entityData.entityType) {
case ('ip'): {
params = {
@@ -115,11 +115,24 @@ export default {
}
},
methods: {
entityDetail (entity) {
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')
},
queryTraffic () {
get(api.entityTraffic, { entityType: this.entityData.entityType, name: this.entityName }).then(response => {
const queryParams = {
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000),
entityType: this.entityData.entityType,
name: this.entityName
}
get(api.entityTraffic, queryParams).then(response => {
if (response.code === 200) {
response.data.result.forEach(t => {
if (t.name === 'bytes_sent_rate') {
@@ -133,14 +146,26 @@ export default {
})
},
querySecurity () {
get(api.entitySecurityNum, { entityType: this.entityData.entityType, name: this.entityName }).then(response => {
const queryParams = {
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000),
entityType: this.entityData.entityType,
name: this.entityName
}
get(api.entitySecurityNum, queryParams).then(response => {
if (response.code === 200) {
this.entityData.securityCount = response.data.result[0].count
}
})
},
queryAlert () {
get(api.entityAlertNum, { entityType: this.entityData.entityType, name: this.entityName }).then(response => {
const queryParams = {
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000),
entityType: this.entityData.entityType,
name: this.entityName
}
get(api.entityAlertNum, queryParams).then(response => {
if (response.code === 200) {
this.entityData.alertCount = response.data.result[0].value
}
@@ -211,6 +236,7 @@ export default {
}
}
}
return {}
})
chartOption = {
...this.chartOption,

View File

@@ -18,7 +18,7 @@
<span>{{$t('search.searchHistory')}}</span>
</div>
<div class="foot__item">
<span @click="search">{{$t('overall.explore')}}</span>
<span @click="search({})">{{$t('overall.explore')}}</span>
<el-divider direction="vertical"></el-divider>
<span>{{$t('overall.help')}}</span>
</div>
@@ -41,7 +41,7 @@ export default {
}
},
methods: {
search (params) {
search (params = {}) {
this.$emit('search', params)
},
addParams (params) {