CN-1150: 实体列表接口对接

This commit is contained in:
刘洪洪
2023-07-07 17:22:51 +08:00
parent 87cd43dde2
commit 9c46e1af47
23 changed files with 1432 additions and 983 deletions

View File

@@ -10,10 +10,9 @@
right: 10px;
.search__suffix {
margin-left: 8px; // 新版实体列表改版,后续记得解开
// margin-left: 8px;
.cn-icon-search-advance, .cn-icon-search-normal {
//.cn-icon-search-advance, .cn-icon-search-normal, .cn-icon-filter {
.cn-icon-search-advance, .cn-icon-search-normal, .cn-icon-filter {
color: #A6AAAE;
font-size: 18px;
}

View File

@@ -222,3 +222,10 @@
padding: 0 4px;
//color: white;
}
.performance-event-remark {
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: #353636;
font-weight: 400;
}

View File

@@ -1,104 +1,100 @@
.entity-filter-case {
display: flex;
flex-direction: column;
width: 280px;
margin-right: 10px;
width: 320px;
margin-right: 20px;
overflow: auto;
z-index: 1;
border: 1px solid rgba(226, 229, 236, 1) !important;
border-radius: 4px !important;
.filter-case__header {
background-color: #E1E6ED;
margin-bottom: 10px;
padding-left: 8px;
height: 36px;
line-height: 36px;
color: #666;
font-size: 14px;
background: #F7F7F7;
box-shadow: 0 1px 0 0 rgba(226,229,236,1);
border-radius: 4px 4px 0 0;
}
.entity-filter {
display: flex;
flex-direction: column;
border: 1px solid #E7EAED;
margin-bottom: 10px;
background-color: white;
.filter__header {
height: 46px;
margin: 0 15px;
line-height: 46px;
border-bottom: 1px solid #EFF2F5;
margin: 0 20px;
font-size: 14px;
color: #333;
}
.filter__body {
padding: 11px 0 21px 0;
.filter__row {
padding: 0 15px;
align-items: center;
cursor: pointer;
transition: all linear .2s;
.filter__row-popover {
display: flex;
line-height: 26px;
color: #353636;
font-weight: 500;
}
&:hover, &.filter__row--active {
background-color: #F3F7FA;
.filter__body {
width: calc(100% - 40px);
margin: 0 20px;
.filter-hr {
width: calc(100% + 20px);
margin-left: -10px;
margin-top: 10px;
height: 1px;
background: #EFF2F5;
//background: #000;
}
.row__label {
font-size: 14px;
flex: 8;
.filter__body-item {
height: 26px;
line-height: 26px;
display: flex;
i {
color: #8FA1BE;
align-items: center;
justify-content: space-between;
cursor: pointer;
.filter__body-item-left {
display: flex;
align-items: center;
font-size: 14px;
color: #353636;
font-weight: 400;
.filter-country-flag {
width: 18px;
height: 12px;
}
span {
padding-left: 6px;
color: #333;
}
}
.row__value {
color: #666;
position: relative;
display: inline-block;
.filter__body-item-left-index {
width: 16px;
height: 16px;
text-align: center;
flex: 1;
.chart__loading img {
left: unset;
right: 0;
}
}
}
}
}
}
.filter__row-popover {
.pop-title {
i {
background: #EFF2F5;
border-radius: 2px;
margin-right: 6px;
font-family: NotoSansHans-Black;
font-size: 9px;
color: #96A2B0;
font-weight: 900;
display: flex;
align-items: center;
justify-content: center;
}
.filter__body-item-left-label {
max-width: 180px;
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #353636;
font-weight: 400;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.entity-pop-custom {
.filter-top-box {
.chart__loading {
height: calc(100% - 65px);
top: 64px;
}
.top-table-percent{
display:grid;
grid-template-columns: 50% auto;
grid-template-rows: 100%;
grid-row-gap: 0px;
grid-column-gap: 0px;
.top-table-progress{
align-content: center;
padding-top: 8px;
}
}
.customer-no-border-table {
.el-table__body-wrapper {
height: calc(100% - 36px);
}
.filter__body-item-right {
flex-shrink: 0;
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: #717171;
font-weight: 400;
}
}
}

View File

@@ -5,7 +5,7 @@
background: #FFFFFF;
border-radius: 2px;
transition: all .2s;
//border: 1px solid #E2E5EC; 新版实体列表改版,后续记得解开
border: 1px solid #E2E5EC;
&:hover .cn-entity__header .header__content {

View File

@@ -160,6 +160,10 @@
&:last-of-type {
padding-bottom: 0;
}
.el-popper {
min-width: 90px !important;
}
}
.row__content-accept {
margin-left: 39px;
@@ -187,6 +191,12 @@
color: #666666;
}
}
.overview__row-related {
display: flex;
align-items: center;
margin-bottom: 8px;
}
}
.overview__content.domain__content {
@@ -306,3 +316,8 @@
.margin-l-140 {
margin-left: 140px;
}
.line-center {
display: flex;
align-items: center;
}

View File

@@ -1,5 +1,5 @@
.entity-list {
width: calc(100% - 290px);
width: 100%;
height: calc(100% - 42px);
flex: 1;
position: relative;

View File

@@ -1,9 +1,8 @@
.cn-entity--list {
display: flex;
//border: 1px #E2E5EC solid;
//margin-bottom: 10px;
//border-radius: 4px;
// 新版实体列表改版,后续记得解开
border: 1px #E2E5EC solid;
margin-bottom: 10px;
border-radius: 4px;
.cn-entity__collapse {
margin-bottom: 1px;
@@ -13,8 +12,8 @@
justify-content: center;
align-items: flex-start;
background-color: #F3F7FA;
//border-radius: 4px 0 0 4px;
// 新版实体列表改版,后续记得解开
border-radius: 4px 0 0 4px;
span {
transform: rotate(0);
transition: all linear .2s;
@@ -41,12 +40,12 @@
overflow: hidden;
display: flex;
flex-wrap: wrap;
//align-content: center;
align-content: center;
padding: 16px 0;
margin-bottom: 1px;
background-color: white;
//border-radius: 0 4px 4px 0;
// 新版实体列表改版,后续记得解开
border-radius: 0 4px 4px 0;
.cn-entity__icon {
margin-left: 26px;
margin-right: 10px;
@@ -77,13 +76,12 @@
font-size: 16px;
padding-bottom: 3px;
color: #333333;
//.cn-entity__header-title {
// margin-right: 10px;
//}
//.cn-entity__header-tag {
//
//}
// 新版实体列表改版,后续记得解开
.cn-entity__header-title {
margin-right: 10px;
}
.cn-entity__header-tag {
}
}
.cn-entity__body {
@@ -161,27 +159,21 @@
color: #666;
}
}
.row-item-label {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #717171;
font-weight: 400;
}
//.show-detail {
// flex-shrink: 0;
// padding: 0 30px;
// font-size: 12px;
// color: #3976CB;
// //color: #2C72C6;
// //font-weight: 400;
// //margin-top: -17px;
// // 新版实体列表改版,后续记得解开
//
// &:hover {
// cursor: pointer;
// }
//
// //i {
// // font-size: 12px;
// // margin-right: 5px;
// //}
//}
.row-item-value {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #353636;
font-weight: 400;
}
}
}
}
}

View File

@@ -75,13 +75,10 @@
</div>
<div class="tag-search__add" @click="addCondition">{{$t('entities.advancedSearch.add')}}</div>
<div class="search__suffixes search__suffixes--tag-mode">
<div class="search__suffix" @click="changeMode">
<!-- 新版实体列表改版后续记得解开-->
<!-- <div class="search__suffix" style="margin-right: 12px" @click="changeMode">-->
<div class="search__suffix" style="margin-right: 12px" @click="changeMode">
<i class="cn-icon cn-icon-search-normal"></i>
</div>
<div class="search__suffix" @click="search">
<!-- <div class="search__suffix new-search__suffix" @click="search">-->
<div class="search__suffix new-search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</div>
@@ -440,20 +437,19 @@ export default {
}
</script>
<!--// 新版实体列表改版,后续记得解开-->
<!--<style lang="scss">-->
<!--.new-search__suffix {-->
<!-- width: 41px;-->
<!-- height: 41px;-->
<!-- line-height: 41px;-->
<!-- background: #38ACD2;-->
<!-- text-align: center;-->
<!-- margin-top: -10px;-->
<!-- margin-right: -10px;-->
<style lang="scss">
.new-search__suffix {
width: 41px;
height: 41px;
line-height: 41px;
background: #38ACD2;
text-align: center;
margin-top: -10px;
margin-right: -10px;
<!-- .el-icon-search {-->
<!-- color: #fff !important;-->
<!-- margin-top: 9px !important;-->
<!-- }-->
<!--}-->
<!--</style>-->
.el-icon-search {
color: #fff !important;
margin-top: 9px !important;
}
}
</style>

View File

@@ -4,13 +4,12 @@
></textarea>
<div class="search__suffixes search__suffixes--text-mode">
<div class="search__suffix" @click="changeMode">
<i class="cn-icon cn-icon-search-advance"></i>
<i class="cn-icon cn-icon-filter"></i>
</div>
<!-- <div class="search__suffix-close" @click="cleanParams">-->
<!-- <i class="el-icon-error"></i>-->
<!-- </div>-->
<!-- <div class="search__suffix new-search__suffix" @click="search">-->
<div class="search__suffix" @click="search">
<div class="search__suffix-close" @click="cleanParams">
<i class="el-icon-error"></i>
</div>
<div class="search__suffix new-search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</div>
@@ -43,9 +42,9 @@ export default {
},
emits: ['changeMode', 'search'],
methods: {
// cleanParams () {
// toRaw(this.codeMirror).setValue('')
// },
cleanParams () {
toRaw(this.codeMirror).setValue('')
},
initCodeMirror () {
this.codeMirror = CodeMirror.fromTextArea(this.$refs.textSearch, {
mode: {
@@ -189,28 +188,28 @@ export default {
}
</script>
<!--<style lang="scss">-->
<!--.search__suffix-close {-->
<!-- .el-icon-error {-->
<!-- font-size: 17px;-->
<!-- color: #C4C4C4;-->
<!-- margin: 0 12px;-->
<!-- cursor: pointer;-->
<!-- }-->
<!--}-->
<style lang="scss">
.search__suffix-close {
.el-icon-error {
font-size: 17px;
color: #C4C4C4;
margin: 0 12px;
cursor: pointer;
}
}
<!--.new-search__suffix {-->
<!-- width: 41px;-->
<!-- height: 41px;-->
<!-- line-height: 41px;-->
<!-- background: #38ACD2;-->
<!-- text-align: center;-->
<!-- margin-top: -10px;-->
<!-- margin-right: -10px;-->
.new-search__suffix {
width: 41px;
height: 41px;
line-height: 41px;
background: #38ACD2;
text-align: center;
margin-top: -10px;
margin-right: -10px;
<!-- .el-icon-search {-->
<!-- color: #fff !important;-->
<!-- margin-top: 9px !important;-->
<!-- }-->
<!--}-->
<!--</style>-->
.el-icon-search {
color: #fff !important;
margin-top: 9px !important;
}
}
</style>

View File

@@ -20,10 +20,7 @@ export default {
entityDetectionStyle () {
const route = this.$route.name !== undefined ? this.$route.name : this.$route
if (listScrollPath.indexOf(route.path) > -1) {
// 新版实体列表改版,后续记得解开
const style = route.path === listScrollPath[0] ? 'overflow:auto;background-color: #EFF2F5;' : 'overflow:auto;'
// const style = 'overflow:auto;'
return style
return 'overflow:auto;'
} else {
return ''
}

View File

@@ -19,7 +19,12 @@ export default {
this.loadingRelationshipOne = true
get(relationshipUrlOne, this.getQueryParams()).then(response => {
if (response.code === 200) {
const relationshipDataOne = response.data.result
const relationshipDataOne = []
if (response.data.result.length > 0) {
response.data.result.forEach(item => {
relationshipDataOne.push({ value: item, show: true })
})
}
// 将请求数据 传入方法中
this.relatedServerWidth(relationshipDataOne, refOne, 1)
}
@@ -30,7 +35,12 @@ export default {
this.loadingRelationshipTwo = true
get(relationshipUrlTow, this.getQueryParams()).then(response => {
if (response.code === 200) {
const relationshipDataTwo = response.data.result
const relationshipDataTwo = []
if (response.data.result.length > 0) {
response.data.result.forEach(item => {
relationshipDataTwo.push({ value: item, show: true })
})
}
// 将请求数据 传入方法中
this.relatedServerWidth(relationshipDataTwo, refTow, 2)
}
@@ -45,7 +55,7 @@ export default {
let flag = true
data.forEach((item) => {
// 每条数据的宽度
const width = getTextRect(item.appName || item.domain || item.ip).width + 67
const width = getTextRect(item.value).width + 67
if (width > 67 && width !== 0) {
sum += width
if (flag && sum >= relatedServerWidth && num === 1) {

View File

@@ -451,6 +451,300 @@ if (openMock) {
}
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/query/list.*`), 'get', function (requestObj) {
const result = {
pageNo: 1,
pageSize: 10,
total: 3,
list: [
{ entityValue: '192.168.12.34', entityType: 'ip' },
{ entityValue: 'gdbzkz.com', entityType: 'domain' },
{ entityValue: 'qqvideo', entityType: 'app' }
]
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/basic.*`), 'get', function (requestObj) {
const entityType = getEntityType(requestObj.url)
let result = {}
switch (entityType) {
case ('domain'): {
result = {
whois: {
registrarName: 'Beijing Baidu Company',
registrantOrg: 'Beijing Baidu Netcom Science Technology Co., Ltd.',
registrantCountry: 'China',
email: '信息已设置隐私保护',
createDate: 1685329698,
expireDate: 1685329698
},
icp: {
icpSiteLicense: '京ICP证030173号',
icpCompanyName: '北京百度网讯科技有限公司',
icpCompanyType: '企业'
},
category: {
name: '门户网站',
group: '互联网',
reputationLevel: 'Trustworthy'
}
}
break
}
case ('ip'): {
result = {
asn: { id: 2, asn: '14061', organization: 'DIGITALOCEAN-ASN - DigitalOcean, LLC, US' },
location: {
continent: 'North America',
country: 'United States',
province: 'New York',
city: '',
lngwgs: '-74.006',
latwgs: '40.713',
isp: 'dba Omsoft',
owner: 'tie net'
}
}
break
}
case ('app'): {
result = {
category: {
appName: 'QQ',
appId: 333,
appCategory: '娱乐',
appSubcategory: '聊天',
appRisk: '1',
appDescription: '聊天社交软件',
appLongname: 'Tencent qq',
appTechnology: 'socket',
appCompany: 'tencent',
appCompanyCategory: '互联网'
}
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/kb/intelligence/tag.*`), 'get', function (requestObj) {
const entityType = getEntityType(requestObj.url)
let result = {}
switch (entityType) {
case ('domain'): {
result = {
malware: {
threatType: 'command and control',
malwareName: '情报攻击',
malwareAlias: '攻击'
},
darkweb: {
nodeType: 'mtproxy'
},
userDefinedTags: [
{
id: 1,
tagValue: '门户网站'
}
]
}
break
}
case ('ip'): {
result = {
malware: {
threatType: 'command and control',
malwareName: 'IcedID',
malwareAlias: 'BokBot,IceID'
},
darkweb: {
nodeType: '12p'
},
psiphon3Ip: {
type: 'high',
method: 'passive_ml',
confidence: 88,
confidenceLevel: 'confirmed'
},
userDefinedTags: [
{
id: 1,
tagValue: '门户网站'
}
]
}
break
}
case ('app'): {
result = {
userDefinedTags: [
{
id: 1,
tagValue: '门户网站'
},
{
id: 2,
tagValue: '新闻软件'
}
]
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/domain/relate.*`), 'get', function (requestObj) {
const relateType = getRelateType(requestObj.url)
let result = {}
switch (relateType) {
case ('ip'): {
result = {
total: 5,
result: ['bittorrent', 'qq_web', 'wechat', 'tencent', 'outlook']
}
break
}
case ('app'): {
result = {
total: 5,
result: ['192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180']
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/ip/relate.*`), 'get', function (requestObj) {
const relateType = getRelateType(requestObj.url)
let result = {}
switch (relateType) {
case ('domain'): {
result = {
total: 5,
result: ['bittorrent', 'qq_web', 'wechat', 'tencent', 'outlook']
}
break
}
case ('app'): {
result = {
total: 5,
result: ['gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com']
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/app/relate.*`), 'get', function (requestObj) {
const relateType = getRelateType(requestObj.url)
let result = {}
switch (relateType) {
case ('ip'): {
result = {
total: 5,
result: ['gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com']
}
break
}
case ('domain'): {
result = {
total: 5,
result: ['192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180']
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/traffic/performance.*`), 'get', function (requestObj) {
const result = {
resultType: 'object',
result: {
httpResponseLatencyValue: null,
httpResponseLatencyP50: null,
httpResponseLatencyP90: null,
httpResponseLatencyP99: null,
sslConLatencyValue: 191,
sslConLatencyP50: 137,
sslConLatencyP90: 311,
sslConLatencyP99: 1610,
establishLatencyValue: 42,
establishLatencyP50: 33,
establishLatencyP90: 54,
establishLatencyP99: 177,
sequenceGapLossPercentValue: 0.001,
sequenceGapLossPercentP50: 0,
sequenceGapLossPercentP90: 0.0038,
sequenceGapLossPercentP99: 0.0087,
pktRetransPercentValue: 0.0124,
pktRetransPercentP50: 0.0096,
pktRetransPercentP90: 0.0183,
pktRetransPercentP99: 0.0769
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/overview/active.*`), 'get', function (requestObj) {
const data = { domainCount: 755, ipCount: 8373, appCount: 263 }
return {
msg: 'success',
code: 200,
data: data
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/overview/new.*`), 'get', function (requestObj) {
const data = { domainCount: 262, ipCount: 4201, appCount: 43 }
return {
msg: 'success',
code: 200,
data: data
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/overview/total.*`), 'get', function (requestObj) {
const data = { domainCount: 7686274, ipCount: 2169957, appCount: 856 }
return {
msg: 'success',
code: 200,
data: data
}
})
}
const getQuery = (url) => {
@@ -467,3 +761,31 @@ const getQuery = (url) => {
}
return result
}
const getEntityType = (url) => {
let entityType = ''
if (url.indexOf('/domain?') > -1) {
entityType = 'domain'
}
if (url.indexOf('/ip?') > -1) {
entityType = 'ip'
}
if (url.indexOf('/app?') > -1) {
entityType = 'app'
}
return entityType
}
const getRelateType = (url) => {
let entityType = ''
if (url.indexOf('/domains?') > -1) {
entityType = 'domain'
}
if (url.indexOf('/ips?') > -1) {
entityType = 'ip'
}
if (url.indexOf('/apps?') > -1) {
entityType = 'app'
}
return entityType
}

View File

@@ -262,7 +262,47 @@ export const api = {
ipRelatedDomain: apiVersion + '/entity/graph/relation/ip/relate/domains',
ipRelatedApp: apiVersion + '/entity/graph/relation/ip/relate/apps',
appRelatedIp: apiVersion + '/entity/graph/relation/app/relate/ips',
appRelatedDomain: apiVersion + '/entity/graph/relation/app/relate/domains'
appRelatedDomain: apiVersion + '/entity/graph/relation/app/relate/domains',
basicInfo: apiVersion + '/entity/graph/relation/basic',
tags: apiVersion + '/entity/graph/relation/kb/intelligence/tag'
},
entityList: {
list: apiVersion + '/entity/explorer/query/list', // 实体列表
domainBasicInfo: apiVersion + '/entity/explorer/detail/basic/domain', // Domain实体响应结果
ipBasicInfo: apiVersion + '/entity/explorer/detail/basic/ip', // ip实体响应
appBasicInfo: apiVersion + '/entity/explorer/detail/basic/app', // app实体响应
domainTags: apiVersion + '/entity/explorer/detail/kb/intelligence/tag/domain', // Domain实体标签响应结果
ipTags: apiVersion + '/entity/explorer/detail/kb/intelligence/tag/ip', // ip实体标签响应结果
appTags: apiVersion + '/entity/explorer/detail/kb/intelligence/tag/app', // app实体标签响应结果
domainThroughput: apiVersion + '/entity/explorer/detail/traffic/throughput/domain', // 实体流量信息
ipThroughput: apiVersion + '/entity/explorer/detail/traffic/throughput/ip', // 实体流量信息
appThroughput: apiVersion + '/entity/explorer/detail/traffic/throughput/app', // 实体流量信息
domainPerformance: apiVersion + '/entity/explorer/detail/traffic/performance/domain', // domain网络质量
ipPerformance: apiVersion + '/entity/explorer/detail/traffic/performance/ip', // ip网络质量
appPerformance: apiVersion + '/entity/explorer/detail/traffic/performance/app', // app网络质量
domainRelatedApp: apiVersion + '/entity/explorer/detail/domain/relate/apps', // 域名相关app
domainRelatedIp: apiVersion + '/entity/explorer/detail/domain/relate/ips', // 域名相关ip
appRelatedDomain: apiVersion + '/entity/explorer/detail/app/relate/domains', // app相关域名
appRelatedIp: apiVersion + '/entity/explorer/detail/app/relate/ips', // app相关ip
ipRelatedApp: apiVersion + '/entity/explorer/detail/ip/relate/apps', // ip相关app
ipRelatedDomain: apiVersion + '/entity/explorer/detail/ip/relate/domains', // ip相关域名
ipRelatedPort: apiVersion + '/entity/explorer/detail/ip/relate/ports', // ip开放端口
domainTrafficMap: apiVersion + '/entity/explorer/detail/traffic/map/domain', // domain流量地图
ipTrafficMap: apiVersion + '/entity/explorer/detail/traffic/map/ip', // ip流量地图
appTrafficMap: apiVersion + '/entity/explorer/detail/traffic/map/app', // app流量地图
summaryCount: apiVersion + '/entity/explorer/query/summaryCount', // 实体基数统计
aggCountry: apiVersion + '/entity/explorer/top/aggCountry', // 国家实体基数统计
aggAsn: apiVersion + '/entity/explorer/top/aggAsn', // ASN实体基数统计
aggCity: apiVersion + '/entity/explorer/top/aggCity', // 城市实体基数统计
domainSecurity: apiVersion + '/entity/explorer/detail/event/security/domain', // domain安全事件详情
ipSecurity: apiVersion + '/entity/explorer/detail/event/security/domain', // ip安全事件详情
appSecurity: apiVersion + '/entity/explorer/detail/event/security/domain', // app安全事件详情
domainEventPerformance: apiVersion + '/entity/explorer/detail/event/performance/domain', // domain服务质量详情
ipEventPerformance: apiVersion + '/entity/explorer/detail/event/performance/ip', // ip服务质量详情
appEventPerformance: apiVersion + '/entity/explorer/detail/event/performance/app', // app服务质量详情
entityActive: apiVersion + '/entity/explorer/overview/active', // entity首页active数据概览
entityNew: apiVersion + '/entity/explorer/overview/new', // entity首页new数据概览
entityTotal: apiVersion + '/entity/explorer/overview/total' // entity首页total数据概览
}
}
}

View File

@@ -282,7 +282,7 @@ export const columnList = [
label: 'IP.Address'
},
{
name: 'ip_location_country',
name: 'country',
type: 'string',
label: 'IP.Country'
},
@@ -292,12 +292,12 @@ export const columnList = [
label: 'IP.Province'
},
{
name: 'ip_location_city',
name: 'region',
type: 'string',
label: 'IP.City'
},
{
name: 'ip_asn',
name: 'asn',
type: 'string',
label: 'IP.ASN'
},
@@ -367,7 +367,7 @@ export const columnList = [
label: 'Domain.Whois address'
},
{
name: 'domain_whois_city',
name: 'region',
type: 'string',
label: 'Domain.Whois city'
},
@@ -377,7 +377,7 @@ export const columnList = [
label: 'Domain.Whois state'
},
{
name: 'domain_whois_country',
name: 'country',
type: 'string',
label: 'Domain.Whois country'
},

View File

@@ -3,27 +3,29 @@
class="entity-explorer"
:class="{'entity-explorer--show-list': showList}">
<!-- 顶部工具栏在列表页显示 -->
<!-- <div class="explorer-top-tools explorer-top-tools-new" v-show="showList">-->
<!-- <div class="explorer-detection-top-tools">-->
<!-- <div class="explorer-top-tools-title">{{$t('network.entity')}}</div>-->
<!-- </div>-->
<!-- <div class="explorer-top-tools">-->
<!-- <DateTimeRange :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" :date-range="timeFilter.dateRangeValue" ref="dateTimeRange" @change="reload"/>-->
<!-- <TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/>-->
<!-- <el-button-group size="mini">-->
<!-- <el-button size="mini" @click="listMode = 'list'" :class="{'active': listMode === 'list'}"><i class="cn-icon cn-icon-list"></i></el-button>-->
<!-- <el-button size="mini" @click="listMode = 'block'" :class="{'active': listMode === 'block'}"><i class="cn-icon cn-icon-blocks"></i></el-button>-->
<!-- </el-button-group>-->
<div class="explorer-top-tools" v-show="showList">
<DateTimeRange :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" :date-range="timeFilter.dateRangeValue" ref="dateTimeRange" @change="reload"/>
<TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/>
<el-button-group size="mini">
<el-button size="mini" @click="setListMode('list')" :class="{'active': listMode === 'list'}"><i class="cn-icon cn-icon-list"></i></el-button>
<el-button size="mini" @click="setListMode('block')" :class="{'active': listMode === 'block'}"><i class="cn-icon cn-icon-blocks"></i></el-button>
</el-button-group>
<div class="explorer-top-tools explorer-top-tools-new" style="margin: 2px 0;" v-show="showList">
<div class="explorer-detection-top-tools">
<div class="explorer-top-tools-title" style="padding: 0;margin-left: -10px;">{{$t('network.entity')}}</div>
</div>
</div>
<!-- </div>-->
<!-- 搜索组件 -->
<explorer-search
v-if="!showList"
ref="search"
:class="{'explorer-search--show-list': showList}"
:show-list="showList"
@search="search"
></explorer-search>
<!-- 内容区 -->
<div v-if="showList" style="display: flex;flex-direction: row;">
<entity-filter
:filter-data="newFilterData"
:loading-left="loadingLeft"
:q="q"
:time-filter="timeFilter"
@filter="filter"
></entity-filter>
<div class="explorer-container" style="height: calc(100% - 62px);flex-direction: column;width: 100%;">
<explorer-search
ref="search"
:class="{'explorer-search--show-list': showList}"
@@ -31,21 +33,17 @@
@search="search"
></explorer-search>
<!--todo静态数据之后记得修改-->
<!-- <div class="explorer-result" v-if="showList">-->
<!-- 8,866 resultsIP 2666Domain 3200APP 3000-->
<!-- </div>-->
<!-- 内容区 -->
<div class="explorer-container" v-if="showList" style="height: calc(100% - 20px); flex-direction: column">
<div style="display: flex; height: auto;">
<entity-filter
:filter-data="filterData"
:loading-left="loadingLeft"
:q="q"
:time-filter="timeFilter"
@filter="filter"
></entity-filter>
<div style="display: flex;flex-direction: column;height: calc(100% - 42px);">
<div class="explorer-result" v-if="showList">
<loading :loading="loadingCount"></loading>
<span>{{ summaryCount.total }}</span>resultsIP
<span>{{ summaryCount.ipCount }}</span>Domain
<span>{{ summaryCount.fqdnCount }}</span>APP
<span>{{ summaryCount.appCount }}</span>
</div>
<entity-list
style="width: 100%;"
:list-data="listData"
:list-mode="listMode"
:pageObj="pageObj"
@@ -56,7 +54,9 @@
></entity-list>
</div>
</div>
<div class="explorer-foot" v-else>
</div>
<div class="explorer-foot" v-if="!showList">
<div>
<el-divider direction="vertical"></el-divider>
<div class="entity-overview">
@@ -150,8 +150,6 @@
<script>
import ExplorerSearch from '@/views/entityExplorer/search/ExplorerSearch'
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
import TimeRefresh from '@/components/common/TimeRange/TimeRefresh'
import EntityFilter from '@/views/entityExplorer/EntityFilter'
import EntityList from '@/views/entityExplorer/entityList/EntityList'
import { entityType, defaultPageSize, riskLevelMapping } from '@/utils/constants'
@@ -172,8 +170,6 @@ export default {
components: {
Loading,
ExplorerSearch,
DateTimeRange,
TimeRefresh,
EntityFilter,
EntityList
},
@@ -210,23 +206,15 @@ export default {
{
label: this.$t('overall.country'),
column: 'countryDistinctCount',
topColumn: 'ip_location_country', // top弹框查询字段
topColumn: 'country', // top弹框查询字段
icon: 'cn-icon cn-icon-country',
showTopTen: false,
value: 0
},
{
label: this.$t('overall.province'),
column: 'provinceDistinctCount',
topColumn: 'ip_location_province', // top弹框查询字段
icon: 'cn-icon cn-icon-position',
showTopTen: false,
value: 0
},
{
label: this.$t('overall.city'),
column: 'cityDistinctCount',
topColumn: 'ip_location_city', // top弹框查询字段
topColumn: 'region', // top弹框查询字段
icon: 'cn-icon cn-icon-city',
showTopTen: false,
value: 0
@@ -234,7 +222,7 @@ export default {
{
label: this.$t('entities.asn'),
column: 'asnDistinctCount',
topColumn: 'ip_asn', // top弹框查询字段
topColumn: 'asn', // top弹框查询字段
icon: 'cn-icon cn-icon-cloud',
showTopTen: false,
value: 0
@@ -346,6 +334,26 @@ export default {
]
}
],
newFilterData: [
{
icon: 'cn-icon cn-icon-registration-country',
title: 'Top Countries',
totalCount: 0,
data: []
},
{
icon: 'cn-icon cn-icon-city',
title: 'Top Cities',
totalCount: 0,
data: []
},
{
icon: 'cn-icon cn-icon-as',
title: 'Top ASNs',
totalCount: 0,
data: []
}
],
listData: [],
q: '',
metaList: [],
@@ -367,7 +375,14 @@ export default {
// 实体详情列表页面 左侧筛选条件
loadingLeft: false,
initFlag: false, // 初始化标志避免初始化时pageSize和pageNo会调用搜索
timer: null // 初始化标志的延时器,需要销毁
timer: null, // 初始化标志的延时器,需要销毁
summaryCount: {
total: 0,
fqdnCount: 0,
ipCount: 0,
appCount: 0
},
loadingCount: false // 实体基数统计的loading
}
},
methods: {
@@ -420,6 +435,12 @@ export default {
return result
},
search (param) {
// todo 下版本08版本删除 ---- start
if (param && param.q.indexOf("QUERY('") > -1) {
this.$message.error(this.$t('overall.versionNotSupportThisFormat'))
return true
}
// 下版本08版本删除 ---- end
let q
let metaList
if (param) {
@@ -489,29 +510,35 @@ export default {
} else {
this.limitFilterType = false
}
this.queryFilter({ entityType: entityType, q: this.q, ...this.timeFilter })
if (entityType === 'ip') {
this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
}
// this.queryFilter({ entityType: entityType, q: this.q, ...this.timeFilter })
// if (entityType === 'ip') {
// this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
// }
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ q: this.q, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
// this.queryListTotal({ q: this.q, ...this.timeFilter })
} else {
this.limitFilterType = false
this.queryFilter({ entityType: 'ip', q: this.q, ...this.timeFilter })
this.queryFilter({ entityType: 'domain', q: this.q, ...this.timeFilter })
this.queryFilter({ entityType: 'app', q: this.q, ...this.timeFilter })
this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'ip', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'domain', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'app', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ q: this.q, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
// this.queryListTotal({ q: this.q, ...this.timeFilter })
}
} else {
this.limitFilterType = false
this.queryFilter({ entityType: 'ip', ...this.timeFilter })
this.queryFilter({ entityType: 'app', ...this.timeFilter })
this.queryFilter({ entityType: 'domain', ...this.timeFilter })
this.queryFilter({ entityType: 'dns', ...this.timeFilter })
// this.queryFilter({ entityType: 'ip', ...this.timeFilter })
// this.queryFilter({ entityType: 'app', ...this.timeFilter })
// this.queryFilter({ entityType: 'domain', ...this.timeFilter })
// this.queryFilter({ entityType: 'dns', ...this.timeFilter })
this.queryFilterNew({ ...this.pageObj, ...this.timeFilter })
this.queryList({ ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ ...this.timeFilter })
this.queryCount({ ...this.pageObj, ...this.timeFilter })
// this.queryListTotal({ ...this.timeFilter })
// 延时一秒避免初始化时pageSize为20pageNo为1也会调用“搜索”的情况
if (!this.initFlag) {
@@ -636,27 +663,87 @@ export default {
}
})
},
/** 新版查询filter数据 */
queryFilterNew (params) {
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || ''
}
this.loadingLeft = true
const aggCountry = get(api.entity.entityList.aggCountry, queryParams)
const aggCity = get(api.entity.entityList.aggCity, queryParams)
const aggAsn = get(api.entity.entityList.aggAsn, queryParams)
Promise.all([aggCountry, aggCity, aggAsn]).then(response => {
response.forEach((item, index) => {
if (item.code === 200 && item.data.list) {
this.newFilterData[index].data = []
item.data.list.forEach(item => {
const obj = { label: item.value, flag: '011-china', topColumn: 'country', value: item.uniqueEntities }
if (index === 0) {
obj.flag = item.uniqueEntities // 接口字段名称为'China'目前svg名称为'011-china',后续再指定方案调整
}
if (index === 1) {
obj.topColumn = 'region'
}
if (index === 2) {
obj.topColumn = 'asn'
}
this.newFilterData[index].data.push(obj)
})
}
})
}).catch(e => {
// e
}).finally(() => {
this.loadingLeft = false
})
},
/** 实体列表查询 */
queryList (params) {
this.listLoading = true
const queryParams = {
...params,
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime)
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || ''
}
get(api.entityList, queryParams).then(response => {
get(api.entity.entityList.list, queryParams).then(response => {
if (response.code === 200) {
this.listData = []
this.$nextTick(() => {
this.listData = response.data.result
this.listData = response.data.list
this.pageObj.total = response.data.total
})
} else {
console.error(response.message)
this.$message.error(response.message)
}
}).finally(() => {
this.listLoading = false
})
},
/** 实体基数统计 */
queryCount (params) {
this.loadingCount = true
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || '' // 目前版本搜索不支持实体名称搜索,下版本改进
}
get(api.entity.entityList.summaryCount, queryParams).then(response => {
if (response.code === 200) {
this.summaryCount = response.data
} else {
this.summaryCount = { total: 0, fqdnCount: 0, ipCount: 0, appCount: 0 }
}
}).catch(e => {
console.log(e)
this.summaryCount = { total: 0, fqdnCount: 0, ipCount: 0, appCount: 0 }
}).finally(() => {
this.loadingCount = false
})
},
queryListTotal (params) {
const queryParams = {
@@ -677,11 +764,6 @@ export default {
},
getEntityIndexData () {
const now = window.$dayJs.tz().valueOf()
const timeFilter = {
startTime: parseInt(now / 1000 - 3600),
endTime: parseInt(now / 1000)
}
// Total
this.loadingApp = true
this.loadingDomain = true
@@ -695,61 +777,37 @@ export default {
this.loadingDomainActive = true
this.loadingIpActive = true
get(api.entityTotal, { entityType: 'app' }).then(response => {
get(api.entity.entityList.entityActive).then(response => {
if (response.code === 200) {
this.entityAppTotal = response.data.result
}
this.loadingApp = false
})
get(api.entityTotal, { entityType: 'domain' }).then(response => {
if (response.code === 200) {
this.entityDomainTotal = response.data.result
this.entityDomainTotal = response.data.domainCount
this.entityIpTotal = response.data.ipCount
this.entityAppTotal = response.data.appCount
}
this.loadingDomain = false
})
get(api.entityTotal, { entityType: 'ip' }).then(response => {
if (response.code === 200) {
this.entityIpTotal = response.data.result
}
this.loadingIp = false
this.loadingApp = false
})
// New
get(api.entityNew, { entityType: 'app', ...timeFilter }).then(response => {
get(api.entity.entityList.entityNew).then(response => {
if (response.code === 200) {
this.entityAppNew = response.data.result
}
this.loadingAppNew = false
})
get(api.entityNew, { entityType: 'domain', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityDomainNew = response.data.result
this.entityDomainNew = response.data.domainCount
this.entityIpNew = response.data.ipCount
this.entityAppNew = response.data.appCount
}
this.loadingDomainNew = false
})
get(api.entityNew, { entityType: 'ip', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityIpNew = response.data.result
}
this.loadingIpNew = false
this.loadingAppNew = false
})
// Active
get(api.entityActive, { entityType: 'app', ...timeFilter }).then(response => {
get(api.entity.entityList.entityActive).then(response => {
if (response.code === 200) {
this.entityAppActive = response.data.result
}
this.loadingAppActive = false
})
get(api.entityActive, { entityType: 'domain', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityDomainActive = response.data.result
this.entityDomainActive = response.data.domainCount
this.entityIpActive = response.data.ipCount
this.entityAppActive = response.data.appCount
}
this.loadingDomainActive = false
})
get(api.entityActive, { entityType: 'ip', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityIpActive = response.data.result
}
this.loadingIpActive = false
this.loadingAppActive = false
})
},
cleanFilterData (index) {

View File

@@ -1,150 +1,76 @@
<template >
<template>
<div class="entity-filter-case">
<div class="filter-case__header">{{$t('entities.filter')}}</div>
<div
class="entity-filter"
v-for="(filters, index) in filterData"
:key="index"
>
<div class="filter__header">{{filters.title}}</div>
<div class="filter__body">
<div class="filter-case__header">{{ $t('entities.filter1') }}</div>
<div class="filter__row" v-for="(item, i) in filters.data" :key="i">
<el-popover popper-class="filter__row-popover" placement="right-start" :width="440" v-model:visible="item.showTopTen">
<template #reference>
<div class="filter__row-popover" @click="showTopDialog(i, item, filters)">
<div class="row__label">
<div class="entity-filter" v-for="(item, index) in filterData" :key="index">
<div class="filter__header">
<i :class="item.icon"></i>
<span>{{item.label}}</span>
{{ item.title }}
</div>
<div class="row__value">
<loading :loading="loadingLeft" size="small"></loading>
<span>{{item.value}}</span>
<div class="filter__body" style="position: relative">
<loading :loading="loadingLeft" style="top: -5px;"></loading>
<div class="filter__body-item" v-for="(data, i) in item.data" :key="i" @click="filter(data.label, data)">
<div class="filter__body-item-left">
<!-- 当前无更好方案匹配国旗后续解决-->
<!-- <div v-if="data.flag">-->
<!-- <img :src="require(`../../../public/images/flag/${data.flag}.svg`)" class="filter-country-flag"/>-->
<!-- </div>-->
<div class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">{{ data.label }}</span>
</el-tooltip>
</div>
</div>
</template>
<entity-top
ref="entityTopTenPop"
:loading="loading"
:popover-data="popoverData"
:item-data="itemData"
:total-count="totalCount"
:top-column="item.topColumn"
@filter="filter"
></entity-top>
</el-popover>
<div class="filter__body-item-right">{{ data.value }}</div>
</div>
<div class="filter-hr"></div>
</div>
</div>
</div>
</template>
<script>
import EntityTop from '@/views/entityExplorer/EntityTop'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import Loading from '@/components/common/Loading'
import { getSecond } from '@/utils/date-util'
export default {
name: 'EntityFilter',
components: {
Loading,
EntityTop
},
components: { Loading },
props: {
filterData: Array,
q: String,
timeFilter: Object,
loadingLeft: Boolean
filterData: {
type: Object
},
loadingLeft: {
type: Boolean
}
},
data () {
return {
topList: 'list',
topData: [],
entityTopTenData: [],
currentColumn: {},
totalCount: 0,
loading: false,
popoverData: [],
itemData: {}
}
},
watch: {
currentColumn (n, o) {
if (n.column === 'dnsServerOrgCount') {
this.totalCount = this.filterData[3].orgTotalCount
} else if (n.column === 'dnsServerSoftwareCount') {
this.totalCount = this.filterData[3].softwareTotalCount
} else if (n.column === 'dnsServerOsCount') {
this.totalCount = this.filterData[3].osTotalCount
} else if (n.column === 'categoryDistinctCount' && n.type === 'app') {
this.totalCount = this.filterData[1].totalCount
} else {
let count = 0
this.filterData.forEach(f => {
const filter = f.data.some(d => d.column === n.column)
if (filter) {
count = f.totalCount
}
})
this.totalCount = count
}
disabledLabel: true
}
},
methods: {
showTopDialog (i, item, filter) {
if (this.currentColumn.column === item.column && item.showTopTen) {
item.showTopTen = false
return
}
this.filterData.forEach(f => {
f.data.forEach(ff => {
ff.showTopTen = false
})
})
item.showTopTen = true
this.currentColumn = {
column: item.column,
type: filter.type
}
const queryParams = {
q: this.q,
entityType: filter.type,
column: item.topColumn,
top: 10,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
this.loading = true
this.popoverData = []
this.itemData = {}
get(api.filterTop, queryParams).then(response => {
if (response.code === 200) {
if (this.currentColumn.column === item.column) {
if (filter.type === 'dns') {
this.popoverData = response.data.result.filter(f => {
return f.count > 0
})
/**
* 判断文字是否溢出超出则鼠标移入tooltip显示否则鼠标移入不显示
* @param id
*/
handleMouse (id) {
const dom = document.getElementById(id)
if (dom) {
const width = document.getElementById(id).offsetWidth
this.disabledLabel = width < 180
} else {
this.popoverData = response.data.result
this.disabledLabel = true
}
this.itemData = item
}
} else {
this.popoverData = []
this.itemData = item
}
this.loading = false
}).catch(e => {
this.popoverData = []
this.itemData = item
this.loading = false
})
},
filter (name, topData) {
this.showTopDialog('', topData)
this.$emit('filter', name, topData)
filter (name, data) {
this.$emit('filter', name, data)
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -9,75 +9,76 @@
<div class="cn-entity__case">
<div class="cn-entity__icon"><i :class="iconClass"></i></div>
<div class="cn-entity__row">
<div class="cn-entity__header">
{{ entityData.ipAddr || entityData.domainName || entityData.appName || 'Unknown' }}
<!-- <div class="cn-entity__header" style="display: flex">-->
<!-- <span class="cn-entity__header-title">{{ entityData.ipAddr || entityData.domainName || entityData.appName || 'Unknown' }}</span>-->
<!-- <span class="entity-detail" style="display: flex">-->
<!-- <span style="width: 62px;" class="entity-tag entity-tag&#45;&#45;small entity-tag&#45;&#45;level-two-positive margin-r-6">信息技术</span>-->
<!-- <span style="width: 50px;" class="entity-tag entity-tag&#45;&#45;small entity-tag&#45;&#45;level-two-normal margin-r-6">互联网</span>-->
<!-- </span>-->
<!--标签-->
<div class="cn-entity__header" style="display: flex;align-items: center">
<span class="cn-entity__header-title">{{ entityData.entityValue || 'Unknown' }}</span>
<span class="entity-detail" style="display: flex;margin-left: 6px;margin-top: 1px;">
<span v-for="(item, index) in levelTwoTags" :key="index" class="entity-tag entity-tag--small margin-r-10" :class="`entity-tag--level-two-${item.type}`">
{{ item.value }}
</span>
</span>
</div>
<div class="cn-entity__body">
<div class="body__basic-info">
<div class="basic-info">
<template v-if="entityData.entityType === 'ip'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-country"></i>
<span>{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.ipLocationCountry || '-' }}</span>
<span class="row-item-label">{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.location ? entityData.location.country : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-position"></i>
<span>{{ $t('overall.region') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ ipLocationRegion(entityData) }}</span>
<span class="row-item-label">{{ $t('overall.region') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.location ? ipLocationRegion(entityData.location) : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-cloud"></i>
<span>{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.ipAsn || '-' }}</span>
<span class="row-item-label">{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.asn ? entityData.asn.asn : '-' }}</span>
</div>
</template>
<template v-else-if="entityData.entityType === 'domain'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-category"></i>
<span>{{ $t('entities.domainDetail.categoryGroup') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.domainCategoryGroup || '-' }}</span>
<i class="cn-icon cn-icon-sub-category"></i>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.name : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i>
<span>{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.domainCategory || '-' }}</span>
<i class="cn-icon cn-icon-category"></i>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.group : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-credit"></i>
<span>{{ $t('entities.reputationLevel') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.domainReputationScore || '-' }}</span>
<span class="row-item-label">{{ $t('entities.reputationLevel') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.reputationLevel : '-' }}</span>
</div>
</template>
<template v-else-if="entityData.entityType === 'app'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-id"></i>
<span>{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.appCategory || '-' }}</span>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.appCategory : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-category"></i>
<span>{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.appSubcategory || '-' }}</span>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.appSubcategory : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i>
<span>{{ $t('entities.risk') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ appRisk(entityData.appRisk) || '-' }}</span>
<span class="row-item-label">{{ $t('entities.risk') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? appRisk(entityData.category.appRisk) : '-' }}</span>
</div>
</template>
<!-- 通用字段 -->
<div class="basic-info__item">
<div class="item__box">
<i class="cn-icon cn-icon-rise"></i>
<span>{{ $t('entities.sentThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span>
<span class="row-item-label">{{ $t('entities.sentThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">
{{
entityData.bytesSentRate ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'
}}
@@ -107,8 +108,8 @@
<div class="basic-info__item">
<div class="item__box">
<i class="cn-icon cn-icon-fall"></i>
<span>{{ $t('entities.receivedThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span>
<span class="row-item-label">{{ $t('entities.receivedThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">
{{
entityData.bytesReceivedRate ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'
}}
@@ -133,23 +134,7 @@
</div>
</div>
</div>
<!--新版实体列表改版去除这一段-->
<div class="basic-info__item">
<i class="cn-icon cn-icon-entity-alert"></i>
<span>{{ $t('entities.recentAlert') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.performanceCount }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-safe"></i>
<span>{{ $t('entities.recentSecurity') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.securityCount }}</span>
</div>
<!--新版实体列表改版去除这一段-->
</div>
<!-- <div class="show-detail" @click="showDetail">-->
<!-- {{ $t('overall.detail') }}>-->
<!-- </div>-->
<!-- 新版实体列表改版后续记得解开-->
</div>
</div>
</div>
@@ -172,6 +157,10 @@ import DetailOverview from '@/views/entityExplorer/entityList/detailOverview/Det
import entityListMixin from './entityListMixin'
import relatedServer from '@/mixins/relatedServer'
import Loading from '@/components/common/Loading'
import axios from 'axios'
import { api } from '@/utils/api'
import { entityDetailTags, psiphon3IpType } from '@/utils/constants'
import _ from 'lodash'
export default {
name: 'Row',
@@ -188,35 +177,109 @@ export default {
data () {
return {
loading: false,
isCollapse: true // 是否是折叠状态
isCollapse: true, // 是否是折叠状态
levelTwoTags: []
}
},
computed: {
ipLocationRegion () {
return function (entityData) {
const hasProvinceAndCity =
entityData.ipLocationProvince &&
entityData.ipLocationCity &&
entityData.ipLocationProvince !== 'null' &&
entityData.ipLocationCity !== 'null'
entityData.province &&
entityData.city &&
entityData.province !== 'null' &&
entityData.city !== 'null'
const hasProvince =
entityData.ipLocationProvince &&
entityData.ipLocationProvince !== 'null'
entityData.province &&
entityData.province !== 'null'
const hasCity =
entityData.ipLocationCity && entityData.ipLocationCity !== 'null'
entityData.city && entityData.city !== 'null'
if (hasProvinceAndCity) {
return `${entityData.ipLocationProvince}, ${entityData.ipLocationCity}`
return `${entityData.province}, ${entityData.city}`
} else if (hasProvince) {
return entityData.ipLocationProvince
return entityData.province
} else if (hasCity) {
return entityData.ipLocationCity
return entityData.city
} else {
return '-'
}
}
}
},
mounted () {
this.initData()
this.initTagsData()
},
methods: {
initData () {
let url = ''
switch (this.entity.entityType) {
case ('domain'): {
url = api.entity.entityList.domainBasicInfo
break
}
case ('ip'): {
url = api.entity.entityList.ipBasicInfo
break
}
case ('app'): {
url = api.entity.entityList.appBasicInfo
break
}
}
axios.get(`${url}?resource=${this.entity.entityValue}`).then(response => {
this.$nextTick(() => {
this.entityData = { ...response.data.data, ...this.entity }
})
})
},
initTagsData () {
let url = ''
switch (this.entity.entityType) {
case ('domain'): {
url = api.entity.entityList.domainTags
break
}
case ('ip'): {
url = api.entity.entityList.ipTags
break
}
case ('app'): {
url = api.entity.entityList.appTags
break
}
}
axios.get(`${url}?resource=${this.entity.entityValue}`).then(responese => {
const res = responese.data
if (res.code === 200) {
Object.keys(res.data).forEach(k => {
if (k !== 'userDefinedTags' && res.data[k]) {
Object.keys(res.data[k]).forEach(k2 => {
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
if (find) {
this.levelTwoTags.push({ key: k2, value: this.tagValueHandler(k, k2, res.data[k][k2]), type: find.type })
}
})
}
})
if (_.isArray(res.data.userDefinedTags)) {
this.levelTwoTags = _.concat(this.levelTwoTags, res.data.userDefinedTags.map(tag => ({ value: tag.tagValue, type: 'normal' })))
}
this.hideTagArea = _.isEmpty(this.levelTwoTags)
}
})
},
tagValueHandler (k, k2, value) {
if (k === 'psiphon3Ip') {
if (k2 === 'type') {
const find = psiphon3IpType.find(t => t.value === value)
if (find) {
return find.name
}
}
}
return value
},
/* 切换折叠状态 */
switchCollapse () {
this.isCollapse = !this.isCollapse

View File

@@ -4,23 +4,23 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">APP ID</div>
<div class="row__content">{{entity.appId|| '-'}}</div>
<div class="row__content">{{entity.category.appId || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
<div class="row__content">{{entity.appCategory|| '-'}}</div>
<div class="row__content">{{entity.category.appCategory || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.subcategory')}}</div>
<div class="row__content">{{entity.appSubcategory || '-'}}</div>
<div class="row__content">{{entity.category.appSubcategory || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.riskLevel')}}</div>
<div class="row__content">{{appRisk(entity.appRisk) || '-'}}</div>
<div class="row__content">{{appRisk(parseInt(entity.category.appRisk)) || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{entity.appDescription || '-'}}</div>
<div class="row__content">{{entity.category.appDescription || '-'}}</div>
</div>
</div>
</div>
@@ -43,103 +43,78 @@
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailSend${entity.appName}`" ></div>
<div class="row__charts" :id="`entityDetailSend${entity.entityValue}`" ></div>
</div>
</div>
<div class="row__content">
<div class="row__content row__content-accept">
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailReceived${entity.appName}`" ></div>
<div class="row__charts" :id="`entityDetailReceived${entity.entityValue}`" ></div>
</div>
</div>
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.networkQualityRating')}}</div>
<loading :loading="loadingNetworkQuality" size="small" inner-style="left: 1rem;"></loading>
<div class="entity-score" v-if="!loadingNetworkQuality">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.relationship')}}</div>
<div class="overview__content domain__content">
<div class="overview__tags domain__tags" ref="relationship">
<div class="overview__domain-tabs overview__domain-tabs-loading">
<div class="overview__tags domain__tags" ref="relationship"></div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedIp')}}</div>
<loading :loading="loadingRelationshipOne" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataOne.length}}</span>
<span class="tag__desc">{{$t('entities.relatedDomains')}}</span>
<div class="row__content overview__row-related">
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<span class="tag__desc">{{item.domain}}</span>
</div>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataOne, 1)" v-if="relationshipShowOne">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreOne" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataOne" :key="item">
<span v-if="item.domain" :title="item.domain">{{item.domain}}</span>
<div v-if="relationshipShowOne">
<el-popover placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataOne" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
<div class="overview__domain-tabs overview__domain-tabs-loading">
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedIp')}}</div>
<loading :loading="loadingRelationshipTwo" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataTwo.length}}</span>
<span class="tag__desc">{{$t('entities.relatedServerIp')}}</span>
<div class="row__content">
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<span class="tag__desc">{{item.ip}}</span>
</div>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataTwo, 2)" v-if="relationshipShowTwo">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreTwo" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataTwo" :key="item">
<span v-if="item.ip" :title="item.ip">{{item.ip}}</span>
<div v-if="relationshipShowTwo">
<el-popover class="entity-expand-detail" placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataTwo" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.networkQuality')}}</div>
<div class="overview__content overview__content-loading-net">
<loading :loading="loadingNetworkQuality" size="small"></loading>
<div class="overview__row overview__row--single-value">
<chart-single-value
v-for="(chartInfo, i) in singleValues.chartInfos"
:chart-info="chartInfo"
:chart-data="singleValues.chartDatas[i]"
:key="i"
class="cn-chart__single-value--detail-overview"
></chart-single-value>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('entities.accessLink')}}</div>
<div class="overview__content">
<div class="overview__tags">
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingOut" size="small"></loading>
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutId ? entityData.linkOutId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingIn" size="small"></loading>
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInId ? entityData.linkInId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.alert')}}</div>
<div class="overview__content overview__content-loading">
@@ -148,20 +123,17 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="performanceData.length > 0">
<div class="row__label">{{$t('entities.recentAlert')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentAlert')}}</div>
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
<div>{{performance.eventType}}</div>
</div>
<div class="row__content-loading" style="position: relative;" >
<loading :loading="!loadingAlert && loadingPerformance[index]?loadingPerformance[index]:false" :id="`loading${entity.ipAddr}_${index}`" size="small"></loading>
<div class="row__charts" :id="`entityPerformanceChart${entity.appName}_${index}`"></div>
<div class="row__content-loading" style="position: relative;">
<div class="performance-event-remark">{{performance.eventType}}</div>
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font" v-if="performanceData && performanceData.length > 5">
@@ -177,15 +149,26 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="securityData.length > 0">
<div class="row__label">{{$t('entities.recentSecurity')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentSecurity')}}</div>
<div class="row__content">{{entityData.securityNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(security, index) in entityData.securityList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
<div>{{security.securityType}}</div>
</div>
<div class="cn-detection__header">
<i class="cn-icon cn-icon-attacker"></i>
<span>{{ security.offenderIp }}</span>
<div class="domain" v-if="security.offenderIp===security.serverIp">{{ security.domain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span>{{ security.victimIp }}</span>
<div class="domain" v-if="security.victimIp===security.serverIp">{{ security.domain }}</div>
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font" v-if="securityData && securityData.length > 5">
@@ -213,34 +196,38 @@ import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import ChartSingleValue from '@/views/charts/charts/ChartSingleValue'
import { get } from '@/utils/http'
import relatedServer from '@/mixins/relatedServer'
import { getSecond, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
export default {
name: 'App',
mixins: [entityDetailMixin, relatedServer],
components: {
Chart,
Loading,
ChartSingleValue
Loading
},
data () {
return {
// entityData: {}
entityType: 'app',
trafficUrl: api.entityAppDetailTraffic,
// trafficUrl: api.entityAppDetailTraffic,
trafficUrl: api.entity.entityList.appThroughput,
relationUrl: api.entityAppDetailRelation,
networkQuantityUrl: api.entityAppDetailNetworkQuantity,
// networkQuantityUrl: api.entityAppDetailNetworkQuantity,
networkQuantityUrl: api.entity.entityList.appPerformance,
linkInUrl: api.entityAppDetailLinkIn,
linkOutUrl: api.entityAppDetailLinkOut,
performanceUrl: api.entityAppDetailPerformance,
securityUrl: api.entityAppDetailSecurity,
trafficUrlMap: api.entityAppDetailTrafficMap,
relatedServerDomainUrl: api.entityAppRelatedServerDomain,
relatedServerIpUrl: api.entityAppRelatedServerIp,
// performanceUrl: api.entityAppDetailPerformance,
performanceUrl: api.entity.entityList.appEventPerformance,
securityUrl: api.entity.entityList.appSecurity,
// securityUrl: api.entityAppDetailSecurity,
// trafficUrlMap: api.entityAppDetailTrafficMap,
trafficUrlMap: api.entity.entityList.appTrafficMap,
relatedServerDomainUrl: api.entity.entityList.appRelatedDomain,
relatedServerIpUrl: api.entity.entityList.appRelatedIp,
chartData: null,
listMode: 'list',
singleValues: {
@@ -296,7 +283,8 @@ export default {
i18n: 'entities.pktRetransPercent'
}
],
chartDatas: [null, null, null, null, null],
chartDatas: [null, null, null, null, null]
},
loadingTraffic: false,
loadingRelationshipOne: false,
loadingRelationshipTwo: false,
@@ -307,23 +295,22 @@ export default {
loadingSecurityEvents: false,
loadingMap: false
}
}
},
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
const queryParams = {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
appName: this.entity.appName
resource: this.entity.entityValue,
appName: this.entity.entityValue
}
return queryParams
},
getPerformanceQueryParams () {
const queryParams = {
appName: this.entity.appName
return {
appName: this.entity.entityValue
}
return queryParams
},
handleRelationData (result) {
this.entityData.domainCount = result.domainCount
@@ -353,6 +340,7 @@ export default {
})
},
setup (props) {
const entityData = ref({ ...props.entity })
return {
chart: {
params: {
@@ -366,7 +354,8 @@ export default {
entityCopy: {
..._.cloneDeep(props.entity)
},
unitConvert
unitConvert,
entityData
}
}
}

View File

@@ -4,36 +4,32 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
<div class="row__content">{{entityData.domainCategory || '-'}}</div>
<div class="row__content">{{entityData.category ? entityData.category.categoryName : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.domainDetail.categoryGroup')}}</div>
<div class="row__content">{{entityData.domainCategoryGroup || '-'}}</div>
<div class="row__content">{{entityData.category ? entityData.category.categoryGroup : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.reputationLevel')}}</div>
<div class="row__content">{{entityData.domainReputationScore || '-'}}</div>
<div class="row__content">{{entityData.category ? entityData.category.reputationLevel : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.registration')}}</div>
<div class="row__content">{{entityData.domainWhoisAddress || '-'}}</div>
<div class="row__content">{{entityData.whois ? entityData.whois.registrantCountry : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.org')}}</div>
<div class="row__content">{{entityData.domainWhoisOrg || '-'}}</div>
<div class="row__content">{{entityData.whois ? entityData.whois.registrantOrg : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpCompanyName')}}</div>
<div class="row__content">{{entityData.domainIcpCompanyName || '-'}}</div>
<div class="row__content">{{entityData.icp ? entityData.icp.icpCompanyName : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpLicense')}}</div>
<div class="row__content">{{entityData.domainIcpSiteLicense || '-'}}</div>
<div class="row__content">{{entityData.icp ? entityData.icp.icpSiteLicense : '-'}}</div>
</div>
<!-- <div class="overview__row">
<div class="row__label row__label&#45;&#45;width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{entityData.domainDescription || '-'}}</div>
</div>-->
</div>
</div>
<div class="overview-item">
@@ -55,103 +51,78 @@
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailSend${entity.domainName}`" >{</div>
<div class="row__charts" :id="`entityDetailSend${entity.entityValue}`" >{</div>
</div>
</div>
<div class="row__content">
<div class="row__content row__content-accept">
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailReceived${entity.domainName}`" ></div>
<div class="row__charts" :id="`entityDetailReceived${entity.entityValue}`" ></div>
</div>
</div>
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.networkQualityRating')}}</div>
<loading :loading="loadingNetworkQuality" size="small" inner-style="left: 1rem;"></loading>
<div class="entity-score" v-if="!loadingNetworkQuality">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.relationship')}}</div>
<div class="overview__content domain__content">
<div class="overview__tags domain__tags" ref="relationship">
<div class="overview__domain-tabs overview__domain-tabs-loading">
<div class="overview__tags domain__tags" ref="relationship"></div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedApp')}}</div>
<loading :loading="loadingRelationshipOne" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataOne.length}}</span>
<span class="tag__desc">{{$t('entities.relatedApp')}}</span>
<div class="row__content overview__row-related">
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<span class="tag__desc">{{item.appName}}</span>
</div>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataOne, 1)" v-if="relationshipShowOne">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreOne" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataOne" :key="item">
<span v-if="item.appName" :title="item.appName">{{item.appName}}</span>
<div v-if="relationshipShowOne">
<el-popover placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataOne" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
<div class="overview__domain-tabs overview__domain-tabs-loading">
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedIp')}}</div>
<loading :loading="loadingRelationshipTwo" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataTwo.length}}</span>
<span class="tag__desc">{{$t('entities.relatedServerIp')}}</span>
<div class="row__content">
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<span class="tag__desc">{{item.ip}}</span>
</div>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataTwo, 2)" v-if="relationshipShowTwo">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreTwo" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataTwo" :key="item">
<span v-if="item.ip" :title="item.ip">{{item.ip}}</span>
<div v-if="relationshipShowTwo">
<el-popover class="entity-expand-detail" placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataTwo" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.networkQuality')}}</div>
<div class="overview__content overview__content-loading-net">
<loading :loading="loadingNetworkQuality" size="small"></loading>
<div class="overview__row overview__row--single-value">
<chart-single-value
v-for="(chartInfo, i) in singleValues.chartInfos"
:chart-info="chartInfo"
:chart-data="singleValues.chartDatas[i]"
:key="i"
class="cn-chart__single-value--detail-overview"
></chart-single-value>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('entities.accessLink')}}</div>
<div class="overview__content">
<div class="overview__tags">
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingOut" size="small"></loading>
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutId ? entityData.linkOutId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingIn" size="small"></loading>
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInId ? entityData.linkInId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.alert')}}</div>
<div class="overview__content overview__content-loading">
@@ -160,18 +131,16 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="performanceData.length > 0">
<div class="row__label">{{$t('entities.recentAlert')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentAlert')}}</div>
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
<div>{{performance.eventType}}</div>
</div>
<div class="row__content-loading" style="position: relative;" >
<loading :loading="!loadingAlert && loadingPerformance[index]?loadingPerformance[index]:false" :id="`loading${entity.ipAddr}_${index}`" size="small"></loading>
<div class="row__charts" :id="`entityPerformanceChart${entity.domainName}_${index}`"></div>
<div class="row__content-loading" style="position: relative;">
<div class="performance-event-remark">{{performance.eventType}}</div>
</div>
<div class="row__desc"></div>
</div>
@@ -188,14 +157,24 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="securityData.length > 0">
<div class="row__label">{{$t('entities.recentSecurity')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentSecurity')}}</div>
<div class="row__content">{{entityData.securityNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(security, i) in entityData.securityList" :key="i">
<div class="row__label row__label--width160">{{dateFormatByAppearance(getMillisecond(security.startTime)) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(getMillisecond(security.startTime)) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
<div>{{security.securityType}}</div>
</div>
<div class="cn-detection__header">
<i class="cn-icon cn-icon-attacker"></i>
<span>{{ security.offenderIp }}</span>
<div class="domain" v-if="security.offenderIp===security.serverIp">{{ security.domain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span>{{ security.victimIp }}</span>
<div class="domain" v-if="security.victimIp===security.serverIp">{{ security.domain }}</div>
</div>
<div class="row__desc"></div>
</div>
@@ -218,7 +197,6 @@
</template>
<script>
import ChartSingleValue from '@/views/charts/charts/ChartSingleValue'
import { api } from '@/utils/api'
import entityDetailMixin from './entityDetailMixin'
import { unitTypes } from '@/utils/constants'
@@ -227,13 +205,13 @@ import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import { get } from '@/utils/http'
import relatedServer from '@/mixins/relatedServer'
import { getSecond, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
export default {
name: 'Domain',
components: {
ChartSingleValue,
Loading,
Chart
},
@@ -242,16 +220,21 @@ export default {
return {
// entityData: {},
entityType: 'domain',
trafficUrl: api.entityDomainDetailTraffic,
// trafficUrl: api.entityDomainDetailTraffic,
trafficUrl: api.entity.entityList.domainThroughput,
relationUrl: api.entityDomainDetailRelation,
networkQuantityUrl: api.entityDomainDetailNetworkQuantity,
networkQuantityUrl: api.entity.entityList.domainPerformance,
// networkQuantityUrl: api.entityDomainDetailNetworkQuantity,
linkInUrl: api.entityDomainDetailLinkIn,
linkOutUrl: api.entityDomainDetailLinkOut,
performanceUrl: api.entityDomainDetailPerformance,
securityUrl: api.entityDomainDetailSecurity,
trafficUrlMap: api.entityDomainDetailTrafficMap,
relatedServerIpUrl: api.entityDomainRelatedServerIp,
relatedServerAppUrl: api.entityDomainRelatedServerApp,
// performanceUrl: api.entityDomainDetailPerformance,
performanceUrl: api.entity.entityList.domainEventPerformance,
// securityUrl: api.entityDomainDetailSecurity,
securityUrl: api.entity.entityList.domainSecurity,
// trafficUrlMap: api.entityDomainDetailTrafficMap,
trafficUrlMap: api.entity.entityList.domainTrafficMap,
relatedServerIpUrl: api.entity.entityList.domainRelatedIp,
relatedServerAppUrl: api.entity.entityList.domainRelatedApp,
basicProperties: api.entityDomainDetailBasic,
chartData: null,
listMode: 'list',
@@ -308,7 +291,8 @@ export default {
i18n: 'entities.pktRetransPercent'
}
],
chartDatas: [null, null, null, null, null],
chartDatas: [null, null, null, null, null]
},
loadingTraffic: false,
loadingRelationshipOne: false,
loadingRelationshipTwo: false,
@@ -319,23 +303,22 @@ export default {
loadingSecurityEvents: false,
loadingMap: false
}
}
},
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
const queryParams = {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
domain: this.entity.domainName
resource: this.entity.entityValue,
domain: this.entity.entityValue
}
return queryParams
},
getPerformanceQueryParams () {
const queryParams = {
domain: this.entity.domainName
return {
domain: this.entity.entityValue
}
return queryParams
},
handleRelationData (result) {
this.entityData.appCount = result.appCount
@@ -383,9 +366,10 @@ export default {
})
},
setup (props) {
const entityData = ref({ ...props.entity })
const entityCopy = {
..._.cloneDeep(props.entity),
domain: props.entity.domainName
domain: props.entity.entityValue
}
return {
chart: {
@@ -398,7 +382,8 @@ export default {
},
entityCopy,
unitTypes,
unitConvert
unitConvert,
entityData
}
}
}

View File

@@ -4,11 +4,15 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.location')}}</div>
<div class="row__content">{{ipLocationRegion(entity)}}</div>
<div class="row__content">{{ipLocationRegion(entity.location)}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">ASN</div>
<div class="row__content">{{entity.ipAsn || '-'}}</div>
<div class="row__content">{{entity.asn ? entity.asn.asn : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.openPort')}}</div>
<div class="row__content">{{ openPort }}</div>
</div>
</div>
</div>
@@ -44,7 +48,7 @@
<!-- 曲线-->
<div class="row__content-loading">
<loading :loading="!loadingDns && loading" size="small"></loading>
<div class="row__charts" :id="`entityDnsServerInfo${entity.ipAddr}`"></div>
<div class="row__charts" :id="`entityDnsServerInfo${entity.entityValue}`"></div>
</div>
</div>
</div>
@@ -70,103 +74,77 @@
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailSend${entity.ipAddr}`"></div>
<div class="row__charts" :id="`entityDetailSend${entity.entityValue}`"></div>
</div>
</div>
<div class="row__content">
<div class="row__content row__content-accept">
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailReceived${entity.ipAddr}`"></div>
<div class="row__charts" :id="`entityDetailReceived${entity.entityValue}`"></div>
</div>
</div>
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.networkQualityRating')}}</div>
<loading :loading="loadingNetworkQuality" size="small" inner-style="left: 1rem;"></loading>
<div class="entity-score" v-if="!loadingNetworkQuality">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.relationship')}}</div>
<div class="overview__content domain__content">
<div class="overview__tags domain__tags" ref="relationship">
<div class="overview__domain-tabs overview__domain-tabs-loading">
<div class="overview__tags domain__tags" ref="relationship"></div>
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedApp')}}</div>
<loading :loading="loadingRelationshipOne" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataOne.length}}</span>
<span class="tag__desc">{{$t('entities.relatedDomains')}}</span>
<div class="row__content">
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<span class="tag__desc">{{item.domain}}</span>
</div>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataOne, 1)" v-if="relationshipShowOne">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreOne" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataOne" :key="item">
<span v-if="item.domain" :title="item.domain">{{item.domain}}</span>
<div v-if="relationshipShowOne">
<el-popover placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div class="popover-content" v-for="(item, index) in relationshipDataOne" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
<div class="overview__domain-tabs overview__domain-tabs-loading">
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.relatedDomain')}}</div>
<loading :loading="loadingRelationshipTwo" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataTwo.length}}</span>
<span class="tag__desc">{{$t('entities.relatedApp')}}</span>
<div class="row__content">
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<span class="tag__desc">{{item.appName}}</span>
</div>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataTwo, 2)" v-if="relationshipShowTwo">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreTwo" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataTwo" :key="item">
<span v-if="item.appName" :title="item.appName">{{item.appName}}</span>
<div v-if="relationshipShowTwo">
<el-popover class="entity-expand-detail" placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataTwo" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.networkQuality')}}</div>
<div class="overview__content overview__content-loading-net">
<loading :loading="loadingNetworkQuality" size="small"></loading>
<div class="overview__row overview__row--single-value">
<chart-single-value
v-for="(chartInfo, i) in singleValues.chartInfos"
:chart-info="chartInfo"
:chart-data="singleValues.chartDatas[i]"
:key="i"
class="cn-chart__single-value--detail-overview"
></chart-single-value>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('entities.accessLink')}}</div>
<div class="overview__content">
<div class="overview__tags">
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingOut" size="small"></loading>
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutId ? entityData.linkOutId : '-'}},&nbsp;</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingIn" size="small"></loading>
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInId ? entityData.linkInId : '-'}},&nbsp;</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.alert')}}</div>
<div class="overview__content overview__content-loading">
@@ -175,18 +153,16 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="performanceData.length > 0">
<div class="row__label">{{$t('entities.recentAlert')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentAlert')}}</div>
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
<div>{{performance.eventType}}</div>
</div>
<div class="row__content-loading" style="position: relative; padding-left: 10px;">
<loading :loading="!loadingAlert && loadingPerformance[index]?loadingPerformance[index]:false" :id="`loading${entity.ipAddr}_${index}`" size="small"></loading>
<div class="row__charts" :id="`entityPerformanceChart${entity.ipAddr}_${index}`"></div>
<div class="row__content-loading" style="position: relative;">
<div class="performance-event-remark">{{performance.eventType}}</div>
</div>
<div class="row__desc"></div>
</div>
@@ -203,15 +179,26 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="securityData.length > 0">
<div class="row__label">{{$t('entities.recentSecurity')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentSecurity')}}</div>
<div class="row__content">{{entityData.securityNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(security, index) in entityData.securityList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
<div>{{security.securityType}}</div>
</div>
<div class="cn-detection__header" >
<i class="cn-icon cn-icon-attacker"></i>
<span>{{ security.offenderIp }}</span>
<div class="domain" v-if="security.offenderIp===security.serverIp">{{ security.domain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span>{{ security.victimIp }}</span>
<div class="domain" v-if="security.victimIp===security.serverIp">{{ security.domain }}</div>
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font" v-if="securityData && securityData.length > 5">
@@ -234,7 +221,6 @@
<script>
import entityDetailMixin from './entityDetailMixin'
import ChartSingleValue from '@/views/charts/charts/ChartSingleValue'
import { api } from '@/utils/api'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
@@ -242,30 +228,35 @@ import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import { get } from '@/utils/http'
import relatedServer from '@/mixins/relatedServer'
import { getSecond, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import axios from 'axios'
export default {
name: 'Ip',
mixins: [entityDetailMixin, relatedServer],
components: {
Loading,
Chart,
ChartSingleValue
Chart
},
data () {
return {
entityType: 'ip',
trafficUrl: api.entityIpDetailTraffic,
trafficUrlMap: api.entityIpDetailTrafficMap,
// trafficUrl: api.entityIpDetailTraffic,
trafficUrl: api.entity.entityList.ipThroughput,
// trafficUrlMap: api.entityIpDetailTrafficMap,
trafficUrlMap: api.entity.entityList.ipTrafficMap,
relationUrl: api.entityIpDetailRelation,
networkQuantityUrl: api.entityIpDetailNetworkQuantity,
// networkQuantityUrl: api.entityIpDetailNetworkQuantity,
networkQuantityUrl: api.entity.entityList.ipPerformance,
linkInUrl: api.entityIpDetailLinkIn,
linkOutUrl: api.entityIpDetailLinkOut,
performanceUrl: api.entityIpDetailPerformance,
securityUrl: api.entityIpDetailSecurity,
relatedServerDomainUrl: api.entityIpRelatedServerDomain,
relatedServerAppUrl: api.entityIpRelatedServerApp,
performanceUrl: api.entity.entityList.ipEventPerformance,
// performanceUrl: api.entityIpDetailPerformance,
// securityUrl: api.entityIpDetailSecurity,
securityUrl: api.entity.entityList.ipSecurity,
relatedServerDomainUrl: api.entity.entityList.ipRelatedDomain,
relatedServerAppUrl: api.entity.entityList.ipRelatedApp,
entityDetectionsIpUrl: api.entityDetectionsIp,
entityDetectionsIpQueryRateUrl: api.entityDetectionsIpQueryRate,
listMode: 'list',
@@ -336,21 +327,22 @@ export default {
loadingIn: false,
loadingAlert: false,
loadingSecurityEvents: false,
loadingMap: false
loadingMap: false,
openPort: '-'
}
},
computed: {
ipLocationRegion () {
return function (entityData) {
let result = ''
if (entityData.ipLocationCountry) {
result += `${entityData.ipLocationCountry},`
if (entityData.country) {
result += `${entityData.country},`
}
if (entityData.ipLocationProvince) {
result += `${entityData.ipLocationProvince},`
if (entityData.province) {
result += `${entityData.province},`
}
if (entityData.ipLocationCity) {
result += `${entityData.ipLocationCity},`
if (entityData.city) {
result += `${entityData.city},`
}
result = result.substr(0, result.length - 1)
if (!result) {
@@ -381,19 +373,19 @@ export default {
},
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
const queryParams = {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
ip: this.entity.ipAddr
resource: this.entity.entityValue,
ip: this.entity.entityValue
}
return queryParams
},
getPerformanceQueryParams () {
const queryParams = {
serverIp: this.entity.ipAddr
return {
serverIp: this.entity.entityValue
}
return queryParams
},
handleRelationData (result) {
this.entityData.appCount = result.appCount
@@ -411,11 +403,29 @@ export default {
queryRelated () {
this.getRelatedServerDataOne(this.relatedServerDomainUrl)
this.getRelatedServerDataTwo(this.relatedServerAppUrl)
},
getOpenPort () {
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
resource: this.entity.entityValue
}
axios.get(api.entity.entityList.ipRelatedPort, { params: params }).then(res => {
if (res.data.code === 200 && res.data.data.result.length) {
this.openPort = ''
res.data.data.result.forEach(item => {
this.openPort += item.port + '/' + item.l7Protocol + ','
})
this.openPort = this.openPort.slice(0, -1)
}
})
}
},
mounted () {
this.queryParams = this.getQueryParams()
this.chartGetMap()
this.getOpenPort()
this.$nextTick(() => {
setTimeout(() => {
this.queryRelated()
@@ -425,7 +435,7 @@ export default {
setup (props) {
const entityCopy = {
..._.cloneDeep(props.entity),
ip: props.entity.ipAddr
ip: props.entity.entityValue
}
return {
chart: {
@@ -434,7 +444,7 @@ export default {
unitType: 'number',
valueColumn: 'sessions'
},
id: props.entity.ipAddr,
id: props.entity.entityValue,
type: 2
},
entityCopy,
@@ -444,3 +454,110 @@ export default {
}
}
</script>
<style lang="scss">
//.type-content {
// margin-bottom:15px;
// display:flex;
// flex-flow: row wrap;
// width:100%;
.data-item {
display: flex;
justify-content: center;
align-items: center;
background: rgba(119,131,145,0.06);
border: 1px solid rgba(119,131,145,0.36);
border-radius: 2px;
height:28px;
padding:8px 15px;
margin-right:10px;
//margin-bottom:15px;
font-size: 12px;
color: #353636;
font-weight: 400;
white-space: nowrap;
}
.show-more-related {
height: 28px;
line-height: 28px;
padding-bottom: 12px;
cursor: pointer;
}
//}
.entity-score {
.circle-icon {
border-radius: 3px;
width: 6px;
height: 6px;
margin-right: 4px;
}
.data-score-red {
background: #E26154;
}
.data-score-yellow {
background: #E5A219;
}
.data-score-green {
background: #749F4D;
}
height:24px;
font-size: 14px;
color: #046ECA;
font-weight: 500;
display:flex;
align-items: center;
justify-content: center;
}
.cn-detection__header {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
padding-bottom: 3px;
color: #333333;
align-items: center;
font-size: 12px;
i {
color: #7b8fa2;
margin-right: 5px;
font-size: 18px;
}
.line {
color: #da5656;
margin-left: 12px;
font-size: xx-small;
font-weight: bold;
}
.circle {
width: 10px;
height: 10px;
border: 2px solid #da5656;
border-radius: 10px;
margin-top: 4px;
margin-right: 12px;
}
.domain {
background: #EFF2F5;
border-radius: 2px;
font-size: 14px;
color: #333333;
letter-spacing: 0;
line-height: 14px;
margin-left: 5px;
font-style: italic;
padding: 0 2px;
font-weight: 500;
}
}
.el-popper {
min-width: 90px !important;
width: auto !important;
max-width: 300px !important;
max-height: 180px !important;
overflow: scroll !important;
line-height: 24px !important;
}
</style>

View File

@@ -6,7 +6,7 @@ import { riskLevelMapping, unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import { shallowRef, markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy } from '@/utils/tools'
import { sortBy, reverseSortBy, computeScore } from '@/utils/tools'
import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api'
@@ -45,29 +45,13 @@ export default {
metricChart: null,
performanceChartList: [],
loadingPerformance: [],
score: '-', // 网络质量评分
performanceMetricEndTimeInterval: 3600 // 服务质量事件指标的结束时间与开始时间的秒间隔
}
},
computed: {
entityName () {
let name
switch (this.entity.entityType) {
case ('ip'): {
name = this.entity.ipAddr
break
}
case ('domain'): {
name = this.entity.domainName
break
}
case ('app'): {
name = this.entity.appName
break
}
default: break
}
return name
return this.entity.entityValue
},
appRisk () {
return function (level) {
@@ -234,7 +218,7 @@ export default {
this.performanceChartList.push(metricChart)
this.echartsArray.push(shallowRef(metricChart))
} else {
const chartDom = document.getElementById(`entityPerformanceChart${this.entityName}${index}`)
const chartDom = document.getElementById(`entityPerformanceChart${this.entityName}_${index}`)
chartDom.innerHTML = '<span style="padding-left:5px;">-</span>'
}
})
@@ -272,6 +256,14 @@ export default {
if (this.networkQuantityUrl) {
get(this.networkQuantityUrl, this.getQueryParams()).then(response => {
if (response.code === 200) {
const data = {
establishLatencyMs: response.data.result.establishLatencyValue || null,
httpResponseLatency: response.data.result.httpResponseLatencyValue || null,
sslConLatency: response.data.result.sslConLatencyValue || null,
tcpLostlenPercent: response.data.result.sequenceGapLossPercentValue || null,
pktRetransPercent: response.data.result.pktRetransPercentValue || null
}
this.score = computeScore(data)
this.entityData.establishLatencyValue = response.data.result.establishLatencyValue
this.entityData.establishLatencyP50 = response.data.result.establishLatencyP50
this.entityData.establishLatencyP90 = response.data.result.establishLatencyP90
@@ -389,11 +381,6 @@ export default {
this.entityData.performanceNum = response.data.result.length
this.performanceData = response.data.result
this.entityData.performanceList = this.getTargetPageData(1, this.showMore.performancePageSize, this.performanceData)
this.$nextTick(() => {
setTimeout(() => {
this.queryEntityDetailPerformanceChart(this.entityData.performanceList, 0)
}, 200)
})
}
this.loadingAlert = false
})
@@ -412,16 +399,16 @@ export default {
},
performanceShowMore (num) {
const startIndex = this.showMore.performancePageSize
// const startIndex = this.showMore.performancePageSize
this.showMore.performancePageSize += num
this.entityData.performanceList = this.getTargetPageData(this.showMore.pageNo, this.showMore.performancePageSize, this.performanceData)
this.$nextTick(() => {
setTimeout(() => {
if (this.entityData.performanceList && this.entityData.performanceList.length > 0) {
this.queryEntityDetailPerformanceChart(this.entityData.performanceList.slice(startIndex, this.entityData.performanceList.length), startIndex)
}
}, 200)
})
// this.$nextTick(() => {
// setTimeout(() => lltext
// if (this.entityData.performanceList && this.entityData.performanceList.length > 0) {
// this.queryEntityDetailPerformanceChart(this.entityData.performanceList.slice(startIndex, this.entityData.performanceList.length), startIndex)
// }
// }, 200)
// })
},
securityShowMore (num) {
@@ -520,6 +507,7 @@ export default {
this.chartOption = _.cloneDeep(entityListLineOption)
setTimeout(() => { this.queryEntityDetail() })
const _this = this
this.entityData = { ...this.entityData, ...this.entity }
this.emitter.on('switch-collapse', function () {
setTimeout(() => { _this.queryEntityDetail() }, 200)
})

View File

@@ -45,43 +45,10 @@ export default {
return className
},
entityType () {
let type
switch (this.entityData.entityType) {
case 'ip': {
type = this.entityData.ipAddr
break
}
case 'domain': {
type = this.entityData.domainName
break
}
case 'app': {
type = this.entityData.appName
break
}
default:
break
}
return type
return this.entity.entityValue
},
entityName () {
let name
switch (this.entityData.entityType) {
case ('ip'): {
name = this.entity.ipAddr
break
}
case ('domain'): {
name = this.entity.domainName
break
}
case ('app'): {
name = this.entity.appName
break
}
default: break
}
return name
return this.entity.entityValue
},
appRisk () {
return function (level) {
@@ -94,32 +61,11 @@ export default {
queryParams () {
let params
const now = window.$dayJs.tz().valueOf()
switch (this.entityData.entityType) {
case ('ip'): {
// eslint-disable-next-line prefer-const
params = {
startTime: this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : Math.floor(now / 1000),
ip: this.entityData.ipAddr
}
break
}
case ('domain'): {
params = {
startTime: this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : Math.floor(now / 1000),
domain: this.entityData.domainName
}
break
}
case ('app'): {
params = {
startTime: this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : Math.floor(now / 1000),
appName: this.entityData.appName
}
break
}
default: break
resource: this.entityData.entityValue
}
return params
}
@@ -130,7 +76,7 @@ export default {
path: '/entityDetail',
query: {
entityType: this.entityData.entityType,
entityName: this.entityData.ipAddr || this.entityData.domainName || this.entityData.appName
entityName: this.entityData.entityValue
}
})
window.open(href, '_blank')
@@ -140,7 +86,7 @@ export default {
path: '/entityGraph',
query: {
entityType: this.entityData.entityType,
entityName: this.entityData.ipAddr || this.entityData.domainName || this.entityData.appName
entityName: this.entityData.entityValue
}
})
window.open(href, '_blank')
@@ -209,9 +155,7 @@ export default {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
appName: this.entityType,
domain: this.entityType,
ip: this.entityType
resource: this.entityType
}
},
queryEntityDetailTraffic () {
@@ -295,24 +239,23 @@ export default {
},
resize () {
this.echartsArray.forEach(item => { item.value.resize() })
}
},
watch: {
entityData: {
deep: true,
handler (n) {
if (n.entityType) {
switch (n.entityType) {
initUrl () {
if (this.entity.entityType) {
switch (this.entity.entityType) {
case 'ip': {
this.trafficUrl = api.entityIpDetailTraffic
// this.trafficUrl = api.entityIpDetailTraffic
this.trafficUrl = api.entity.entityList.ipThroughput
break
}
case 'domain': {
this.trafficUrl = api.entityDomainDetailTraffic
// this.trafficUrl = api.entityDomainDetailTraffic
this.trafficUrl = api.entity.entityList.domainThroughput
break
}
case 'app': {
this.trafficUrl = api.entityAppDetailTraffic
// this.trafficUrl = api.entityAppDetailTraffic
this.trafficUrl = api.entity.entityList.appThroughput
break
}
default:
@@ -320,13 +263,22 @@ export default {
}
}
}
},
watch: {
entityData: {
deep: true,
handler (n) {
this.initUrl()
}
}
},
mounted () {
this.debounceFunc = this.$_.debounce(this.resize, 200)
window.addEventListener('resize', this.debounceFunc)
this.chartOption = _.cloneDeep(entityListLineOption)
this.entityData = _.cloneDeep(this.entity)
// this.entityData = _.cloneDeep(this.entity)
this.entityData = { ...this.entityData, ...this.entity }
this.initUrl()
setTimeout(() => {
this.querySecurity()
this.queryEntityDetailTraffic()

View File

@@ -2,9 +2,7 @@
<div class="explorer-search">
<div class="explorer-search__title" v-show="!showList">{{$t('search.title')}}</div>
<div class="explorer-search__input-case" :class="{'explorer-search__input-case--question-mark-in-line': showList}">
<div class="explorer-search__input">
<!--新版实体列表改版后续记得解开-->
<!--<div class="explorer-search__input" style="border: 1px #DEDEDE solid;height: 42px;">-->
<div class="explorer-search__input" style="border: 1px #DEDEDE solid;height: 42px;">
<advanced-search
ref="search"
:column-list="columnList"