NEZ-2702 feat:stat图表增加对比功能(Comparison)

This commit is contained in:
zyh
2023-03-21 16:53:15 +08:00
parent f49284dc50
commit bc49fc6ff8
17 changed files with 258 additions and 40 deletions

View File

@@ -540,6 +540,22 @@
bottom: 0;
z-index: 1;
}
.comparison-text{
text-align: center;
color: $--color-text-secondary;
word-break: normal;
.nz-icon{
font-size: 1em;
color: $--color-text-regular;
}
.comparison-increase{
color: #19be6b;
}
.comparison-decrease{
// color: #ed4014;
color: #eb1010;
}
}
}
}
.chart-gauge-box{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,20 @@
"css_prefix_text": "nz-icon-",
"description": "",
"glyphs": [
{
"icon_id": "20226387",
"name": "下降",
"font_class": "xiajiang1",
"unicode": "e61e",
"unicode_decimal": 58910
},
{
"icon_id": "20226386",
"name": "上升",
"font_class": "shangsheng",
"unicode": "e61d",
"unicode_decimal": 58909
},
{
"icon_id": "8990443",
"name": "竖向分布",

File diff suppressed because one or more lines are too long

View File

@@ -14,19 +14,19 @@
fontSize: item.fontSize,
}"
>
<div style="position: relative;z-index: 10;">
<div style="maxWidth:100%;position: relative;z-index: 10;">
<!-- all -->
<template v-if="chartInfo.param.text==='all'">
<div v-if="item.mapping" :style="{color:item.mapping.color.text}">
<p>{{item.legend}}</p>
<p style="white-space: nowrap;">{{item.legend}}</p>
<template v-if="item.mapping && item.mapping.icon">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
</template>
<span>{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span>
<span style="white-space: nowrap;">{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span>
</div>
<div v-else>
<p>{{item.legend}}</p>
<span>{{item.showValue}}</span>
<p style="white-space: nowrap;">{{item.legend}}</p>
<span style="white-space: nowrap;">{{item.showValue}}</span>
</div>
</template>
<!-- legend -->
@@ -35,9 +35,9 @@
<template v-if="item.mapping && item.mapping.icon">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
</template>
<span>{{item.legend}}</span>
<span style="white-space: nowrap;">{{item.legend}}</span>
</div>
<div v-else>{{item.legend}}</div>
<div v-else style="white-space: nowrap;">{{item.legend}}</div>
</template>
<!-- value -->
<template v-if="chartInfo.param.text==='value'|| !chartInfo.param.text">
@@ -45,12 +45,30 @@
<template v-if="item.mapping && item.mapping.icon">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
</template>
<span>{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span>
<span style="white-space: nowrap;">{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span>
</div>
<div v-else>{{item.showValue}}</div>
<div v-else style="white-space: nowrap;">{{item.showValue}}</div>
</template>
<!-- none -->
<template v-if="chartInfo.param.text==='none'"></template>
<!-- comparison -->
<template v-if="comparisonShow(item)">
<div class="comparison-text" :style="{fontSize: parseInt(item.fontSize)/2>12?parseInt(item.fontSize)/2+'px':'12px'}">
<span v-if="chartInfo.param.comparison==='hour'">{{$t('dashboard.hourComparison')}}</span>
<span v-if="chartInfo.param.comparison==='day'">{{$t('dashboard.dayComparison')}}</span>
<span v-if="chartInfo.param.comparison==='week'">{{$t('dashboard.weekComparison')}}</span>
<span v-if="chartInfo.param.comparison==='month'">{{$t('dashboard.monthComparison')}}</span>
<span style="white-space: nowrap;">
<!-- number -->
<span :class="item.comparison.startsWith('+')?'comparison-increase':'comparison-decrease'">&nbsp;{{item.comparison}}</span>
<!-- icon -->
<i v-if="item.comparison.startsWith('+')" class="nz-icon nz-icon-shangsheng comparison-increase"></i>
<i v-else-if="item.comparison=='-'||item.comparison=='0%'" class="nz-icon nz-icon-xiajiang1"></i>
<i v-else class="nz-icon nz-icon-xiajiang1 comparison-decrease"></i>
</span>
</div>
</template>
</div>
<!-- sparkline -->
@@ -91,6 +109,7 @@ export default {
data () {
return {
statData: [],
previousData: [],
boxWidth: 0,
boxHeight: 0,
boxPadding: 2,
@@ -130,6 +149,7 @@ export default {
},
initStatData (chartInfo, originalDatas) {
this.statData = []
this.previousData = []
const decimals = this.chartInfo.param.decimals || 2
return new Promise(resolve => {
let colorIndex = 0
@@ -156,10 +176,16 @@ export default {
stat.showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(stat.value, null, -1, decimals)
stat.mapping = this.selectMapping(stat.value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
stat.data = data.values
this.statData.push(stat)
// 判断是否是对比的数据
if (expressionIndex < chartInfo.elements.length) {
this.statData.push(stat)
} else {
this.previousData.push(stat)
}
colorIndex++
})
})
// 计算最大值
let maxValue = 0
if (this.statData.length > 0) {
maxValue = 0
@@ -173,6 +199,35 @@ export default {
}
this.maxValue = maxValue
// 计算同比变化量
if (this.chartInfo.param.comparison && this.chartInfo.param.comparison !== 'none') {
this.statData.forEach(item => {
const previousName = 'Previous ' + item.name
const findItem = this.previousData.find((subItem) => previousName === subItem.name)
if (!findItem) {
item.comparison = '-'
return
}
const value = Number(item.value)
const previousValue = Number(findItem.value)
let comparison
if (isNaN(value) || isNaN(previousValue)) {
comparison = '-'
} else if (value == 0 && previousValue == 0) {
comparison = '0%'
} else if (value == 0 || previousValue == 0) {
comparison = '-'
} else {
comparison = parseFloat(((value - previousValue) / previousValue * 100).toFixed(2))
if (comparison > 0) {
comparison = '+' + comparison
}
comparison += '%'
}
item.comparison = comparison
})
}
this.$emit('chartIsNoData', this.isNoData)
resolve()
})
@@ -431,6 +486,25 @@ export default {
}
}
return len
},
comparisonShow (item) {
const minWidth = 145
let minHeight
switch (this.chartInfo.param.text) {
case 'all':
minHeight = 64
break
case 'legend':
minHeight = 40
break
case 'value':
minHeight = 40
break
case 'none':
minHeight = 24
break
}
return this.chartInfo.param.comparison && this.chartInfo.param.comparison !== 'none' && item.height > minHeight && item.width > minWidth
}
},
mounted () {

View File

@@ -65,7 +65,7 @@
import chartHeader from '@/components/chart/chartHeader'
import ChartScreenHeader from '@/components/chart/ChartScreenHeader'
import chart from '@/components/chart/chart'
import { isChartPie, isTimeSeries, getGroupHeight, isGroup } from './chart/tools'
import { isStat, isTimeSeries, getGroupHeight, isGroup } from './chart/tools'
import { chartType, fromRoute } from '@/components/common/js/constants'
import bus from '@/libs/bus'
import axios from 'axios'
@@ -269,9 +269,6 @@ export default {
query += '&direction=forward'
}
}
// if (isChartPie(this.chartInfo.type)) {
// query += `&statistics=${this.chartInfo.param.statistics || 'last'}`
// }
query += `&query=${encodeURIComponent(this.variablesReplace(element.expression))}`
return this.$get(query)
})
@@ -292,14 +289,58 @@ export default {
if (this.chartInfo.datasource === 'logs') {
query += '&format=1'
}
// if (isChartPie(this.chartInfo.type)) {
// query += `&statistics=${this.chartInfo.param.statistics || 'last'}`
// }
query += `&query=${encodeURIComponent(element.expression)}`
return this.$get(query)
})
requests = requests.concat(multipleRequests)
}
// stat图表开启对比
if (isStat(this.chartInfo.type)) {
let comparisonSt // 比较开始时间
let comparisonEt // 比较结束时间
const oneDay = 86400
switch (this.chartInfo.param.comparison) {
case 'none': {
break
}
case 'hour': { // 比较一小时前
comparisonSt = startTime - 3600
comparisonEt = endTime - 3600
break
}
case 'day': { // 比较一天前
comparisonSt = startTime - oneDay
comparisonEt = endTime - oneDay
break
}
case 'week': { // 比较一星期前
comparisonSt = startTime - (oneDay * 7)
comparisonEt = endTime - (oneDay * 7)
break
}
case 'month': { // 比较一个月前
comparisonSt = startTime - (oneDay * 30)
comparisonEt = endTime - (oneDay * 30)
break
}
}
if (comparisonSt && comparisonEt) {
const comparisonRequests = elements.map((element) => {
let query = `${urlPre}/api/v1/query_range?start=${comparisonSt}&end=${comparisonEt}&step=${step}`
if (element.filter) {
query += `&filter=${element.filter}`
}
if (this.chartInfo.datasource === 'logs') {
query += '&format=1'
}
query += `&query=${encodeURIComponent(element.expression)}`
return this.$get(query)
})
requests = requests.concat(comparisonRequests)
}
}
const chartData = []
axios.all(requests).then((res) => {
res.forEach((r, rIndex) => {
@@ -314,7 +355,7 @@ export default {
chartData.push({ error: r.msg || r.error || r })
this.isError = true
}
} else {
} else if (isTimeSeries(this.chartInfo.type)) {
if (r.status === 'success') {
r.data.result.forEach(item => {
item.elements = elements[rIndex - elements.length]
@@ -328,6 +369,17 @@ export default {
chartData.push({ error: r.msg || r.error || r })
this.isError = true
}
} else if (isStat(this.chartInfo.type)) { // stat图表开启对比
if (r.status === 'success') {
r.data.result.forEach(item => {
item.elements = elements[rIndex - elements.length]
this.allDataLength++
})
chartData.push(r.data.result)
} else {
chartData.push({ error: r.msg || r.error || r })
this.isError = true
}
}
})
this.chartData = JSON.parse(JSON.stringify(chartData))

View File

@@ -467,8 +467,9 @@ export default {
if (!this.chart.groupId || this.chart.groupId == -1) {
this.chart.groupId = ''
}
if (this.chart.type === 'stat' && !this.chart.param.sparklineMode) {
this.chart.param.sparklineMode = 'none'
if (this.chart.type === 'stat') {
if (!this.chart.param.sparklineMode) { this.chart.param.sparklineMode = 'none' }
if (!this.chart.param.comparison) { this.chart.param.comparison = 'none' }
}
if (this.chart.type == 'table') {
const arr = this.chart.param.indexs ? this.chart.param.indexs.split(',') : []

View File

@@ -410,7 +410,7 @@
show-word-limit v-model="chartConfig.param.decimals"/>
</el-form-item>
<!-- Sparkline mode -->
<el-form-item :label="$t('dashboard.panel.chartForm.sparklineMode')" class="form-item--half-width" v-if="isShowSparkline(chartConfig.type)">
<el-form-item :label="$t('dashboard.chartForm.sparklineMode')" class="form-item--half-width" v-if="isStat(chartConfig.type)">
<el-select
v-model="chartConfig.param.sparklineMode"
placeholder=""
@@ -428,6 +428,26 @@
</el-form-item>
</div>
<div class="form-items--half-width-group">
<!-- comparison -->
<el-form-item :label="$t('dashboard.chartForm.comparison')" class="form-item--half-width" v-if="isStat(chartConfig.type)">
<el-select
v-model="chartConfig.param.comparison"
placeholder=""
popper-class="right-box-select-top prevent-clickoutside"
size="small"
@change="change"
>
<el-option
v-for="item in comparisonTypeList"
:key="item.value"
:label="$t(item.name)"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</div>
<!-- Right Y Axis -->
<div v-if="isShowRightYAxis(chartConfig.type)">
<div class="form__sub-title">
@@ -1216,8 +1236,9 @@ export default {
case 'bubble':
case 'rank':
case 'funnel':
if (this.oldType === 'stat') {
this.chartConfig.param.sparklineMode = 'line'
if (type === 'stat') {
if (!this.chartConfig.param.sparklineMode) { this.chartConfig.param.sparklineMode = 'line' }
if (!this.chartConfig.param.comparison) { this.chartConfig.param.comparison = 'none' }
}
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'sankey' || this.oldType === 'hexagon' || this.oldType === 'bubble' || this.oldType === 'rank' || this.oldType === 'funnel') {
break
@@ -1243,7 +1264,8 @@ export default {
varValue: '',
result: 'show'
},
sparklineMode: 'line'
sparklineMode: 'line',
comparison: 'none'
}
break
case 'bar':

View File

@@ -243,8 +243,9 @@ export default {
delete params.param.min
delete params.param.max
}
if (!this.isShowSparkline(params.type)) {
if (!this.isStat(params.type)) {
delete params.param.sparklineMode
delete params.param.comparison
}
if (!params.x && !params.y && this.from === 'endpointQuery') { // endpointQuery 新增 放在最后
params.x = 0

View File

@@ -232,7 +232,7 @@ export default {
default: return false
}
},
isShowSparkline (type) {
isStat (type) {
switch (type) {
case 'stat':
return true

View File

@@ -391,6 +391,28 @@ export default {
id: 'none',
name: this.$t('project.topology.none')
}
],
comparisonTypeList: [
{
id: 'none',
name: this.$t('project.topology.none')
},
{
id: 'hour',
name: this.$t('dashboard.chartForm.comparison.hour')
},
{
id: 'day',
name: this.$t('dashboard.chartForm.comparison.day')
},
{
id: 'week',
name: this.$t('dashboard.chartForm.comparison.week')
},
{
id: 'month',
name: this.$t('dashboard.chartForm.comparison.month')
}
]
}
},

View File

@@ -486,7 +486,6 @@ export default {
if (!this.hasButton('dashboard_view')) {
return
}
console.log(this.rightBox.panel.show)
this.rightBox.panel.show = true
// 关闭selectDashboard弹框
this.$refs.selectDashboard && this.$refs.selectDashboard.esc()
@@ -602,8 +601,9 @@ export default {
if (!this.chart.groupId || this.chart.groupId == -1) {
this.chart.groupId = ''
}
if (this.chart.type === 'stat' && !this.chart.param.sparklineMode) {
this.chart.param.sparklineMode = 'none'
if (this.chart.type === 'stat') {
if (!this.chart.param.sparklineMode) { this.chart.param.sparklineMode = 'none' }
if (!this.chart.param.comparison) { this.chart.param.comparison = 'none' }
}
if (this.chart.type == 'table') {
const arr = this.chart.param.indexs ? this.chart.param.indexs.split(',') : []

View File

@@ -93,9 +93,9 @@ export default new Vue({
const thirtyDay = 2592000000
if (numInterval < oneDay) { // 小于1天step为15s
step = '15s'
} else if (numInterval < sevenDay) { // 小于7天step为15s
} else if (numInterval < sevenDay) { // 小于7天step为5m
step = '5m'
} else if (numInterval < thirtyDay) { // 小于30天step为15s
} else if (numInterval < thirtyDay) { // 小于30天step为10m
step = '10m'
} else {
step = '30m'