279 lines
7.8 KiB
Vue
279 lines
7.8 KiB
Vue
<template>
|
||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 400px;"></div>
|
||
</template>
|
||
|
||
<script>
|
||
import * as echarts from 'echarts';
|
||
|
||
export default {
|
||
name: 'operatingLineBar',
|
||
props: {
|
||
echartData: {
|
||
type: Object,
|
||
// 确保默认值中 flag 是数组,避免 undefined
|
||
default: () => ({
|
||
locations: [],
|
||
target: [],
|
||
value: [],
|
||
proportion: [],
|
||
flag: [] // 强制初始化为空数组
|
||
})
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
myChart: null
|
||
};
|
||
},
|
||
watch: {
|
||
echartData: {
|
||
handler() {
|
||
this.initData();
|
||
},
|
||
deep: true,
|
||
immediate: true // 首次绑定就执行 handler
|
||
}
|
||
},
|
||
mounted() {
|
||
this.$nextTick(() => {
|
||
this.initData();
|
||
});
|
||
},
|
||
methods: {
|
||
initData() {
|
||
const chartDom = this.$refs.cockpitEffChip;
|
||
if (!chartDom) {
|
||
console.error('图表容器未找到!');
|
||
return;
|
||
}
|
||
|
||
if (!this.myChart) {
|
||
this.myChart = echarts.init(chartDom);
|
||
}
|
||
|
||
// 解构时给 flag 加默认值,防止 undefined
|
||
const {
|
||
locations = [],
|
||
target = [],
|
||
value = [],
|
||
proportion = [],
|
||
flag = []
|
||
} = this.echartData;
|
||
console.log('this.echartData', this.echartData);
|
||
|
||
// 确保 flag 数组长度与 value 一致(补全缺失的 flag 值为 0)
|
||
const safeFlag = [...flag];
|
||
while (safeFlag.length < value.length) {
|
||
safeFlag.push(0); // 缺失的 flag 默认为 0(达标)
|
||
}
|
||
|
||
const option = {
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
type: 'cross',
|
||
label: {
|
||
backgroundColor: '#6a7985'
|
||
}
|
||
},
|
||
formatter: (params) => {
|
||
if (!params.length) return '';
|
||
const currentIndex = params[0].dataIndex;
|
||
// 使用处理后的 safeFlag,避免越界
|
||
const currentFlag = safeFlag[currentIndex] || 0;
|
||
// const statusText = currentFlag === 0 ? '达标' : '不达标';
|
||
let html = `${params[0].axisValue}<br/>`;
|
||
params.forEach(item => {
|
||
const unit = item.seriesName === '完成率' ? '%' : '万元';
|
||
html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
||
});
|
||
return html;
|
||
}
|
||
},
|
||
grid: {
|
||
top: 30,
|
||
bottom: 30,
|
||
right: 20,
|
||
left: 60,
|
||
},
|
||
xAxis: [
|
||
{
|
||
type: 'category',
|
||
boundaryGap: true,
|
||
axisTick: { show: false },
|
||
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: locations
|
||
}
|
||
],
|
||
yAxis: [
|
||
{
|
||
type: 'value',
|
||
name: '万元',
|
||
nameTextStyle: {
|
||
color: 'rgba(0, 0, 0, 0.45)',
|
||
fontSize: 12,
|
||
align: 'right'
|
||
},
|
||
min: 0,
|
||
max: (val) => val.max > 0 ? Math.ceil(val.max * 1.1) : 10,
|
||
scale: false,
|
||
axisTick: { show: false },
|
||
axisLabel: {
|
||
color: 'rgba(0, 0, 0, 0.45)',
|
||
fontSize: 12,
|
||
formatter: '{value}'
|
||
},
|
||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||
splitNumber: 4
|
||
},
|
||
{
|
||
type: 'value',
|
||
nameTextStyle: {
|
||
color: 'rgba(0, 0, 0, 0.45)',
|
||
fontSize: 12,
|
||
align: 'left'
|
||
},
|
||
// min: 0,
|
||
// max: 100,
|
||
axisTick: { show: false },
|
||
axisLabel: {
|
||
color: 'rgba(0, 0, 0, 0.45)',
|
||
fontSize: 12,
|
||
formatter: '{value}%'
|
||
},
|
||
splitLine: { show: false },
|
||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||
splitNumber: 4
|
||
}
|
||
],
|
||
series: [
|
||
{
|
||
name: '完成率',
|
||
type: 'line',
|
||
yAxisIndex: 1,
|
||
lineStyle: {
|
||
color: 'rgba(40, 138, 255, .5)',
|
||
width: 2
|
||
},
|
||
itemStyle: {
|
||
color: 'rgba(40, 138, 255, 1)',
|
||
borderColor: 'rgba(40, 138, 255, 1)',
|
||
borderWidth: 2,
|
||
radius: 4
|
||
},
|
||
areaStyle: {
|
||
opacity: 0.2,
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: 'rgba(40, 138, 255, .9)' },
|
||
{ offset: 1, color: 'rgba(40, 138, 255, 0)' }
|
||
])
|
||
},
|
||
data: proportion,
|
||
symbol: 'circle',
|
||
symbolSize: 6
|
||
},
|
||
{
|
||
name: '预算',
|
||
type: 'bar',
|
||
yAxisIndex: 0,
|
||
barWidth: 14,
|
||
itemStyle: {
|
||
// 移除多余的 normal 层级,直接配置 color 渐变
|
||
color: {
|
||
type: 'linear',
|
||
x: 0,
|
||
y: 0,
|
||
x2: 0,
|
||
y2: 1,
|
||
colorStops: [
|
||
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
|
||
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
|
||
]
|
||
},
|
||
borderRadius: [4, 4, 0, 0],
|
||
borderWidth: 0
|
||
},
|
||
// itemStyle: {
|
||
// color: 'rgba(40, 137, 255, 1)',
|
||
// borderRadius: [4, 4, 0, 0],
|
||
// borderWidth: 0
|
||
// },
|
||
data: target
|
||
},
|
||
{
|
||
name: '实际',
|
||
type: 'bar',
|
||
yAxisIndex: 0,
|
||
barWidth: 14,
|
||
itemStyle: {
|
||
// 根据 safeFlag 动态返回不同的渐变色
|
||
color: (params) => {
|
||
const dataIndex = params.dataIndex;
|
||
const currentFlag = safeFlag[dataIndex] || 0; // 默认为0(不达标)
|
||
|
||
// 达标时的渐变(绿色系)
|
||
if (currentFlag === 1) {
|
||
return {
|
||
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)' } // 深绿
|
||
]
|
||
};
|
||
}
|
||
// 不达标时的渐变(橙色系)
|
||
else {
|
||
return {
|
||
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],
|
||
borderWidth: 0
|
||
},
|
||
data: value
|
||
}
|
||
]
|
||
};
|
||
|
||
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>
|