Files
yudao-dev/src/views/home/components/coreBottomBar.vue
‘937886381’ 51e66cf6e1 修改
2026-01-06 17:09:52 +08:00

267 lines
7.1 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="coreBar">
<div class="barTop">
<div class="title">销售指标趋势</div>
<div class="legend">
<span class="legend-item target">预算</span>
<span class="legend-item real">实际</span>
</div>
<div class="button-group">
<div style="letter-spacing: 4px" class="item-button" :class="{ active: activeButton === 0 }"
@click="activeButton = 0">
单价
</div>
<div class="button-line lineOne" v-if="activeButton !== 0 && activeButton !== 1"></div>
<div style="letter-spacing: 4px" class="item-button" :class="{ active: activeButton === 1 }"
@click="activeButton = 1">
净价
</div>
<div class="button-line lineTwo" v-if="activeButton !== 1 && activeButton !== 2"></div>
<div style="letter-spacing: 4px" class="item-button" :class="{ active: activeButton === 2 }"
@click="activeButton = 2">
销量
</div>
<div class="button-line lineThree" v-if="activeButton !== 2 && activeButton !== 3"></div>
<div class="item-button" :class="{ active: activeButton === 3 }" @click="activeButton = 3">
双镀销量
</div>
</div>
</div>
<div class="lineBottom" style="height: 219px; width: 100%">
<!-- 传递当前选中的 series 数据给子组件key 确保数据更新时重新渲染 -->
<coreLineChart style="height: 219px; width: 500px" :chart-series="currentSeries" :x-axis-data="xAxisData"
:dateData="dateData" :key="activeButton + JSON.stringify(currentSeries)" />
</div>
</div>
</template>
<script>
import coreLineChart from './coresBar.vue';
import * as echarts from 'echarts';
export default {
name: "Container",
components: { coreLineChart },
props: {
line: { // 接收父组件传递的 sale 数据对象
type: Object,
default: () => ({})
},
dateData: { // 接收父组件传递的 sale 数据对象
type: Object,
default: () => ({})
}
},
data() {
return {
activeButton: 0,
// 定义按钮与 line 数据中 key 的映射关系
buttonToDataKey: [
'单价',
'净价',
'销量',
'双镀销量' // 注意:数据中的 key 是“双镀面板”,按钮显示的是“双镀产品”
]
};
},
computed: {
// 动态生成 X 轴数据
xAxisData() {
const lineData = this.line || {};
// 获取当前激活按钮对应的数据
const currentDataKey = this.buttonToDataKey[this.activeButton];
const currentIndicatorData = lineData[currentDataKey];
// 使用 'target' 的键作为 x 轴,如果 'target' 不存在,则使用 'real' 的键
if (currentIndicatorData && currentIndicatorData.target) {
return Object.keys(currentIndicatorData.target);
} else if (currentIndicatorData && currentIndicatorData.real) {
return Object.keys(currentIndicatorData.real);
}
return [];
},
// 根据激活按钮动态返回对应 series 数据
currentSeries() {
const lineData = this.line || {};
const currentDataKey = this.buttonToDataKey[this.activeButton];
const chartData = lineData[currentDataKey];
if (!chartData) {
return [];
}
// 提取目标和实际数据的值,并确保顺序与 X 轴一致
const xAxisKeys = this.xAxisData;
const targetDataValues = xAxisKeys.map(date => chartData.target ? chartData.target[date] : 0);
const realDataValues = xAxisKeys.map(date => chartData.real ? chartData.real[date] : 0);
return [
{
name: '预算',
type: 'line',
stack: 'Total',
symbol: 'circle',
symbolSize: 6,
lineStyle: { color: 'rgba(91, 230, 190, 1)', width: 2 },
itemStyle: {
color: 'rgba(91, 230, 190, 1)',
borderColor: 2
},
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(91, 230, 190, 0.4)' },
{ offset: 1, color: 'rgba(91, 230, 190, 0)' },
]),
},
data: targetDataValues
},
{
name: '实际',
type: 'line',
symbol: 'circle',
symbolSize: 6,
lineStyle: { color: 'rgba(255, 132, 0, 1)', width: 2 },
itemStyle: {
color: 'rgba(255, 132, 0, 1)',
borderColor: 'rgba(255, 132, 0, 1)',
borderWidth: 2,
},
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 132, 0, 0.4)' },
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
]),
},
data: realDataValues
}
];
}
}
};
</script>
<style scoped lang="scss">
/* 原有样式保持不变 */
.coreBar {
display: flex;
flex-direction: column;
padding: 12px;
.barTop {
display: flex;
gap: 16px;
align-items: center;
.title {
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
}
.legend {
display: flex;
gap: 24px;
margin-left: 10px;
.legend-item {
position: relative;
font-family: PingFangSC, PingFang SC;
font-size: 14px;
color: rgba(0, 0, 0, 0.8);
&::before {
content: "";
position: absolute;
left: -13px;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 6px;
border-radius: 50%;
}
&::after {
content: "";
position: absolute;
left: -18px;
top: 50%;
transform: translateY(-50%);
width: 16px;
height: 2px;
}
}
.target::before,
.target::after {
background-color: rgba(91, 230, 190, 1);
}
.real::before,
.real::after {
background-color: rgba(255, 132, 0, 1);
}
}
.button-group {
display: flex;
position: relative;
gap: 2px;
margin-left: 30px;
width: 230px;
align-items: center;
height: 24px;
background: #ecf4fe;
border-radius: 12px;
.button-line {
position: absolute;
width: 1px;
height: 14px;
border: 1px solid rgba(11, 88, 255, 0.25);
}
.lineOne {
top: 5px;
left: 54px;
}
.lineTwo {
top: 5px;
left: 108px;
}
.lineThree {
top: 5px;
left: 162px;
}
.item-button {
cursor: pointer;
width: 54px;
height: 24px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #0b58ff;
line-height: 24px;
text-align: center;
}
.item-button.active {
background: #3071ff;
border-radius: 12px;
color: #ffffff;
font-weight: 500;
}
}
}
}
</style>