NEZ-2064 feat : 通过d3js 实现 pie chart
This commit is contained in:
@@ -1,6 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div
|
||||||
|
:class="legendPlacement"
|
||||||
|
ref="pie-chart-box"
|
||||||
|
class="nz-chart__component nz-chart__component--time-series">
|
||||||
<div :ref="`chart-canvas-${chartId}`" style="height: 100%; width: 100%">
|
<div :ref="`chart-canvas-${chartId}`" style="height: 100%; width: 100%">
|
||||||
|
<div style="height: 85%; width: 100%">
|
||||||
<div :id="`chart-canvas-${chartId}`" class="chart__canvas chart-svg"></div>
|
<div :id="`chart-canvas-${chartId}`" class="chart__canvas chart-svg"></div>
|
||||||
|
</div>
|
||||||
|
<chart-legend
|
||||||
|
v-if="hasLegend"
|
||||||
|
:chart-data="chartData"
|
||||||
|
:chart-info="chartInfo"
|
||||||
|
:legends="dataset"
|
||||||
|
:is-fullscreen="isFullscreen"
|
||||||
|
@pieData="pieData"
|
||||||
|
></chart-legend>
|
||||||
<div
|
<div
|
||||||
:class="`chart-canvas-tooltip-${chartId}`"
|
:class="`chart-canvas-tooltip-${chartId}`"
|
||||||
:id="`chart-canvas-tooltip-${chartId}`"
|
:id="`chart-canvas-tooltip-${chartId}`"
|
||||||
@@ -28,6 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -38,6 +53,8 @@ import chartMixin from '@/components/chart/chartMixin'
|
|||||||
import chartDataFormat from '@/components/chart/chartDataFormat'
|
import chartDataFormat from '@/components/chart/chartDataFormat'
|
||||||
import { getMetricTypeValue } from '@/components/common/js/tools'
|
import { getMetricTypeValue } from '@/components/common/js/tools'
|
||||||
import { initColor } from '@/components/chart/chart/tools'
|
import { initColor } from '@/components/chart/chart/tools'
|
||||||
|
import legend from '@/components/chart/chart/legend'
|
||||||
|
import { chartLegendPlacement } from '@/components/common/js/constants'
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -65,6 +82,9 @@ export default {
|
|||||||
chartOption: Object,
|
chartOption: Object,
|
||||||
isFullscreen: Boolean
|
isFullscreen: Boolean
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
chartLegend: legend
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.colorList = initColor(20)
|
this.colorList = initColor(20)
|
||||||
this.initChart()
|
this.initChart()
|
||||||
@@ -82,7 +102,7 @@ export default {
|
|||||||
this.chartOption.series[0],
|
this.chartOption.series[0],
|
||||||
this.chartData
|
this.chartData
|
||||||
)[0].data
|
)[0].data
|
||||||
this.drawArcPie()
|
this.drawArcPie(this.dataset)
|
||||||
}, 200)
|
}, 200)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -100,11 +120,15 @@ export default {
|
|||||||
const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
|
const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
|
||||||
const showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(value, null, -1, decimals)
|
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 mapping = this.selectMapping(value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
|
||||||
|
const type = 'pie'
|
||||||
|
const color = this.colorList[colorIndex]
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
// eslint-disable-next-line vue/no-mutating-props
|
||||||
mapping && this.chartOption.color && (this.chartOption.color[colorIndex] = mapping.color.bac)
|
mapping && this.chartOption.color && (this.chartOption.color[colorIndex] = mapping.color.bac)
|
||||||
const legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
|
const legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
|
||||||
s.data.push({
|
s.data.push({
|
||||||
value: value,
|
value: value,
|
||||||
|
type: type,
|
||||||
|
color: color,
|
||||||
realValue: value,
|
realValue: value,
|
||||||
showValue: showValue,
|
showValue: showValue,
|
||||||
name: legend.name,
|
name: legend.name,
|
||||||
@@ -128,7 +152,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 绘制饼图
|
// 绘制饼图
|
||||||
drawArcPie () {
|
drawArcPie (data) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
d3.select(`#chart-canvas-${this.chartId}`).selectAll('svg').remove()// 清空作图区域
|
d3.select(`#chart-canvas-${this.chartId}`).selectAll('svg').remove()// 清空作图区域
|
||||||
// 重新赋值宽高
|
// 重新赋值宽高
|
||||||
@@ -139,7 +163,7 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
}
|
}
|
||||||
// 如果数据全为0 则设置默认值(否则图表不显示)
|
// 如果数据全为0 则设置默认值(否则图表不显示)
|
||||||
let dataset = lodash.cloneDeep(this.dataset)
|
let dataset = lodash.cloneDeep(data)
|
||||||
if (dataset.every(item => item.value == 0)) {
|
if (dataset.every(item => item.value == 0)) {
|
||||||
dataset = dataset.map(item => {
|
dataset = dataset.map(item => {
|
||||||
return {
|
return {
|
||||||
@@ -159,7 +183,7 @@ export default {
|
|||||||
const pie = d3.pie().value(function (d) {
|
const pie = d3.pie().value(function (d) {
|
||||||
return d.value
|
return d.value
|
||||||
})
|
})
|
||||||
const piedata = pie(this.dataset)
|
const piedata = pie(data)
|
||||||
const arc = d3
|
const arc = d3
|
||||||
.arc()
|
.arc()
|
||||||
.innerRadius(this.innerRadius)
|
.innerRadius(this.innerRadius)
|
||||||
@@ -184,34 +208,63 @@ export default {
|
|||||||
// 边界线
|
// 边界线
|
||||||
// .attr('stroke', 'white')
|
// .attr('stroke', 'white')
|
||||||
// 颜色
|
// 颜色
|
||||||
.attr('fill', (d, i) => this.colorList[i])
|
.attr('fill', (d, i) => {
|
||||||
|
if (d.data.color) {
|
||||||
|
return d.data.color
|
||||||
|
} else {
|
||||||
|
return this.colorList[i]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 文字
|
// 文字
|
||||||
arcs
|
arcs
|
||||||
.append('foreignObject')
|
.append('foreignObject')
|
||||||
.attr('y', d => { // y轴居中减文字大小的一半
|
.attr('y', d => { // y轴居中减文字大小的一半
|
||||||
const y = arc.centroid(d)[1]
|
const y = arc.centroid(d)[1]
|
||||||
return y - Math.sqrt(2) / 2 * (this.outerRadius / 2) + Math.floor(this.outerRadius / 18)
|
return y - Math.sqrt(2) / 2 * (this.outerRadius / 2) + Math.ceil(this.outerRadius / 16)
|
||||||
})
|
})
|
||||||
.attr('transform', (d, i) => {
|
.attr('transform', (d, i) => {
|
||||||
return 'translate(' + (arc.centroid(d)[0] - Math.abs(arc.centroid(d)[0]) - Math.floor(this.outerRadius / 18)) + ',' + (0) + ')' // x轴为最左侧 //x轴居中减文字大小的一半
|
return 'translate(' + (arc.centroid(d)[0] - Math.abs(arc.centroid(d)[0]) - Math.ceil(this.outerRadius / 16)) + ',' + (0) + ')' // x轴为最左侧 //x轴居中减文字大小的一半
|
||||||
})
|
})
|
||||||
.style('font-size', () => {
|
.style('font-size', (i) => {
|
||||||
return Math.floor(this.outerRadius / 9)
|
if (Math.ceil(this.outerRadius / 8) > 30) {
|
||||||
|
return 30
|
||||||
|
} else {
|
||||||
|
return Math.ceil(this.outerRadius / 8)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.attr('width', function (d, i) {
|
.attr('width', (d, i) => {
|
||||||
if ((d.endAngle - d.startAngle) < 0.25) {
|
if ((d.endAngle - d.startAngle) < 0.25) {
|
||||||
return 0
|
return 0
|
||||||
} else {
|
} else {
|
||||||
return Math.abs(arc.centroid(d)[0]) * 2
|
return Math.abs(arc.centroid(d)[0]) * 2
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr('height', function (d, i) {
|
.attr('height', (d, i) => {
|
||||||
return Math.abs(arc.centroid(d)[1])
|
if (Math.ceil(this.outerRadius / 8) > 30) {
|
||||||
|
return 90
|
||||||
|
} else {
|
||||||
|
return Math.ceil(this.outerRadius / 8) * 3
|
||||||
|
}
|
||||||
|
// return Math.abs(arc.centroid(d)[1]) - Math.ceil(this.outerRadius / 10)
|
||||||
})
|
})
|
||||||
// .attr('height', '20px')
|
.style('padding-left', () => {
|
||||||
.style('line-height', '20px')
|
if (this.isFullscreen) {
|
||||||
.style('text-align', 'center')
|
return 20
|
||||||
|
} else {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.style('padding-right', (d, i) => {
|
||||||
|
const path = document.getElementsByClassName('path')[i]
|
||||||
|
const w = path.getBoundingClientRect().width
|
||||||
|
if (((d.endAngle - d.startAngle) < 0.25) || (Math.abs(arc.centroid(d)[0]) * 2 < w / 2)) {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return (Math.abs(arc.centroid(d)[0]) * 2 - w / 3) / 2
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.style('line-height', '16px')
|
||||||
.html((d) => {
|
.html((d) => {
|
||||||
return this.drawText(d)
|
return this.drawText(d)
|
||||||
})
|
})
|
||||||
@@ -253,7 +306,7 @@ export default {
|
|||||||
if (str && valueStr) {
|
if (str && valueStr) {
|
||||||
return `
|
return `
|
||||||
<div style="width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;">
|
<div style="width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;">
|
||||||
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;">
|
<p style="width: 100%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;line-height: 150%">
|
||||||
<span>${str}</span>
|
<span>${str}</span>
|
||||||
</p>
|
</p>
|
||||||
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;">
|
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;">
|
||||||
@@ -264,14 +317,14 @@ export default {
|
|||||||
node.data.mapping.color &&
|
node.data.mapping.color &&
|
||||||
node.data.mapping.color.icon
|
node.data.mapping.color.icon
|
||||||
};font-size:1em;"></i>
|
};font-size:1em;"></i>
|
||||||
<span style="color:#000">${valueStr}</span>
|
<span>${valueStr}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
} else if (str) {
|
} else if (str) {
|
||||||
return `
|
return `
|
||||||
<div style="width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;">
|
<div style="color:#000;width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer">
|
||||||
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;">
|
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;line-height: 150%">
|
||||||
<i class="${
|
<i class="${
|
||||||
node.data.mapping && node.data.mapping.icon
|
node.data.mapping && node.data.mapping.icon
|
||||||
}" style="color: ${
|
}" style="color: ${
|
||||||
@@ -279,14 +332,14 @@ export default {
|
|||||||
node.data.mapping.color &&
|
node.data.mapping.color &&
|
||||||
node.data.mapping.color.icon
|
node.data.mapping.color.icon
|
||||||
};font-size:1em;"></i>
|
};font-size:1em;"></i>
|
||||||
<span style="color:#000">${str}</span>
|
<span style="color:#000;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;display:inline-block">${str}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
} else if (valueStr) {
|
} else if (valueStr) {
|
||||||
return `
|
return `
|
||||||
<div style="width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;">
|
<div style="width:100%;height: 100%;display: flex;align-items: center;justify-content: center;flex-direction: column;cursor: pointer;">
|
||||||
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;">
|
<p style="width: 80%;height: 100%;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;text-align:center;display: flex;align-items: center;justify-content: center;line-height: 150%">
|
||||||
<i class="${
|
<i class="${
|
||||||
node.data.mapping && node.data.mapping.icon
|
node.data.mapping && node.data.mapping.icon
|
||||||
}" style="color: ${
|
}" style="color: ${
|
||||||
@@ -294,7 +347,7 @@ export default {
|
|||||||
node.data.mapping.color &&
|
node.data.mapping.color &&
|
||||||
node.data.mapping.color.icon
|
node.data.mapping.color.icon
|
||||||
};font-size:1em;"></i>
|
};font-size:1em;"></i>
|
||||||
<span style="color:#000">${valueStr}</span>
|
<span style="color:#000;text-overflow: ellipsis;white-space: nowrap;overflow:hidden;display:inline-block">${valueStr}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -346,7 +399,7 @@ export default {
|
|||||||
},
|
},
|
||||||
resize () {
|
resize () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.drawArcPie()
|
this.drawArcPie(this.dataset)
|
||||||
}, 50)
|
}, 50)
|
||||||
},
|
},
|
||||||
clearCache () {
|
clearCache () {
|
||||||
@@ -356,10 +409,38 @@ export default {
|
|||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
this.clearCache()
|
this.clearCache()
|
||||||
|
},
|
||||||
|
hasLegend () {
|
||||||
|
try {
|
||||||
|
return [chartLegendPlacement.bottom, chartLegendPlacement.left, chartLegendPlacement.right].indexOf(this.chartInfo.param.legend.placement) > -1
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legendPlacement () {
|
||||||
|
try {
|
||||||
|
switch (this.chartInfo.param.legend.placement) {
|
||||||
|
case 'left':
|
||||||
|
case 'right':
|
||||||
|
case 'bottom': {
|
||||||
|
return `nz-chart__component--${this.chartInfo.param.legend.placement}`
|
||||||
|
}
|
||||||
|
default: return ''
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pieData (data) {
|
||||||
|
this.drawArcPie(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
|
.legend-container{
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
</style>>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
:class="{'row--inactive': isGrey[index]}"
|
:class="{'row--inactive': isGrey[index]}"
|
||||||
class="legend--table-row"
|
class="legend--table-row"
|
||||||
@click="clickLegend(item.name, index)"
|
@click="clickLegend(item.name, index,item)"
|
||||||
>
|
>
|
||||||
<div :title="item.alias ? item.alias : item.name" class="legend--table-cell">
|
<div :title="item.alias ? item.alias : item.name" class="legend--table-cell">
|
||||||
<span :style="{background: item.color}" class="legend-shape"></span>{{item.alias ? item.alias : item.name}}
|
<span :style="{background: item.color}" class="legend-shape"></span>{{item.alias ? item.alias : item.name}}
|
||||||
@@ -98,6 +98,10 @@ export default {
|
|||||||
this.clickLegendTreemap(legendName, index, hasGrey, curIsGrey, currentIsTheOnlyOneHighlight)
|
this.clickLegendTreemap(legendName, index, hasGrey, curIsGrey, currentIsTheOnlyOneHighlight)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (this.chartInfo.type === 'pie') {
|
||||||
|
this.clickLegendPie(legendName, index, hasGrey, curIsGrey, currentIsTheOnlyOneHighlight)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (echarts) {
|
if (echarts) {
|
||||||
// 判断timeSeries类型图表 先取消多表联动
|
// 判断timeSeries类型图表 先取消多表联动
|
||||||
@@ -161,6 +165,17 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
clickLegendPie (legendName, index, hasGrey, curIsGrey, currentIsTheOnlyOneHighlight) {
|
||||||
|
if (!hasGrey) { // 1.除当前legend外全置灰
|
||||||
|
this.isGrey = this.isGrey.map((g, i) => i !== index)
|
||||||
|
} else if (currentIsTheOnlyOneHighlight) { // 2.全高亮
|
||||||
|
this.isGrey = this.isGrey.map(() => false)
|
||||||
|
} else { // 对应高亮
|
||||||
|
this.$set(this.isGrey, index, !this.isGrey[index])
|
||||||
|
}
|
||||||
|
const data = this.legends.filter((item, i) => !this.isGrey[i])
|
||||||
|
this.$emit('pieData', data)
|
||||||
|
},
|
||||||
clickLegendBar (legendName, index, hasGrey, curIsGrey, currentIsTheOnlyOneHighlight) {
|
clickLegendBar (legendName, index, hasGrey, curIsGrey, currentIsTheOnlyOneHighlight) {
|
||||||
const echarts = getChart(this.chartId)
|
const echarts = getChart(this.chartId)
|
||||||
if (echarts) {
|
if (echarts) {
|
||||||
|
|||||||
Reference in New Issue
Block a user