Files
yudao-dev/src/views/productionVisualization/equipmentBoard/components/pieChartStatus.vue
‘937886381’ c0a38c568f 大屏
2025-11-18 09:31:39 +08:00

260 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 外层容器图表 + 自定义图例 -->
<div class="chart-legend-wrapper">
<!-- 图表容器 -->
<div :ref="chartRef" id="coreLineChart" class="chart-container"></div>
<!-- 自定义图例三排三列 + 带数值 -->
<div class="custom-legend">
<div class="legend-item" v-for="(item, index) in chartData" :key="index"
>
<!-- 左侧颜色块 + 名称垂直居中对齐 -->
<div class="legend-left">
<div class="legend-color" :style="{ backgroundColor: customColors[index] }"></div>
<span class="legend-name">{{ item.name }}</span>
</div>
<!-- 右侧数值与左侧垂直居中对齐 -->
<span class="legend-value">{{ item.value }}</span>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'Container',
props: {
chartRef: {
type: String,
required: true
}
},
data() {
return {
myChart: null,
selectedStatus: [],
customColors: [
'rgba(255, 235, 106, 1)',
'rgba(255, 206, 106, 1)',
'rgba(255, 151, 71, 1)',
'rgba(152, 92, 255, 1)',
'rgba(113, 100, 255, 1)',
'rgba(143, 146, 255, 1)',
'rgba(40, 138, 255, 1)',
'rgba(80, 181, 255, 1)',
'rgba(168, 233, 255, 1)',
],
chartData: [
{ value: 348, name: '暂停中' },
{ value: 435, name: '已终止-回收' },
{ value: 580, name: '已终止-待回收' },
{ value: 484, name: '已终止' },
{ value: 484, name: '已完成' },
{ value: 484, name: '执行中' },
{ value: 484, name: '异常' },
{ value: 484, name: '待执行' },
{ value: 484, name: '待下发' },
]
};
},
mounted() {
this.$nextTick(() => {
this.initData();
});
},
methods: {
initData() {
const chartDom = this.$refs[this.chartRef];
if (!chartDom) {
console.error(`图表容器未找到!请确认父组件传递的 chartRef 为 "${this.chartRef}"`);
return;
}
this.myChart = echarts.init(chartDom);
this.selectedStatus = this.chartData.map(() => true);
const total = this.chartData.reduce((sum, item) => sum + item.value, 0);
const option = {
legend: { show: false },
tooltip: {
show: true,
trigger: 'item',
backgroundColor: '#FFFFFF',
boxShadow: '0px 4px 8px 0px rgba(0, 0, 0, 0.16)',
borderRadius: '4px',
width: 153,
height: 42,
borderWidth: 0,
textStyle: {
color: 'rgba(0, 0, 0, 0.85)',
fontSize: 14,
lineHeight: 1.5
},
formatter: (params) => {
const color = this.customColors[params.dataIndex];
const percent = ((params.value / total) * 100).toFixed(1);
return `
<div style="display: flex; align-items: center; gap: 8px;">
<div style="width: 12px; height: 12px; background: ${color};"></div>
<div style="display: flex; align-items: center; gap: 20px;">
<span>${params.name}</span>
<span>${params.value}</span>
</div>
</div>
`;
}
},
title: [
{
text: total.toString(),
left: 'center',
top: '35%',
textStyle: {
fontSize: 23,
letterSpacing: 5,
color: 'rgba(0, 0, 0, 0.65)',
fontFamily: 'PingFangSC, PingFang SC'
}
},
{
text: '总数',
left: 'center',
top: '50%',
textStyle: {
fontSize: 16,
color: 'rgba(0, 0, 0, 0.65)',
fontFamily: 'PingFangSC, PingFang SC'
}
}
],
series: [
{
type: 'pie',
radius: ['70%', '100%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
label: { show: false },
labelLine: { show: false },
itemStyle: {
color: (params) => this.customColors[params.dataIndex],
borderWidth: 2,
borderColor: '#fff'
},
data: this.chartData.map((item, index) => ({
...item,
itemStyle: { color: this.customColors[index] }
})),
selected: this.selectedStatus.reduce((obj, selected, index) => {
obj[this.chartData[index].name] = selected;
return obj;
}, {})
}
]
};
option && this.myChart.setOption(option);
window.addEventListener('resize', () => this.myChart?.resize());
this.$once('hook:destroyed', () => {
window.removeEventListener('resize', () => this.myChart?.resize());
this.myChart?.dispose();
});
},
handleLegendClick(index) {
this.selectedStatus[index] = !this.selectedStatus[index];
this.myChart.setOption({
series: [{
selected: this.selectedStatus.reduce((obj, selected, i) => {
obj[this.chartData[i].name] = selected;
return obj;
}, {})
}]
});
}
}
};
</script>
<style scoped lang="scss">
// 外层容器:横向排列,垂直居中
.chart-legend-wrapper {
width: 100%;
height: 170px;
display: flex;
align-items: center; // 整体垂直居中
}
// 图表容器
.chart-container {
height: 180px;
width: 30%;
}
// 图例容器:三列网格布局,垂直居中
.custom-legend {
margin-left: 10px;
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(3, 1fr); // 三列
grid-template-rows: repeat(3, 1fr); // 两行6个item刚好2行3列
// gap: 15px 10px; // 行列间距
align-items: center; // 网格内垂直居中
}
// 单个图例项:横向排列,垂直居中
.legend-item {
display: flex;
align-items: left; // 内部元素垂直居中
flex-direction: column;
width: 100%;
cursor: pointer;
// gap: 10px; // 左右两部分间距
}
// 左侧:颜色块 + 名称(垂直居中)
.legend-left {
display: flex;
align-items: center; // 颜色块与名称垂直居中
gap: 8px; // 颜色块与名称间距
flex: 1; // 占满剩余空间,确保右侧数值靠右
}
// 颜色块
.legend-color {
width: 8px;
height: 8px;
border-radius: 2px;
flex-shrink: 0; // 防止被压缩
}
// 名称
.legend-name {
font-size: 14px;
color: rgba(140, 140, 140, 1);
font-family: 'PingFangSC, PingFang SC';
white-space: nowrap; // 防止换行
}
// 数值
.legend-value {
font-size: 14px;
margin-left: 16px;
color: rgba(0, 0, 0, 0.65);
font-weight: 500;
white-space: nowrap; // 防止换行
}
// 未选中状态
// .legend-item.unselected {
// .legend-name,
// .legend-value {
// color: rgba(0, 0, 0, 0.3);
// }
// .legend-color {
// opacity: 0.5;
// }
// }
</style>