1093: 实体关系探索--右侧详情信息静态页面开发

This commit is contained in:
刘洪洪
2023-06-29 10:46:00 +08:00
parent 7f15139a38
commit 09b37512c9
10 changed files with 1030 additions and 33 deletions

View File

@@ -85,3 +85,6 @@
@import 'views/charts2/entityDetailTabs'; @import 'views/charts2/entityDetailTabs';
@import 'views/charts2/digitalCertificate'; @import 'views/charts2/digitalCertificate';
@import 'views/charts2/entityDetailBasicInfo'; @import 'views/charts2/entityDetailBasicInfo';
@import "views/charts2/graphRightListBlock";
@import "views/charts2/graphRightDetailBlock";

View File

@@ -0,0 +1,170 @@
$font-size: 14px;
.graph-detail-basic-info {
position: relative;
padding-bottom: 20px;
display: flex;
justify-content: space-between;
//height: 100%;
.entity-type {
color: #717171;
}
.graph-basic-info {
display: flex;
align-items: center;
justify-content: space-between;
.graph-basic-info-name__block {
display: flex;
align-items: center;
.graph-basic-info-name {
padding-right: 10px;
font-family: Helvetica-Bold;
font-size: 20px;
color: #353636;
font-weight: 700;
}
.graph-basic-info-icon {
display: flex;
justify-content: center;
align-items: center;
height: 28px;
width: 28px;
border-radius: 50%;
background-color: #EFF1F4;
cursor: pointer;
flex-shrink: 0;
i {
color: #717171;
font-size: 12px;
}
}
}
}
.graph-detail__icon {
width: 52px;
height: 52px;
overflow: hidden;
display: flex;
justify-content: center;
justify-items: center;
align-items: center;
margin-right: 10px;
border-radius: 50%;
background-color: #e5edf3;
i {
font-size: 26px;
color: #4E84B4;
}
}
}
.graph-close {
color: #575757;
font-size: 8px;
cursor: pointer;
}
.graph-basic-info__block {
margin: 10px 0;
.graph-header__icon {
width: 3px !important;
height: 14px !important;
}
.graph-basic-info__block-content {
.graph-content-item, .graph-content-relationship-item {
display: flex;
line-height: 24px;
}
.graph-content-item {
.graph-content-item-label, .graph-content-item-value {
width: 100px;
font-family: NotoSansSChineseRegular;
font-size: $font-size;
color: #717171;
font-weight: 400;
padding-right: 10px;
flex-shrink: 0;
}
.graph-content-item-value {
width: 230px;
color: #353636;
font-weight: 400;
overflow-wrap: break-word;
}
}
.graph-content-relationship-item {
justify-content: space-between;
line-height: 24px;
cursor: pointer;
&:hover {
background: rgba(135, 135, 135, 0.1);
}
.graph-relationship-item-label, .graph-relationship-item-value {
font-family: NotoSansSChineseRegular;
font-size: $font-size; // 原型上为12px呈现效果不好后续再调节
color: #353636;
font-weight: 400;
//height: 40px;
display: flex;
align-items: center !important;
padding-top: 1px;
}
.graph-relationship-item-value {
color: #717171;
}
}
}
.graph-tag-list {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
margin: 6px 0;
.graph-tag-item {
margin-bottom: 10px;
margin-right: 9px;
padding: 0 8px;
font-size: 12px;
}
}
}
.padding-b-10 {
padding-bottom: 10px;
}
//修改popover样式
.graph-popover {
width: auto !important;
background: #303133 !important;
color: #fff !important;
font-size: 12px !important;
padding: 10px !important;
}
.graph-popover .el-popper__arrow::before {
background: #303133 !important;
}
.graph-expand-relationship__icon:hover {
color: #D80000 !important;
}

View File

@@ -0,0 +1,131 @@
$font-size: 14px;
.graph-list-header {
display: flex;
justify-content: space-between;
.graph-list-header-title {
display: flex;
align-items: center;
margin-bottom: 14px;
span {
font-family: PingFangSC-Semibold;
font-size: 16px;
color: #353636;
line-height: 22px;
font-weight: 600;
}
.graph-list-header-icon {
font-size: 21px;
color: #717171;
margin-right: 9px;
}
}
.graph-list-header-number {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #717171;
font-weight: 400;
span {
font-weight: bold;
}
}
}
.graph-list-expand-btn-block {
margin-top: 20px;
margin-bottom: 30px;
.graph-list-expand-btn {
background: #38ACD2;
border: 1px solid rgba(46, 136, 166, 0.85);
border-radius: 2px;
}
}
.graph-list-content-header {
font-family: NotoSansHans-Medium;
font-size: 14px;
color: #353636;
font-weight: bold;
}
.graph-list-content {
padding: 0 10px;
.graph-list-item-ip {
margin-bottom: 12px;
font-family: NotoSansSChineseRegular;
font-size: $font-size;
color: #353636;
font-weight: 600;
cursor: pointer;
&:hover {
color: #D80000;
}
}
.graph-list-item-block {
width: 300px;
background: rgba(247, 247, 247, 1);
border: 1px solid rgba(226, 229, 236, 1);
border-radius: 2px;
padding: 15px;
.graph-list-item, .graph-list-item__app {
display: flex;
.graph-list-item-label, .graph-list-item-label__app {
width: 62px;
margin-right: 15px;
font-family: NotoSansSChineseRegular;
font-size: $font-size; // 原型上为12px但页面呈现效果不好
color: #717171;
font-weight: 400;
flex-shrink: 0;
}
.graph-list-item-label__app {
width: 78px;
line-height: 24px;
}
.graph-list-country-flag {
width: 20px;
height: 18px;
margin-right: 5px;
}
.graph-list-item-value {
font-family: NotoSansSChineseRegular;
font-size: $font-size;
color: #353636;
font-weight: 400;
}
.graph-list-item-value1 {
display: flex;
align-content: center;
}
}
.graph-list-item {
align-items: center;
}
}
}
.padding-b-20 {
padding-bottom: 20px;
}
.graph-list-dividing-line {
width: 300px;
height: 1px;
background: #ECECEC;
margin: 12px 0;
}

View File

@@ -140,7 +140,6 @@
.row__charts { .row__charts {
height: 19px; height: 19px;
width: 60px; width: 60px;
padding-left: 5px;
} }
} }
@@ -164,29 +163,50 @@
} }
} }
.show-detail { //.show-detail {
flex-shrink: 0; // flex-shrink: 0;
padding: 0 30px; // padding: 0 30px;
font-size: 12px; // font-size: 12px;
color: #3976CB; // color: #3976CB;
//color: #2C72C6; // //color: #2C72C6;
//font-weight: 400; // //font-weight: 400;
//margin-top: -17px; // //margin-top: -17px;
// 新版实体列表改版,后续记得解开 // // 新版实体列表改版,后续记得解开
//
&:hover { // &:hover {
cursor: pointer; // cursor: pointer;
} // }
//
//i { // //i {
// font-size: 12px; // // font-size: 12px;
// margin-right: 5px; // // margin-right: 5px;
//} // //}
} //}
} }
} }
} }
.new-show-detail {
flex-shrink: 0;
padding: 0 30px;
font-size: 12px;
color: #2C72C6;
font-weight: 400;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
&:hover {
cursor: pointer;
}
i {
font-size: 12px;
margin-right: 5px;
}
}
.cn-entity__detail-overview { .cn-entity__detail-overview {
flex-basis: 100%; flex-basis: 100%;
padding: 0 10px; padding: 0 10px;

View File

@@ -1,22 +1,81 @@
<template> <template>
<div class="entity-graph"> <div class="entity-graph">
<div class="entity-graph__chart"></div> <div class="entity-graph__chart"></div>
<div class="entity-graph__detail"> <div class="entity-graph__detail" v-if="mode !== ''">
<ip-list v-if="mode === 'ipList'"></ip-list> <ip-list
v-if="mode === 'ipList'"
:entity="entity"
@closeBlock="onCloseBlock"
@mouseenter="onMouseenter"
@expandDetail="onExpandDetail">
</ip-list>
<app-or-domain-list
v-if="mode === 'appList' || mode === 'domainList'"
:entity="entity"
@expandDetail="onExpandDetail"
@mouseenter="onMouseenter"
@closeBlock="onCloseBlock">
</app-or-domain-list>
<graph-detail
v-if="mode === 'appDetail' || mode === 'ipDetail' || mode === 'domainDetail'"
:entity="entity"
@expand="onExpand"
@closeBlock="onCloseBlock">
</graph-detail>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import IpList from '@/views/entityExplorer/entityGraphDetail/IpList' import IpList from '@/views/entityExplorer/entityGraphDetail/IpList'
import GraphDetail from '@/views/entityExplorer/entityGraphDetail/GraphDetail'
import AppOrDomainList from '@/views/entityExplorer/entityGraphDetail/AppOrDomainList'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
export default { export default {
name: 'EntityRelationship', name: 'EntityRelationship',
components: { components: {
IpList IpList,
GraphDetail,
AppOrDomainList
},
setup () {
const route = useRoute()
const { entityType, name } = route.query
const entity = ref({
entityType: entityType,
entityName: name
})
const mode = ref('')
mode.value = 'appList'
return {
entity,
mode
}
}, },
data () { data () {
return { return {
mode: 'ipList' // ipList, ipDetail, domainList, domainDetail, appList, appDetail // mode: 'appList' // ipList, ipDetail, domainList, domainDetail, appList, appDetail
}
},
methods: {
onCloseBlock () {
// todo 关闭右侧graph面板
this.mode = ''
},
onExpandDetail (mode) {
this.mode = mode
},
onExpand (val) {
// todo 调用接口,拓展关系
},
onMouseenter (val) {
// todo 鼠标移动过graph列表名称时graph图的分支图形会变大一点
if (!val.isTrusted) {
// 鼠标移动反馈
}
} }
} }
} }
@@ -33,6 +92,7 @@ export default {
width: 360px; width: 360px;
border-left: 1px solid #E2E5EC; border-left: 1px solid #E2E5EC;
overflow: auto; overflow: auto;
padding: 20px;
} }
} }
</style> </style>

View File

@@ -0,0 +1,155 @@
<template>
<div>
<div class="graph-list-header">
<div>
<div class="graph-list-header-title">
<i class="graph-list-header-icon" :class="entityIcon"></i>
<span>{{ $t(entityName) }}</span>
</div>
<div class="graph-list-header-number" style="margin-bottom: 30px">
{{ $t('entity.graph.associatedQuantity') }}<span>2</span>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<div>
<div class="digital-certificate">
<div class="digital-certificate-header padding-b-20">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-list-content-header ">
{{ $t('entity.graph.expandedEntityQuantity') }}:&nbsp;&nbsp;
<span>{{ cardData.length }}</span>
</div>
</div>
<div class="graph-list-content">
<div v-for="(item, index) in cardData" :key="index" @mouseenter="onMouseenter(item)">
<div class="graph-list-item-ip" @click="expandDetail">{{ item.name }}</div>
<div class="graph-list-item-block">
<div v-for="card in item.data" :key="card.name">
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{ card.label }}:</div>
<div class="graph-list-item-value">{{ card.value }}</div>
</div>
</div>
</div>
<div class="graph-list-dividing-line" v-if="index !== cardData.length - 1"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import i18n from '@/i18n'
import { ref } from 'vue'
export default {
name: 'AppOrDomainList',
props: {
entity: {
type: Object
}
},
data () {
return {
}
},
mounted () {
this.initData()
},
setup (props) {
const cardData = ref([]) // 列表数据
const entityIcon = ref('') // 顶部icon
const entityName = ref('') // 顶部名称,非实体名
switch (props.entity.entityType) {
case 'app': {
entityIcon.value = 'cn-icon cn-icon-app-name'
entityName.value = 'entities.tab.relatedApp'
cardData.value = [
{
name: 'Wechat',
data: [
{ name: 'appId', label: i18n.global.t('entities.domainDetail.appid'), value: '2376' },
{ name: 'appCategory', label: i18n.global.t('entities.category'), value: 'Sensitive' },
{ name: 'appSubcategory', label: i18n.global.t('entities.subcategory'), value: 'Parked Domain' },
{ name: 'appRisk', label: i18n.global.t('entities.riskLevel'), value: 'Moderate Risk' },
{ name: 'appDescription', label: i18n.global.t('config.dataSource.description'), value: 'Wechat is a Chinese multinational networking and services company.' }
]
},
{
name: 'Wechat',
data: [
{ name: 'appId', label: i18n.global.t('entities.domainDetail.appid'), value: '2376' },
{ name: 'appCategory', label: i18n.global.t('entities.category'), value: 'Sensitive' },
{ name: 'appSubcategory', label: i18n.global.t('entities.subcategory'), value: 'Parked Domain' },
{ name: 'appRisk', label: i18n.global.t('entities.riskLevel'), value: 'Moderate Risk' },
{ name: 'appDescription', label: i18n.global.t('config.dataSource.description'), value: 'Wechat is a Chinese multinational networking and services company.' }
]
}
]
break
}
case 'domain': {
entityIcon.value = 'cn-icon cn-icon-subdomain'
entityName.value = 'entities.subdomain'
cardData.value = [
{
name: '-*.webproxy.at.1.baidu.com',
data: [
{ name: 'categoryName', label: i18n.global.t('entities.category'), value: 'Business and Economy' },
{ name: 'categoryGroup', label: i18n.global.t('entities.group'), value: 'Productivity' },
{ name: 'registration', label: i18n.global.t('entities.registration'), value: 'China' },
{ name: 'registrantOrg', label: i18n.global.t('entities.registry'), value: 'Beijing Baidu Netcom Science Technology Co, Ltd' },
{ name: 'icpLicense', label: i18n.global.t('entities.icpLicense'), value: '京ICP备24537号' }
]
},
{
name: '-*.webproxy.at.1.baidu.com',
data: [
{ name: 'categoryName', label: i18n.global.t('entities.category'), value: 'Business and Economy' },
{ name: 'categoryGroup', label: i18n.global.t('entities.group'), value: 'Productivity' },
{ name: 'registration', label: i18n.global.t('entities.registration'), value: 'China' },
{ name: 'registrantOrg', label: i18n.global.t('entities.registry'), value: 'Beijing Baidu Netcom Science Technology Co, Ltd' },
{ name: 'icpLicense', label: i18n.global.t('entities.icpLicense'), value: '京ICP备24537号' }
]
}
]
break
}
}
return {
entityIcon,
entityName,
cardData
}
},
methods: {
initData () {
// 此处请求接口
},
closeBlock () {
this.$emit('closeBlock')
},
expandDetail () {
if (this.entity.entityType === 'app') {
this.$emit('expandDetail', 'appDetail')
} else if (this.entity.entityType === 'domain') {
this.$emit('expandDetail', 'domainDetail')
}
},
onMouseenter (val) {
// 鼠标移动过graph列表名称时graph图的分支图形会变大一点
this.$emit('mouseenter', val)
}
}
}
</script>

View File

@@ -0,0 +1,366 @@
<template>
<!--title-->
<div class="graph-detail-basic-info">
<div style="display: flex">
<div class="graph-detail__icon"><i :class="iconClass"></i></div>
<div style="display: flex;flex-direction: column;">
<div class="entity-graph-type">{{ entityType[entity.entityType] }}</div>
<div class="graph-basic-info">
<div class="graph-basic-info-name__block">
<div class="graph-basic-info-name" id="entityName">{{ entity.entityName }}</div>
<div class="graph-basic-info-icon" @click="copyEntityName">
<i class="cn-icon cn-icon-copy"></i>
</div>
</div>
</div>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<!--basic info-->
<div class="digital-certificate graph-basic-info__block">
<div class="digital-certificate-header padding-b-10">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="digital-certificate-header-name">
{{ $t('overall.basicInfo') }}
</div>
</div>
<div class="graph-basic-info__block-content">
<div class="graph-content-item" v-for="item in detailCards" :key="item.name">
<div class="graph-content-item-label">{{ item.label }}:</div>
<div class="graph-content-item-value">{{ item.value || '-' }}</div>
</div>
</div>
</div>
<!--关系拓展-->
<div class="digital-certificate graph-basic-info__block">
<div class="digital-certificate-header padding-b-10">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="digital-certificate-header-name">
{{ $t('entity.graph.relationshipExpansion') }}
</div>
</div>
<div class="graph-basic-info__block-content">
<div v-for="item in relationList" :key="item.name" @click="expandRelation(item.name)">
<el-popover
v-if="item.value.length === item.total"
placement="top"
auto-close="2000"
trigger="click"
:content="$t('entity.graph.completed')"
popper-class="graph-popover"
>
<template #reference>
<div class="graph-content-item graph-content-relationship-item">
<div class="graph-relationship-item-label">
<i class="margin-r-6" :class="item.icon"></i>
<span>{{ item.label }}</span>
</div>
<div class="graph-relationship-item-value">
<span class="margin-r-6">{{ item.value.length }}/{{ item.total }}</span>
<i class="cn-icon cn-icon-expand-relationship" :style="{color: iconColor(item.value.length, item.total)}"></i>
</div>
</div>
</template>
</el-popover>
<div v-else class="graph-content-item graph-content-relationship-item">
<div class="graph-relationship-item-label">
<i class="margin-r-6" :class="item.icon"></i>
<span>{{ item.label }}</span>
</div>
<div class="graph-relationship-item-value">
<span class="margin-r-6">{{ item.value.length }}/{{ item.total }}</span>
<el-tooltip
effect="dark"
:content="$t('entity.graph.expandDownward')"
placement="top-end"
>
<i class="cn-icon cn-icon-expand-relationship graph-expand-relationship__icon" :style="{color: iconColor(item.value.length, item.total)}"></i>
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
<!--标签-->
<div class="digital-certificate graph-basic-info__block">
<div class="digital-certificate-header padding-b-10">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="digital-certificate-header-name">
{{ $t('entity.graph.labels') }}
</div>
</div>
<div class="entity-detail graph-basic-info__block-content">
<div class="graph-tag-list">
<div v-for="ic in tagList" :key="ic.key">
<div class="entity-tag graph-tag-item" :class="`entity-tag--level-two-${ic.type}`">
{{ic.value}}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import i18n from '@/i18n'
import { copySelectionText, selectElementText } from '@/utils/tools'
import { entityType, riskLevelMapping } from '@/utils/constants'
import axios from '_axios@0.21.4@axios'
import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin'
import { dateFormatByAppearance } from '@/utils/date-util'
import { ref } from 'vue'
import _ from 'lodash'
export default {
name: 'DomainDetail',
props: {
entity: {
type: Object
}
},
mixins: [chartMixin],
data () {
return {
entityType
}
},
computed: {
iconClass () {
let className
switch (this.entity.entityType) {
case ('ip'): {
className = 'cn-icon cn-icon-ip2'
break
}
case ('domain'): {
className = 'cn-icon cn-icon-domain2'
break
}
case ('app'): {
className = 'cn-icon cn-icon-app2'
break
}
default:
break
}
return className
}
},
mounted () {
this.initData()
},
setup (props) {
const detailCards = ref([])
const relationList = ref([])
const tagList = ref([])
// 标签列表,后续请求接口时处理
tagList.value = [
{ key: 'isp', value: '信息技术', type: 'positive' },
{ key: 'malwareName', value: '互联网', type: 'normal' },
{ key: 'malwareName1', value: '互联网', type: 'normal' },
{ key: 'malwareName2', value: '互联网', type: 'normal' },
{ key: 'malwareName3', value: '互联网', type: 'normal' },
{ key: 'malwareName4', value: '互联网', type: 'normal' },
{ key: 'malwareName5', value: '互联网', type: 'normal' },
{ key: 'malwareName6', value: '互联网', type: 'normal' },
{ key: 'malwareName7', value: '互联网', type: 'normal' },
{ key: 'malwareName8', value: '互联网', type: 'normal' },
{ key: 'malwareName9', value: '互联网', type: 'normal' },
{ key: 'malwareName10', value: '互联网', type: 'normal' },
{ key: 'malwareName11', value: '互联网', type: 'normal' },
{ key: 'malwareName12', value: '互联网', type: 'normal' }
]
switch (props.entity.entityType) {
case 'ip': {
detailCards.value = _.concat(detailCards.value,
{ name: 'asn', label: 'ASN', value: '' },
{ name: 'asOrg', label: i18n.global.t('entities.asOrg'), value: '' },
// { name: 'asSubnet', label: i18n.global.t('entities.asSubnet'), value: '' },
{ name: 'isp', label: 'ISP', value: '' },
{ name: 'location', label: i18n.global.t('entities.geographicLocation'), value: '' }
// { name: 'dnsPtr', label: 'DNS PTR', value: '' }
)
relationList.value = _.concat(relationList.value,
{ icon: 'cn-icon cn-icon-subdomain', name: 'resolveIp', label: i18n.global.t('entity.graph.resolveDomain'), value: [], total: 12 },
{ icon: 'cn-icon cn-icon-app-name', name: 'relatedApp', label: i18n.global.t('entities.tab.relatedApp'), value: [], total: 6 }
)
break
}
case 'domain': {
detailCards.value = _.concat(detailCards.value,
{ name: 'categoryName', label: i18n.global.t('entities.category'), value: '' },
{ name: 'categoryGroup', label: i18n.global.t('entities.domainDetail.subcategory'), value: '' },
{ name: 'reputationLevel', label: i18n.global.t('entities.creditLevel2'), value: '' },
{ name: 'expireDate', label: i18n.global.t('entities.graph.expirationDate'), value: '' },
{ name: 'registrarName', label: i18n.global.t('entities.registrar'), value: '' },
{ name: 'registrantOrg', label: i18n.global.t('entities.registry'), value: '' },
{ name: 'registrantCountry', label: i18n.global.t('entities.registrationCountry'), value: '' },
{ name: 'createDate', label: i18n.global.t('entities.registrationDate'), value: '' },
{ name: 'email', label: i18n.global.t('entities.registryEmail'), value: '' }
)
relationList.value = _.concat(relationList.value,
{ icon: 'cn-icon cn-icon-resolve-ip', name: 'resolveIp', label: i18n.global.t('entities.graph.resolveIp'), value: [], total: 12 },
{ icon: 'cn-icon cn-icon-subdomain', name: 'subdomain', label: i18n.global.t('entities.subdomain'), value: [1, 2], total: 2 },
{ icon: 'cn-icon cn-icon-app-name', name: 'relatedApp', label: i18n.global.t('entities.tab.relatedApp'), value: [], total: 6 }
)
break
}
case 'app': {
detailCards.value = _.concat(detailCards.value,
{ name: 'appCategory', label: i18n.global.t('entities.category'), value: '' },
{ name: 'appSubcategory', label: i18n.global.t('entities.subcategory'), value: '' },
{ name: 'appRisk', label: i18n.global.t('entities.riskLevel'), value: '' },
{ name: 'appTechnology', label: i18n.global.t('overall.technology'), value: '' },
{ name: 'appName', label: i18n.global.t('overall.appName2'), value: '' },
{ name: 'appLongname', label: i18n.global.t('overall.appFullName'), value: '' },
{ name: 'appDescription', label: i18n.global.t('config.dataSource.description'), value: '' }
)
relationList.value = _.concat(relationList.value,
{ icon: 'cn-icon cn-icon-resolve-ip', name: 'resolveIp', label: i18n.global.t('entities.graph.resolveIp'), value: [], total: 12 },
{ icon: 'cn-icon cn-icon-subdomain', name: 'resolveDomain', label: i18n.global.t('entity.graph.resolveDomain'), value: [], total: 12 }
)
}
}
return {
detailCards,
relationList,
tagList
}
},
methods: {
initData () {
axios.get(`${api.entity.basicInfo}/${this.entity.entityType}?resource=${this.entity.entityName}`).then(response => {
const res = response.data
if (res.code === 200) {
switch (this.entity.entityType) {
case 'ip': {
if (res.data.asn) {
this.detailCards.find(c => c.name === 'asn').value = res.data.asn.asn
this.detailCards.find(c => c.name === 'asOrg').value = res.data.asn.organization
// AS子网接口未返回
}
if (res.data.location) {
this.detailCards.find(c => c.name === 'isp').value = res.data.location.isp
this.detailCards.find(c => c.name === 'location').value = this.handleLocation(res.data.location)
// DNS PTR接口未返回
}
break
}
case 'domain': {
if (res.data.category) {
this.detailCards.find(c => c.name === 'categoryName').value = res.data.category.categoryName
this.detailCards.find(c => c.name === 'categoryGroup').value = res.data.category.categoryGroup
this.detailCards.find(c => c.name === 'reputationLevel').value = res.data.category.reputationLevel
}
if (res.data.whois) {
this.detailCards.find(c => c.name === 'expireDate').value = dateFormatByAppearance(res.data.whois.expireDate)
this.detailCards.find(c => c.name === 'registrarName').value = res.data.whois.registrarName
this.detailCards.find(c => c.name === 'registrantOrg').value = res.data.whois.registrantOrg
this.detailCards.find(c => c.name === 'registrantCountry').value = res.data.whois.registrantCountry
this.detailCards.find(c => c.name === 'createDate').value = dateFormatByAppearance(res.data.whois.createDate)
this.detailCards.find(c => c.name === 'email').value = res.data.whois.email
}
break
}
case 'app': {
if (res.data.category) {
this.detailCards.find(c => c.name === 'appCategory').value = res.data.category.appCategory
this.detailCards.find(c => c.name === 'appSubcategory').value = res.data.category.appSubcategory
this.detailCards.find(c => c.name === 'appRisk').value = this.appRisk(res.data.category.appRisk)
this.detailCards.find(c => c.name === 'appTechnology').value = res.data.category.appTechnology
this.detailCards.find(c => c.name === 'appName').value = res.data.category.appName
this.detailCards.find(c => c.name === 'appLongname').value = res.data.category.appLongname
this.detailCards.find(c => c.name === 'appDescription').value = res.data.category.appDescription
}
break
}
}
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})
},
/** 复制实体名称 */
copyEntityName () {
selectElementText(document.getElementById('entityName'))
if (copySelectionText()) {
this.$message.success(this.$t('tip.copySuccess'))
} else {
this.$message.error('Unknown error')
}
},
/** 修改关系拓展图标颜色,全部拓展浅灰色,否则深灰色 */
iconColor (length, total) {
if (length < total) {
return 'rgba(57, 57, 57, 1)'
} else {
return 'rgba(57, 57, 57, 0.5)'
}
},
// 关闭右侧详情栏
closeBlock () {
this.$emit('closeBlock')
},
/** 构造地址,国-省市-市 */
handleLocation (data) {
const location = []
if (data.country) {
location.push(data.country)
}
if (data.province) {
location.push(data.province)
}
if (data.city) {
location.push(data.city)
}
return location.join(' - ')
},
appRisk (level) {
const m = riskLevelMapping.find(mapping => {
return mapping.value === level
})
return (m && m.name) || level
},
/** 关系拓展 */
expandRelation (name) {
// todo 模拟效果,后续修改
const obj = this.relationList.find(item => item.name === name)
if (obj) {
const num = obj.total - obj.value.length
if (num > 0) {
const len = num < 10 ? num : 10
for (let i = 0; i < len; i++) {
obj.value.push(i)
}
this.$emit('expand')
}
}
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
}
}
}
</script>

View File

@@ -1,13 +1,95 @@
<template> <template>
<div>
<div class="graph-list-header">
<div>
<div class="graph-list-header-title">
<i class="cn-icon cn-icon-resolve-ip graph-list-header-icon"></i>
<span>{{ $t('entities.graph.resolveIp') }}</span>
</div>
<div class="graph-list-header-number">
{{ $t('entity.graph.associatedQuantity') }}<span>12</span>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<div class="graph-list-expand-btn-block">
<el-button type="primary" class="graph-list-expand-btn">
{{ $t('entity.graph.continueToExpand') }}
</el-button>
</div>
<div>
<div class="digital-certificate">
<div class="digital-certificate-header padding-b-20">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-list-content-header ">
{{ $t('entity.graph.expandedEntityQuantity') }}:&nbsp;&nbsp;
<span>{{ ipList.length }}</span>
</div>
</div>
<div class="graph-list-content">
<div v-for="(item, index) in ipList" :key="index" @mouseenter="onMouseenter(item)">
<div class="graph-list-item-ip"><span @click="expandDetail">{{ item.ip }}</span></div>
<div class="graph-list-item-block">
<div class="graph-list-item padding-b-10">
<div class="graph-list-item-label">{{ $t('overall.location') }}:</div>
<div class="graph-list-item-value graph-list-item-value1">
<img :src="require(`../../../../public/images/flag/${item.flag}.svg`)" class="graph-list-country-flag"/>
<span>{{ item.city }}</span>
</div>
</div>
<div class="graph-list-item">
<div class="graph-list-item-label">ASN:</div>
<div class="graph-list-item-value">{{ item.asn }}</div>
</div>
</div>
<div class="graph-list-dividing-line" v-if="index !== ipList.length - 1"></div>
</div>
</div>
</div>
</div>
</div>
</template> </template>
<script> <script>
export default { export default {
name: 'IpList' name: 'IpList',
data () {
return {
ipList: [
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' },
{ ip: '192.168.22.11', flag: '011-china', city: 'China, Beijing', asn: 'Sensitive' }
]
}
},
methods: {
closeBlock () {
this.$emit('closeBlock')
},
expandDetail () {
this.$emit('expandDetail', 'ipDetail')
},
onMouseenter (val) {
// 鼠标移动过graph列表名称时graph图的分支图形会变大一点
this.$emit('mouseenter', val)
}
}
} }
</script> </script>
<style scoped> <style lang="scss">
</style> </style>

View File

@@ -146,17 +146,17 @@
</div> </div>
<!--新版实体列表改版去除这一段--> <!--新版实体列表改版去除这一段-->
</div> </div>
<div class="show-detail" @click="showDetail"> <!-- <div class="show-detail" @click="showDetail">-->
{{ $t('overall.detail') }}> <!-- {{ $t('overall.detail') }}>-->
</div>
<!-- 新版实体列表改版后续记得解开-->
<!-- <div class="show-detail">-->
<!-- <div @click="showDetail"><i class="cn-icon cn-icon-detail"></i>{{ $t('overall.detail') }} ></div>-->
<!-- <div><i class="cn-icon cn-icon-graph"></i>{{ $t('entities.graph') }} ></div>-->
<!-- </div>--> <!-- </div>-->
<!-- 新版实体列表改版后续记得解开-->
</div> </div>
</div> </div>
</div> </div>
<div class="new-show-detail">
<div @click="showDetail"><i class="cn-icon cn-icon-detail"></i>{{ $t('overall.detail') }} ></div>
<div @click="showGraph"><i class="cn-icon cn-icon-graph"></i>{{ $t('entities.graph') }} ></div>
</div>
<el-collapse-transition> <el-collapse-transition>
<div class="cn-entity__detail-overview" v-if="!isCollapse"> <div class="cn-entity__detail-overview" v-if="!isCollapse">
<el-divider></el-divider> <el-divider></el-divider>

View File

@@ -135,6 +135,16 @@ export default {
}) })
window.open(href, '_blank') window.open(href, '_blank')
}, },
showGraph () {
const { href } = this.$router.resolve({
path: '/entityGraph',
query: {
entityType: this.entityData.entityType,
name: this.entityData.ipAddr || this.entityData.domainName || this.entityData.appName
}
})
window.open(href, '_blank')
},
querySecurity () { querySecurity () {
const queryParams = { const queryParams = {
startTime: getSecond(this.timeFilter.startTime), startTime: getSecond(this.timeFilter.startTime),