NEZ-2037 feat:新增chart类型bubble
This commit is contained in:
@@ -420,6 +420,7 @@ td .nz-icon-gear:before {
|
||||
.chart-time-series,
|
||||
.chart-treemap,
|
||||
.chart-pie,
|
||||
.chart-bubble,
|
||||
.chart-canvas-tooltip,
|
||||
.line-chart-block-Zindex,
|
||||
.alert-label,
|
||||
|
||||
@@ -26,6 +26,15 @@
|
||||
:is-fullscreen="isFullscreen"
|
||||
@chartIsNoData="chartIsNoData"
|
||||
></chart-pie>
|
||||
<chart-bubble
|
||||
:ref="'chart' + chartInfo.id"
|
||||
v-if="isChartBubble(chartInfo.type)"
|
||||
:chart-data="chartData"
|
||||
:chart-info="chartInfo"
|
||||
:chart-option="chartOption"
|
||||
:is-fullscreen="isFullscreen"
|
||||
@chartIsNoData="chartIsNoData"
|
||||
></chart-bubble>
|
||||
<chart-bar
|
||||
:ref="'chart' + chartInfo.id"
|
||||
v-if="isChartBar(chartInfo.type)"
|
||||
@@ -200,6 +209,7 @@ import chartGroup from './chart/chartGroup'
|
||||
import chartLog from './chart/chartLog'
|
||||
import chartNoData from './chart/chartNoData'
|
||||
import chartPie from './chart/chartPie'
|
||||
import chartBubble from './chart/chartBubble'
|
||||
import chartStat from './chart/chartStat'
|
||||
import chartTable from './chart/chartTable'
|
||||
import chartText from './chart/chartText'
|
||||
@@ -210,7 +220,7 @@ import chartValue from './chart/chartValue'
|
||||
import chartHexagonD3 from './chart/chartHexagonD3'
|
||||
import chartMap from './chart/chartMap'
|
||||
import chartTopology from './chart/chartTopology'
|
||||
import { getOption, isTimeSeries, isHexagon, isUrl, isText, isChartPie, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isClock, isTopology } from './chart/tools'
|
||||
import { getOption, isTimeSeries, isHexagon, isUrl, isText, isChartPie, isChartBubble, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isClock, isTopology } from './chart/tools'
|
||||
import lodash from 'lodash'
|
||||
|
||||
export default {
|
||||
@@ -228,6 +238,7 @@ export default {
|
||||
chartLog,
|
||||
chartNoData,
|
||||
chartPie,
|
||||
chartBubble,
|
||||
chartStat,
|
||||
chartTable,
|
||||
chartText,
|
||||
@@ -295,6 +306,7 @@ export default {
|
||||
isTimeSeries,
|
||||
isHexagon,
|
||||
isChartPie,
|
||||
isChartBubble,
|
||||
isChartBar,
|
||||
isUrl,
|
||||
isText,
|
||||
|
||||
249
nezha-fronted/src/components/chart/chart/chartBubble.vue
Normal file
249
nezha-fronted/src/components/chart/chart/chartBubble.vue
Normal file
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<div
|
||||
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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chartMixin from '@/components/chart/chartMixin'
|
||||
import chartFormat from '@/components/chart/chartFormat'
|
||||
import * as echarts from 'echarts'
|
||||
import * as d3 from 'd3'
|
||||
import { getChart, setChart } from '@/components/common/js/common'
|
||||
import { getMetricTypeValue } from '@/components/common/js/tools'
|
||||
import chartDataFormat from '@/components/chart/chartDataFormat'
|
||||
import { initColor } from '@/components/chart/chart/tools'
|
||||
import lodash from 'lodash'
|
||||
|
||||
export default {
|
||||
name: 'chart-bubble',
|
||||
components: {
|
||||
},
|
||||
mixins: [chartMixin, chartFormat],
|
||||
props: {
|
||||
chartInfo: Object,
|
||||
chartData: Array,
|
||||
chartOption: Object,
|
||||
isFullscreen: Boolean
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
colorList: [],
|
||||
chartDot: 2,
|
||||
isInit: true, // 是否是初始化,初始化时为true,图表初始化结束后设为false
|
||||
chartId: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initChart (chartOption = this.chartOption) {
|
||||
this.legends = []
|
||||
const parentNode = {
|
||||
id: 'parentNode',
|
||||
name: 'parentNode',
|
||||
depth: -1
|
||||
}
|
||||
chartOption.dataset.source = [parentNode, ...this.initBubbleData(this.chartInfo, [], this.chartData)]
|
||||
if (this.isNoData) {
|
||||
return
|
||||
}
|
||||
chartOption.series[0].renderItem = this.renderItem
|
||||
chartOption.tooltip.formatter = this.formatterFunc
|
||||
chartOption.tooltip.position = this.tooltipPosition
|
||||
/* 使用setTimeout延迟渲染图表,避免样式错乱 */
|
||||
setTimeout(() => {
|
||||
const myChart = this.isInit ? echarts.init(document.getElementById(`chart-canvas-${this.chartId}`)) : getChart(this.chartId)
|
||||
if (!myChart) {
|
||||
return
|
||||
}
|
||||
myChart.setOption(chartOption)
|
||||
this.isInit && setChart(this.chartId, myChart) // 缓存;不使用vue的data是为避免整个chart被监听导致卡顿
|
||||
this.isInit = false
|
||||
}, 200)
|
||||
},
|
||||
initBubbleData (chartInfo, seriesTemplate, originalDatas) {
|
||||
let colorIndex = 0
|
||||
const decimals = this.chartInfo.param.decimals || 2
|
||||
const s = lodash.cloneDeep(seriesTemplate)
|
||||
this.isNoData = true
|
||||
originalDatas.forEach((originalData, expressionIndex) => {
|
||||
originalData.forEach((data, dataIndex) => {
|
||||
this.isNoData = false
|
||||
if (s) {
|
||||
const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
|
||||
const showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(value, null, -1, decimals)
|
||||
const mapping = this.selectMapping(value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
mapping && this.chartOption.color && (this.chartOption.color[colorIndex] = mapping.color.bac)
|
||||
const legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
|
||||
s.push({
|
||||
value: value,
|
||||
realValue: value,
|
||||
showValue: showValue,
|
||||
name: legend.name,
|
||||
alias: legend.alias,
|
||||
labels: data.metric,
|
||||
seriesIndex: expressionIndex,
|
||||
dataIndex: dataIndex,
|
||||
mapping: mapping,
|
||||
id: colorIndex, // 气泡id
|
||||
background: mapping ? mapping.color.bac : this.colorList[colorIndex] // 气泡颜色
|
||||
})
|
||||
colorIndex++
|
||||
}
|
||||
})
|
||||
})
|
||||
this.$emit('chartIsNoData', this.isNoData)
|
||||
return s
|
||||
},
|
||||
formatterFunc: function (params, ticket, callback) {
|
||||
const self = this
|
||||
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 class="tooltip-title" style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;margin-bottom: 5px">${params.data.alias}</div>
|
||||
<div style="font-size:12px;display:flex;justify-content: space-between;">
|
||||
<div>value</div>
|
||||
<div>
|
||||
<div style="display: ${params.data.mapping && params.data.mapping.icon ? 'inline-block' : 'none'}">
|
||||
<i class="${params.data.mapping && params.data.mapping.icon}" style="color: ${params.data.mapping && params.data.mapping.color && params.data.mapping.color.icon}"></i>
|
||||
</div>
|
||||
<div style="display: ${params.data.mapping && params.data.mapping.display ? 'none' : 'inline-block'}">${params.data.showValue}</div>
|
||||
<div style="display: ${params.data.mapping && params.data.mapping.display ? 'inline-block' : 'none'}">${self.handleDisplay(params.data.mapping.display, { ...params.data.labels, value: params.data.showValue })}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
},
|
||||
renderItem (params, api) {
|
||||
// 如果数据全为0 则设置默认值(否则图表不显示)
|
||||
let seriesData = lodash.cloneDeep(this.chartOption.dataset.source)
|
||||
if (seriesData.every(item => !item.value || item.value == 0)) {
|
||||
seriesData = seriesData.map(item => {
|
||||
if (item.id !== 'parentNode') {
|
||||
return {
|
||||
...item,
|
||||
value: 100
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...item
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const displayRoot = stratify()
|
||||
function stratify () {
|
||||
return d3
|
||||
.stratify()
|
||||
.parentId(function (d) {
|
||||
// 判断是否是父节点
|
||||
if (d.id !== 'parentNode') {
|
||||
return 'parentNode'
|
||||
}
|
||||
})(seriesData)
|
||||
.sum(function (d) {
|
||||
return d.value || 0
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
return b.value - a.value
|
||||
})
|
||||
}
|
||||
function overallLayout (params, api) {
|
||||
const context = params.context
|
||||
d3
|
||||
.pack()
|
||||
.size([api.getWidth() - 2, api.getHeight() - 2])
|
||||
.padding(6)(displayRoot)
|
||||
context.nodes = {}
|
||||
displayRoot.descendants().forEach(function (node) {
|
||||
context.nodes[node.id] = node
|
||||
})
|
||||
}
|
||||
const context = params.context
|
||||
// Only do that layout once in each time `setOption` called.
|
||||
// 每次调用“setOption”时,只能进行一次布局。
|
||||
if (!context.layout) {
|
||||
context.layout = true
|
||||
overallLayout(params, api)
|
||||
}
|
||||
const nodePath = api.value('id')
|
||||
// const nodeName = nodePath
|
||||
// .slice(nodePath.lastIndexOf('.') + 1)
|
||||
// .split(/(?=[A-Z][^A-Z])/g)
|
||||
// .join('')
|
||||
const node = context.nodes[nodePath]
|
||||
if (node.id === 'parentNode') {
|
||||
node.r = 0
|
||||
}
|
||||
if (!node) {
|
||||
// Reder nothing.
|
||||
return
|
||||
}
|
||||
const z2 = api.value('depth') * 2
|
||||
return {
|
||||
type: 'circle',
|
||||
shape: {
|
||||
cx: node.x,
|
||||
cy: node.y,
|
||||
r: node.r
|
||||
},
|
||||
transition: ['shape'],
|
||||
z2: z2,
|
||||
textContent: {
|
||||
type: 'text',
|
||||
style: {
|
||||
// transition: isLeaf ? 'fontSize' : null,
|
||||
text: this.pieFormatterLabel(node),
|
||||
fill: node.data.mapping ? node.data.mapping.color.text : '#000000',
|
||||
width: node.r * 1.3,
|
||||
overflow: 'truncate',
|
||||
fontSize: node.r / 3,
|
||||
lineHeight: node.r / 2.2
|
||||
|
||||
},
|
||||
emphasis: {
|
||||
style: {
|
||||
// overflow: null,
|
||||
// fontSize: Math.max(node.r / 3, 12)
|
||||
}
|
||||
}
|
||||
},
|
||||
textConfig: {
|
||||
position: 'inside'
|
||||
},
|
||||
style: {
|
||||
fill: node.data.background
|
||||
},
|
||||
emphasis: {
|
||||
style: {
|
||||
// shadowBlur: 20,
|
||||
// shadowOffsetX: 3,
|
||||
// shadowOffsetY: 5,
|
||||
// shadowColor: 'rgba(0,0,0,0.3)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
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.chartInfo.loaded && this.initChart(this.chartOption)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
const chartBubble = {
|
||||
dataset: {
|
||||
source: []
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'item',
|
||||
confine: false,
|
||||
extraCssText: 'z-index:1000;',
|
||||
z: 9,
|
||||
animation: false,
|
||||
appendToBody: true,
|
||||
className: 'chart-bubble'
|
||||
},
|
||||
hoverLayerThreshold: Infinity,
|
||||
series: [{
|
||||
type: 'custom',
|
||||
renderItem: undefined,
|
||||
progressive: 0,
|
||||
coordinateSystem: 'none'
|
||||
}]
|
||||
}
|
||||
export default chartBubble
|
||||
@@ -1,6 +1,7 @@
|
||||
import { chartType } from '@/components/common/js/constants'
|
||||
import chartBarOption from './options/chartBar'
|
||||
import chartPieOption from './options/chartPie'
|
||||
import chartBubbleOption from './options/chartBubble'
|
||||
import lodash from 'lodash'
|
||||
import {
|
||||
chartTimeSeriesLineOption,
|
||||
@@ -39,6 +40,10 @@ export function getOption (type) {
|
||||
chartOption = lodash.cloneDeep(chartPieOption)
|
||||
break
|
||||
}
|
||||
case chartType.bubble: {
|
||||
chartOption = lodash.cloneDeep(chartBubbleOption)
|
||||
break
|
||||
}
|
||||
case chartType.treemap: {
|
||||
chartOption = lodash.cloneDeep(chartTreemapOption)
|
||||
break
|
||||
@@ -68,6 +73,9 @@ export function isHexagon (type) {
|
||||
export function isChartPie (type) {
|
||||
return type === chartType.pie
|
||||
}
|
||||
export function isChartBubble (type) {
|
||||
return type === chartType.bubble
|
||||
}
|
||||
export function isChartBar (type) {
|
||||
return type === chartType.bar
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ export const endpoint = {
|
||||
{ value: 1, label: i18n.t('endpoint.metricLabel') },
|
||||
{ value: 2, label: i18n.t('endpoint.metricEnable') },
|
||||
{ value: 3, label: i18n.t('endpoint.logEnable') }
|
||||
],
|
||||
]
|
||||
}
|
||||
export const alertMessage = {
|
||||
severityData: [
|
||||
@@ -407,6 +407,7 @@ export const chartType = {
|
||||
stat: 'stat',
|
||||
gauge: 'gauge',
|
||||
pie: 'pie',
|
||||
bubble: 'bubble',
|
||||
treemap: 'treemap',
|
||||
log: 'log',
|
||||
text: 'text',
|
||||
|
||||
@@ -910,6 +910,10 @@ export default {
|
||||
id: 'pie',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
|
||||
},
|
||||
{
|
||||
id: 'bubble',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
|
||||
},
|
||||
{
|
||||
id: 'log',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.log.label')
|
||||
@@ -980,7 +984,8 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon') {
|
||||
case 'bubble':
|
||||
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon' || this.oldType === 'bubble') {
|
||||
break
|
||||
}
|
||||
this.chartConfig.param = {
|
||||
|
||||
@@ -55,6 +55,7 @@ export default {
|
||||
case 'treemap':
|
||||
case 'gauge':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
return false
|
||||
default: return false
|
||||
}
|
||||
@@ -72,6 +73,7 @@ export default {
|
||||
case 'treemap':
|
||||
case 'gauge':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
return true
|
||||
default: return false
|
||||
}
|
||||
@@ -89,6 +91,7 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'bubble':
|
||||
return false
|
||||
default: return false
|
||||
}
|
||||
@@ -105,6 +108,7 @@ export default {
|
||||
case 'gauge':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
case 'bar':
|
||||
return false
|
||||
default: return false
|
||||
@@ -132,6 +136,7 @@ export default {
|
||||
case 'gauge':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
return true
|
||||
default: return false
|
||||
}
|
||||
@@ -142,6 +147,7 @@ export default {
|
||||
case 'bar':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
@@ -159,6 +165,7 @@ export default {
|
||||
case 'bar':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
@@ -190,6 +197,7 @@ export default {
|
||||
case 'bar':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
|
||||
@@ -243,6 +243,10 @@ export default {
|
||||
id: 'pie',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
|
||||
},
|
||||
{
|
||||
id: 'bubble',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
|
||||
},
|
||||
{
|
||||
id: 'table',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.table.label')
|
||||
@@ -286,6 +290,10 @@ export default {
|
||||
id: 'pie',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
|
||||
},
|
||||
{
|
||||
id: 'bubble',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
|
||||
},
|
||||
{
|
||||
id: 'log',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.log.label')
|
||||
|
||||
@@ -834,6 +834,10 @@ export default {
|
||||
id: 'pie',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
|
||||
},
|
||||
{
|
||||
id: 'bubble',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
|
||||
},
|
||||
{
|
||||
id: 'gauge',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
|
||||
@@ -899,7 +903,8 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon') {
|
||||
case 'bubble':
|
||||
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon' || this.oldType === 'bubble') {
|
||||
break
|
||||
}
|
||||
this.chartConfig.param = {
|
||||
|
||||
Reference in New Issue
Block a user