Files
yudao-dev/src/views/home/components/profitBar.vue
‘937886381’ dfa4ff3f54 修改
2025-11-14 17:04:22 +08:00

423 lines
11 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="right-container">
<div class="legend">
<span class="legend-item">
<span class="legend-icon line yield"></span>
完成率
</span>
<span class="legend-item">
<span class="legend-icon square target"></span>
目标
</span>
<span class="legend-item close-item">
<span class="legend-icon square achieved"></span>
</span>
<span class="legend-item close-item">
<span class="legend-icon square unachieved"></span>
实际
</span>
</div>
<!-- 按钮组 -->
<div class="button-group">
<div class="item-button category-btn">
<span class="item-text" style="width: 88px;">类目选择</span>
</div>
<div class="dropdown-container">
<div class="item-button profit-btn active"
@click.stop="isDropdownShow = !isDropdownShow">
<span class="item-text profit-text">{{ selectedProfit || '请选择' }}</span>
<span class="dropdown-arrow" :class="{ 'rotate': isDropdownShow }"></span>
</div>
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="lineBottom" style="height: 100%; width: 100%">
<!-- 调用方法传递数据通过 :key 触发组件更新 -->
<operatingLineBar :chart-data="dataChart" :key="selectedProfit + JSON.stringify(formattedData)"
style="height: 99%; width: 100%" />
</div>
</div>
</template>
<script>
import operatingLineBar from './operatingBottomLineBar.vue';
import * as echarts from 'echarts'
export default {
name: "Container",
components: { operatingLineBar },
props: [
"formattedData" // 接收父组件处理后的数据
],
data() {
return {
activeButton: 0,
isDropdownShow: false,
dataChart:{},
selectedProfit: '', // 初始为空字符串
profitOptions: ['利润总额', '毛利率', '产量', '销量', '成本', '营业收入'],
};
},
methods: {
// 核心:将原计算属性改为方法,主动处理数据
getChartData() {
const currentKey = this.selectedProfit
// console.log(this.formattedData,'22222');
// 严格判断数据有效性
if (
!this.formattedData ||
typeof this.formattedData !== 'object' ||
!this.formattedData[currentKey]
) {
return { series: [], allPlaceNames: [] };
}
const targetData = this.formattedData[currentKey];
const allPlaceNames = this.formattedData.allPlaceNames || [];
// 生成图表系列数据
const 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: targetData.map(item => item.proportion),
symbol: 'circle',
symbolSize: 6
},
{
name: '目标',
type: 'bar',
yAxisIndex: 0,
barWidth: 18,
itemStyle: {
color: '#2889FF',
borderRadius: [4, 4, 0, 0]
},
data: targetData.map(item => item.targetValue)
},
{
name: '实际',
type: 'bar',
yAxisIndex: 0,
barWidth: 18,
itemStyle: {
// 根据 safeFlag 动态返回不同的渐变色
color: (params) => {
const dataIndex = params.dataIndex;
const currentFlag = targetData[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: targetData.map(item => item.value)
}
];
// return { series, allPlaceNames };
this.dataChart = { series, allPlaceNames };
console.log(this.dataChart,'dataChart');
},
selectProfit(item) {
this.selectedProfit = item;
this.activeButton = 1;
this.isDropdownShow = false;
this.getChartData()
}
},
mounted() {
// 初始化时如果有数据,默认选中第一个选项
// this.selectedProfit = '总额'
// this.getChartData()
// 点击外部关闭下拉菜单
const handleOutsideClick = (e) => {
if (!this.$el.contains(e.target)) {
this.isDropdownShow = false;
}
};
document.addEventListener('click', handleOutsideClick);
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('click', handleOutsideClick);
});
},
watch: {
// 深度监听formattedData变化确保异步加载的数据能被捕获
formattedData: {
handler(newVal) {
if (newVal && Object.keys(newVal).length) {
// 数据加载完成后,默认选中第一个选项
this.selectedProfit = this.profitOptions[0];
this.getChartData()
}
},
deep: true,
immediate: true // 初始化时立即执行一次
},
// selectedProfit: {
// handler(newVal) {
// this.getChartData()
// },
// deep: true,
// immediate: true // 初始化时立即执行一次
// },
}
};
</script>
<style scoped lang="scss">
/* 样式保持不变 */
.coreBar {
display: flex;
flex-direction: column;
width: 100%;
padding: 12px;
.barTop {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 16px;
width: 100%;
.right-container {
display: flex;
align-items: center;
gap: 24px;
margin-right: 46px;
}
.legend {
display: flex;
gap: 16px;
align-items: center;
margin: 0;
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.8);
text-align: left;
font-style: normal;
white-space: nowrap;
}
.legend-icon {
display: inline-block;
}
.legend-icon.line {
width: 12px;
height: 2px;
position: relative;
&::before {
position: absolute;
content: "";
top: -2px;
left: 3px;
width: 6px;
border-radius: 50%;
height: 6px;
background-color: rgba(40, 138, 255, 1);
}
}
.legend-icon.square {
width: 8px;
height: 8px;
}
.yield {
background: rgba(40, 138, 255, 1);
}
.target {
background: #2889FF;
}
.achieved {
background: rgba(40, 203, 151, 1);
}
.unachieved {
background: rgba(255, 132, 0, 1);
}
.legend-item.close-item+.legend-item.close-item {
margin-left: -8px;
}
}
.button-group {
display: flex;
position: relative;
gap: 2px;
align-items: center;
height: 24px;
background: #ecf4fe;
margin: 0;
.dropdown-container {
position: relative;
z-index: 10;
}
.item-button {
cursor: pointer;
height: 24px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
line-height: 24px;
font-style: normal;
letter-spacing: 2px;
padding: 0 24px 0 12px;
overflow: hidden;
.item-text {
display: inline-block;
}
}
.category-btn {
width: 88px;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
background: #ffffff;
color: #0b58ff;
text-align: center;
}
.profit-btn {
width: 118px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
position: relative;
background: #ffffff;
color: #0b58ff;
text-align: left;
&.active {
background: #3071ff;
color: rgba(249, 252, 255, .8);
}
.profit-text {
text-align: left;
width: 100%;
}
}
.dropdown-arrow {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 6px solid currentColor;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-right: 4px solid transparent;
transition: transform 0.2s ease;
&.rotate {
transform: rotate(90deg);
}
}
.dropdown-options {
position: absolute;
top: 100%;
right: 0;
margin-top: 2px;
width: 118px;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
.dropdown-option {
padding: 6px 12px;
font-size: 12px;
color: #333;
cursor: pointer;
text-align: left;
letter-spacing: 1px;
&:hover {
background: #f5f7fa;
color: #3071ff;
}
}
}
}
}
}
</style>