You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.2 KiB
307 lines
9.2 KiB
|
6 months ago
|
<template>
|
||
|
|
<div class="chart-container" ref="chartRef" style="width: 450px; height: 400px;"></div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import * as echarts from 'echarts';
|
||
|
|
|
||
|
|
export default {
|
||
|
|
props: {
|
||
|
|
// 合并后的数据结构
|
||
|
|
chartData: {
|
||
|
|
type: Object,
|
||
|
|
required: true,
|
||
|
|
default: () => ({
|
||
|
|
level1: [{ value: 100, name: '防御材料' }],
|
||
|
|
level2: [
|
||
|
|
{ value: 20, name: '袋类' },
|
||
|
|
{ value: 45, name: '土工布类' },
|
||
|
|
{ value: 25, name: '桩类' },
|
||
|
|
{ value: 10, name: '金属丝及其制品类' }
|
||
|
|
],
|
||
|
|
level3: [
|
||
|
|
{ value: 8, name: '个', parent: '袋类' },
|
||
|
|
{ value: 12, name: '只', parent: '袋类' },
|
||
|
|
{ value: 20, name: '捆', parent: '土工布类' },
|
||
|
|
{ value: 25, name: '匹', parent: '土工布类' },
|
||
|
|
{ value: 13, name: '根', parent: '桩类' },
|
||
|
|
{ value: 12, name: '堆', parent: '桩类' },
|
||
|
|
{ value: 2, name: '条', parent: '金属丝及其制品类' },
|
||
|
|
{ value: 4, name: '捆', parent: '金属丝及其制品类' },
|
||
|
|
{ value: 4, name: '个', parent: '金属丝及其制品类' }
|
||
|
|
]
|
||
|
|
})
|
||
|
|
}
|
||
|
|
},
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
chart: null
|
||
|
|
}
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
// 生成唯一的颜色
|
||
|
|
generateUniqueColors(count) {
|
||
|
|
const baseColors = [
|
||
|
|
{ start: '#319ae2', end: '#4F7CAC' }, // 深蓝 → 浅蓝
|
||
|
|
{ start: '#f57e4f', end: '#ebcc88' }, // 鲜黄橙 → 浅黄橙
|
||
|
|
{ start: '#705fbc', end: '#8d8cd6' }, // 深紫色 → 浅紫色
|
||
|
|
{ start: '#90f9bf', end: '#88ba91' } // 深青蓝 → 浅青蓝
|
||
|
|
];
|
||
|
|
const colors = [];
|
||
|
|
for (let i = 0; i < count; i++) {
|
||
|
|
colors.push(baseColors[i % baseColors.length]);
|
||
|
|
}
|
||
|
|
return colors;
|
||
|
|
// const colors = [];
|
||
|
|
// const baseColors = [
|
||
|
|
// '#003366', // 深蓝
|
||
|
|
// '#005577', // 深青蓝
|
||
|
|
// '#007799', // 深湖蓝
|
||
|
|
// '#8800cc', // 深紫
|
||
|
|
// '#cc0066', // 深玫红
|
||
|
|
// '#ff3366', // 鲜艳玫红
|
||
|
|
// '#ff6600', // 鲜橙
|
||
|
|
// '#ff9900', // 鲜黄橙
|
||
|
|
// '#009966', // 深绿
|
||
|
|
// '#006666', // 深青
|
||
|
|
// '#003399', // 深靛蓝
|
||
|
|
// '#660099', // 深紫罗兰
|
||
|
|
// '#990033', // 深酒红
|
||
|
|
// '#0066cc', // 深天蓝
|
||
|
|
// '#0099cc', // 鲜艳蓝绿
|
||
|
|
// '#333399' // 深蓝紫
|
||
|
|
// ];
|
||
|
|
|
||
|
|
// for (let i = 0; i < count; i++) {
|
||
|
|
// const startColor = baseColors[i % baseColors.length];
|
||
|
|
// const endColor = baseColors[(i + 4) % baseColors.length];
|
||
|
|
// colors.push({
|
||
|
|
// start: startColor,
|
||
|
|
// end: endColor
|
||
|
|
// });
|
||
|
|
// }
|
||
|
|
// return colors;
|
||
|
|
},
|
||
|
|
|
||
|
|
initChart() {
|
||
|
|
const chartDom = this.$refs.chartRef;
|
||
|
|
this.chart = echarts.init(chartDom);
|
||
|
|
|
||
|
|
// 核心配置:统一各层的起始角度、排列方向
|
||
|
|
const commonPieConfig = {
|
||
|
|
clockwise: false,
|
||
|
|
startAngle: 90,
|
||
|
|
avoidLabelOverlap: true
|
||
|
|
};
|
||
|
|
|
||
|
|
// 为第二层生成颜色
|
||
|
|
const level2Colors = {};
|
||
|
|
const colors = this.generateUniqueColors(this.chartData.level2.length);
|
||
|
|
this.chartData.level2.forEach((item, index) => {
|
||
|
|
level2Colors[item.name] = colors[index];
|
||
|
|
});
|
||
|
|
|
||
|
|
// 计算每个父级分类下的子项数量
|
||
|
|
const parentItemCounts = {};
|
||
|
|
this.chartData.level3.forEach(item => {
|
||
|
|
parentItemCounts[item.parent] = (parentItemCounts[item.parent] || 0) + 1;
|
||
|
|
});
|
||
|
|
|
||
|
|
// 为第三层数据分配颜色和角度
|
||
|
|
const level3DataWithColor = this.chartData.level3.map((item, index) => {
|
||
|
|
const colorConfig = level2Colors[item.parent] || { start: '#2bb8fc', end: '#93cbfd' };
|
||
|
|
const parentCount = parentItemCounts[item.parent];
|
||
|
|
const parentIndex = this.chartData.level2.findIndex(d => d.name === item.parent);
|
||
|
|
const parentAngle = 360 / this.chartData.level2.length;
|
||
|
|
const childAngle = parentAngle / parentCount;
|
||
|
|
const startAngle = 90 + (parentIndex * parentAngle);
|
||
|
|
const itemIndex = this.chartData.level3.filter(d => d.parent === item.parent).indexOf(item);
|
||
|
|
const itemStartAngle = startAngle + (itemIndex * childAngle);
|
||
|
|
|
||
|
|
return {
|
||
|
|
...item,
|
||
|
|
itemStyle: {
|
||
|
|
color: {
|
||
|
|
type: 'linear',
|
||
|
|
x: 0,
|
||
|
|
y: 0,
|
||
|
|
x2: 1,
|
||
|
|
y2: 0,
|
||
|
|
colorStops: [{
|
||
|
|
offset: 0,
|
||
|
|
color: colorConfig.start
|
||
|
|
}, {
|
||
|
|
offset: 1,
|
||
|
|
color: colorConfig.end
|
||
|
|
}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
startAngle: itemStartAngle,
|
||
|
|
endAngle: itemStartAngle + childAngle
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
// 为第二层数据添加渐变效果
|
||
|
|
const level2DataWithGradient = this.chartData.level2.map((item) => {
|
||
|
|
const colorConfig = level2Colors[item.name];
|
||
|
|
return {
|
||
|
|
...item,
|
||
|
|
itemStyle: {
|
||
|
|
color: {
|
||
|
|
type: 'linear',
|
||
|
|
x: 0,
|
||
|
|
y: 0,
|
||
|
|
x2: 1,
|
||
|
|
y2: 0,
|
||
|
|
colorStops: [{
|
||
|
|
offset: 0,
|
||
|
|
color: colorConfig.start
|
||
|
|
}, {
|
||
|
|
offset: 1,
|
||
|
|
color: colorConfig.end
|
||
|
|
}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
// 为第一层数据添加渐变效果
|
||
|
|
const level1DataWithGradient = this.chartData.level1.map(item => ({
|
||
|
|
...item,
|
||
|
|
itemStyle: {
|
||
|
|
color: {
|
||
|
|
type: 'linear',
|
||
|
|
x: 0,
|
||
|
|
y: 0,
|
||
|
|
x2: 1,
|
||
|
|
y2: 0,
|
||
|
|
colorStops: [{
|
||
|
|
offset: 0,
|
||
|
|
color: '#2bb8fc',
|
||
|
|
}, {
|
||
|
|
offset: 1,
|
||
|
|
color: '#93cbfd'
|
||
|
|
}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}));
|
||
|
|
|
||
|
|
const option = {
|
||
|
|
// title: {
|
||
|
|
// text: '防御材料多层环形分布',
|
||
|
|
// left: 'center',
|
||
|
|
// textStyle: { color: '#fff', fontSize: 16 }
|
||
|
|
// },
|
||
|
|
tooltip: {
|
||
|
|
trigger: 'item',
|
||
|
|
formatter: '{b} : {c} ({d}%)'
|
||
|
|
},
|
||
|
|
series: [
|
||
|
|
// 第三层(最底层)
|
||
|
|
{
|
||
|
|
...commonPieConfig,
|
||
|
|
type: 'pie',
|
||
|
|
z: 1,
|
||
|
|
radius: ['45%', '65%'],
|
||
|
|
label: {
|
||
|
|
show: true,
|
||
|
|
position: 'outside',
|
||
|
|
color: '#fff',
|
||
|
|
formatter: '{b}\n{d}%',
|
||
|
|
fontSize: 12
|
||
|
|
},
|
||
|
|
labelLine: {
|
||
|
|
show: true,
|
||
|
|
length: 10,
|
||
|
|
length2: 15,
|
||
|
|
lineStyle: { color: '#fff' }
|
||
|
|
},
|
||
|
|
itemStyle: {
|
||
|
|
shadowBlur: 10,
|
||
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||
|
|
shadowOffsetX: 2,
|
||
|
|
shadowOffsetY: 2
|
||
|
|
},
|
||
|
|
data: level3DataWithColor
|
||
|
|
},
|
||
|
|
// 第二层(中间层)
|
||
|
|
{
|
||
|
|
...commonPieConfig,
|
||
|
|
type: 'pie',
|
||
|
|
z: 2,
|
||
|
|
radius: ['25%', '45%'],
|
||
|
|
label: {
|
||
|
|
show: true,
|
||
|
|
position: 'inside',
|
||
|
|
color: '#fff',
|
||
|
|
formatter: '{b}\n{c}',
|
||
|
|
fontSize: 13
|
||
|
|
},
|
||
|
|
itemStyle: {
|
||
|
|
shadowBlur: 10,
|
||
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||
|
|
shadowOffsetX: 2,
|
||
|
|
shadowOffsetY: 2
|
||
|
|
},
|
||
|
|
data: level2DataWithGradient
|
||
|
|
},
|
||
|
|
// 第一层(最上层)
|
||
|
|
{
|
||
|
|
...commonPieConfig,
|
||
|
|
type: 'pie',
|
||
|
|
z: 3,
|
||
|
|
radius: ['0%', '25%'],
|
||
|
|
label: {
|
||
|
|
show: true,
|
||
|
|
position: 'inside',
|
||
|
|
color: '#fff',
|
||
|
|
formatter: '{b}\n{c}',
|
||
|
|
fontSize: 14
|
||
|
|
},
|
||
|
|
itemStyle: {
|
||
|
|
shadowBlur: 10,
|
||
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||
|
|
shadowOffsetX: 2,
|
||
|
|
shadowOffsetY: 2
|
||
|
|
},
|
||
|
|
data: level1DataWithGradient
|
||
|
|
}
|
||
|
|
]
|
||
|
|
};
|
||
|
|
|
||
|
|
this.chart.setOption(option);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
mounted() {
|
||
|
|
this.initChart();
|
||
|
|
|
||
|
|
// 响应式:窗口变化时重绘
|
||
|
|
window.addEventListener('resize', () => {
|
||
|
|
this.chart && this.chart.resize();
|
||
|
|
});
|
||
|
|
},
|
||
|
|
watch: {
|
||
|
|
chartData: {
|
||
|
|
handler() {
|
||
|
|
this.initChart();
|
||
|
|
},
|
||
|
|
deep: true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
beforeDestroy() {
|
||
|
|
if (this.chart) {
|
||
|
|
this.chart.dispose();
|
||
|
|
this.chart = null;
|
||
|
|
}
|
||
|
|
window.removeEventListener('resize', this.chart.resize);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.chart-container {
|
||
|
|
background: transparent; /* 深色背景匹配设计图 */
|
||
|
|
padding: 20px;
|
||
|
|
}
|
||
|
|
</style>
|