NEZ-2520 feat:新增漏斗图图表类型

This commit is contained in:
zyh
2023-02-02 10:00:21 +08:00
parent 0dde0b0458
commit 8c7ee1b620
19 changed files with 2127 additions and 174 deletions

View File

@@ -647,3 +647,9 @@
.tickLineFirst{
stroke: $--color-text-primary;;
}
.text-ellipsis{
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

View File

@@ -134,15 +134,17 @@ export default {
case 'table' :
case 'stat' :
case 'gauge' :
case 'sankey' :
case 'pie' :
case 'bubble' :
case 'treemap' :
case 'log' :
case 'hexagon' :
case 'diagram' :
case 'url':
case 'clock':
case 'bubble' :
case 'rank' :
case 'sankey' :
case 'funnel' :
return true
default: return false
}

View File

@@ -26,24 +26,6 @@
: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-rank
:ref="'chart' + chartInfo.id"
v-if="isChartRank(chartInfo.type)"
:chart-data="chartData"
:chart-info="chartInfo"
:chart-option="chartOption"
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-rank>
<chart-bar
:ref="'chart' + chartInfo.id"
v-if="isChartBar(chartInfo.type)"
@@ -117,15 +99,6 @@
: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)"
@@ -210,6 +183,42 @@
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-topology>
<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-rank
:ref="'chart' + chartInfo.id"
v-if="isChartRank(chartInfo.type)"
:chart-data="chartData"
:chart-info="chartInfo"
:chart-option="chartOption"
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-rank>
<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-funnel
:ref="'chart' + chartInfo.id"
v-if="isFunnel(chartInfo.type)"
:chart-data="chartData"
:chart-info="chartInfo"
:chart-option="chartOption"
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-funnel>
</template>
</div>
</template>
@@ -223,13 +232,10 @@ 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'
import chartPie from './chart/chartPie'
import chartBubble from './chart/chartBubble'
import chartRank from './chart/chartRank'
import chartStat from './chart/chartStat'
import chartTable from './chart/chartTable'
import chartText from './chart/chartText'
@@ -240,7 +246,11 @@ 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, isChartRank, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isSankey, isClock, isTopology } from './chart/tools'
import chartBubble from './chart/chartBubble'
import chartRank from './chart/chartRank'
import chartSankey from './chart/chartSankey'
import chartFunnel from './chart/chartFunnel'
import { getOption, isTimeSeries, isHexagon, isUrl, isText, isChartPie, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isClock, isTopology, isChartBubble, isChartRank, isSankey, isFunnel } from './chart/tools'
import lodash from 'lodash'
export default {
@@ -254,13 +264,10 @@ export default {
chartDiagram,
chartEndpointInfo,
chartGauge,
chartSankey,
chartGroup,
chartLog,
chartNoData,
chartPie,
chartBubble,
chartRank,
chartStat,
chartTable,
chartText,
@@ -270,7 +277,11 @@ export default {
chartValue,
chartHexagonD3,
chartMap,
chartTopology
chartTopology,
chartBubble,
chartRank,
chartSankey,
chartFunnel
},
props: {
chartInfo: Object,
@@ -328,8 +339,6 @@ export default {
isTimeSeries,
isHexagon,
isChartPie,
isChartBubble,
isChartRank,
isChartBar,
isUrl,
isText,
@@ -344,9 +353,12 @@ export default {
isMap,
isTable,
isGauge,
isSankey,
isClock,
isTopology,
isChartBubble,
isChartRank,
isSankey,
isFunnel,
chartIsNoData (flag) {
this.chartChildrenData = flag
},

View File

@@ -173,7 +173,10 @@ export default {
return d.y - d.r
})
.style('font-size', function (d) {
return d.r / 3 > 10 ? d.r / 3 : 0
let fontSize
fontSize = d.r / 4 > 10 ? d.r / 4 : 0
fontSize = fontSize > 30 ? 30 : fontSize
return fontSize
})
.style('border-radius', '50%')
.html((d) => {

View File

@@ -0,0 +1,240 @@
<template>
<div
ref="funnel-chart-box"
class="nz-chart__component"
>
<div :id="`chart-canvas-${chartId}`" class="chart__canvas" style="display:flex;justify-content: center;align-items: center;"></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 D3Funnel from './d3-funnel/index.js'
import chartMixin from '@/components/chart/chartMixin'
import chartFormat from '@/components/chart/chartFormat'
import { getMetricTypeValue } from '@/components/common/js/tools'
import chartDataFormat from '@/components/chart/chartDataFormat'
import { initColor } from '@/components/chart/chart/tools'
import { getChart, setChart } from '@/components/common/js/common'
import lodash from 'lodash'
export default {
name: 'chart-funnel',
components: {
},
mixins: [chartMixin, chartFormat],
props: {
chartInfo: Object,
chartData: Array,
chartOption: Object,
isFullscreen: Boolean
},
computed: {
},
data () {
return {
colorList: [],
isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false
chartId: '',
funnelData: [],
tooltip: {
x: 0,
y: 0,
title: 0,
value: 0,
mapping: {},
show: false
}
}
},
methods: {
initChart (customAnimate) {
this.legends = []
this.initData(this.chartInfo, this.chartData)
if (this.isNoData) {
return
}
/* 使用setTimeout延迟渲染图表避免样式错乱 */
setTimeout(() => {
this.drawChart(customAnimate)
}, 200)
},
initData (chartInfo, originalDatas) {
this.funnelData = []
let colorIndex = 0
const decimals = this.chartInfo.param.decimals || 2
this.isNoData = true
originalDatas.forEach((originalData, expressionIndex) => {
originalData.forEach((data, dataIndex) => {
this.isNoData = false
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)
const legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
this.funnelData.push({
value: Number(value),
realValue: value,
showValue: showValue,
name: legend.name,
alias: legend.alias,
labels: {
...data.metric,
legend: legend.alias
},
seriesIndex: expressionIndex,
dataIndex: dataIndex,
mapping: mapping,
backgroundColor: mapping ? mapping.color.bac.substr(0, 7) : this.colorList[colorIndex], // 仅限十六进制
label: legend.alias
})
colorIndex++
})
})
this.$emit('chartIsNoData', this.isNoData)
},
drawChart (customAnimate) {
this.$nextTick(() => {
const options = {
chart: {
width: '85%',
height: '85%',
bottomPinch: this.funnelData.length > 1 ? 1 : 0,
customAnimate: customAnimate === true || this.isInit
},
block: {
dynamicHeight: true,
highlight: true
},
label: {
format: this.formatterLabel
},
events: {
mouseenter: {
block: this.chartEnter
},
mousemove: {
block: this.chartMove
},
mouseleave: {
block: this.chartLeave
}
}
}
const myChart = new D3Funnel(document.getElementById(`chart-canvas-${this.chartId}`))
const funnelData = lodash.cloneDeep(this.funnelData)
myChart.draw(funnelData, options)
setChart(this.chartId, myChart)
this.isInit = false
})
},
// 处理label
formatterLabel (label, value, e, data) {
let str = ''
let valueStr = ''
if (this.chartInfo.param.text === 'all') {
str += data.alias
valueStr = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { ...data.labels, 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, { ...data.labels, value: data.showValue }) : data.showValue
}
if (this.chartInfo.param.text === 'legend') {
str += data.alias
}
if (this.chartInfo.param.text === 'none') {
str += ''
}
if (str && valueStr) {
return `<div style="width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;color:#000000">
<p class='text-ellipsis' style="width: 80%;text-align:center;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<span>${str}</span>
</p>
<p class='text-ellipsis' style="width: 80%;text-align:center;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:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;color:#000000">
<p class='text-ellipsis' style="width: 80%;text-align:center;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:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;color:#000000">
<p class='text-ellipsis' style="width: 80%;text-align:center;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.drawChart()
}, 50)
},
chartEnter (e, node) {
this.tooltip.title = node.data.alias
this.tooltip.value = node.data.showValue
this.tooltip.mapping = node.data.mapping
this.tooltip.show = true
this.setPosition(e)
},
chartMove (e) {
this.tooltip.show = true
this.setPosition(e)
},
chartLeave () {
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
}
}
},
mounted () {
this.colorList = initColor(20)
this.chartInfo.loaded && this.initChart(true)
},
beforeDestroy () {
getChart(this.chartId) && getChart(this.chartId).destroy()
}
}
</script>

View File

@@ -0,0 +1,177 @@
class Colorizer {
/**
* @return {void}
*/
constructor () {
this.hexExpression = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i
this.instanceId = null
this.labelFill = null
this.scale = null
}
/**
* @param {string} instanceId
*
* @return {void}
*/
setInstanceId (instanceId) {
this.instanceId = instanceId
}
/**
* @param {string} fill
*
* @return {void}
*/
setLabelFill (fill) {
this.labelFill = fill
}
/**
* @param {function|Array} scale
*
* @return {void}
*/
setScale (scale) {
this.scale = scale
}
/**
* Given a raw data block, return an appropriate color for the block.
*
* @param {string} fill
* @param {Number} index
* @param {string} fillType
*
* @return {Object}
*/
getBlockFill (fill, index, fillType) {
const raw = this.getBlockRawFill(fill, index)
return {
raw,
actual: this.getBlockActualFill(raw, index, fillType)
}
}
/**
* Return the raw hex color for the block.
*
* @param {string} fill
* @param {Number} index
*
* @return {string}
*/
getBlockRawFill (fill, index) {
// Use the block's color, if set and valid
if (this.hexExpression.test(fill)) {
return fill
}
// Otherwise, attempt to use the array scale
if (Array.isArray(this.scale)) {
return this.scale[index]
}
// Finally, use a functional scale
return this.scale(index)
}
/**
* Return the actual background for the block.
*
* @param {string} raw
* @param {Number} index
* @param {string} fillType
*
* @return {string}
*/
getBlockActualFill (raw, index, fillType) {
if (fillType === 'solid') {
return raw
}
return `url(#${this.getGradientId(index)})`
}
/**
* Return the gradient ID for the given index.
*
* @param {Number} index
*
* @return {string}
*/
getGradientId (index) {
return `${this.instanceId}-gradient-${index}`
}
/**
* Given a raw data block, return an appropriate label color.
*
* @param {string} labelFill
*
* @return {string}
*/
getLabelColor (labelFill) {
return this.hexExpression.test(labelFill) ? labelFill : this.labelFill
}
/**
* Shade a color to the given percentage.
*
* @param {string} color A hex color.
* @param {number} shade The shade adjustment. Can be positive or negative.
*
* @return {string}
*/
shade (color, shade) {
const { R, G, B } = this.hexToRgb(color)
const t = shade < 0 ? 0 : 255
const p = shade < 0 ? shade * -1 : shade
const converted = 0x1000000 +
((Math.round((t - R) * p) + R) * 0x10000) +
((Math.round((t - G) * p) + G) * 0x100) +
(Math.round((t - B) * p) + B)
return `#${converted.toString(16).slice(1)}`
}
/**
* Convert a hex color to an RGB object.
*
* @param {string} color
*
* @returns {{R: Number, G: number, B: number}}
*/
hexToRgb (color) {
let hex = color.slice(1)
if (hex.length === 3) {
hex = this.expandHex(hex)
}
const f = parseInt(hex, 16)
/* eslint-disable no-bitwise */
const R = f >> 16
const G = (f >> 8) & 0x00FF
const B = f & 0x0000FF
/* eslint-enable */
return { R, G, B }
}
/**
* Expands a three character hex code to six characters.
*
* @param {string} hex
*
* @return {string}
*/
expandHex (hex) {
return hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
}
}
export default Colorizer

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,75 @@
class Formatter {
/**
* Register the format function.
*
* @param {string|function} format
*
* @return {function}
*/
getFormatter (format) {
if (typeof format === 'function') {
return format
}
return (label, value, formattedValue) => (
this.stringFormatter(label, value, formattedValue, format)
)
}
/**
* Format the given value according to the data point or the format.
*
* @param {string} label
* @param {number} value
* @param {*} formattedValue
* @param {function} formatter
*
* @return string
*/
format ({ label, value, formattedValue = null }, formatter) {
return formatter(label, value, formattedValue, arguments[0])
}
/**
* Format the string according to a simple expression.
*
* {l}: label
* {v}: raw value
* {f}: formatted value
*
* @param {string} label
* @param {number} value
* @param {*} formattedValue
* @param {string} expression
*
* @return {string}
*/
stringFormatter (label, value, formattedValue, expression) {
let formatted = formattedValue
// Attempt to use supplied formatted value
// Otherwise, use the default
if (formattedValue === null) {
formatted = this.getDefaultFormattedValue(value)
}
return expression
.split('{l}')
.join(label)
.split('{v}')
.join(value)
.split('{f}')
.join(formatted)
}
/**
* @param {number} value
*
* @return {string}
*/
getDefaultFormattedValue (value) {
return value.toLocaleString()
}
}
export default Formatter

View File

@@ -0,0 +1,245 @@
class Navigator {
/**
* Given a list of path commands, returns the compiled description.
*
* @param {Array} commands
*
* @return {string}
*/
plot (commands) {
let path = ''
commands.forEach((command) => {
path += `${command[0]}${command[1]},${command[2]} `
})
return path.replace(/ +/g, ' ').trim()
}
/**
* @param {Object} dimensions
* @param {boolean} isValueOverlay
*
* @return {Array}
*/
makeCurvedPaths (dimensions, isValueOverlay = false) {
const points = this.makeBezierPoints(dimensions)
if (isValueOverlay) {
return this.makeBezierPath(points, dimensions.ratio)
}
return this.makeBezierPath(points)
}
/**
* @param {Number} centerX
* @param {Number} prevLeftX
* @param {Number} prevRightX
* @param {Number} prevHeight
* @param {Number} nextLeftX
* @param {Number} nextRightX
* @param {Number} nextHeight
* @param {Number} curveHeight
*
* @return {Object}
*/
makeBezierPoints ({
centerX,
prevLeftX,
prevRightX,
prevHeight,
nextLeftX,
nextRightX,
nextHeight,
curveHeight
}) {
return {
p00: {
x: prevLeftX,
y: prevHeight
},
p01: {
x: centerX,
y: prevHeight + (curveHeight / 2)
},
p02: {
x: prevRightX,
y: prevHeight
},
p10: {
x: nextLeftX,
y: nextHeight
},
p11: {
x: centerX,
y: nextHeight + curveHeight
},
p12: {
x: nextRightX,
y: nextHeight
}
}
}
/**
* @param {Object} p00
* @param {Object} p01
* @param {Object} p02
* @param {Object} p10
* @param {Object} p11
* @param {Object} p12
* @param {Number} ratio
*
* @return {Array}
*/
makeBezierPath ({
p00,
p01,
p02,
p10,
p11,
p12
}, ratio = 1) {
const curve0 = this.getQuadraticBezierCurve(p00, p01, p02, ratio)
const curve1 = this.getQuadraticBezierCurve(p10, p11, p12, ratio)
return [
// Top Bezier curve
[curve0.p0.x, curve0.p0.y, 'M'],
[curve0.p1.x, curve0.p1.y, 'Q'],
[curve0.p2.x, curve0.p2.y, ''],
// Right line
[curve1.p2.x, curve1.p2.y, 'L'],
// Bottom Bezier curve
[curve1.p2.x, curve1.p2.y, 'M'],
[curve1.p1.x, curve1.p1.y, 'Q'],
[curve1.p0.x, curve1.p0.y, ''],
// Left line
[curve0.p0.x, curve0.p0.y, 'L']
]
}
/**
* @param {Object} p0
* @param {Object} p1
* @param {Object} p2
* @param {Number} t
*
* @return {Object}
*/
getQuadraticBezierCurve (p0, p1, p2, t = 1) {
// Quadratic Bezier curve syntax: M(P0) Q(P1) P2
// Where P0, P2 are the curve endpoints and P1 is the control point
// More generally, at 0 <= t <= 1, we have the following:
// Q0(t), which varies linearly from P0 to P1
// Q1(t), which varies linearly from P1 to P2
// B(t), which is interpolated linearly between Q0(t) and Q1(t)
// For an intermediate curve at 0 <= t <= 1:
// P1(t) = Q0(t)
// P2(t) = B(t)
return {
p0,
p1: {
x: this.getLinearInterpolation(p0, p1, t, 'x'),
y: this.getLinearInterpolation(p0, p1, t, 'y')
},
p2: {
x: this.getQuadraticInterpolation(p0, p1, p2, t, 'x'),
y: this.getQuadraticInterpolation(p0, p1, p2, t, 'y')
}
}
}
/**
* @param {Object} p0
* @param {Object} p1
* @param {Number} t
* @param {string} axis
*
* @return {Number}
*/
getLinearInterpolation (p0, p1, t, axis) {
return p0[axis] + (t * (p1[axis] - p0[axis]))
}
/**
* @param {Object} p0
* @param {Object} p1
* @param {Object} p2
* @param {Number} t
* @param {string} axis
*
* @return {Number}
*/
getQuadraticInterpolation (p0, p1, p2, t, axis) {
return (((1 - t) ** 2) * p0[axis]) +
(2 * (1 - t) * t * p1[axis]) +
((t ** 2) * p2[axis])
}
/**
* @param {Number} prevLeftX
* @param {Number} prevRightX
* @param {Number} prevHeight
* @param {Number} nextLeftX
* @param {Number} nextRightX
* @param {Number} nextHeight
* @param {Number} ratio
* @param {boolean} isValueOverlay
*
* @return {Object}
*/
makeStraightPaths ({
prevLeftX,
prevRightX,
prevHeight,
nextLeftX,
nextRightX,
nextHeight,
ratio
}, isValueOverlay = false) {
if (isValueOverlay) {
const lengthTop = (prevRightX - prevLeftX)
const lengthBtm = (nextRightX - nextLeftX)
let rightSideTop = (lengthTop * (ratio || 0)) + prevLeftX
let rightSideBtm = (lengthBtm * (ratio || 0)) + nextLeftX
// Overlay should not be longer than the max length of the path
rightSideTop = Math.min(rightSideTop, lengthTop)
rightSideBtm = Math.min(rightSideBtm, lengthBtm)
return [
// Start position
[prevLeftX, prevHeight, 'M'],
// Move to right
[rightSideTop, prevHeight, 'L'],
// Move down
[rightSideBtm, nextHeight, 'L'],
// Move to left
[nextLeftX, nextHeight, 'L'],
// Wrap back to top
[prevLeftX, prevHeight, 'L']
]
}
return [
// Start position
[prevLeftX, prevHeight, 'M'],
// Move to right
[prevRightX, prevHeight, 'L'],
// Move down
[nextRightX, nextHeight, 'L'],
// Move to left
[nextLeftX, nextHeight, 'L'],
// Wrap back to top
[prevLeftX, prevHeight, 'L']
]
}
}
export default Navigator

View File

@@ -0,0 +1,78 @@
class Utils {
/**
* Determine whether the given parameter is an extendable object.
*
* @param {*} a
*
* @return {boolean}
*/
static isExtendableObject (a) {
return typeof a === 'object' && a !== null && !Array.isArray(a)
}
/**
* Extends an object with the members of another.
*
* @param {Object} a The object to be extended.
* @param {Object} b The object to clone from.
*
* @return {Object}
*/
static extend (a, b) {
let result = {}
// If a is non-trivial, extend the result with it
if (Object.keys(a).length > 0) {
result = Utils.extend({}, a)
}
// Copy over the properties in b into a
Object.keys(b).forEach((prop) => {
if (Utils.isExtendableObject(b[prop])) {
if (Utils.isExtendableObject(a[prop])) {
result[prop] = Utils.extend(a[prop], b[prop])
} else {
result[prop] = Utils.extend({}, b[prop])
}
} else {
result[prop] = b[prop]
}
})
return result
}
/**
* Convert the legacy block array to a block object.
*
* @param {Array} block
*
* @returns {Object}
*/
static convertLegacyBlock (block) {
return {
label: block[0],
value: Utils.getRawBlockCount(block),
formattedValue: Array.isArray(block[1]) ? block[1][1] : null,
backgroundColor: block[2],
labelColor: block[3]
}
}
/**
* Given a raw data block, return its count.
*
* @param {Array} block
*
* @return {Number}
*/
static getRawBlockCount (block) {
if (Array.isArray(block)) {
return Array.isArray(block[1]) ? block[1][0] : block[1]
}
return block.value
}
}
export default Utils

View File

@@ -0,0 +1,2 @@
// Export default to provide support for non-ES6 solutions
module.exports = require('./d3-funnel/D3Funnel').default

View File

@@ -130,7 +130,8 @@ export default {
if (isTimeSeries(this.chartInfo.type) && (this.isConnect && this.isConnect !== 'none')) {
chart.connect('timeSeriesGroup')
}
if (this.chartInfo.type !== 'pie' && this.chartInfo.type !== 'bar' && this.chartInfo.type !== 'treemap') {
if (this.chartInfo.type == 'line' || this.chartInfo.type == 'area' || this.chartInfo.type == 'point') {
if (this.chartInfo.param.min || this.chartInfo.param.max) {
return
}

View File

@@ -67,26 +67,18 @@ export function getOption (type) {
export function isTimeSeries (type) {
return type === chartType.line || type === chartType.area || type === chartType.point
}
export function isHexagon (type) {
return type === chartType.hexagon
}
export function isChartPie (type) {
return type === chartType.pie
}
export function isChartBubble (type) {
return type === chartType.bubble
}
export function isChartRank (type) {
return type === chartType.rank
}
export function isChartBar (type) {
return type === chartType.bar
}
export function isUrl (type) {
return type === chartType.url
}
export function isText (type) {
return type === chartType.text
}
@@ -123,15 +115,25 @@ 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
}
export function isTopology (type) {
return type === chartType.topologyLink
}
export function isChartBubble (type) {
return type === chartType.bubble
}
export function isChartRank (type) {
return type === chartType.rank
}
export function isSankey (type) {
return type === chartType.sankey
}
export function isFunnel (type) {
return type === chartType.funnel
}
export function getGroupHeight (arr) {
if (arr.length) {
let lastItem = []

View File

@@ -23,7 +23,7 @@ export function getChart (key) {
}
export function setChart (key, value) {
chartCache[`chart${key}`] && chartCache[`chart${key}`].dispose()
chartCache[`chart${key}`] && chartCache[`chart${key}`].dispose && chartCache[`chart${key}`].dispose()
chartCache[`chart${key}`] = value
}
const hexagonCache = {}

View File

@@ -263,10 +263,6 @@ export const chart = {
value: 'gauge',
label: i18n.t('dashboard.panel.chartForm.typeVal.gauge.label')
},
{
value: 'sankey',
label: i18n.t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
value: 'hexagon',
label: i18n.t('dashboard.panel.chartForm.typeVal.hexagonFigure.label')
@@ -307,7 +303,6 @@ export const chart = {
value: 'group',
label: i18n.t('dashboard.panel.chartForm.group')
},
{
value: 'logs',
label: i18n.t('overall.logs')
@@ -324,14 +319,6 @@ export const chart = {
value: 'pie',
label: i18n.t('dashboard.panel.chartForm.typeVal.pie.label')
},
{
value: 'bubble',
label: i18n.t('dashboard.panel.chartForm.typeVal.bubble.label')
},
{
value: 'rank',
label: i18n.t('dashboard.panel.chartForm.typeVal.rank.label')
},
{
value: 'log',
label: i18n.t('dashboard.panel.chartForm.typeVal.log.label')
@@ -351,6 +338,22 @@ export const chart = {
{
value: 'clock',
label: i18n.t('dashboard.panel.chartForm.typeVal.clock.label')
},
{
value: 'bubble',
label: i18n.t('dashboard.panel.chartForm.typeVal.bubble.label')
},
{
value: 'rank',
label: i18n.t('dashboard.panel.chartForm.typeVal.rank.label')
},
{
value: 'sankey',
label: i18n.t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
value: 'funnel',
label: i18n.t('dashboard.panel.chartForm.typeVal.funnel.label')
}
]
}
@@ -471,10 +474,7 @@ export const chartType = {
table: 'table',
stat: 'stat',
gauge: 'gauge',
sankey: 'sankey',
pie: 'pie',
bubble: 'bubble',
rank: 'rank',
treemap: 'treemap',
log: 'log',
text: 'text',
@@ -487,7 +487,11 @@ export const chartType = {
topology: 'topology',
map: 'map',
hexagon: 'hexagon',
topologyLink: 'topologyLink'
topologyLink: 'topologyLink',
bubble: 'bubble',
rank: 'rank',
sankey: 'sankey',
funnel: 'funnel'
}
export const chartLegendPlacement = {

View File

@@ -319,7 +319,7 @@
<el-form-item
:label="$t('config.assetLabel.display')"
class="form-item--half-width"
prop="param.nullType"
prop="param.text"
v-if="isShowText(chartConfig.type)"
>
<el-select
@@ -983,65 +983,7 @@ export default {
name: this.$t('overall.hidden')
}
],
chartTypeList: [
{
id: 'line',
name: this.$t('dashboard.panel.chartForm.typeVal.line.label')
},
{
id: 'area',
name: this.$t('dashboard.panel.chartForm.typeVal.stackArea.label')
},
{
id: 'point',
name: this.$t('dashboard.panel.chartForm.typeVal.point.label')
},
{
id: 'stat',
name: this.$t('dashboard.panel.chartForm.typeVal.singleStat.label')
},
{
id: 'hexagon',
name: this.$t('dashboard.panel.chartForm.typeVal.hexagonFigure.label')
},
{
id: 'bar',
name: this.$t('dashboard.panel.chartForm.typeVal.bar.label')
},
{
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')
},
{
id: 'pie',
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
},
{
id: 'bubble',
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
},
{
id: 'rank',
name: this.$t('dashboard.panel.chartForm.typeVal.rank.label')
},
{
id: 'log',
name: this.$t('dashboard.panel.chartForm.typeVal.log.label')
},
{
id: 'table',
name: this.$t('dashboard.panel.chartForm.typeVal.table.label')
}
]
chartTypeList: []
}
},
methods: {
@@ -1119,7 +1061,8 @@ export default {
case 'sankey':
case 'bubble':
case 'rank':
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'sankey' || this.oldType === 'hexagon' || this.oldType === 'bubble' || this.oldType === 'rank') {
case 'funnel':
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'sankey' || this.oldType === 'hexagon' || this.oldType === 'bubble' || this.oldType === 'rank' || this.oldType === 'funnel') {
break
}
this.chartConfig.param = {

View File

@@ -55,8 +55,6 @@ export default {
case 'treemap':
case 'gauge':
case 'pie':
case 'bubble':
case 'rank':
return false
default: return false
}
@@ -77,6 +75,7 @@ export default {
case 'bubble':
case 'rank':
case 'sankey':
case 'funnel':
return true
default: return false
}
@@ -94,9 +93,6 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
case 'bubble':
case 'rank':
return false
default: return false
}
@@ -111,11 +107,8 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
case 'treemap':
case 'pie':
case 'bubble':
case 'rank':
case 'bar':
return false
default: return false
@@ -141,11 +134,12 @@ export default {
case 'hexagon':
case 'bar':
case 'gauge':
case 'sankey':
case 'treemap':
case 'pie':
case 'bubble':
case 'rank':
case 'sankey':
case 'funnel':
return true
default: return false
}
@@ -156,12 +150,9 @@ export default {
case 'bar':
case 'treemap':
case 'pie':
case 'bubble':
case 'rank':
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
return false
case 'line':
case 'area':
@@ -176,12 +167,13 @@ export default {
case 'bar':
case 'treemap':
case 'pie':
case 'bubble':
case 'rank':
case 'stat':
case 'hexagon':
case 'gauge':
case 'bubble':
case 'rank':
case 'sankey':
case 'funnel':
return true
case 'line':
case 'area':
@@ -210,12 +202,13 @@ export default {
case 'bar':
case 'treemap':
case 'pie':
case 'bubble':
case 'rank':
case 'stat':
case 'hexagon':
case 'gauge':
case 'bubble':
case 'rank':
case 'sankey':
case 'funnel':
return true
default: return false
}

View File

@@ -243,18 +243,6 @@ export default {
id: 'bar',
name: this.$t('dashboard.panel.chartForm.typeVal.bar.label')
},
{
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')
},
{
id: 'pie',
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
@@ -263,10 +251,26 @@ export default {
id: 'bubble',
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
},
{
id: 'funnel',
name: this.$t('dashboard.panel.chartForm.typeVal.funnel.label')
},
{
id: 'rank',
name: this.$t('dashboard.panel.chartForm.typeVal.rank.label')
},
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
},
{
id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
},
{
id: 'table',
name: this.$t('dashboard.panel.chartForm.typeVal.table.label')
@@ -298,18 +302,6 @@ export default {
id: 'bar',
name: this.$t('dashboard.panel.chartForm.typeVal.bar.label')
},
{
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')
},
{
id: 'pie',
name: this.$t('dashboard.panel.chartForm.typeVal.pie.label')
@@ -318,10 +310,26 @@ export default {
id: 'bubble',
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
},
{
id: 'funnel',
name: this.$t('dashboard.panel.chartForm.typeVal.funnel.label')
},
{
id: 'rank',
name: this.$t('dashboard.panel.chartForm.typeVal.rank.label')
},
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
},
{
id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
},
{
id: 'log',
name: this.$t('dashboard.panel.chartForm.typeVal.log.label')

View File

@@ -294,7 +294,7 @@
<el-form-item
:label="$t('config.assetLabel.display')"
class="form-item--half-width"
prop="param.nullType"
prop="param.text"
v-if="isShowText(chartConfig.type)"
>
<el-select
@@ -894,6 +894,10 @@ export default {
id: 'bubble',
name: this.$t('dashboard.panel.chartForm.typeVal.bubble.label')
},
{
id: 'funnel',
name: this.$t('dashboard.panel.chartForm.typeVal.funnel.label')
},
{
id: 'rank',
name: this.$t('dashboard.panel.chartForm.typeVal.rank.label')
@@ -966,7 +970,8 @@ export default {
case 'gauge':
case 'bubble':
case 'rank':
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon' || this.oldType === 'bubble' || this.oldType === 'rank') {
case 'funnel':
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon' || this.oldType === 'bubble' || this.oldType === 'rank' || this.oldType === 'funnel') {
break
}
this.chartConfig.param = {