Files
yudao-dev/src/views/home/components/productBar.vue
2026-03-05 11:12:34 +08:00

287 lines
7.4 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 style="position: relative;">
<div class="legend">
<span class="legend-item-line">
<span class="line target"></span>
预算
</span>
<span class="legend-item-line">
<span class="line real"></span>
实际
</span>
</div>
<div ref="cockpitEffChip" id="coreLineChart" style="height: 219px; width: 100%;"></div>
</div>
</template>
<script>
import * as echarts from 'echarts';
export default {
name: 'Container',
props: ["chartData",'dateData'],
components: {},
data() {
return {
myChart: null, // 存储 echarts 实例
};
},
// 关键:监听 chartData 变化
watch: {
chartData: {
handler(newData) {
this.updateChart(newData);
},
immediate: true, // 组件初始化时立即执行一次
deep: true, // 深度监听对象内部变化
}
},
mounted() {
this.$nextTick(() => {
this.initChart();
});
},
methods: {
// 初始化图表实例
initChart() {
const chartDom = this.$refs.cockpitEffChip;
if (!chartDom) {
console.error('图表容器未找到!');
return;
}
this.myChart = echarts.init(chartDom);
// 初始化时调用一次更新
this.updateChart(this.chartData);
// 监听窗口缩放
window.addEventListener('resize', () => {
this.myChart?.resize();
});
},
// 核心:根据数据更新图表
updateChart(data) {
if (!this.myChart) {
// 如果实例还未初始化,则等待 initChart 完成后再更新
setTimeout(() => this.updateChart(data), 0);
return;
}
// 1. 处理数据,如果 data 无效则清空图表
if (!data || typeof data !== 'object' || (!data.real && !data.target)) {
this.myChart.setOption({
xAxis: { data: [] },
series: [{ data: [] }, { data: [] }]
});
return;
}
// 2. 提取 X 轴数据(从 real 或 target 中取键名)
const xAxisData = data.real ? Object.keys(data.real) : Object.keys(data.target);
console.log('xAxisData', xAxisData);
// 3. 提取 "实际" 和 "目标" 系列的数据
const realData = data.real ? Object.values(data.real) : [];
const targetData = data.target ? Object.values(data.target) : [];
// 4. 准备 echarts 的 option 配置
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
grid: {
top: 35,
bottom: 20,
right: 13,
},
xAxis: [
{
type: 'category',
boundaryGap: false,
axisTick: { show: false },
axisLine: {
show: true,
onZero: false,
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
},
axisLabel: {
color: 'rgba(0, 0, 0, 0.45)',
fontSize: 12,
interval: 0,
// width: 38,
overflow: 'break',
formatter: (value) => {
const dateParts = value.split('-'); // ["2025", "07", "01"]
if (dateParts.length < 2) return value;
// 去掉月份前面的0然后加上"月"
const month = dateParts[1].replace(/^0+/, '');
return `${month}`;
}
},
data: xAxisData
}
],
yAxis: {
type: 'value',
name: '元/㎡',
// nameLocation:'center',
nameTextStyle: { color: 'rgba(0, 0, 0, 0.45)', fontSize: 14, align: 'right' },
min: 0,
// max: function (value) { return Math.ceil(value.max * 1.1); }, // 增加一点余量
axisTick: { show: false },
axisLabel: {
color: 'rgba(0, 0, 0, 0.45)',
fontSize: 12
},
splitLine: {
lineStyle: {
color: 'rgba(0, 0, 0, 0.15)',
}
},
axisLine: {
show: true,
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
}
},
series: [
{
name: '实际',
type: 'line',
// stack: 'Total', // 趋势图通常不需要堆叠
symbol: 'circle',
symbolSize: 8,
lineStyle: {
color: 'rgba(255, 132, 0, 1)', // 加深颜色
width: 2,
},
itemStyle: {
color: 'rgba(255, 132, 0, 1)',
borderColor: '#fff',
borderWidth: 2,
},
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 132, 0, .5)' },
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
]),
},
data: realData // 使用提取出的 "实际" 数据
},
{
name: '预算',
type: 'line',
// stack: 'Total',
symbol: 'circle',
symbolSize: 8,
lineStyle: {
color: 'rgba(98, 213, 180, 1)', // 加深颜色
width: 2,
// type: 'dashed' // 目标线使用虚线
},
itemStyle: {
color: 'rgba(98, 213, 180, 1)',
borderColor: '#fff',
borderWidth: 2,
},
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(98, 213, 180, .5)' },
{ offset: 1, color: 'rgba(98, 213, 180, 0)' },
]),
},
data: targetData // 使用提取出的 "目标" 数据
},
]
};
// 5. 应用配置项更新图表
this.myChart.setOption(option, true);
}
},
beforeDestroy() {
// 组件销毁时清理
window.removeEventListener('resize', () => {
this.myChart?.resize();
});
this.myChart?.dispose();
}
};
</script>
<style lang="scss" scoped>
/* (你的样式代码保持不变) */
.legend {
position: absolute;
right: 12px;
top: 0px;
display: flex;
/* 使用 flex 布局让两个图例项并排且对齐 */
gap: 5px;
}
.legend-item-line {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.8);
text-align: left;
font-style: normal;
position: relative;
padding-left: 24px;
/* 为圆点和线条留出空间 */
display: flex;
align-items: center;
.line {
position: absolute;
left: 6px;
/* 线条从圆点右侧开始 */
top: 50%;
transform: translateY(-50%);
display: inline-block;
width: 16px;
/* 线条长度 */
height: 2px;
margin-right: 4px;
}
.target {
background: rgba(98, 213, 180, 1);
}
.real {
background: rgba(255, 132, 0, 1);
}
&::before {
content: "";
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
background-color: rgba(255, 132, 0, 1);
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
}
}
.legend-item-line:nth-child(1) {
&::before {
background-color: rgba(98, 213, 180, 1);
}
}
</style>