NEZ-1254 feat: chart架构、时序图定义
This commit is contained in:
@@ -74,6 +74,7 @@
|
|||||||
"generate-asset-webpack-plugin": "^0.3.0",
|
"generate-asset-webpack-plugin": "^0.3.0",
|
||||||
"git-revision-webpack-plugin": "^3.0.6",
|
"git-revision-webpack-plugin": "^3.0.6",
|
||||||
"html-webpack-plugin": "^2.30.1",
|
"html-webpack-plugin": "^2.30.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"node-notifier": "^5.1.2",
|
"node-notifier": "^5.1.2",
|
||||||
"optimize-css-assets-webpack-plugin": "^3.2.0",
|
"optimize-css-assets-webpack-plugin": "^3.2.0",
|
||||||
"ora": "^1.2.0",
|
"ora": "^1.2.0",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
.panel-chart {
|
.panel-chart {
|
||||||
border: 1px solid $--chart-box-border-color;
|
border: 1px solid $--chart-box-border-color;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.chart-header {
|
.chart-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -68,3 +70,102 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.nz-chart {
|
||||||
|
height: calc(100% - 39px);
|
||||||
|
|
||||||
|
.nz-chart__component {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.chart__canvas {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
&.nz-chart__component--bottom {
|
||||||
|
flex-direction: column;
|
||||||
|
.legend-container {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 80px;
|
||||||
|
min-height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.nz-chart__component--right {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
&.nz-chart__component--left {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
&.nz-chart__component--right, &.nz-chart__component--left {
|
||||||
|
.legend-container {
|
||||||
|
flex-direction: column;
|
||||||
|
width: unset;
|
||||||
|
max-width: 50%;
|
||||||
|
max-height: unset;
|
||||||
|
min-height: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.legend-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 18px;
|
||||||
|
padding: 0 10px 3px 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.legend-item, .legend--table-row {
|
||||||
|
&.legend-item--inactive, &.row--inactive {
|
||||||
|
color: $--color-text-secondary;
|
||||||
|
|
||||||
|
.legend-shape {
|
||||||
|
background-color: $--background-color-1 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格类型
|
||||||
|
.legend--table {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.legend--table-row {
|
||||||
|
display: table-row;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.legend--table-row:not(:first-of-type):hover {
|
||||||
|
background-color: $--background-color-1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.legend--table-row:first-of-type {
|
||||||
|
color:#33a2e5;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 12px;
|
||||||
|
.legend--table-cell:not(:first-of-type) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.legend--table-cell:first-of-type {
|
||||||
|
max-width: 600px;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
.legend--table-cell {
|
||||||
|
display: table-cell;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-break: break-all;
|
||||||
|
padding: 1px 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
.ft-gr{
|
.ft-gr{
|
||||||
color:lightgray;
|
color:lightgray;
|
||||||
}
|
}
|
||||||
.legend-container{
|
/*.legend-container{
|
||||||
width: calc(100% - 15px);
|
width: calc(100% - 15px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height:80px;
|
max-height:80px;
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
.legend-container table tr:hover{
|
.legend-container table tr:hover{
|
||||||
background-color: $--background-color-1;
|
background-color: $--background-color-1;
|
||||||
}
|
}*/
|
||||||
.nz-icon-warning{
|
.nz-icon-warning{
|
||||||
color: #e6a23c;
|
color: #e6a23c;
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
max-height: 80px;
|
max-height: 80px;
|
||||||
min-height:25px;
|
min-height:25px;
|
||||||
}
|
}
|
||||||
.legend-item{
|
/*.legend-item{
|
||||||
text-overflow:ellipsis;
|
text-overflow:ellipsis;
|
||||||
white-space:nowrap;
|
white-space:nowrap;
|
||||||
margin-right:10px;
|
margin-right:10px;
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
display:inline-block;
|
display:inline-block;
|
||||||
float:left;
|
float:left;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}*/
|
||||||
.nz-chart-dropdown {
|
.nz-chart-dropdown {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 30px;
|
top: 30px;
|
||||||
|
|||||||
@@ -775,7 +775,7 @@ export default {
|
|||||||
if (!params.panelId) {
|
if (!params.panelId) {
|
||||||
this.finshGetData = false
|
this.finshGetData = false
|
||||||
return
|
return
|
||||||
} // 没有panelId不调用接口
|
}
|
||||||
this.$get('visual/panel/chart?panelId=' + params.panelId + '&groupId=0' + '&pageSize=-1').then(response => {
|
this.$get('visual/panel/chart?panelId=' + params.panelId + '&groupId=0' + '&pageSize=-1').then(response => {
|
||||||
response = chartData
|
response = chartData
|
||||||
console.log(chartData)
|
console.log(chartData)
|
||||||
|
|||||||
@@ -1,10 +1,85 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="nz-chart">
|
||||||
|
<chart-no-data v-if="isNoData"></chart-no-data>
|
||||||
|
<template v-else>
|
||||||
|
<chart-time-series
|
||||||
|
v-if="isTimeSeries(chartInfo.type)"
|
||||||
|
:chart-data="chartData"
|
||||||
|
:chart-info="chartInfo"
|
||||||
|
:chart-option="chartOption"
|
||||||
|
></chart-time-series>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import chartAssetInfo from './chart/chartAssetInfo'
|
||||||
|
import chartBar from './chart/chartBar'
|
||||||
|
import chartClock from './chart/chartClock'
|
||||||
|
import chartDiagram from './chart/chartDiagram'
|
||||||
|
import chartEndpointInfo from './chart/chartEndpointInfo'
|
||||||
|
import chartGauge from './chart/chartGauge'
|
||||||
|
import chartGroup from './chart/chartGroup'
|
||||||
|
import chartLog from './chart/chartLog'
|
||||||
|
import chartNoData from './chart/chartNoData'
|
||||||
|
import chartPie from './chart/chartPie'
|
||||||
|
import chartStat from './chart/chartStat'
|
||||||
|
import chartTable from './chart/chartTable'
|
||||||
|
import chartText from './chart/chartText'
|
||||||
|
import chartTimeSeries from './chart/chartTimeSeries'
|
||||||
|
import chartTreemap from './chart/chartTreemap'
|
||||||
|
import chartUrl from './chart/chartUrl'
|
||||||
|
import chartValue from './chart/chartValue'
|
||||||
|
import { getOption, isTimeSeries } from './chart/tools'
|
||||||
|
import lodash from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'chart'
|
name: 'chart',
|
||||||
|
components: {
|
||||||
|
chartAssetInfo,
|
||||||
|
chartBar,
|
||||||
|
chartClock,
|
||||||
|
chartDiagram,
|
||||||
|
chartEndpointInfo,
|
||||||
|
chartGauge,
|
||||||
|
chartGroup,
|
||||||
|
chartLog,
|
||||||
|
chartNoData,
|
||||||
|
chartPie,
|
||||||
|
chartStat,
|
||||||
|
chartTable,
|
||||||
|
chartText,
|
||||||
|
chartTimeSeries,
|
||||||
|
chartTreemap,
|
||||||
|
chartUrl,
|
||||||
|
chartValue
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
chartInfo: Object,
|
||||||
|
chartData: [Object, Array, String], // 数据查询后传入chart组件,chart组件内不查询,只根据接传递的数据来渲染
|
||||||
|
customChartOption: Object // 需要自定义echarts的option时传入,非必须;传入该值时仍需传对应格式的chartData
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNoData () {
|
||||||
|
return !this.chartData || this.chartData.length === 0
|
||||||
|
},
|
||||||
|
chartOption () {
|
||||||
|
if (this.customChartOption) {
|
||||||
|
return lodash.cloneDeep(this.customChartOption)
|
||||||
|
} else {
|
||||||
|
return getOption(this.chartInfo.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isTimeSeries
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
13
nezha-fronted/src/components/chart/chart/chartNoData.vue
Normal file
13
nezha-fronted/src/components/chart/chart/chartNoData.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="nz-chart__no-data"
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'chartNoData'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,13 +1,209 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div :class="legendPlacement" class="nz-chart__component nz-chart__component--time-series">
|
||||||
|
<div :id="`chart-canvas-${chartInfo.id}`" class="chart__canvas"></div>
|
||||||
|
<chart-legend
|
||||||
|
v-if="hasLegend"
|
||||||
|
:chart-data="chartData"
|
||||||
|
:chart-info="chartInfo"
|
||||||
|
:legends="legends"
|
||||||
|
></chart-legend>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
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 { initColor } from '@/components/chart/chart/tools'
|
||||||
|
|
||||||
|
let myChart = null
|
||||||
export default {
|
export default {
|
||||||
name: 'chart-time-series' // x轴是时间的图,包括折线、柱状、堆叠、散点
|
name: 'chart-time-series', // x轴是时间的图,包括折线、柱状、堆叠、散点
|
||||||
|
components: {
|
||||||
|
chartLegend: legend
|
||||||
|
},
|
||||||
|
mixins: [chartMixin],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
stackTotalColor: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasLegend () {
|
||||||
|
try {
|
||||||
|
return [chartLegendPlacement.bottom, chartLegendPlacement.left, chartLegendPlacement.right].indexOf(this.chartInfo.param.legend.placement) > -1
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legendPlacement () {
|
||||||
|
try {
|
||||||
|
switch (this.chartInfo.param.legend.placement) {
|
||||||
|
case 'left':
|
||||||
|
case 'right':
|
||||||
|
case 'bottom': {
|
||||||
|
return `nz-chart__component--${this.chartInfo.param.legend.placement}`
|
||||||
|
}
|
||||||
|
default: return ''
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initChart (chartOption) {
|
||||||
|
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)
|
||||||
|
chartOption.tooltip.formatter = this.tooltipFormatter()
|
||||||
|
chartOption.yAxis.axisLabel.formatter = this.yAxisLabelFormatter(minValue, maxValue)
|
||||||
|
setTimeout(() => {
|
||||||
|
myChart = echarts.init(document.getElementById(`chart-canvas-${this.chartInfo.id}`))
|
||||||
|
setChart(this.chartInfo.id, myChart) // 缓存;不使用vue的data是为避免整个chart被监听导致卡顿
|
||||||
|
myChart.setOption(chartOption)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getMinMaxFromData (originalData) {
|
||||||
|
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) => {
|
||||||
|
return a[1] - b[1]
|
||||||
|
})
|
||||||
|
minValue = sorted[0][1]
|
||||||
|
maxValue = sorted[sorted.length - 1][1]
|
||||||
|
}
|
||||||
|
return { minTime, maxTime, minValue, maxValue }
|
||||||
|
},
|
||||||
|
xAxisLabelFormatter (minTime, maxTime) {
|
||||||
|
return function (value, index) {
|
||||||
|
let offset = localStorage.getItem('nz-sys-timezone')
|
||||||
|
offset = moment.tz(offset).format('Z')
|
||||||
|
offset = Number.parseInt(offset)
|
||||||
|
const localOffset = new Date().getTimezoneOffset() * 60 * 1000 * -1 // 默认 一分钟显示时区偏移的结果
|
||||||
|
const tData = new Date(value - localOffset + offset * 60 * 60 * 1000)
|
||||||
|
let hour = tData.getHours()
|
||||||
|
hour = hour > 9 ? hour : '0' + hour // 加0补充为两位数字
|
||||||
|
let minute = tData.getMinutes()
|
||||||
|
minute = minute > 9 ? minute : '0' + minute // 如果分钟小于10,则在前面加0补充为两位数字
|
||||||
|
if (minTime !== null && maxTime !== null) {
|
||||||
|
const diffSec = (maxTime - minTime) / 1000
|
||||||
|
const secOneDay = 24 * 60 * 60// 1天的秒数
|
||||||
|
const secOneMonth = secOneDay * 30// 30天的秒数
|
||||||
|
if (diffSec <= secOneDay) { // 同一天
|
||||||
|
return [hour, minute].join(':')
|
||||||
|
} else if (diffSec < secOneMonth) { // 大于1天,小于30天
|
||||||
|
return [tData.getMonth() + 1, tData.getDate()].join('/') + ' ' + [hour, minute].join(':')
|
||||||
|
} else { // 大于等于30天
|
||||||
|
return [tData.getMonth() + 1, tData.getDate()].join('/')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [tData.getFullYear(), tData.getMonth() + 1, tData.getDate()].join('/') + '\n' +
|
||||||
|
[hour, minute].join(':')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltipFormatter () {
|
||||||
|
const self = this
|
||||||
|
return function (params) {
|
||||||
|
let str = '<div>'
|
||||||
|
let sum = 0
|
||||||
|
params.forEach((item, i) => {
|
||||||
|
const color = self.colorList[item.seriesIndex]
|
||||||
|
const previousItem = params.find((series) => ('Previous ' + item.seriesName) === series.seriesName)
|
||||||
|
let paramsDot = bus.countDecimals(item.data[1])
|
||||||
|
if (paramsDot < self.chartDot) {
|
||||||
|
paramsDot = self.chartDot
|
||||||
|
} else if (paramsDot > 6) {
|
||||||
|
paramsDot = 6
|
||||||
|
}
|
||||||
|
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>'
|
||||||
|
}
|
||||||
|
str += '</div>'
|
||||||
|
str += '</div>'
|
||||||
|
})
|
||||||
|
if (self.chartInfo.type === chartType.stackArea) {
|
||||||
|
if (!self.stackTotalColor || 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>'
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxisLabelFormatter (minValue, maxValue) {
|
||||||
|
const self = this
|
||||||
|
return function (val, index) {
|
||||||
|
const value = formatScientificNotation(val, 2)
|
||||||
|
let chartUnit = self.chartInfo.unit
|
||||||
|
chartUnit = chartUnit || 2
|
||||||
|
const unit = chartDataFormat.getUnit(chartUnit)
|
||||||
|
// TODO 弄清楚dot逻辑
|
||||||
|
/* if (chartDataFormat.Interval(maxValue, copies, unit.type, 'min') < 1 && dot < 2) {
|
||||||
|
dot = 2
|
||||||
|
}
|
||||||
|
if (dot == 0) {
|
||||||
|
dot = 1
|
||||||
|
}
|
||||||
|
dot = bus.countDecimals(value)
|
||||||
|
if (dot < self.chartDot) {
|
||||||
|
dot = self.chartDot
|
||||||
|
} */
|
||||||
|
return unit.compute(value, index, -1, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.chartOption.color || (this.chartOption.color = initColor(20))
|
||||||
|
this.colorList = this.chartOption.color
|
||||||
|
this.initChart(this.chartOption)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
114
nezha-fronted/src/components/chart/chart/legend.vue
Normal file
114
nezha-fronted/src/components/chart/chart/legend.vue
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="legendArea" class='legend-container'>
|
||||||
|
<!-- 带统计的是table形式 -->
|
||||||
|
<template v-if="isStatistics">
|
||||||
|
<div class="legend--table">
|
||||||
|
<div class="legend--table-row table-header">
|
||||||
|
<div class="legend--table-cell"></div>
|
||||||
|
<div v-for="statistics in chartInfo.param.legend.values" :key="statistics" class="legend--table-cell">{{statistics}}</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="(item, index) in legends"
|
||||||
|
:key="index"
|
||||||
|
:class="{'row--inactive': isGrey[index]}"
|
||||||
|
class="legend--table-row"
|
||||||
|
@click="clickLegend(item.name, index)"
|
||||||
|
>
|
||||||
|
<div :title="item.alias ? item.alias : item.name" class="legend--table-cell">
|
||||||
|
<span :style="{background: item.color}" class="legend-shape"></span>{{item.alias ? item.alias : item.name}}
|
||||||
|
</div>
|
||||||
|
<div v-for="(statistics, index) in item.statistics" :key="index" :class="{'legend-item--inactive': isGrey[index]}" class="legend--table-cell">{{statistics.value}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 否则是普通形式 -->
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in legends"
|
||||||
|
:key="index"
|
||||||
|
:class="{'legend-item--inactive': isGrey[index]}"
|
||||||
|
:title="item.alias ? item.alias : item.name"
|
||||||
|
class="legend-item"
|
||||||
|
@click="clickLegend(item.name, index)"
|
||||||
|
>
|
||||||
|
<span :style="{background: item.color}" class="legend-shape"></span>{{item.alias ? item.alias : item.name}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import lodash from 'lodash'
|
||||||
|
import { getChart } from '@/components/common/js/common'
|
||||||
|
export default {
|
||||||
|
name: 'chartLegend',
|
||||||
|
props: {
|
||||||
|
chartInfo: Object,
|
||||||
|
chartData: Array,
|
||||||
|
legends: Array
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isGrey: [],
|
||||||
|
legendDefaultCount: 20, // 初始显示的legend条数
|
||||||
|
showLegends: [] // 要显示的legend;若legend数量过多,初始时这个数据里只有前20条legend
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isStatistics () {
|
||||||
|
return !lodash.isEmpty(this.chartInfo.param.legend.values)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickLegend (legendName, index) {
|
||||||
|
/* 点击legend
|
||||||
|
* 1.当前如果是全高亮状态,则全部置灰,只留被点击的legend高亮
|
||||||
|
* 2.如果点击的是唯一高亮的legend,则变为全高亮状态
|
||||||
|
* 3.否则只改变被点击的legend状态
|
||||||
|
* */
|
||||||
|
let highlightNum = 0 // 高亮数量
|
||||||
|
this.isGrey.forEach(g => {
|
||||||
|
if (!g) {
|
||||||
|
highlightNum++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const hasGrey = highlightNum < this.isGrey.length // 是否有置灰的
|
||||||
|
const curIsGrey = this.isGrey[index] // 当前legend的状态
|
||||||
|
const currentIsTheOnlyOneHighlight = !curIsGrey && highlightNum === 1 // 当前legend是否是目前唯一高亮的
|
||||||
|
const echarts = getChart(this.chartInfo.id)
|
||||||
|
if (echarts) {
|
||||||
|
if (!hasGrey) { // 1.除当前legend外全置灰
|
||||||
|
echarts.dispatchAction({
|
||||||
|
type: 'legendInverseSelect'
|
||||||
|
})
|
||||||
|
echarts.dispatchAction({
|
||||||
|
type: 'legendSelect',
|
||||||
|
name: legendName
|
||||||
|
})
|
||||||
|
this.isGrey = this.isGrey.map((g, i) => i !== index)
|
||||||
|
} else if (currentIsTheOnlyOneHighlight) { // 2.全高亮
|
||||||
|
echarts.dispatchAction({
|
||||||
|
type: 'legendAllSelect'
|
||||||
|
})
|
||||||
|
this.isGrey = this.isGrey.map(() => false)
|
||||||
|
} else {
|
||||||
|
const type = curIsGrey ? 'legendSelect' : 'legendUnSelect'
|
||||||
|
echarts.dispatchAction({
|
||||||
|
type: type,
|
||||||
|
name: legendName
|
||||||
|
})
|
||||||
|
this.$set(this.isGrey, index, !this.isGrey[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
legends (n) {
|
||||||
|
this.isGrey = n.map(() => false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,97 @@
|
|||||||
|
import { initColor } from '@/components/chart/chart/tools'
|
||||||
export const chartTimeSeriesLineOption = {
|
export const chartTimeSeriesLineOption = {
|
||||||
|
title: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
confine: false,
|
||||||
|
extraCssText: 'z-index:1000;'
|
||||||
|
// formatter: 动态生成
|
||||||
|
},
|
||||||
|
color: initColor(),
|
||||||
|
grid: {
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
top: 20,
|
||||||
|
bottom: 10,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'time',
|
||||||
|
animation: false,
|
||||||
|
showAllSymbol: false,
|
||||||
|
axisLabel: {
|
||||||
|
interval: '0',
|
||||||
|
showMaxLabel: false,
|
||||||
|
rotate: 0,
|
||||||
|
show: true,
|
||||||
|
fontSize: 10
|
||||||
|
// formatter: 动态生成
|
||||||
|
},
|
||||||
|
axisPointer: { // y轴上显示指针对应的值
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#d9d9d9',
|
||||||
|
opacity: 0.8,
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
splitLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#d9d9d9',
|
||||||
|
opacity: 0.8,
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 去掉y轴
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 10
|
||||||
|
// formatter: 动态生成
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: '',
|
||||||
|
type: 'line',
|
||||||
|
symbol: 'emptyCircle', // 去掉点
|
||||||
|
symbolSize: [2, 2],
|
||||||
|
smooth: 0.2, // 曲线变平滑
|
||||||
|
showSymbol: false,
|
||||||
|
data: [],
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
opacity: 0.9
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
useUTC: false // 使用本地时间
|
||||||
}
|
}
|
||||||
export const chartTimeSeriesBarOption = {
|
export const chartTimeSeriesAreaOption = {
|
||||||
|
|
||||||
}
|
}
|
||||||
export const chartTimeSeriesScatterOption = {
|
export const chartTimeSeriesScatterOption = {
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import chartBarOption from './chartBar'
|
|
||||||
import chartGaugeOption from './chartGauge'
|
|
||||||
import chartPieOption from './chartPie'
|
|
||||||
import { chartTimeSeriesLineOption, chartTimeSeriesBarOption, chartTimeSeriesScatterOption } from './chartTimeSeries'
|
|
||||||
import chartTreemapOption from './chartTreemap'
|
|
||||||
|
|
||||||
export default { chartBarOption, chartGaugeOption, chartPieOption, chartTimeSeriesLineOption, chartTimeSeriesBarOption, chartTimeSeriesScatterOption, chartTreemapOption }
|
|
||||||
66
nezha-fronted/src/components/chart/chart/tools.js
Normal file
66
nezha-fronted/src/components/chart/chart/tools.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { chartType } from '@/components/common/js/constants'
|
||||||
|
import chartBarOption from './options/chartBar'
|
||||||
|
import chartGaugeOption from './options/chartGauge'
|
||||||
|
import chartPieOption from './options/chartPie'
|
||||||
|
import lodash from 'lodash'
|
||||||
|
import {
|
||||||
|
chartTimeSeriesLineOption,
|
||||||
|
chartTimeSeriesScatterOption,
|
||||||
|
chartTimeSeriesAreaOption
|
||||||
|
} from './options/chartTimeSeries'
|
||||||
|
import chartTreemapOption from './options/chartTreemap'
|
||||||
|
import {randomcolor} from "@/components/common/js/radomcolor/randomcolor";
|
||||||
|
|
||||||
|
export function getOption (type) {
|
||||||
|
let chartOption = null
|
||||||
|
switch (type) {
|
||||||
|
case chartType.stackArea: {
|
||||||
|
chartOption = lodash.cloneDeep(chartTimeSeriesAreaOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case chartType.point: {
|
||||||
|
chartOption = lodash.cloneDeep(chartTimeSeriesScatterOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case chartType.line: {
|
||||||
|
chartOption = lodash.cloneDeep(chartTimeSeriesLineOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case chartType.bar: {
|
||||||
|
chartOption = lodash.cloneDeep(chartBarOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case chartType.gauge: {
|
||||||
|
chartOption = lodash.cloneDeep(chartGaugeOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case chartType.pie: {
|
||||||
|
chartOption = lodash.cloneDeep(chartPieOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case chartType.treemap: {
|
||||||
|
chartOption = lodash.cloneDeep(chartTreemapOption)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
return chartOption
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTimeSeries (type) {
|
||||||
|
return type === chartType.line || type === chartType.stackArea || type === chartType.point
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initColor (colorNum = 20) {
|
||||||
|
const colorList = [
|
||||||
|
'#FF5200', '#3685FF', '#FF8D00', '#00DCA2',
|
||||||
|
'#954Eff', '#FFCB01', '#f65A96', '#00BFD0',
|
||||||
|
'#FF8BEA', '#4D7693', '#72577C', '#99D750',
|
||||||
|
'#DD8270', '#C475EE', '#7E83FB', '#7EB090',
|
||||||
|
'#FF9094', '#00CCF5', '#CF6684', '#4E55FF'
|
||||||
|
]
|
||||||
|
for (let i = 0; i < colorNum - 20; i++) {
|
||||||
|
colorList.push(randomcolor())
|
||||||
|
}
|
||||||
|
return colorList
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
:vertical-compact="true"
|
:vertical-compact="true"
|
||||||
>
|
>
|
||||||
<grid-item
|
<grid-item
|
||||||
v-for="(item, index) in copyDataList"
|
v-for="item in copyDataList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:h="item.h"
|
:h="item.h"
|
||||||
:i="item.i"
|
:i="item.i"
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
<panel-chart
|
<panel-chart
|
||||||
:ref="'chart' + item.id"
|
:ref="'chart' + item.id"
|
||||||
:chart-info="item"
|
:chart-info="item"
|
||||||
|
:time-range="timeRange"
|
||||||
></panel-chart>
|
></panel-chart>
|
||||||
</grid-item>
|
</grid-item>
|
||||||
</grid-layout>
|
</grid-layout>
|
||||||
@@ -43,10 +44,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import VueGridLayout from 'vue-grid-layout'
|
import VueGridLayout from 'vue-grid-layout'
|
||||||
import { fromRoute } from '@/components/common/js/constants'
|
import { fromRoute } from '@/components/common/js/constants'
|
||||||
import bus from '@/libs/bus'
|
|
||||||
import chartTempData from '@/components/charts/chartTempData'
|
|
||||||
import axios from 'axios'
|
|
||||||
import chartDataFormat from '@/components/charts/chartDataFormat'
|
|
||||||
import panelChart from '@/components/chart/panelChart'
|
import panelChart from '@/components/chart/panelChart'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -55,6 +52,7 @@ export default {
|
|||||||
// TODO isModel
|
// TODO isModel
|
||||||
from: { type: String },
|
from: { type: String },
|
||||||
obj: Object,
|
obj: Object,
|
||||||
|
timeRange: Array, // 时间范围
|
||||||
panelLock: { type: Boolean, default: true },
|
panelLock: { type: Boolean, default: true },
|
||||||
dataList: Array // 看板中所有图表信息
|
dataList: Array // 看板中所有图表信息
|
||||||
},
|
},
|
||||||
@@ -104,9 +102,16 @@ export default {
|
|||||||
handler (n, o) {
|
handler (n, o) {
|
||||||
this.noData = !n || n.length < 1
|
this.noData = !n || n.length < 1
|
||||||
this.copyDataList = n.map(item => {
|
this.copyDataList = n.map(item => {
|
||||||
|
let param = {}
|
||||||
|
try {
|
||||||
|
param = JSON.parse(item.param)
|
||||||
|
} catch (e) {
|
||||||
|
console.info(e)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
i: item.id
|
i: item.id,
|
||||||
|
param
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
92
nezha-fronted/src/components/chart/chartMixin.js
Normal file
92
nezha-fronted/src/components/chart/chartMixin.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import lodash from 'lodash'
|
||||||
|
import { getMetricTypeValue } from '@/components/common/js/tools'
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
colorList: [],
|
||||||
|
chartDot: 2,
|
||||||
|
legends: [] // { name, alias, color, statistics: [{type: min, value: xxx}, ...] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
chartInfo: Object,
|
||||||
|
chartData: Array,
|
||||||
|
chartOption: Object
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleTimeSeries (chartInfo, seriesTemplate, originalDatas) {
|
||||||
|
const series = []
|
||||||
|
let colorIndex = 0
|
||||||
|
originalDatas.forEach((originalData, expressionIndex) => {
|
||||||
|
originalData.forEach((data, dataIndex) => {
|
||||||
|
const s = lodash.cloneDeep(seriesTemplate)
|
||||||
|
s.data = data.values
|
||||||
|
s.name = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
|
||||||
|
if (chartInfo.param.stack) { // 堆叠
|
||||||
|
s.stack = 'Total'
|
||||||
|
s.areaStyle = {}
|
||||||
|
}
|
||||||
|
series.push(s)
|
||||||
|
colorIndex++
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return series
|
||||||
|
},
|
||||||
|
// 单个legend
|
||||||
|
handleLegend (chartInfo, data, expressionIndex, dataIndex, colorIndex) {
|
||||||
|
let legend = '' // up
|
||||||
|
if (data.metric.__name__) {
|
||||||
|
legend += `${data.metric.__name__}{`
|
||||||
|
}
|
||||||
|
const tagKeysArr = Object.keys(data.metric)
|
||||||
|
tagKeysArr.forEach(tagKey => {
|
||||||
|
if (tagKey !== '__name__') {
|
||||||
|
legend += `${tagKey}="${data.metric[tagKey]}",`
|
||||||
|
}
|
||||||
|
if (legend.endsWith(',')) {
|
||||||
|
legend = legend.substr(0, legend.length - 1)
|
||||||
|
}
|
||||||
|
if (data.metric.__name__) {
|
||||||
|
legend += '}'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!legend) {
|
||||||
|
legend = chartInfo.elements[expressionIndex].expression
|
||||||
|
}
|
||||||
|
// 处理legend别名
|
||||||
|
let alias = this.handleLegendAlias(legend, chartInfo.elements[expressionIndex].legend)
|
||||||
|
if (!alias) {
|
||||||
|
alias = legend
|
||||||
|
}
|
||||||
|
const name = legend + '-' + chartInfo.elements[expressionIndex].id + '-' + dataIndex
|
||||||
|
|
||||||
|
// 若需要统计,处理统计数据
|
||||||
|
const statisticsTypes = chartInfo.param.legend.values
|
||||||
|
let statistics = []
|
||||||
|
if (!lodash.isEmpty(statisticsTypes)) {
|
||||||
|
statistics = statisticsTypes.map(type => {
|
||||||
|
return { type, value: getMetricTypeValue(data.values, type) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.legends.push({ name, alias, statistics, color: this.colorList[colorIndex] })
|
||||||
|
return name
|
||||||
|
},
|
||||||
|
handleLegendAlias (legend, aliasExpression) {
|
||||||
|
if (/\{\{.+\}\}/.test(aliasExpression)) {
|
||||||
|
const labelValue = aliasExpression.replace(/(\{\{.+?\}\})/g, function (i) {
|
||||||
|
const label = i.substr(i.indexOf('{{') + 2, i.indexOf('}}') - i.indexOf('{{') - 2)
|
||||||
|
const reg = new RegExp(label + '=".+?"')
|
||||||
|
let value = null
|
||||||
|
if (reg.test(legend)) {
|
||||||
|
const find = legend.match(reg)[0]
|
||||||
|
value = find.substr(find.indexOf('"') + 1, find.lastIndexOf('"') - find.indexOf('"') - 1)
|
||||||
|
}
|
||||||
|
return value || label
|
||||||
|
})
|
||||||
|
return labelValue
|
||||||
|
} else {
|
||||||
|
return aliasExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,19 +6,23 @@
|
|||||||
:chart-info="chartInfo"
|
:chart-info="chartInfo"
|
||||||
></chart-header>
|
></chart-header>
|
||||||
<!-- chart -->
|
<!-- chart -->
|
||||||
<div class="chart-container">
|
<!-- 数据查询后传入chart组件,chart组件内不查询,只根据接传递的数据来渲染 -->
|
||||||
<!-- 数据获取后传入chart组件,chart组件内不发送查询请求,只根据接传递的数据来渲染 -->
|
<chart
|
||||||
<chart
|
:chart-data="chartData"
|
||||||
:chart-data="chartData"
|
:chart-info="chartInfo"
|
||||||
:chart-info="chartInfo"
|
></chart>
|
||||||
></chart>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import chartHeader from '@/components/chart/chartHeader'
|
import chartHeader from '@/components/chart/chartHeader'
|
||||||
import chart from '@/components/chart/chart'
|
import chart from '@/components/chart/chart'
|
||||||
|
import { isTimeSeries } from './chart/tools'
|
||||||
|
import { chartType, fromRoute } from '@/components/common/js/constants'
|
||||||
|
import bus from '@/libs/bus'
|
||||||
|
import axios from 'axios'
|
||||||
|
import chartTempData from '@/components/charts/chartTempData'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'panelChart',
|
name: 'panelChart',
|
||||||
components: {
|
components: {
|
||||||
@@ -26,16 +30,83 @@ export default {
|
|||||||
chart
|
chart
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
chartInfo: Object
|
chartInfo: Object, // 其中的param json串已转化为对象
|
||||||
|
timeRange: Array // 时间范围
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
chartData: null
|
chartData: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getChartData () {
|
getChartData (isRefresh) {
|
||||||
|
// TODO assetInfo、endpointInfo、echarts等进行不同的处理
|
||||||
|
let startTime = ''
|
||||||
|
let endTime = ''
|
||||||
|
if (isRefresh) { // 刷新则视情况更新时间范围
|
||||||
|
const now = new Date(bus.computeTimezone(new Date().getTime()))
|
||||||
|
const origin = new Date(this.timeRange[1])
|
||||||
|
const numInterval = now.getTime() - origin.getTime()
|
||||||
|
if (numInterval >= 60000) { // 大于1分钟,则start、end均往后移numInterval,否则时间不变
|
||||||
|
startTime = this.getNewTime(this.timeRange[0], numInterval)
|
||||||
|
endTime = bus.timeFormate(now, 'yyyy-MM-dd hh:mm:ss')
|
||||||
|
} else {
|
||||||
|
startTime = this.timeRange[0]
|
||||||
|
endTime = this.timeRange[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startTime = this.timeRange[0]
|
||||||
|
endTime = this.timeRange[1]
|
||||||
|
}
|
||||||
|
const step = bus.getStep(startTime, endTime)
|
||||||
|
startTime = this.$stringTimeParseToUnix(startTime)
|
||||||
|
endTime = this.$stringTimeParseToUnix(endTime)
|
||||||
|
|
||||||
|
const elements = this.chartInfo.elements || []
|
||||||
|
this.query(elements, startTime, endTime, step)
|
||||||
|
},
|
||||||
|
query (elements, startTime, endTime, step) {
|
||||||
|
switch (this.chartInfo.dataSource) {
|
||||||
|
case 1:
|
||||||
|
case 2: {
|
||||||
|
let urlPre = ''
|
||||||
|
if (this.chartInfo.dataSource === 1) {
|
||||||
|
urlPre = '/prom'
|
||||||
|
} else if (this.chartInfo.dataSource === 2) {
|
||||||
|
urlPre = '/logs/loki'
|
||||||
|
}
|
||||||
|
const requests = elements.map((element) => {
|
||||||
|
if (this.from === fromRoute.chartTemp) {
|
||||||
|
return chartTempData
|
||||||
|
}
|
||||||
|
let query = `${urlPre}/api/v1/query_range?start=${startTime}&end=${endTime}&step=${step}`
|
||||||
|
if (isTimeSeries(this.chartInfo.type)) {
|
||||||
|
query += `&nullType=${this.chartInfo.param.nullType || 'null'}`
|
||||||
|
}
|
||||||
|
query += `&query=${element.expression}`
|
||||||
|
return this.$get(query)
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartData = []
|
||||||
|
axios.all(requests).then(res => {
|
||||||
|
res.forEach(r => {
|
||||||
|
if (r.status === 'success') {
|
||||||
|
chartData.push(r.data.result)
|
||||||
|
} else {
|
||||||
|
chartData.push({ error: r.msg || r.error || r })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.chartData = chartData
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const chartData = {
|
|||||||
name: '123',
|
name: '123',
|
||||||
panelId: 1243,
|
panelId: 1243,
|
||||||
groupId: 0,
|
groupId: 0,
|
||||||
|
dataSource: 1,
|
||||||
span: 12,
|
span: 12,
|
||||||
height: 6,
|
height: 6,
|
||||||
updateBy: 1,
|
updateBy: 1,
|
||||||
@@ -19,33 +20,22 @@ const chartData = {
|
|||||||
type: 'line',
|
type: 'line',
|
||||||
unit: 2,
|
unit: 2,
|
||||||
weight: 0,
|
weight: 0,
|
||||||
param: {
|
param: '{' +
|
||||||
last: 0,
|
' "style":"line",' +
|
||||||
legendValue: {
|
' "stack":false,' +
|
||||||
total: 'off',
|
' "legend":{' +
|
||||||
min: 'off',
|
' "placement":"bottom",' +
|
||||||
avg: 'off',
|
' "values":["min","max","avg","first","last","total"]' +
|
||||||
last: 'off',
|
' },' +
|
||||||
max: 'off'
|
' "thresholds":[{' +
|
||||||
},
|
' "color":"#eee",' +
|
||||||
threshold: '',
|
' "val":"10.1"' +
|
||||||
valueMapping: {
|
' },{' +
|
||||||
mapping: [
|
' "color":"#aaa",' +
|
||||||
{
|
' "val":"base"' +
|
||||||
color: {
|
' }],' +
|
||||||
bac: '#fff',
|
' "nullType":"zero"' +
|
||||||
text: '#000'
|
'}',
|
||||||
},
|
|
||||||
text: '',
|
|
||||||
value: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
state: '1',
|
|
||||||
url: '',
|
|
||||||
nullType: 'null'
|
|
||||||
},
|
|
||||||
pid: null,
|
pid: null,
|
||||||
buildIn: 0,
|
buildIn: 0,
|
||||||
remark: '123',
|
remark: '123',
|
||||||
@@ -59,7 +49,17 @@ const chartData = {
|
|||||||
{
|
{
|
||||||
id: 68527,
|
id: 68527,
|
||||||
chartId: 690483,
|
chartId: 690483,
|
||||||
expression: '123',
|
// expression: 'up{asset="44.37"}',
|
||||||
|
expression: 'label_replace(up{instance=~\'.*10090\',job=\'\'},"asset","$1","instance","(.*)")',
|
||||||
|
type: 'expert',
|
||||||
|
legend: '{{asset}}',
|
||||||
|
buildIn: 0,
|
||||||
|
seq: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 68528,
|
||||||
|
chartId: 690483,
|
||||||
|
expression: 'Hadoop_HBase_Healthy',
|
||||||
type: 'expert',
|
type: 'expert',
|
||||||
legend: '',
|
legend: '',
|
||||||
buildIn: 0,
|
buildIn: 0,
|
||||||
@@ -125,6 +125,7 @@ const chartData = {
|
|||||||
name: '233',
|
name: '233',
|
||||||
panelId: 1243,
|
panelId: 1243,
|
||||||
groupId: 0,
|
groupId: 0,
|
||||||
|
dataSource: 1,
|
||||||
span: 6,
|
span: 6,
|
||||||
height: 4,
|
height: 4,
|
||||||
updateBy: 1,
|
updateBy: 1,
|
||||||
@@ -132,32 +133,22 @@ const chartData = {
|
|||||||
type: 'line',
|
type: 'line',
|
||||||
unit: 2,
|
unit: 2,
|
||||||
weight: 1,
|
weight: 1,
|
||||||
param: {
|
param: '{' +
|
||||||
last: 0,
|
' "style":"line",' +
|
||||||
legendValue: {
|
' "stack":true,' +
|
||||||
total: 'off',
|
' "legend":{' +
|
||||||
min: 'off',
|
' "placement":"right"' +
|
||||||
avg: 'off',
|
// ' "values":["min","max","avg","first","last","total"]' +
|
||||||
last: 'off',
|
' },' +
|
||||||
max: 'off'
|
' "thresholds":[{' +
|
||||||
},
|
' "color":"#eee",' +
|
||||||
threshold: '',
|
' "val":"10.1"' +
|
||||||
valueMapping: {
|
' },{' +
|
||||||
mapping: [
|
' "color":"#aaa",' +
|
||||||
{
|
' "val":"base"' +
|
||||||
color: {
|
' }],' +
|
||||||
bac: '#fff',
|
' "nullType":"zero"' +
|
||||||
text: '#000'
|
'}',
|
||||||
},
|
|
||||||
text: '',
|
|
||||||
value: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
url: '',
|
|
||||||
nullType: 'null'
|
|
||||||
},
|
|
||||||
pid: null,
|
pid: null,
|
||||||
buildIn: 0,
|
buildIn: 0,
|
||||||
remark: '',
|
remark: '',
|
||||||
@@ -205,7 +196,6 @@ const chartData = {
|
|||||||
type: null,
|
type: null,
|
||||||
unit: null,
|
unit: null,
|
||||||
weight: null,
|
weight: null,
|
||||||
param: null,
|
|
||||||
pid: null,
|
pid: null,
|
||||||
buildIn: null,
|
buildIn: null,
|
||||||
remark: null,
|
remark: null,
|
||||||
@@ -241,35 +231,26 @@ const chartData = {
|
|||||||
height: 4,
|
height: 4,
|
||||||
updateBy: 1,
|
updateBy: 1,
|
||||||
updateAt: '2021-11-10 09:51:06',
|
updateAt: '2021-11-10 09:51:06',
|
||||||
type: 'group',
|
type: 'line',
|
||||||
unit: 2,
|
unit: 2,
|
||||||
weight: 2,
|
weight: 2,
|
||||||
param: {
|
dataSource: 1,
|
||||||
last: 0,
|
param: '{' +
|
||||||
legendValue: {
|
' "style":"line",' +
|
||||||
total: 'off',
|
' "stack":true,' +
|
||||||
min: 'off',
|
' "legend":{' +
|
||||||
avg: 'off',
|
' "placement":"left"' +
|
||||||
last: 'off',
|
// ' "values":["min","max","avg","first","last","total"]' +
|
||||||
max: 'off'
|
' },' +
|
||||||
},
|
' "thresholds":[{' +
|
||||||
threshold: '',
|
' "color":"#eee",' +
|
||||||
valueMapping: {
|
' "val":"10.1"' +
|
||||||
mapping: [
|
' },{' +
|
||||||
{
|
' "color":"#aaa",' +
|
||||||
color: {
|
' "val":"base"' +
|
||||||
bac: '#fff',
|
' }],' +
|
||||||
text: '#000'
|
' "nullType":"zero"' +
|
||||||
},
|
'}',
|
||||||
text: '',
|
|
||||||
value: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
url: '',
|
|
||||||
nullType: 'null'
|
|
||||||
},
|
|
||||||
pid: null,
|
pid: null,
|
||||||
buildIn: 0,
|
buildIn: 0,
|
||||||
remark: '123',
|
remark: '123',
|
||||||
@@ -279,7 +260,26 @@ const chartData = {
|
|||||||
w: 6,
|
w: 6,
|
||||||
h: 4,
|
h: 4,
|
||||||
i: 690485,
|
i: 690485,
|
||||||
elements: null,
|
elements: [
|
||||||
|
{
|
||||||
|
id: 68527,
|
||||||
|
chartId: 690483,
|
||||||
|
expression: 'up{asset="44.37"}',
|
||||||
|
type: 'expert',
|
||||||
|
legend: '',
|
||||||
|
buildIn: 0,
|
||||||
|
seq: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 68528,
|
||||||
|
chartId: 690483,
|
||||||
|
expression: 'Hadoop_HBase_Healthy',
|
||||||
|
type: 'expert',
|
||||||
|
legend: '',
|
||||||
|
buildIn: 0,
|
||||||
|
seq: null
|
||||||
|
}
|
||||||
|
],
|
||||||
sync: null,
|
sync: null,
|
||||||
panel: {
|
panel: {
|
||||||
id: 1243,
|
id: 1243,
|
||||||
|
|||||||
@@ -366,3 +366,50 @@ export const fromRoute = {
|
|||||||
apiKey: 'apiKey',
|
apiKey: 'apiKey',
|
||||||
chartTemp: 'chartTemp'
|
chartTemp: 'chartTemp'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const chartDataSource = [
|
||||||
|
{
|
||||||
|
label: 'metrics',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'logs',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'misc',
|
||||||
|
value: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system',
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const chartType = {
|
||||||
|
line: 'line',
|
||||||
|
stackArea: 'stackArea',
|
||||||
|
point: 'point',
|
||||||
|
bar: 'bar',
|
||||||
|
table: 'table',
|
||||||
|
singleStat: 'singleStat',
|
||||||
|
gauge: 'gauge',
|
||||||
|
pie: 'pie',
|
||||||
|
treemap: 'treemap',
|
||||||
|
log: 'log',
|
||||||
|
text: 'text',
|
||||||
|
url: 'url',
|
||||||
|
group: 'group',
|
||||||
|
diagram: 'diagram',
|
||||||
|
assetInfo: 'assetInfo',
|
||||||
|
endpointInfo: 'endpointInfo',
|
||||||
|
topology: 'topology',
|
||||||
|
map: 'map'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const chartLegendPlacement = {
|
||||||
|
hidden: 'hidden',
|
||||||
|
left: 'left',
|
||||||
|
right: 'right',
|
||||||
|
bottom: 'bottom'
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div id="tableList" class="table-list">
|
<div id="tableList" class="table-list">
|
||||||
<div id="dashboardScrollbar" ref="dashboardScrollbar" class="table-list-box" style="overflow: auto;">
|
<div id="dashboardScrollbar" ref="dashboardScrollbar" class="table-list-box">
|
||||||
<div class="box-content" v-loading="chartListLoading">
|
<div class="box-content" v-loading="chartListLoading">
|
||||||
<chart-list
|
<chart-list
|
||||||
ref="chartList"
|
ref="chartList"
|
||||||
@@ -88,6 +88,7 @@
|
|||||||
:data-list="dataList"
|
:data-list="dataList"
|
||||||
:from="fromRoute.panel"
|
:from="fromRoute.panel"
|
||||||
:panel-lock="panelLock"
|
:panel-lock="panelLock"
|
||||||
|
:time-range="searchTime"
|
||||||
@on-edit-chart="editChart"
|
@on-edit-chart="editChart"
|
||||||
@on-refresh-time="refreshTime"
|
@on-refresh-time="refreshTime"
|
||||||
@on-remove-chart="delChart"
|
@on-remove-chart="delChart"
|
||||||
@@ -357,7 +358,6 @@ export default {
|
|||||||
this.$get('visual/panel/chart/' + data.id).then(res => {
|
this.$get('visual/panel/chart/' + data.id).then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
const chartData = res.data.data
|
const chartData = res.data.data
|
||||||
// console.log(typeof chartData.param)
|
|
||||||
if (typeof chartData.param === 'string') {
|
if (typeof chartData.param === 'string') {
|
||||||
chartData.param = chartData.param ? JSON.parse(chartData.param) : {}
|
chartData.param = chartData.param ? JSON.parse(chartData.param) : {}
|
||||||
}
|
}
|
||||||
@@ -456,7 +456,6 @@ export default {
|
|||||||
},
|
},
|
||||||
// 获取数据,用在子页面
|
// 获取数据,用在子页面
|
||||||
getData (params) {
|
getData (params) {
|
||||||
console.info(0)
|
|
||||||
if (!this.hasButton('panel_view')) {
|
if (!this.hasButton('panel_view')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -470,7 +469,6 @@ export default {
|
|||||||
}
|
}
|
||||||
this.$get('visual/panel/chart?panelId=' + params.panelId + '&groupId=0' + '&pageSize=-1').then(response => {
|
this.$get('visual/panel/chart?panelId=' + params.panelId + '&groupId=0' + '&pageSize=-1').then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
console.info(1)
|
|
||||||
this.chartListLoading = false
|
this.chartListLoading = false
|
||||||
this.dataList = chartData.data.list.map(item => {
|
this.dataList = chartData.data.list.map(item => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user