左边饼图是echarts完成,右边的图例是div写的,因为图例的样式没法用legend的样式完成到这样
主页面Html:
<div class="chartHeight" style="position: relative;">
<pieChartContract :opt="contractStatics" />
<div class="contractLegend">
<div v-for="item,index in contractStatics.seriesData" :key="index" class="contractLegendItem" :style="'border-left: 1px dotted '+ color[index]">
<div class="dashed" :style="'background:' + color[index]"></div>
<div>{{ item.name }}</div>
<div class="contractLegendData" :style="'color:'+ color[index]">{{ item.value }}亿元 30%</div>
</div>
</div>
</div>
.chartHeight{
width: 100%;
height: 220px;
}
.contractLegend{
position: absolute;
left: 60%;
top: 50%;
transform: translateY(-50%);
}
.contractLegendItem{
font-size: 14px;
margin-bottom: 20px;
color: #595D64;
text-indent: 10px;
position: relative;
justify-content: flex-start;
}
.contractLegendItem:last-child{
margin-bottom: 0;
}
.contractLegendItem .dashed{
content: "";
position: absolute;
top: 0;
left: -2px;
width: 3px;
height: 10px;
opacity: 1;
border-radius: 5px;
}
.contractLegendData{
font-size: 16px;
text-align: left;
margin-top: 6px;
}
主页面js:
data() {
return {
contractStatics: {
seriesData: [{
name: '技术服务类',
value: 30.5,
itemStyle: {
color: '#3562D4',
},
},
{
name: '监理监测类',
value: 41.4,
itemStyle: {
color: '#2BC4CD',
},
},
{
name: '工程总承包类',
value: 21.1,
itemStyle: {
color: '#E68B29',
},
}],
},
}
}
import pieChartContract from "./components/pieChartContract";
pieChartContract.vue组件全部:
<template>
<div style="width: 100%;height: 100%;">
<ChartPanel ref="chart" :option="options" :style="opt.yAxisName ? 'height:calc(100% - 16px)' : ''"></ChartPanel>
</div>
</template>
<script>
import * as echarts from 'echarts'
import ChartPanel from '@/components/ChartPanel';
export default {
components: {
ChartPanel
},
props: {
opt: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
options: null
}
},
watch: {
opt: {
deep: true,
immediate: true,
handler(val) {
this.getOpt(val)
}
}
},
methods: {
getOpt(val) {
let {
seriesData,
} = val
let seriesData1 = []
let seriesData2 = []
let seriesData3 = []
let color = ['#3666E0', '#26D7E0', '#F0922E']
seriesData.forEach((item, index) => {
seriesData1.push({
...item,
itemStyle: {
color: color[index],
opacity: 1
}
})
seriesData2.push({
...item,
itemStyle: {
color: color[index],
opacity: 0.4
}
})
seriesData3.push({
...item,
itemStyle: {
color: color[index],
opacity: 0.1
}
})
})
this.options = {
legend: {
orient: 'vertical',
show: false,
left: '60%',
y: 'center',
itemWidth: 1,
itemHeight: 32,
itemGap: 20,
textStyle: {
color: '#595D64',
fontSize: 14,
lineHeight: 20,
rich: {
percent0: {
color: color[0],
fontSize: 16,
},
percent1: {
color: color[1],
fontSize: 16,
},
percent2: {
color: color[2],
fontSize: 16,
}
},
},
formatter: name => {
let dataIndex = 0
let obj = (seriesData.filter((item, index) => {
if (item.name == name) {
dataIndex = index
return item
}
}))[0]
return obj.name + '\r\n{percent' + dataIndex + '|' + obj.value + '亿元} ' +
' {percent' + dataIndex + '|30%}'
},
},
tooltip: {
show: true,
borderWidth: 0,
formatter: params => {
let result = `<div style="font-weight:700">${params.name}</div>
<div>
<span style="color:${params.color};font-weight:700">${params.value}亿元 ${params.percent}%</span>
</div>
`
return result
}
},
series: [
{
type: 'pie',
radius: ['45%', '70%'],
center: ['25%', '50%'],
hoverAnimation: false,
z: 10,
label: {
show: false,
},
data: seriesData1,
labelLine: {
show: false,
},
},
{
type: 'pie',
radius: ['35%', '45%'],
center: ['25%', '50%'],
hoverAnimation: false,
label: {
show: false,
},
data: seriesData2,
labelLine: {
show: false,
},
},
{
type: 'pie',
radius: ['29%', '35%'],
center: ['25%', '50%'],
hoverAnimation: false,
label: {
show: false,
},
data: seriesData3,
labelLine: {
show: false,
},
},
],
};
this.$nextTick(() => {
this.$refs.chart.initChart(echarts, chart => {
this.options && chart.setOption(this.options, true);
});
})
}
}
}
</script>
ChartPanel.vue 对echarts组件的二次封装
<template>
<div ref="chartPanel" class="chart-panel" :class="className" :style="{ height: height, width: width }" />
</template>
<script>
import * as echarts from 'echarts'
require('echarts/theme/macarons')
import { debounce } from '@/utils'
export default {
name: 'ChartPanel',
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '100%'
},
mini: {
type: Boolean,
default: false
},
autoResize: {
type: Boolean,
default: true
},
option: {
type: Object,
required: true
},
initName: {
type: String,
default() { return '' }
},
initMap: {
type: Object,
default: null
}
},
data() {
return {
chart: null,
timer: null
}
},
watch: {
option: {
deep: true,
handler(val) {
if (this.initName && this.initMap) {
this.initChart()
} else {
if (this.chart) {
this.chart.clear()
const that = this
that.timer = setTimeout(() => {
that.chart.setOption(val, true)
}, 500)
}
}
}
},
initName: {
deep: true,
handler(val) {
this.initChart()
this.resize()
}
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
if (this.autoResize) {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
}
this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
if (this.autoResize) {
window.removeEventListener('resize', this.__resizeHandler)
}
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
this.chart.dispose()
this.chart = null
clearTimeout(this.timer)
},
methods: {
sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}
},
initChart() {
this.chart = echarts.init(this.$el)
if (this.initName && this.initMap) {
echarts.registerMap(this.initName, this.initMap)
}
if (this.option != null) {
this.chart.clear()
const that = this
this.timer = setTimeout(() => {
that.chart.setOption(that.option, true)
}, 500)
}
this.$emit('initChart', this.chart)
},
resize() {
if (this.chart != null) {
this.chart.resize()
}
}
}
}
</script>
debounce方法:
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result;
const later = function() {
const last = +new Date() - timestamp;
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function(...args) {
context = this;
timestamp = +new Date();
const callNow = immediate && !timeout;
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
}