NEZ-2133 feat:新增 sankey 图表类型
This commit is contained in:
24
nezha-fronted/package-lock.json
generated
24
nezha-fronted/package-lock.json
generated
@@ -5943,6 +5943,30 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz",
|
||||
"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": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"cytoscape": "^3.15.2",
|
||||
"d3": "^6.7.0",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"d3-sankey": "^0.12.3",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"echarts": "^5.2.2",
|
||||
"element-ui": "^2.15.3",
|
||||
|
||||
@@ -427,6 +427,7 @@ td .nz-icon-gear:before {
|
||||
}
|
||||
.chart-bar,
|
||||
.chart-gauge,
|
||||
.chart-sankey,
|
||||
.chart-time-series,
|
||||
.chart-treemap,
|
||||
.chart-pie,
|
||||
|
||||
@@ -134,7 +134,9 @@ export default {
|
||||
case 'table' :
|
||||
case 'stat' :
|
||||
case 'gauge' :
|
||||
case 'sankey' :
|
||||
case 'pie' :
|
||||
case 'bubble' :
|
||||
case 'treemap' :
|
||||
case 'log' :
|
||||
case 'hexagon' :
|
||||
|
||||
@@ -108,6 +108,15 @@
|
||||
:is-fullscreen="isFullscreen"
|
||||
@chartIsNoData="chartIsNoData"
|
||||
></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
|
||||
:ref="'chart' + chartInfo.id"
|
||||
v-if="isDiagram(chartInfo.type)"
|
||||
@@ -205,6 +214,7 @@ import chartClock from './chart/chartClock'
|
||||
import chartDiagram from './chart/chartDiagram'
|
||||
import chartEndpointInfo from './chart/chartEndpointInfo'
|
||||
import chartGauge from './chart/chartGauge'
|
||||
import chartSankey from './chart/chartSankey'
|
||||
import chartGroup from './chart/chartGroup'
|
||||
import chartLog from './chart/chartLog'
|
||||
import chartNoData from './chart/chartNoData'
|
||||
@@ -220,7 +230,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, 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'
|
||||
|
||||
export default {
|
||||
@@ -234,6 +244,7 @@ export default {
|
||||
chartDiagram,
|
||||
chartEndpointInfo,
|
||||
chartGauge,
|
||||
chartSankey,
|
||||
chartGroup,
|
||||
chartLog,
|
||||
chartNoData,
|
||||
@@ -321,6 +332,7 @@ export default {
|
||||
isMap,
|
||||
isTable,
|
||||
isGauge,
|
||||
isSankey,
|
||||
isClock,
|
||||
isTopology,
|
||||
chartIsNoData (flag) {
|
||||
|
||||
@@ -48,7 +48,6 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
colorList: [],
|
||||
chartDot: 2,
|
||||
isInit: true, // 是否是初始化,初始化时为true,图表初始化结束后设为false
|
||||
chartId: '',
|
||||
bubbleData: [],
|
||||
@@ -276,11 +275,7 @@ export default {
|
||||
},
|
||||
mounted () {
|
||||
this.colorList = initColor(20)
|
||||
this.chartInfo.loaded && this.initChart(this.chartOption)
|
||||
this.chartInfo.loaded && this.initChart()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
387
nezha-fronted/src/components/chart/chart/chartSankey.vue
Normal file
387
nezha-fronted/src/components/chart/chart/chartSankey.vue
Normal 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)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 汇总 source,target 相同的数据
|
||||
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>
|
||||
@@ -114,6 +114,9 @@ export function isTable (type) {
|
||||
export function isGauge (type) {
|
||||
return type === chartType.gauge
|
||||
}
|
||||
export function isSankey (type) {
|
||||
return type === chartType.sankey
|
||||
}
|
||||
export function isClock (type) {
|
||||
return type === chartType.clock
|
||||
}
|
||||
|
||||
@@ -408,6 +408,7 @@ export const chartType = {
|
||||
table: 'table',
|
||||
stat: 'stat',
|
||||
gauge: 'gauge',
|
||||
sankey: 'sankey',
|
||||
pie: 'pie',
|
||||
bubble: 'bubble',
|
||||
treemap: 'treemap',
|
||||
|
||||
@@ -340,6 +340,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="form-items--half-width-group" v-if="isGauge(chartConfig.type)">
|
||||
<!--min-->
|
||||
<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"/>
|
||||
</el-form-item>
|
||||
</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)">
|
||||
<!--decimals-->
|
||||
<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 draggable from 'vuedraggable'
|
||||
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 {
|
||||
name: 'chartConfig',
|
||||
@@ -969,6 +1004,10 @@ export default {
|
||||
id: 'gauge',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
|
||||
},
|
||||
{
|
||||
id: 'sankey',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
|
||||
},
|
||||
{
|
||||
id: 'treemap',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
|
||||
@@ -995,6 +1034,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
isGauge,
|
||||
isSankey,
|
||||
beforeInit () {
|
||||
this.promqlType = this.type
|
||||
this.chartTypeList = this[this.type + 'ChartTypeList']
|
||||
@@ -1017,6 +1057,18 @@ export default {
|
||||
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) {
|
||||
switch (type) {
|
||||
case 'line':
|
||||
@@ -1053,8 +1105,9 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
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
|
||||
}
|
||||
this.chartConfig.param = {
|
||||
|
||||
@@ -74,6 +74,7 @@ export default {
|
||||
case 'gauge':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
case 'sankey':
|
||||
return true
|
||||
default: return false
|
||||
}
|
||||
@@ -91,6 +92,7 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
case 'bubble':
|
||||
return false
|
||||
default: return false
|
||||
@@ -106,6 +108,7 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
@@ -134,6 +137,7 @@ export default {
|
||||
case 'hexagon':
|
||||
case 'bar':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
case 'treemap':
|
||||
case 'pie':
|
||||
case 'bubble':
|
||||
@@ -151,6 +155,7 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
return false
|
||||
case 'line':
|
||||
case 'area':
|
||||
@@ -169,6 +174,7 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
return true
|
||||
case 'line':
|
||||
case 'area':
|
||||
@@ -201,6 +207,7 @@ export default {
|
||||
case 'stat':
|
||||
case 'hexagon':
|
||||
case 'gauge':
|
||||
case 'sankey':
|
||||
return true
|
||||
default: return false
|
||||
}
|
||||
|
||||
@@ -247,6 +247,10 @@ export default {
|
||||
id: 'gauge',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
|
||||
},
|
||||
{
|
||||
id: 'sankey',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
|
||||
},
|
||||
{
|
||||
id: 'treemap',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
|
||||
@@ -294,6 +298,10 @@ export default {
|
||||
id: 'gauge',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
|
||||
},
|
||||
{
|
||||
id: 'sankey',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
|
||||
},
|
||||
{
|
||||
id: 'treemap',
|
||||
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
|
||||
|
||||
Reference in New Issue
Block a user