CN-1481 Subscribe详情页KPI组件开发;CN-1482 Subscribe详情页top app组件开发
This commit is contained in:
@@ -9,6 +9,8 @@ const DEFAULT_TIME_FILTER_RANGE = {
|
|||||||
entity: {
|
entity: {
|
||||||
list: 60,
|
list: 60,
|
||||||
trafficLine: 60,
|
trafficLine: 60,
|
||||||
|
subscriberKpi: 60,
|
||||||
|
subscriberTopApp: 60,
|
||||||
informationAggregation: 0,
|
informationAggregation: 0,
|
||||||
relatedEntity: 60 * 24 * 7,
|
relatedEntity: 60 * 24 * 7,
|
||||||
openPort: 60 * 24 * 7,
|
openPort: 60 * 24 * 7,
|
||||||
|
|||||||
@@ -83,6 +83,8 @@
|
|||||||
|
|
||||||
@import 'views/setting/knowledgeBase';
|
@import 'views/setting/knowledgeBase';
|
||||||
@import 'views/charts2/entityDetailLine';
|
@import 'views/charts2/entityDetailLine';
|
||||||
|
@import 'views/charts2/EntityDetailSubscriberKpi.scss';
|
||||||
|
@import 'views/charts2/EntityDetailSubscriberTopApp.scss';
|
||||||
@import 'views/charts2/entityDetailTabs';
|
@import 'views/charts2/entityDetailTabs';
|
||||||
@import 'views/charts2/digitalCertificate';
|
@import 'views/charts2/digitalCertificate';
|
||||||
@import 'views/charts2/entityDetailBasicInfo';
|
@import 'views/charts2/entityDetailBasicInfo';
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
.subscriber-kpi {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.subscriber-kpi-header {
|
||||||
|
height:34px;
|
||||||
|
padding-bottom:10px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
display:flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.subscriber-kpi-title {
|
||||||
|
height:24px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscriber-kpi-body {
|
||||||
|
border: 1px solid #E2E5EC;
|
||||||
|
border-radius: 4px;
|
||||||
|
height:calc(100% - 34px);
|
||||||
|
.subscriber-kpi-content {
|
||||||
|
height: calc(100% - 36px);
|
||||||
|
padding: 20px 0 20px 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.panel-chart__no-data {
|
||||||
|
height: calc(100% - 46px);
|
||||||
|
}
|
||||||
|
.kpi-type {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content:space-between;
|
||||||
|
height: calc(100% - 65px);
|
||||||
|
.kpi-type-value {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom:20px;
|
||||||
|
.kpi-type-value-name {
|
||||||
|
line-height: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #575757;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.kpi-type-data {
|
||||||
|
display:flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.kpi-type-value-number {
|
||||||
|
font-family: Helvetica-Bold;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.data-trend {
|
||||||
|
display: flex;
|
||||||
|
width: 50%;
|
||||||
|
|
||||||
|
.data-total-trend {
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 2px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
.data-total-trend-black {
|
||||||
|
background-color: rgba(113,113,113,0.12);
|
||||||
|
color: #717171;
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
|
.data-total-trend-green {
|
||||||
|
background-color: rgba(126,159,84,0.12);
|
||||||
|
color: #7E9F54;
|
||||||
|
}
|
||||||
|
.data-total-trend-red {
|
||||||
|
background-color: rgba(226,97,84,0.12);
|
||||||
|
color: #E26154;
|
||||||
|
.cn-icon-rise1{
|
||||||
|
color: #E44D3E;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
.subscriber-top-app {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.subscriber-top-app-header {
|
||||||
|
height:34px;
|
||||||
|
padding-bottom:10px;
|
||||||
|
font-family: NotoSansHans-Medium;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 500;
|
||||||
|
display:flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.subscriber-top-app-title {
|
||||||
|
height:24px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscriber-top-app-body {
|
||||||
|
border: 1px solid #E2E5EC;
|
||||||
|
border-radius: 4px;
|
||||||
|
height:calc(100% - 34px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px 20px 20px;
|
||||||
|
|
||||||
|
.panel-chart__no-data {
|
||||||
|
height: calc(100% - 46px);
|
||||||
|
}
|
||||||
|
.top-app-left {
|
||||||
|
height:100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-right:15px;
|
||||||
|
.app-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #353636;
|
||||||
|
font-weight: 400;
|
||||||
|
height:calc(100%/10);
|
||||||
|
//padding:5.6px 0 5.6px;
|
||||||
|
.app-index {
|
||||||
|
text-align: right;
|
||||||
|
width:20px;
|
||||||
|
margin-right:20px;
|
||||||
|
}
|
||||||
|
.app-name {
|
||||||
|
width:50px;
|
||||||
|
margin-right:30px;
|
||||||
|
}
|
||||||
|
.top-app-divider {
|
||||||
|
height:10px;
|
||||||
|
background: #717171;
|
||||||
|
margin-left:10px;
|
||||||
|
margin-right:8px;
|
||||||
|
}
|
||||||
|
.app-trend {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
i {
|
||||||
|
margin-right:3px;
|
||||||
|
font-size:12px;
|
||||||
|
color: #717171;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.app-up {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #717171;
|
||||||
|
letter-spacing: -0.2px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.app-down {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #717171;
|
||||||
|
letter-spacing: -0.2px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.top-app-right {
|
||||||
|
height: 100%;
|
||||||
|
width:calc(100% - 248px);
|
||||||
|
position: relative;
|
||||||
|
.chart-content {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1887,6 +1887,8 @@ export const chartColor5 = ['#E26154', '#E7B34E', '#88AF65']
|
|||||||
|
|
||||||
export const chartColor6 = ['#E99F67', '#D9C74B']
|
export const chartColor6 = ['#E99F67', '#D9C74B']
|
||||||
export const chartColorForBehaviorPattern = ['#7acac7', '#b4d38e', '#fee9b9', '#fec396', '#fb9b79', '#e3799c', '#edd5f5', '#868cac', '#a4adde', '#64b4e6']
|
export const chartColorForBehaviorPattern = ['#7acac7', '#b4d38e', '#fee9b9', '#fec396', '#fb9b79', '#e3799c', '#edd5f5', '#868cac', '#a4adde', '#64b4e6']
|
||||||
|
export const chartColorForSubscriberTopApp = ['#A7C186', '#AFCC8A', '#BEDCAC', '#80BEA5', '#7BBBBC', '#8CB9C8', '#E6BF88', '#E6D99B', '#E0D1B0', '#ECAE95']
|
||||||
|
|
||||||
export const iso36112 = {
|
export const iso36112 = {
|
||||||
[storageKey.iso36112Capital]: 'data/countriesWithCapital',
|
[storageKey.iso36112Capital]: 'data/countriesWithCapital',
|
||||||
[storageKey.iso36112WorldLow]: 'worldChinaLow',
|
[storageKey.iso36112WorldLow]: 'worldChinaLow',
|
||||||
|
|||||||
@@ -166,6 +166,18 @@
|
|||||||
:entity="entity"
|
:entity="entity"
|
||||||
@toggleLoading="toggleLoading"
|
@toggleLoading="toggleLoading"
|
||||||
></entity-detail-basic-info>
|
></entity-detail-basic-info>
|
||||||
|
<entity-detail-subscriber-kpi
|
||||||
|
v-else-if="chart.type === typeMapping.entityDetail.subscriberKpi"
|
||||||
|
:chart="chart"
|
||||||
|
:entity="entity"
|
||||||
|
@toggleLoading="toggleLoading"
|
||||||
|
></entity-detail-subscriber-kpi>
|
||||||
|
<entity-detail-subscriber-top-app
|
||||||
|
v-else-if="chart.type === typeMapping.entityDetail.subscriberTopApp"
|
||||||
|
:chart="chart"
|
||||||
|
:entity="entity"
|
||||||
|
@toggleLoading="toggleLoading"
|
||||||
|
></entity-detail-subscriber-top-app>
|
||||||
<entity-detail-line
|
<entity-detail-line
|
||||||
v-else-if="chart.type === typeMapping.entityDetail.line"
|
v-else-if="chart.type === typeMapping.entityDetail.line"
|
||||||
:chart="chart"
|
:chart="chart"
|
||||||
@@ -210,6 +222,8 @@ import DnsRecentEvents from '@/views/charts2/charts/dnsInsight/DnsRecentEvents'
|
|||||||
import DnsTrafficLine from '@/views/charts2/charts/dnsInsight/DnsTrafficLine'
|
import DnsTrafficLine from '@/views/charts2/charts/dnsInsight/DnsTrafficLine'
|
||||||
import EntityDetailBasicInfo from '@/views/charts2/charts/entityDetail/EntityDetailBasicInfo'
|
import EntityDetailBasicInfo from '@/views/charts2/charts/entityDetail/EntityDetailBasicInfo'
|
||||||
import EntityDetailLine from '@/views/charts2/charts/entityDetail/EntityDetailLine'
|
import EntityDetailLine from '@/views/charts2/charts/entityDetail/EntityDetailLine'
|
||||||
|
import EntityDetailSubscriberKpi from '@/views/charts2/charts/entityDetail/EntityDetailSubscriberKpi'
|
||||||
|
import EntityDetailSubscriberTopApp from '@/views/charts2/charts/entityDetail/EntityDetailSubscriberTopApp'
|
||||||
import EntityDetailTabsChart from '@/views/charts2/charts/entityDetail/EntityDetailTabs'
|
import EntityDetailTabsChart from '@/views/charts2/charts/entityDetail/EntityDetailTabs'
|
||||||
|
|
||||||
import { getNowTime } from '@/utils/date-util'
|
import { getNowTime } from '@/utils/date-util'
|
||||||
@@ -245,6 +259,8 @@ export default {
|
|||||||
DnsRecentEvents,
|
DnsRecentEvents,
|
||||||
DnsTrafficLine,
|
DnsTrafficLine,
|
||||||
EntityDetailBasicInfo,
|
EntityDetailBasicInfo,
|
||||||
|
EntityDetailSubscriberKpi,
|
||||||
|
EntityDetailSubscriberTopApp,
|
||||||
EntityDetailLine,
|
EntityDetailLine,
|
||||||
EntityDetailTabsChart
|
EntityDetailTabsChart
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export const typeMapping = {
|
|||||||
},
|
},
|
||||||
entityDetail: {
|
entityDetail: {
|
||||||
basicInfo: 712,
|
basicInfo: 712,
|
||||||
|
subscriberKpi: 714,
|
||||||
|
subscriberTopApp: 715,
|
||||||
line: 107,
|
line: 107,
|
||||||
tabsChart: 713
|
tabsChart: 713
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<div class="subscriber-kpi">
|
||||||
|
<div class="subscriber-kpi-header">
|
||||||
|
<div class="subscriber-kpi-title">{{$t('subscriber.kpi')}}</div>
|
||||||
|
<date-time-range
|
||||||
|
class="entity-detail-date-time-range"
|
||||||
|
:start-time="timeFilter.startTime"
|
||||||
|
:end-time="timeFilter.endTime"
|
||||||
|
:date-range="timeFilter.dateRangeValue"
|
||||||
|
ref="dateTimeRange"
|
||||||
|
@change="reload"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subscriber-kpi-body">
|
||||||
|
<chart-no-data v-if="isNoData" test-id="noData"></chart-no-data>
|
||||||
|
<chart-error v-if="showError" :content="errorMsg" />
|
||||||
|
|
||||||
|
<div class="subscriber-kpi-content" v-if="!isNoData && !showError">
|
||||||
|
<div class="kpi-type">
|
||||||
|
<div class="kpi-type-value">
|
||||||
|
<div class="kpi-type-value-name">{{$t('subscriber.volume')}}</div>
|
||||||
|
<div class="kpi-type-data" >
|
||||||
|
<div class="kpi-type-value-number">
|
||||||
|
{{unitConvert($_.get(kpiData, 'volume'), unitTypes.number).join(' ')}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-trend">
|
||||||
|
<div class="data-total-trend data-total-trend-red">
|
||||||
|
<i class="cn-icon-rise1 cn-icon"></i>
|
||||||
|
<span >32%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="kpi-type-value">
|
||||||
|
<div class="kpi-type-value-name">{{$t('subscriber.throughput')}}</div>
|
||||||
|
<div class="kpi-type-data" >
|
||||||
|
<div class="kpi-type-value-number" >{{unitConvert($_.get(kpiData, 'throughput'), unitTypes.bps).join(' ')}}</div>
|
||||||
|
<div class="data-trend">
|
||||||
|
|
||||||
|
<div class="data-total-trend data-total-trend-green">
|
||||||
|
<i class="cn-icon-decline cn-icon"></i>
|
||||||
|
<span >8%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="kpi-type-value">
|
||||||
|
<div class="kpi-type-value-name">{{$t('subscriber.latency')}}</div>
|
||||||
|
<div class="kpi-type-data" >
|
||||||
|
<div class="kpi-type-value-number" >{{unitConvert($_.get(kpiData, 'latency'), unitTypes.time).join(' ')}}</div>
|
||||||
|
<div class="data-trend">
|
||||||
|
|
||||||
|
<div class="data-total-trend data-total-trend-black">
|
||||||
|
<i class="cn-icon-constant cn-icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="kpi-type-value">
|
||||||
|
<div class="kpi-type-value-name">{{$t('subscriber.packetLoss')}}</div>
|
||||||
|
<div class="kpi-type-data" >
|
||||||
|
<div class="kpi-type-value-number" >{{unitConvert($_.get(kpiData, 'packetLoss'), unitTypes.percent).join(' ')}}</div>
|
||||||
|
<div class="data-trend">
|
||||||
|
|
||||||
|
<div class="data-total-trend data-total-trend-green">
|
||||||
|
<i class="cn-icon-decline cn-icon"></i>
|
||||||
|
<span >66%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import { ref, shallowRef } from 'vue'
|
||||||
|
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||||
|
import chartMixin from '@/views/charts2/chart-mixin'
|
||||||
|
import ChartError from '@/components/common/Error'
|
||||||
|
import unitConvert from '@/utils/unit-convert'
|
||||||
|
import { unitTypes } from '@/utils/constants'
|
||||||
|
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { getNowTime, getSecond } from '@/utils/date-util'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EntityDetailSubscriberKpi',
|
||||||
|
components: {
|
||||||
|
ChartError,
|
||||||
|
ChartNoData
|
||||||
|
},
|
||||||
|
mixins: [chartMixin],
|
||||||
|
setup () {
|
||||||
|
const { query } = useRoute()
|
||||||
|
const queryCondition = ref(query.queryCondition || '')
|
||||||
|
|
||||||
|
// 获取url携带的range、startTime、endTime
|
||||||
|
const rangeParam = query.kpiRange
|
||||||
|
const startTimeParam = query.kpiStartTime
|
||||||
|
const endTimeParam = query.kpiEndTime
|
||||||
|
|
||||||
|
// 优先级:url > config.js > 默认值。
|
||||||
|
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.entity.subscriberKpi || 60)
|
||||||
|
const timeFilter = ref({ dateRangeValue })
|
||||||
|
if (!startTimeParam || !endTimeParam) {
|
||||||
|
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||||
|
timeFilter.value.startTime = startTime
|
||||||
|
timeFilter.value.endTime = endTime
|
||||||
|
} else {
|
||||||
|
timeFilter.value.startTime = parseInt(startTimeParam)
|
||||||
|
timeFilter.value.endTime = parseInt(endTimeParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryCondition,
|
||||||
|
timeFilter
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
kpiData: {
|
||||||
|
volume: 5300000000,
|
||||||
|
throughput: 600000,
|
||||||
|
latency: 21,
|
||||||
|
packetLoss: 0.0192
|
||||||
|
},
|
||||||
|
unitConvert,
|
||||||
|
unitTypes,
|
||||||
|
isNoData: false,
|
||||||
|
showError: false,
|
||||||
|
errorMsg: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
timeFilter: {
|
||||||
|
handler () {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init (val, show, active, n) {
|
||||||
|
const params = {
|
||||||
|
resource: this.entity.entityName,
|
||||||
|
startTime: getSecond(this.timeFilter.startTime),
|
||||||
|
endTime: getSecond(this.timeFilter.endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.queryCondition) {
|
||||||
|
// params.q = this.queryCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toggleLoading(true)
|
||||||
|
axios.get(`${api.entity.throughput}/${this.entity.entityType}`, { params: params }).then(response => {
|
||||||
|
const res = response.data
|
||||||
|
if (response.status === 200) {
|
||||||
|
this.isNoData = res.data.result.length === 0
|
||||||
|
this.showError = false
|
||||||
|
if (!this.isNoData) {
|
||||||
|
this.kpiData = res.data.result
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.httpError(res)
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
this.httpError(e)
|
||||||
|
}).finally(() => {
|
||||||
|
this.toggleLoading(false)
|
||||||
|
// 测试代码
|
||||||
|
// this.isNoData = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
httpError (e) {
|
||||||
|
this.isNoData = false
|
||||||
|
this.showError = true
|
||||||
|
this.errorMsg = this.errorMsgHandler(e)
|
||||||
|
},
|
||||||
|
reload (startTime, endTime, dateRangeValue) {
|
||||||
|
this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime) }
|
||||||
|
const { query } = this.$route
|
||||||
|
|
||||||
|
const newUrl = urlParamsHandler(window.location.href, query, {
|
||||||
|
kpiStartTime: this.timeFilter.startTime,
|
||||||
|
kpiEndTime: this.timeFilter.endTime,
|
||||||
|
kpiRange: dateRangeValue.value
|
||||||
|
})
|
||||||
|
overwriteUrl(newUrl)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.init()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeUnmount () {
|
||||||
|
this.unitConvert = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
<template>
|
||||||
|
<div class="subscriber-top-app">
|
||||||
|
<div class="subscriber-top-app-header">
|
||||||
|
<div class="subscriber-top-app-title">{{$t('subscriber.topApp')}}</div>
|
||||||
|
<date-time-range
|
||||||
|
class="entity-detail-date-time-range"
|
||||||
|
:start-time="timeFilter.startTime"
|
||||||
|
:end-time="timeFilter.endTime"
|
||||||
|
:date-range="timeFilter.dateRangeValue"
|
||||||
|
ref="dateTimeRange"
|
||||||
|
@change="reload"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subscriber-top-app-body">
|
||||||
|
<chart-no-data v-if="isNoData" test-id="noData"></chart-no-data>
|
||||||
|
<chart-error v-if="showError" :content="errorMsg" />
|
||||||
|
|
||||||
|
<div class="top-app-left" v-show="!isNoData && !showError">
|
||||||
|
<div class="app-data" v-for="(app, index) in topAppData">
|
||||||
|
<div class="app-index">{{++index}}</div>
|
||||||
|
<div class="app-name">{{app.name}}</div>
|
||||||
|
<div class="app-trend">
|
||||||
|
<i class="cn-icon cn-icon-egress"></i>
|
||||||
|
<div class="app-up">{{app.up}}</div>
|
||||||
|
</div>
|
||||||
|
<el-divider direction="vertical" class="top-app-divider"/>
|
||||||
|
<div class="app-trend">
|
||||||
|
<i class="cn-icon cn-icon-ingress"></i>
|
||||||
|
<div class="app-down">{{app.down}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="top-app-right" >
|
||||||
|
<div class="chart-content" id="subscriberTopAppChart"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/utils/api'
|
||||||
|
import { ref, shallowRef } from 'vue'
|
||||||
|
import ChartNoData from '@/views/charts/charts/ChartNoData'
|
||||||
|
import chartMixin from '@/views/charts2/chart-mixin'
|
||||||
|
import ChartError from '@/components/common/Error'
|
||||||
|
import unitConvert from '@/utils/unit-convert'
|
||||||
|
import { unitTypes } from '@/utils/constants'
|
||||||
|
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { getNowTime, getSecond } from '@/utils/date-util'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import { entityDetailSubscriberTopApp } from '@/views/charts2/charts/options/echartOption'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EntityDetailSubscriberTopApp',
|
||||||
|
components: {
|
||||||
|
ChartError,
|
||||||
|
ChartNoData
|
||||||
|
},
|
||||||
|
mixins: [chartMixin],
|
||||||
|
setup () {
|
||||||
|
const { query } = useRoute()
|
||||||
|
const queryCondition = ref(query.queryCondition || '')
|
||||||
|
|
||||||
|
// 获取url携带的range、startTime、endTime
|
||||||
|
const rangeParam = query.topAppRange
|
||||||
|
const startTimeParam = query.topAppStartTime
|
||||||
|
const endTimeParam = query.topAppEndTime
|
||||||
|
|
||||||
|
// 优先级:url > config.js > 默认值。
|
||||||
|
const dateRangeValue = rangeParam ? parseInt(rangeParam) : (DEFAULT_TIME_FILTER_RANGE.entity.subscriberTopApp || 60)
|
||||||
|
const timeFilter = ref({ dateRangeValue })
|
||||||
|
if (!startTimeParam || !endTimeParam) {
|
||||||
|
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||||
|
timeFilter.value.startTime = startTime
|
||||||
|
timeFilter.value.endTime = endTime
|
||||||
|
} else {
|
||||||
|
timeFilter.value.startTime = parseInt(startTimeParam)
|
||||||
|
timeFilter.value.endTime = parseInt(endTimeParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryCondition,
|
||||||
|
timeFilter,
|
||||||
|
myChart: shallowRef(null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
topAppData: [
|
||||||
|
{
|
||||||
|
name: 'Wetchat',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'QQ',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Douyin',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Weibo',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Tecent',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Kuaishou',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Taobao',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Jd',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Aiqiyi',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Baidu',
|
||||||
|
up: '1.2GB',
|
||||||
|
down: '28MB'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
unitConvert,
|
||||||
|
unitTypes,
|
||||||
|
isNoData: false,
|
||||||
|
showError: false,
|
||||||
|
errorMsg: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
timeFilter: {
|
||||||
|
handler () {
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init (val, show, active, n) {
|
||||||
|
const params = {
|
||||||
|
resource: this.entity.entityName,
|
||||||
|
startTime: getSecond(this.timeFilter.startTime),
|
||||||
|
endTime: getSecond(this.timeFilter.endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.queryCondition) {
|
||||||
|
// params.q = this.queryCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toggleLoading(true)
|
||||||
|
axios.get(`${api.entity.throughput}/${this.entity.entityType}`, { params: params }).then(response => {
|
||||||
|
const res = response.data
|
||||||
|
if (response.status === 200) {
|
||||||
|
this.isNoData = res.data.result.length === 0
|
||||||
|
this.showError = false
|
||||||
|
if (!this.isNoData) {
|
||||||
|
this.topAppData = res.data.result
|
||||||
|
this.initEchart()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.httpError(res)
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
this.httpError(e)
|
||||||
|
}).finally(() => {
|
||||||
|
this.toggleLoading(false)
|
||||||
|
// 无数据时的测试代码
|
||||||
|
//this.isNoData = false
|
||||||
|
//this.initEchart()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initEchart (data) {
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
count: 0.307,
|
||||||
|
app: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.206,
|
||||||
|
app: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.09,
|
||||||
|
app: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.082,
|
||||||
|
app: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.06,
|
||||||
|
app: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.032,
|
||||||
|
app: 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.024,
|
||||||
|
app: 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.017,
|
||||||
|
app: 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.009,
|
||||||
|
app: 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
count: 0.006,
|
||||||
|
app: 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const chartDom = document.getElementById('subscriberTopAppChart')
|
||||||
|
this.myChart = echarts.getInstanceByDom(chartDom)
|
||||||
|
if (this.myChart) {
|
||||||
|
echarts.dispose(this.myChart)
|
||||||
|
}
|
||||||
|
this.myChart = echarts.init(chartDom)
|
||||||
|
const chartOption = this.$_.cloneDeep(entityDetailSubscriberTopApp)
|
||||||
|
chartOption.series[0].data = data.map(d => {
|
||||||
|
return [d.count, d.app]
|
||||||
|
}).reverse()
|
||||||
|
this.myChart.setOption(chartOption)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resize () {
|
||||||
|
if (this.myChart) {
|
||||||
|
this.myChart.resize()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
httpError (e) {
|
||||||
|
this.isNoData = false
|
||||||
|
this.showError = true
|
||||||
|
this.errorMsg = this.errorMsgHandler(e)
|
||||||
|
},
|
||||||
|
reload (startTime, endTime, dateRangeValue) {
|
||||||
|
this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime) }
|
||||||
|
const { query } = this.$route
|
||||||
|
|
||||||
|
const newUrl = urlParamsHandler(window.location.href, query, {
|
||||||
|
topAppStartTime: this.timeFilter.startTime,
|
||||||
|
topAppEndTime: this.timeFilter.endTime,
|
||||||
|
topAppRange: dateRangeValue.value
|
||||||
|
})
|
||||||
|
overwriteUrl(newUrl)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.myChart = null
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.init()
|
||||||
|
})
|
||||||
|
window.addEventListener('resize', this.resize)
|
||||||
|
},
|
||||||
|
beforeUnmount () {
|
||||||
|
window.removeEventListener('resize', this.resize)
|
||||||
|
if (this.myChart) {
|
||||||
|
echarts.dispose(this.myChart)
|
||||||
|
}
|
||||||
|
// 检测时发现该方法占用较大内存,且未被释放
|
||||||
|
this.unitConvert = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -5,7 +5,8 @@ import {
|
|||||||
chartColor5,
|
chartColor5,
|
||||||
chartColor6,
|
chartColor6,
|
||||||
chartColorForBehaviorPattern,
|
chartColorForBehaviorPattern,
|
||||||
unitTypes
|
unitTypes,
|
||||||
|
chartColorForSubscriberTopApp
|
||||||
} from '@/utils/constants'
|
} from '@/utils/constants'
|
||||||
import unitConvert from '@/utils/unit-convert'
|
import unitConvert from '@/utils/unit-convert'
|
||||||
import { axisFormatter } from '@/views/charts/charts/tools'
|
import { axisFormatter } from '@/views/charts/charts/tools'
|
||||||
@@ -666,3 +667,58 @@ export const stackedBarChartOption = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const entityDetailSubscriberTopApp = {
|
||||||
|
xAxis: {
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisLabel: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: 0,
|
||||||
|
left: 15,
|
||||||
|
right: 20,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#E2E5EC'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
barWidth: 14,
|
||||||
|
barMaxHeight: 20,
|
||||||
|
itemStyle: {
|
||||||
|
color: function (params) {
|
||||||
|
const colorList = chartColorForSubscriberTopApp
|
||||||
|
return colorList[params.dataIndex]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
type: 'bar',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'right',
|
||||||
|
valueAnimation: true,
|
||||||
|
formatter: function (param, index, callback) {
|
||||||
|
return `${unitConvert(param.value[0], unitTypes.percent, null, null, 1).join(' ')}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user