NEZ-1280 feat: 时间序列chart微调

This commit is contained in:
chenjinsong
2021-11-29 15:43:07 +08:00
parent d892f4e645
commit 4d30133907
8 changed files with 262 additions and 71 deletions

View File

@@ -97,6 +97,7 @@
&.nz-chart__component--right, &.nz-chart__component--left {
.legend-container {
flex-direction: column;
padding-top: 25px;
width: unset;
max-width: 50%;
max-height: unset;
@@ -169,3 +170,34 @@
}
}
}
.nz-chart__tooltip {
.tooltip__row {
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
display: flex;
justify-content: space-between;
min-width: 150px;
max-width: 600px;
line-height: 18px;
font-size: 12px;
.row__label {
max-width: 500px;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
.row__color-block {
display: inline-block;
margin-right: 5px;
border-radius: 10px;
width: 15px;
height: 5px;
}
}
.row__value {
padding-left: 5px;
}
}
}

View File

@@ -1,5 +1,9 @@
<template>
<div :class="legendPlacement" class="nz-chart__component nz-chart__component--time-series">
<div
:class="legendPlacement"
class="nz-chart__component nz-chart__component--time-series" @mouseenter="mouseEnterChart"
@mouseleave="mouseLeaveChart"
>
<div :id="`chart-canvas-${chartInfo.id}`" class="chart__canvas"></div>
<chart-legend
v-if="hasLegend"
@@ -14,17 +18,15 @@
import legend from './legend'
import chartMixin from '@/components/chart/chartMixin'
import * as echarts from 'echarts'
import lodash from 'lodash'
import moment from 'moment-timezone'
import bus from '@/libs/bus'
import { formatScientificNotation } from '@/components/common/js/tools'
import chartDataFormat from '@/components/charts/chartDataFormat'
import { randomcolor } from '@/components/common/js/radomcolor/randomcolor'
import { chartType, chartLegendPlacement } from '@/components/common/js/constants'
import { setChart } from '@/components/common/js/common'
import { getChart, setChart } from '@/components/common/js/common'
import { initColor } from '@/components/chart/chart/tools'
let myChart = null
export default {
name: 'chart-time-series', // x轴是时间的图包括折线、柱状、堆叠、散点
components: {
@@ -33,7 +35,8 @@ export default {
mixins: [chartMixin],
data () {
return {
stackTotalColor: null
stackTotalColor: null,
isStack: false
}
},
computed: {
@@ -64,30 +67,39 @@ export default {
this.legends = []
chartOption.series = this.handleTimeSeries(this.chartInfo, chartOption.series[0], this.chartData) // 生成series和legends
const { minTime, maxTime, minValue, maxValue } = this.getMinMaxFromData(this.chartData[0])
chartOption.xAxis.axisLabel.formatter = this.xAxisLabelFormatter(minTime, maxTime)
const { minTime, maxTime, minValue, maxValue } = this.getMinMaxFromData(this.chartData) // 此处时间是秒
chartOption.xAxis.axisLabel.formatter = this.xAxisLabelFormatter(minTime * 1000, maxTime * 1000) // 需转为毫秒
chartOption.tooltip.formatter = this.tooltipFormatter()
chartOption.yAxis.axisLabel.formatter = this.yAxisLabelFormatter(minValue, maxValue)
setTimeout(() => {
myChart = echarts.init(document.getElementById(`chart-canvas-${this.chartInfo.id}`))
const myChart = echarts.init(document.getElementById(`chart-canvas-${this.chartInfo.id}`))
setChart(this.chartInfo.id, myChart) // 缓存不使用vue的data是为避免整个chart被监听导致卡顿
myChart.setOption(chartOption)
this.initToolboxEvent(myChart) // 初始化toolbox事件
})
},
getMinMaxFromData (originalData) {
getMinMaxFromData (originalDatas) {
let minTime = null
let maxTime = null
let minValue = null
let maxValue = null
if (!lodash.isEmpty(originalData)) {
minTime = originalData[0][0]
maxTime = originalData[originalData.length - 1][0]
const sorted = originalData.sort((a, b) => {
// 将数据提为二维数组
let datas = []
originalDatas.forEach((originalData, expressionIndex) => {
originalData.forEach((data, dataIndex) => {
datas = [...datas, ...data.values]
})
})
const timeSorted = datas.sort((a, b) => {
return a[0] - b[0]
})
const valueSorted = datas.sort((a, b) => {
return a[1] - b[1]
})
minValue = sorted[0][1]
maxValue = sorted[sorted.length - 1][1]
}
minTime = timeSorted[0][0]
maxTime = timeSorted[timeSorted.length - 1][0]
minValue = valueSorted[0][1]
maxValue = valueSorted[valueSorted.length - 1][1]
return { minTime, maxTime, minValue, maxValue }
},
xAxisLabelFormatter (minTime, maxTime) {
@@ -121,7 +133,7 @@ export default {
tooltipFormatter () {
const self = this
return function (params) {
let str = '<div>'
let str = '<div class="nz-chart__tooltip">'
let sum = 0
params.forEach((item, i) => {
const color = self.colorList[item.seriesIndex]
@@ -134,44 +146,53 @@ export default {
}
const val = formatScientificNotation(item.data[1], paramsDot)
sum += self.numberWithEConvent(val)
// TODO 改成class
str += '<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;display: flex; justify-content: space-between; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 12px;">'
str += `<div style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;"><span style='display:inline-block;margin-right:5px;border-radius:10px;width:15px;height:5px;background-color: ${color};}'></span>${item.seriesName} </div>`
str += '<div style="padding-left: 10px;min-width: 75px;text-align: right">'
str += chartDataFormat.getUnit(self.chartInfo.unit ? self.chartInfo.unit : 2).compute(val, null, -1, paramsDot)
if (previousItem) {
str += '<span style="padding-left: 10px; display: inline-block;width: 65px;text-align: right">'
const previousval = formatScientificNotation(item.data[1], paramsDot)
let minusVal = 0
if (previousval <= val) {
minusVal = val - previousval
str += '+'
} else {
minusVal = previousval - val
str += '-'
}
str += chartDataFormat.getUnit(self.chartInfo.unit ? self.chartInfo.unit : 2).compute(minusVal, null, -1, paramsDot)
str += '</span>'
let previousDom = ''
if (previousItem) {
const previousVal = formatScientificNotation(item.data[1], paramsDot)
let minusVal = 0
let operator
if (previousVal <= val) {
minusVal = val - previousVal
operator = '+'
} else {
minusVal = previousVal - val
operator = '-'
}
str += '</div>'
str += '</div>'
previousDom = `
<span>${operator}${chartDataFormat.getUnit(self.chartInfo.unit ? self.chartInfo.unit : 2).compute(minusVal, null, -1, paramsDot)}</span>
`
}
str += `
<div class="tooltip__row">
<div class="row__label">
<span class="row__color-block" style="background-color: ${color}"></span>
<span>${item.seriesName}</span>
</div>
<div class="row__value">
<span>${chartDataFormat.getUnit(self.chartInfo.unit ? self.chartInfo.unit : 2).compute(val, null, -1, paramsDot)}</span>
${previousDom}
</div>
</div>
`
})
if (self.chartInfo.type === chartType.stackArea) {
if (!self.stackTotalColor || self.stackTotalColor == '') {
if (self.chartInfo.type === chartType.area) {
if (!self.stackTotalColor) {
self.stackTotalColor = randomcolor()
}
sum = parseFloat(Number(sum).toFixed(2))
// TODO 改成class
str += '<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;display: flex; justify-content: space-between; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 12px;">'
str += '<div style="line-height: 18px; font-size: 12px;padding-left:0px;">'
str += `<span style='display:inline-block;margin-right:5px;border-radius:10px;width:15px;height:5px;background-color: ${self.stackTotalColor};}'></span>`
str += self.$t('dashboard.panel.chartTotal')
str += '</div>'
str += '<div style="padding-left: 10px;">'
str += chartDataFormat.getUnit(self.chartInfo.unit ? self.chartInfo.unit : 2).compute(sum, null, self.chartDot)
str += '</div>'
str += '</div>'
str += `
<div class="tooltip__row">
<div class="row__label">
<span class="row__color-block" style="background-color: ${self.stackTotalColor}"></span>
<span>${self.$t('dashboard.panel.chartTotal')}</span>
</div>
<div class="row__value">
<span>${chartDataFormat.getUnit(self.chartInfo.unit ? self.chartInfo.unit : 2).compute(sum, null, self.chartDot)}</span>
</div>
</div>
`
}
str += '</div>'
@@ -198,11 +219,98 @@ export default {
} */
return unit.compute(value, index, -1, 2)
}
},
initToolboxEvent (myChart) {
const self = this
myChart.on('magictypechanged', function (params) {
self.isStack = !self.isStack
if (self.isStack) {
myChart.setOption({
toolbox: {
feature: {
dataZoom: {
yAxisIndex: false,
title: {
zoom: self.$t('overall.toolBox.zoom'),
back: self.$t('overall.toolBox.back')
}
},
magicType: {
type: ['stack'],
title: {
stack: self.$t('overall.toolBox.stack')
},
iconStyle: {
borderColor: '#7e7e7e'
},
emphasis: {
borderColor: '#53a3cb'
}
}
}
}
})
} else {
myChart.setOption({
toolbox: {
feature: {
dataZoom: {
yAxisIndex: false,
title: {
zoom: self.$t('overall.toolBox.zoom'),
back: self.$t('overall.toolBox.back')
}
},
magicType: {
type: ['stack'],
title: {
stack: self.$t('overall.toolBox.stack')
},
iconStyle: {
borderColor: '#7e7e7e'
},
emphasis: {
borderColor: '#7e7e7e'
}
}
}
}
})
}
})
},
mouseEnterChart () {
const myChart = getChart(this.chartInfo.id)
if (myChart) {
setTimeout(() => {
myChart.setOption({
toolbox: {
show: true
}
})
}, 300)
}
},
mouseLeaveChart () {
const myChart = getChart(this.chartInfo.id)
if (myChart) {
setTimeout(() => {
myChart.setOption({
toolbox: {
show: false
}
})
}, 300)
}
}
},
mounted () {
this.chartOption.color || (this.chartOption.color = initColor(20))
this.colorList = this.chartOption.color
try {
this.isStack = this.chartInfo.param.legend.stack
} catch (e) {}
this.initChart(this.chartOption)
}
}

View File

@@ -1,4 +1,5 @@
import { initColor } from '@/components/chart/chart/tools'
import i18n from '@/components/common/i18n'
export const chartTimeSeriesLineOption = {
title: {
show: false
@@ -7,7 +8,31 @@ export const chartTimeSeriesLineOption = {
show: false
},
toolbox: {
show: false
show: false,
top: '0',
right: '18',
showTitle: false,
feature: {
dataZoom: {
yAxisIndex: false,
title: {
zoom: i18n.t('overall.toolBox.zoom'),
back: i18n.t('overall.toolBox.back')
}
},
magicType: {
type: ['stack'],
title: {
stack: i18n.t('overall.toolBox.stack')
},
iconStyle: {
borderColor: '#7e7e7e'
},
emphasis: {
borderColor: '#7e7e7e'
}
}
}
},
tooltip: {
trigger: 'axis',
@@ -18,9 +43,9 @@ export const chartTimeSeriesLineOption = {
color: initColor(),
grid: {
left: 20,
right: 20,
top: 20,
bottom: 10,
right: 35,
top: 35,
bottom: 15,
containLabel: true
},
xAxis: {
@@ -92,8 +117,18 @@ export const chartTimeSeriesLineOption = {
useUTC: false // 使用本地时间
}
export const chartTimeSeriesAreaOption = {
...chartTimeSeriesLineOption,
series: [{
...chartTimeSeriesLineOption.series[0],
areaStyle: {}
}]
}
export const chartTimeSeriesScatterOption = {
...chartTimeSeriesLineOption,
toolbox: {},
series: [{
name: '',
type: 'scatter',
data: []
}]
}

View File

@@ -14,7 +14,7 @@ import {randomcolor} from "@/components/common/js/radomcolor/randomcolor";
export function getOption (type) {
let chartOption = null
switch (type) {
case chartType.stackArea: {
case chartType.area: {
chartOption = lodash.cloneDeep(chartTimeSeriesAreaOption)
break
}
@@ -48,7 +48,7 @@ export function getOption (type) {
}
export function isTimeSeries (type) {
return type === chartType.line || type === chartType.stackArea || type === chartType.point
return type === chartType.line || type === chartType.area || type === chartType.point
}
export function initColor (colorNum = 20) {

View File

@@ -24,7 +24,22 @@ export default {
s.name = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
if (chartInfo.param.stack) { // 堆叠
s.stack = 'Total'
s.areaStyle = {}
}
if (!lodash.isEmpty(chartInfo.param.thresholds)) { // 阈值
s.markLine = {
symbol: 'circle',
symbolSize: 5
}
s.markLine.data = chartInfo.param.thresholds.map(threshold => {
return {
yAxis: threshold.val,
lineStyle: {
color: threshold.color,
width: 2,
type: 'dotted'
}
}
})
}
series.push(s)
colorIndex++

View File

@@ -17,11 +17,11 @@ const chartData = {
height: 6,
updateBy: 1,
updateAt: '2021-11-10 07:06:09',
type: 'line',
type: 'area',
unit: 2,
weight: 0,
param: '{' +
' "style":"line",' +
' "style":"area",' +
' "stack":false,' +
' "legend":{' +
' "placement":"bottom",' +
@@ -29,10 +29,10 @@ const chartData = {
' },' +
' "thresholds":[{' +
' "color":"#eee",' +
' "val":"10.1"' +
' "val":"0.9"' +
' },{' +
' "color":"#aaa",' +
' "val":"base"' +
' "val":"1.1"' +
' }],' +
' "nullType":"zero"' +
'}',
@@ -130,22 +130,22 @@ const chartData = {
height: 4,
updateBy: 1,
updateAt: '2021-11-10 09:05:13',
type: 'line',
type: 'point',
unit: 2,
weight: 1,
param: '{' +
' "style":"line",' +
' "stack":true,' +
' "style":"point",' +
' "stack":false,' +
' "legend":{' +
' "placement":"right"' +
// ' "values":["min","max","avg","first","last","total"]' +
' },' +
' "thresholds":[{' +
' "color":"#eee",' +
' "val":"10.1"' +
' "color":"#ff0000",' +
' "val":"100"' +
' },{' +
' "color":"#aaa",' +
' "val":"base"' +
' "color":"#00ff00",' +
' "val":"250"' +
' }],' +
' "nullType":"zero"' +
'}',

View File

@@ -23,6 +23,7 @@ export function getChart (key) {
}
export function setChart (key, value) {
chartCache[`chart${key}`] && chartCache[`chart${key}`].dispose()
chartCache[`chart${key}`] = value
}
const hexagonCache = {}

View File

@@ -388,7 +388,7 @@ export const chartDataSource = [
export const chartType = {
line: 'line',
stackArea: 'stackArea',
area: 'area',
point: 'point',
bar: 'bar',
table: 'table',