Files
yudao-dev/src/views/home/salesVolumeAnalysisComponents/monthlyThreeRelatedMetrics.vue
2026-03-25 14:10:27 +08:00

267 lines
7.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 style="flex: 1">
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<div class="topItem-container" style="display: flex; gap: 8px">
<div
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
>
<div class="title">
{{ item.name }}·{{ item.unit }}
</div>
<div style='font-size: 16px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{item.detailData.completeRate}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:item.detailData.flag>0?'#30B590':'#FF9423'}" >{{item.detailData.diff}}</span></span>
</div>
<operatingSingleBar :detailData="item.detailData"></operatingSingleBar>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
relatedMon: {
type: Object,
default: () => ({
双镀成本: { completeRate: 0, diff: 0, real: 0, target: 0, thb: 0 },
双镀均价: { completeRate: 0, diff: 0, real: 0, target: 0, thb: 0 },
双镀毛利: { completeRate: 0, diff: 0, real: 0, target: 0, thb: 0 },
})
},
title: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 兜底数据:防止字段缺失时报错
defaultData: {
completeRate: 0,
diff: 0,
real: 0,
target: 0,
thb: 0
}
}
},
computed: {
indicatorDefs() {
return [
{ key: 'financialCost', name: '双镀成本', unit: '元/㎡'},
{ key: 'financialPrice', name: '双镀均价', unit: '元/㎡'},
{ key: 'financialProfit', name: '双镀毛利', unit: '万元'},
]
},
indicators() {
let _this = this
const fallback = { target: 0, real: 0, completeRate: 0, diff: 0, flag: 0 }
const list = Object.entries(_this.relatedMon).map(([title, data]) => {
return {
title: title,
target: data.target,
real: data.real,
completeRate: data.completeRate,
diff: data.diff
};
});
return _this.indicatorDefs.map(def => {
const data = list.find(item => item && item.title === def.name) || fallback
const detailData = {
...data,
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target),
}
return {
...def,
detailData,
sortValue: Number((data && data.real) ?? 0)
}
})
},
sortedIndicators() {
const unitOrder = ['元/㎡','万元']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx
}
return this.indicators.slice().sort((a, b) => {
const ur = unitRank(a.unit) - unitRank(b.unit)
if (ur !== 0) return ur
const vr = (b.sortValue ?? -Infinity) - (a.sortValue ?? -Infinity)
if (vr !== 0) return vr
return String(a.key).localeCompare(String(b.key))
})
},
},
watch: {
relatedMon: {
handler(newValue) {
this.updateChart()
},
immediate: true, // 初始化时立即执行
deep: true
}
},
mounted() {
// 初始化图表
this.$nextTick(() => this.updateChart())
},
methods: {
/**
* 判断完成率对应的flag值<100为0≥100为1
* @param {number} rate 完成率原始值如89代表89%
* @returns {0|1} flag值
*/
getRateFlag(rate, real, target) {
if (isNaN(rate) || rate === null || rate === undefined) return 0;
// 1. 完成率 >= 100 => 达标
if (rate >= 100) return 1;
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
if (rate === 0 && target === 0) return 1;
// 其他情况 => 未达标
return 0;
},
/**
* 图表更新方法:可在这里补充全局的图表刷新逻辑
* 若子组件内部已监听 chartData 变化,此方法可留空或补充额外逻辑
*/
updateChart() {
console.log('数据更新当前relatedMon:', this.relatedMon)
// 打印各维度的flag值方便调试
// console.log('销量flag:', this.getRateFlag(this.relatedMon.销量.completeRate))
// console.log('成本flag:', this.getRateFlag(this.relatedMon.成本.completeRate))
// console.log('运费flag:', this.getRateFlag(this.relatedMon.运费.completeRate))
}
}
}
</script>
<style lang='scss' scoped>
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
.scroll-container {
/* 1. 固定容器高度根据页面布局调整示例300px超出则滚动 */
max-height: 210px;
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
overflow-y: auto;
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
overflow-x: hidden;
/* 4. 内边距:与标题栏和容器边缘对齐 */
padding: 10px 0;
/* 5. 隐藏滚动条(兼容主流浏览器) */
/* Chrome/Safari */
&::-webkit-scrollbar {
display: none;
}
/* Firefox */
scrollbar-width: none;
/* IE/Edge */
-ms-overflow-style: none;
}
.dashboard {
width: 250px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
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;
letter-spacing: 2px;
}
.number {
display: flex;
align-items: center;
gap: 6px;
// width: 190px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
white-space: nowrap;
}
.mom {
width: 120px;
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;
z-index: 1000;
}
}
// .line {
// width: 500px;
// height: 205px;
// background: #F9FCFF;
// }
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</style>
<!-- <style>
/* 全局 tooltip 样式(不使用 scoped确保生效 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style> -->