NEZ-2133 feat:新增 sankey 图表类型

This commit is contained in:
zyh
2022-08-19 10:54:37 +08:00
parent 229d84d906
commit fbc7779fc9
12 changed files with 503 additions and 9 deletions

View File

@@ -5943,6 +5943,30 @@
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz", "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz",
"integrity": "sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw==" "integrity": "sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw=="
}, },
"d3-sankey": {
"version": "0.12.3",
"resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
"integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
"requires": {
"d3-array": "1 - 2",
"d3-shape": "^1.2.0"
},
"dependencies": {
"d3-path": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
},
"d3-shape": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
"integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
"requires": {
"d3-path": "1"
}
}
}
},
"d3-scale": { "d3-scale": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",

View File

@@ -37,6 +37,7 @@
"cytoscape": "^3.15.2", "cytoscape": "^3.15.2",
"d3": "^6.7.0", "d3": "^6.7.0",
"d3-hexbin": "^0.2.2", "d3-hexbin": "^0.2.2",
"d3-sankey": "^0.12.3",
"d3-zoom": "^3.0.0", "d3-zoom": "^3.0.0",
"echarts": "^5.2.2", "echarts": "^5.2.2",
"element-ui": "^2.15.3", "element-ui": "^2.15.3",

View File

@@ -427,6 +427,7 @@ td .nz-icon-gear:before {
} }
.chart-bar, .chart-bar,
.chart-gauge, .chart-gauge,
.chart-sankey,
.chart-time-series, .chart-time-series,
.chart-treemap, .chart-treemap,
.chart-pie, .chart-pie,

View File

@@ -134,7 +134,9 @@ export default {
case 'table' : case 'table' :
case 'stat' : case 'stat' :
case 'gauge' : case 'gauge' :
case 'sankey' :
case 'pie' : case 'pie' :
case 'bubble' :
case 'treemap' : case 'treemap' :
case 'log' : case 'log' :
case 'hexagon' : case 'hexagon' :

View File

@@ -108,6 +108,15 @@
:is-fullscreen="isFullscreen" :is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData" @chartIsNoData="chartIsNoData"
></chart-gauge> ></chart-gauge>
<chart-sankey
:ref="'chart' + chartInfo.id"
v-if="isSankey(chartInfo.type)"
:chart-data="chartData"
:chart-info="chartInfo"
:chart-option="chartOption"
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-sankey>
<chart-diagram <chart-diagram
:ref="'chart' + chartInfo.id" :ref="'chart' + chartInfo.id"
v-if="isDiagram(chartInfo.type)" v-if="isDiagram(chartInfo.type)"
@@ -205,6 +214,7 @@ import chartClock from './chart/chartClock'
import chartDiagram from './chart/chartDiagram' import chartDiagram from './chart/chartDiagram'
import chartEndpointInfo from './chart/chartEndpointInfo' import chartEndpointInfo from './chart/chartEndpointInfo'
import chartGauge from './chart/chartGauge' import chartGauge from './chart/chartGauge'
import chartSankey from './chart/chartSankey'
import chartGroup from './chart/chartGroup' import chartGroup from './chart/chartGroup'
import chartLog from './chart/chartLog' import chartLog from './chart/chartLog'
import chartNoData from './chart/chartNoData' import chartNoData from './chart/chartNoData'
@@ -220,7 +230,7 @@ import chartValue from './chart/chartValue'
import chartHexagonD3 from './chart/chartHexagonD3' import chartHexagonD3 from './chart/chartHexagonD3'
import chartMap from './chart/chartMap' import chartMap from './chart/chartMap'
import chartTopology from './chart/chartTopology' import chartTopology from './chart/chartTopology'
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 { getOption, isTimeSeries, isHexagon, isUrl, isText, isChartPie, isChartBubble, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isSankey, isClock, isTopology } from './chart/tools'
import lodash from 'lodash' import lodash from 'lodash'
export default { export default {
@@ -234,6 +244,7 @@ export default {
chartDiagram, chartDiagram,
chartEndpointInfo, chartEndpointInfo,
chartGauge, chartGauge,
chartSankey,
chartGroup, chartGroup,
chartLog, chartLog,
chartNoData, chartNoData,
@@ -321,6 +332,7 @@ export default {
isMap, isMap,
isTable, isTable,
isGauge, isGauge,
isSankey,
isClock, isClock,
isTopology, isTopology,
chartIsNoData (flag) { chartIsNoData (flag) {

View File

@@ -48,7 +48,6 @@ export default {
data () { data () {
return { return {
colorList: [], colorList: [],
chartDot: 2,
isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false
chartId: '', chartId: '',
bubbleData: [], bubbleData: [],
@@ -276,11 +275,7 @@ export default {
}, },
mounted () { mounted () {
this.colorList = initColor(20) this.colorList = initColor(20)
this.chartInfo.loaded && this.initChart(this.chartOption) this.chartInfo.loaded && this.initChart()
} }
} }
</script> </script>
<style scoped>
</style>

View File

@@ -0,0 +1,387 @@
<template>
<div
ref="pie-chart-box"
class="nz-chart__component nz-chart__component--time-series" @mouseenter="mouseEnterChart"
@mouseleave="mouseLeaveChart"
index="300"
>
<div :id="`chart-canvas-${chartId}`" class="chart__canvas">
<svg :id="`sankey-svg-${chartId}`" width="100%" height="100%"></svg>
</div>
<div :class="`chart-canvas-tooltip-${chartId}`" :id="`chart-canvas-tooltip-${chartId}`" class="chart-canvas-tooltip" :style="{left:tooltip.x+'px',top:tooltip.y+'px'}" v-if="tooltip.show">
<div class="chart-canvas-tooltip-title tooltip-title">
{{tooltip.title}}
</div>
<div class="chart-canvas-tooltip-content">
<div>value</div>
<div>
<div v-if="tooltip.mapping && tooltip.mapping.icon" style="display: inline-block">
<i :class="tooltip.mapping.icon" :style="{color: tooltip.mapping.color.icon}"></i>
</div>
<div style="display: inline-block">{{tooltip.value}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import chartMixin from '@/components/chart/chartMixin'
import chartFormat from '@/components/chart/chartFormat'
import * as d3 from 'd3'
import * as d3Sankey from 'd3-sankey'
import { getMetricTypeValue } from '@/components/common/js/tools'
import chartDataFormat from '@/components/chart/chartDataFormat'
import { initColor } from '@/components/chart/chart/tools'
import { randomcolor } from '@/components/common/js/radomcolor/randomcolor'
import lodash from 'lodash'
export default {
name: 'chart-sankey',
mixins: [chartMixin, chartFormat],
props: {
chartInfo: Object,
chartData: Array,
chartOption: Object,
isFullscreen: Boolean
},
data () {
return {
colorList: [],
isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false
chartId: '',
linksData: [],
nodesData: [],
tooltip: {
x: 0,
y: 0,
title: 0,
value: 0,
mapping: {},
show: false
}
}
},
methods: {
initChart () {
this.linksData = this.initsankeyData(this.chartInfo, this.chartData) // 生成links
this.isNoData = !this.linksData.length
this.$emit('chartIsNoData', this.isNoData)
if (this.isNoData) {
return
}
// 根据link获取node
this.linksData.forEach(item => {
this.nodesData.push({ node: item.source })
this.nodesData.push({ node: item.target })
})
// 去重相同的node
for (let i = 0; i < this.nodesData.length; i++) {
for (let j = i + 1; j < this.nodesData.length; j++) {
if (this.nodesData[i].node === this.nodesData[j].node) {
this.nodesData.splice(j, 1)
j = j - 1
}
}
}
/* 使用setTimeout延迟渲染图表避免样式错乱 */
setTimeout(() => {
this.drawSankeyChart()
this.isInit = false
}, 200)
},
initsankeyData (chartInfo, originalDatas) {
this.linksData = []
this.nodesData = []
const sankeyData = []
const decimals = this.chartInfo.param.decimals || 2
originalDatas.forEach((originalData) => {
originalData.forEach((data, dataIndex) => {
this.isNoData = false
const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
const obj = {
value: value,
realValue: value,
labels: data.metric,
dataIndex: dataIndex
}
if (data.metric[chartInfo.param.sourceLabel] && data.metric[chartInfo.param.targetLabel]) {
obj.source = data.metric[chartInfo.param.sourceLabel]
obj.target = data.metric[chartInfo.param.targetLabel]
sankeyData.push(obj)
}
})
})
// 汇总 sourcetarget 相同的数据
const links = []
const tempObj = {}
sankeyData.forEach((item) => {
const key = item.source + '-' + item.target
if (!Object.prototype.hasOwnProperty.call(tempObj, key)) {
tempObj[key] = item
tempObj[key].showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(tempObj[key].value, null, -1, decimals)
} else {
const num1 = parseFloat(tempObj[key].value)
const num2 = parseFloat(item.value)
tempObj[key].value = num1 + num2
tempObj[key].showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(tempObj[key].value, null, -1, decimals)
}
})
for (const key in tempObj) {
links.push(tempObj[key])
}
return links
},
drawSankeyChart () {
this.$nextTick(() => {
// 清空作图区域
d3.select(`#sankey-svg-${this.chartId}`).selectAll('g').remove()
// 获取svg宽高 初始化画布
const svgDom = document.getElementById(`sankey-svg-${this.chartId}`)
const width = svgDom && svgDom.getBoundingClientRect().width
const height = svgDom && svgDom.getBoundingClientRect().height
const margin1 = 100
const margin2 = 50
const svg = d3.select(`#sankey-svg-${this.chartId}`)
const chart = svg.append('g').attr('transform', `translate(${margin2}, ${margin2})`)
// 创建桑基图生成器
const sankey = d3Sankey
.sankey()
.nodeWidth(20)
.nodePadding(20)
.size([width - 2 * margin1, height - 2 * margin2])
.nodeId((d) => d.node)
const nodesData = lodash.cloneDeep(this.nodesData)
const linksData = lodash.cloneDeep(this.linksData)
const { nodes, links } = sankey({
nodes: nodesData,
links: linksData
})
// 设置节点颜色
nodes.forEach((item, index) => {
if (index >= 20) {
const colorRandom = randomcolor()
this.colorList.push(colorRandom)
}
const mapping = this.selectMapping(item.value, this.chartInfo.param.valueMapping, this.chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
item.mapping = mapping
item.background = mapping ? mapping.color.bac : this.colorList[index]
const decimals = this.chartInfo.param.decimals || 2
item.showValue = chartDataFormat.getUnit(this.chartInfo.unit ? this.chartInfo.unit : 2).compute(item.value, null, -1, decimals)
})
// 创建一个连线绘制组,绑定连线数据(links)
chart
.append('g')
.attr('fill', 'none')
.selectAll()
.data(links)
.join('path')
.attr('linkNodes', (d) => { // 设置与当前连线相连的节点(必须以字母开头)
return 'i-' + d.source.index + ' ' + 'i-' + d.target.index
})
.attr('d', d3Sankey.sankeyLinkHorizontal())
.attr('stroke', (d, i) => {
return d.source.background
})
.attr('stroke-width', (d) => d.width)
.style('stroke-opacity', '0.5')
.attr('cursor', 'pointer')
.style('transition', 'all 0.3s')
// 创建一个节点绘制组,绑定节点数据(nodes)。
chart
.append('g')
.selectAll()
.data(nodes)
.join('g')
.attr('class', 'node')
.attr('linkNodes', (d) => { // 设置与当前节点相连的节点(必须以字母开头)
let nodeStr = ''
d.targetLinks.forEach(link => {
nodeStr += 'i-' + link.source.index + ' '
})
nodeStr += 'i-' + d.index
d.sourceLinks.forEach(link => {
nodeStr += ' ' + 'i-' + link.target.index
})
return nodeStr
})
.attr('index', (d) => { // 设置标识(必须以字母开头)
return 'i-' + d.index
})
.append('rect')
.attr('fill', (d, i) => {
return d.background
})
.attr('x', (d) => d.x0)
.attr('y', (d) => d.y0)
.attr('height', (d) => d.y1 - d.y0)
.attr('width', (d) => d.x1 - d.x0)
.attr('cursor', 'pointer')
.style('transition', 'all 0.3s')
// 节点添加文字
chart
.selectAll('.node')
.append('foreignObject')
// .attr('width', 20)
.attr('height', function (d) { return d.y1 - d.y0 })
.attr('x', function (d) { return d.x0 + 30 })
.attr('y', function (d) { return d.y0 })
.style('overflow', 'visible')
.style('cursor', 'pointer')
.style('transition', 'all 0.3s')
.html((d) => {
return this.sankeyFormatterLabel(d)
})
// 划过连线
chart.selectAll('path')
.on('mouseover', (e, d) => {
chart.selectAll('.node, path').style('fill-opacity', '0.1').style('stroke-opacity', '0.1')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '0.1')
const hoverNodes = d3.select(e.target).style('stroke-opacity', '0.8').attr('linkNodes').split(' ')
hoverNodes.forEach((index) => {
chart.selectAll('[index=' + index + ']').style('fill-opacity', '1').selectAll('foreignObject').style('opacity', '1')
})
// 显示悬浮框
this.tooltip.title = d.source.node + ' ——> ' + d.target.node
this.tooltip.value = d.showValue
this.tooltip.mapping = ''
this.tooltip.show = true
this.setPosition(e)
})
.on('mousemove', (e) => {
if (this.tooltip.show) {
this.setPosition(e)
}
})
.on('mouseleave', () => {
chart.selectAll('.node, path').style('fill-opacity', '1').style('stroke-opacity', '0.5')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '1')
// 隐藏悬浮框
this.tooltip.show = false
})
// 划过节点
chart.selectAll('.node')
.on('mouseover', (e, d) => {
chart.selectAll('.node, path').style('fill-opacity', '0.1').style('stroke-opacity', '0.1')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '0.1')
chart.selectAll('[linkNodes~=' + 'i-' + d.index + ']')
.style('fill-opacity', '1')
.style('stroke-opacity', '0.8')
.selectAll('foreignObject')
.style('opacity', '1')
// 显示悬浮框
this.tooltip.title = d.node
this.tooltip.value = d.showValue
this.tooltip.mapping = d.mapping
this.tooltip.show = true
this.setPosition(e)
})
.on('mousemove', (e) => {
if (this.tooltip.show) {
this.setPosition(e)
}
})
.on('mouseleave', () => {
chart.selectAll('.node, path').style('fill-opacity', '1').style('stroke-opacity', '0.5')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '1')
// 隐藏悬浮框
this.tooltip.show = false
})
})
},
setPosition (e) {
const windowWidth = window.innerWidth// 窗口宽度
const windowHeight = window.innerHeight// 窗口高度
const box = document.getElementById(`chart-canvas-tooltip-${this.chartId}`)
if (box) {
const boxWidth = box.offsetWidth
const boxHeight = box.offsetHeight
if (e.pageX < (windowWidth / 2)) { // 说明鼠标在左边放不下提示框
this.tooltip.x = e.pageX + 15
} else {
this.tooltip.x = e.pageX - boxWidth - 15
}
if (e.pageY + 50 + boxHeight < windowHeight) { // 说明鼠标上面放不下提示框
this.tooltip.y = e.pageY + 15
} else {
this.tooltip.y = e.pageY - boxHeight - 10
}
} else {
this.tooltip.y = e.pageY + 15
this.tooltip.x = e.pageX + 15
}
},
// 处理label
sankeyFormatterLabel (data) {
let str = ''
let valueStr = ''
if (this.chartInfo.param.text === 'all') {
str += data.node
valueStr = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { value: data.showValue }) : data.showValue
}
if (this.chartInfo.param.text === 'value' || !this.chartInfo.param.text) {
valueStr = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { value: data.showValue }) : data.showValue
}
if (this.chartInfo.param.text === 'legend') {
str += data.node
}
if (this.chartInfo.param.text === 'none') {
str += ''
}
if (str && valueStr) {
return `
<div style="width:auto;height: 100%;display: flex;justify-content: center;flex-direction: column;color:#000000">
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<span>${str}</span>
</p>
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<i class="${data.mapping && data.mapping.icon}" style="color: ${data.mapping && data.mapping.color && data.mapping.color.icon};font-size:1em;"></i>
<span>${valueStr}</span>
</p>
</div>
`
} else if (str) {
return `
<div style="width:auto;height: 100%;display: flex;justify-content: center;flex-direction: column;color:#000000">
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<i class="${data.mapping && data.mapping.icon}" style="color: ${data.mapping && data.mapping.color && data.mapping.color.icon};font-size:1em;"></i>
<span>${str}</span>
</p>
</div>
`
} else if (valueStr) {
return `
<div style="width:auto;height: 100%;display: flex;justify-content: center;flex-direction: column;color:#000000">
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<i class="${data.mapping && data.mapping.icon}" style="color: ${data.mapping && data.mapping.color && data.mapping.color.icon};font-size:1em;"></i>
<span>${valueStr}</span>
</p>
</div>
`
}
},
resize () {
setTimeout(() => {
this.drawSankeyChart()
}, 50)
}
},
mounted () {
this.colorList = initColor(20)
this.chartInfo.loaded && this.initChart()
}
}
</script>

View File

@@ -114,6 +114,9 @@ export function isTable (type) {
export function isGauge (type) { export function isGauge (type) {
return type === chartType.gauge return type === chartType.gauge
} }
export function isSankey (type) {
return type === chartType.sankey
}
export function isClock (type) { export function isClock (type) {
return type === chartType.clock return type === chartType.clock
} }

View File

@@ -408,6 +408,7 @@ export const chartType = {
table: 'table', table: 'table',
stat: 'stat', stat: 'stat',
gauge: 'gauge', gauge: 'gauge',
sankey: 'sankey',
pie: 'pie', pie: 'pie',
bubble: 'bubble', bubble: 'bubble',
treemap: 'treemap', treemap: 'treemap',

View File

@@ -340,6 +340,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>
<div class="form-items--half-width-group" v-if="isGauge(chartConfig.type)"> <div class="form-items--half-width-group" v-if="isGauge(chartConfig.type)">
<!--min--> <!--min-->
<el-form-item :label="$t('dashboard.panel.chartForm.min')" class="form-item--half-width"> <el-form-item :label="$t('dashboard.panel.chartForm.min')" class="form-item--half-width">
@@ -360,6 +361,40 @@
show-word-limit v-model="chartConfig.param.max"/> show-word-limit v-model="chartConfig.param.max"/>
</el-form-item> </el-form-item>
</div> </div>
<div class="form-items--half-width-group" v-if="isSankey(chartConfig.type)" key="sankey">
<!--Source label-->
<el-form-item class="form-item--half-width" :label="$t('dashboard.panel.chartForm.sourceLabel')" prop="param.sourceLabel"
:rules="[
{ required: true, message: $t('validate.required'), trigger: 'blur'},
{ pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: $t('dashboard.panel.matchRegex'), trigger: 'blur'},
{ validator: labelValidator,trigger: 'blur'},
]"
>
<el-input
size="small"
style="margin-top: 2px"
:placeholder="$t('overall.placeHolder')"
@change="change"
v-model="chartConfig.param.sourceLabel"/>
</el-form-item>
<!--Target label-->
<el-form-item class="form-item--half-width" :label="$t('dashboard.panel.chartForm.targetLabel')" prop="param.targetLabel"
:rules="[
{ required: true, message: $t('validate.required'), trigger: 'blur'},
{ pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: $t('dashboard.panel.matchRegex'), trigger: 'blur'},
{ validator: labelValidator,trigger: 'blur'},
]"
>
<el-input
size="small"
style="margin-top: 2px"
:placeholder="$t('overall.placeHolder')"
@change="change"
v-model="chartConfig.param.targetLabel"/>
</el-form-item>
</div>
<div class="form-items--half-width-group" v-if="isShowDecimals(chartConfig.type)"> <div class="form-items--half-width-group" v-if="isShowDecimals(chartConfig.type)">
<!--decimals--> <!--decimals-->
<el-form-item :label="$t('overall.decimal')" class="form-item--half-width"> <el-form-item :label="$t('overall.decimal')" class="form-item--half-width">
@@ -898,7 +933,7 @@ import chartTypeShow from '@/components/common/rightBox/chart/chartTypeShow'
import VueTagsInput from '@johmun/vue-tags-input' import VueTagsInput from '@johmun/vue-tags-input'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import { randomcolor, ColorReverse } from '@/components/common/js/radomcolor/randomcolor' import { randomcolor, ColorReverse } from '@/components/common/js/radomcolor/randomcolor'
import { isGauge } from '@/components/chart/chart/tools' import { isGauge, isSankey } from '@/components/chart/chart/tools'
export default { export default {
name: 'chartConfig', name: 'chartConfig',
@@ -969,6 +1004,10 @@ export default {
id: 'gauge', id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label') name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
}, },
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{ {
id: 'treemap', id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label') name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
@@ -995,6 +1034,7 @@ export default {
}, },
methods: { methods: {
isGauge, isGauge,
isSankey,
beforeInit () { beforeInit () {
this.promqlType = this.type this.promqlType = this.type
this.chartTypeList = this[this.type + 'ChartTypeList'] this.chartTypeList = this[this.type + 'ChartTypeList']
@@ -1017,6 +1057,18 @@ export default {
this.expressionChange() this.expressionChange()
} }
}, },
// 变量名校验 防止重复
labelValidator (rule, value, callback) {
const sourceLabel = this.chartConfig.param.sourceLabel
const targetLabel = this.chartConfig.param.targetLabel
setTimeout(() => {
if (sourceLabel === targetLabel) {
callback(new Error(this.$t('error.labelEqual')))
} else {
callback()
}
}, 100)
},
chartTypeChange (type) { chartTypeChange (type) {
switch (type) { switch (type) {
case 'line': case 'line':
@@ -1053,8 +1105,9 @@ export default {
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':
case 'gauge': case 'gauge':
case 'sankey':
case 'bubble': case 'bubble':
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon' || this.oldType === 'bubble') { if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'sankey' || this.oldType === 'hexagon' || this.oldType === 'bubble') {
break break
} }
this.chartConfig.param = { this.chartConfig.param = {

View File

@@ -74,6 +74,7 @@ export default {
case 'gauge': case 'gauge':
case 'pie': case 'pie':
case 'bubble': case 'bubble':
case 'sankey':
return true return true
default: return false default: return false
} }
@@ -91,6 +92,7 @@ export default {
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':
case 'gauge': case 'gauge':
case 'sankey':
case 'bubble': case 'bubble':
return false return false
default: return false default: return false
@@ -106,6 +108,7 @@ export default {
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':
case 'gauge': case 'gauge':
case 'sankey':
case 'treemap': case 'treemap':
case 'pie': case 'pie':
case 'bubble': case 'bubble':
@@ -134,6 +137,7 @@ export default {
case 'hexagon': case 'hexagon':
case 'bar': case 'bar':
case 'gauge': case 'gauge':
case 'sankey':
case 'treemap': case 'treemap':
case 'pie': case 'pie':
case 'bubble': case 'bubble':
@@ -151,6 +155,7 @@ export default {
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':
case 'gauge': case 'gauge':
case 'sankey':
return false return false
case 'line': case 'line':
case 'area': case 'area':
@@ -169,6 +174,7 @@ export default {
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':
case 'gauge': case 'gauge':
case 'sankey':
return true return true
case 'line': case 'line':
case 'area': case 'area':
@@ -201,6 +207,7 @@ export default {
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':
case 'gauge': case 'gauge':
case 'sankey':
return true return true
default: return false default: return false
} }

View File

@@ -247,6 +247,10 @@ export default {
id: 'gauge', id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label') name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
}, },
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{ {
id: 'treemap', id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label') name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
@@ -294,6 +298,10 @@ export default {
id: 'gauge', id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label') name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
}, },
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{ {
id: 'treemap', id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label') name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')