267 lines
7.1 KiB
Vue
267 lines
7.1 KiB
Vue
<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>
|