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

366 lines
9.3 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: 458px">
<operatingLineBar :echartData="echartData" style="height: 99%; width: 100%" />
</div>
</div>
</template>
<script>
import operatingLineBar from './operatingLineBar.vue';
export default {
name: "Container",
components: { operatingLineBar },
props: ["name", "size", "icon", 'costGroupData', 'costTypes', 'locations'],
data() {
return {
isDropdownShow: false,
selectedProfit: '', // 默认选中"原料"
echartData: {
locations: [],
target: [],
value: [],
proportion: [],
flag: [] // 接收 flag 数组0/1
},
profitOptions: [], // 可根据实际需求修改选项
};
},
computed: {
defaultProfit() {
// 若 costTypes 有数据,取第一个;否则默认空
return this.profitOptions.length > 0 ? this.profitOptions[0] : '';
},
},
watch: {
defaultProfit(newVal) {
// 仅在 selectedProfit 为空时赋值(避免用户手动选择后被覆盖)
if (!this.selectedProfit && newVal) {
this.selectedProfit = newVal;
this.getEchartData();
}
},
costTypes(newVal) {
if (newVal && newVal.length > 0) {
this.profitOptions = newVal;
// 关键costTypes 变化时,若未选中值则自动选第一个
if (!this.selectedProfit) {
this.selectedProfit = newVal[0];
this.getEchartData();
}
}
},
// 监听 locations 变化,重新加载数据(避免数据依赖未更新)
locations() {
if (this.selectedProfit) {
this.getEchartData();
}
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.isDropdownShow = false;
this.getEchartData();
},
getEchartData() {
// 若 selectedProfit 无值,返回空数据
if (!this.selectedProfit || !this.profitOptions.length) return;
// 从 costGroupData 中获取当前选中类型的数据(如“原片”“加工”)
const selectedTypeData = this.costGroupData[this.selectedProfit] || {};
// 整理数据格式:按地名分组,提取 target/value/proportion
const locations = [];
const target = [];
const value = [];
const proportion = [];
const flag = [];
// 遍历所有地名,对应提取数据
this.locations.forEach(location => {
const locationData = selectedTypeData[location] || [];
const firstItem = locationData[0] || {}; // 每组取第一条数据(可根据需求调整)
locations.push(location);
target.push(firstItem.target || 0);
value.push(firstItem.value || 0);
proportion.push(firstItem.proportion || 0);
flag.push(firstItem.flag || 0);
});
// 更新 ECharts 数据(包含 flag 字段)
this.echartData = {
locations, // x轴地名列表
target, // 预算值数组
value, // 实际值数组
proportion,
flag
};
}
},
mounted() {
// 点击外部关闭下拉菜单
const handleOutsideClick = (e) => {
if (!this.$el.contains(e.target)) {
this.isDropdownShow = false;
}
};
// 关键:页面加载时强制初始化
setTimeout(() => {
// 优先使用 costTypes 数据初始化
if (this.costTypes && this.costTypes.length > 0) {
this.profitOptions = this.costTypes;
this.selectedProfit = this.costTypes[0];
this.getEchartData();
}
// 若 costTypes 未及时加载,用 profitOptions 初始化
else if (this.profitOptions.length > 0) {
this.selectedProfit = this.profitOptions[0];
this.getEchartData();
}
}, 100); // 轻微延迟,确保 props 数据已传递
document.addEventListener('click', handleOutsideClick);
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('click', handleOutsideClick);
});
}
};
</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;
margin-left: 0px;
gap: 16px;
width: 100%;
.right-container {
display: flex;
align-items: center;
gap: 24px;
}
.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: 102px;
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: 102px;
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>