264 lines
8.6 KiB
Vue
264 lines
8.6 KiB
Vue
<template>
|
||
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
||
</template>
|
||
<script>
|
||
import * as echarts from 'echarts';
|
||
|
||
export default {
|
||
components: {},
|
||
data() {
|
||
return {
|
||
myChart: null
|
||
};
|
||
},
|
||
props: {
|
||
refName: {
|
||
type: String,
|
||
default: 'verticalBarChart',
|
||
},
|
||
detailData: {
|
||
type: Object,
|
||
default: () => ({
|
||
completeRate: 0,
|
||
diff: 0,
|
||
real: 0,
|
||
target: 0,
|
||
thb: 0,
|
||
flag: 0
|
||
}),
|
||
}
|
||
},
|
||
mounted() {
|
||
this.$nextTick(() => this.updateChart());
|
||
},
|
||
watch: {
|
||
detailData: {
|
||
handler() {
|
||
this.updateChart();
|
||
},
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
},
|
||
methods: {
|
||
updateChart() {
|
||
const chartDom = this.$refs[this.refName];
|
||
if (!chartDom) {
|
||
console.error('图表容器未找到!');
|
||
return;
|
||
}
|
||
|
||
// 修复:优化实例销毁逻辑,避免重复dispose
|
||
if (this.myChart) {
|
||
this.myChart.clear(); // 先清空,再重新渲染
|
||
} else {
|
||
this.myChart = echarts.init(chartDom);
|
||
}
|
||
|
||
// 解构数据,避免重复取值
|
||
console.log('this.detailData', this.detailData);
|
||
|
||
const { diff, completeRate, real, target, flag } = this.detailData;
|
||
// 确保数值为数字类型
|
||
const realValue = Number(real) || 0;
|
||
const targetValue = Number(target) || 0;
|
||
const diffValue = Number(diff) || 0;
|
||
const rateValue = Number(completeRate) || 0;
|
||
const flagValue = Number(flag) || 0;
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'cross',
|
||
label: { backgroundColor: '#6a7985' }
|
||
}
|
||
},
|
||
grid: {
|
||
top: 10,
|
||
bottom: 30,
|
||
right: 50,
|
||
left: 30,
|
||
containLabel: true,
|
||
show: false
|
||
},
|
||
xAxis: {
|
||
type: 'value',
|
||
axisTick: { show: false },
|
||
min: 0,
|
||
splitNumber: 4,
|
||
axisLine: { show: true, lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||
axisLabel: { color: 'rgba(0, 0, 0, 0.45)', fontSize: 12, interval: 0, padding: [5, 0, 0, 0] }
|
||
},
|
||
yAxis: {
|
||
type: 'category',
|
||
axisLabel: { color: 'rgba(0, 0, 0, 0.75)', fontSize: 12, interval: 0, padding: [5, 0, 0, 0] },
|
||
axisLine: { show: true, lineStyle: { color: '#E5E6EB', width: 1, type: 'solid' } },
|
||
axisTick: { show: false },
|
||
data: ['实际', '预算']
|
||
},
|
||
series: [
|
||
{
|
||
type: 'bar',
|
||
barWidth: 24,
|
||
// 修复:拆分数据项,确保每个柱子的样式独立生效
|
||
data: [
|
||
// 实际值柱子(核心:绑定flag颜色)
|
||
{
|
||
value: realValue,
|
||
label: {
|
||
show: true,
|
||
position: 'right',
|
||
offset: [0, 25],
|
||
width: 68,
|
||
height: 20,
|
||
formatter: (params) => {
|
||
const currentDiff = diffValue || 0;
|
||
const currentFlag = flagValue || 0;
|
||
|
||
const prefix = currentFlag === 1 ? '+' : '';
|
||
|
||
// 根据标志位选择不同的样式类
|
||
if (currentFlag === 1) {
|
||
// 达标 - 使用 rate-achieved 样式
|
||
return `{achieved|${currentDiff}}{text|差值}`;
|
||
} else {
|
||
// 未达标 - 使用 rate-unachieved 样式
|
||
return `{unachieved|${currentDiff}}{text|差值}`;
|
||
}
|
||
},
|
||
backgroundColor: {
|
||
type: 'linear',
|
||
x: 0, y: 0, x2: 0, y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
||
{ offset: 0.2, color: '#ffffff' },
|
||
{ offset: 1, color: '#ffffff' }
|
||
]
|
||
},
|
||
shadowColor: 'rgba(191,203,215,0.5)',
|
||
shadowBlur: 2,
|
||
shadowOffsetX: 0,
|
||
shadowOffsetY: 2,
|
||
borderRadius: 4,
|
||
borderColor: '#BFCBD577',
|
||
borderWidth: 0,
|
||
lineHeight: 20,
|
||
rich: {
|
||
text: {
|
||
width: 'auto',
|
||
padding: [5, 10, 5, 0],
|
||
align: 'center',
|
||
color: '#464646',
|
||
fontSize: 11,
|
||
lineHeight: 20
|
||
},
|
||
achieved: {
|
||
width: 'auto',
|
||
padding: [5, 0, 5, 10],
|
||
align: 'center',
|
||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
||
fontSize: 11,
|
||
lineHeight: 20
|
||
},
|
||
// 未达标样式
|
||
unachieved: {
|
||
width: 'auto',
|
||
padding: [5, 0, 5, 10],
|
||
align: 'center',
|
||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
||
fontSize: 11,
|
||
lineHeight: 20
|
||
}
|
||
}
|
||
},
|
||
// 修复:flag颜色判断独立绑定到实际值柱子
|
||
itemStyle: {
|
||
color: flagValue === 1
|
||
? {
|
||
type: 'linear',
|
||
x: 0, y: 0, x2: 0, y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: 'rgba(174, 239, 224, 1)' },
|
||
{ offset: 1, color: 'rgba(118, 218, 190, 1)' }
|
||
]
|
||
}
|
||
: {
|
||
type: 'linear',
|
||
x: 0, y: 0, x2: 0, y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: 'rgba(253, 209, 129, 1)' },
|
||
{ offset: 1, color: 'rgba(249, 164, 74, 1)' }
|
||
]
|
||
},
|
||
borderRadius: [4, 4, 0, 0]
|
||
}
|
||
},
|
||
// 预算值柱子(固定蓝色渐变)
|
||
{
|
||
value: targetValue,
|
||
label: {
|
||
show: true,
|
||
position: 'right',
|
||
offset: [0, 25],
|
||
width: 68,
|
||
height: 20,
|
||
formatter: `{value|完成率}{rate|${rateValue}%}`,
|
||
backgroundColor: {
|
||
type: 'linear',
|
||
x: 0, y: 0, x2: 0, y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
||
{ offset: 0.2, color: '#ffffff' },
|
||
{ offset: 1, color: '#ffffff' }
|
||
]
|
||
},
|
||
shadowColor: 'rgba(191,203,215,0.5)',
|
||
shadowBlur: 2,
|
||
shadowOffsetX: 0,
|
||
shadowOffsetY: 2,
|
||
borderRadius: 4,
|
||
borderWidth: 0,
|
||
lineHeight: 20,
|
||
rich: {
|
||
value: { width: 'auto', padding: [5, 0, 5, 10], align: 'center', color: '#464646', fontSize: 11, lineHeight: 20 },
|
||
rate: { width: 'auto', padding: [5, 10, 5, 0], align: 'center', color: '#0B58FF', fontSize: 11, lineHeight: 20 }
|
||
}
|
||
},
|
||
itemStyle: {
|
||
color: {
|
||
type: 'linear',
|
||
x: 1, y: 0, x2: 0, y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: '#82CCFF' },
|
||
{ offset: 1, color: '#4B9DFF' }
|
||
]
|
||
},
|
||
borderRadius: [4, 4, 0, 0],
|
||
borderWidth: 0
|
||
}
|
||
}
|
||
]
|
||
}
|
||
]
|
||
};
|
||
|
||
this.myChart.setOption(option, true); // 新增:true表示替换所有配置,避免缓存
|
||
|
||
// 优化:防抖resize,避免频繁触发
|
||
const resizeHandler = () => {
|
||
this.myChart && this.myChart.resize();
|
||
};
|
||
window.removeEventListener('resize', resizeHandler); // 先移除再添加,避免重复绑定
|
||
window.addEventListener('resize', resizeHandler);
|
||
|
||
this.$once('hook:destroyed', () => {
|
||
window.removeEventListener('resize', resizeHandler);
|
||
this.myChart && this.myChart.dispose();
|
||
this.myChart = null;
|
||
});
|
||
}
|
||
},
|
||
};
|
||
</script>
|