This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
cyber-narrator-cn-ui/src/views/charts/Chart.vue

1993 lines
68 KiB
Vue
Raw Normal View History

<template>
2021-07-01 21:39:10 +08:00
<!-- 标题 -->
2021-06-25 19:11:00 +08:00
<div
2021-08-26 17:14:51 +08:00
v-if="isTitle"
class="cn-chart cn-chart__title"
:style="computePosition">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</div>
2021-07-01 21:39:10 +08:00
<!-- Tabs -->
2021-07-05 17:40:43 +08:00
<el-tabs
2021-07-01 21:39:10 +08:00
class="cn-chart cn-chart__tabs"
v-else-if="isTabs"
2021-07-05 17:40:43 +08:00
v-model="activeTab"
@tab-click="changeTab"
:style="computePosition"
:ref="`chart-${chart.id}`"
2021-07-05 17:40:43 +08:00
>
<el-tab-pane
2021-08-26 17:14:51 +08:00
v-for="tab in chartInfo.children"
:label="tab.i18n ? $t(tab.i18n) : tab.name" :name="`${tab.id}`"
:key="tab.id"
:ref="`chart-${chart.id}`"
2021-07-05 17:40:43 +08:00
>
<template v-for="chart in tab.children">
2021-08-06 15:03:30 +08:00
<chart v-if="activeTab == tab.id" :key="chart.id" :chart="chart" :time-filter="timeFilter" :ref="`chart-${chart.id}`" :entity="entity"></chart>
2021-07-05 22:58:12 +08:00
</template>
2021-07-05 17:40:43 +08:00
</el-tab-pane>
</el-tabs>
2021-06-24 17:59:51 +08:00
<!-- 地图 -->
<chart-map
2021-06-25 19:11:00 +08:00
v-else-if="isMap"
2021-06-24 17:59:51 +08:00
:style="computePosition"
2021-08-11 22:14:23 +08:00
:loading="loading"
2021-06-24 17:59:51 +08:00
>
<template #chartErrorInfo>
2021-08-26 17:14:51 +08:00
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
</template>
2021-06-24 17:59:51 +08:00
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
<template #operations>
<el-popover trigger="hover" placement="top" :content="chartInfo.remark" v-if="chartInfo.remark">
<template #reference>
<span class="header__operation-btn"><i class="cn-icon el-icon-info"></i></span>
</template>
</el-popover>
<span class="header__operation-btn" @click="refresh"><i class="cn-icon cn-icon-refresh"></i></span>
2021-06-24 17:59:51 +08:00
</template>
<template #default>
2021-09-18 17:48:52 +08:00
<template v-if="isIpBasicInfo">
<el-descriptions :column="1">
2021-09-22 09:08:21 +08:00
<el-descriptions-item label="ASN:">{{detailData ? detailData.asn : '-'}}</el-descriptions-item>
<el-descriptions-item label="AS Org:">{{detailData ? detailData.asnOrg : '-'}}</el-descriptions-item>
<el-descriptions-item :label="$t('entities.asSubnet') + ':'">{{detailData.asnSubnet || '-'}}</el-descriptions-item>
2021-09-22 09:08:21 +08:00
<el-descriptions-item label="ISP:">{{detailData.isp || '-'}}</el-descriptions-item>
<el-descriptions-item label="DNS PTR:">{{detailData.dnsPTR || '-'}}</el-descriptions-item>
2021-09-18 17:48:52 +08:00
</el-descriptions>
<div class="chart-location">
<el-descriptions :column="1">
<el-descriptions-item :label="$t('overall.location') + ':'">{{location}}</el-descriptions-item>
</el-descriptions>
2021-09-22 09:08:21 +08:00
<div class="chart-drawing" style="padding: 0 36px 30px 0;" :id="`chart${chartInfo.id}`"></div>
2021-09-18 17:48:52 +08:00
</div>
</template>
<div v-else class="chart-drawing" :id="`chart${chartInfo.id}`"></div>
2021-06-24 17:59:51 +08:00
</template>
</chart-map>
<!-- echarts类的图如饼图柱状图折线图等 -->
<echarts-frame
2021-08-26 17:14:51 +08:00
v-else-if="isEcharts"
:layout="layout"
:style="computePosition"
:chartInfo="chartInfo"
:loading="loading"
:no-data="noData"
>
2021-08-26 17:14:51 +08:00
<template #chartErrorInfo>
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
</template>
<template #title v-if="layout.indexOf(layoutConstant.HEADER) > -1">
{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}
</template>
<template #operations v-if="layout.indexOf(layoutConstant.HEADER) > -1">
<div class="header__operation header__operation--echarts" v-if="chart.type === 31">
<el-select
2021-08-11 22:14:23 +08:00
size="mini"
v-model="orderPieTable"
class="option__select select-column"
placeholder=""
popper-class="option-popper"
@change="orderPieTableChange"
>
<el-option v-for="item in chartPieTableTopOptions" :key="item.value" :value="item.value">&nbsp{{item.name}}</el-option>
</el-select>
</div>
<el-popover trigger="hover" placement="top" :content="chartInfo.remark" v-if="chartInfo.remark">
<template #reference>
<span class="header__operation-btn"><i class="cn-icon el-icon-info"></i></span>
</template>
</el-popover>
<span class="header__operation-btn" @click="refresh"><i class="cn-icon cn-icon-refresh"></i></span>
</template>
<template #default>
2021-09-22 09:08:21 +08:00
<!-- IP详情 开放端口 -->
<template v-if="isIpOpenPort">
<div class="ip-detail__open-port">
<div class="open-port__table">
<div style="height: 100%; overflow: hidden auto;">
<div class="open-port__table-row open-port__table-row--header">
<div class="open-port__table-cell" style="min-width: 100px;">Port</div>
<div class="open-port__table-cell" style="min-width: 130px;">{{$t('overall.protocol')}}</div>
<div class="open-port__table-cell">Banner</div>
<div class="open-port__table-cell" style="min-width: 200px;">Update at</div>
</div>
<div class="open-port__table-row" v-for="(data, index) in detailData" :key="index">
<div class="open-port__table-cell">{{data.port || '-'}}</div>
<div class="open-port__table-cell">{{data.protocol || '-'}}</div>
<div class="open-port__table-cell">{{data.banner || '-'}}</div>
<div class="open-port__table-cell">{{data.utime || '-'}}</div>
</div>
</div>
</div>
<div class="open-port__chart">
<div class="open-port__chart-title">{{$t('overall.protocolsStatistics')}}</div>
<div class="open-port__chart-body chart-drawing" :id="`chart${chartInfo.id}`"></div>
</div>
</div>
</template>
<!-- IP详情 托管域名 -->
2021-09-22 09:08:21 +08:00
<template v-else-if="isIpHostedDomain">
<div class="ip-detail__hosted-domain">
<div class="hosted-domain__list">
<div class="hosted-domain__list-title">{{$t('overall.domain')}}</div>
<div class="hosted-domain__list-body">
<div class="hosted-domain__list-row" v-for="(data, i) in detailData" :key="i">{{data}}</div>
</div>
</div>
<div class="hosted-domain__chart">
<div>
2021-09-22 23:05:18 +08:00
<div class="hosted-domain__chart-title">{{$t('entities.byCategory')}}</div>
2021-09-22 09:08:21 +08:00
<div class="chart-drawing" :id="`chart${chartInfo.id}-0`"></div>
</div>
<div>
<div class="hosted-domain__chart-title">{{$t('entities.byCredit')}}</div>
<div class="chart-drawing" :id="`chart${chartInfo.id}-1`"></div>
</div>
</div>
</div>
</template>
<!-- APP详情 关联域名 -->
<template v-else-if="isAppRelatedDomain">
<div class="app-detail__related-domain">
<div class="related-domain__list">
<div class="related-domain__list-title">{{$t('overall.domain')}}</div>
<div class="related-domain__list-body">
<div class="related-domain__list-row" v-for="(data, i) in detailData" :key="i"><i class="cn-icon cn-icon-domain"></i>&nbsp;{{data}}</div>
</div>
</div>
<div class="related-domain__chart">
<div>
<div class="related-domain__chart-title">{{$t('entities.byCategory')}}</div>
<div class="chart-drawing" :id="`chart${chartInfo.id}-0`"></div>
</div>
<div>
<div class="related-domain__chart-title">{{$t('entities.byCredit')}}</div>
<div class="chart-drawing" :id="`chart${chartInfo.id}-1`"></div>
</div>
</div>
</div>
</template>
2021-10-08 21:09:51 +08:00
<template v-else-if="isSankey">
<div class="sankey-box">
<div class="chart-drawing" :id="`chart${chartInfo.id}`"></div>
<div class="sankey__label" style="left: 5%;">{{$t('entities.inboundLinkId')}}</div>
<div class="sankey__label" style="left: 50%;">{{entity.ip || entity.domain || entity.app}}</div>
<div class="sankey__label" style="right: 5%; transform: translateX(50%)">{{$t('entities.outboundLinkId')}}</div>
</div>
</template>
2021-09-22 09:08:21 +08:00
<div v-else class="chart-drawing" :id="`chart${chartInfo.id}`"></div>
</template>
<template #footer v-if="layout.indexOf(layoutConstant.FOOTER) > -1">
<!-- 带Table的饼图展示Table -->
<template v-if="isEchartsWithTable">
<pie-table :tableData="pieTableData" ref="pieTable" :chartInfo="chartInfo" :time-filter="timeFilter" :order="orderPieTable"/>
2021-07-05 22:58:12 +08:00
</template>
<template v-else-if="isEchartsWithStatistics">
<statistics-legend :data="statisticsData" :chart-info="chartInfo" @toggleLegend="toggleStatisticsLegend"></statistics-legend>
</template>
</template>
</echarts-frame>
<!-- 单值图 -->
<single-value
2021-08-26 17:14:51 +08:00
v-else-if="isSingleValue"
:type="chartInfo.type"
:style="computePosition"
:icon="singleValue.icon"
2021-09-07 11:38:06 +08:00
:loading="loading"
>
2021-08-26 17:14:51 +08:00
<template #chartErrorInfo>
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
</template>
2021-09-18 17:48:52 +08:00
<template #title>
<span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span>
<span
v-if="chartInfo.params && chartInfo.params.as"
class="ip-detail-as"
>
as&nbsp;<span style="text-transform: capitalize">{{chartInfo.params.as}}</span>
</span>
</template>
2021-07-13 18:38:32 +08:00
<template #data>
<span>{{handleSingleValue[0] || handleSingleValue[0] === 0 ? handleSingleValue[0] : '-'}}</span>
<span class="single-value__unit">{{handleSingleValue[1]}}</span>
2021-07-13 18:38:32 +08:00
</template>
<template #chart>
<div class="chart-drawing" :id="`chart${chartInfo.id}`"></div>
</template>
</single-value>
<!-- 表格 -->
<chart-table
2021-08-26 17:14:51 +08:00
v-else-if="isTable"
:table-columns="table.tableColumns"
:table-data="table.currentPageData"
:style="computePosition"
:loading="loading"
:no-data="noData"
>
2021-08-26 17:14:51 +08:00
<template #chartErrorInfo>
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
</template>
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
<template #operations>
<el-popover trigger="hover" placement="top" :content="chartInfo.remark" v-if="chartInfo.remark">
<template #reference>
<span class="header__operation-btn"><i class="cn-icon el-icon-info"></i></span>
</template>
</el-popover>
<span class="header__operation-btn" @click="refresh"><i class="cn-icon cn-icon-refresh"></i></span>
2021-06-21 20:33:39 +08:00
<div class="header__operation header__operation--table">
<el-select
size="mini"
v-model="table.limit"
class="option__select select-topn"
2021-06-21 20:33:39 +08:00
placeholder=""
popper-class="option-popper"
2021-07-09 21:58:49 +08:00
@change="tableLimitChange"
2021-06-21 20:33:39 +08:00
>
<el-option v-for="item in chartTableTopOptions" :key="item" :value="item">TOP&nbsp;{{item}}</el-option>
<template #prefix>TOP&nbsp;</template>
</el-select>
</div>
<div class="header__operation header__operation--table">
<el-select
size="mini"
v-model="table.orderBy"
class="option__select select-column"
:placeholder="$t('overall.field')"
popper-class="option-popper"
2021-07-09 21:58:49 +08:00
@change="tableLimitChange"
2021-06-21 20:33:39 +08:00
>
2021-07-13 18:49:41 +08:00
<template v-for="(item, index) in table.tableColumns" :key="item.prop">
<el-option v-if="index > 0" :value="item.prop">{{item.prop}}</el-option>
</template>
2021-06-21 20:33:39 +08:00
</el-select>
</div>
<!-- <div class="header__operation header__operation&#45;&#45;table">
<span class="option__button"><i class="cn-icon cn-icon-style"></i></span>
<div class="icon-group-divide"></div>
<span class="option__button"><i class="cn-icon cn-icon-dropdown"></i></span>
</div>
<div class="header__operation header__operation--table">
<span class="option__button"><i class="cn-icon cn-icon-full-screen"></i></span>
</div>-->
</template>
2021-06-21 20:33:39 +08:00
<template #footer>
<chart-table-pagination
2021-08-27 15:08:37 +08:00
ref="tablePagination"
2021-06-21 20:33:39 +08:00
:total="table.tableData.length"
@pageJump="pageJump"
></chart-table-pagination>
</template>
</chart-table>
<!-- group -->
<div
v-else-if="isGroup"
class="cn-chart cn-chart__group"
:style="computePosition"
>
<div class="cn-chart__header">
2021-09-18 17:48:52 +08:00
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
<div class="header__title">
<span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span>
<span
v-if="chartInfo.params && chartInfo.params.as"
class="ip-detail-as"
>
as&nbsp;<span style="text-transform: capitalize">{{chartInfo.params.as}}</span>
</span>
</div>
</div>
<div class="cn-chart__body">
<template v-for="chart in chartInfo.children" :key="chart.id">
2021-09-18 17:48:52 +08:00
<chart :chart="chart" :time-filter="timeFilter" :ref="`chart-${chart.id}`" :entity="entity" :parent-data="groupData"></chart>
</template>
</div>
</div>
2021-09-22 23:05:18 +08:00
<!-- Domain详情-whois -->
<div
v-else-if="isDomainWhois"
class="cn-chart cn-chart__whois"
:style="computePosition"
>
<div class="cn-chart__header">
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
<div class="header__title">
<span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span>
</div>
</div>
<div class="cn-chart__body">
<div class="domain-detail-list">
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.sponsor')}}</div>
<div class="domain-detail-list__content">{{detailData.sponsor || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.org')}}</div>
<div class="domain-detail-list__content">{{detailData.org || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">Email</div>
<div class="domain-detail-list__content">{{detailData.email || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('overall.country')}}</div>
<div class="domain-detail-list__content">{{detailData.orgCountry || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.creationDate')}}</div>
<div class="domain-detail-list__content">{{detailData.creationDate || '-'}}</div>
</div>
<div class="domain-detail-list__row">
<div class="domain-detail-list__label">{{$t('entities.expirationDate')}}</div>
<div class="domain-detail-list__content">{{detailData.expirationDate || '-'}}</div>
</div>
</div>
</div>
</div>
<!-- Domain详情-DNS记录 -->
<div
v-else-if="isDomainDnsRecord"
class="cn-chart cn-chart__dns-record"
:style="computePosition"
>
<div class="cn-chart__header">
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
<div class="header__title">
<span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span>
</div>
</div>
<div class="cn-chart__body">
<div class="entity-detail__dns-record">
<div class="dns-record__table">
<div style="height: 100%; overflow: hidden auto;">
<div class="dns-record__table-row dns-record__table-row--header">
<div class="dns-record__table-cell" style="min-width: 200px;">Type</div>
<div class="dns-record__table-cell" style="width: 100%;">Value</div>
</div>
<div class="dns-record__table-row" v-for="(data, index) in detailData" :key="index">
<div class="dns-record__table-cell">{{data.type || '-'}}</div>
<div class="dns-record__table-cell">{{data.value || '-'}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- APP详情-基本信息 -->
<div
v-else-if="isAppBasicInfo"
class="cn-chart cn-chart__app-basic"
:style="computePosition"
>
<div class="cn-chart__header">
<chart-error
:isError="isError"
:errorInfo="errorInfo"
>
</chart-error>
<div class="header__title">
<span :title="chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name">{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</span>
</div>
</div>
<div class="cn-chart__body">
<div style="display: flex; justify-content: space-between; width: 100%;">
<el-descriptions :column="1" style="padding: 20px 30px;">
2021-09-30 00:50:11 +08:00
<el-descriptions-item :label="$t('overall.appName') + ':'">{{detailData.name || '-'}}</el-descriptions-item>
<el-descriptions-item :label="$t('overall.appFullName') + ':'">{{detailData.allName || '-'}}</el-descriptions-item>
<el-descriptions-item :label="$t('overall.technology') + ':'">{{detailData.tech || '-'}}</el-descriptions-item>
<el-descriptions-item :label="$t('overall.remark') + ':'">{{detailData.description || '-'}}</el-descriptions-item>
2021-09-22 23:05:18 +08:00
</el-descriptions>
<div style="display: flex;">
<single-value
:type="51"
icon="cn-icon cn-icon-category"
:loading="false"
style="width: 250px;"
>
<template #title>
<span>{{$t('entities.category')}}</span>
</template>
<template #data>
2021-09-30 00:50:11 +08:00
<span>{{detailData.category ? detailData.category : '-'}}</span>
2021-09-22 23:05:18 +08:00
</template>
</single-value>
<single-value
:type="51"
icon="cn-icon cn-icon-sub-category"
:loading="false"
style="width: 250px;"
>
<template #title>
<span>{{$t('entities.subcategory')}}</span>
</template>
<template #data>
2021-09-30 00:50:11 +08:00
<span>{{detailData.subcategory ? detailData.subcategory : '-'}}</span>
2021-09-22 23:05:18 +08:00
</template>
</single-value>
<single-value
:type="51"
icon="cn-icon cn-icon-credit"
:loading="false"
style="width: 250px;"
>
<template #title>
<span>{{$t('entities.reputationLevel')}}</span>
</template>
<template #data>
2021-09-30 00:50:11 +08:00
<span>{{detailData.risk ? detailData.risk : '-'}}</span>
2021-09-22 23:05:18 +08:00
</template>
</single-value>
</div>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
2021-06-24 17:59:51 +08:00
import * as am4Core from '@amcharts/amcharts4/core'
import * as am4Maps from '@amcharts/amcharts4/maps'
2021-07-05 22:58:12 +08:00
import { shallowRef } from 'vue'
import { tableTitleMapping, legendMapping } from '@/components/charts/chart-table-title'
2021-06-24 17:59:51 +08:00
import {
isEcharts,
isSingleValue,
isTable,
2021-06-25 19:11:00 +08:00
isTitle,
2021-06-24 17:59:51 +08:00
isMap,
getOption,
getTypeCategory,
getLayout,
layoutConstant,
isEchartsWithTable,
2021-07-05 22:58:12 +08:00
isEchartsWithStatistics,
2021-07-01 21:39:10 +08:00
isMapLine,
2021-07-10 12:11:59 +08:00
isMapBlock,
2021-08-06 15:03:30 +08:00
isSingleValueWithEcharts,
2021-08-17 17:56:09 +08:00
isRelationShip,
2021-07-06 10:55:09 +08:00
isTabs,
isGroup,
2021-09-18 17:48:52 +08:00
isSankey,
isIpBasicInfo,
isIpOpenPort,
isIpHostedDomain,
2021-09-22 23:05:18 +08:00
isDomainWhois,
isDomainDnsRecord,
isAppBasicInfo,
isAppRelatedDomain,
2021-07-06 10:55:09 +08:00
getChartColor
2021-06-24 17:59:51 +08:00
} from '@/components/charts/chart-options'
import ChartError from '@/components/charts/ChartError'
import EchartsFrame from '@/components/charts/EchartsFrame'
import SingleValue from '@/components/charts/ChartSingleValue'
2021-06-24 17:59:51 +08:00
import ChartTable from '@/components/charts/ChartTable'
import ChartMap from '@/components/charts/ChartMap'
2021-06-23 15:57:34 +08:00
import PieTable from '@/components/charts/PieTable'
2021-07-05 22:58:12 +08:00
import StatisticsLegend from '@/components/charts/StatisticsLegend'
2021-06-21 20:33:39 +08:00
import ChartTablePagination from '@/components/charts/ChartTablePagination'
import unitConvert, { getUnitType } from '@/utils/unit-convert'
import { chartTableDefaultPageSize, chartTableTopOptions, storageKey, chartPieTableTopOptions, unitTypes } from '@/utils/constants'
2021-08-17 17:56:09 +08:00
import { get, post } from '@/utils/http'
import { replaceUrlPlaceholder, getCapitalGeo, getGeoData, lineToSpace } from '@/utils/tools'
import { HeatLegend } from '@/components/amcharts/heatLegend'
export default {
name: 'Chart',
props: {
2021-06-23 15:57:34 +08:00
chart: Object, // 图表对象包括id、name、type等数据
2021-08-06 15:03:30 +08:00
timeFilter: Object,
2021-09-18 17:48:52 +08:00
parentData: Object,
entity: {
type: Object,
default: () => {}
}
},
components: {
EchartsFrame,
SingleValue,
2021-06-21 20:33:39 +08:00
ChartTablePagination,
2021-06-24 17:59:51 +08:00
ChartTable,
PieTable,
2021-07-05 22:58:12 +08:00
StatisticsLegend,
ChartMap,
ChartError
},
data () {
return {
2021-06-21 20:33:39 +08:00
table: {
pageSize: chartTableDefaultPageSize,
limit: chartTableTopOptions[0], // top-n
2021-07-07 18:47:52 +08:00
orderBy: 'sessions',
2021-06-21 20:33:39 +08:00
tableColumns: [], // table字段
tableData: [], // table的所有数据
currentPageData: [] // table当前页的数据
2021-06-23 15:57:34 +08:00
},
2021-06-25 19:11:00 +08:00
pieTableData: [],
singleValue: {
value: '-',
icon: ''
},
2021-07-05 17:40:43 +08:00
activeTab: '',
2021-09-18 17:48:52 +08:00
groupData: '', // group类型的查询数据用于传递给子chart子chart通过params.dataKey取值
2021-09-22 09:08:21 +08:00
detailData: '', // 详情类型图表的数据
statisticsData: [],
orderPieTable: chartPieTableTopOptions[0].value,
selectPieChartName: '',
2021-07-23 11:39:10 +08:00
allSelectPieChartName: [],
2021-08-11 22:14:23 +08:00
chartOption: null,
loading: true,
2021-08-15 15:49:29 +08:00
noData: false, // 查询结果为空
throttle: null, // 节流器
2021-08-26 17:14:51 +08:00
isError: false, // 接口响应是否报错
2021-08-26 18:58:01 +08:00
errorInfo: '', // 接口具体错误信息
polygonSeries: null
}
},
methods: {
initChart () {
2021-08-11 22:14:23 +08:00
this.loading = true
2021-08-06 15:03:30 +08:00
try {
const chartParams = this.chartInfo.params
if (this.isMap) {
const { chart, polygonSeries } = this.initMap(`chart${this.chartInfo.id}`)
2021-08-26 18:58:01 +08:00
this.myChart = chart
this.polygonSeries = polygonSeries
2021-08-06 15:03:30 +08:00
if (chartParams) {
2021-08-26 18:58:01 +08:00
this.loadMap(this.polygonSeries)
2021-06-23 15:57:34 +08:00
}
// TODO 优化:缓存地图,重新查询时只更改数据,不再次初始化
2021-08-06 15:03:30 +08:00
} else if (this.isEcharts) {
if (this.isIpHostedDomain || this.isAppRelatedDomain) {
const dom = document.getElementById(`chart${this.chartInfo.id}-0`)
const dom2 = document.getElementById(`chart${this.chartInfo.id}-1`)
!this.myChart && (this.myChart = echarts.init(dom))
!this.myChart2 && (this.myChart2 = echarts.init(dom2))
2021-09-22 09:08:21 +08:00
} else {
const dom = document.getElementById(`chart${this.chartInfo.id}`)
!this.myChart && (this.myChart = echarts.init(dom))
}
2021-08-06 15:03:30 +08:00
this.chartOption = this.$_.cloneDeep(getOption(this.chart.type))
if (chartParams) {
if (this.isEchartsWithTable) {
this.initEchartsWithPieTable(chartParams)
} else if (this.isEchartsWithStatistics) {
this.initEchartsWithStatistics(chartParams)
2021-08-17 17:56:09 +08:00
} else if (this.isRelationShip) {
this.initRelationShip(chartParams)
2021-09-18 17:48:52 +08:00
} else if (this.isSankey) {
this.initSankey(chartParams)
2021-09-22 09:08:21 +08:00
} else if (this.isIpOpenPort) {
this.initIpOpenPort(chartParams)
} else if (this.isIpHostedDomain) {
this.initIpHostedDomain(chartParams)
} else if (this.isAppRelatedDomain) {
this.initAppRelatedDomain(chartParams)
2021-08-06 15:03:30 +08:00
} else {
this.initECharts(chartParams)
2021-07-01 21:39:10 +08:00
}
2021-08-06 15:03:30 +08:00
}
} else if (this.isTable) {
if (chartParams) {
this.initChartTable(chartParams)
}
} else if (this.isSingleValue) {
if (chartParams) {
this.singleValue.icon = chartParams.icon
2021-09-18 17:48:52 +08:00
const gotData = new Promise(resolve => {
let result = ''
2021-09-18 17:48:52 +08:00
if (chartParams.dataKey) {
if (this.parentData && (this.parentData[chartParams.dataKey] || this.parentData[chartParams.dataKey] === 0)) {
result = this.parentData[chartParams.dataKey]
2021-09-18 17:48:52 +08:00
} else {
this.noData = true
}
resolve(result)
2021-08-26 17:14:51 +08:00
} else {
2021-09-18 17:48:52 +08:00
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
result = response.data.result
2021-09-18 17:48:52 +08:00
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
resolve(result)
2021-09-18 17:48:52 +08:00
})
2021-08-06 15:03:30 +08:00
}
2021-09-18 17:48:52 +08:00
})
gotData.then(result => {
2021-08-11 22:14:23 +08:00
if (this.isSingleValueWithEcharts) { // 带曲线的单值图
const dom = document.getElementById(`chart${this.chartInfo.id}`)
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = this.$_.cloneDeep(getOption(this.chart.type))
const seriesTemplate = this.chartOption.series[0]
this.chartOption.series = result.map((r, i) => {
return {
...seriesTemplate,
name: r.legend,
data: r.values.map(v => [Number(v[0]) * 1000, Number(v[1]), chartParams.unitType]),
lineStyle: {
color: getChartColor[i]
}
}
})
this.myChart.setOption(this.chartOption)
this.singleValue.value = result[0].values[result[0].values.length - 1][1]
/* const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
2021-08-11 22:14:23 +08:00
const dom = document.getElementById(`chart${this.chartInfo.id}`)
!this.myChart && (this.myChart = echarts.init(dom))
this.chartOption = this.$_.cloneDeep(getOption(this.chart.type))
const seriesTemplate = this.chartOption.series[0]
2021-08-11 22:14:23 +08:00
get(replaceUrlPlaceholder(chartParams.urlLine, queryParams)).then(response => {
if (response.code === 200) {
this.chartOption.series = response.data.result.map((r, i) => {
return {
...seriesTemplate,
name: r.legend,
data: r.values.map(v => [Number(v[0]) * 1000, Number(v[1]), chartParams.unitType]),
lineStyle: {
color: getChartColor[i]
}
}
2021-08-11 22:14:23 +08:00
})
2021-08-26 17:14:51 +08:00
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
2021-08-11 22:14:23 +08:00
}
this.myChart.setOption(this.chartOption)
}) */
2021-08-11 22:14:23 +08:00
} else {
this.singleValue.value = result
2021-08-11 22:14:23 +08:00
this.loading = false
}
2021-08-13 18:57:49 +08:00
}).catch(() => {
this.singleValue.value = ''
}).finally(() => {
setTimeout(() => {
this.loading = false
this.$nextTick(() => {
this.echartsResize()
})
}, 250)
2021-08-11 22:14:23 +08:00
})
2021-08-06 15:03:30 +08:00
}
} else if (this.isTabs) {
if (!this.$_.isEmpty(this.chartInfo.children)) {
this.activeTab = `${this.chartInfo.children[0].id}`
}
2021-09-18 17:48:52 +08:00
} else if (this.isGroup) {
if (chartParams && chartParams.url) {
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
this.groupData = response.data.result
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
}).catch(e => {
2021-09-18 17:48:52 +08:00
this.isError = true
this.errorInfo = e
})
}
2021-09-22 23:05:18 +08:00
} else if (this.isDomainWhois || this.isDomainDnsRecord) {
const queryParams = { domain: this.entity.domain }
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
this.detailData = response.data.result
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
}).catch(e => {
this.isError = true
this.errorInfo = e
})
2021-09-30 00:50:11 +08:00
} else if (this.isAppBasicInfo) {
const queryParams = { app: this.entity.app }
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
this.detailData = response.data.result
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
}).catch(e => {
this.isError = true
this.errorInfo = e
})
2021-07-05 17:40:43 +08:00
}
2021-08-06 15:03:30 +08:00
} catch (e) {
console.error(e)
}
2021-06-21 20:33:39 +08:00
},
reloadChart () {
this.initChart()
this.$nextTick(() => {
if (!this.$_.isEmpty(this.chart.children)) {
this.chart.children.forEach(chart => {
this.$refs[`chart-${chart.id}`].reloadChart()
})
}
})
},
getTitle (r) {
let title = ''
if (r.establishLatency || r.httpResponseLatency || r.sslConLatency) {
2021-08-26 17:14:51 +08:00
title += `: ${unitConvert(r.establishLatency || r.httpResponseLatency || r.sslConLatency, unitTypes.time).join(' ')}`
}
if (r.sequenceGapLossPercent || r.pktRetransPercent) {
2021-08-15 15:49:29 +08:00
const d = unitConvert(r.sequenceGapLossPercent || r.pktRetransPercent, unitTypes.number)
2021-08-26 17:14:51 +08:00
title += d[0] === '0.00' ? ': 0' : `: ${d.join(' ')} %`
}
if (r.sessions) {
2021-08-26 17:14:51 +08:00
title += `\nSessions: ${unitConvert(r.sessions, unitTypes.number).join(' ')}`
}
if (r.packets) {
title += `\nPackets: ${unitConvert(r.packets, unitTypes.number).join(' ')}`
}
if (r.bytes) {
2021-08-26 17:14:51 +08:00
title += `\nBytes: ${unitConvert(r.bytes, unitTypes.byte).join(' ')}`
}
2021-08-15 15:49:29 +08:00
title = title || ': 0'
return title
2021-07-01 15:39:48 +08:00
},
2021-08-26 17:14:51 +08:00
getTitle2 (item, valueColumn) {
let title = ''
switch (valueColumn) {
case 'sessions': {
title = `\nSessions: ${unitConvert(item.value, unitTypes.number).join(' ')}`
break
}
case 'packets': {
title = `\nPackets: ${unitConvert(item.value, unitTypes.number).join(' ')}`
break
}
case 'bytes': {
title = `\nBytes: ${unitConvert(item.value, unitTypes.byte).join(' ')}`
break
}
case 'establishLatency':
case 'httpResponseLatency':
case 'sslConLatency': {
const result = unitConvert(item.value, unitTypes.time)
title = `: ${result[0] === 0 ? 0 : result.join(' ')}`
break
}
case 'sequenceGapLossPercent':
case 'pktRetransPercent': {
title = `: ${unitConvert(item.value, unitTypes.number).join(' ')}`
break
}
default: break
}
return title
},
2021-07-05 17:40:43 +08:00
changeTab (tab) {
this.activeTab = tab.paneName
},
2021-06-24 17:59:51 +08:00
initMap (id) {
2021-06-29 19:45:44 +08:00
const chart = am4Core.create(id, am4Maps.MapChart)
chart.geodata = getGeoData(storageKey.iso36112WorldLow)
2021-06-29 19:45:44 +08:00
chart.projection = new am4Maps.projections.Miller()
const polygonSeries = chart.series.push(new am4Maps.MapPolygonSeries())
polygonSeries.useGeodata = true
polygonSeries.exclude = ['AQ'] // 排除南极洲
2021-07-10 12:11:59 +08:00
return {
chart,
polygonSeries
}
2021-06-24 17:59:51 +08:00
},
2021-07-10 12:11:59 +08:00
loadMap (polygonSeries) {
2021-07-13 18:38:32 +08:00
const chartParams = this.chartInfo.params
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), country: '', region: '', ...this.entity } // 统计数据的查询参数
2021-07-08 18:02:57 +08:00
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200 && !this.$_.isEmpty(response.data.result)) {
2021-07-08 18:02:57 +08:00
const data = response.data.result
2021-09-22 09:08:21 +08:00
if (this.isIpBasicInfo) {
this.detailData = data
} else {
data.forEach(r => {
const serverCountryCapital = r.serverId && getCapitalGeo(r.serverId)
const clientCountryCapital = r.clientId && getCapitalGeo(r.clientId)
serverCountryCapital && (r.serverLongitude = serverCountryCapital.capitalLongitude)
serverCountryCapital && (r.serverLatitude = serverCountryCapital.capitalLatitude)
clientCountryCapital && (r.clientLongitude = clientCountryCapital.capitalLongitude)
clientCountryCapital && (r.clientLatitude = clientCountryCapital.capitalLatitude)
2021-07-08 18:02:57 +08:00
})
2021-09-22 09:08:21 +08:00
if (this.isMapLine) {
const lineSeries = this.myChart.series.push(new am4Maps.MapLineSeries())
const lineTemplate = lineSeries.mapLines.template
lineTemplate.stroke = am4Core.color('#A258EC')
lineTemplate.line.nonScalingStroke = true
lineTemplate.line.strokeDasharray = '4 3'
lineTemplate.nonScalingStroke = true
lineTemplate.arrow.nonScaling = true
lineTemplate.arrow.width = 4
lineTemplate.arrow.height = 6
lineSeries.data = [
{
multiGeoLine: data.map(d => {
return [
{
latitude: parseFloat(d.serverLatitude),
longitude: parseFloat(d.serverLongitude)
},
{
latitude: parseFloat(d.clientLatitude),
longitude: parseFloat(d.clientLongitude)
}
]
})
}
]
const imageSeries = this.myChart.series.push(new am4Maps.MapImageSeries())
imageSeries.dataFields.value = 'sessions'
const imageSeriesTemplate = imageSeries.mapImages.template
const circle = imageSeriesTemplate.createChild(am4Core.Circle)
2021-07-08 18:02:57 +08:00
2021-09-22 09:08:21 +08:00
circle.fillOpacity = 0.7
circle.nonScaling = true
circle.tooltipText = '{title}'
const radiusHeat = imageSeries.heatRules.push({
target: circle,
property: 'radius',
min: 8,
max: 30
2021-07-08 18:02:57 +08:00
})
2021-09-22 09:08:21 +08:00
const colorHeat = imageSeries.heatRules.push({
target: circle,
property: 'fill',
min: am4Core.color('#D2A8FF'),
max: am4Core.color('#A258EC')
2021-07-08 18:02:57 +08:00
})
2021-09-22 09:08:21 +08:00
imageSeriesTemplate.propertyFields.latitude = 'latitude'
imageSeriesTemplate.propertyFields.longitude = 'longitude'
const pointData = []
data.forEach(d => {
pointData.push({
...d,
latitude: parseFloat(d.serverLatitude),
longitude: parseFloat(d.serverLongitude),
title: this.getTitle(d)
2021-08-26 17:14:51 +08:00
})
2021-09-22 09:08:21 +08:00
pointData.push({
...d,
latitude: parseFloat(d.clientLatitude),
longitude: parseFloat(d.clientLongitude),
title: this.getTitle(d)
})
})
imageSeries.data = pointData
} else if (this.isMapBlock) {
const sumData = []
data.forEach(r => {
const hit = sumData.find(s => s.id === r.serverId)
const value = Number(r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions) || 0
2021-09-22 09:08:21 +08:00
if (hit) {
hit.value += value
2021-09-22 09:08:21 +08:00
} else {
sumData.push({
id: r.serverId,
value
2021-09-22 09:08:21 +08:00
})
}
})
const seriesData = sumData.map(r => {
return {
...r,
title: this.getTitle2(r, chartParams.valueColumn)
}
})
polygonSeries.data = [...seriesData]
const sorted = seriesData.sort((a, b) => b.value - a.value)
const allZero = this.$_.isEmpty(sorted) || Number(sorted[0].value) === 0 // 数据全为0的情况legend只显示1个颜色
2021-08-13 18:57:49 +08:00
2021-09-22 09:08:21 +08:00
polygonSeries.heatRules.push({
property: 'fill',
target: polygonSeries.mapPolygons.template,
min: am4Core.color('#EABA2B'),
max: allZero ? am4Core.color('#EABA2B') : am4Core.color('#D95D41')
})
2021-09-22 09:08:21 +08:00
const heatLegend = this.myChart.createChild(HeatLegend)
heatLegend.markerContainer.height = 6
heatLegend.series = polygonSeries
heatLegend.align = 'left'
heatLegend.markerCount = allZero ? 1 : 3
heatLegend.minValue = 0
heatLegend.fontSize = 12
heatLegend.maxValue = allZero ? 1 : Number(sorted[0].value)
heatLegend.width = allZero ? am4Core.percent(10) : am4Core.percent(25)
heatLegend.marginLeft = 15
heatLegend.valign = 'bottom'
2021-08-13 18:57:49 +08:00
2021-09-22 09:08:21 +08:00
const minRange = heatLegend.valueAxis.axisRanges.create()
minRange.value = heatLegend.minValue
minRange.label.text = minRange.value === 0 ? 0 : unitConvert(heatLegend.minValue, chartParams.unitType).join(' ')
const maxRange = heatLegend.valueAxis.axisRanges.create()
maxRange.value = heatLegend.maxValue
maxRange.label.text = maxRange.value === 0 ? 0 : unitConvert(heatLegend.maxValue, chartParams.unitType).join(' ')
2021-08-26 17:14:51 +08:00
2021-09-22 09:08:21 +08:00
heatLegend.valueAxis.renderer.labels.template.adapter.add('text', function (labelText) {
return ''
})
2021-08-26 17:14:51 +08:00
2021-09-22 09:08:21 +08:00
const polygonTemplate = polygonSeries.mapPolygons.template
polygonTemplate.tooltipText = '{name}{title}'
polygonTemplate.nonScalingStroke = true
polygonTemplate.strokeWidth = 0.5
}
2021-07-08 18:02:57 +08:00
}
} else if (response.code !== 200) {
2021-08-26 17:14:51 +08:00
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
2021-07-08 18:02:57 +08:00
}
2021-08-13 18:57:49 +08:00
}).finally(() => {
2021-08-11 22:14:23 +08:00
setTimeout(() => { this.loading = false }, 250)
2021-07-08 18:02:57 +08:00
})
},
2021-06-21 20:33:39 +08:00
pageJump (val) {
this.table.currentPageData = this.getTargetPageData(val, this.table.pageSize, this.table.tableData)
},
getTargetPageData (pageNum, pageSize, tableData) {
return this.$_.slice(tableData, (pageNum - 1) * pageSize, pageNum * pageSize)
},
refresh () {
this.initChart()
},
getTableTitle (data) {
if (data.length > 0) {
const dataColumns = Object.keys(data[0]) // 返回数据的字段
const columns = dataColumns.map(c => tableTitleMapping[c]) // 展示字段
2021-09-30 00:50:11 +08:00
const keys = ['clientIp', 'serverIp', 'ip', 'appId', 'app', 'domain']
return columns.sort((a, b) => {
if (keys.indexOf(a.prop) > -1) {
return -1
} else if (keys.indexOf(b.prop) > -1) {
return 1
} else {
return 0
}
})
} else {
return []
}
},
toggleStatisticsLegend (index) {
this.statisticsData[index].active = !this.statisticsData[index].active
this.statisticsData.forEach((d, i) => {
if (d.active) {
this.myChart.dispatchAction({
type: 'legendSelect',
name: d.legend
})
} else {
this.myChart.dispatchAction({
type: 'legendUnSelect',
name: d.legend
})
}
})
},
orderPieTableChange () {
if (this.chart.type === 31) {
const chartParams = this.chartInfo.params || null // 图表参数
2021-07-10 11:18:40 +08:00
this.initEchartsWithPieTable(chartParams)
}
},
2021-08-15 15:49:29 +08:00
timeLineIsAllZero (data) {
2021-08-15 18:53:27 +08:00
if (data.resultType === 'matrix') {
let allZero = true
try {
data.result.forEach(d => {
2021-08-15 18:53:27 +08:00
d.values.forEach(r => {
if (r[1] && r[1] !== '0') {
2021-08-15 18:53:27 +08:00
allZero = false
throw new Error('break')
}
})
2021-08-15 15:49:29 +08:00
})
2021-08-15 18:53:27 +08:00
} catch (e) {}
return allZero
}
2021-08-15 15:49:29 +08:00
},
2021-07-10 11:18:40 +08:00
initECharts (chartParams) {
2021-08-19 18:50:50 +08:00
if (chartParams.showLegend === false) {
this.chartOption.legend.show = false
}
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
2021-07-10 11:18:40 +08:00
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
2021-08-15 15:49:29 +08:00
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
return
} else {
this.noData = false
}
2021-07-10 11:18:40 +08:00
const seriesTemplate = this.chartOption.series[0]
2021-08-15 18:53:27 +08:00
const allZero = this.timeLineIsAllZero(response.data)
2021-08-15 15:49:29 +08:00
if (allZero) {
this.chartOption.yAxis = {
...this.chartOption.yAxis,
min: 0,
max: 5,
interval: 1
}
}
2021-07-10 11:18:40 +08:00
this.chartOption.series = response.data.result.map(r => {
return {
...seriesTemplate,
name: legendMapping[`${this.entity && this.entity.ip ? 'ip_' : ''}${r.legend}`] ? legendMapping[`${this.entity && this.entity.ip ? 'ip_' : ''}${r.legend}`] : lineToSpace(r.legend),
data: r.values.map(v => [Number(v[0]) * 1000, Number(v[1]), chartParams.unitType])
2021-07-10 11:18:40 +08:00
}
})
const rows = (response.data.result.length - 1) / 4 + 1 // 根据legend个数动态预留legend空间
const gridTop = 10 + 27 * rows
this.chartOption.grid.top = gridTop
if (chartParams.unitType === unitTypes.byte) {
this.chartOption.yAxis.axisLabel.formatter = function (value, index, a, b) {
return unitConvert(value, unitTypes.byte).join(' ')
}
this.chartOption.grid.left = 75
}
2021-08-26 17:14:51 +08:00
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
2021-07-10 11:18:40 +08:00
}
this.myChart.setOption(this.chartOption)
2021-08-13 18:57:49 +08:00
}).finally(() => {
setTimeout(() => {
this.loading = false
this.$nextTick(() => {
this.echartsResize()
})
}, 250)
2021-07-10 11:18:40 +08:00
})
},
2021-08-17 17:56:09 +08:00
initRelationShip (chartParams) {
2021-08-19 18:50:50 +08:00
const queryParams = { ...this.entity, limit: 5 }
2021-08-17 17:56:09 +08:00
post(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
2021-08-19 18:50:50 +08:00
if (!response.data.result) {
this.noData = true
return
} else {
this.noData = false
}
2021-08-17 17:56:09 +08:00
2021-08-19 18:50:50 +08:00
const data = []
const links = []
handleData(data, links, response.data.result)
this.chartOption.series[0].data = data
this.chartOption.series[0].links = links
2021-08-17 17:56:09 +08:00
}
2021-08-19 18:50:50 +08:00
this.myChart.setOption(this.chartOption)
2021-08-17 17:56:09 +08:00
}).finally(() => {
setTimeout(() => {
this.loading = false
setTimeout(() => {
this.myChart.resize()
})
}, 250)
2021-08-17 17:56:09 +08:00
})
2021-08-19 18:50:50 +08:00
const vm = this
function handleData (data, links, item) {
if (!data.some(d => d.name === item.name)) {
data.push({ name: item.name, ...handleStyle(item) })
}
if (!vm.$_.isEmpty(item.from) && !vm.$_.isEmpty(item.to)) {
links.push({ target: item.to, source: item.from })
}
if (!vm.$_.isEmpty(item.leaf)) {
item.leaf.forEach(i => {
handleData(data, links, i)
})
}
}
function handleStyle (item) {
const style = {}
switch (item.type) {
case 'app_id': {
style.itemStyle = { color: '#73DEB3' }
style.symbol = 'circle'
break
}
case 'domain': {
style.itemStyle = { color: '#73A0FA' }
style.symbol = 'circle'
break
}
case 'client_ip': {
style.itemStyle = { color: '#E8F6FF', borderColor: '#C9C9C9' }
style.symbol = 'roundRect'
style.symbolSize = [80, 25]
break
}
case 'server_ip': {
style.itemStyle = { color: '#E2FCEF', borderColor: '#C9C9C9' }
style.symbol = 'roundRect'
style.symbolSize = [80, 25]
break
}
}
return style
}
2021-09-18 17:48:52 +08:00
},
initSankey (chartParams) {
2021-10-08 21:09:51 +08:00
const vm = this
const entityName = this.entity.ip || this.entity.domain || this.entity.app
this.chartOption.series[0].tooltip = {
formatter: function ({ name }) {
return `
<div class="sankey__tooltip">
<div class="sankey__tooltip-row">
<div class="sankey__row-label">Via:</div>
<div class="sankey__row-value">1521</div>
</div>
<div class="sankey__tooltip-row">
<div class="sankey__row-label">To:</div>
<div class="sankey__row-value">21522</div>
</div>
<div class="sankey__tooltip-row">
<div style="margin: 6px 0; height: 1px; width: 100%; background-color: #E7EAED;"></div>
</div>
<div class="sankey__tooltip-row">
<div class="sankey__row-label">Traffic:</div>
<div class="sankey__row-value">150bps(8%)</div>
</div>
<div class="sankey__tooltip-row">
<div class="sankey__row-label">Performance:</div>
</div>
<div class="sankey__tooltip-table">
<div class="sankey__table-row">
<div class="sankey__table-cell">${vm.$t('networkAppPerformance.tripTime')}:</div>
<div class="sankey__table-cell">58ms</div>
</div>
<div class="sankey__table-row">
<div class="sankey__table-cell">${vm.$t('overall.packetLoss')}:</div>
<div class="sankey__table-cell">5%</div>
</div>
<div class="sankey__table-row">
<div class="sankey__table-cell">${vm.$t('overall.packetRetrans')}:</div>
<div class="sankey__table-cell">58%</div>
</div>
</div>
</div>
`
}
}
this.chartOption.series[0].data = [
{
name: '1521'
},
{
name: '2714'
},
{
name: entityName,
label: {
show: false
}
},
{
name: '21521'
},
{
name: '22714'
},
{
name: '29047'
},
{
name: '21522'
},
{
name: '22715'
},
{
name: '29048'
},
{
name: '121521'
},
{
name: '122714'
},
{
name: '129047'
},
{
name: '121522'
},
{
name: '122715'
},
{
name: '129048'
}
]
this.chartOption.series[0].links = [
{
source: '1521',
target: entityName,
value: 6779
},
{
source: '2714',
target: entityName,
value: 4417
},
{
source: entityName,
target: '21521',
value: 704
},
{
source: entityName,
target: '22714',
value: 55
},
{
source: entityName,
target: '29047',
value: 509
},
{
source: entityName,
target: '21522',
value: 3140
},
{
source: entityName,
target: '22715',
value: 550
},
{
source: entityName,
target: '29048',
value: 1290
},
{
source: entityName,
target: '121521',
value: 704
},
{
source: entityName,
target: '122714',
value: 55
},
{
source: entityName,
target: '129047',
value: 509
},
{
source: entityName,
target: '121522',
value: 2040
},
{
source: entityName,
target: '122715',
value: 550
},
{
source: entityName,
target: '129048',
value: 1090
}
]
this.myChart.setOption(this.chartOption)
setTimeout(() => {
this.loading = false
this.$nextTick(() => {
this.echartsResize()
})
}, 250)
2021-09-22 09:08:21 +08:00
},
initIpOpenPort (chartParams) {
get(replaceUrlPlaceholder(chartParams.url, { ip: this.entity.ip })).then(response => {
if (response.code === 200) {
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
} else {
this.noData = false
this.detailData = response.data.result
2021-09-22 09:08:21 +08:00
const protocols = []
this.detailData.forEach((d, i) => {
const index = protocols.findIndex(p => p.name === d.protocol.toUpperCase())
if (index === -1) {
protocols.push({ name: d.protocol.toUpperCase(), value: 1, itemStyle: { color: getChartColor(i) } })
} else {
protocols[index].value++
}
})
this.chartOption.series[0].data = protocols
this.chartOption.xAxis.data = protocols.map(p => p.name)
this.myChart.setOption(this.chartOption)
}
}
}).finally(() => {
setTimeout(() => {
this.loading = false
this.$nextTick(() => {
this.echartsResize()
})
}, 250)
})
},
initIpHostedDomain (chartParams) {
get(replaceUrlPlaceholder(chartParams.url, { ip: this.entity.ip })).then(response => {
if (response.code === 200) {
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
} else {
this.noData = false
this.detailData = response.data.result
2021-09-22 09:08:21 +08:00
}
}
this.chartOption.series[0].data = [
{
name: 'test1',
value: 32
},
{
name: 'test2',
value: 21
},
{
name: 'test3',
value: 20
},
{
name: 'test4',
value: 7
}
]
this.myChart.setOption(this.chartOption)
this.myChart2.setOption(this.chartOption)
2021-09-22 09:08:21 +08:00
}).finally(() => {
setTimeout(() => {
this.loading = false
}, 250)
})
2021-08-17 17:56:09 +08:00
},
initAppRelatedDomain (chartParams) {
this.noData = false
this.loading = false
this.chartOption.series[0].data = [
{
name: 'test1',
value: 32
},
{
name: 'test2',
value: 21
},
{
name: 'test3',
value: 20
},
{
name: 'test4',
value: 7
}
]
this.myChart.setOption(this.chartOption)
this.myChart2.setOption(this.chartOption)
2021-09-30 00:50:11 +08:00
get(replaceUrlPlaceholder(chartParams.url, { app: this.entity.app })).then(response => {
if (response.code === 200) {
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
} else {
2021-09-30 00:50:11 +08:00
this.detailData = response.data.result
this.noData = false
}
}
}).finally(() => {
setTimeout(() => {
this.loading = false
}, 250)
2021-09-30 00:50:11 +08:00
})
},
2021-07-10 11:18:40 +08:00
initEchartsWithStatistics (chartParams) {
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
2021-07-10 11:18:40 +08:00
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
2021-08-15 15:49:29 +08:00
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
return
} else {
this.noData = false
}
2021-08-15 18:53:27 +08:00
const allZero = this.timeLineIsAllZero(response.data)
2021-08-15 15:49:29 +08:00
if (allZero) {
this.chartOption.yAxis = {
...this.chartOption.yAxis,
min: 0,
max: 5,
interval: 1
}
}
this.statisticsData = response.data.result.map(d => {
return {
...d,
active: true
}
})
2021-07-10 11:18:40 +08:00
const seriesTemplate = this.chartOption.series[0]
this.chartOption.series = response.data.result.map((r, i) => {
return {
...seriesTemplate,
name: r.legend,
data: r.values.map(v => [Number(v[0]) * 1000, Number(v[1]), chartParams.unitType]),
2021-07-10 11:18:40 +08:00
lineStyle: {
color: getChartColor[i]
}
}
})
2021-08-26 17:14:51 +08:00
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
2021-07-10 11:18:40 +08:00
}
this.myChart.setOption(this.chartOption)
2021-08-13 18:57:49 +08:00
}).finally(() => {
setTimeout(() => {
this.loading = false
this.$nextTick(() => {
this.echartsResize()
})
}, 250)
2021-07-10 11:18:40 +08:00
})
},
initEchartsWithPieTable (chartParams) {
2021-07-07 18:33:05 +08:00
const self = this
chartParams.valueColumn = this.orderPieTable
const unitType = getUnitType(chartParams.valueColumn)
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), limit: 10, order: this.orderPieTable, ...this.entity } // 统计数据的查询参数
const tableQueryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), limit: 10, order: this.orderPieTable, ...this.entity } // 统计数据的查询参数
tableQueryParams[chartParams.nameColumn] = [] // 处理两个图表不一样的地方
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
2021-08-15 15:49:29 +08:00
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
return
} else {
this.noData = false
}
2021-08-15 18:53:27 +08:00
const allZero = this.timeLineIsAllZero(response.data)
2021-08-15 15:49:29 +08:00
if (allZero) {
this.chartOption.yAxis = {
...this.chartOption.yAxis,
min: 0,
max: 5,
interval: 1
}
}
const data = response.data.result.map(d => {
tableQueryParams[chartParams.nameColumn].push(d[chartParams.nameColumn])
return {
data: d,
name: d[chartParams.nameColumn],
value: parseInt(d[chartParams.valueColumn]),
unitType: unitType
}
})
2021-07-23 11:39:10 +08:00
this.allSelectPieChartName = tableQueryParams[chartParams.nameColumn]
this.chartOption.series[0].data = data
if (this.chartOption.series[0].data && this.chartOption.series[0].data.length > 10) { // pieWithTable 图例超过10个改为滚动显示
this.chartOption.legend.type = 'scroll'
}
this.myChart.setOption(this.chartOption)
if (!this.$_.isEmpty(data)) {
get(replaceUrlPlaceholder(chartParams.urlTable, tableQueryParams)).then(response2 => {
if (response2.code === 200) {
this.pieTableData = response2.data.result
2021-08-26 17:14:51 +08:00
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
})
}
2021-08-26 17:14:51 +08:00
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
2021-08-13 18:57:49 +08:00
}).finally(() => {
setTimeout(() => {
this.loading = false
this.$nextTick(() => {
this.echartsResize()
})
}, 250)
})
// legend点击事件
this.myChart.off('legendselectchanged')
2021-07-10 12:30:06 +08:00
this.myChart.on('legendselectchanged', function (params) {
self.myChart.setOption({
legend: { selected: { [params.name]: true } }
})
const index = self.chartOption.series[0].data.findIndex(d => d.name === params.name)
if (self.selectPieChartName !== params.name) {
self.myChart.dispatchAction({
type: 'select',
seriesIndex: 0,
dataIndex: index
})
self.selectPieChartName = params.name
self.loadPieTableData(params.name)
} else {
self.myChart.dispatchAction({
type: 'unselect',
seriesIndex: 0,
dataIndex: index
})
self.selectPieChartName = ''
self.loadPieTableData(this.allSelectPieChartName)
}
2021-07-10 12:30:06 +08:00
})
// 饼图色块点击事件
this.myChart.off('click')
this.myChart.on('click', function (echartParams) {
// 若是已选,则点击后取消选择,并查询全部数据
if (echartParams.name === self.selectPieChartName) {
self.selectPieChartName = ''
self.loadPieTableData(this.allSelectPieChartName)
} else { // 否则查询当前name数据
self.selectPieChartName = echartParams.name
self.loadPieTableData(echartParams.name)
}
})
},
loadPieTableData (name = '') {
const childrenParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), limit: 10, order: this.orderPieTable, ...this.entity }
childrenParams[this.chartInfo.params.nameColumn] = name
get(replaceUrlPlaceholder(this.chartInfo.params.urlTable, childrenParams)).then(response => {
if (response.code === 200) {
this.pieTableData = response.data.result
}
})
},
2021-07-09 21:58:49 +08:00
tableLimitChange () {
const chartParams = this.chartInfo.params || null // 图表参数
2021-07-07 18:47:52 +08:00
this.initChartTable(chartParams)
},
initChartTable (chartParams) {
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), limit: this.table.limit, order: this.table.orderBy, ...this.entity }
2021-07-07 18:47:52 +08:00
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
if (response.code === 200) {
2021-08-15 15:49:29 +08:00
if (this.$_.isEmpty(response.data.result)) {
this.noData = true
return
} else {
this.noData = false
}
2021-07-07 18:47:52 +08:00
this.table.tableData = response.data.result
this.table.tableColumns = this.getTableTitle(response.data.result)
2021-07-07 18:47:52 +08:00
this.table.currentPageData = this.getTargetPageData(1, this.table.pageSize, this.table.tableData)
2021-08-26 17:14:51 +08:00
} else {
this.isError = true
this.noData = true
2021-08-27 17:11:58 +08:00
this.errorInfo = response.msg || response.message || this.$t('tip.unknownError')
2021-07-07 18:47:52 +08:00
}
2021-08-13 18:57:49 +08:00
}).finally(() => {
2021-08-27 15:08:37 +08:00
this.$nextTick(() => {
this.$refs.tablePagination.resetPageNo()
})
2021-08-13 18:57:49 +08:00
setTimeout(() => { this.loading = false }, 250)
2021-07-07 18:47:52 +08:00
})
},
echartsResize () {
this.myChart && this.myChart.resize()
2021-07-07 18:47:52 +08:00
}
},
computed: {
computePosition () {
const gridColumn = `${this.chartInfo.x} / ${this.chartInfo.x + this.chartInfo.w}`
const gridRow = `${this.chartInfo.y} / ${this.chartInfo.y + this.chartInfo.h}`
return {
gridColumn,
gridRow
}
2021-07-13 18:38:32 +08:00
},
2021-09-18 17:48:52 +08:00
location () {
let location = ''
2021-09-22 09:08:21 +08:00
if (this.detailData) {
if (this.detailData.country) {
location = this.detailData.country
if (this.detailData.region) {
location += ', '
location += this.detailData.region
}
} else if (this.detailData.region) {
location = this.detailData.region
2021-09-18 17:48:52 +08:00
}
}
return location
},
2021-07-13 18:38:32 +08:00
handleSingleValue () {
const value = this.singleValue.value
const unitType = this.chartInfo.params.unitType
const result = unitConvert(value, unitType)
switch (unitType) {
case unitTypes.percent: {
result[0] = result[0] < 0.01 ? '< 0.01' : result[0]
break
}
case unitTypes.time: {
result[0] = result[0] < 1 ? '< 1' : result[0]
break
}
default: break
2021-07-13 18:38:32 +08:00
}
return result
}
},
mounted () {
this.initChart()
this.throttle = this.$_.throttle(this.echartsResize, 500)
window.addEventListener('resize', this.throttle)
},
2021-07-09 21:58:49 +08:00
watch: {
chart: {
immediate: true,
deep: true,
handler (n, o) {
if (o) {
this.initChart()
}
}
},
timeFilter: {
immediate: true,
deep: true,
handler (n, o) {
2021-08-02 13:22:15 +08:00
if (n && o) {
this.$nextTick(() => {
this.initChart()
})
}
}
},
parentData: {
immediate: true,
deep: true,
handler (n, o) {
if (n) {
this.$nextTick(() => {
this.initChart()
})
}
}
2021-07-09 21:58:49 +08:00
}
},
setup (props) {
const chartInfo = JSON.parse(JSON.stringify(props.chart))
chartInfo.category = getTypeCategory(props.chart.type)
return {
chartInfo,
layoutConstant,
2021-06-21 20:33:39 +08:00
chartTableTopOptions,
chartPieTableTopOptions,
isEcharts: isEcharts(props.chart.type),
isEchartsWithTable: isEchartsWithTable(props.chart.type),
2021-07-05 22:58:12 +08:00
isEchartsWithStatistics: isEchartsWithStatistics(props.chart.type),
isSingleValue: isSingleValue(props.chart.type),
2021-08-06 15:03:30 +08:00
isSingleValueWithEcharts: isSingleValueWithEcharts(props.chart.type),
2021-08-17 17:56:09 +08:00
isRelationShip: isRelationShip(props.chart.type),
isTable: isTable(props.chart.type),
2021-06-24 17:59:51 +08:00
isMap: isMap(props.chart.type),
2021-06-25 19:11:00 +08:00
isTitle: isTitle(props.chart.type),
2021-06-24 17:59:51 +08:00
isMapLine: isMapLine(props.chart.type),
2021-07-10 12:11:59 +08:00
isMapBlock: isMapBlock(props.chart.type),
2021-07-01 21:39:10 +08:00
isTabs: isTabs(props.chart.type),
isGroup: isGroup(props.chart.type),
2021-09-18 17:48:52 +08:00
isSankey: isSankey(props.chart.type),
isIpBasicInfo: isIpBasicInfo(props.chart.type),
isIpHostedDomain: isIpHostedDomain(props.chart.type),
isIpOpenPort: isIpOpenPort(props.chart.type),
2021-09-22 23:05:18 +08:00
isDomainWhois: isDomainWhois(props.chart.type),
isDomainDnsRecord: isDomainDnsRecord(props.chart.type),
isAppBasicInfo: isAppBasicInfo(props.chart.type),
isAppRelatedDomain: isAppRelatedDomain(props.chart.type),
2021-07-05 22:58:12 +08:00
layout: getLayout(props.chart.type),
myChart: shallowRef(null),
myChart2: shallowRef(null) // 个别有两个图表的chart
}
},
unmounted () {
window.removeEventListener('resize', this.throttle)
}
}
</script>
2021-09-22 09:08:21 +08:00
<style lang="scss">
.ip-detail__open-port {
display: flex;
height: 100%;
width: 100%;
.open-port__table {
flex: 1;
display: table;
height: 100%;
border-right: 1px solid $--right-box-border-color;
.open-port__table-row {
display: table-row;
font-size: 14px;
color: #333333;
}
.open-port__table-row.open-port__table-row--header {
padding: 13px 30px 0;
height: 40px;
color: #6B717B;
}
.open-port__table-cell {
display: table-cell;
2021-09-22 23:05:18 +08:00
vertical-align: middle;
2021-09-22 09:08:21 +08:00
padding: 13px 30px;
}
}
.open-port__chart {
display: flex;
flex-direction: column;
flex: 0 0 30%;
height: 100%;
.open-port__chart-title {
padding-left: 20px;
line-height: 50px;
flex: 0 0 50px;
}
.open-port__chart-body {
flex: 1;
}
}
}
.ip-detail__hosted-domain, .app-detail__related-domain {
2021-09-22 09:08:21 +08:00
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
.hosted-domain__list, .related-domain__list {
2021-09-22 09:08:21 +08:00
display: flex;
flex-direction: column;
flex: 0 0 25%;
overflow: auto;
padding-bottom: 20px;
border-bottom: 1px solid $--right-box-border-color;
.hosted-domain__list-title, .related-domain__list-title {
2021-09-22 09:08:21 +08:00
padding: 13px 30px 0;
height: 40px;
color: #6B717B;
}
.hosted-domain__list-body, .related-domain__list-body {
2021-09-22 09:08:21 +08:00
display: flex;
flex-direction: column;
height: calc(100% - 40px);
overflow: hidden auto;
}
.hosted-domain__list-row, .related-domain__list-row {
2021-09-22 09:08:21 +08:00
padding: 5px 30px;
color: #3976CB;
2021-09-30 00:50:11 +08:00
i {
color: #B8C1D1;
}
2021-09-22 09:08:21 +08:00
}
}
.hosted-domain__chart, .related-domain__chart {
2021-09-22 09:08:21 +08:00
display: flex;
flex-direction: column;
flex: 1;
&>div {
flex: 0 0 50%;
display: flex;
flex-direction: column;
.hosted-domain__chart-title {
padding-left: 20px;
line-height: 50px;
flex: 0 0 50px;
}
.chart-drawing {
flex: 1;
}
}
}
}
2021-09-22 23:05:18 +08:00
.domain-detail-list {
display: table;
width: 100%;
.domain-detail-list__row {
display: table-row;
.domain-detail-list__label {
display: table-cell;
2021-09-30 00:50:11 +08:00
padding: 13px 30px;
2021-09-22 23:05:18 +08:00
border-bottom: 1px solid $--content-right-background-color;
width: 170px;
color: #6B717B;
}
.domain-detail-list__content {
display: table-cell;
2021-09-30 00:50:11 +08:00
padding: 13px 0 ;
2021-09-22 23:05:18 +08:00
border-bottom: 1px solid $--content-right-background-color;
color: #3976CB;
}
}
}
.entity-detail__dns-record {
display: flex;
height: 100%;
width: 100%;
.dns-record__table {
display: table;
height: 100%;
width: 100%;
.dns-record__table-row {
display: table-row;
font-size: 14px;
color: #333333;
}
.dns-record__table-row.dns-record__table-row--header {
padding: 13px 30px 0;
height: 40px;
color: #6B717B;
}
.dns-record__table-cell {
display: table-cell;
border-bottom: 1px solid $--content-right-background-color;
vertical-align: middle;
padding: 13px 30px;
}
.dns-record__table-row:not(.dns-record__table-row--header) .dns-record__table-cell:last-of-type {
color: #3976CB;
}
}
}
.app-detail__related-domain {
flex-direction: row;
.related-domain__list {
border-bottom: none;
}
.related-domain__chart {
flex-direction: row;
border-left: 1px solid $--right-box-border-color;
padding: 3vh 5vw;
&>div {
flex: 0 0 50%;
display: flex;
flex-direction: column;
.related-domain__chart-title {
padding-left: 20px;
line-height: 50px;
flex: 0 0 50px;
color: #3976CB;
}
.chart-drawing {
flex: 1;
}
}
}
}
2021-10-08 21:09:51 +08:00
.sankey-box {
width: 100%;
height: 100%;
position: relative;
.sankey__label {
position: absolute;
color: #333;
bottom: 50px;
font-weight: bold;
transform: translateX(-50%);
}
}
.sankey__tooltip {
width: 270px;
height: 200px;
padding: 5px;
display: flex;
flex-direction: column;
.sankey__tooltip-row {
display: flex;
padding: 2px 0;
.sankey__row-label {
flex: 0 0 98px;
color: #666;
}
.sankey__row-value {
flex: 1;
color: #333;
}
}
.sankey__tooltip-table {
padding: 2px 0 2px 22px;
display: table;
.sankey__table-row {
display: table-row;
padding: 1px 0 4px 0;
.sankey__table-cell {
display: table-cell;
font-size: 12px;
color: #333;
}
.sankey__table-cell:first-of-type {
color: #999;
}
}
}
}
2021-09-22 09:08:21 +08:00
</style>