达标函数删除,图表label置于最顶层
This commit is contained in:
@@ -128,10 +128,6 @@ export default {
|
|||||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// sortChange(value) {
|
|
||||||
// this.sort = value
|
|
||||||
// this.getData()
|
|
||||||
// },
|
|
||||||
getData() {
|
getData() {
|
||||||
getAccountsReceivableData({
|
getAccountsReceivableData({
|
||||||
startTime: this.dateData.startTime,
|
startTime: this.dateData.startTime,
|
||||||
@@ -140,15 +136,8 @@ export default {
|
|||||||
index: undefined,
|
index: undefined,
|
||||||
factory: undefined
|
factory: undefined
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
console.log('res==============================',res);
|
|
||||||
this.monthData= res.data.month
|
this.monthData= res.data.month
|
||||||
this.ytdData = res.data.ytd
|
this.ytdData = res.data.ytd
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -198,28 +187,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,298 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container name="数据趋势" icon="cockpitItemIcon" size="opLargeBg" topSize="large">
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%; gap: 16px">
|
|
||||||
<div class="right" style="
|
|
||||||
height: 191px;
|
|
||||||
display: flex;
|
|
||||||
width: 1595px;
|
|
||||||
background-color: rgba(249, 252, 255, 1);
|
|
||||||
">
|
|
||||||
<dataTrendBar @changeItem="handleChange" :chartData="chartData" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from "../components/container.vue";
|
|
||||||
import dataTrendBar from "./dataTrendBar.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ProductionStatus",
|
|
||||||
components: { Container, dataTrendBar },
|
|
||||||
props: {
|
|
||||||
trend: {
|
|
||||||
type: Array,
|
|
||||||
// 默认值与实际数据结构一致(12个月)
|
|
||||||
default: () => [
|
|
||||||
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chartData: {
|
|
||||||
months: [], // 月份数组(2025年01月 - 2025年12月)
|
|
||||||
rates: [], // 每月完成率(百分比)
|
|
||||||
reals: [], // 每月实际值
|
|
||||||
budgets: [],// 每月预算值
|
|
||||||
diffs: [], // 每月差值
|
|
||||||
flags: [] // 每月达标标识(≥100 → 1,<100 → 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
trend: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.processTrendData(newVal);
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.processTrendData(this.trend);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleChange(value) {
|
|
||||||
this.$emit("handleChange", value);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 处理趋势数据(适配12个月的数组结构)
|
|
||||||
* @param {Array} trendData - 原始趋势数组(12个月)
|
|
||||||
*/
|
|
||||||
processTrendData(trendData) {
|
|
||||||
// 数据兜底:确保是数组且长度为12
|
|
||||||
const validTrend = Array.isArray(trendData)
|
|
||||||
? trendData
|
|
||||||
: []
|
|
||||||
|
|
||||||
// 初始化空数组
|
|
||||||
const months = [];
|
|
||||||
const rates = [];
|
|
||||||
const reals = [];
|
|
||||||
const budgets = [];
|
|
||||||
const diffs = [];
|
|
||||||
const flags = [];
|
|
||||||
|
|
||||||
// 遍历12个月数据
|
|
||||||
validTrend.forEach(item => {
|
|
||||||
// 基础数据提取(兜底处理)
|
|
||||||
const month = item.title ?? '';
|
|
||||||
const budget = Number(item.budget) || 0;
|
|
||||||
const real = Number(item.real) || 0;
|
|
||||||
const rate = Number(item.rate) || 0;
|
|
||||||
const diff = Number(item.diff) || 0;
|
|
||||||
|
|
||||||
// 计算达标标识(≥100 → 1,<100 → 0)
|
|
||||||
const flag = this.getRateFlag(rate, real, budget);
|
|
||||||
|
|
||||||
// 填充数组
|
|
||||||
months.push(month);
|
|
||||||
rates.push(rate); // 转为百分比并取整
|
|
||||||
reals.push(real);
|
|
||||||
budgets.push(budget);
|
|
||||||
diffs.push(diff);
|
|
||||||
flags.push(flag);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新chartData(响应式)
|
|
||||||
this.chartData = {
|
|
||||||
months,
|
|
||||||
rates,
|
|
||||||
reals,
|
|
||||||
budgets,
|
|
||||||
diffs,
|
|
||||||
flags
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('处理后的趋势数据:', this.chartData);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算达标标识
|
|
||||||
* @param {Number} rate - 完成率(原始值,如1.2 → 120%)
|
|
||||||
* @returns {Number} 1: 达标(≥100%),0: 未达标(<100%)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 滚动容器样式 */
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 设备项样式优化 */
|
|
||||||
.proBarInfo {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 8px 27px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfo {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slot {
|
|
||||||
width: 21px;
|
|
||||||
height: 23px;
|
|
||||||
background: rgba(0, 106, 205, 0.22);
|
|
||||||
backdrop-filter: blur(1.5px);
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #68b5ff;
|
|
||||||
line-height: 23px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eq-name {
|
|
||||||
margin-left: 8px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eqStatus {
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: right;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitLine {
|
|
||||||
width: 1px;
|
|
||||||
height: 14px;
|
|
||||||
border: 1px solid #adadad;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yield {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #00ffff;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: right;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfoLeft {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfoRight {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarWrapper {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
margin-top: 6px;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarLine {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(65deg, rgba(82, 82, 82, 0) 0%, #acacac 100%);
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarLineTop {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(65deg,
|
|
||||||
rgba(53, 223, 247, 0) 0%,
|
|
||||||
rgba(54, 220, 246, 0.92) 92%,
|
|
||||||
#36f6e5 100%,
|
|
||||||
#37acf5 100%);
|
|
||||||
border-radius: 5px;
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图表相关样式 */
|
|
||||||
.chartImgBottom {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 45px;
|
|
||||||
left: 58px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
left: 57px;
|
|
||||||
bottom: 42px;
|
|
||||||
width: 1px;
|
|
||||||
height: 20px;
|
|
||||||
background-color: #00e8ff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 全局 tooltip 样式 */
|
|
||||||
.production-status-chart-tooltip {
|
|
||||||
background: #0a2b4f77 !important;
|
|
||||||
border: none !important;
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.production-status-chart-tooltip * {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,477 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="coreBar">
|
|
||||||
<!-- 新增行容器:包裹“各基地情况”和barTop -->
|
|
||||||
<div class="header-row">
|
|
||||||
<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">
|
|
||||||
<span class="legend-icon square achieved"></span>
|
|
||||||
实际·达标
|
|
||||||
</span>
|
|
||||||
<span class="legend-item">
|
|
||||||
<span class="legend-icon square unachieved"></span>
|
|
||||||
实际·未达标
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<div class="item-button category-btn">
|
|
||||||
<span class="item-text">类目选择</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>
|
|
||||||
<div class="lineBottom" style="height: 100%; width: 100%">
|
|
||||||
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import operatingLineBar from './operatingLineBarSale.vue';
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Container",
|
|
||||||
components: { operatingLineBar },
|
|
||||||
props: ["chartData"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
activeButton: 0,
|
|
||||||
isDropdownShow: false,
|
|
||||||
selectedProfit: '营业收入', // 选中的名称,初始为null
|
|
||||||
profitOptions: [
|
|
||||||
'营业收入',
|
|
||||||
'单价',
|
|
||||||
'销量',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// profitOptions() {
|
|
||||||
// return this.categoryData.map(item => item.name) || [];
|
|
||||||
// },
|
|
||||||
currentDataSource() {
|
|
||||||
console.log('yyyy', this.chartData);
|
|
||||||
|
|
||||||
return this.chartData
|
|
||||||
},
|
|
||||||
locations() {
|
|
||||||
console.log('this.chartData', this.chartData);
|
|
||||||
|
|
||||||
return this.chartData.months
|
|
||||||
},
|
|
||||||
// 根据按钮切换生成对应的 chartData
|
|
||||||
chartD() {
|
|
||||||
const data = this.currentDataSource;
|
|
||||||
console.log(this.currentDataSource, 'currentDataSource');
|
|
||||||
|
|
||||||
const salesData = {
|
|
||||||
allPlaceNames: this.locations,
|
|
||||||
series: [
|
|
||||||
// 1. 完成率(折线图)
|
|
||||||
{
|
|
||||||
name: '完成率',
|
|
||||||
type: 'line',
|
|
||||||
yAxisIndex: 1, // 绑定右侧Y轴(需在子组件启用配置)
|
|
||||||
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: data.rates, // 完成率(%)
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 6
|
|
||||||
},
|
|
||||||
// 2. 目标(柱状图)
|
|
||||||
{
|
|
||||||
name: '预算',
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 0, // 左侧Y轴(万元)
|
|
||||||
barWidth: 14,
|
|
||||||
itemStyle: {
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
|
|
||||||
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
data: data.budgets // 目标销量(万元)
|
|
||||||
},
|
|
||||||
// 3. 实际(柱状图,含达标状态)
|
|
||||||
{
|
|
||||||
name: '实际',
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 0,
|
|
||||||
barWidth: 14,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
|
||||||
height: 20,
|
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
|
||||||
formatter: (params) => {
|
|
||||||
const diff = data.diffs || [];
|
|
||||||
const flags = data.flags || [];
|
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
|
||||||
} else {
|
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backgroundColor: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
|
||||||
{ offset: 0.2, color: '#ffffff' },
|
|
||||||
{ offset: 1, color: '#ffffff' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
shadowColor: 'rgba(191,203,215,0.5)',
|
|
||||||
shadowBlur: 2,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
borderRadius: 4,
|
|
||||||
borderColor: '#BFCBD577',
|
|
||||||
borderWidth: 0,
|
|
||||||
lineHeight: 20,
|
|
||||||
rich: {
|
|
||||||
text: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 10, 5, 0],
|
|
||||||
align: 'center',
|
|
||||||
color: '#464646',
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
achieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
|
||||||
const safeFlag = data.flags;
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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: data.reals // 实际销量(万元)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 根据按钮状态返回对应数据
|
|
||||||
return salesData;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectProfit(item) {
|
|
||||||
this.selectedProfit = item;
|
|
||||||
this.isDropdownShow = false;
|
|
||||||
this.$emit("changeItem", item);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.coreBar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
|
|
||||||
// 新增:头部行容器,实现一行排列
|
|
||||||
.header-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end; // 左右两端对齐
|
|
||||||
align-items: center; // 垂直居中
|
|
||||||
// width: 100%;
|
|
||||||
margin-bottom: 8px; // 与下方图表区保留间距(可根据需求调整)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 各基地情况标题样式
|
|
||||||
.base-title {
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
font-style: normal;
|
|
||||||
padding: 0 0 0 16px; // 保留原有内边距
|
|
||||||
white-space: nowrap; // 防止文字换行
|
|
||||||
}
|
|
||||||
|
|
||||||
.barTop {
|
|
||||||
// 移除原有flex和justify-content,由header-row控制
|
|
||||||
width: auto; // 自适应宽度
|
|
||||||
// 保留原有align-items,确保内部元素垂直居中
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
|
|
||||||
// 1. 右侧容器:包裹图例和按钮组,整体靠右
|
|
||||||
.right-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center; // 图例和按钮组垂直居中
|
|
||||||
gap: 24px; // 图例与按钮组的间距,避免贴紧
|
|
||||||
margin-right: 46px; // 右侧整体留边,与原按钮组边距一致
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 图例:在右侧容器内横向排列
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 按钮组:在右侧容器内,保留原有样式
|
|
||||||
.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;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.item-text {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-btn {
|
|
||||||
width: 75px;
|
|
||||||
border-top-left-radius: 12px;
|
|
||||||
border-bottom-left-radius: 12px;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #0b58ff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profit-btn {
|
|
||||||
width: 123px;
|
|
||||||
border-top-right-radius: 12px;
|
|
||||||
border-bottom-right-radius: 12px;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 18px 0 8px;
|
|
||||||
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: 123px;
|
|
||||||
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>
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
|
||||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
|
||||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
|
||||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
|
||||||
<div class="dashboard">
|
|
||||||
<div class="title">
|
|
||||||
{{ month }}月完成率
|
|
||||||
</div>
|
|
||||||
<div class="number">
|
|
||||||
<div class="yield">
|
|
||||||
{{ monthData?.rate || 0 }}%
|
|
||||||
</div>
|
|
||||||
<div class="mom">
|
|
||||||
环比{{ monthData?.momRate }}%
|
|
||||||
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="line" style="padding: 0px;">
|
|
||||||
<verticalBarChart :detailData="monthData">
|
|
||||||
</verticalBarChart>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import electricityGauge from './electricityGauge.vue'
|
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
|
||||||
monthData: { // 接收父组件传递的设备数据数组
|
|
||||||
type: Object,
|
|
||||||
default: () => {} // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
title: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
month: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
// itemData: {
|
|
||||||
// handler(newValue, oldValue) {
|
|
||||||
// // this.updateChart()
|
|
||||||
// },
|
|
||||||
// deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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: 264px;
|
|
||||||
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 {
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 46px;
|
|
||||||
color: #0B58FF;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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> -->
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
<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; width: 100%;">
|
|
||||||
<!-- 销量模块(直接传递整合了flag的salesData) -->
|
|
||||||
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
销量·万㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[0].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[0].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 单价模块(直接传递整合了flag的unitPriceData) -->
|
|
||||||
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
单价·元/㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[1].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[1].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, operatingSingleBar },
|
|
||||||
props: {
|
|
||||||
monthAnalysis: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [
|
|
||||||
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dateData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
factory: {
|
|
||||||
type: [String,Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
// 初始化数据包含flag字段
|
|
||||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
|
||||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
monthAnalysis: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.updateChart(newVal)
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.updateChart(this.monthAnalysis)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDashboardClick(path) {
|
|
||||||
this.$router.push({
|
|
||||||
path: path,
|
|
||||||
query: {
|
|
||||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
|
||||||
dateData: this.dateData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 判断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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
|
||||||
// 数据兜底
|
|
||||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
|
|
||||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
|
||||||
this.salesData = {
|
|
||||||
...salesItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unitPriceData = {
|
|
||||||
...unitPriceItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调试:确认整合后的数据
|
|
||||||
console.log('整合flag后的销量数据:', this.salesData);
|
|
||||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topItem-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 205px;
|
|
||||||
background: #F9FCFF;
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-wrap {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
height: 32px;
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 32px;
|
|
||||||
color: #0B58FF;
|
|
||||||
line-height: 32px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
width: 97px;
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -173,26 +173,62 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,45 +261,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
width: 1220px;
|
width: 1220px;
|
||||||
background-color: rgba(249, 252, 255, 1);
|
background-color: rgba(249, 252, 255, 1);
|
||||||
">
|
">
|
||||||
<!-- <top-item /> -->
|
|
||||||
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
|
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,23 +87,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
*/
|
*/
|
||||||
@@ -118,7 +100,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -139,7 +121,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
width: 1220px;
|
width: 1220px;
|
||||||
background-color: rgba(249, 252, 255, 1);
|
background-color: rgba(249, 252, 255, 1);
|
||||||
">
|
">
|
||||||
<!-- <top-item /> -->
|
|
||||||
<operatingBar :dateData="dateData" @sort-change="sortChange" :chartData="chartData" />
|
<operatingBar :dateData="dateData" @sort-change="sortChange" :chartData="chartData" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,23 +82,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
@@ -114,7 +96,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -135,7 +117,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -1,245 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="lineBottom" style="height: 180px; width: 100%">
|
|
||||||
<operatingLineBarSaleSingle :refName=" 'totalOperating' " :chartData="chartD" style="height: 99%; width: 100%" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import operatingLineBarSaleSingle from './operatingLineBarSaleSingle.vue';
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Container",
|
|
||||||
components: { operatingLineBarSaleSingle },
|
|
||||||
props: ["detailData"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
locations() {
|
|
||||||
return ['预算', '实际'];
|
|
||||||
},
|
|
||||||
chartD() {
|
|
||||||
// 背景图片路径(若不需要可注释)
|
|
||||||
// const bgImageUrl = require('@/assets/img/labelBg.png');
|
|
||||||
console.log('this.detailData', this.detailData);
|
|
||||||
const rate = this.detailData?.rate || 0
|
|
||||||
const diff = this.detailData?.diff || 0
|
|
||||||
console.log('diff', diff);
|
|
||||||
|
|
||||||
const seriesData = [
|
|
||||||
{
|
|
||||||
value: this.detailData?.budget || 0,
|
|
||||||
flag: 1, // 实际项:达标(绿色)
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
|
|
||||||
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: this.detailData?.real || 0,
|
|
||||||
flag: this.detailData?.flag, // 实际项:达标(绿色)
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
allPlaceNames: ['预算', '实际'],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: 60,
|
|
||||||
barCategoryGap: '50%',
|
|
||||||
data: seriesData,
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const currentFlag = params.data.flag || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
console.log('data', data);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {},
|
|
||||||
};
|
|
||||||
</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%;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFangSC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
font-style: normal;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
gap: 2px;
|
|
||||||
width: 283px;
|
|
||||||
align-items: center;
|
|
||||||
height: 24px;
|
|
||||||
background: #ecf4fe;
|
|
||||||
border-radius: 12px;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.item-button {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 142px;
|
|
||||||
height: 24px;
|
|
||||||
font-family: PingFangSC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #0b58ff;
|
|
||||||
line-height: 24px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
letter-spacing: 8px;
|
|
||||||
padding-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-button.active {
|
|
||||||
width: 142px;
|
|
||||||
height: 24px;
|
|
||||||
background: #3071ff;
|
|
||||||
border-radius: 12px;
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
|
||||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
|
||||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
|
||||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
|
||||||
<div class="dashboard">
|
|
||||||
<div class="title">
|
|
||||||
累计完成率
|
|
||||||
</div>
|
|
||||||
<div class="number">
|
|
||||||
<div class="yield">
|
|
||||||
{{ ytdData?.rate || 0}}%
|
|
||||||
</div>
|
|
||||||
<div class="mom">
|
|
||||||
同比{{ ytdData?.yoyRate || 0}}%
|
|
||||||
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :id=" 'totalG' " :detailData="ytdData" id="totalGauge"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="line" style="padding: 0px;">
|
|
||||||
<verticalBarChart :refName=" 'totalVerticalBarChart' " :detailData="ytdData">
|
|
||||||
</verticalBarChart>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import electricityGauge from './electricityGauge.vue'
|
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
|
||||||
ytdData: { // 接收父组件传递的设备数据数组
|
|
||||||
type: Object,
|
|
||||||
default: () => {} // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
title: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
month: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
itemData: {
|
|
||||||
handler(newValue, oldValue) {
|
|
||||||
// this.updateChart()
|
|
||||||
},
|
|
||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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: 264px;
|
|
||||||
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 {
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 46px;
|
|
||||||
color: #0B58FF;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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> -->
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="width: 100%; height: 210px;position: relative;">
|
|
||||||
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:detailData.flags>0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
myChart: null // 存储图表实例,避免重复创建
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
// 明确接收的props结构,增强可读性
|
|
||||||
refName: {
|
|
||||||
type: String,
|
|
||||||
default: () => 'verticalBarChart',
|
|
||||||
},
|
|
||||||
detailData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateChart();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 新增:监听 chartData 变化
|
|
||||||
watch: {
|
|
||||||
// 深度监听数据变化,仅更新图表配置(不销毁实例)
|
|
||||||
detailData: {
|
|
||||||
handler() {
|
|
||||||
console.log(this.chartData, 'chartData');
|
|
||||||
|
|
||||||
this.updateChart();
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true // 初始化时立即执行
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
updateChart() {
|
|
||||||
const chartDom = this.$refs[this.refName];
|
|
||||||
if (!chartDom) {
|
|
||||||
console.error('图表容器未找到!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.myChart) {
|
|
||||||
this.myChart.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.myChart = echarts.init(chartDom);
|
|
||||||
const diff = this.detailData.diff || 0
|
|
||||||
const rate = this.detailData.rate || 0
|
|
||||||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: {
|
|
||||||
type: 'cross',
|
|
||||||
label: {
|
|
||||||
backgroundColor: '#6a7985'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
top: 40,
|
|
||||||
bottom: 15,
|
|
||||||
right: 80,
|
|
||||||
left: 10,
|
|
||||||
containLabel: true,
|
|
||||||
show: false // 隐藏grid背景,避免干扰
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
|
||||||
type: 'value',
|
|
||||||
// offset: 0,
|
|
||||||
// boundaryGap: true ,
|
|
||||||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
|
||||||
axisTick: { show: false },
|
|
||||||
min: 0,
|
|
||||||
//
|
|
||||||
splitNumber: 4,
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0, 0, 0, 0.45)',
|
|
||||||
fontSize: 12,
|
|
||||||
interval: 0,
|
|
||||||
padding: [5, 0, 0, 0]
|
|
||||||
},
|
|
||||||
// data: xData // 数值轴不需要手动设置data,由series的数据自动生成
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'category',
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0, 0, 0, 0.75)',
|
|
||||||
fontSize: 12,
|
|
||||||
interval: 0,
|
|
||||||
padding: [5, 0, 0, 0]
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true, // 显示Y轴轴线(关键)
|
|
||||||
lineStyle: {
|
|
||||||
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
|
|
||||||
width: 1, // 轴线宽度
|
|
||||||
type: 'solid' // 实线(可选:dashed虚线、dotted点线)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: { show: false },
|
|
||||||
// padding: [300, 100, 100, 100],
|
|
||||||
data: ['实际', '预算'] // y轴分类:实际、预算
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
// name: '预算',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: 24,
|
|
||||||
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
|
|
||||||
// 数据长度与yAxis的分类数量匹配(实际、预算各一个值)
|
|
||||||
data: [{
|
|
||||||
value: this.detailData.real,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'right',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: flagValue === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
value: this.detailData.target,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'right',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
// 预算的渐变颜色(蓝系渐变)
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 1, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: '#82CCFF' }, // 浅蓝
|
|
||||||
{ offset: 1, color: '#4B9DFF' } // 深蓝
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
},],
|
|
||||||
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
option && this.myChart.setOption(option);
|
|
||||||
|
|
||||||
// 窗口缩放适配和销毁逻辑保持不变
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
this.myChart && this.myChart.resize();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$once('hook:destroyed', () => {
|
|
||||||
window.removeEventListener('resize', () => {
|
|
||||||
this.myChart && this.myChart.resize();
|
|
||||||
});
|
|
||||||
this.myChart && this.myChart.dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
<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; width: 100%;">
|
|
||||||
<!-- 销量模块(直接传递整合了flag的salesData) -->
|
|
||||||
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
销量·万㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[0].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[0].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 单价模块(直接传递整合了flag的unitPriceData) -->
|
|
||||||
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
单价·元/㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[1].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[1].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, operatingSingleBar },
|
|
||||||
props: {
|
|
||||||
ytdAnalysis: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [
|
|
||||||
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dateData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
// 初始化数据包含flag字段
|
|
||||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
|
||||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
ytdAnalysis: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.updateChart(newVal)
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.updateChart(this.ytdAnalysis)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDashboardClick(path) {
|
|
||||||
this.$router.push({
|
|
||||||
path: path,
|
|
||||||
query: {
|
|
||||||
factory: this.$route.query.factory ? this.$route.query.factory : 5,
|
|
||||||
dateData: this.dateData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 判断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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
|
||||||
// 数据兜底
|
|
||||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
|
|
||||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
|
||||||
this.salesData = {
|
|
||||||
...salesItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unitPriceData = {
|
|
||||||
...unitPriceItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调试:确认整合后的数据
|
|
||||||
console.log('整合flag后的销量数据:', this.salesData);
|
|
||||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topItem-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 205px;
|
|
||||||
background: #F9FCFF;
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-wrap {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
height: 32px;
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 32px;
|
|
||||||
color: #0B58FF;
|
|
||||||
line-height: 32px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
width: 97px;
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -128,10 +128,6 @@ export default {
|
|||||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// sortChange(value) {
|
|
||||||
// this.sort = value
|
|
||||||
// this.getData()
|
|
||||||
// },
|
|
||||||
getData() {
|
getData() {
|
||||||
getDepreciationAnalysisData({
|
getDepreciationAnalysisData({
|
||||||
startTime: this.dateData.startTime,
|
startTime: this.dateData.startTime,
|
||||||
@@ -144,12 +140,6 @@ export default {
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.monthData= res.data.month
|
this.monthData= res.data.month
|
||||||
this.ytdData = res.data.ytd
|
this.ytdData = res.data.ytd
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -199,28 +189,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,298 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container name="数据趋势" icon="cockpitItemIcon" size="opLargeBg" topSize="large">
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%; gap: 16px">
|
|
||||||
<div class="right" style="
|
|
||||||
height: 191px;
|
|
||||||
display: flex;
|
|
||||||
width: 1595px;
|
|
||||||
background-color: rgba(249, 252, 255, 1);
|
|
||||||
">
|
|
||||||
<dataTrendBar @changeItem="handleChange" :chartData="chartData" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from "../components/container.vue";
|
|
||||||
import dataTrendBar from "./dataTrendBar.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ProductionStatus",
|
|
||||||
components: { Container, dataTrendBar },
|
|
||||||
props: {
|
|
||||||
trend: {
|
|
||||||
type: Array,
|
|
||||||
// 默认值与实际数据结构一致(12个月)
|
|
||||||
default: () => [
|
|
||||||
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chartData: {
|
|
||||||
months: [], // 月份数组(2025年01月 - 2025年12月)
|
|
||||||
rates: [], // 每月完成率(百分比)
|
|
||||||
reals: [], // 每月实际值
|
|
||||||
budgets: [],// 每月预算值
|
|
||||||
diffs: [], // 每月差值
|
|
||||||
flags: [] // 每月达标标识(≥100 → 1,<100 → 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
trend: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.processTrendData(newVal);
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.processTrendData(this.trend);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleChange(value) {
|
|
||||||
this.$emit("handleChange", value);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 处理趋势数据(适配12个月的数组结构)
|
|
||||||
* @param {Array} trendData - 原始趋势数组(12个月)
|
|
||||||
*/
|
|
||||||
processTrendData(trendData) {
|
|
||||||
// 数据兜底:确保是数组且长度为12
|
|
||||||
const validTrend = Array.isArray(trendData)
|
|
||||||
? trendData
|
|
||||||
: []
|
|
||||||
|
|
||||||
// 初始化空数组
|
|
||||||
const months = [];
|
|
||||||
const rates = [];
|
|
||||||
const reals = [];
|
|
||||||
const budgets = [];
|
|
||||||
const diffs = [];
|
|
||||||
const flags = [];
|
|
||||||
|
|
||||||
// 遍历12个月数据
|
|
||||||
validTrend.forEach(item => {
|
|
||||||
// 基础数据提取(兜底处理)
|
|
||||||
const month = item.title ?? '';
|
|
||||||
const budget = Number(item.budget) || 0;
|
|
||||||
const real = Number(item.real) || 0;
|
|
||||||
const rate = Number(item.rate) || 0;
|
|
||||||
const diff = Number(item.diff) || 0;
|
|
||||||
|
|
||||||
// 计算达标标识(≥100 → 1,<100 → 0)
|
|
||||||
const flag = this.getRateFlag(rate, real, budget);
|
|
||||||
|
|
||||||
// 填充数组
|
|
||||||
months.push(month);
|
|
||||||
rates.push(rate); // 转为百分比并取整
|
|
||||||
reals.push(real);
|
|
||||||
budgets.push(budget);
|
|
||||||
diffs.push(diff);
|
|
||||||
flags.push(flag);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新chartData(响应式)
|
|
||||||
this.chartData = {
|
|
||||||
months,
|
|
||||||
rates,
|
|
||||||
reals,
|
|
||||||
budgets,
|
|
||||||
diffs,
|
|
||||||
flags
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('处理后的趋势数据:', this.chartData);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算达标标识
|
|
||||||
* @param {Number} rate - 完成率(原始值,如1.2 → 120%)
|
|
||||||
* @returns {Number} 1: 达标(≥100%),0: 未达标(<100%)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 滚动容器样式 */
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 设备项样式优化 */
|
|
||||||
.proBarInfo {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 8px 27px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfo {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slot {
|
|
||||||
width: 21px;
|
|
||||||
height: 23px;
|
|
||||||
background: rgba(0, 106, 205, 0.22);
|
|
||||||
backdrop-filter: blur(1.5px);
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #68b5ff;
|
|
||||||
line-height: 23px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eq-name {
|
|
||||||
margin-left: 8px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eqStatus {
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: right;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitLine {
|
|
||||||
width: 1px;
|
|
||||||
height: 14px;
|
|
||||||
border: 1px solid #adadad;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yield {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #00ffff;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: right;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfoLeft {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfoRight {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarWrapper {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
margin-top: 6px;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarLine {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(65deg, rgba(82, 82, 82, 0) 0%, #acacac 100%);
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarLineTop {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(65deg,
|
|
||||||
rgba(53, 223, 247, 0) 0%,
|
|
||||||
rgba(54, 220, 246, 0.92) 92%,
|
|
||||||
#36f6e5 100%,
|
|
||||||
#37acf5 100%);
|
|
||||||
border-radius: 5px;
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图表相关样式 */
|
|
||||||
.chartImgBottom {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 45px;
|
|
||||||
left: 58px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
left: 57px;
|
|
||||||
bottom: 42px;
|
|
||||||
width: 1px;
|
|
||||||
height: 20px;
|
|
||||||
background-color: #00e8ff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 全局 tooltip 样式 */
|
|
||||||
.production-status-chart-tooltip {
|
|
||||||
background: #0a2b4f77 !important;
|
|
||||||
border: none !important;
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.production-status-chart-tooltip * {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,477 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="coreBar">
|
|
||||||
<!-- 新增行容器:包裹“各基地情况”和barTop -->
|
|
||||||
<div class="header-row">
|
|
||||||
<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">
|
|
||||||
<span class="legend-icon square achieved"></span>
|
|
||||||
实际·达标
|
|
||||||
</span>
|
|
||||||
<span class="legend-item">
|
|
||||||
<span class="legend-icon square unachieved"></span>
|
|
||||||
实际·未达标
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<div class="item-button category-btn">
|
|
||||||
<span class="item-text">类目选择</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>
|
|
||||||
<div class="lineBottom" style="height: 100%; width: 100%">
|
|
||||||
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import operatingLineBar from './operatingLineBarSale.vue';
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Container",
|
|
||||||
components: { operatingLineBar },
|
|
||||||
props: ["chartData"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
activeButton: 0,
|
|
||||||
isDropdownShow: false,
|
|
||||||
selectedProfit: '营业收入', // 选中的名称,初始为null
|
|
||||||
profitOptions: [
|
|
||||||
'营业收入',
|
|
||||||
'单价',
|
|
||||||
'销量',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// profitOptions() {
|
|
||||||
// return this.categoryData.map(item => item.name) || [];
|
|
||||||
// },
|
|
||||||
currentDataSource() {
|
|
||||||
console.log('yyyy', this.chartData);
|
|
||||||
|
|
||||||
return this.chartData
|
|
||||||
},
|
|
||||||
locations() {
|
|
||||||
console.log('this.chartData', this.chartData);
|
|
||||||
|
|
||||||
return this.chartData.months
|
|
||||||
},
|
|
||||||
// 根据按钮切换生成对应的 chartData
|
|
||||||
chartD() {
|
|
||||||
const data = this.currentDataSource;
|
|
||||||
console.log(this.currentDataSource, 'currentDataSource');
|
|
||||||
|
|
||||||
const salesData = {
|
|
||||||
allPlaceNames: this.locations,
|
|
||||||
series: [
|
|
||||||
// 1. 完成率(折线图)
|
|
||||||
{
|
|
||||||
name: '完成率',
|
|
||||||
type: 'line',
|
|
||||||
yAxisIndex: 1, // 绑定右侧Y轴(需在子组件启用配置)
|
|
||||||
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: data.rates, // 完成率(%)
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 6
|
|
||||||
},
|
|
||||||
// 2. 目标(柱状图)
|
|
||||||
{
|
|
||||||
name: '预算',
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 0, // 左侧Y轴(万元)
|
|
||||||
barWidth: 14,
|
|
||||||
itemStyle: {
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
|
|
||||||
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
data: data.budgets // 目标销量(万元)
|
|
||||||
},
|
|
||||||
// 3. 实际(柱状图,含达标状态)
|
|
||||||
{
|
|
||||||
name: '实际',
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 0,
|
|
||||||
barWidth: 14,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
|
||||||
height: 20,
|
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
|
||||||
formatter: (params) => {
|
|
||||||
const diff = data.diffs || [];
|
|
||||||
const flags = data.flags || [];
|
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
|
||||||
} else {
|
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backgroundColor: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
|
||||||
{ offset: 0.2, color: '#ffffff' },
|
|
||||||
{ offset: 1, color: '#ffffff' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
shadowColor: 'rgba(191,203,215,0.5)',
|
|
||||||
shadowBlur: 2,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
borderRadius: 4,
|
|
||||||
borderColor: '#BFCBD577',
|
|
||||||
borderWidth: 0,
|
|
||||||
lineHeight: 20,
|
|
||||||
rich: {
|
|
||||||
text: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 10, 5, 0],
|
|
||||||
align: 'center',
|
|
||||||
color: '#464646',
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
achieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
|
||||||
const safeFlag = data.flags;
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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: data.reals // 实际销量(万元)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 根据按钮状态返回对应数据
|
|
||||||
return salesData;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectProfit(item) {
|
|
||||||
this.selectedProfit = item;
|
|
||||||
this.isDropdownShow = false;
|
|
||||||
this.$emit("changeItem", item);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.coreBar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
|
|
||||||
// 新增:头部行容器,实现一行排列
|
|
||||||
.header-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end; // 左右两端对齐
|
|
||||||
align-items: center; // 垂直居中
|
|
||||||
// width: 100%;
|
|
||||||
margin-bottom: 8px; // 与下方图表区保留间距(可根据需求调整)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 各基地情况标题样式
|
|
||||||
.base-title {
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
font-style: normal;
|
|
||||||
padding: 0 0 0 16px; // 保留原有内边距
|
|
||||||
white-space: nowrap; // 防止文字换行
|
|
||||||
}
|
|
||||||
|
|
||||||
.barTop {
|
|
||||||
// 移除原有flex和justify-content,由header-row控制
|
|
||||||
width: auto; // 自适应宽度
|
|
||||||
// 保留原有align-items,确保内部元素垂直居中
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
|
|
||||||
// 1. 右侧容器:包裹图例和按钮组,整体靠右
|
|
||||||
.right-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center; // 图例和按钮组垂直居中
|
|
||||||
gap: 24px; // 图例与按钮组的间距,避免贴紧
|
|
||||||
margin-right: 46px; // 右侧整体留边,与原按钮组边距一致
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 图例:在右侧容器内横向排列
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 按钮组:在右侧容器内,保留原有样式
|
|
||||||
.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;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.item-text {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-btn {
|
|
||||||
width: 75px;
|
|
||||||
border-top-left-radius: 12px;
|
|
||||||
border-bottom-left-radius: 12px;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #0b58ff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profit-btn {
|
|
||||||
width: 123px;
|
|
||||||
border-top-right-radius: 12px;
|
|
||||||
border-bottom-right-radius: 12px;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 18px 0 8px;
|
|
||||||
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: 123px;
|
|
||||||
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>
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
|
||||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
|
||||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
|
||||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
|
||||||
<div class="dashboard">
|
|
||||||
<div class="title">
|
|
||||||
{{ month }}月完成率
|
|
||||||
</div>
|
|
||||||
<div class="number">
|
|
||||||
<div class="yield">
|
|
||||||
{{ monthData?.rate || 0 }}%
|
|
||||||
</div>
|
|
||||||
<div class="mom">
|
|
||||||
环比{{ monthData?.momRate }}%
|
|
||||||
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="line" style="padding: 0px;">
|
|
||||||
<verticalBarChart :detailData="monthData">
|
|
||||||
</verticalBarChart>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import electricityGauge from './electricityGauge.vue'
|
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
|
||||||
monthData: { // 接收父组件传递的设备数据数组
|
|
||||||
type: Object,
|
|
||||||
default: () => {} // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
title: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
month: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
// itemData: {
|
|
||||||
// handler(newValue, oldValue) {
|
|
||||||
// // this.updateChart()
|
|
||||||
// },
|
|
||||||
// deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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: 264px;
|
|
||||||
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 {
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 46px;
|
|
||||||
color: #0B58FF;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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> -->
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
<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; width: 100%;">
|
|
||||||
<!-- 销量模块(直接传递整合了flag的salesData) -->
|
|
||||||
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
销量·万㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[0].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[0].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 单价模块(直接传递整合了flag的unitPriceData) -->
|
|
||||||
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
单价·元/㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[1].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[1].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, operatingSingleBar },
|
|
||||||
props: {
|
|
||||||
monthAnalysis: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [
|
|
||||||
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dateData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
factory: {
|
|
||||||
type: [String,Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
// 初始化数据包含flag字段
|
|
||||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
|
||||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
monthAnalysis: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.updateChart(newVal)
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.updateChart(this.monthAnalysis)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDashboardClick(path) {
|
|
||||||
this.$router.push({
|
|
||||||
path: path,
|
|
||||||
query: {
|
|
||||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
|
||||||
dateData: this.dateData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 判断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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
|
||||||
// 数据兜底
|
|
||||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
|
|
||||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
|
||||||
this.salesData = {
|
|
||||||
...salesItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unitPriceData = {
|
|
||||||
...unitPriceItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调试:确认整合后的数据
|
|
||||||
console.log('整合flag后的销量数据:', this.salesData);
|
|
||||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topItem-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 205px;
|
|
||||||
background: #F9FCFF;
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-wrap {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
height: 32px;
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 32px;
|
|
||||||
color: #0B58FF;
|
|
||||||
line-height: 32px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
width: 97px;
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -173,26 +173,62 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,45 +261,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
width: 1220px;
|
width: 1220px;
|
||||||
background-color: rgba(249, 252, 255, 1);
|
background-color: rgba(249, 252, 255, 1);
|
||||||
">
|
">
|
||||||
<!-- <top-item /> -->
|
|
||||||
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
|
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,23 +87,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
*/
|
*/
|
||||||
@@ -118,7 +100,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -139,7 +121,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
|
||||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
|
||||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
|
||||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
|
||||||
<div class="dashboard">
|
|
||||||
<div class="title">
|
|
||||||
累计完成率
|
|
||||||
</div>
|
|
||||||
<div class="number">
|
|
||||||
<div class="yield">
|
|
||||||
{{ ytdData?.rate || 0}}%
|
|
||||||
</div>
|
|
||||||
<div class="mom">
|
|
||||||
同比{{ ytdData?.yoyRate || 0}}%
|
|
||||||
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :id=" 'totalG' " :detailData="ytdData" id="totalGauge"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="line" style="padding: 0px;">
|
|
||||||
<verticalBarChart :refName=" 'totalVerticalBarChart' " :detailData="ytdData">
|
|
||||||
</verticalBarChart>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import electricityGauge from './electricityGauge.vue'
|
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
|
||||||
ytdData: { // 接收父组件传递的设备数据数组
|
|
||||||
type: Object,
|
|
||||||
default: () => {} // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
title: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
month: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
itemData: {
|
|
||||||
handler(newValue, oldValue) {
|
|
||||||
// this.updateChart()
|
|
||||||
},
|
|
||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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: 264px;
|
|
||||||
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 {
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 46px;
|
|
||||||
color: #0B58FF;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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> -->
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="width: 100%; height: 210px;position: relative;">
|
|
||||||
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:detailData.flags>0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
myChart: null // 存储图表实例,避免重复创建
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
// 明确接收的props结构,增强可读性
|
|
||||||
refName: {
|
|
||||||
type: String,
|
|
||||||
default: () => 'verticalBarChart',
|
|
||||||
},
|
|
||||||
detailData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateChart();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 新增:监听 chartData 变化
|
|
||||||
watch: {
|
|
||||||
// 深度监听数据变化,仅更新图表配置(不销毁实例)
|
|
||||||
detailData: {
|
|
||||||
handler() {
|
|
||||||
console.log(this.chartData, 'chartData');
|
|
||||||
|
|
||||||
this.updateChart();
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true // 初始化时立即执行
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
updateChart() {
|
|
||||||
const chartDom = this.$refs[this.refName];
|
|
||||||
if (!chartDom) {
|
|
||||||
console.error('图表容器未找到!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.myChart) {
|
|
||||||
this.myChart.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.myChart = echarts.init(chartDom);
|
|
||||||
const diff = this.detailData.diff || 0
|
|
||||||
const rate = this.detailData.rate || 0
|
|
||||||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: {
|
|
||||||
type: 'cross',
|
|
||||||
label: {
|
|
||||||
backgroundColor: '#6a7985'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
top: 40,
|
|
||||||
bottom: 15,
|
|
||||||
right: 80,
|
|
||||||
left: 10,
|
|
||||||
containLabel: true,
|
|
||||||
show: false // 隐藏grid背景,避免干扰
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
|
||||||
type: 'value',
|
|
||||||
// offset: 0,
|
|
||||||
// boundaryGap: true ,
|
|
||||||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
|
||||||
axisTick: { show: false },
|
|
||||||
min: 0,
|
|
||||||
//
|
|
||||||
splitNumber: 4,
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0, 0, 0, 0.45)',
|
|
||||||
fontSize: 12,
|
|
||||||
interval: 0,
|
|
||||||
padding: [5, 0, 0, 0]
|
|
||||||
},
|
|
||||||
// data: xData // 数值轴不需要手动设置data,由series的数据自动生成
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'category',
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0, 0, 0, 0.75)',
|
|
||||||
fontSize: 12,
|
|
||||||
interval: 0,
|
|
||||||
padding: [5, 0, 0, 0]
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true, // 显示Y轴轴线(关键)
|
|
||||||
lineStyle: {
|
|
||||||
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
|
|
||||||
width: 1, // 轴线宽度
|
|
||||||
type: 'solid' // 实线(可选:dashed虚线、dotted点线)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: { show: false },
|
|
||||||
// padding: [300, 100, 100, 100],
|
|
||||||
data: ['实际', '预算'] // y轴分类:实际、预算
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
// name: '预算',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: 24,
|
|
||||||
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
|
|
||||||
// 数据长度与yAxis的分类数量匹配(实际、预算各一个值)
|
|
||||||
data: [{
|
|
||||||
value: this.detailData.real,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'right',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: flagValue === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
value: this.detailData.target,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'right',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
// 预算的渐变颜色(蓝系渐变)
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 1, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: '#82CCFF' }, // 浅蓝
|
|
||||||
{ offset: 1, color: '#4B9DFF' } // 深蓝
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
},],
|
|
||||||
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
option && this.myChart.setOption(option);
|
|
||||||
|
|
||||||
// 窗口缩放适配和销毁逻辑保持不变
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
this.myChart && this.myChart.resize();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$once('hook:destroyed', () => {
|
|
||||||
window.removeEventListener('resize', () => {
|
|
||||||
this.myChart && this.myChart.resize();
|
|
||||||
});
|
|
||||||
this.myChart && this.myChart.dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
<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; width: 100%;">
|
|
||||||
<!-- 销量模块(直接传递整合了flag的salesData) -->
|
|
||||||
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
销量·万㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[0].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[0].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 单价模块(直接传递整合了flag的unitPriceData) -->
|
|
||||||
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
单价·元/㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[1].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[1].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, operatingSingleBar },
|
|
||||||
props: {
|
|
||||||
ytdAnalysis: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [
|
|
||||||
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dateData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
// 初始化数据包含flag字段
|
|
||||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
|
||||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
ytdAnalysis: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.updateChart(newVal)
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.updateChart(this.ytdAnalysis)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDashboardClick(path) {
|
|
||||||
this.$router.push({
|
|
||||||
path: path,
|
|
||||||
query: {
|
|
||||||
factory: this.$route.query.factory ? this.$route.query.factory : 5,
|
|
||||||
dateData: this.dateData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 判断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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
|
||||||
// 数据兜底
|
|
||||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
|
|
||||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
|
||||||
this.salesData = {
|
|
||||||
...salesItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unitPriceData = {
|
|
||||||
...unitPriceItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调试:确认整合后的数据
|
|
||||||
console.log('整合flag后的销量数据:', this.salesData);
|
|
||||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topItem-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 205px;
|
|
||||||
background: #F9FCFF;
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-wrap {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
height: 32px;
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 32px;
|
|
||||||
color: #0B58FF;
|
|
||||||
line-height: 32px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
width: 97px;
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -128,10 +128,6 @@ export default {
|
|||||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// sortChange(value) {
|
|
||||||
// this.sort = value
|
|
||||||
// this.getData()
|
|
||||||
// },
|
|
||||||
getData() {
|
getData() {
|
||||||
getElectricityCostAnalysisData({
|
getElectricityCostAnalysisData({
|
||||||
startTime: this.dateData.startTime,
|
startTime: this.dateData.startTime,
|
||||||
@@ -191,27 +187,6 @@ export default {
|
|||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
},
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -52,14 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -69,20 +61,11 @@ import screenfull from "screenfull";
|
|||||||
import changeBase from "../components/changeBase.vue";
|
import changeBase from "../components/changeBase.vue";
|
||||||
import monthlyOverview from "../electricityCostAnalysisComponents/monthlyOverview.vue";
|
import monthlyOverview from "../electricityCostAnalysisComponents/monthlyOverview.vue";
|
||||||
import totalOverview from "../electricityCostAnalysisComponents/totalOverview.vue";
|
import totalOverview from "../electricityCostAnalysisComponents/totalOverview.vue";
|
||||||
// import totalOverview from "../operatingComponents/totalOverview.vue";
|
|
||||||
import relatedIndicatorsAnalysis from "../electricityCostAnalysisComponents/relatedIndicatorsAnalysis.vue";
|
import relatedIndicatorsAnalysis from "../electricityCostAnalysisComponents/relatedIndicatorsAnalysis.vue";
|
||||||
import dataTrend from "../electricityCostAnalysisComponents/dataTrend.vue";
|
import dataTrend from "../electricityCostAnalysisComponents/dataTrend.vue";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getElectricityCostAnalysisFData } from '@/api/cockpit'
|
import { getElectricityCostAnalysisFData } from '@/api/cockpit'
|
||||||
// import PSDO from "./components/PSDO.vue";
|
|
||||||
// import psiLineChart from "./components/psiLineChart.vue";
|
|
||||||
|
|
||||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
|
||||||
// import orderProgress from "./components/orderProgress.vue";
|
|
||||||
// import keyWork from "./components/keyWork.vue";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
// import html2canvas from 'html2canvas'
|
|
||||||
// import JsPDF from 'jspdf'
|
|
||||||
export default {
|
export default {
|
||||||
name: "electricityCostAnalysisBase",
|
name: "electricityCostAnalysisBase",
|
||||||
components: {
|
components: {
|
||||||
@@ -108,8 +91,7 @@ export default {
|
|||||||
totalData: {},
|
totalData: {},
|
||||||
trend: [],
|
trend: [],
|
||||||
relatedData: {},
|
relatedData: {},
|
||||||
trendName: '原片电费',
|
trendName: '原片电费'
|
||||||
// cusProData: {},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -275,35 +257,12 @@ export default {
|
|||||||
},
|
},
|
||||||
changeDate(val) {
|
changeDate(val) {
|
||||||
this.date = val;
|
this.date = val;
|
||||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
|
||||||
// this.getData()
|
|
||||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||||
this.loopTime();
|
this.loopTime();
|
||||||
} else {
|
} else {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// () {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -30,11 +30,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {};
|
||||||
// 移除:原 chartData 定义,改为计算属性
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
// 移除:原 watch 监听配置,计算属性自动响应 trendData 变化
|
|
||||||
computed: {
|
computed: {
|
||||||
/**
|
/**
|
||||||
* chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组
|
* chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组
|
||||||
@@ -58,7 +55,7 @@ export default {
|
|||||||
diffValueArr.push(item.diff ?? 0);
|
diffValueArr.push(item.diff ?? 0);
|
||||||
targetValueArr.push(item.budget ?? 0);
|
targetValueArr.push(item.budget ?? 0);
|
||||||
proportionArr.push(item.rate ?? 0);
|
proportionArr.push(item.rate ?? 0);
|
||||||
completedArr.push(item.rate && item.rate>=100 ? 1 : 0);
|
completedArr.push(item.rate>=100 ? 1 : 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组装并返回格式化后的数据(结构与原一致)
|
// 组装并返回格式化后的数据(结构与原一致)
|
||||||
|
|||||||
@@ -142,28 +142,63 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 14,
|
barWidth: 14,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
||||||
|
const safeFlag = data.completed || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.value || [] // 实际销量(万元)
|
||||||
|
},
|
||||||
|
// 4. 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (data.value || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [0, 0],
|
offset: [0, 0],
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
width: 68,
|
||||||
height: 20,
|
height: 20,
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
formatter: () => {
|
||||||
formatter: (params) => {
|
|
||||||
const diff = data.diffValue || [];
|
const diff = data.diffValue || [];
|
||||||
const flags = data.completed || [];
|
const flags = data.completed || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -197,48 +232,21 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
|
||||||
const safeFlag = data.completed || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.value || [] // 实际销量(万元)
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge id="month" :detailData="factoryData"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
||||||
@@ -84,12 +81,7 @@ export default {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
}
|
||||||
/**
|
|
||||||
* 判断完成率对应的flag值(<100为0,≥100为1)
|
|
||||||
* @param {number} rate 完成率(原始值,如89代表89%)
|
|
||||||
* @returns {0|1} flag值
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -174,26 +174,62 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -226,45 +262,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -101,21 +101,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
|
||||||
* 核心方法:按levelId匹配地名生成locations
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
processChartData() {
|
processChartData() {
|
||||||
// 初始化空数据结构
|
// 初始化空数据结构
|
||||||
const initTopBarData = {
|
const initTopBarData = {
|
||||||
@@ -132,7 +117,7 @@ export default {
|
|||||||
topBarData.targets.push(level1Data.target || 0);
|
topBarData.targets.push(level1Data.target || 0);
|
||||||
topBarData.reals.push(level1Data.real || 0);
|
topBarData.reals.push(level1Data.real || 0);
|
||||||
topBarData.rate.push(level1Data.rate || 0);
|
topBarData.rate.push(level1Data.rate || 0);
|
||||||
topBarData.flags.push(this.getRateFlag(level1Data.rate, level1Data.real, level1Data.target));
|
topBarData.flags.push(level1Data.rate >= 100 ? 1 : 0);
|
||||||
// 2. 处理levelId≠1的整合数据(核心:levelId匹配地名)
|
// 2. 处理levelId≠1的整合数据(核心:levelId匹配地名)
|
||||||
const barData = { ...initBarData };
|
const barData = { ...initBarData };
|
||||||
// 筛选有效数据:levelId≠1 且 levelId在baseIndexToNameMap中
|
// 筛选有效数据:levelId≠1 且 levelId在baseIndexToNameMap中
|
||||||
@@ -145,7 +130,7 @@ export default {
|
|||||||
barData.targets.push(item.target || 0);
|
barData.targets.push(item.target || 0);
|
||||||
barData.reals.push(item.real || 0);
|
barData.reals.push(item.real || 0);
|
||||||
barData.rate.push(item.rate || 0);
|
barData.rate.push(item.rate || 0);
|
||||||
barData.flags.push(this.getRateFlag(item.rate, item.real, item.target));
|
barData.flags.push(item.rate >= 100 ? 1 : 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. 更新chartData
|
// 3. 更新chartData
|
||||||
|
|||||||
@@ -101,21 +101,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
|
||||||
* 核心方法:按levelId匹配地名生成locations
|
|
||||||
*/
|
|
||||||
getRateFlag(rate, real, budget) {
|
|
||||||
if (isNaN(rate) || rate === null || rate === undefined) return 0;
|
|
||||||
|
|
||||||
// 1. 完成率 >= 100 => 达标
|
|
||||||
if (rate >= 100) return 1;
|
|
||||||
|
|
||||||
// 2. 完成率 = 0 且 (目标值=0 或 实际值=目标值=0) => 达标
|
|
||||||
// if (rate === 0 && budget === 0) return 1;
|
|
||||||
|
|
||||||
// 其他情况 => 未达标
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
processChartData() {
|
processChartData() {
|
||||||
// 初始化空数据结构
|
// 初始化空数据结构
|
||||||
const initTopBarData = {
|
const initTopBarData = {
|
||||||
@@ -132,7 +117,7 @@ export default {
|
|||||||
topBarData.targets.push(level1Data.target || 0);
|
topBarData.targets.push(level1Data.target || 0);
|
||||||
topBarData.reals.push(level1Data.real || 0);
|
topBarData.reals.push(level1Data.real || 0);
|
||||||
topBarData.rate.push(level1Data.rate || 0);
|
topBarData.rate.push(level1Data.rate || 0);
|
||||||
topBarData.flags.push(this.getRateFlag(level1Data.rate, level1Data.real, level1Data.target));
|
topBarData.flags.push(level1Data.rate >= 100 ? 1 : 0);
|
||||||
|
|
||||||
// 2. 处理levelId≠1的整合数据(核心:levelId匹配地名)
|
// 2. 处理levelId≠1的整合数据(核心:levelId匹配地名)
|
||||||
const barData = { ...initBarData };
|
const barData = { ...initBarData };
|
||||||
@@ -146,7 +131,7 @@ export default {
|
|||||||
barData.targets.push(item.budget || 0);
|
barData.targets.push(item.budget || 0);
|
||||||
barData.reals.push(item.real || 0);
|
barData.reals.push(item.real || 0);
|
||||||
barData.rate.push(item.rate || 0);
|
barData.rate.push(item.rate || 0);
|
||||||
barData.flags.push(this.getRateFlag(item.rate, item.real, item.budget));
|
barData.flags.push(item.rate >= 100 ? 1 : 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. 更新chartData
|
// 3. 更新chartData
|
||||||
|
|||||||
@@ -28,9 +28,6 @@
|
|||||||
import Container from '../components/container.vue'
|
import Container from '../components/container.vue'
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
import operatingSingleBar from './operatingSingleBar.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, operatingSingleBar },
|
components: { Container, operatingSingleBar },
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge id="year" :detailData="factoryData"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
||||||
@@ -37,9 +34,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -87,12 +81,7 @@ export default {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
}
|
||||||
/**
|
|
||||||
* 判断完成率对应的flag值(<100为0,≥100为1)
|
|
||||||
* @param {number} rate 完成率(原始值,如89代表89%)
|
|
||||||
* @returns {0|1} flag值
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,39 +26,26 @@
|
|||||||
grid-template-columns: 1624px;
|
grid-template-columns: 1624px;
|
||||||
">
|
">
|
||||||
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
|
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
|
||||||
<!-- <keyWork /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ReportHeader from "../components/noRouterHeader.vue";
|
import ReportHeader from "../components/noRouterHeader.vue";
|
||||||
import { Sidebar } from "../../../layout/components";
|
import { Sidebar } from "../../../layout/components";
|
||||||
import screenfull from "screenfull";
|
import screenfull from "screenfull";
|
||||||
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
|
|
||||||
// import premProdStatus from "./components/premProdStatus.vue";
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import operatingLineChart from "../expenseAnalysisComponents/operatingLineChart";
|
import operatingLineChart from "../expenseAnalysisComponents/operatingLineChart";
|
||||||
import operatingLineChartCumulative from "../expenseAnalysisComponents/operatingLineChartCumulative.vue";
|
import operatingLineChartCumulative from "../expenseAnalysisComponents/operatingLineChartCumulative.vue";
|
||||||
|
|
||||||
import { getExpenseAnalysisGroupData } from '@/api/cockpit'
|
import { getExpenseAnalysisGroupData } from '@/api/cockpit'
|
||||||
import moment from "moment";
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
ReportHeader,
|
ReportHeader,
|
||||||
operatingLineChartCumulative,
|
operatingLineChartCumulative,
|
||||||
operatingLineChart,
|
operatingLineChart,
|
||||||
// premProdStatus,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -152,12 +139,6 @@ export default {
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.monthData = res.data.month
|
this.monthData = res.data.month
|
||||||
this.ytdData = res.data.ytd
|
this.ytdData = res.data.ytd
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -207,28 +188,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -52,14 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -69,7 +61,6 @@ import screenfull from "screenfull";
|
|||||||
import changeBase from "../components/changeBase.vue";
|
import changeBase from "../components/changeBase.vue";
|
||||||
import monthlyOverview from "../expenseAnalysisComponents/monthlyOverview.vue";
|
import monthlyOverview from "../expenseAnalysisComponents/monthlyOverview.vue";
|
||||||
import totalOverview from "../expenseAnalysisComponents/totalOverview.vue";
|
import totalOverview from "../expenseAnalysisComponents/totalOverview.vue";
|
||||||
// import totalOverview from "../operatingComponents/totalOverview.vue";
|
|
||||||
import monthlyRelatedMetrics from "../expenseAnalysisComponents/monthlyRelatedMetrics.vue";
|
import monthlyRelatedMetrics from "../expenseAnalysisComponents/monthlyRelatedMetrics.vue";
|
||||||
import yearRelatedMetrics from "../expenseAnalysisComponents/yearRelatedMetrics.vue";
|
import yearRelatedMetrics from "../expenseAnalysisComponents/yearRelatedMetrics.vue";
|
||||||
import dataTrend from "../expenseAnalysisComponents/dataTrend.vue";
|
import dataTrend from "../expenseAnalysisComponents/dataTrend.vue";
|
||||||
@@ -77,15 +68,7 @@ import dataTrend from "../expenseAnalysisComponents/dataTrend.vue";
|
|||||||
import profitLineChart from "../costComponents/profitLineChart.vue";
|
import profitLineChart from "../costComponents/profitLineChart.vue";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getExpenseAnalysisFactoryData } from '@/api/cockpit'
|
import { getExpenseAnalysisFactoryData } from '@/api/cockpit'
|
||||||
// import PSDO from "./components/PSDO.vue";
|
|
||||||
// import psiLineChart from "./components/psiLineChart.vue";
|
|
||||||
|
|
||||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
|
||||||
// import orderProgress from "./components/orderProgress.vue";
|
|
||||||
// import keyWork from "./components/keyWork.vue";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
// import html2canvas from 'html2canvas'
|
|
||||||
// import JsPDF from 'jspdf'
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
@@ -289,28 +272,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// () {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,20 +26,7 @@ export default {
|
|||||||
trend: {
|
trend: {
|
||||||
type: Array,
|
type: Array,
|
||||||
// 默认值与实际数据结构一致(12个月)
|
// 默认值与实际数据结构一致(12个月)
|
||||||
default: () => [
|
default: () => []
|
||||||
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -98,7 +85,7 @@ export default {
|
|||||||
const diff = Number(item.diff) || 0;
|
const diff = Number(item.diff) || 0;
|
||||||
|
|
||||||
// 计算达标标识(≥100 → 1,<100 → 0)
|
// 计算达标标识(≥100 → 1,<100 → 0)
|
||||||
const flag = this.getRateFlag(rate, real, budget);
|
const flag = item.rate >= 100 ? 1 : 0;
|
||||||
|
|
||||||
// 填充数组
|
// 填充数组
|
||||||
months.push(month);
|
months.push(month);
|
||||||
@@ -121,24 +108,6 @@ export default {
|
|||||||
|
|
||||||
console.log('处理后的趋势数据:', this.chartData);
|
console.log('处理后的趋势数据:', this.chartData);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算达标标识
|
|
||||||
* @param {Number} rate - 完成率(原始值,如1.2 → 120%)
|
|
||||||
* @returns {Number} 1: 达标(≥100%),0: 未达标(<100%)
|
|
||||||
*/
|
|
||||||
getRateFlag(rate, real, target) {
|
|
||||||
// 先处理无效值的情况
|
|
||||||
if (isNaN(rate) || rate === null || rate === undefined) return 0;
|
|
||||||
|
|
||||||
// 实际值和目标值都为0时,算作达标
|
|
||||||
if (real === 0 && target === 0 && rate === 0) {
|
|
||||||
return 1; // 达标
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他情况:rate >= 100 或 rate === 0 时达标
|
|
||||||
return (rate >= 100) ? 1 : 0;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -145,86 +145,7 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 14,
|
barWidth: 14,
|
||||||
label: {
|
label: { show: false },
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
|
||||||
height: 20,
|
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
|
||||||
formatter: function (params) {
|
|
||||||
const diff = data.diffs || [];
|
|
||||||
const flags = data.flags || [];
|
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
|
|
||||||
// const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
|
||||||
} else {
|
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backgroundColor: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
x2: 0,
|
|
||||||
y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' }, // 顶部0px位置:阴影最强
|
|
||||||
// { offset: 0.1, color: 'rgba(205, 215, 224, 0.4)' }, // 1px位置:阴影减弱(对应1px)
|
|
||||||
// { offset: 0.15, color: 'rgba(205, 215, 224, 0.6)' }, // 3px位置:阴影几乎消失(对应3px扩散)
|
|
||||||
{ offset: 0.2, color: '#ffffff' }, // 主体白色
|
|
||||||
{ offset: 1, color: '#ffffff' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 外阴影:0px 2px 2px 0px rgba(191,203,215,0.5)
|
|
||||||
shadowColor: 'rgba(191,203,215,0.5)',
|
|
||||||
shadowBlur: 2,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
// 圆角:4px
|
|
||||||
borderRadius: 4,
|
|
||||||
// 移除边框
|
|
||||||
borderColor: '#BFCBD577',
|
|
||||||
borderWidth: 0,
|
|
||||||
// 文字垂直居中(针对富文本)
|
|
||||||
lineHeight: 20,
|
|
||||||
rich: {
|
|
||||||
text: {
|
|
||||||
// 缩小宽度和内边距,适配68px容器
|
|
||||||
width: 'auto', // 自动宽度,替代固定40px
|
|
||||||
padding: [5, 10, 5, 0], // 缩小内边距
|
|
||||||
align: 'center',
|
|
||||||
color: '#464646', // 文字灰色
|
|
||||||
fontSize: 11, // 缩小字体,适配小尺寸
|
|
||||||
lineHeight: 20 // 垂直居中
|
|
||||||
},
|
|
||||||
achieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: (params) => {
|
color: (params) => {
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
||||||
@@ -252,6 +173,82 @@ export default {
|
|||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
},
|
},
|
||||||
data: data.reals // 实际销量(万元)
|
data: data.reals // 实际销量(万元)
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
offset: [0, 0],
|
||||||
|
width: 68,
|
||||||
|
height: 20,
|
||||||
|
formatter: () => {
|
||||||
|
const diff = data.diffs || [];
|
||||||
|
const flags = data.flags || [];
|
||||||
|
const currentDiff = diff[index] || 0;
|
||||||
|
const currentFlag = flags[index] || 0;
|
||||||
|
if (currentFlag === 1) {
|
||||||
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
|
} else {
|
||||||
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColor: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
||||||
|
{ offset: 0.2, color: '#ffffff' },
|
||||||
|
{ offset: 1, color: '#ffffff' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
shadowColor: 'rgba(191,203,215,0.5)',
|
||||||
|
shadowBlur: 2,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 2,
|
||||||
|
borderRadius: 4,
|
||||||
|
borderColor: '#BFCBD577',
|
||||||
|
borderWidth: 0,
|
||||||
|
lineHeight: 20,
|
||||||
|
rich: {
|
||||||
|
text: {
|
||||||
|
width: 'auto',
|
||||||
|
padding: [5, 10, 5, 0],
|
||||||
|
align: 'center',
|
||||||
|
color: '#464646',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 20
|
||||||
|
},
|
||||||
|
achieved: {
|
||||||
|
width: 'auto',
|
||||||
|
padding: [5, 0, 5, 10],
|
||||||
|
align: 'center',
|
||||||
|
color: '#76DABE',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 20
|
||||||
|
},
|
||||||
|
unachieved: {
|
||||||
|
width: 'auto',
|
||||||
|
padding: [5, 0, 5, 10],
|
||||||
|
align: 'center',
|
||||||
|
color: '#F9A44A',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :detailData="monthData">
|
<verticalBarChart :detailData="monthData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -65,29 +59,6 @@ export default {
|
|||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
// itemData: {
|
|
||||||
// handler(newValue, oldValue) {
|
|
||||||
// // this.updateChart()
|
|
||||||
// },
|
|
||||||
// deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||||
// this.$nextTick(() => this.updateChart())
|
// this.$nextTick(() => this.updateChart())
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag(data.rate, data.real, data.budget),
|
flag: data.rate>=100?1:0
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -100,18 +100,6 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true // 初始化立即执行
|
immediate: true // 初始化立即执行
|
||||||
}
|
}
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
methods: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -173,26 +173,58 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
// const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,45 +257,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,23 +86,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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 (real <= target) return 1;
|
|
||||||
|
|
||||||
// 其他情况 => 未达标
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
*/
|
*/
|
||||||
@@ -116,7 +99,7 @@ export default {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -137,7 +120,7 @@ export default {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -81,23 +81,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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 (real <= target) return 1;
|
|
||||||
|
|
||||||
// 其他情况 => 未达标
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
*/
|
*/
|
||||||
@@ -111,7 +94,7 @@ export default {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate > 100 ? 1 : 0];
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
groupDiff,
|
groupDiff,
|
||||||
@@ -131,7 +114,7 @@ export default {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate > 100 ? 1 : 0);
|
||||||
console.log('factoryFlags', factoryFlags);
|
console.log('factoryFlags', factoryFlags);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
|
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
|
||||||
@@ -38,13 +35,9 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
props: {
|
||||||
ytdData: { // 接收父组件传递的设备数据数组
|
ytdData: { // 接收父组件传递的设备数据数组
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -73,21 +66,6 @@ export default {
|
|||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||||
// this.$nextTick(() => this.updateChart())
|
// this.$nextTick(() => this.updateChart())
|
||||||
|
|||||||
@@ -50,18 +50,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
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;
|
|
||||||
},
|
|
||||||
updateChart() {
|
updateChart() {
|
||||||
const chartDom = this.$refs[this.refName];
|
const chartDom = this.$refs[this.refName];
|
||||||
if (!chartDom) {
|
if (!chartDom) {
|
||||||
@@ -76,7 +64,7 @@ export default {
|
|||||||
this.myChart = echarts.init(chartDom);
|
this.myChart = echarts.init(chartDom);
|
||||||
const diff = this.detailData.diff || 0
|
const diff = this.detailData.diff || 0
|
||||||
const rate = this.detailData.rate || 0
|
const rate = this.detailData.rate || 0
|
||||||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
const flagValue = this.detailData.rate >= 100 ? 1 : 0
|
||||||
this.flag = flagValue
|
this.flag = flagValue
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
@@ -87,17 +75,7 @@ export default {
|
|||||||
label: {
|
label: {
|
||||||
backgroundColor: '#6a7985'
|
backgroundColor: '#6a7985'
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 40,
|
top: 40,
|
||||||
@@ -110,9 +88,6 @@ export default {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
||||||
type: 'value',
|
type: 'value',
|
||||||
// offset: 0,
|
|
||||||
// boundaryGap: true ,
|
|
||||||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
|
||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
min: 0,
|
min: 0,
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag(data.rate, data.real, data.budget),
|
flag: data.rate>=100?1:0
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -105,18 +105,6 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true // 初始化立即执行
|
immediate: true // 初始化立即执行
|
||||||
}
|
}
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
methods: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,26 +25,15 @@
|
|||||||
grid-template-columns: 1624px;
|
grid-template-columns: 1624px;
|
||||||
">
|
">
|
||||||
<operatingLineChartCumulative :dateData="dateData" :totalData="totalData" />
|
<operatingLineChartCumulative :dateData="dateData" :totalData="totalData" />
|
||||||
<!-- <keyWork /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ReportHeader from "../components/noRouterHeader.vue";
|
import ReportHeader from "../components/noRouterHeader.vue";
|
||||||
import { Sidebar } from "../../../layout/components";
|
import { Sidebar } from "../../../layout/components";
|
||||||
import screenfull from "screenfull";
|
import screenfull from "screenfull";
|
||||||
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
|
|
||||||
// import premProdStatus from "./components/premProdStatus.vue";
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import operatingLineChart from "../fullCostAnalysisComponents/operatingLineChart";
|
import operatingLineChart from "../fullCostAnalysisComponents/operatingLineChart";
|
||||||
import operatingLineChartCumulative from "../fullCostAnalysisComponents/operatingLineChartCumulative.vue";
|
import operatingLineChartCumulative from "../fullCostAnalysisComponents/operatingLineChartCumulative.vue";
|
||||||
@@ -57,7 +46,6 @@ export default {
|
|||||||
ReportHeader,
|
ReportHeader,
|
||||||
operatingLineChartCumulative,
|
operatingLineChartCumulative,
|
||||||
operatingLineChart,
|
operatingLineChart,
|
||||||
// premProdStatus,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -97,13 +85,11 @@ export default {
|
|||||||
variables() {
|
variables() {
|
||||||
return variables;
|
return variables;
|
||||||
},
|
},
|
||||||
// ...mapGetters(['sidebar']),
|
|
||||||
styles() {
|
styles() {
|
||||||
const v = Math.floor(this.value * this.beilv * 100) / 10000;
|
const v = Math.floor(this.value * this.beilv * 100) / 10000;
|
||||||
return {
|
return {
|
||||||
transform: `scale(${v})`,
|
transform: `scale(${v})`,
|
||||||
transformOrigin: "left top",
|
transformOrigin: "left top",
|
||||||
// overflow: hidden;
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -148,12 +134,6 @@ export default {
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.thisMonData = res.data.thisMonData
|
this.thisMonData = res.data.thisMonData
|
||||||
this.totalData = res.data.totalData
|
this.totalData = res.data.totalData
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -203,28 +183,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -52,14 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -69,20 +61,11 @@ import screenfull from "screenfull";
|
|||||||
import changeBase from "../components/changeBase.vue";
|
import changeBase from "../components/changeBase.vue";
|
||||||
import monthlyOverview from "../fullCostAnalysisComponents/monthlyOverview.vue";
|
import monthlyOverview from "../fullCostAnalysisComponents/monthlyOverview.vue";
|
||||||
import totalOverview from "../fullCostAnalysisComponents/totalOverview.vue";
|
import totalOverview from "../fullCostAnalysisComponents/totalOverview.vue";
|
||||||
// import totalOverview from "../operatingComponents/totalOverview.vue";
|
|
||||||
import relatedIndicatorsAnalysis from "../fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue";
|
import relatedIndicatorsAnalysis from "../fullCostAnalysisComponents/relatedIndicatorsAnalysis.vue";
|
||||||
import dataTrend from "../fullCostAnalysisComponents/dataTrend.vue";
|
import dataTrend from "../fullCostAnalysisComponents/dataTrend.vue";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
||||||
// import PSDO from "./components/PSDO.vue";
|
|
||||||
// import psiLineChart from "./components/psiLineChart.vue";
|
|
||||||
|
|
||||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
|
||||||
// import orderProgress from "./components/orderProgress.vue";
|
|
||||||
// import keyWork from "./components/keyWork.vue";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
// import html2canvas from 'html2canvas'
|
|
||||||
// import JsPDF from 'jspdf'
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
@@ -93,7 +76,6 @@ export default {
|
|||||||
totalOverview,
|
totalOverview,
|
||||||
dataTrend,
|
dataTrend,
|
||||||
relatedIndicatorsAnalysis
|
relatedIndicatorsAnalysis
|
||||||
// psiLineChart
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -108,7 +90,6 @@ export default {
|
|||||||
totalData: {},
|
totalData: {},
|
||||||
trend: {},
|
trend: {},
|
||||||
relatedData: {},
|
relatedData: {},
|
||||||
// cusProData: {},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -192,34 +173,19 @@ export default {
|
|||||||
const requestParams = {
|
const requestParams = {
|
||||||
startTime: this.dateData.startTime,
|
startTime: this.dateData.startTime,
|
||||||
endTime: this.dateData.endTime,
|
endTime: this.dateData.endTime,
|
||||||
// index: this.index,
|
|
||||||
// sort: 1,
|
|
||||||
paramName: '全成本',
|
paramName: '全成本',
|
||||||
paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'],
|
paramList: ['制造成本', '财务费用', '销售费用', '管理费用', '运费'],
|
||||||
baseId: this.factory,
|
baseId: this.factory,
|
||||||
// baseId: Number(this.factory),
|
|
||||||
};
|
};
|
||||||
// 调用接口
|
// 调用接口
|
||||||
getUnitPriceAnalysisBaseData(requestParams).then((res) => {
|
getUnitPriceAnalysisBaseData(requestParams).then((res) => {
|
||||||
this.monData = res.data.monData
|
this.monData = res.data.monData
|
||||||
this.totalData = res.data.totalData
|
this.totalData = res.data.totalData
|
||||||
// this.relatedMon = res.data.relatedMon
|
|
||||||
this.relatedData = {
|
this.relatedData = {
|
||||||
relatedTotal: res.data.relatedTotal,
|
relatedTotal: res.data.relatedTotal,
|
||||||
relatedMon: res.data.relatedMon,
|
relatedMon: res.data.relatedMon,
|
||||||
}
|
}
|
||||||
this.trend = res.data.trend
|
this.trend = res.data.trend
|
||||||
// this.relatedTotal = res.data.relatedTotal
|
|
||||||
// this.cusProData = {
|
|
||||||
// customerPriceMon: res.data.customerPriceMon,
|
|
||||||
// customerPriceTotal: res.data.customerPriceTotal,
|
|
||||||
// customerSaleMon: res.data.customerSaleMon,
|
|
||||||
// customerSaleTotal: res.data.customerSaleTotal,
|
|
||||||
// productMonSale: res.data.productMonSale,
|
|
||||||
// productPriceMon: res.data.productPriceMon,
|
|
||||||
// productPriceTotal: res.data.productPriceTotal,
|
|
||||||
// productTotalSale: res.data.productTotalSale,
|
|
||||||
// }
|
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -229,7 +195,6 @@ export default {
|
|||||||
this.dateData = {
|
this.dateData = {
|
||||||
startTime: obj.startTime,
|
startTime: obj.startTime,
|
||||||
endTime: obj.endTime,
|
endTime: obj.endTime,
|
||||||
// mode: obj.mode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getData()
|
this.getData()
|
||||||
@@ -286,35 +251,12 @@ export default {
|
|||||||
},
|
},
|
||||||
changeDate(val) {
|
changeDate(val) {
|
||||||
this.date = val;
|
this.date = val;
|
||||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
|
||||||
// this.getData()
|
|
||||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||||
this.loopTime();
|
this.loopTime();
|
||||||
} else {
|
} else {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// () {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -110,9 +110,7 @@ export default {
|
|||||||
reals.push(monthData.real || 0);
|
reals.push(monthData.real || 0);
|
||||||
targets.push(monthData.target || 0);
|
targets.push(monthData.target || 0);
|
||||||
diffs.push(monthData.diff || 0);
|
diffs.push(monthData.diff || 0);
|
||||||
|
flags.push(completeRate>=100?1:0);
|
||||||
// 生成达标状态(复用 getRateFlag 逻辑)
|
|
||||||
flags.push(this.getRateFlag(completeRate, monthData.real, monthData.target));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -191,84 +189,7 @@ export default {
|
|||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 14,
|
barWidth: 14,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
|
||||||
height: 20,
|
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
|
||||||
formatter: function (params) {
|
|
||||||
const diff = diffs || [];
|
|
||||||
// const flags = flags || [];
|
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
console.log('currentFlag', flags, params.dataIndex);
|
|
||||||
|
|
||||||
// const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
|
||||||
} else {
|
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backgroundColor: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
x2: 0,
|
|
||||||
y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' }, // 顶部0px位置:阴影最强
|
|
||||||
// { offset: 0.1, color: 'rgba(205, 215, 224, 0.4)' }, // 1px位置:阴影减弱(对应1px)
|
|
||||||
// { offset: 0.15, color: 'rgba(205, 215, 224, 0.6)' }, // 3px位置:阴影几乎消失(对应3px扩散)
|
|
||||||
{ offset: 0.2, color: '#ffffff' }, // 主体白色
|
|
||||||
{ offset: 1, color: '#ffffff' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 外阴影:0px 2px 2px 0px rgba(191,203,215,0.5)
|
|
||||||
shadowColor: 'rgba(191,203,215,0.5)',
|
|
||||||
shadowBlur: 2,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
// 圆角:4px
|
|
||||||
borderRadius: 4,
|
|
||||||
// 移除边框
|
|
||||||
borderColor: '#BFCBD577',
|
|
||||||
borderWidth: 0,
|
|
||||||
// 文字垂直居中(针对富文本)
|
|
||||||
lineHeight: 20,
|
|
||||||
rich: {
|
|
||||||
text: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 10, 5, 0],
|
|
||||||
align: 'center',
|
|
||||||
color: '#464646',
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
achieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: (params) => {
|
color: (params) => {
|
||||||
@@ -295,6 +216,82 @@ export default {
|
|||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
},
|
},
|
||||||
data: reals, // 动态实际值
|
data: reals, // 动态实际值
|
||||||
|
},
|
||||||
|
// 4. 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
offset: [0, 0],
|
||||||
|
width: 68,
|
||||||
|
height: 20,
|
||||||
|
formatter: () => {
|
||||||
|
const currentDiff = diffs[index] || 0;
|
||||||
|
const currentFlag = flags[index] || 0;
|
||||||
|
if (currentFlag === 1) {
|
||||||
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
|
} else {
|
||||||
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColor: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
||||||
|
{ offset: 0.2, color: '#ffffff' },
|
||||||
|
{ offset: 1, color: '#ffffff' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
shadowColor: 'rgba(191,203,215,0.5)',
|
||||||
|
shadowBlur: 2,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowOffsetY: 2,
|
||||||
|
borderRadius: 4,
|
||||||
|
borderColor: '#BFCBD577',
|
||||||
|
borderWidth: 0,
|
||||||
|
lineHeight: 20,
|
||||||
|
rich: {
|
||||||
|
text: {
|
||||||
|
width: 'auto',
|
||||||
|
padding: [5, 10, 5, 0],
|
||||||
|
align: 'center',
|
||||||
|
color: '#464646',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 20
|
||||||
|
},
|
||||||
|
achieved: {
|
||||||
|
width: 'auto',
|
||||||
|
padding: [5, 0, 5, 10],
|
||||||
|
align: 'center',
|
||||||
|
color: '#76DABE',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 20
|
||||||
|
},
|
||||||
|
unachieved: {
|
||||||
|
width: 'auto',
|
||||||
|
padding: [5, 0, 5, 10],
|
||||||
|
align: 'center',
|
||||||
|
color: '#F9A44A',
|
||||||
|
fontSize: 11,
|
||||||
|
lineHeight: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -309,19 +306,6 @@ export default {
|
|||||||
this.unit = item.unit;
|
this.unit = item.unit;
|
||||||
this.isDropdownShow = false;
|
this.isDropdownShow = false;
|
||||||
},
|
},
|
||||||
// 复用达标状态判断方法
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,10 +17,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge"> -->
|
|
||||||
<!-- 传递包含flag的factoryData给仪表盘组件 -->
|
|
||||||
<!-- <electricityGauge id="month" :detailData="factoryData"></electricityGauge> -->
|
|
||||||
<!-- </div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
||||||
@@ -87,7 +83,7 @@ export default {
|
|||||||
target: 0,
|
target: 0,
|
||||||
thb: 0,
|
thb: 0,
|
||||||
...rawData,
|
...rawData,
|
||||||
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段
|
flag: rawData.completeRate >= 100 ? 1 : 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -101,23 +97,6 @@ export default {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断完成率对应的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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -174,26 +174,62 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
// const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -226,45 +262,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,24 +80,6 @@ export default {
|
|||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断完成率对应的flag值(<100为0,≥100为1)
|
|
||||||
* @param {number} rate 完成率(原始值,如80代表80%)
|
|
||||||
* @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. 实际小于等于目标) => 达标
|
|
||||||
if (real <= target) return 1;
|
|
||||||
|
|
||||||
// 其他情况 => 未达标
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
||||||
*/
|
*/
|
||||||
@@ -116,7 +98,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: [ksxnData.diff],
|
diff: [ksxnData.diff],
|
||||||
reals: [ksxnData.real],
|
reals: [ksxnData.real],
|
||||||
rate: [ksxnData.completeRate],
|
rate: [ksxnData.completeRate],
|
||||||
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)],
|
flags: [ksxnData.completeRate >= 100 ? 1 : 0],
|
||||||
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,7 +113,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
||||||
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
||||||
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
||||||
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识
|
flags: factoryDataList.map(item => item.completeRate >= 100 ? 1 : 0), // 完成率标识
|
||||||
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -80,23 +80,6 @@ export default {
|
|||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断完成率对应的flag值(<100为0,≥100为1)
|
|
||||||
* @param {number} rate 完成率(原始值,如80代表80%)
|
|
||||||
* @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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
||||||
@@ -116,7 +99,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: [ksxnData.diff],
|
diff: [ksxnData.diff],
|
||||||
reals: [ksxnData.real],
|
reals: [ksxnData.real],
|
||||||
rate: [ksxnData.completeRate],
|
rate: [ksxnData.completeRate],
|
||||||
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)],
|
flags: [ksxnData.completeRate>=100?1:0],
|
||||||
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,7 +114,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
||||||
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
||||||
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
||||||
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识
|
flags: factoryDataList.map(item => item.completeRate>=100?1:0), // 完成率标识
|
||||||
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,6 @@
|
|||||||
import Container from '../components/container.vue'
|
import Container from '../components/container.vue'
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
import operatingSingleBar from './operatingSingleBar.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, operatingSingleBar },
|
components: { Container, operatingSingleBar },
|
||||||
@@ -100,7 +97,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target),
|
flag:data.completeRate > 100 ? 1 : 0,
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -158,18 +155,6 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
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;
|
|
||||||
},
|
|
||||||
handleChange(value) {
|
handleChange(value) {
|
||||||
console.log('value', value, this.relatedData);
|
console.log('value', value, this.relatedData);
|
||||||
this.currentTab = value;
|
this.currentTab = value;
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge id="year" :detailData="factoryData"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :detailData="factoryData">
|
<verticalBarChart :detailData="factoryData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -90,7 +84,7 @@ export default {
|
|||||||
target: 0,
|
target: 0,
|
||||||
thb: 0,
|
thb: 0,
|
||||||
...rawData,
|
...rawData,
|
||||||
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段
|
flag: rawData.completeRate >= 100 ? 1 : 0 // 根据完成率计算flag
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -104,23 +98,6 @@ export default {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断完成率对应的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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,39 +26,26 @@
|
|||||||
grid-template-columns: 1624px;
|
grid-template-columns: 1624px;
|
||||||
">
|
">
|
||||||
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
|
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
|
||||||
<!-- <keyWork /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ReportHeader from "../components/noRouterHeader.vue";
|
import ReportHeader from "../components/noRouterHeader.vue";
|
||||||
import { Sidebar } from "../../../layout/components";
|
import { Sidebar } from "../../../layout/components";
|
||||||
import screenfull from "screenfull";
|
import screenfull from "screenfull";
|
||||||
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
|
|
||||||
// import premProdStatus from "./components/premProdStatus.vue";
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import operatingLineChart from "../grossMarginComponents/operatingLineChart";
|
import operatingLineChart from "../grossMarginComponents/operatingLineChart";
|
||||||
import operatingLineChartCumulative from "../grossMarginComponents/operatingLineChartCumulative.vue";
|
import operatingLineChartCumulative from "../grossMarginComponents/operatingLineChartCumulative.vue";
|
||||||
|
|
||||||
import { getGrossMarginGroupData } from '@/api/cockpit'
|
import { getGrossMarginGroupData } from '@/api/cockpit'
|
||||||
import moment from "moment";
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
ReportHeader,
|
ReportHeader,
|
||||||
operatingLineChartCumulative,
|
operatingLineChartCumulative,
|
||||||
operatingLineChart,
|
operatingLineChart,
|
||||||
// premProdStatus,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -157,12 +144,6 @@ export default {
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.monthData = res.data.month
|
this.monthData = res.data.month
|
||||||
this.ytdData = res.data.ytd
|
this.ytdData = res.data.ytd
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -212,28 +193,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -53,14 +53,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -70,7 +62,6 @@ import screenfull from "screenfull";
|
|||||||
import changeBase from "../components/changeBase.vue";
|
import changeBase from "../components/changeBase.vue";
|
||||||
import monthlyOverview from "../grossMarginComponents/monthlyOverview.vue";
|
import monthlyOverview from "../grossMarginComponents/monthlyOverview.vue";
|
||||||
import totalOverview from "../grossMarginComponents/totalOverview.vue";
|
import totalOverview from "../grossMarginComponents/totalOverview.vue";
|
||||||
// import totalOverview from "../operatingComponents/totalOverview.vue";
|
|
||||||
import monthlyRelatedMetrics from "../grossMarginComponents/monthlyRelatedMetrics.vue";
|
import monthlyRelatedMetrics from "../grossMarginComponents/monthlyRelatedMetrics.vue";
|
||||||
import yearRelatedMetrics from "../grossMarginComponents/yearRelatedMetrics.vue";
|
import yearRelatedMetrics from "../grossMarginComponents/yearRelatedMetrics.vue";
|
||||||
import dataTrend from "../grossMarginComponents/dataTrend.vue";
|
import dataTrend from "../grossMarginComponents/dataTrend.vue";
|
||||||
@@ -78,15 +69,7 @@ import dataTrend from "../grossMarginComponents/dataTrend.vue";
|
|||||||
import profitLineChart from "../costComponents/profitLineChart.vue";
|
import profitLineChart from "../costComponents/profitLineChart.vue";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getGrossMarginFactoryData } from '@/api/cockpit'
|
import { getGrossMarginFactoryData } from '@/api/cockpit'
|
||||||
// import PSDO from "./components/PSDO.vue";
|
|
||||||
// import psiLineChart from "./components/psiLineChart.vue";
|
|
||||||
|
|
||||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
|
||||||
// import orderProgress from "./components/orderProgress.vue";
|
|
||||||
// import keyWork from "./components/keyWork.vue";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
// import html2canvas from 'html2canvas'
|
|
||||||
// import JsPDF from 'jspdf'
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
@@ -284,35 +267,12 @@ export default {
|
|||||||
},
|
},
|
||||||
changeDate(val) {
|
changeDate(val) {
|
||||||
this.date = val;
|
this.date = val;
|
||||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
|
||||||
// this.getData()
|
|
||||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||||
this.loopTime();
|
this.loopTime();
|
||||||
} else {
|
} else {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 导出
|
|
||||||
// () {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -27,18 +27,6 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
// 默认值与实际数据结构一致(12个月)
|
// 默认值与实际数据结构一致(12个月)
|
||||||
default: () => [
|
default: () => [
|
||||||
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -98,7 +86,7 @@ export default {
|
|||||||
const diff = Number(item.diff) || 0;
|
const diff = Number(item.diff) || 0;
|
||||||
|
|
||||||
// 计算达标标识(≥100 → 1,<100 → 0)
|
// 计算达标标识(≥100 → 1,<100 → 0)
|
||||||
const flag = this.getRateFlag(rate, real, budget);
|
const flag = item.rate >= 100 ? 1 : 0;
|
||||||
|
|
||||||
// 填充数组
|
// 填充数组
|
||||||
months.push(month);
|
months.push(month);
|
||||||
@@ -120,25 +108,7 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
console.log('处理后的趋势数据:', this.chartData);
|
console.log('处理后的趋势数据:', this.chartData);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算达标标识
|
|
||||||
* @param {Number} rate - 完成率(原始值,如1.2 → 120%)
|
|
||||||
* @returns {Number} 1: 达标(≥100%),0: 未达标(<100%)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -146,28 +146,59 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 14,
|
barWidth: 14,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
||||||
|
const safeFlag = data.flags;
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals // 实际销量(万元)
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [0, 0],
|
offset: [0, 0],
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
width: 68,
|
||||||
height: 20,
|
height: 20,
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
formatter: () => {
|
||||||
formatter: (params) => {
|
|
||||||
const diff = data.diffs || [];
|
const diff = data.diffs || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -201,48 +232,21 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
|
||||||
const safeFlag = data.flags;
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals // 实际销量(万元)
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :detailData="monthData">
|
<verticalBarChart :detailData="monthData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -74,21 +68,6 @@ export default {
|
|||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||||
// this.$nextTick(() => this.updateChart())
|
// this.$nextTick(() => this.updateChart())
|
||||||
|
|||||||
@@ -96,18 +96,6 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
updateChart(data) {
|
||||||
// 修复核心:正确获取默认值(调用 factory 函数)
|
// 修复核心:正确获取默认值(调用 factory 函数)
|
||||||
@@ -124,12 +112,12 @@ getRateFlag(rate, real, target) {
|
|||||||
// 整合flag字段
|
// 整合flag字段
|
||||||
this.ytdIncomeData = {
|
this.ytdIncomeData = {
|
||||||
...incomeItem,
|
...incomeItem,
|
||||||
flag: this.getRateFlag(incomeItem.rate, incomeItem.real, incomeItem.budget)
|
flag: incomeItem.rate > 100 ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ytdCostData = {
|
this.ytdCostData = {
|
||||||
...costItem,
|
...costItem,
|
||||||
flag: this.getRateFlag(costItem.rate, costItem.real, costItem.budget)
|
flag: costItem.rate > 100 ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('累计收入数据:', this.ytdIncomeData);
|
console.log('累计收入数据:', this.ytdIncomeData);
|
||||||
|
|||||||
@@ -174,26 +174,58 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -226,45 +258,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,10 +17,6 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
}),
|
}),
|
||||||
// 校验数据格式
|
|
||||||
// validator: (value) => {
|
|
||||||
// return Array.isArray(value.series) && Array.isArray(value.allPlaceNames);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -72,16 +68,6 @@ export default {
|
|||||||
backgroundColor: '#6a7985'
|
backgroundColor: '#6a7985'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 30,
|
top: 30,
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate >=100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -139,7 +139,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate >=100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -81,24 +81,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
@@ -113,7 +95,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate > 100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -134,7 +116,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.rate, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate > 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -40,33 +40,6 @@ export default {
|
|||||||
const salesData = {
|
const salesData = {
|
||||||
allPlaceNames: this.locations,
|
allPlaceNames: this.locations,
|
||||||
series: [
|
series: [
|
||||||
// 1. 完成率(折线图)
|
|
||||||
// {
|
|
||||||
// name: '完成率',
|
|
||||||
// type: 'line',
|
|
||||||
// yAxisIndex: 1, // 绑定右侧Y轴(需在子组件启用配置)
|
|
||||||
// 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: data.rates, // 完成率(%)
|
|
||||||
// symbol: 'circle',
|
|
||||||
// symbolSize: 6
|
|
||||||
// },
|
|
||||||
// 2. 目标(柱状图)
|
|
||||||
{
|
{
|
||||||
name: '预算',
|
name: '预算',
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
|
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -73,21 +67,6 @@ export default {
|
|||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||||
// this.$nextTick(() => this.updateChart())
|
// this.$nextTick(() => this.updateChart())
|
||||||
|
|||||||
@@ -50,18 +50,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
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;
|
|
||||||
},
|
|
||||||
updateChart() {
|
updateChart() {
|
||||||
const chartDom = this.$refs[this.refName];
|
const chartDom = this.$refs[this.refName];
|
||||||
if (!chartDom) {
|
if (!chartDom) {
|
||||||
@@ -76,7 +64,7 @@ export default {
|
|||||||
this.myChart = echarts.init(chartDom);
|
this.myChart = echarts.init(chartDom);
|
||||||
const diff = this.detailData.diff || 0
|
const diff = this.detailData.diff || 0
|
||||||
const rate = this.detailData.rate || 0
|
const rate = this.detailData.rate || 0
|
||||||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
const flagValue = this.detailData.rate>=100?1:0
|
||||||
this.flag = flagValue
|
this.flag = flagValue
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
@@ -88,16 +76,6 @@ export default {
|
|||||||
backgroundColor: '#6a7985'
|
backgroundColor: '#6a7985'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 40,
|
top: 40,
|
||||||
@@ -110,9 +88,6 @@ export default {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
||||||
type: 'value',
|
type: 'value',
|
||||||
// offset: 0,
|
|
||||||
// boundaryGap: true ,
|
|
||||||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
|
||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
min: 0,
|
min: 0,
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -101,19 +101,6 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// 保留原flag判断逻辑(≥100返回1,<100返回0)
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
updateChart(data) {
|
||||||
// 数据兜底:确保是数组且长度≥2
|
// 数据兜底:确保是数组且长度≥2
|
||||||
@@ -128,12 +115,12 @@ export default {
|
|||||||
// 整合flag字段到收入/全成本数据中
|
// 整合flag字段到收入/全成本数据中
|
||||||
this.incomeData = {
|
this.incomeData = {
|
||||||
...incomeItem,
|
...incomeItem,
|
||||||
flag: this.getRateFlag(incomeItem.rate, incomeItem.real, incomeItem.budget)
|
flag: incomeItem.rate >= 100 ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
this.totalCostData = {
|
this.totalCostData = {
|
||||||
...totalCostItem,
|
...totalCostItem,
|
||||||
flag: this.getRateFlag(totalCostItem.rate, totalCostItem.real, totalCostItem.budget)
|
flag: totalCostItem.rate >= 100 ? 1 : 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// 调试:确认数据赋值正确
|
// 调试:确认数据赋值正确
|
||||||
|
|||||||
@@ -26,39 +26,26 @@
|
|||||||
grid-template-columns: 1624px;
|
grid-template-columns: 1624px;
|
||||||
">
|
">
|
||||||
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
|
<operatingLineChartCumulative :dateData="dateData" :ytdData="ytdData" />
|
||||||
<!-- <keyWork /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ReportHeader from "../components/noRouterHeader.vue";
|
import ReportHeader from "../components/noRouterHeader.vue";
|
||||||
import { Sidebar } from "../../../layout/components";
|
import { Sidebar } from "../../../layout/components";
|
||||||
import screenfull from "screenfull";
|
import screenfull from "screenfull";
|
||||||
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
|
|
||||||
// import premProdStatus from "./components/premProdStatus.vue";
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import operatingLineChart from "../inputOutputRatioComponents/operatingLineChart";
|
import operatingLineChart from "../inputOutputRatioComponents/operatingLineChart";
|
||||||
import operatingLineChartCumulative from "../inputOutputRatioComponents/operatingLineChartCumulative.vue";
|
import operatingLineChartCumulative from "../inputOutputRatioComponents/operatingLineChartCumulative.vue";
|
||||||
|
|
||||||
import { getInputOutputRateGroupData } from '@/api/cockpit'
|
import { getInputOutputRateGroupData } from '@/api/cockpit'
|
||||||
import moment from "moment";
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
ReportHeader,
|
ReportHeader,
|
||||||
operatingLineChartCumulative,
|
operatingLineChartCumulative,
|
||||||
operatingLineChart,
|
operatingLineChart,
|
||||||
// premProdStatus,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -201,28 +188,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -52,14 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -69,21 +61,12 @@ import screenfull from "screenfull";
|
|||||||
import changeBase from "../components/changeBase.vue";
|
import changeBase from "../components/changeBase.vue";
|
||||||
import monthlyOverview from "../inputOutputRatioComponents/monthlyOverview.vue";
|
import monthlyOverview from "../inputOutputRatioComponents/monthlyOverview.vue";
|
||||||
import totalOverview from "../inputOutputRatioComponents/totalOverview.vue";
|
import totalOverview from "../inputOutputRatioComponents/totalOverview.vue";
|
||||||
// import totalOverview from "../operatingComponents/totalOverview.vue";
|
|
||||||
import monthlyRelatedMetrics from "../inputOutputRatioComponents/monthlyRelatedMetrics.vue";
|
import monthlyRelatedMetrics from "../inputOutputRatioComponents/monthlyRelatedMetrics.vue";
|
||||||
import yearRelatedMetrics from "../inputOutputRatioComponents/yearRelatedMetrics.vue";
|
import yearRelatedMetrics from "../inputOutputRatioComponents/yearRelatedMetrics.vue";
|
||||||
import dataTrend from "../inputOutputRatioComponents/dataTrend.vue";
|
import dataTrend from "../inputOutputRatioComponents/dataTrend.vue";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getInputOutputRateFactoryData } from '@/api/cockpit'
|
import { getInputOutputRateFactoryData } from '@/api/cockpit'
|
||||||
// import PSDO from "./components/PSDO.vue";
|
|
||||||
// import psiLineChart from "./components/psiLineChart.vue";
|
|
||||||
|
|
||||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
|
||||||
// import orderProgress from "./components/orderProgress.vue";
|
|
||||||
// import keyWork from "./components/keyWork.vue";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
// import html2canvas from 'html2canvas'
|
|
||||||
// import JsPDF from 'jspdf'
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
@@ -112,11 +95,6 @@ export default {
|
|||||||
monthAnalysis: [],
|
monthAnalysis: [],
|
||||||
ytdAnalysis: [],
|
ytdAnalysis: [],
|
||||||
trend: [],
|
trend: [],
|
||||||
// trendData: [],
|
|
||||||
// parentItemList: [
|
|
||||||
// { name: "燃料成本", target: 0, value: 0, proportion: 0, flag: 1 },
|
|
||||||
// { name: "天然气", target: 0, value: 0, proportion: 0, flag: 1 }
|
|
||||||
// ],
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -287,28 +265,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// () {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,20 +26,7 @@ export default {
|
|||||||
trend: {
|
trend: {
|
||||||
type: Array,
|
type: Array,
|
||||||
// 默认值与实际数据结构一致(12个月)
|
// 默认值与实际数据结构一致(12个月)
|
||||||
default: () => [
|
default: () => []
|
||||||
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -96,9 +83,7 @@ export default {
|
|||||||
const real = Number(item.real) || 0;
|
const real = Number(item.real) || 0;
|
||||||
const rate = Number(item.rate) || 0;
|
const rate = Number(item.rate) || 0;
|
||||||
const diff = Number(item.diff) || 0;
|
const diff = Number(item.diff) || 0;
|
||||||
|
const flag = item.rate>=100 ? 1 : 0;
|
||||||
// 计算达标标识(≥100 → 1,<100 → 0)
|
|
||||||
const flag = this.getRateFlag(rate,real,budget);
|
|
||||||
|
|
||||||
// 填充数组
|
// 填充数组
|
||||||
months.push(month);
|
months.push(month);
|
||||||
@@ -120,25 +105,7 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
console.log('处理后的趋势数据:', this.chartData);
|
console.log('处理后的趋势数据:', this.chartData);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算达标标识
|
|
||||||
* @param {Number} rate - 完成率(原始值,如1.2 → 120%)
|
|
||||||
* @returns {Number} 1: 达标(≥100%),0: 未达标(<100%)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -145,28 +145,59 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 14,
|
barWidth: 14,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
||||||
|
const safeFlag = data.flags;
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals // 实际销量(万元)
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [0, 0],
|
offset: [0, 0],
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
width: 68,
|
||||||
height: 20,
|
height: 20,
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
formatter: () => {
|
||||||
formatter: (params) => {
|
|
||||||
const diff = data.diffs || [];
|
const diff = data.diffs || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -200,48 +231,21 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
|
||||||
const safeFlag = data.flags;
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals // 实际销量(万元)
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :detailData="monthData">
|
<verticalBarChart :detailData="monthData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -64,35 +58,6 @@ export default {
|
|||||||
chart: null,
|
chart: null,
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
// itemData: {
|
|
||||||
// handler(newValue, oldValue) {
|
|
||||||
// // this.updateChart()
|
|
||||||
// },
|
|
||||||
// deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag(data.rate, data.real, data.budget),
|
flag: data.rate > 100 ? 1 : 0,
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -101,22 +101,6 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true // 初始化立即执行
|
immediate: true // 初始化立即执行
|
||||||
}
|
}
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
methods: {
|
|
||||||
// 达标标识判断(≥100返回1,<100返回0)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -174,26 +174,58 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -226,45 +258,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -87,23 +87,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
@@ -118,7 +101,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate > 100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -139,7 +122,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate,item.real,item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate > 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import operatingLineBarSaleSingle from './operatingLineBarSaleSingle.vue';
|
import operatingLineBarSaleSingle from './operatingLineBarSaleSingle.vue';
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Container",
|
name: "Container",
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :id="'totalG'" :detailData="ytdData" id="totalGauge"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
|
<verticalBarChart :refName="'totalVerticalBarChart'" :detailData="ytdData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -73,27 +67,6 @@ export default {
|
|||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
deep: true // 若对象内属性变化需触发,需加 deep: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ getRateFlag(rate, real, target) {
|
|||||||
this.myChart = echarts.init(chartDom);
|
this.myChart = echarts.init(chartDom);
|
||||||
const diff = this.detailData.diff || 0
|
const diff = this.detailData.diff || 0
|
||||||
const rate = this.detailData.rate || 0
|
const rate = this.detailData.rate || 0
|
||||||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
const flagValue = this.detailData.rate >=100?1:0
|
||||||
this.flag = flagValue
|
this.flag = flagValue
|
||||||
const option = {
|
const option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -87,16 +87,6 @@ getRateFlag(rate, real, target) {
|
|||||||
backgroundColor: '#6a7985'
|
backgroundColor: '#6a7985'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
top: 40,
|
top: 40,
|
||||||
@@ -109,9 +99,6 @@ getRateFlag(rate, real, target) {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
||||||
type: 'value',
|
type: 'value',
|
||||||
// offset: 0,
|
|
||||||
// boundaryGap: true ,
|
|
||||||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
|
||||||
axisTick: { show: false },
|
axisTick: { show: false },
|
||||||
min: 0,
|
min: 0,
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag(data.rate, data.real, data.budget),
|
flag: data.rate > 100 ? 1 : 0,
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -101,22 +101,6 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true // 初始化立即执行
|
immediate: true // 初始化立即执行
|
||||||
}
|
}
|
||||||
},
|
|
||||||
mounted() {},
|
|
||||||
methods: {
|
|
||||||
// 达标标识判断(≥100返回1,<100返回0)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -128,10 +128,6 @@ export default {
|
|||||||
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
this.dateData = this.$route.query.dateData ? this.$route.query.dateData : undefined
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// sortChange(value) {
|
|
||||||
// this.sort = value
|
|
||||||
// this.getData()
|
|
||||||
// },
|
|
||||||
getData() {
|
getData() {
|
||||||
getInventoryData({
|
getInventoryData({
|
||||||
startTime: this.dateData.startTime,
|
startTime: this.dateData.startTime,
|
||||||
@@ -144,12 +140,6 @@ export default {
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.monthData= res.data.month
|
this.monthData= res.data.month
|
||||||
this.ytdData = res.data.ytd
|
this.ytdData = res.data.ytd
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -199,28 +189,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,298 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container name="数据趋势" icon="cockpitItemIcon" size="opLargeBg" topSize="large">
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%; gap: 16px">
|
|
||||||
<div class="right" style="
|
|
||||||
height: 191px;
|
|
||||||
display: flex;
|
|
||||||
width: 1595px;
|
|
||||||
background-color: rgba(249, 252, 255, 1);
|
|
||||||
">
|
|
||||||
<dataTrendBar @changeItem="handleChange" :chartData="chartData" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from "../components/container.vue";
|
|
||||||
import dataTrendBar from "./dataTrendBar.vue";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "ProductionStatus",
|
|
||||||
components: { Container, dataTrendBar },
|
|
||||||
props: {
|
|
||||||
trend: {
|
|
||||||
type: Array,
|
|
||||||
// 默认值与实际数据结构一致(12个月)
|
|
||||||
default: () => [
|
|
||||||
// { title: "2025年01月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年02月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年03月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年04月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年05月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年06月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年07月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年08月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年09月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年10月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年11月", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
// { title: "2025年12月", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chartData: {
|
|
||||||
months: [], // 月份数组(2025年01月 - 2025年12月)
|
|
||||||
rates: [], // 每月完成率(百分比)
|
|
||||||
reals: [], // 每月实际值
|
|
||||||
budgets: [],// 每月预算值
|
|
||||||
diffs: [], // 每月差值
|
|
||||||
flags: [] // 每月达标标识(≥100 → 1,<100 → 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
trend: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.processTrendData(newVal);
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.processTrendData(this.trend);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleChange(value) {
|
|
||||||
this.$emit("handleChange", value);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 处理趋势数据(适配12个月的数组结构)
|
|
||||||
* @param {Array} trendData - 原始趋势数组(12个月)
|
|
||||||
*/
|
|
||||||
processTrendData(trendData) {
|
|
||||||
// 数据兜底:确保是数组且长度为12
|
|
||||||
const validTrend = Array.isArray(trendData)
|
|
||||||
? trendData
|
|
||||||
: []
|
|
||||||
|
|
||||||
// 初始化空数组
|
|
||||||
const months = [];
|
|
||||||
const rates = [];
|
|
||||||
const reals = [];
|
|
||||||
const budgets = [];
|
|
||||||
const diffs = [];
|
|
||||||
const flags = [];
|
|
||||||
|
|
||||||
// 遍历12个月数据
|
|
||||||
validTrend.forEach(item => {
|
|
||||||
// 基础数据提取(兜底处理)
|
|
||||||
const month = item.title ?? '';
|
|
||||||
const budget = Number(item.budget) || 0;
|
|
||||||
const real = Number(item.real) || 0;
|
|
||||||
const rate = Number(item.rate) || 0;
|
|
||||||
const diff = Number(item.diff) || 0;
|
|
||||||
|
|
||||||
// 计算达标标识(≥100 → 1,<100 → 0)
|
|
||||||
const flag = this.getRateFlag(rate, real, budget);
|
|
||||||
|
|
||||||
// 填充数组
|
|
||||||
months.push(month);
|
|
||||||
rates.push(rate); // 转为百分比并取整
|
|
||||||
reals.push(real);
|
|
||||||
budgets.push(budget);
|
|
||||||
diffs.push(diff);
|
|
||||||
flags.push(flag);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新chartData(响应式)
|
|
||||||
this.chartData = {
|
|
||||||
months,
|
|
||||||
rates,
|
|
||||||
reals,
|
|
||||||
budgets,
|
|
||||||
diffs,
|
|
||||||
flags
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('处理后的趋势数据:', this.chartData);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算达标标识
|
|
||||||
* @param {Number} rate - 完成率(原始值,如1.2 → 120%)
|
|
||||||
* @returns {Number} 1: 达标(≥100%),0: 未达标(<100%)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
/* 滚动容器样式 */
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 设备项样式优化 */
|
|
||||||
.proBarInfo {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 8px 27px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfo {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slot {
|
|
||||||
width: 21px;
|
|
||||||
height: 23px;
|
|
||||||
background: rgba(0, 106, 205, 0.22);
|
|
||||||
backdrop-filter: blur(1.5px);
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #68b5ff;
|
|
||||||
line-height: 23px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eq-name {
|
|
||||||
margin-left: 8px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.eqStatus {
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #ffffff;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: right;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.splitLine {
|
|
||||||
width: 1px;
|
|
||||||
height: 14px;
|
|
||||||
border: 1px solid #adadad;
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yield {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #00ffff;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: right;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfoLeft {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarInfoEqInfoRight {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarWrapper {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
margin-top: 6px;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarLine {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(65deg, rgba(82, 82, 82, 0) 0%, #acacac 100%);
|
|
||||||
opacity: 0.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.proBarLineTop {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(65deg,
|
|
||||||
rgba(53, 223, 247, 0) 0%,
|
|
||||||
rgba(54, 220, 246, 0.92) 92%,
|
|
||||||
#36f6e5 100%,
|
|
||||||
#37acf5 100%);
|
|
||||||
border-radius: 5px;
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图表相关样式 */
|
|
||||||
.chartImgBottom {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 45px;
|
|
||||||
left: 58px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
left: 57px;
|
|
||||||
bottom: 42px;
|
|
||||||
width: 1px;
|
|
||||||
height: 20px;
|
|
||||||
background-color: #00e8ff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 全局 tooltip 样式 */
|
|
||||||
.production-status-chart-tooltip {
|
|
||||||
background: #0a2b4f77 !important;
|
|
||||||
border: none !important;
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.production-status-chart-tooltip * {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,477 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="coreBar">
|
|
||||||
<!-- 新增行容器:包裹“各基地情况”和barTop -->
|
|
||||||
<div class="header-row">
|
|
||||||
<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">
|
|
||||||
<span class="legend-icon square achieved"></span>
|
|
||||||
实际·达标
|
|
||||||
</span>
|
|
||||||
<span class="legend-item">
|
|
||||||
<span class="legend-icon square unachieved"></span>
|
|
||||||
实际·未达标
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<div class="item-button category-btn">
|
|
||||||
<span class="item-text">类目选择</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>
|
|
||||||
<div class="lineBottom" style="height: 100%; width: 100%">
|
|
||||||
<operatingLineBar :chartData="chartD" style="height: 99%; width: 100%" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import operatingLineBar from './operatingLineBarSale.vue';
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "Container",
|
|
||||||
components: { operatingLineBar },
|
|
||||||
props: ["chartData"],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
activeButton: 0,
|
|
||||||
isDropdownShow: false,
|
|
||||||
selectedProfit: '营业收入', // 选中的名称,初始为null
|
|
||||||
profitOptions: [
|
|
||||||
'营业收入',
|
|
||||||
'单价',
|
|
||||||
'销量',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// profitOptions() {
|
|
||||||
// return this.categoryData.map(item => item.name) || [];
|
|
||||||
// },
|
|
||||||
currentDataSource() {
|
|
||||||
console.log('yyyy', this.chartData);
|
|
||||||
|
|
||||||
return this.chartData
|
|
||||||
},
|
|
||||||
locations() {
|
|
||||||
console.log('this.chartData', this.chartData);
|
|
||||||
|
|
||||||
return this.chartData.months
|
|
||||||
},
|
|
||||||
// 根据按钮切换生成对应的 chartData
|
|
||||||
chartD() {
|
|
||||||
const data = this.currentDataSource;
|
|
||||||
console.log(this.currentDataSource, 'currentDataSource');
|
|
||||||
|
|
||||||
const salesData = {
|
|
||||||
allPlaceNames: this.locations,
|
|
||||||
series: [
|
|
||||||
// 1. 完成率(折线图)
|
|
||||||
{
|
|
||||||
name: '完成率',
|
|
||||||
type: 'line',
|
|
||||||
yAxisIndex: 1, // 绑定右侧Y轴(需在子组件启用配置)
|
|
||||||
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: data.rates, // 完成率(%)
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 6
|
|
||||||
},
|
|
||||||
// 2. 目标(柱状图)
|
|
||||||
{
|
|
||||||
name: '预算',
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 0, // 左侧Y轴(万元)
|
|
||||||
barWidth: 14,
|
|
||||||
itemStyle: {
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(130, 204, 255, 1)' },
|
|
||||||
{ offset: 1, color: 'rgba(75, 157, 255, 1)' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
data: data.budgets // 目标销量(万元)
|
|
||||||
},
|
|
||||||
// 3. 实际(柱状图,含达标状态)
|
|
||||||
{
|
|
||||||
name: '实际',
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 0,
|
|
||||||
barWidth: 14,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'top',
|
|
||||||
offset: [0, 0],
|
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
|
||||||
height: 20,
|
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
|
||||||
formatter: (params) => {
|
|
||||||
const diff = data.diffs || [];
|
|
||||||
const flags = data.flags || [];
|
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
|
||||||
} else {
|
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backgroundColor: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 0, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: 'rgba(205, 215, 224, 0.6)' },
|
|
||||||
{ offset: 0.2, color: '#ffffff' },
|
|
||||||
{ offset: 1, color: '#ffffff' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
shadowColor: 'rgba(191,203,215,0.5)',
|
|
||||||
shadowBlur: 2,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
borderRadius: 4,
|
|
||||||
borderColor: '#BFCBD577',
|
|
||||||
borderWidth: 0,
|
|
||||||
lineHeight: 20,
|
|
||||||
rich: {
|
|
||||||
text: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 10, 5, 0],
|
|
||||||
align: 'center',
|
|
||||||
color: '#464646',
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
achieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
|
||||||
width: 'auto',
|
|
||||||
padding: [5, 0, 5, 10],
|
|
||||||
align: 'center',
|
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
|
||||||
fontSize: 11,
|
|
||||||
lineHeight: 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// 达标状态:1=达标(绿色),0=未达标(橙色)
|
|
||||||
const safeFlag = data.flags;
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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: data.reals // 实际销量(万元)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 根据按钮状态返回对应数据
|
|
||||||
return salesData;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
selectProfit(item) {
|
|
||||||
this.selectedProfit = item;
|
|
||||||
this.isDropdownShow = false;
|
|
||||||
this.$emit("changeItem", item);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.coreBar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
|
|
||||||
// 新增:头部行容器,实现一行排列
|
|
||||||
.header-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end; // 左右两端对齐
|
|
||||||
align-items: center; // 垂直居中
|
|
||||||
// width: 100%;
|
|
||||||
margin-bottom: 8px; // 与下方图表区保留间距(可根据需求调整)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 各基地情况标题样式
|
|
||||||
.base-title {
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
font-style: normal;
|
|
||||||
padding: 0 0 0 16px; // 保留原有内边距
|
|
||||||
white-space: nowrap; // 防止文字换行
|
|
||||||
}
|
|
||||||
|
|
||||||
.barTop {
|
|
||||||
// 移除原有flex和justify-content,由header-row控制
|
|
||||||
width: auto; // 自适应宽度
|
|
||||||
// 保留原有align-items,确保内部元素垂直居中
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
|
|
||||||
// 1. 右侧容器:包裹图例和按钮组,整体靠右
|
|
||||||
.right-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center; // 图例和按钮组垂直居中
|
|
||||||
gap: 24px; // 图例与按钮组的间距,避免贴紧
|
|
||||||
margin-right: 46px; // 右侧整体留边,与原按钮组边距一致
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 图例:在右侧容器内横向排列
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 按钮组:在右侧容器内,保留原有样式
|
|
||||||
.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;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.item-text {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-btn {
|
|
||||||
width: 75px;
|
|
||||||
border-top-left-radius: 12px;
|
|
||||||
border-bottom-left-radius: 12px;
|
|
||||||
background: #ffffff;
|
|
||||||
color: #0b58ff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profit-btn {
|
|
||||||
width: 123px;
|
|
||||||
border-top-right-radius: 12px;
|
|
||||||
border-bottom-right-radius: 12px;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 18px 0 8px;
|
|
||||||
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: 123px;
|
|
||||||
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>
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
|
||||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
|
||||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
|
||||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
|
||||||
<div class="dashboard">
|
|
||||||
<div class="title">
|
|
||||||
{{ month }}月完成率
|
|
||||||
</div>
|
|
||||||
<div class="number">
|
|
||||||
<div class="yield">
|
|
||||||
{{ monthData?.rate || 0 }}%
|
|
||||||
</div>
|
|
||||||
<div class="mom">
|
|
||||||
环比{{ monthData?.momRate }}%
|
|
||||||
<img v-if="monthData?.momRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :detailData="monthData" id="month"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="line" style="padding: 0px;">
|
|
||||||
<verticalBarChart :detailData="monthData">
|
|
||||||
</verticalBarChart>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import electricityGauge from './electricityGauge.vue'
|
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
|
||||||
monthData: { // 接收父组件传递的设备数据数组
|
|
||||||
type: Object,
|
|
||||||
default: () => {} // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
title: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
month: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
// itemData: {
|
|
||||||
// handler(newValue, oldValue) {
|
|
||||||
// // this.updateChart()
|
|
||||||
// },
|
|
||||||
// deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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: 264px;
|
|
||||||
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 {
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 46px;
|
|
||||||
color: #0B58FF;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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> -->
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
<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; width: 100%;">
|
|
||||||
<!-- 销量模块(直接传递整合了flag的salesData) -->
|
|
||||||
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
销量·万㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[0].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[0].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 单价模块(直接传递整合了flag的unitPriceData) -->
|
|
||||||
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
单价·元/㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{monthAnalysis[1].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:monthAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{monthAnalysis[1].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, operatingSingleBar },
|
|
||||||
props: {
|
|
||||||
monthAnalysis: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [
|
|
||||||
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dateData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
factory: {
|
|
||||||
type: [String,Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
// 初始化数据包含flag字段
|
|
||||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
|
||||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
monthAnalysis: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.updateChart(newVal)
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.updateChart(this.monthAnalysis)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDashboardClick(path) {
|
|
||||||
this.$router.push({
|
|
||||||
path: path,
|
|
||||||
query: {
|
|
||||||
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
|
|
||||||
dateData: this.dateData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 判断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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
|
||||||
// 数据兜底
|
|
||||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
|
|
||||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
|
||||||
this.salesData = {
|
|
||||||
...salesItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unitPriceData = {
|
|
||||||
...unitPriceItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调试:确认整合后的数据
|
|
||||||
console.log('整合flag后的销量数据:', this.salesData);
|
|
||||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topItem-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 205px;
|
|
||||||
background: #F9FCFF;
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-wrap {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
height: 32px;
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 32px;
|
|
||||||
color: #0B58FF;
|
|
||||||
line-height: 32px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
width: 97px;
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -173,26 +173,62 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际差值标签(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '-';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,45 +261,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
width: 1220px;
|
width: 1220px;
|
||||||
background-color: rgba(249, 252, 255, 1);
|
background-color: rgba(249, 252, 255, 1);
|
||||||
">
|
">
|
||||||
<!-- <top-item /> -->
|
|
||||||
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
|
<operatingBar :dateData="dateData" :chartData="chartData" @sort-change="sortChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,23 +87,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断rate对应的flag值(<1为0,>1为1)
|
|
||||||
* @param {number} rate 处理后的rate值(已*100)
|
|
||||||
* @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
|
* 核心处理函数:在所有数据都准备好后,才组装 chartData
|
||||||
*/
|
*/
|
||||||
@@ -118,7 +100,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const groupReal = [this.groupData.real]; // 实际值数组
|
const groupReal = [this.groupData.real]; // 实际值数组
|
||||||
const groupRate = [this.groupData.rate]; // 完成率数组
|
const groupRate = [this.groupData.rate]; // 完成率数组
|
||||||
// 新增:集团rate对应的flag
|
// 新增:集团rate对应的flag
|
||||||
const groupFlag = [this.getRateFlag(groupRate[0], groupReal[0], groupTarget[0])];
|
const groupFlag = [this.groupData.rate >= 100 ? 1 : 0];
|
||||||
|
|
||||||
console.log('集团数据数组:', {
|
console.log('集团数据数组:', {
|
||||||
groupTarget,
|
groupTarget,
|
||||||
@@ -139,7 +121,7 @@ getRateFlag(rate, real, target) {
|
|||||||
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
const factoryRate = this.factoryData.map(item => item.rate || 0);
|
||||||
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
const factoryDiff = this.factoryData.map(item => item.diff || 0);
|
||||||
// 新增:每个工厂rate对应的flag数组
|
// 新增:每个工厂rate对应的flag数组
|
||||||
const factoryFlags = this.factoryData.map(item => this.getRateFlag(item.rate, item.real, item.budget));
|
const factoryFlags = this.factoryData.map(item => item.rate >= 100 ? 1 : 0);
|
||||||
|
|
||||||
// 3. 组装最终的chartData(供子组件使用)
|
// 3. 组装最终的chartData(供子组件使用)
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="flex: 1">
|
|
||||||
<Container :name="title" icon="cockpitItemIcon" size="operatingRevenueBg" topSize="middle">
|
|
||||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
|
||||||
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
|
|
||||||
<!-- 新增:topItem 专属包裹容器,统一控制样式和布局 -->
|
|
||||||
<div class="topItem-container" style="display: flex; gap: 8px;">
|
|
||||||
<div class="dashboard">
|
|
||||||
<div class="title">
|
|
||||||
累计完成率
|
|
||||||
</div>
|
|
||||||
<div class="number">
|
|
||||||
<div class="yield">
|
|
||||||
{{ ytdData?.rate || 0}}%
|
|
||||||
</div>
|
|
||||||
<div class="mom">
|
|
||||||
同比{{ ytdData?.yoyRate || 0}}%
|
|
||||||
<img v-if="ytdData?.yoyRate >= 0" class="arrow" src="../../../assets/img/topArrow.png" alt="">
|
|
||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge :id=" 'totalG' " :detailData="ytdData" id="totalGauge"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="line" style="padding: 0px;">
|
|
||||||
<verticalBarChart :refName=" 'totalVerticalBarChart' " :detailData="ytdData">
|
|
||||||
</verticalBarChart>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import electricityGauge from './electricityGauge.vue'
|
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
|
||||||
// mixins: [resize],
|
|
||||||
props: {
|
|
||||||
ytdData: { // 接收父组件传递的设备数据数组
|
|
||||||
type: Object,
|
|
||||||
default: () => {} // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
title: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
month: { // 接收父组件传递的设备数据数组
|
|
||||||
type: String,
|
|
||||||
default: () => '' // 默认空数组,避免报错
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
itemData: {
|
|
||||||
handler(newValue, oldValue) {
|
|
||||||
// this.updateChart()
|
|
||||||
},
|
|
||||||
deep: true // 若对象内属性变化需触发,需加 deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// computed: {
|
|
||||||
// // 处理排序:包含“总成本”的项放前面,其余项按原顺序排列
|
|
||||||
// sortedItemData() {
|
|
||||||
// // 过滤出包含“总成本”的项(不区分大小写)
|
|
||||||
// const totalCostItems = this.itemData.filter(item =>
|
|
||||||
// item.name && item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 过滤出不包含“总成本”的项
|
|
||||||
// const otherItems = this.itemData.filter(item =>
|
|
||||||
// !item.name || !item.name.includes('总成本')
|
|
||||||
// );
|
|
||||||
// // 合并:总成本项在前,其他项在后
|
|
||||||
// return [...totalCostItems, ...otherItems];
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
mounted() {
|
|
||||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
|
||||||
// this.$nextTick(() => this.updateChart())
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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: 264px;
|
|
||||||
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 {
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 46px;
|
|
||||||
color: #0B58FF;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: center;
|
|
||||||
font-style: normal;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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> -->
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div style="width: 100%; height: 210px;position: relative;">
|
|
||||||
<div style='font-size: 16px;position: absolute;right: 20px;top:10px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{detailData.rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:detailData.flags>0?'#30B590':'#FF9423'}" >{{detailData.diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
<div :ref="refName" id="coreLineChart" style="width: 100%; height: 210px;"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
myChart: null // 存储图表实例,避免重复创建
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
// 明确接收的props结构,增强可读性
|
|
||||||
refName: {
|
|
||||||
type: String,
|
|
||||||
default: () => 'verticalBarChart',
|
|
||||||
},
|
|
||||||
detailData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updateChart();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// 新增:监听 chartData 变化
|
|
||||||
watch: {
|
|
||||||
// 深度监听数据变化,仅更新图表配置(不销毁实例)
|
|
||||||
detailData: {
|
|
||||||
handler() {
|
|
||||||
console.log(this.chartData, 'chartData');
|
|
||||||
|
|
||||||
this.updateChart();
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true // 初始化时立即执行
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
updateChart() {
|
|
||||||
const chartDom = this.$refs[this.refName];
|
|
||||||
if (!chartDom) {
|
|
||||||
console.error('图表容器未找到!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.myChart) {
|
|
||||||
this.myChart.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.myChart = echarts.init(chartDom);
|
|
||||||
const diff = this.detailData.diff || 0
|
|
||||||
const rate = this.detailData.rate || 0
|
|
||||||
const flagValue = this.getRateFlag(this.detailData.rate, this.detailData.real, this.detailData.target) || 0
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: {
|
|
||||||
type: 'cross',
|
|
||||||
label: {
|
|
||||||
backgroundColor: '#6a7985'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// formatter: (params) => {
|
|
||||||
// let html = `${params[0].axisValue}<br/>`;
|
|
||||||
// params.forEach(item => {
|
|
||||||
// const unit = item.seriesName === '完成率' ? '%' : (
|
|
||||||
// ['产量', '销量'].includes(this.$parent.selectedProfit) ? '片' : '万元'
|
|
||||||
// );
|
|
||||||
// html += `${item.marker} ${item.seriesName}: ${item.value}${unit}<br/>`;
|
|
||||||
// });
|
|
||||||
// return html;
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
top: 40,
|
|
||||||
bottom: 15,
|
|
||||||
right: 80,
|
|
||||||
left: 10,
|
|
||||||
containLabel: true,
|
|
||||||
show: false // 隐藏grid背景,避免干扰
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
// 横向柱状图的x轴必须设为数值轴,否则无法正常展示数值
|
|
||||||
type: 'value',
|
|
||||||
// offset: 0,
|
|
||||||
// boundaryGap: true ,
|
|
||||||
// boundaryGap: [10, 0], // 可根据需要开启,控制轴的留白
|
|
||||||
axisTick: { show: false },
|
|
||||||
min: 0,
|
|
||||||
//
|
|
||||||
splitNumber: 4,
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0, 0, 0, 0.45)',
|
|
||||||
fontSize: 12,
|
|
||||||
interval: 0,
|
|
||||||
padding: [5, 0, 0, 0]
|
|
||||||
},
|
|
||||||
// data: xData // 数值轴不需要手动设置data,由series的数据自动生成
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'category',
|
|
||||||
axisLabel: {
|
|
||||||
color: 'rgba(0, 0, 0, 0.75)',
|
|
||||||
fontSize: 12,
|
|
||||||
interval: 0,
|
|
||||||
padding: [5, 0, 0, 0]
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true, // 显示Y轴轴线(关键)
|
|
||||||
lineStyle: {
|
|
||||||
color: '#E5E6EB', // 轴线颜色(浅灰色,可自定义)
|
|
||||||
width: 1, // 轴线宽度
|
|
||||||
type: 'solid' // 实线(可选:dashed虚线、dotted点线)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: { show: false },
|
|
||||||
// padding: [300, 100, 100, 100],
|
|
||||||
data: ['实际', '预算'] // y轴分类:实际、预算
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
// name: '预算',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: 24,
|
|
||||||
// barCategoryGap: '50', // 柱子之间的间距(相对于柱子宽度)
|
|
||||||
// 数据长度与yAxis的分类数量匹配(实际、预算各一个值)
|
|
||||||
data: [{
|
|
||||||
value: this.detailData.real,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'right',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: flagValue === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
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]
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
value: this.detailData.target,
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: 'right',
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
// 预算的渐变颜色(蓝系渐变)
|
|
||||||
color: {
|
|
||||||
type: 'linear',
|
|
||||||
x: 1, y: 0, x2: 0, y2: 1,
|
|
||||||
colorStops: [
|
|
||||||
{ offset: 0, color: '#82CCFF' }, // 浅蓝
|
|
||||||
{ offset: 1, color: '#4B9DFF' } // 深蓝
|
|
||||||
]
|
|
||||||
},
|
|
||||||
borderRadius: [4, 4, 0, 0],
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
},],
|
|
||||||
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
option && this.myChart.setOption(option);
|
|
||||||
|
|
||||||
// 窗口缩放适配和销毁逻辑保持不变
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
this.myChart && this.myChart.resize();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$once('hook:destroyed', () => {
|
|
||||||
window.removeEventListener('resize', () => {
|
|
||||||
this.myChart && this.myChart.resize();
|
|
||||||
});
|
|
||||||
this.myChart && this.myChart.dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
<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; width: 100%;">
|
|
||||||
<!-- 销量模块(直接传递整合了flag的salesData) -->
|
|
||||||
<div class="dashboard left" @click="handleDashboardClick('/salesVolumeAnalysis/salesVolumeAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
销量·万㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[0].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[0].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[0].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="salesData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 单价模块(直接传递整合了flag的unitPriceData) -->
|
|
||||||
<div class="dashboard right" @click="handleDashboardClick('/unitPriceAnalysis/unitPriceAnalysisBase')">
|
|
||||||
<div style='position: relative;'>
|
|
||||||
<div class="title">
|
|
||||||
单价·元/㎡
|
|
||||||
</div>
|
|
||||||
<div style='font-size: 16px;position: absolute;top:-4px;right:15px'>
|
|
||||||
<span>完成率:<span style='color: #0B58FF;'>{{ytdAnalysis[1].rate}}%</span></span>
|
|
||||||
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:ytdAnalysis[1].flags>0?'#30B590':'#FF9423'}" >{{ytdAnalysis[1].diff}}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="chart-wrap">
|
|
||||||
<operatingSingleBar :detailData="unitPriceData"></operatingSingleBar>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Container from './container.vue'
|
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProductionStatus',
|
|
||||||
components: { Container, operatingSingleBar },
|
|
||||||
props: {
|
|
||||||
ytdAnalysis: {
|
|
||||||
type: Array,
|
|
||||||
default: () => [
|
|
||||||
{ title: "销量", budget: 0, real: 0, rate: 0, diff: 0 },
|
|
||||||
{ title: "单价", budget: 0, real: 0, rate: 0, diff: 0 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
dateData: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
month: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null,
|
|
||||||
// 初始化数据包含flag字段
|
|
||||||
salesData: { title: "销量", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 },
|
|
||||||
unitPriceData: { title: "单价", budget: 0, real: 0, rate: 0, diff: 0, flag: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
ytdAnalysis: {
|
|
||||||
handler(newVal) {
|
|
||||||
this.updateChart(newVal)
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.updateChart(this.ytdAnalysis)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleDashboardClick(path) {
|
|
||||||
this.$router.push({
|
|
||||||
path: path,
|
|
||||||
query: {
|
|
||||||
factory: this.$route.query.factory ? this.$route.query.factory : 5,
|
|
||||||
dateData: this.dateData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 判断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;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChart(data) {
|
|
||||||
// 数据兜底
|
|
||||||
const salesItem = Array.isArray(data) && data[0] ? data[0] : { title: "销量", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
const unitPriceItem = Array.isArray(data) && data[1] ? data[1] : { title: "单价", budget: 0, real: 0, rate: 0, diff: 0 };
|
|
||||||
|
|
||||||
// 核心修改:将flag整合到数据对象中,无需单独定义salesFlag/unitPriceFlag
|
|
||||||
this.salesData = {
|
|
||||||
...salesItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(salesItem.rate, salesItem.real, salesItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
this.unitPriceData = {
|
|
||||||
...unitPriceItem, // 合并原有字段
|
|
||||||
flag: this.getRateFlag(unitPriceItem.rate, unitPriceItem.real, unitPriceItem.budget) // 新增flag字段
|
|
||||||
};
|
|
||||||
|
|
||||||
// 调试:确认整合后的数据
|
|
||||||
console.log('整合flag后的销量数据:', this.salesData);
|
|
||||||
console.log('整合flag后的单价数据:', this.unitPriceData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.scroll-container {
|
|
||||||
max-height: 210px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topItem-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 300px;
|
|
||||||
height: 205px;
|
|
||||||
background: #F9FCFF;
|
|
||||||
padding: 16px 0 0 10px;
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-wrap {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 30px;
|
|
||||||
height: 32px;
|
|
||||||
font-family: YouSheBiaoTiHei;
|
|
||||||
font-size: 32px;
|
|
||||||
color: #0B58FF;
|
|
||||||
line-height: 32px;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mom {
|
|
||||||
width: 97px;
|
|
||||||
height: 18px;
|
|
||||||
font-family: PingFangSC, PingFang SC;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #000000;
|
|
||||||
line-height: 18px;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard.right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -26,39 +26,26 @@
|
|||||||
grid-template-columns: 1624px;
|
grid-template-columns: 1624px;
|
||||||
">
|
">
|
||||||
<operatingLineChartCumulative :dateData="dateData" :totalData="totalData" />
|
<operatingLineChartCumulative :dateData="dateData" :totalData="totalData" />
|
||||||
<!-- <keyWork /> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ReportHeader from "../components/noRouterHeader.vue";
|
import ReportHeader from "../components/noRouterHeader.vue";
|
||||||
import { Sidebar } from "../../../layout/components";
|
import { Sidebar } from "../../../layout/components";
|
||||||
import screenfull from "screenfull";
|
import screenfull from "screenfull";
|
||||||
// import operatingSalesRevenue from "./operatingComponents/operatingSalesRevenue";
|
|
||||||
// import premProdStatus from "./components/premProdStatus.vue";
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import operatingLineChart from "../netPriceAnalysisComponents/operatingLineChart";
|
import operatingLineChart from "../netPriceAnalysisComponents/operatingLineChart";
|
||||||
import operatingLineChartCumulative from "../netPriceAnalysisComponents/operatingLineChartCumulative.vue";
|
import operatingLineChartCumulative from "../netPriceAnalysisComponents/operatingLineChartCumulative.vue";
|
||||||
|
|
||||||
import { getUnitPriceAnalysisGroupData } from '@/api/cockpit'
|
import { getUnitPriceAnalysisGroupData } from '@/api/cockpit'
|
||||||
import moment from "moment";
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
ReportHeader,
|
ReportHeader,
|
||||||
operatingLineChartCumulative,
|
operatingLineChartCumulative,
|
||||||
operatingLineChart,
|
operatingLineChart,
|
||||||
// premProdStatus,
|
|
||||||
Sidebar,
|
Sidebar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -149,12 +136,6 @@ export default {
|
|||||||
console.log(res);
|
console.log(res);
|
||||||
this.thisMonData = res.data.thisMonData
|
this.thisMonData = res.data.thisMonData
|
||||||
this.totalData = res.data.totalData
|
this.totalData = res.data.totalData
|
||||||
|
|
||||||
// this.saleData = res.data.SaleData
|
|
||||||
// this.premiumProduct = res.data.premiumProduct
|
|
||||||
// this.salesTrendMap = res.data.salesTrendMap
|
|
||||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
|
||||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleTimeChange(obj) {
|
handleTimeChange(obj) {
|
||||||
@@ -204,28 +185,7 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenfull.toggle(this.$refs.dayReportB);
|
screenfull.toggle(this.$refs.dayReportB);
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// exportPDF() {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -52,14 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="centerImg" style="
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1; /* 确保在 backp 之上、内容之下 */
|
|
||||||
"></div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -69,21 +61,12 @@ import screenfull from "screenfull";
|
|||||||
import changeBase from "../components/changeBase.vue";
|
import changeBase from "../components/changeBase.vue";
|
||||||
import monthlyOverview from "../netPriceAnalysisComponents/monthlyOverview.vue";
|
import monthlyOverview from "../netPriceAnalysisComponents/monthlyOverview.vue";
|
||||||
import totalOverview from "../netPriceAnalysisComponents/totalOverview.vue";
|
import totalOverview from "../netPriceAnalysisComponents/totalOverview.vue";
|
||||||
// import totalOverview from "../operatingComponents/totalOverview.vue";
|
|
||||||
import monthlyRelatedMetrics from "../netPriceAnalysisComponents/monthlyRelatedMetrics.vue";
|
import monthlyRelatedMetrics from "../netPriceAnalysisComponents/monthlyRelatedMetrics.vue";
|
||||||
import yearRelatedMetrics from "../netPriceAnalysisComponents/yearRelatedMetrics.vue";
|
import yearRelatedMetrics from "../netPriceAnalysisComponents/yearRelatedMetrics.vue";
|
||||||
import dataTrend from "../netPriceAnalysisComponents/dataTrend.vue";
|
import dataTrend from "../netPriceAnalysisComponents/dataTrend.vue";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
import { getUnitPriceAnalysisBaseData } from '@/api/cockpit'
|
||||||
// import PSDO from "./components/PSDO.vue";
|
|
||||||
// import psiLineChart from "./components/psiLineChart.vue";
|
|
||||||
|
|
||||||
// import coreBottomLeft from "./components/coreBottomLeft.vue";
|
|
||||||
// import orderProgress from "./components/orderProgress.vue";
|
|
||||||
// import keyWork from "./components/keyWork.vue";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
// import html2canvas from 'html2canvas'
|
|
||||||
// import JsPDF from 'jspdf'
|
|
||||||
export default {
|
export default {
|
||||||
name: "DayReport",
|
name: "DayReport",
|
||||||
components: {
|
components: {
|
||||||
@@ -95,7 +78,6 @@ export default {
|
|||||||
monthlyRelatedMetrics,
|
monthlyRelatedMetrics,
|
||||||
yearRelatedMetrics,
|
yearRelatedMetrics,
|
||||||
dataTrend
|
dataTrend
|
||||||
// psiLineChart
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -275,35 +257,12 @@ export default {
|
|||||||
},
|
},
|
||||||
changeDate(val) {
|
changeDate(val) {
|
||||||
this.date = val;
|
this.date = val;
|
||||||
// this.weekDay = this.weekArr[moment(this.date).format('e')]
|
|
||||||
// this.getData()
|
|
||||||
if (this.date === moment().format("yyyy-MM-DD")) {
|
if (this.date === moment().format("yyyy-MM-DD")) {
|
||||||
this.loopTime();
|
this.loopTime();
|
||||||
} else {
|
} else {
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// 导出
|
|
||||||
// () {
|
|
||||||
// this.$message.success('正在导出,请稍等!')
|
|
||||||
// const element = document.getElementById('dayRepDom')
|
|
||||||
// element.style.display = 'block'
|
|
||||||
// const fileName = '株洲碲化镉生产日报' + moment().format('yyMMDD') + '.pdf'
|
|
||||||
// html2canvas(element, {
|
|
||||||
// dpi: 300, // Set to 300 DPI
|
|
||||||
// scale: 3 // Adjusts your resolution
|
|
||||||
// }).then(function(canvas) {
|
|
||||||
// const imgWidth = 595.28
|
|
||||||
// const imgHeight = 841.89
|
|
||||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
|
||||||
// const PDF = new JsPDF('', 'pt', [imgWidth, imgHeight])
|
|
||||||
// PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
|
|
||||||
// setTimeout(() => {
|
|
||||||
// PDF.save(fileName) // 导出文件名
|
|
||||||
// }, 1000)
|
|
||||||
// })
|
|
||||||
// element.style.display = 'none'
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -97,24 +97,11 @@ export default {
|
|||||||
rates.push(data.completeRate || 0);
|
rates.push(data.completeRate || 0);
|
||||||
reals.push(data.real || 0);
|
reals.push(data.real || 0);
|
||||||
targets.push(data.target || 0);
|
targets.push(data.target || 0);
|
||||||
// 复用 getRateFlag 逻辑
|
flags.push(data.completeRate>=100?1:0);
|
||||||
flags.push(this.getRateFlag(data.completeRate, data.real, data.target));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { rates, reals, targets, flags };
|
return { rates, reals, targets, flags };
|
||||||
},
|
},
|
||||||
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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default {
|
|||||||
targets.push(monthData.target || 0);
|
targets.push(monthData.target || 0);
|
||||||
diffs.push(monthData.diff || 0);
|
diffs.push(monthData.diff || 0);
|
||||||
// 生成达标状态(复用 getRateFlag 逻辑)
|
// 生成达标状态(复用 getRateFlag 逻辑)
|
||||||
flags.push(this.getRateFlag(completeRate, monthData.real, monthData.target));
|
flags.push(completeRate>=100?1:0);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -176,29 +176,55 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 14,
|
barWidth: 14,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const currentFlag = flags[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: reals, // 动态实际值
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [0, 0],
|
offset: [0, 0],
|
||||||
// 固定label尺寸:68px×20px
|
|
||||||
width: 68,
|
width: 68,
|
||||||
height: 20,
|
height: 20,
|
||||||
// 关键:去掉换行,让文字在一行显示,适配小尺寸
|
formatter: () => {
|
||||||
formatter: (params) => {
|
const currentDiff = diffs[index] || 0;
|
||||||
|
const currentFlag = flags[index] || 0;
|
||||||
// const flags = flags || [];
|
|
||||||
const currentDiff = diffs[params.dataIndex] || 0;
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
console.log('flags[params.dataIndex]', flags);
|
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -232,46 +258,21 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: reals, // 动态实际值
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -284,19 +285,6 @@ export default {
|
|||||||
selectProfit(item) {
|
selectProfit(item) {
|
||||||
this.selectedProfit = item;
|
this.selectedProfit = item;
|
||||||
this.isDropdownShow = false;
|
this.isDropdownShow = false;
|
||||||
},
|
|
||||||
// 复用达标状态判断方法
|
|
||||||
getRateFlag(rate, real, target) {
|
|
||||||
// 先处理无效值的情况
|
|
||||||
if (isNaN(rate) || rate === null || rate === undefined) return 0;
|
|
||||||
|
|
||||||
// 实际值和目标值都为0时,算作达标
|
|
||||||
if (real === 0 && target === 0 && rate === 0) {
|
|
||||||
return 1; // 达标
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其他情况:rate >= 100 或 rate === 0 时达标
|
|
||||||
return (rate >= 100) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="下降">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge id="month" :detailData="factoryData"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
<!-- 传递包含flag的factoryData给柱状图组件 -->
|
||||||
@@ -86,7 +83,7 @@ export default {
|
|||||||
target: 0,
|
target: 0,
|
||||||
thb: 0,
|
thb: 0,
|
||||||
...rawData,
|
...rawData,
|
||||||
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段
|
flag: rawData.completeRate >= 100 ? 1 : 0 // 根据完成率计算flag
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -100,23 +97,6 @@ export default {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断完成率对应的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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -29,9 +29,6 @@
|
|||||||
import Container from './container.vue'
|
import Container from './container.vue'
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
import operatingSingleBar from './operatingSingleBar.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, operatingSingleBar },
|
components: { Container, operatingSingleBar },
|
||||||
@@ -92,7 +89,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target),
|
flag: data.completeRate >= 100 ? 1 : 0,
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -140,23 +137,6 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断完成率对应的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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图表更新方法:可在这里补充全局的图表刷新逻辑
|
* 图表更新方法:可在这里补充全局的图表刷新逻辑
|
||||||
@@ -164,10 +144,6 @@ getRateFlag(rate, real, target) {
|
|||||||
*/
|
*/
|
||||||
updateChart() {
|
updateChart() {
|
||||||
console.log('数据更新,当前relatedMon:', this.relatedMon)
|
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,26 +173,58 @@ export default {
|
|||||||
type: 'bar',
|
type: 'bar',
|
||||||
yAxisIndex: 0,
|
yAxisIndex: 0,
|
||||||
barWidth: 40,
|
barWidth: 40,
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) => {
|
||||||
|
const safeFlag = data.flags || [];
|
||||||
|
const currentFlag = safeFlag[params.dataIndex] || 0;
|
||||||
|
return currentFlag === 1
|
||||||
|
? {
|
||||||
|
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)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
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: data.reals || []
|
||||||
|
},
|
||||||
|
// 实际柱状图的标签层(独立scatter系列,zlevel=1确保标签在最上层)
|
||||||
|
{
|
||||||
|
name: '__实际差值标签',
|
||||||
|
type: 'scatter',
|
||||||
|
yAxisIndex: 0,
|
||||||
|
zlevel: 1,
|
||||||
|
symbolSize: 0,
|
||||||
|
tooltip: { show: false },
|
||||||
|
data: (data.reals || []).map((value, index) => ({
|
||||||
|
value: value,
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
offset: [32, 0],
|
offset: [32, 0],
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 22,
|
height: 22,
|
||||||
formatter: (params) => {
|
formatter: () => {
|
||||||
const diff = data.diff || [];
|
const diff = data.diff || [];
|
||||||
const flags = data.flags || [];
|
const flags = data.flags || [];
|
||||||
const currentDiff = diff[params.dataIndex] || 0;
|
const currentDiff = diff[index] || 0;
|
||||||
const currentFlag = flags[params.dataIndex] || 0;
|
const currentFlag = flags[index] || 0;
|
||||||
|
|
||||||
const prefix = currentFlag === 1 ? '+' : '';
|
|
||||||
|
|
||||||
// 根据标志位选择不同的样式类
|
|
||||||
if (currentFlag === 1) {
|
if (currentFlag === 1) {
|
||||||
// 达标 - 使用 rate-achieved 样式
|
|
||||||
return `{achieved|${currentDiff}}{text|差值}`;
|
return `{achieved|${currentDiff}}{text|差值}`;
|
||||||
} else {
|
} else {
|
||||||
// 未达标 - 使用 rate-unachieved 样式
|
|
||||||
return `{unachieved|${currentDiff}}{text|差值}`;
|
return `{unachieved|${currentDiff}}{text|差值}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,45 +257,19 @@ export default {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#76DABE', // 与达标的 offset: 1 颜色一致
|
color: '#76DABE',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
// 未达标样式
|
|
||||||
unachieved: {
|
unachieved: {
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
padding: [5, 0, 5, 10],
|
padding: [5, 0, 5, 10],
|
||||||
align: 'center',
|
align: 'center',
|
||||||
color: '#F9A44A', // 与未达标的 offset: 1 颜色一致
|
color: '#F9A44A',
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
const safeFlag = data.flags || [];
|
|
||||||
const currentFlag = safeFlag[params.dataIndex] || 0;
|
|
||||||
return currentFlag === 1
|
|
||||||
? {
|
|
||||||
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)' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
: {
|
}))
|
||||||
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: data.reals || []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,24 +80,6 @@ export default {
|
|||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断完成率对应的flag值(<100为0,≥100为1)
|
|
||||||
* @param {number} rate 完成率(原始值,如80代表80%)
|
|
||||||
* @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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
||||||
*/
|
*/
|
||||||
@@ -116,7 +98,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: [ksxnData.diff],
|
diff: [ksxnData.diff],
|
||||||
reals: [ksxnData.real],
|
reals: [ksxnData.real],
|
||||||
rate: [ksxnData.completeRate],
|
rate: [ksxnData.completeRate],
|
||||||
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)],
|
flags: [ksxnData.completeRate >= 100 ? 1 : 0],
|
||||||
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,7 +113,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
||||||
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
||||||
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
||||||
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识
|
flags: factoryDataList.map(item => item.completeRate>=100?1:0), // 完成率标识
|
||||||
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -79,25 +79,6 @@ export default {
|
|||||||
sortChange(value) {
|
sortChange(value) {
|
||||||
this.$emit('sort-change', value);
|
this.$emit('sort-change', value);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断完成率对应的flag值(<100为0,≥100为1)
|
|
||||||
* @param {number} rate 完成率(原始值,如80代表80%)
|
|
||||||
* @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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
* 核心处理函数:解析thisMonData,组装集团和工厂数据
|
||||||
*/
|
*/
|
||||||
@@ -116,7 +97,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: [ksxnData.diff],
|
diff: [ksxnData.diff],
|
||||||
reals: [ksxnData.real],
|
reals: [ksxnData.real],
|
||||||
rate: [ksxnData.completeRate],
|
rate: [ksxnData.completeRate],
|
||||||
flags: [this.getRateFlag(ksxnData.completeRate, ksxnData.real, ksxnData.target)],
|
flags: [ksxnData.completeRate >= 100 ? 1 : 0],
|
||||||
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
thb: [ksxnData.thb] // 新增thb字段(如果子组件需要)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -131,7 +112,7 @@ getRateFlag(rate, real, target) {
|
|||||||
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
diff: factoryDataList.map(item => item.diff || 0), // 差值
|
||||||
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
reals: factoryDataList.map(item => item.real || 0), // 实际值
|
||||||
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
rates: factoryDataList.map(item => item.completeRate || 0), // 完成率
|
||||||
flags: factoryDataList.map(item => this.getRateFlag(item.completeRate, item.real, item.target)), // 完成率标识
|
flags: factoryDataList.map(item => item.completeRate >= 100 ? 1 : 0), // 完成率标识
|
||||||
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
thb: factoryDataList.map(item => item.thb || 0) // thb字段
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
<img v-else class="arrow" src="../../../assets/img/downArrow.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="electricityGauge">
|
|
||||||
<electricityGauge id="year" :detailData="factoryData"></electricityGauge>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="line" style="padding: 0px;">
|
<div class="line" style="padding: 0px;">
|
||||||
<verticalBarChart :detailData="factoryData">
|
<verticalBarChart :detailData="factoryData">
|
||||||
@@ -38,9 +35,6 @@ import Container from './container.vue'
|
|||||||
import electricityGauge from './electricityGauge.vue'
|
import electricityGauge from './electricityGauge.vue'
|
||||||
import verticalBarChart from './verticalBarChart.vue'
|
import verticalBarChart from './verticalBarChart.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, electricityGauge, verticalBarChart },
|
components: { Container, electricityGauge, verticalBarChart },
|
||||||
@@ -90,7 +84,7 @@ export default {
|
|||||||
target: 0,
|
target: 0,
|
||||||
thb: 0,
|
thb: 0,
|
||||||
...rawData,
|
...rawData,
|
||||||
flag: this.getRateFlag(rawData.completeRate, rawData.real, rawData.target) // 新增flag字段
|
flag: rawData.completeRate >= 100 ? 1 : 0 // 根据完成率计算flag
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -103,24 +97,7 @@ export default {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
}
|
||||||
/**
|
|
||||||
* 判断完成率对应的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;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -29,9 +29,6 @@
|
|||||||
import Container from './container.vue'
|
import Container from './container.vue'
|
||||||
import operatingSingleBar from './operatingSingleBar.vue'
|
import operatingSingleBar from './operatingSingleBar.vue'
|
||||||
|
|
||||||
// import * as echarts from 'echarts'
|
|
||||||
// import rawItem from './raw-Item.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProductionStatus',
|
name: 'ProductionStatus',
|
||||||
components: { Container, operatingSingleBar },
|
components: { Container, operatingSingleBar },
|
||||||
@@ -92,7 +89,7 @@ export default {
|
|||||||
const data = list.find(item => item && item.title === def.name) || fallback
|
const data = list.find(item => item && item.title === def.name) || fallback
|
||||||
const detailData = {
|
const detailData = {
|
||||||
...data,
|
...data,
|
||||||
flag: _this.getRateFlag((data || _this.defaultData).completeRate, (data || _this.defaultData).real, (data || _this.defaultData).target),
|
flag: data.completeRate >= 100 ? 1 : 0,
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...def,
|
...def,
|
||||||
@@ -140,24 +137,6 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 判断完成率对应的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 变化,此方法可留空或补充额外逻辑
|
* 若子组件内部已监听 chartData 变化,此方法可留空或补充额外逻辑
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user