228 lines
6.8 KiB
Vue
228 lines
6.8 KiB
Vue
<template>
|
||
<div style="width: 100%; height: 210px;position: relative;">
|
||
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
|
||
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
|
||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:detailData.diff>=0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
|
||
</div>
|
||
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import * as echarts from 'echarts';
|
||
|
||
export default {
|
||
components: {},
|
||
data() {
|
||
return {
|
||
myChart: null // 存储图表实例,避免重复创建
|
||
};
|
||
},
|
||
props: {
|
||
// 明确接收的props结构,增强可读性
|
||
refName: {
|
||
type: String,
|
||
default: () => 'verticalBarChart',
|
||
},
|
||
detailData: {
|
||
type: Object,
|
||
default: () => ({
|
||
}),
|
||
}
|
||
},
|
||
mounted() {
|
||
this.$nextTick(() => {
|
||
this.updateChart();
|
||
});
|
||
},
|
||
|
||
// 新增:监听 chartData 变化
|
||
watch: {
|
||
// 深度监听数据变化,仅更新图表配置(不销毁实例)
|
||
detailData: {
|
||
handler() {
|
||
console.log(this.chartData, 'chartData');
|
||
|
||
this.updateChart();
|
||
},
|
||
deep: true,
|
||
immediate: true // 初始化时立即执行
|
||
}
|
||
},
|
||
methods: {
|
||
getRateFlag(rate, real, target) {
|
||
if (isNaN(rate) || rate === null || rate === undefined) return 0;
|
||
|
||
// 1. 完成率 >= 100 => 达标
|
||
if (rate >= 100) return 1;
|
||
|
||
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
|
||
if (rate === 0 && target === 0) return 1;
|
||
|
||
// 其他情况 => 未达标
|
||
return 0;
|
||
},
|
||
updateChart() {
|
||
const chartDom = this.$refs[this.refName];
|
||
if (!chartDom) {
|
||
console.error('图表容器未找到!');
|
||
return;
|
||
}
|
||
|
||
if (this.myChart) {
|
||
this.myChart.dispose();
|
||
}
|
||
|
||
|
||
this.myChart = echarts.init(chartDom);
|
||
const diff = this.detailData.diff || 0
|
||
const rate = this.detailData.rate || 0
|
||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'cross',
|
||
label: {
|
||
backgroundColor: '#6a7985'
|
||
}
|
||
},
|
||
// formatter: (params) => {
|
||
// let html = `${params[0].axisValue}<br/>`;
|
||
// params.forEach(item => {
|
||
// const unit = item.seriesName === '完成率' ? '%' : (
|
||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
||
// );
|
||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
||
// });
|
||
// return html;
|
||
// }
|
||
},
|
||
grid: {
|
||
top: 40,
|
||
bottom: 15,
|
||
right: 80,
|
||
left: 10,
|
||
containLabel: true,
|
||
show: false // 隐藏grid背景,避免干扰
|
||
},
|
||
xAxis: {
|
||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
||
type: 'value',
|
||
// offset: 0,
|
||
// boundaryGap: true ,
|
||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
||
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]
|
||
},
|
||
// data: xData // 数值轴不需要手动设置data,由series的数据自动生成
|
||
},
|
||
yAxis: {
|
||
type: 'category',
|
||
axisLabel: {
|
||
color: 'rgba(0, 0, 0, 0.75)',
|
||
fontSize: 12,
|
||
interval: 0,
|
||
padding: [5, 0, 0, 0]
|
||
},
|
||
axisLine: {
|
||
show: true, // 显示Y轴轴线(关键)
|
||
lineStyle: {
|
||
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
|
||
width: 1, // 轴线宽度
|
||
type: 'solid' // 实线(可选:dashed虚线、dotted点线)
|
||
}
|
||
},
|
||
axisTick: { show: false },
|
||
// padding: [300, 100, 100, 100],
|
||
data: ['实际', '预算'] // y轴分类:实际、预算
|
||
},
|
||
series: [
|
||
{
|
||
// name: '预算',
|
||
type: 'bar',
|
||
barWidth: 24,
|
||
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
|
||
// 数据长度与yAxis的分类数量匹配(实际、预算各一个值)
|
||
data: [{
|
||
value: this.detailData.real,
|
||
label: {
|
||
show: true,
|
||
position: 'right',
|
||
fontSize: 14,
|
||
},
|
||
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: this.detailData.target,
|
||
label: {
|
||
show: true,
|
||
position: 'right',
|
||
fontSize: 14,
|
||
},
|
||
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
|
||
},
|
||
},],
|
||
|
||
},
|
||
]
|
||
};
|
||
|
||
option && this.myChart.setOption(option);
|
||
|
||
// 窗口缩放适配和销毁逻辑保持不变
|
||
window.addEventListener('resize', () => {
|
||
this.myChart && this.myChart.resize();
|
||
});
|
||
|
||
this.$once('hook:destroyed', () => {
|
||
window.removeEventListener('resize', () => {
|
||
this.myChart && this.myChart.resize();
|
||
});
|
||
this.myChart && this.myChart.dispose();
|
||
});
|
||
}
|
||
},
|
||
};
|
||
</script>
|