NEZ-1283 feat: chart-pie 组件开发

This commit is contained in:
zhangyu
2021-12-07 17:43:21 +08:00
parent 2b8737562c
commit c47f6f2ac3
13 changed files with 428 additions and 51 deletions

View File

@@ -778,7 +778,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 => {
response = chartData response = chartData
console.log(chartData)
if (response.code === 200) { if (response.code === 200) {
setTimeout(() => { setTimeout(() => {
this.finshGetData = false this.finshGetData = false
@@ -1802,8 +1801,8 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
const chartBox = document.getElementById('chart-' + item.id) const chartBox = document.getElementById('chart-' + item.id)
if (chartBox) { if (chartBox) {
chartBox.style.width = `100%` chartBox.style.width = '100%'
chartBox.style.height = `100%` chartBox.style.height = '100%'
if (item.type === 'group') { if (item.type === 'group') {
chartBox.style.height = 'auto' chartBox.style.height = 'auto'
} }

View File

@@ -10,6 +10,13 @@
:chart-option="chartOption" :chart-option="chartOption"
:is-fullscreen="isFullscreen" :is-fullscreen="isFullscreen"
></chart-time-series> ></chart-time-series>
<chart-pie
v-if="isChartPie(chartInfo.type)"
:chart-data="chartData"
:chart-info="chartInfo"
:chart-option="chartOption"
:is-fullscreen="isFullscreen"
></chart-pie>
<chartHexagon <chartHexagon
:ref="'chart'+chartInfo.id" :ref="'chart'+chartInfo.id"
v-if="isHexagonFigure(chartInfo.type)" v-if="isHexagonFigure(chartInfo.type)"
@@ -53,7 +60,7 @@ import chartTreemap from './chart/chartTreemap'
import chartUrl from './chart/chartUrl' import chartUrl from './chart/chartUrl'
import chartValue from './chart/chartValue' import chartValue from './chart/chartValue'
import chartHexagon from './chart/chartHexagon' import chartHexagon from './chart/chartHexagon'
import { getOption, isTimeSeries, isHexagonFigure, isUrl, isText } from './chart/tools' import { getOption, isTimeSeries, isHexagonFigure, isUrl, isText, isChartPie } from './chart/tools'
import lodash from 'lodash' import lodash from 'lodash'
export default { export default {
@@ -101,6 +108,7 @@ export default {
methods: { methods: {
isTimeSeries, isTimeSeries,
isHexagonFigure, isHexagonFigure,
isChartPie,
isUrl, isUrl,
isText, isText,
resize () { resize () {

View File

@@ -257,7 +257,6 @@ export default {
} }
this.assetData.id = data this.assetData.id = data
const boxWidth = this.$refs.hexagonBox.$el.offsetWidth const boxWidth = this.$refs.hexagonBox.$el.offsetWidth
console.log(boxWidth)
this.boxWidth = boxWidth this.boxWidth = boxWidth
// this.assetData.rate=window.screen.height/1297; // this.assetData.rate=window.screen.height/1297;
this.$nextTick(() => { this.$nextTick(() => {
@@ -284,12 +283,10 @@ export default {
this.assetData.position.mt = e.offsetY - (window.innerHeight - this.$refs.alertLabel.$el.offsetHeight) this.assetData.position.mt = e.offsetY - (window.innerHeight - this.$refs.alertLabel.$el.offsetHeight)
} }
if (boxWidth / 2 > e.offsetX) { if (boxWidth / 2 > e.offsetX) {
console.log(123)
this.assetData.position.left = e.offsetX + 35 this.assetData.position.left = e.offsetX + 35
this.assetData.position.right = 0 this.assetData.position.right = 0
this.LRTriangle = true this.LRTriangle = true
} else { } else {
console.log(456)
this.assetData.position.right = boxWidth - (e.offsetX - 35) this.assetData.position.right = boxWidth - (e.offsetX - 35)
this.assetData.position.left = 0 this.assetData.position.left = 0
this.LRTriangle = false this.LRTriangle = false

View File

@@ -1,10 +1,214 @@
<template> <template>
<div
:class="legendPlacement"
ref="pie-chart-box"
class="nz-chart__component nz-chart__component--time-series" @mouseenter="mouseEnterChart"
@mouseleave="mouseLeaveChart"
>
<div :id="`chart-canvas-${chartId}`" class="chart__canvas"></div>
<chart-legend
v-if="hasLegend"
:chart-data="chartData"
:chart-info="chartInfo"
:legends="legends"
:is-fullscreen="isFullscreen"
></chart-legend>
</div>
</template> </template>
<script> <script>
import legend from '@/components/chart/chart/legend'
import chartMixin from '@/components/chart/chartMixin'
import { chartLegendPlacement } from '@/components/common/js/constants'
import * as echarts from 'echarts'
import { getChart, setChart } from '@/components/common/js/common'
import moment from 'moment-timezone'
import bus from '@/libs/bus'
import { formatScientificNotation, getMetricTypeValue } from '@/components/common/js/tools'
import chartDataFormat from '@/components/charts/chartDataFormat'
import { randomcolor } from '@/components/common/js/radomcolor/randomcolor'
import { initColor } from '@/components/chart/chart/tools'
import lodash from 'lodash'
export default { export default {
name: 'chart-pie' name: 'chart-pie',
components: {
chartLegend: legend
},
mixins: [chartMixin],
props: {
chartInfo: Object,
chartData: Array,
chartOption: Object,
isFullscreen: Boolean
},
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 ''
}
}
},
data () {
return {
colorList: [],
chartDot: 2,
isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false
legends: [], // { name, alias, color, statistics: [{type: min, value: xxx}, ...] }
toolboxIconColor: {
active: '#53a3cb',
inactive: '#7e7e7e'
},
chartId: ''
}
},
methods: {
initChart (chartOption) {
this.legends = []
chartOption.series = this.initPieData(this.chartInfo, chartOption.series[0], this.chartData) // 生成series和legends
// chartOption.series.label.formatter = this.pieFormatterLabel
chartOption.tooltip.formatter = this.formatterFunc
console.log(chartOption, 'chartOption')
/* 使用setTimeout延迟渲染图表避免样式错乱 */
setTimeout(() => {
const myChart = this.isInit ? echarts.init(document.getElementById(`chart-canvas-${this.chartId}`)) : getChart(this.chartId)
myChart.setOption(chartOption)
this.isInit && setChart(this.chartId, myChart) // 缓存不使用vue的data是为避免整个chart被监听导致卡顿
this.isInit = false
}, 200)
},
getMinMaxFromData (originalDatas) {
let minTime = null
let maxTime = null
let minValue = null
let maxValue = null
// 将数据提为二维数组
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]
})
console.log(timeSorted, valueSorted)
minTime = timeSorted.length ? timeSorted[0][0] : ''
maxTime = timeSorted.length ? timeSorted[timeSorted.length - 1][0] : ''
minValue = valueSorted.length ? valueSorted[0][1] : ''
maxValue = valueSorted.length ? valueSorted[valueSorted.length - 1][1] : ''
return { minTime, maxTime, minValue, maxValue }
},
initPieData (chartInfo, seriesTemplate, originalDatas) {
let colorIndex = 0
const s = lodash.cloneDeep(seriesTemplate)
s.data = []
originalDatas.forEach((originalData, expressionIndex) => {
originalData.forEach((data, dataIndex) => {
if (s) {
const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
const mapping = this.selectMapping(value, chartInfo.param.valueMapping)
console.log(mapping)
// eslint-disable-next-line vue/no-mutating-props
mapping && (this.chartOption.color[colorIndex] = mapping.color.bac)
s.data.push({
value: chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(value, null, -1, 2),
realValue: value,
name: this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex),
labels: data.metric,
seriesIndex: expressionIndex,
dataIndex: dataIndex,
mapping: mapping,
label: {
...s.label,
formatter: this.pieFormatterLabel,
rich: {
color: {
color: mapping ? mapping.color.text : '#333333'
}
}
}
})
colorIndex++
}
})
})
return s
},
pieFormatterLabel (params) {
let str = '{color|'
if (this.chartInfo.param.text === 'all') {
str += params.data.name
str += ' : '
str += params.data.mapping && params.data.mapping.display ? params.data.mapping.display : params.data.value
}
if (this.chartInfo.param.text === 'value') {
str += params.data.mapping && params.data.mapping.display ? params.data.mapping.display : params.data.value
}
if (this.chartInfo.param.text === 'legend') {
str += params.data.name
}
if (this.chartInfo.param.text === 'none') {
str += ''
}
str += '}'
return str
},
formatterFunc: function (params, ticket, callback) {
const chartInfo = this.chartInfo
return `<div>
<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 14px;">
<div style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;margin-bottom: 5px">${params.data.name}</div>
<div style="font-size:12px;display:flex;justify-content: space-between;">
<div>value</div>
<div style="display: ${params.data.mapping && params.data.mapping.display ? 'none' : 'inline-block'}">${chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(params.value, null, -1, 2)}</div>
<div style="display: ${params.data.mapping && params.data.mapping.display ? 'inline-block' : 'none'}">${params.data.mapping.display}</div>
</div>
<div style="font-size:12px;display:flex;justify-content: space-between;">
<div>percent</div>
<div>${params.percent}%</div>
</div>
</div>
</div>
`
}
},
watch: {
chartData: {
deep: true,
handler (n) {
this.initChart(this.chartOption)
}
}
},
mounted () {
// eslint-disable-next-line vue/no-mutating-props
this.chartOption.color || (this.chartOption.color = initColor(20))
this.colorList = this.chartOption.color
try {
this.isStack = this.chartInfo.param.stack
} catch (e) {}
// this.initChart(this.chartOption)
}
} }
</script> </script>

View File

@@ -102,10 +102,11 @@ export default {
const valueSorted = datas.sort((a, b) => { const valueSorted = datas.sort((a, b) => {
return a[1] - b[1] return a[1] - b[1]
}) })
minTime = timeSorted[0][0] console.log(timeSorted, valueSorted)
maxTime = timeSorted[timeSorted.length - 1][0] minTime = timeSorted.length ? timeSorted[0][0] : ''
minValue = valueSorted[0][1] maxTime = timeSorted.length ? timeSorted[timeSorted.length - 1][0] : ''
maxValue = valueSorted[valueSorted.length - 1][1] minValue = valueSorted.length ? valueSorted[0][1] : ''
maxValue = valueSorted.length ? valueSorted[valueSorted.length - 1][1] : ''
return { minTime, maxTime, minValue, maxValue } return { minTime, maxTime, minValue, maxValue }
}, },
xAxisLabelFormatter (minTime, maxTime) { xAxisLabelFormatter (minTime, maxTime) {

View File

@@ -1,4 +1,29 @@
const chartPieOption = { const chartPieOption = {
tooltip: {
trigger: 'item'
},
legend: {
show: false
},
series: [
{
type: 'pie',
radius: '55%',
center: ['50%', '50%'],
data: [],
label: {
show: true,
color: '#333',
position: 'inside',
rotate: true,
width: 200,
overflow: 'truncate',
ellipsis: '...'
},
labelLine: {
show: true
}
}
]
} }
export default chartPieOption export default chartPieOption

View File

@@ -54,7 +54,9 @@ export function isTimeSeries (type) {
export function isHexagonFigure (type) { export function isHexagonFigure (type) {
return type === chartType.hexagonFigure return type === chartType.hexagonFigure
} }
export function isChartPie (type) {
return type === chartType.pie
}
export function isUrl (type) { export function isUrl (type) {
return type === chartType.url return type === chartType.url
} }

View File

@@ -141,6 +141,7 @@ export default {
} catch (e) { } catch (e) {
console.info(e) console.info(e)
} }
console.log(param)
param.showHeader = true param.showHeader = true
return { return {
...item, ...item,

View File

@@ -23,11 +23,14 @@ export default {
}, },
methods: { methods: {
handleTimeSeries (chartInfo, seriesTemplate, originalDatas) { handleTimeSeries (chartInfo, seriesTemplate, originalDatas) {
console.log(chartInfo, seriesTemplate, originalDatas)
const series = [] const series = []
let colorIndex = 0 let colorIndex = 0
originalDatas.forEach((originalData, expressionIndex) => { originalDatas.forEach((originalData, expressionIndex) => {
originalData.forEach((data, dataIndex) => { originalData.forEach((data, dataIndex) => {
const s = lodash.cloneDeep(seriesTemplate) const s = lodash.cloneDeep(seriesTemplate)
console.log(seriesTemplate)
if (s) {
s.data = data.values s.data = data.values
s.name = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex) s.name = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
if (chartInfo.param.stack) { // 堆叠 if (chartInfo.param.stack) { // 堆叠
@@ -51,6 +54,7 @@ export default {
} }
series.push(s) series.push(s)
colorIndex++ colorIndex++
}
}) })
}) })
return series return series
@@ -78,13 +82,14 @@ export default {
} }
// 处理legend别名 // 处理legend别名
let alias = this.handleLegendAlias(legend, chartInfo.elements[expressionIndex].legend) let alias = this.handleLegendAlias(legend, chartInfo.elements[expressionIndex].legend)
console.log(alias)
if (!alias) { if (!alias) {
alias = legend alias = legend
} }
const name = legend + '-' + chartInfo.elements[expressionIndex].id + '-' + dataIndex const name = alias + '-' + dataIndex
// 若需要统计,处理统计数据 // 若需要统计,处理统计数据
const statisticsTypes = chartInfo.param.legend.values const statisticsTypes = chartInfo.param.legend ? chartInfo.param.legend.values : ''
let statistics = [] let statistics = []
if (!lodash.isEmpty(statisticsTypes)) { if (!lodash.isEmpty(statisticsTypes)) {
statistics = statisticsTypes.map(type => { statistics = statisticsTypes.map(type => {
@@ -111,6 +116,30 @@ export default {
return aliasExpression return aliasExpression
} }
}, },
selectMapping (value, valueMapping) {
let mapping = ''
if (valueMapping.show) {
valueMapping.mapping.forEach(item => {
if (item.type === 'value') {
if (value == item.value) {
mapping = item
}
}
if (item.type === 'range') {
if (value > item.from && value < item.to) {
mapping = item
}
}
if (item.type === 'regx') {
const reg = new RegExp(item.regx)
if (reg.test(value)) {
mapping = item
}
}
})
}
return mapping
},
mouseEnterChart () { mouseEnterChart () {
const myChart = getChart(this.chartId) const myChart = getChart(this.chartId)
if (myChart) { if (myChart) {

View File

@@ -24,7 +24,7 @@
<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 { isChartPie, isTimeSeries } 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'
@@ -96,6 +96,9 @@ export default {
if (isTimeSeries(this.chartInfo.type)) { if (isTimeSeries(this.chartInfo.type)) {
query += `&nullType=${this.chartInfo.param.nullType || 'null'}` query += `&nullType=${this.chartInfo.param.nullType || 'null'}`
} }
if (isChartPie(this.chartInfo.type)) {
query += `&statistics=${this.chartInfo.param.statistics || 'last'}`
}
query += `&query=${element.expression}` query += `&query=${element.expression}`
return this.$get(query) return this.$get(query)
}) })
@@ -109,6 +112,7 @@ export default {
chartData.push({ error: r.msg || r.error || r }) chartData.push({ error: r.msg || r.error || r })
} }
}) })
console.log(chartData)
this.chartData = chartData this.chartData = chartData
}).finally(() => { }).finally(() => {
this.loading = false this.loading = false

View File

@@ -885,6 +885,119 @@ const chartData = {
}, },
children: [ children: [
],
chartNums: null,
asset: null,
varType: null,
varId: null,
varName: null
},
{
id: 690506,
name: '123',
panelId: 1243,
groupId: 0,
datasource: 'metrics',
span: 4,
height: 4,
updateBy: 1,
updateAt: '2021-11-10 07:06:09',
type: 'pie',
unit: 2,
weight: 0,
param: JSON.stringify(
{
nullType: 'null',
statistics: 'last',
text: 'all',
valueMapping:
{
show: true,
mapping: [
{ type: 'value', show: true, value: 2, display: 'value', color: { bac: '#8ef9ab', text: '#6a21bc' } },
{ type: 'range', show: true, display: 'range', color: { bac: '#ef1cd0', text: '#66d815' }, from: 0, to: 123123 },
{ type: 'regx', show: true, display: 'regx', color: { bac: '#d406e2', text: '#f722ec' }, regx: '123' }]
}
}),
pid: null,
buildIn: 0,
remark: '123',
seq: null,
x: 0,
y: 12,
w: 6,
h: 4,
i: 690506,
elements: [
{
id: 68527,
chartId: 690483,
// expression: 'up{asset="44.37"}',
expression: 'label_replace(up{instance=~\'.*10090\',job=\'\'},"asset","$1","instance","(.*)")',
type: 'expert',
legend: '{{asset}}',
buildIn: 0,
seq: null,
name: 'A'
},
{
id: 68528,
chartId: 690483,
expression: 'Hadoop_HBase_Healthy',
type: 'expert',
legend: '{{asset}}',
buildIn: 0,
seq: null,
name: 'B'
}
],
sync: null,
panel: {
id: 1243,
name: 'test',
createBy: null,
type: null,
link: null,
pid: null,
weight: null,
buildIn: null,
seq: null,
children: null,
parent: null,
chartNum: null
},
group: {
id: 0,
name: null,
panelId: null,
groupId: null,
span: null,
height: null,
updateBy: null,
updateAt: null,
type: null,
unit: null,
weight: null,
param: null,
pid: null,
buildIn: null,
remark: null,
seq: null,
x: null,
y: null,
elements: null,
sync: null,
panel: null,
group: null,
children: null,
chartNums: null,
asset: null,
varType: null,
varId: null,
varName: null
},
children: [
], ],
chartNums: null, chartNums: null,
asset: null, asset: null,

View File

@@ -172,7 +172,6 @@ export default {
immediate: false, immediate: false,
deep: true, deep: true,
handler (n) { handler (n) {
console.log('hexData', n)
this.init() this.init()
} }
}, },
@@ -180,7 +179,6 @@ export default {
immediate: false, immediate: false,
deep: true, deep: true,
handler () { handler () {
console.log('col')
this.init() this.init()
} }
}, },
@@ -295,7 +293,6 @@ export default {
}, },
reload () { reload () {
if (!document.hidden) { if (!document.hidden) {
console.log('reload')
this.init() this.init()
} }
}, },
@@ -816,7 +813,6 @@ export default {
getHexagon(this.hexagonSvgID).svgPolyline.on('mouseout', PolylineOut) getHexagon(this.hexagonSvgID).svgPolyline.on('mouseout', PolylineOut)
}, },
clearData () { // 清除数据 以及解绑事件 防止内存崩溃 clearData () { // 清除数据 以及解绑事件 防止内存崩溃
console.log(123123123)
if (getHexagon(this.hexagonSvgID) && getHexagon(this.hexagonSvgID).hexagonSvg) { if (getHexagon(this.hexagonSvgID) && getHexagon(this.hexagonSvgID).hexagonSvg) {
this.allHexagon.forEach((item) => { this.allHexagon.forEach((item) => {
item.off('mouseover', this.hexagonOver) item.off('mouseover', this.hexagonOver)
@@ -837,7 +833,6 @@ export default {
} }
const dom = document.getElementById(this.hexagonSvgID) const dom = document.getElementById(this.hexagonSvgID)
const child = document.getElementById('SvgHex' + this.hexagonSvgID) const child = document.getElementById('SvgHex' + this.hexagonSvgID)
console.log(dom, child)
if (dom) { if (dom) {
dom.removeChild(child) dom.removeChild(child)
} }
@@ -848,7 +843,6 @@ export default {
}, },
beforeDestroy () { beforeDestroy () {
console.log('beforeDestroy')
this.clearData() this.clearData()
if (getHexagon(this.hexagonSvgID)) { if (getHexagon(this.hexagonSvgID)) {
getHexagon(this.hexagonSvgID).allHexagonRect = null// 文本框 getHexagon(this.hexagonSvgID).allHexagonRect = null// 文本框

View File

@@ -530,7 +530,7 @@
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="item.type === 'range'" v-if="item.type === 'range'"
:prop="'param.valueMapping.mapping.' + index + 'to'" :prop="'param.valueMapping.mapping.' + index + '.to'"
:rules="{ required: true, message: $t('validate.required'), trigger: 'change'}" :rules="{ required: true, message: $t('validate.required'), trigger: 'change'}"
class="thresholds-from-item" class="thresholds-from-item"
> >