254 lines
6.1 KiB
Vue
254 lines
6.1 KiB
Vue
<template>
|
|
<div class="coreBar">
|
|
<div class="barTop">
|
|
<div class="barTop-left" style="display: flex;">
|
|
<div class="title">费用指标趋势</div>
|
|
<div class="legend">
|
|
<span class="legend-item manager">管理费用</span>
|
|
<span class="legend-item sale">销售费用</span>
|
|
<span class="legend-item finance">财务费用</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="lineBottom" style="height: 213px; width: 100%">
|
|
<!-- 传递动态生成的 series 数据和 xAxis 数据给子组件 -->
|
|
<coreLineChart style="height: 213px; width: 680px" :chart-series="chartSeries" :x-axis-data="xAxisData"
|
|
:dateData="dateData" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import coreLineChart from './coresBar.vue';
|
|
import * as echarts from 'echarts';
|
|
|
|
export default {
|
|
name: "Container",
|
|
components: { coreLineChart },
|
|
props: {
|
|
line: { // 接收父组件传递的 cost 数据对象
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
dateData: {
|
|
type: Object,
|
|
default: () => ({})
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
// 图表样式配置项,可以抽离出来方便管理
|
|
chartConfig: {
|
|
manageCost: {
|
|
name: '管理费用',
|
|
lineColor: 'rgba(11, 88, 255, .5)',
|
|
itemColor: 'rgba(11, 88, 255, .5)',
|
|
borderColor: 'rgba(11, 88, 255, 1)',
|
|
areaGradient: [
|
|
{ offset: 0, color: 'rgba(11, 88, 255, .4)' },
|
|
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
|
]
|
|
},
|
|
saleCost: {
|
|
name: '销售费用',
|
|
lineColor: 'rgba(54, 181, 138, .5)',
|
|
itemColor: 'rgba(54, 181, 138, .5)',
|
|
borderColor: 'rgba(54, 181, 138, 1)',
|
|
areaGradient: [
|
|
{ offset: 0, color: 'rgba(54, 181, 138, .4)' },
|
|
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
|
]
|
|
},
|
|
financeCost: {
|
|
name: '财务费用',
|
|
lineColor: 'rgba(255, 132, 0, .5)',
|
|
itemColor: 'rgba(255, 132, 0, .5)',
|
|
borderColor: 'rgba(255, 132, 0, 1)',
|
|
areaGradient: [
|
|
{ offset: 0, color: 'rgba(255, 132, 0, .4)' },
|
|
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
|
]
|
|
}
|
|
}
|
|
};
|
|
},
|
|
computed: {
|
|
// 动态生成 X 轴数据
|
|
xAxisData() {
|
|
// 从 cost.line 中获取任意一个有数据的键的 keys 作为 X 轴
|
|
const lineData = this.line || {};
|
|
const firstKey = Object.keys(lineData)[0];
|
|
return firstKey ? Object.keys(lineData[firstKey]) : [];
|
|
},
|
|
// 动态生成 series 数据
|
|
chartSeries() {
|
|
const lineData = this.line || {};
|
|
const xAxisKeys = this.xAxisData;
|
|
|
|
// 如果没有 X 轴数据,则返回空数组
|
|
if (xAxisKeys.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
// 遍历配置项,生成 series
|
|
return Object.keys(this.chartConfig).map(key => {
|
|
const config = this.chartConfig[key];
|
|
// 确保数据顺序和 X 轴一致
|
|
const dataValues = xAxisKeys.map(date => lineData[key] ? lineData[key][date] : 0);
|
|
|
|
return {
|
|
name: config.name,
|
|
type: 'line',
|
|
stack: 'Total',
|
|
symbol: 'circle',
|
|
symbolSize: 6,
|
|
lineStyle: { color: config.lineColor },
|
|
itemStyle: {
|
|
color: config.itemColor,
|
|
borderColor: config.borderColor,
|
|
borderWidth: 1
|
|
},
|
|
areaStyle: {
|
|
opacity: 0.5,
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, config.areaGradient),
|
|
},
|
|
data: dataValues
|
|
};
|
|
});
|
|
}
|
|
},
|
|
methods: {}
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
/* 原有样式保持不变 */
|
|
.coreBar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 12px;
|
|
|
|
.barTop {
|
|
display: flex;
|
|
gap: 8px;
|
|
justify-content: space-between;
|
|
|
|
.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;
|
|
font-style: normal;
|
|
}
|
|
|
|
.legend {
|
|
display: flex;
|
|
gap: 24px;
|
|
margin-left: 30px;
|
|
|
|
.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: -16px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 12px;
|
|
height: 2px;
|
|
}
|
|
}
|
|
|
|
.manager::before,
|
|
.manager::after {
|
|
background-color: rgba(11, 88, 255, 1);
|
|
}
|
|
|
|
.sale::before,
|
|
.sale::after {
|
|
background-color: rgba(54, 181, 138, 1);
|
|
}
|
|
|
|
.finance::before,
|
|
.finance::after {
|
|
background-color: rgba(255, 132, 0, 1);
|
|
}
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
position: relative;
|
|
gap: 2px;
|
|
width: 252px;
|
|
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: 59px;
|
|
}
|
|
|
|
.lineTwo {
|
|
top: 5px;
|
|
left: 134px;
|
|
}
|
|
|
|
.lineThree {
|
|
top: 5px;
|
|
left: 193px;
|
|
}
|
|
|
|
.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;
|
|
font-style: normal;
|
|
}
|
|
|
|
.item-button.active {
|
|
width: 54px;
|
|
height: 24px;
|
|
background: #3071ff;
|
|
border-radius: 12px;
|
|
color: #ffffff;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|