From a91d866fce5e667467428a726e0a0601c982b8b5 Mon Sep 17 00:00:00 2001 From: zhangyu Date: Thu, 9 Nov 2023 17:49:27 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E6=B7=BB=E5=8A=A0=E6=9B=B2?= =?UTF-8?q?=E7=BA=BF=E5=B9=B3=E6=BB=91=20=E4=BB=A5=E5=8F=8A=E9=80=89?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=9C=86=E7=82=B9=E5=8A=A0=E7=B2=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chart/chart/uplot/chartTimeSeries.vue | 125 ++++++++++++++++-- .../chart/chart/uplot/chartTimeSeriesMixin.js | 23 +++- .../src/components/chart/chart/uplot/stack.js | 29 ++-- .../src/entrance/exportHtml/exportHtml.js | 1 + 4 files changed, 143 insertions(+), 35 deletions(-) diff --git a/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeries.vue b/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeries.vue index 1119cd84f..20efc68e7 100644 --- a/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeries.vue +++ b/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeries.vue @@ -162,7 +162,6 @@ export default { methods: { initChart (chartOptions = this.chartOption) { this.setDataLink() - console.log(this.chartInfo.param) try { this.isStack = !!this.chartInfo.param.stack } catch (e) {} @@ -177,9 +176,18 @@ export default { const cursLeft = -10 const cursTop = -10 const { seriesData, seriesAll } = this.initSeriesData(this.chartData) + this.isNoData = false + if (!seriesData.length || seriesData.length == 1) { + this.isNoData = true + this.$emit('chartIsNoData', this.isNoData) + } + if (this.isNoData) { + return + } // .compute(minusVal, null, -1, decimals) - const opts = { + let opts = { id: `chart-canvas-${this.chartId}-uplot`, + mode: 1, width: 100, height: 100, cursor: { @@ -188,6 +196,45 @@ export default { }, drag: { x: true + }, + dataIdx: (self, seriesIdx, hoveredIdx, cursorXVal) => { + const seriesData = self.data[seriesIdx] + const hoverProximityPx = (self.valToPos(self.data[0][1], 'x') - self.valToPos(self.data[0][0], 'x')) / 2 + if (seriesData[hoveredIdx] == null) { + let nonNullLft = null + let nonNullRgt = null + let i + + i = hoveredIdx + while (nonNullLft == null && i-- > 0) { + if (seriesData[i] != null) { nonNullLft = i } + } + + i = hoveredIdx + while (nonNullRgt == null && i++ < seriesData.length) { + if (seriesData[i] != null) { nonNullRgt = i } + } + + const xVals = self.data[0] + + const curPos = self.valToPos(cursorXVal, 'x') + const rgtPos = nonNullRgt == null ? Infinity : self.valToPos(xVals[nonNullRgt], 'x') + const lftPos = nonNullLft == null ? -Infinity : self.valToPos(xVals[nonNullLft], 'x') + + const lftDelta = curPos - lftPos + const rgtDelta = rgtPos - curPos + + if (lftDelta <= rgtDelta) { + if (lftDelta <= hoverProximityPx) { hoveredIdx = nonNullLft } + } else { + if (rgtDelta <= hoverProximityPx) { hoveredIdx = nonNullRgt } + } + } + return hoveredIdx + }, + points: { + size: 10, + width: 5 } }, plugins: [ @@ -202,18 +249,34 @@ export default { show: false }, series: [ - {} + { + scale: 'x' + } ], tzDate: ts => UPlot.tzDate(new Date(ts * 1e3), localStorage.getItem('nz-sys-timezone')), fmtDate: () => { return self.xAxisLabelFormatter(seriesData[0][0], seriesData[0][seriesData[0].length - 1]) }, axes: [ - {}, + { + scale: 'x', + gap: 5, + grid: { + show: true, + stroke: 'rgba(0, 10, 23, 0.09)', + width: 1 + } + }, { scale: 'left', values: (u, vals, space) => vals.map(v => leftUnitCompute.compute(v, null, -1, decimals)), incrs: incrs, + gap: 5, + grid: { + show: true, + stroke: 'rgba(0, 10, 23, 0.09)', + width: 1 + }, // space: (self) => { // console.log(self) // return 50 @@ -242,8 +305,13 @@ export default { { show: true, side: 1, - grid: { show: false }, + grid: { + show: false, + stroke: 'rgba(0, 10, 23, 0.09)', + width: 1 + }, scale: 'right', + gap: 5, // space: (self) => { // console.log(self) // return 50 @@ -252,17 +320,52 @@ export default { incrs: rightIncrs, ticks: { show: false // true + }, + size (self, values, axisIdx, cycleNum) { + const axis = self.axes[axisIdx] + + // bail out, force convergence + if (cycleNum > 1) { return axis._size } + + let axisSize = axis.ticks.size + axis.gap + + // find longest value + const longestVal = (values ?? []).reduce((acc, val) => ( + val.length > acc.length ? val : acc + ), '') + + if (longestVal != '') { + self.ctx.font = axis.font[0] + axisSize += self.ctx.measureText(longestVal).width / devicePixelRatio + } + + return Math.ceil(axisSize) } } ], scales: { + x: { + key: 'x', + auto: true, + ori: 0, + dir: 1, + time: true + }, left: { key: 'left', - time: false + time: false, + auto: true, + ori: 1, + dir: 1, + distr: 1 }, right: { key: 'right', - time: false + time: false, + auto: true, + ori: 1, + dir: 1, + distr: 1 } } } @@ -273,10 +376,12 @@ export default { scales: [null, null] } } - const data = this.seriesData = seriesData + let data = this.seriesData = seriesData opts.series.push(...seriesAll) if (this.isStack) { - getStackedOpts('', opts, data) + const obj = getStackedOpts('', opts, data) + opts = obj.opts + data = obj.data } this.renderMinMax(opts) if (getChart(this.chartId)) { @@ -294,7 +399,6 @@ export default { }, 100) }, renderMinMax (opts) { - console.log(opts) let leftMin = this.$lodash.get(this.chartInfo, 'param.min', undefined) let leftMax = this.$lodash.get(this.chartInfo, 'param.max', undefined) if (leftMin || leftMax) { @@ -454,7 +558,6 @@ export default { }, resize () { setTimeout(() => { - console.log('chartMixinnz') const dom = this.$refs['timeSeries-chart-box'] const width = dom.offsetWidth const legendHeight = this.$lodash.get(this.$refs, 'legend.$el.offsetHeight', 40) diff --git a/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeriesMixin.js b/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeriesMixin.js index bd7dc6ad5..5406919b2 100644 --- a/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeriesMixin.js +++ b/nezha-fronted/src/components/chart/chart/uplot/chartTimeSeriesMixin.js @@ -6,6 +6,7 @@ import { formatScientificNotation, getMetricTypeValue } from '@/components/commo import bus from '@/libs/bus' import moment from 'moment-timezone' import { getChart } from '@/components/common/js/common' +import uplot from 'uplot' export default { data () { return { @@ -68,6 +69,9 @@ export default { item.statistics = statistics item.values.forEach(value => { let itemValue = value[1] + if (!isNaN(itemValue)) { + itemValue = Number(itemValue) + } if (itemValue === null && nullValueMode !== 'null') { if (nullValueMode === 'zero') { itemValue = 0 @@ -103,24 +107,28 @@ export default { this.seriesColor.push(randomcolor()) } const legend = this.handleLegend(this.chartInfo, series, expressionIndex, dataIndex, chartIndex) - let isRight = false + let isRight if (this.chartInfo.param.enable.rightYAxis) { const elementNames = this.$lodash.get(this.chartInfo, 'param.rightYAxis.elementNames', []) - isRight = elementNames.indexOf(series.elements.name) !== -1 + isRight = elementNames.indexOf(series.elements.name) !== -1 ? 1 : 0 } const name = legend.name const alias = legend.alias const statistics = series.statistics const chartType = this.chartInfo.type + const obj = { name: name, label: alias, scale: isRight ? 'right' : 'left', // right - yAxisIndex: isRight ? 1 : 0, // right - values: (u, v) => series.elements.name + JSON.stringify(series.metric), + yAxisIndex: isRight, // right + // values: (u, v) => series.elements.name + JSON.stringify(series.metric), stroke: this.seriesColor[chartIndex], width: 1 / devicePixelRatio, - expressionIndex: series.expressionIndex + expressionIndex: series.expressionIndex, + paths (taht, seriesIdx, idx0, idx1, filtIdxs) { + return uplot.paths.spline(taht)(taht, seriesIdx, idx0, idx1, filtIdxs) + } } if (chartType === 'area') { obj.fill = this.seriesColor[chartIndex] + '20' // 面积图使用 @@ -128,7 +136,10 @@ export default { if (chartType === 'point') { obj.points = { show: true, - width: 2 + size: 6, + width: 3, + fill: this.seriesColor[chartIndex], + stroke: this.seriesColor[chartIndex] } obj.width = 0 } diff --git a/nezha-fronted/src/components/chart/chart/uplot/stack.js b/nezha-fronted/src/components/chart/chart/uplot/stack.js index 19bbb7a49..7f97d1da9 100644 --- a/nezha-fronted/src/components/chart/chart/uplot/stack.js +++ b/nezha-fronted/src/components/chart/chart/uplot/stack.js @@ -7,10 +7,18 @@ function stack (data, omit) { for (let i = 0; i < d0Len; i++) { accum[i] = 0 } - for (let i = 1; i < data.length; i++) { data2.push(omit(i) ? data[i] : data[i].map((v, i) => (accum[i] += +v))) } + for (let i = 1; i < data.length; i++) { + data2.push(data[i].map((v, i) => { + if (typeof (v) === 'undefined') { + return accum[i] + } + return (accum[i] += +v) + })) + } for (let i = 1; i < data.length; i++) { !omit(i) && bands.push({ + dir: -1, series: [ data.findIndex((s, j) => j > i && !omit(j)), i @@ -19,7 +27,8 @@ function stack (data, omit) { } bands = bands.filter(b => b.series[1] > -1) - + bands = bands.filter(b => b.series[0] > -1) + bands = bands.reverse() return { data: [data[0]].concat(data2), bands @@ -34,25 +43,9 @@ export default function getStackedOpts (title, opt, data, interp) { opts.bands = stacked.bands opts.cursor = opts.cursor || {} - opts.cursor.dataIdx = (u, seriesIdx, closestIdx, xValue) => { - return data[seriesIdx][closestIdx] == null ? null : closestIdx - } opts.series.forEach(s => { s.value = (u, v, si, i) => data[si][i] - - s.points = s.points || {} - - // scan raw unstacked data to return only real points - s.points.filter = (u, seriesIdx, show, gaps) => { - if (show) { - const pts = [] - data[seriesIdx].forEach((v, i) => { - v != null && pts.push(i) - }) - return pts - } - } }) // restack on toggle opts.hooks = { diff --git a/nezha-fronted/src/entrance/exportHtml/exportHtml.js b/nezha-fronted/src/entrance/exportHtml/exportHtml.js index ae6fe9615..050ff2881 100644 --- a/nezha-fronted/src/entrance/exportHtml/exportHtml.js +++ b/nezha-fronted/src/entrance/exportHtml/exportHtml.js @@ -5,6 +5,7 @@ import '@/assets/css/main.scss' import '@/assets/css/font/iconfont.js' import ElementUI from 'element-ui' import i18n from '@/components/common/i18n' +import 'uplot/dist/uPlot.min.css' import Vue from 'vue' import Vuex from 'vuex'