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; bottom: 0;
z-index: 1; 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{ .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-", "css_prefix_text": "nz-icon-",
"description": "", "description": "",
"glyphs": [ "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", "icon_id": "8990443",
"name": "竖向分布", "name": "竖向分布",

File diff suppressed because one or more lines are too long

View File

@@ -14,19 +14,19 @@
fontSize: item.fontSize, fontSize: item.fontSize,
}" }"
> >
<div style="position: relative;z-index: 10;"> <div style="maxWidth:100%;position: relative;z-index: 10;">
<!-- all --> <!-- all -->
<template v-if="chartInfo.param.text==='all'"> <template v-if="chartInfo.param.text==='all'">
<div v-if="item.mapping" :style="{color:item.mapping.color.text}"> <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"> <template v-if="item.mapping && item.mapping.icon">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i> <i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
</template> </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>
<div v-else> <div v-else>
<p>{{item.legend}}</p> <p style="white-space: nowrap;">{{item.legend}}</p>
<span>{{item.showValue}}</span> <span style="white-space: nowrap;">{{item.showValue}}</span>
</div> </div>
</template> </template>
<!-- legend --> <!-- legend -->
@@ -35,9 +35,9 @@
<template v-if="item.mapping && item.mapping.icon"> <template v-if="item.mapping && item.mapping.icon">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i> <i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
</template> </template>
<span>{{item.legend}}</span> <span style="white-space: nowrap;">{{item.legend}}</span>
</div> </div>
<div v-else>{{item.legend}}</div> <div v-else style="white-space: nowrap;">{{item.legend}}</div>
</template> </template>
<!-- value --> <!-- value -->
<template v-if="chartInfo.param.text==='value'|| !chartInfo.param.text"> <template v-if="chartInfo.param.text==='value'|| !chartInfo.param.text">
@@ -45,12 +45,30 @@
<template v-if="item.mapping && item.mapping.icon"> <template v-if="item.mapping && item.mapping.icon">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i> <i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
</template> </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>
<div v-else>{{item.showValue}}</div> <div v-else style="white-space: nowrap;">{{item.showValue}}</div>
</template> </template>
<!-- none --> <!-- none -->
<template v-if="chartInfo.param.text==='none'"></template> <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> </div>
<!-- sparkline --> <!-- sparkline -->
@@ -91,6 +109,7 @@ export default {
data () { data () {
return { return {
statData: [], statData: [],
previousData: [],
boxWidth: 0, boxWidth: 0,
boxHeight: 0, boxHeight: 0,
boxPadding: 2, boxPadding: 2,
@@ -130,6 +149,7 @@ export default {
}, },
initStatData (chartInfo, originalDatas) { initStatData (chartInfo, originalDatas) {
this.statData = [] this.statData = []
this.previousData = []
const decimals = this.chartInfo.param.decimals || 2 const decimals = this.chartInfo.param.decimals || 2
return new Promise(resolve => { return new Promise(resolve => {
let colorIndex = 0 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.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.mapping = this.selectMapping(stat.value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
stat.data = data.values stat.data = data.values
this.statData.push(stat) // 判断是否是对比的数据
if (expressionIndex < chartInfo.elements.length) {
this.statData.push(stat)
} else {
this.previousData.push(stat)
}
colorIndex++ colorIndex++
}) })
}) })
// 计算最大值
let maxValue = 0 let maxValue = 0
if (this.statData.length > 0) { if (this.statData.length > 0) {
maxValue = 0 maxValue = 0
@@ -173,6 +199,35 @@ export default {
} }
this.maxValue = maxValue 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) this.$emit('chartIsNoData', this.isNoData)
resolve() resolve()
}) })
@@ -431,6 +486,25 @@ export default {
} }
} }
return len 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 () { mounted () {

View File

@@ -65,7 +65,7 @@
import chartHeader from '@/components/chart/chartHeader' import chartHeader from '@/components/chart/chartHeader'
import ChartScreenHeader from '@/components/chart/ChartScreenHeader' import ChartScreenHeader from '@/components/chart/ChartScreenHeader'
import chart from '@/components/chart/chart' 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 { chartType, fromRoute } from '@/components/common/js/constants'
import bus from '@/libs/bus' import bus from '@/libs/bus'
import axios from 'axios' import axios from 'axios'
@@ -269,9 +269,6 @@ export default {
query += '&direction=forward' query += '&direction=forward'
} }
} }
// if (isChartPie(this.chartInfo.type)) {
// query += `&statistics=${this.chartInfo.param.statistics || 'last'}`
// }
query += `&query=${encodeURIComponent(this.variablesReplace(element.expression))}` query += `&query=${encodeURIComponent(this.variablesReplace(element.expression))}`
return this.$get(query) return this.$get(query)
}) })
@@ -292,14 +289,58 @@ export default {
if (this.chartInfo.datasource === 'logs') { if (this.chartInfo.datasource === 'logs') {
query += '&format=1' query += '&format=1'
} }
// if (isChartPie(this.chartInfo.type)) {
// query += `&statistics=${this.chartInfo.param.statistics || 'last'}`
// }
query += `&query=${encodeURIComponent(element.expression)}` query += `&query=${encodeURIComponent(element.expression)}`
return this.$get(query) return this.$get(query)
}) })
requests = requests.concat(multipleRequests) 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 = [] const chartData = []
axios.all(requests).then((res) => { axios.all(requests).then((res) => {
res.forEach((r, rIndex) => { res.forEach((r, rIndex) => {
@@ -314,7 +355,7 @@ export default {
chartData.push({ error: r.msg || r.error || r }) chartData.push({ error: r.msg || r.error || r })
this.isError = true this.isError = true
} }
} else { } else if (isTimeSeries(this.chartInfo.type)) {
if (r.status === 'success') { if (r.status === 'success') {
r.data.result.forEach(item => { r.data.result.forEach(item => {
item.elements = elements[rIndex - elements.length] item.elements = elements[rIndex - elements.length]
@@ -328,6 +369,17 @@ export default {
chartData.push({ error: r.msg || r.error || r }) chartData.push({ error: r.msg || r.error || r })
this.isError = true 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)) this.chartData = JSON.parse(JSON.stringify(chartData))

View File

@@ -467,8 +467,9 @@ export default {
if (!this.chart.groupId || this.chart.groupId == -1) { if (!this.chart.groupId || this.chart.groupId == -1) {
this.chart.groupId = '' this.chart.groupId = ''
} }
if (this.chart.type === 'stat' && !this.chart.param.sparklineMode) { if (this.chart.type === 'stat') {
this.chart.param.sparklineMode = 'none' 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') { if (this.chart.type == 'table') {
const arr = this.chart.param.indexs ? this.chart.param.indexs.split(',') : [] 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"/> show-word-limit v-model="chartConfig.param.decimals"/>
</el-form-item> </el-form-item>
<!-- Sparkline mode --> <!-- 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 <el-select
v-model="chartConfig.param.sparklineMode" v-model="chartConfig.param.sparklineMode"
placeholder="" placeholder=""
@@ -428,6 +428,26 @@
</el-form-item> </el-form-item>
</div> </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 --> <!-- Right Y Axis -->
<div v-if="isShowRightYAxis(chartConfig.type)"> <div v-if="isShowRightYAxis(chartConfig.type)">
<div class="form__sub-title"> <div class="form__sub-title">
@@ -1216,8 +1236,9 @@ export default {
case 'bubble': case 'bubble':
case 'rank': case 'rank':
case 'funnel': case 'funnel':
if (this.oldType === 'stat') { if (type === 'stat') {
this.chartConfig.param.sparklineMode = 'line' 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') { if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'sankey' || this.oldType === 'hexagon' || this.oldType === 'bubble' || this.oldType === 'rank' || this.oldType === 'funnel') {
break break
@@ -1243,7 +1264,8 @@ export default {
varValue: '', varValue: '',
result: 'show' result: 'show'
}, },
sparklineMode: 'line' sparklineMode: 'line',
comparison: 'none'
} }
break break
case 'bar': case 'bar':

View File

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

View File

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

View File

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

View File

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