perf: No data提示、折线图优化
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
<slot name="operations"></slot>
|
<slot name="operations"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__body" v-loading="loading">
|
<div class="cn-chart__body" v-loading="loading" v-no-data="noData">
|
||||||
<el-table
|
<el-table
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
tooltip-effect="light"
|
tooltip-effect="light"
|
||||||
@@ -55,7 +55,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
tableColumns: Array,
|
tableColumns: Array,
|
||||||
tableData: Array,
|
tableData: Array,
|
||||||
loading: Boolean
|
loading: Boolean,
|
||||||
|
noData: Boolean
|
||||||
},
|
},
|
||||||
setup () {
|
setup () {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
<slot name="operations"></slot>
|
<slot name="operations"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__body" :class="{'pie-with-table': isPieWithTable}" v-loading="loading">
|
<div class="cn-chart__body" :class="{'pie-with-table': isPieWithTable}" v-loading="loading" v-no-data="noData">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="cn-chart__footer" v-if="layout.indexOf(layoutConstant.FOOTER) > -1" :class="{'pie-with-table': isPieWithTable}" v-loading="loading">
|
<div class="cn-chart__footer" v-if="layout.indexOf(layoutConstant.FOOTER) > -1 && !noData" :class="{'pie-with-table': isPieWithTable}" v-loading="loading">
|
||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,7 +24,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
layout: Array,
|
layout: Array,
|
||||||
chartInfo: Object,
|
chartInfo: Object,
|
||||||
loading: Boolean
|
loading: Boolean,
|
||||||
|
noData: Boolean
|
||||||
},
|
},
|
||||||
setup (props) {
|
setup (props) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import store from '@/store'
|
|||||||
import App from '@/App.vue'
|
import App from '@/App.vue'
|
||||||
import { hasPermission } from '@/permission'
|
import { hasPermission } from '@/permission'
|
||||||
import commonMixin from '@/mixins/common'
|
import commonMixin from '@/mixins/common'
|
||||||
import { cancelWithChange, clickOutside } from '@/utils/tools'
|
import { cancelWithChange, clickOutside, noData } from '@/utils/tools'
|
||||||
import { ClickOutside } from 'element-plus/lib/directives'
|
import { ClickOutside } from 'element-plus/lib/directives'
|
||||||
import i18n from '@/i18n'
|
import i18n from '@/i18n'
|
||||||
import '@/assets/css/main.scss' // 样式入口
|
import '@/assets/css/main.scss' // 样式入口
|
||||||
@@ -36,6 +36,7 @@ app.directive('has', hasPermission) // 注册指令
|
|||||||
app.directive('click-outside', clickOutside)
|
app.directive('click-outside', clickOutside)
|
||||||
app.directive('ele-click-outside', ClickOutside)
|
app.directive('ele-click-outside', ClickOutside)
|
||||||
app.directive('cancel', cancelWithChange)
|
app.directive('cancel', cancelWithChange)
|
||||||
|
app.directive('no-data', noData)
|
||||||
app.config.globalProperties.$_ = _
|
app.config.globalProperties.$_ = _
|
||||||
|
|
||||||
app.mixin(commonMixin)
|
app.mixin(commonMixin)
|
||||||
|
|||||||
@@ -346,7 +346,15 @@ export const clickOutside = {
|
|||||||
delete el.__vueClickOutside__
|
delete el.__vueClickOutside__
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export const noData = {
|
||||||
|
updated (el, binding) {
|
||||||
|
if (el) {
|
||||||
|
if (binding.value) {
|
||||||
|
el.innerHTML = '<div style="display: flex; justify-content: center; align-items: center; height: 100%; color: #999;">No data</div>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
export function isEqual (o1, o2) {
|
export function isEqual (o1, o2) {
|
||||||
const isEqualForInner = function (obj1, obj2) {
|
const isEqualForInner = function (obj1, obj2) {
|
||||||
const o1 = obj1 instanceof Object
|
const o1 = obj1 instanceof Object
|
||||||
@@ -447,3 +455,5 @@ function JSONParse (data) {
|
|||||||
return firstParse
|
return firstParse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
:style="computePosition"
|
:style="computePosition"
|
||||||
:chartInfo="chartInfo"
|
:chartInfo="chartInfo"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
:no-data="noData"
|
||||||
>
|
>
|
||||||
<template #title v-if="layout.indexOf(layoutConstant.HEADER) > -1">
|
<template #title v-if="layout.indexOf(layoutConstant.HEADER) > -1">
|
||||||
{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}
|
{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}
|
||||||
@@ -102,6 +103,7 @@
|
|||||||
:table-data="table.currentPageData"
|
:table-data="table.currentPageData"
|
||||||
:style="computePosition"
|
:style="computePosition"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
:no-data="noData"
|
||||||
>
|
>
|
||||||
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
|
<template #title>{{chartInfo.i18n ? $t(chartInfo.i18n) : chartInfo.name}}</template>
|
||||||
<template #operations>
|
<template #operations>
|
||||||
@@ -230,6 +232,7 @@ export default {
|
|||||||
allSelectPieChartName: [],
|
allSelectPieChartName: [],
|
||||||
chartOption: null,
|
chartOption: null,
|
||||||
loading: true,
|
loading: true,
|
||||||
|
noData: false, // 查询结果为空
|
||||||
throttle: null // 节流器
|
throttle: null // 节流器
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -324,17 +327,19 @@ export default {
|
|||||||
getTitle (r) {
|
getTitle (r) {
|
||||||
let title = ''
|
let title = ''
|
||||||
if (r.establishLatency || r.httpResponseLatency || r.sslConLatency) {
|
if (r.establishLatency || r.httpResponseLatency || r.sslConLatency) {
|
||||||
title = `${title}: ${unitConvert(r.establishLatency || r.httpResponseLatency || r.sslConLatency, unitTypes.time).join(' ')}`
|
title = `: ${unitConvert(r.establishLatency || r.httpResponseLatency || r.sslConLatency, unitTypes.time).join(' ')}`
|
||||||
}
|
}
|
||||||
if (r.sequenceGapLossPercent || r.pktRetransPercent) {
|
if (r.sequenceGapLossPercent || r.pktRetransPercent) {
|
||||||
title = `${title}: ${unitConvert(r.sequenceGapLossPercent || r.pktRetransPercent, unitTypes.number).join(' ')} %`
|
const d = unitConvert(r.sequenceGapLossPercent || r.pktRetransPercent, unitTypes.number)
|
||||||
|
title = d[0] === '0.00' ? ': 0' : `: ${d.join(' ')} %`
|
||||||
}
|
}
|
||||||
if (r.sessions) {
|
if (r.sessions) {
|
||||||
title = `${title}\nSessions: ${unitConvert(r.sessions, unitTypes.number).join(' ')}`
|
title = `\nSessions: ${unitConvert(r.sessions, unitTypes.number).join(' ')}`
|
||||||
}
|
}
|
||||||
if (r.bytes) {
|
if (r.bytes) {
|
||||||
title = `${title}\nBytes: ${unitConvert(r.bytes, unitTypes.byte).join(' ')}`
|
title = `\nBytes: ${unitConvert(r.bytes, unitTypes.byte).join(' ')}`
|
||||||
}
|
}
|
||||||
|
title = title || ': 0'
|
||||||
return title
|
return title
|
||||||
},
|
},
|
||||||
changeTab (tab) {
|
changeTab (tab) {
|
||||||
@@ -445,21 +450,22 @@ export default {
|
|||||||
max: am4Core.color('#D95D41')
|
max: am4Core.color('#D95D41')
|
||||||
})
|
})
|
||||||
|
|
||||||
polygonSeries.data = response.data.result.map(r => {
|
const seriesData = data.map(r => {
|
||||||
|
const value = r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions
|
||||||
return {
|
return {
|
||||||
id: r.serverId,
|
id: r.serverId,
|
||||||
title: this.getTitle(r),
|
title: this.getTitle(r),
|
||||||
value: r.establishLatency || r.httpResponseLatency || r.sslConLatency || r.sequenceGapLossPercent || r.pktRetransPercent || r.sessions
|
value: value || 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const sorted = polygonSeries.data.sort((a, b) => b.value - a.value)
|
polygonSeries.data = [...seriesData]
|
||||||
|
const sorted = seriesData.sort((a, b) => b.value - a.value)
|
||||||
|
|
||||||
const heatLegend = this.myChart.createChild(am4Maps.HeatLegend)
|
const heatLegend = this.myChart.createChild(am4Maps.HeatLegend)
|
||||||
heatLegend.series = polygonSeries
|
heatLegend.series = polygonSeries
|
||||||
heatLegend.align = 'center'
|
heatLegend.align = 'center'
|
||||||
console.info(sorted)
|
heatLegend.minValue = 0
|
||||||
if (!this.$_.isEmpty(sorted)) {
|
if (!this.$_.isEmpty(sorted)) {
|
||||||
heatLegend.minValue = 0
|
|
||||||
heatLegend.maxValue = Number(sorted[0].value)
|
heatLegend.maxValue = Number(sorted[0].value)
|
||||||
}
|
}
|
||||||
heatLegend.markerContainer.height = 6
|
heatLegend.markerContainer.height = 6
|
||||||
@@ -514,11 +520,40 @@ export default {
|
|||||||
this.initEchartsWithPieTable(chartParams)
|
this.initEchartsWithPieTable(chartParams)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
timeLineIsAllZero (data) {
|
||||||
|
let allZero = true
|
||||||
|
try {
|
||||||
|
data.forEach(d => {
|
||||||
|
d.values.forEach(r => {
|
||||||
|
if (r[1] && r[1] !== '0' && r[1] !== 0) {
|
||||||
|
allZero = false
|
||||||
|
throw new Error('break')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (e) {}
|
||||||
|
return allZero
|
||||||
|
},
|
||||||
initECharts (chartParams) {
|
initECharts (chartParams) {
|
||||||
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
|
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
|
||||||
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
|
if (this.$_.isEmpty(response.data.result)) {
|
||||||
|
this.noData = true
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
this.noData = false
|
||||||
|
}
|
||||||
const seriesTemplate = this.chartOption.series[0]
|
const seriesTemplate = this.chartOption.series[0]
|
||||||
|
const allZero = this.timeLineIsAllZero(response.data.result)
|
||||||
|
if (allZero) {
|
||||||
|
this.chartOption.yAxis = {
|
||||||
|
...this.chartOption.yAxis,
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
interval: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
this.chartOption.series = response.data.result.map(r => {
|
this.chartOption.series = response.data.result.map(r => {
|
||||||
return {
|
return {
|
||||||
...seriesTemplate,
|
...seriesTemplate,
|
||||||
@@ -545,6 +580,21 @@ export default {
|
|||||||
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
|
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), ...this.entity }
|
||||||
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
|
if (this.$_.isEmpty(response.data.result)) {
|
||||||
|
this.noData = true
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
this.noData = false
|
||||||
|
}
|
||||||
|
const allZero = this.timeLineIsAllZero(response.data.result)
|
||||||
|
if (allZero) {
|
||||||
|
this.chartOption.yAxis = {
|
||||||
|
...this.chartOption.yAxis,
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
interval: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
this.statisticsData = response.data.result.map(d => {
|
this.statisticsData = response.data.result.map(d => {
|
||||||
return {
|
return {
|
||||||
...d,
|
...d,
|
||||||
@@ -580,6 +630,21 @@ export default {
|
|||||||
tableQueryParams[chartParams.nameColumn] = [] // 处理两个图表不一样的地方
|
tableQueryParams[chartParams.nameColumn] = [] // 处理两个图表不一样的地方
|
||||||
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
|
if (this.$_.isEmpty(response.data.result)) {
|
||||||
|
this.noData = true
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
this.noData = false
|
||||||
|
}
|
||||||
|
const allZero = this.timeLineIsAllZero(response.data.result)
|
||||||
|
if (allZero) {
|
||||||
|
this.chartOption.yAxis = {
|
||||||
|
...this.chartOption.yAxis,
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
interval: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
const data = response.data.result.map(d => {
|
const data = response.data.result.map(d => {
|
||||||
tableQueryParams[chartParams.nameColumn].push(d[chartParams.nameColumn])
|
tableQueryParams[chartParams.nameColumn].push(d[chartParams.nameColumn])
|
||||||
return {
|
return {
|
||||||
@@ -664,6 +729,12 @@ export default {
|
|||||||
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), limit: this.table.limit, order: this.table.orderBy, ...this.entity }
|
const queryParams = { startTime: parseInt(this.timeFilter.startTime / 1000), endTime: parseInt(this.timeFilter.endTime / 1000), limit: this.table.limit, order: this.table.orderBy, ...this.entity }
|
||||||
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
get(replaceUrlPlaceholder(chartParams.url, queryParams)).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
|
if (this.$_.isEmpty(response.data.result)) {
|
||||||
|
this.noData = true
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
this.noData = false
|
||||||
|
}
|
||||||
this.table.tableData = response.data.result
|
this.table.tableData = response.data.result
|
||||||
this.table.tableColumns = this.getTableTitle(response.data.result)
|
this.table.tableColumns = this.getTableTitle(response.data.result)
|
||||||
this.table.currentPageData = this.getTargetPageData(1, this.table.pageSize, this.table.tableData)
|
this.table.currentPageData = this.getTargetPageData(1, this.table.pageSize, this.table.tableData)
|
||||||
|
|||||||
Reference in New Issue
Block a user