制造成本分析接口+单位修改

This commit is contained in:
2026-04-08 15:26:20 +08:00
parent ed0fd63474
commit 0c8bd440ae
51 changed files with 2651 additions and 718 deletions

View File

@@ -130,6 +130,7 @@ export default {
display: flex;
flex-direction: column;
padding: 12px;
margin: 7px 0;
.barTop {
display: flex;

View File

@@ -1,5 +1,5 @@
<template>
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 214px;"></div>
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 216px;"></div>
</template>
<script>
import * as echarts from 'echarts';

View File

@@ -143,7 +143,7 @@ export default {
getUnitPriceAnalysisGroupData({
startTime: this.dateData.startTime,
endTime: this.dateData.endTime,
paramName: '增效额'
paramName: '增效额'
// timeDim: this.dateData.mode
}).then((res) => {
console.log(res);

View File

@@ -192,7 +192,7 @@ export default {
endTime: this.dateData.endTime,
// index: this.index,
// sort: 1,
paramName: '增效额',
paramName: '增效额',
paramList: ['大宗增效额', '石料增效额', '材料增效额', '动力增效额', '技术服务增效额'],
baseId: this.factory,
// baseId: Number(this.factory),

View File

@@ -63,8 +63,8 @@ export default {
return {
activeButton: 0,
isDropdownShow: false,
selectedProfit: '增效额', // 关键修改:默认赋值为「净价」,初始化即展示该类目数据
profitOptions: ['增效额', '动力增效额', '大宗增效额', '技术服务增效额', '材料增效额', '石料增效额']
selectedProfit: '增效额', // 关键修改:默认赋值为「净价」,初始化即展示该类目数据
profitOptions: ['增效额', '动力增效额', '大宗增效额', '技术服务增效额', '材料增效额', '石料增效额']
};
},
computed: {

View File

@@ -62,7 +62,7 @@ import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
import relateSingleFuelCostAnalysis from "../productionCostAnalysisComponents/relateSingleFuelCostAnalysis.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuel.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelYL.vue";
import { mapState } from "vuex";
import { getSingleMaterialAnalysis } from '@/api/cockpit'
import moment from "moment";
@@ -94,11 +94,16 @@ export default {
trendName: '采购单价',
meterialName:'',
materialOptions: [
{value:'硅砂',label:'硅砂'},
{value:'海砂',label:'海砂'},
{value:'纯碱',label:'纯碱'},
{value:'氢氧化铝',label:'氢氧化铝'},
{value:'碎玻璃',label:'碎玻璃'},
{value:'助熔剂',label:'助熔剂'},
{value:'白云石',label:'白云石'},
{value:'石灰石',label:'石灰石'}
{value:'石灰石',label:'石灰石'},
{value:'硅砂',label:'硅砂'},
{value:'纯碱',label:'纯碱'},
{value:'焦锑酸钠',label:'焦锑酸钠'},
{value:'芒硝',label:'芒硝'},
{value:'硝酸钠',label:'硝酸钠'}
]
};
},
@@ -169,7 +174,7 @@ export default {
this.beilv = _this.clientWidth / 1920;
})();
};
this.meterialName = this.$route.query.name ? this.$route.query.name : '硅砂'
this.meterialName = this.$route.query.name ? this.$route.query.name : '氢氧化铝'
if(this.$route.query.factory){
this.factory =Number(this.$route.query.factory)
}else if(this.$store.getters.levelList.length > 0 && this.$store.getters.levelList[0].id !== 1) {

View File

@@ -52,7 +52,7 @@ import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLabor.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLabor2.vue";
import { mapState } from "vuex";
import { getSingleMaterialCostAnalysis } from '@/api/cockpit'
import moment from "moment";

View File

@@ -37,7 +37,7 @@
grid-template-columns: 1624px;
">
<!-- <monthlyRelatedMetrics :itemData="renderList" :title="'月度·相关指标分析'" /> -->
<relateSingleFuelCostAnalysis :dateData="dateData" :relatedData="relatedData" :title="'相关指标分析'" />
<relateSingleFuelCostAnalysis fuelName='电' :dateData="dateData" :relatedData="relatedData" :title="'相关指标分析'" />
</div>
</div>
@@ -47,18 +47,10 @@
gap: 12px;
grid-template-columns: 1624px;
">
<dataTrend @getData="changeItem" :trendData="trend" :title="'数据趋势'" />
<dataTrend fuelName='电' @getData="changeItem" :trendData="trend" :title="'数据趋势'" />
</div>
</div>
</div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div>
</template>
<script>
@@ -68,21 +60,11 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
// import monthlyRelatedMetrics from "../procurementGainAnalysisComponents/monthlyRelatedMetrics.vue";
import relateSingleFuelCostAnalysis from "../productionCostAnalysisComponents/relateSingleFuelCostAnalysisDian.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuel.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelDian.vue";
import { mapState } from "vuex";
import { getCostAnalysisData } 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 html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default {
name: "DayReport",
components: {
@@ -93,7 +75,6 @@ export default {
totalOverview,
relateSingleFuelCostAnalysis,
dataTrend
// psiLineChart
},
data() {
return {

View File

@@ -34,13 +34,9 @@
<div class="left-three" style="
display: grid;
gap: 12px;
grid-template-columns: 804px 804px;
grid-template-columns: 1624px;
">
<monthlyRelatedMetrics :dateData="dateData" :factory="factory" :relatedData="monthRelatedData"
:title="'月度概览'" />
<yearRelatedMetrics :dateData="dateData" :factory="factory" :relatedData="totalRelatedData" :title="'累计概览'" />
<relateFuCostAnalysis :relatedData="relatedData" :title="'相关指标分析'" />
</div>
</div>
<div class="bottom" style="display: flex; gap: 16px;margin-top: 6px;">
@@ -53,14 +49,6 @@
</div>
</div>
</div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div>
</template>
<script>
@@ -70,21 +58,11 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
import monthlyRelatedMetrics from "../productionCostAnalysisComponents/monthlyThreeRelatedMetrics.vue";
import yearRelatedMetrics from "../productionCostAnalysisComponents/yearThreeRelatedMetrics.vue";
import relateFuCostAnalysis from "../productionCostAnalysisComponents/relateFuCostAnalysis.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendProcAuxMat.vue";
import { mapState } from "vuex";
import { getCostAnalysisData } 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 html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default {
name: "DayReport",
components: {
@@ -93,8 +71,7 @@ export default {
monthlyOverview,
Sidebar,
totalOverview,
monthlyRelatedMetrics,
yearRelatedMetrics,
relateFuCostAnalysis,
dataTrend
// psiLineChart
},
@@ -107,11 +84,10 @@ export default {
factory: null,
value: 100,
dateData: {},
trendName: '加工燃料成本',
trendName: '加工辅料',
monData: {},
totalData: {},
monthRelatedData: [],
totalRelatedData: [],
relatedData:{},
trend: [],
};
},
@@ -224,20 +200,17 @@ export default {
return item.name === "加工辅料成本";
});
console.log('this.monData', this.monData);
this.totalData = res.data.totalMonthData.find(item => {
return item.name === "加工辅料成本";
});
// this.relatedMon = res.data.relatedMon
this.monthRelatedData = res.data.currentMonthData.filter(item => {
this.relatedData = {
relatedMon: res.data.currentMonthData.filter(item => {
return item.name !== "加工辅料成本";
}),
relatedTotal: res.data.totalMonthData.filter(item => {
return item.name !== "加工辅料成本";
})
this.totalRelatedData = res.data.totalMonthData.filter(item => {
return item.name !== "加工辅料成本";
}) // 兜底累
;
}
this.trend = res.data.dataTrend
});
},

View File

@@ -60,7 +60,7 @@ import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
// import monthlyRelatedMetrics from "../procurementGainAnalysisComponents/monthlyRelatedMetrics.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLabor.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendProcessingLabor2.vue";
import { mapState } from "vuex";
import { getSingleMaterialCostAnalysis } from '@/api/cockpit'
// import PSDO from "./components/PSDO.vue";

View File

@@ -49,7 +49,7 @@
gap: 12px;
grid-template-columns: 1624px;
">
<dataTrend @getData="changeItem" :trendData="trend" :title="'数据趋势'" />
<dataTrend :fuelName='fuelName' @getData="changeItem" :trendData="trend" :title="'数据趋势'" />
</div>
</div>
</div>

View File

@@ -38,7 +38,6 @@
gap: 12px;
grid-template-columns: 1624px;
">
<!-- <monthlyRelatedMetrics :itemData="renderList" :title="'月度·相关指标分析'" /> -->
<relateSingleFuelCostAnalysis :relatedData="relatedData" :title="'相关指标分析'" />
</div>
@@ -53,14 +52,6 @@
</div>
</div>
</div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div>
</template>
<script>
@@ -70,21 +61,11 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
// import monthlyRelatedMetrics from "../procurementGainAnalysisComponents/monthlyRelatedMetrics.vue";
import relateSingleFuelCostAnalysis from "../productionCostAnalysisComponents/relateSingleFuelCostAnalysisFu.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuel.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelF.vue";
import { mapState } from "vuex";
import { getSingleMaterialAnalysis } 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 html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default {
name: "DayReport",
components: {
@@ -95,7 +76,6 @@ export default {
totalOverview,
relateSingleFuelCostAnalysis,
dataTrend
// psiLineChart
},
data() {
return {
@@ -115,7 +95,9 @@ export default {
auxMatOptions: [
{value:'镀膜液',label:'镀膜液'},
{value:'油墨',label:'油墨'},
{value:'釉料',label:'釉料'}
{value:'釉料',label:'釉料'},
{value:'无水乙醇',label:'无水乙醇'},
{value:'异丙醇',label:'异丙醇'}
]
};
},

View File

@@ -39,7 +39,7 @@
grid-template-columns: 1624px;
">
<!-- <monthlyRelatedMetrics :itemData="renderList" :title="'月度·相关指标分析'" /> -->
<relateSingleFuelCostAnalysis :relatedData="relatedData" :title="'相关指标分析'" />
<relateSingleFuelCostAnalysis :fuelName='fuelName' :relatedData="relatedData" :title="'相关指标分析'" />
</div>
</div>
@@ -49,18 +49,10 @@
gap: 12px;
grid-template-columns: 1624px;
">
<dataTrend @getData="changeItem" :trendData="trend" :title="'数据趋势'" />
<dataTrend :fuelName='fuelName' @getData="changeItem" :trendData="trend" :title="'数据趋势'" />
</div>
</div>
</div>
<!-- <div class="centerImg" style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1; /* 确保在 backp 之上、内容之下 */
"></div> -->
</div>
</template>
<script>
@@ -70,21 +62,11 @@ import screenfull from "screenfull";
import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
// import totalOverview from "../operatingComponents/totalOverview.vue";
// import monthlyRelatedMetrics from "../procurementGainAnalysisComponents/monthlyRelatedMetrics.vue";
import relateSingleFuelCostAnalysis from "../productionCostAnalysisComponents/relateSingleFuelCostAnalysisDian.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuel.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelDian.vue";
import { mapState } from "vuex";
import { getSingleMaterialAnalysis } 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 html2canvas from 'html2canvas'
// import JsPDF from 'jspdf'
export default {
name: "DayReport",
components: {
@@ -95,7 +77,6 @@ export default {
totalOverview,
relateSingleFuelCostAnalysis,
dataTrend
// psiLineChart
},
data() {
return {

View File

@@ -90,6 +90,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -88,6 +88,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -35,7 +35,7 @@
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
{{ item.name }}
</div>
</div>
</div>
@@ -60,19 +60,18 @@ export default {
data() {
return {
isDropdownShow: false,
selectedProfit: '天然气', // 选中的名称初始为null
selectedProfit: '原片燃料成本', // 选中的名称初始为null
profitOptions: [
'天然气',
'LNG液化天然气',
'重油',
'水',
]
{name:'原片燃料成本',unit:'元/㎡'},
{name:'天然气',unit:'元/㎡'},
{name:'LNG液化天然气',unit:'元/㎡'},
{name:'重油',unit:'元/㎡'},
{name:'水',unit:'元/m³'}
],
unit:'元/㎡'
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
return this.chartData
},
@@ -88,6 +87,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:this.unit,
series: [
// 1. 完成率(折线图)
{
@@ -246,9 +246,10 @@ export default {
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.selectedProfit = item.name;
this.unit = item.unit;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item)
this.$emit('handleGetItemData', item.name)
}
},
};

View File

@@ -35,7 +35,7 @@
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
{{ item.nname }}
</div>
</div>
</div>
@@ -62,18 +62,15 @@ export default {
isDropdownShow: false,
selectedProfit: '原片制造费用成本', // 选中的名称初始为null
profitOptions: [
'原片制造费用成本',
'包材',
'备件、机物料',
'折旧',
'其他',
{name:'原片制造费用成本',nname:'原片制造费用'},
{name:'包材',nname:'包材'},
{name:'备件、机物料',nname:'备件、机物料'},
{name:'折旧',nname:'折旧'},
{name:'其他',nname:'其他'},
]
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
return this.chartData
},
@@ -89,6 +86,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{
@@ -247,9 +245,9 @@ export default {
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.selectedProfit = item.nname;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item)
this.$emit('handleGetItemData', item.name)
}
},
};

View File

@@ -63,21 +63,18 @@ export default {
selectedProfit: '原片原料', // 选中的名称初始为null
profitOptions: [
'原片原料',
'硅砂',
'纯碱',
'氢氧化铝',
'碎玻璃',
'复合澄清剂',
'助熔剂',
'白云石',
'石灰石',
'复合澄清剂',
'氢氧化铝',
'助溶剂',
'碎玻璃'
'硅砂',
'纯碱'
]
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
return this.chartData
},
@@ -93,6 +90,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -88,6 +88,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -66,6 +66,8 @@ export default {
'镀膜液',
'油墨',
'釉料',
'无水乙醇',
'异丙醇'
]
};
},
@@ -88,6 +90,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -90,6 +90,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -87,6 +87,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -91,6 +91,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -87,6 +87,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:'元/㎡',
series: [
// 1. 完成率(折线图)
{

View File

@@ -35,7 +35,7 @@
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
{{ item.name }}
</div>
</div>
</div>
@@ -56,24 +56,45 @@ import * as echarts from 'echarts';
export default {
name: "Container",
components: { operatingLineBar },
props: ["chartData"],
props: ["chartData","fuelName"],
data() {
return {
isDropdownShow: false,
selectedProfit: '采购单价', // 选中的名称初始为null
profitOptions: [
'采购单价',
'产量',
'单耗',
'消耗量',
'热耗',
]
selectedProfit: '采购单价',
unit:'元/m³'
};
},
watch: {
fuelName: {
handler(newVal) {
this.profitOptions.forEach(item => {
if (item.name === this.selectedProfit) {
this.unit = item.unit
}
})
}
}
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
profitOptions() {
if (this.fuelName === '重油') {
return [
{ name: '采购单价', unit: '元/吨'},
{ name: '产量', unit: '㎡'},
{ name: '单耗', unit: '千克/㎡'},
{ name: '消耗量', unit: '吨'},
{ name: '热耗', unit: '千卡/千克'}
]
}else{
return [
{ name: '采购单价', unit: '元/m³'},
{ name: '产量', unit: '㎡'},
{ name: '单耗', unit: 'm³/㎡'},
{ name: '消耗量', unit: 'm³'},
{ name: '热耗', unit: '千卡/千克'}
]
}
},
currentDataSource() {
return this.chartData
},
@@ -89,6 +110,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit: this.unit,
series: [
// 1. 完成率(折线图)
{
@@ -247,9 +269,10 @@ export default {
},
methods: {
selectProfit(item) {
this.selectedProfit = item;
this.selectedProfit = item.name;
this.unit = item.unit;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item)
this.$emit('handleGetItemData', item.name)
}
},
};

View File

@@ -35,7 +35,7 @@
<div class="dropdown-options" v-if="isDropdownShow">
<div class="dropdown-option" v-for="(item, index) in profitOptions" :key="index"
@click.stop="selectProfit(item)">
{{ item }}
{{ item.name }}
</div>
</div>
</div>
@@ -61,19 +61,17 @@ export default {
return {
isDropdownShow: false,
selectedProfit: '采购单价', // 选中的名称初始为null
unit:'元/吨',
profitOptions: [
'采购单价',
'产量',
'单耗',
'消耗量',
'日均消耗量'
{name:'采购单价',unit:'元/吨'},
{name:'产量',unit:'㎡'},
{name:'单耗',unit:'千克/㎡'},
{name:'消耗量',unit:'吨'},
{name:'日均消耗量',unit:'吨'}
]
};
},
computed: {
// profitOptions() {
// return this.categoryData.map(item => item.name) || [];
// },
currentDataSource() {
return this.chartData
},
@@ -89,6 +87,7 @@ export default {
const salesData = {
allPlaceNames: this.locations,
unit:this.unit,
series: [
// 1. 完成率(折线图)
{
@@ -247,10 +246,10 @@ export default {
},
methods: {
selectProfit(item) {
console.log('aaaaaa',item)
this.selectedProfit = item;
this.selectedProfit = item.name;
this.unit = item.unit;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item)
this.$emit('handleGetItemData', item.name)
}
},
};

View File

@@ -0,0 +1,497 @@
<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.name }}
</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","fuelName"],
data() {
return {
isDropdownShow: false,
selectedProfit: '采购单价', // 选中的名称初始为null
unit:'元/度'
};
},
watch: {
fuelName: {
handler(newVal) {
this.profitOptions.forEach(item => {
if (item.name === this.selectedProfit) {
this.unit = item.unit
}
})
}
}
},
computed: {
profitOptions() {
if (this.fuelName==='电') {
return [
{name:'采购单价',unit:'元/度'},
{name:'产量',unit:'㎡'},
{name:'单耗',unit:'度/㎡'},
{name:'消耗量',unit:'度'}
]
}else{
return [
{name:'采购单价',unit:'元/m³'},
{name:'产量',unit:'㎡'},
{name:'单耗',unit:'m³/㎡'},
{name:'消耗量',unit:'m³'}
]
}
},
currentDataSource() {
return this.chartData
},
locations() {
return this.chartData.time
},
// 根据按钮切换生成对应的 chartData
chartD() {
// 销量场景数据
const data = this.currentDataSource;
console.log(this.currentDataSource, 'currentDataSource');
console.log('this.currentDataSource', data);
const salesData = {
allPlaceNames: this.locations,
unit:this.unit,
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.proportion || [], // 完成率(%
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.targetValue || [] // 目标销量(万元)
},
// 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.diffValue || [];
const flags = data.completed || [];
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.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 || [] // 实际销量(万元)
}
]
};
return salesData;
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item.name;
this.unit = item.unit;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item.name)
}
},
};
</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: 999; // 提高z-index确保菜单不被遮挡
}
.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); // 箭头旋转方向可根据需求调整比如改为rotate(-90deg)更符合向上展开的视觉
}
}
.dropdown-options {
position: absolute;
// 关键修改1调整top值让菜单显示在选择框上方calc(-100% - 2px)表示向上偏移自身100%再加2px间距
bottom: 100%;
right: 0;
// 移除多余的margin-top避免额外间距
// margin-top: 2px;
width: 123px;
background: #ffffff;
// 关键修改2调整border-radius让菜单顶部圆角匹配选择框的右上角底部圆角为0更美观
border-radius: 8px 8px 0 0;
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>

View File

@@ -0,0 +1,475 @@
<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.name }}
</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 {
isDropdownShow: false,
selectedProfit: '采购单价', // 选中的名称初始为null
unit:'元/千克',
profitOptions: [
{name:'采购单价',unit:'元/千克'},
{name:'产量',unit:'㎡'},
{name:'单耗',unit:'千克/㎡'},
{name:'消耗量',unit:'千克'}
]
};
},
computed: {
currentDataSource() {
return this.chartData
},
locations() {
return this.chartData.time
},
// 根据按钮切换生成对应的 chartData
chartD() {
// 销量场景数据
const data = this.currentDataSource;
console.log(this.currentDataSource, 'currentDataSource');
console.log('this.currentDataSource', data);
const salesData = {
allPlaceNames: this.locations,
unit:this.unit,
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.proportion || [], // 完成率(%
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.targetValue || [] // 目标销量(万元)
},
// 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.diffValue || [];
const flags = data.completed || [];
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.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 || [] // 实际销量(万元)
}
]
};
return salesData;
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item.name;
this.unit = item.unit;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item.name)
}
},
};
</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: 999; // 提高z-index确保菜单不被遮挡
}
.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); // 箭头旋转方向可根据需求调整比如改为rotate(-90deg)更符合向上展开的视觉
}
}
.dropdown-options {
position: absolute;
// 关键修改1调整top值让菜单显示在选择框上方calc(-100% - 2px)表示向上偏移自身100%再加2px间距
bottom: 100%;
right: 0;
// 移除多余的margin-top避免额外间距
// margin-top: 2px;
width: 123px;
background: #ffffff;
// 关键修改2调整border-radius让菜单顶部圆角匹配选择框的右上角底部圆角为0更美观
border-radius: 8px 8px 0 0;
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>

View File

@@ -0,0 +1,476 @@
<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.name }}
</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 {
isDropdownShow: false,
selectedProfit: '采购单价', // 选中的名称初始为null
unit:'元/吨',
profitOptions: [
{name:'采购单价',unit:'元/吨'},
{name:'产量',unit:'㎡'},
{name:'单耗',unit:'千克/㎡'},
{name:'消耗量',unit:'吨'},
{name:'日均消耗量',unit:'吨'}
]
};
},
computed: {
currentDataSource() {
return this.chartData
},
locations() {
return this.chartData.time
},
// 根据按钮切换生成对应的 chartData
chartD() {
// 销量场景数据
const data = this.currentDataSource;
console.log(this.currentDataSource, 'currentDataSource');
console.log('this.currentDataSource', data);
const salesData = {
allPlaceNames: this.locations,
unit:this.unit,
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.proportion || [], // 完成率(%
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.targetValue || [] // 目标销量(万元)
},
// 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.diffValue || [];
const flags = data.completed || [];
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.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 || [] // 实际销量(万元)
}
]
};
return salesData;
}
},
methods: {
selectProfit(item) {
this.selectedProfit = item.name;
this.unit = item.unit;
this.isDropdownShow = false;
this.$emit('handleGetItemData', item.name)
}
},
};
</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: 999; // 提高z-index确保菜单不被遮挡
}
.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); // 箭头旋转方向可根据需求调整比如改为rotate(-90deg)更符合向上展开的视觉
}
}
.dropdown-options {
position: absolute;
// 关键修改1调整top值让菜单显示在选择框上方calc(-100% - 2px)表示向上偏移自身100%再加2px间距
bottom: 100%;
right: 0;
// 移除多余的margin-top避免额外间距
// margin-top: 2px;
width: 123px;
background: #ffffff;
// 关键修改2调整border-radius让菜单顶部圆角匹配选择框的右上角底部圆角为0更美观
border-radius: 8px 8px 0 0;
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>

View File

@@ -9,7 +9,7 @@
background-color: rgba(249, 252, 255, 1);
">
<!-- 直接使用计算属性 chartData无需手动更新 -->
<dataTrendBar @handleGetItemData="getData" :chartData="chartData" />
<dataTrendBar :fuelName='fuelName' @handleGetItemData="getData" :chartData="chartData" />
</div>
</div>
</Container>
@@ -28,6 +28,10 @@ export default {
type: Array,
default: () => [],
},
fuelName: {
type: String,
default: () => "",
}
},
data() {
return {

View File

@@ -18,7 +18,7 @@
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBarSingleFuel.vue";
import dataTrendBar from "./dataTrendBarSingleFuelDian.vue";
export default {
name: "ProductionStatus",
@@ -91,7 +91,6 @@ export default {
return `${year}-${month}`;
},
getData(value) {
console.log('bbbbbbbbbbbbb=================',value)
this.$emit('getData', value)
},
},

View File

@@ -0,0 +1,254 @@
<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);
">
<!-- 直接使用计算属性 chartData无需手动更新 -->
<dataTrendBar :fuelName='fuelName' @handleGetItemData="getData" :chartData="chartData" />
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBarSingleFuelDian.vue";
export default {
name: "ProductionStatus",
components: { Container, dataTrendBar },
props: {
trendData: {
type: Array,
default: () => [],
},
fuelName: {
type: String,
default: ""
}
},
data() {
return {
// 移除:原 chartData 定义,改为计算属性
};
},
// 移除:原 watch 监听配置,计算属性自动响应 trendData 变化
computed: {
/**
* chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组
* @returns {Object} 包含6个独立数组的格式化数据
*/
chartData() {
// 初始化6个独立数组
const timeArr = []; // 格式化后的年月数组
const valueArr = []; // 实际值数组
const diffValueArr = []; // 差异值数组
const targetValueArr = []; // 预算值数组
const proportionArr = []; // 占比数组
const completedArr = []; // 完成率数组
// 遍历传入的 trendData 数组(响应式依赖,变化时自动重算)
this.trendData.forEach((item) => {
// 1. 格式化时间并推入时间数组
const yearMonth = this.formatTimeToYearMonth(item.time);
timeArr.push(yearMonth);
// 2. 提取其他字段兜底为0防止null/undefined影响图表渲染
valueArr.push(item.value ?? 0);
diffValueArr.push(item.diffValue ?? 0);
targetValueArr.push(item.targetValue ?? 0);
proportionArr.push(item.proportion ?? 0);
completedArr.push(item.completed ?? 0);
});
// 组装并返回格式化后的数据(结构与原一致)
return {
time: timeArr,
value: valueArr,
diffValue: diffValueArr,
targetValue: targetValueArr,
proportion: proportionArr,
completed: completedArr,
rawData: this.trendData, // 透传原始数据,方便子组件使用
};
},
},
methods: {
/**
* 格式化时间戳为年月格式YYYY-MM
* @param {Number} timestamp 13位毫秒级时间戳
* @returns {String} 格式化后的年月字符串2025-10
*/
formatTimeToYearMonth(timestamp) {
if (!timestamp || isNaN(timestamp)) {
return ""; // 容错:非有效时间戳返回空字符串
}
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始补0至2位
return `${year}-${month}`;
},
getData(value) {
this.$emit('getData', value)
},
},
};
</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 样式(不使用 scoped确保生效 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style>

View File

@@ -0,0 +1,250 @@
<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);
">
<!-- 直接使用计算属性 chartData无需手动更新 -->
<dataTrendBar @handleGetItemData="getData" :chartData="chartData" />
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBarSingleFuelF.vue";
export default {
name: "ProductionStatus",
components: { Container, dataTrendBar },
props: {
trendData: {
type: Array,
default: () => [],
},
},
data() {
return {
// 移除:原 chartData 定义,改为计算属性
};
},
// 移除:原 watch 监听配置,计算属性自动响应 trendData 变化
computed: {
/**
* chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组
* @returns {Object} 包含6个独立数组的格式化数据
*/
chartData() {
// 初始化6个独立数组
const timeArr = []; // 格式化后的年月数组
const valueArr = []; // 实际值数组
const diffValueArr = []; // 差异值数组
const targetValueArr = []; // 预算值数组
const proportionArr = []; // 占比数组
const completedArr = []; // 完成率数组
// 遍历传入的 trendData 数组(响应式依赖,变化时自动重算)
this.trendData.forEach((item) => {
// 1. 格式化时间并推入时间数组
const yearMonth = this.formatTimeToYearMonth(item.time);
timeArr.push(yearMonth);
// 2. 提取其他字段兜底为0防止null/undefined影响图表渲染
valueArr.push(item.value ?? 0);
diffValueArr.push(item.diffValue ?? 0);
targetValueArr.push(item.targetValue ?? 0);
proportionArr.push(item.proportion ?? 0);
completedArr.push(item.completed ?? 0);
});
// 组装并返回格式化后的数据(结构与原一致)
return {
time: timeArr,
value: valueArr,
diffValue: diffValueArr,
targetValue: targetValueArr,
proportion: proportionArr,
completed: completedArr,
rawData: this.trendData, // 透传原始数据,方便子组件使用
};
},
},
methods: {
/**
* 格式化时间戳为年月格式YYYY-MM
* @param {Number} timestamp 13位毫秒级时间戳
* @returns {String} 格式化后的年月字符串2025-10
*/
formatTimeToYearMonth(timestamp) {
if (!timestamp || isNaN(timestamp)) {
return ""; // 容错:非有效时间戳返回空字符串
}
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始补0至2位
return `${year}-${month}`;
},
getData(value) {
this.$emit('getData', value)
},
},
};
</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 样式(不使用 scoped确保生效 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style>

View File

@@ -0,0 +1,250 @@
<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);
">
<!-- 直接使用计算属性 chartData无需手动更新 -->
<dataTrendBar @handleGetItemData="getData" :chartData="chartData" />
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from "../components/container.vue";
import dataTrendBar from "./dataTrendBarSingleFuelYL.vue";
export default {
name: "ProductionStatus",
components: { Container, dataTrendBar },
props: {
trendData: {
type: Array,
default: () => [],
},
},
data() {
return {
// 移除:原 chartData 定义,改为计算属性
};
},
// 移除:原 watch 监听配置,计算属性自动响应 trendData 变化
computed: {
/**
* chartData 计算属性:自动响应 trendData 变化,格式化并提取各字段数组
* @returns {Object} 包含6个独立数组的格式化数据
*/
chartData() {
// 初始化6个独立数组
const timeArr = []; // 格式化后的年月数组
const valueArr = []; // 实际值数组
const diffValueArr = []; // 差异值数组
const targetValueArr = []; // 预算值数组
const proportionArr = []; // 占比数组
const completedArr = []; // 完成率数组
// 遍历传入的 trendData 数组(响应式依赖,变化时自动重算)
this.trendData.forEach((item) => {
// 1. 格式化时间并推入时间数组
const yearMonth = this.formatTimeToYearMonth(item.time);
timeArr.push(yearMonth);
// 2. 提取其他字段兜底为0防止null/undefined影响图表渲染
valueArr.push(item.value ?? 0);
diffValueArr.push(item.diffValue ?? 0);
targetValueArr.push(item.targetValue ?? 0);
proportionArr.push(item.proportion ?? 0);
completedArr.push(item.completed ?? 0);
});
// 组装并返回格式化后的数据(结构与原一致)
return {
time: timeArr,
value: valueArr,
diffValue: diffValueArr,
targetValue: targetValueArr,
proportion: proportionArr,
completed: completedArr,
rawData: this.trendData, // 透传原始数据,方便子组件使用
};
},
},
methods: {
/**
* 格式化时间戳为年月格式YYYY-MM
* @param {Number} timestamp 13位毫秒级时间戳
* @returns {String} 格式化后的年月字符串2025-10
*/
formatTimeToYearMonth(timestamp) {
if (!timestamp || isNaN(timestamp)) {
return ""; // 容错:非有效时间戳返回空字符串
}
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始补0至2位
return `${year}-${month}`;
},
getData(value) {
this.$emit('getData', value)
},
},
};
</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 样式(不使用 scoped确保生效 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style>

View File

@@ -7,7 +7,7 @@
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard left" @click="handleDashboardClick('电')" :detailData="dianData">
<div class="title">
·
·/
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{dianData.proportion}}%</span></span>

View File

@@ -1,240 +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">
<div
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="handleDashboardClick(item.name)"
>
<div class="title">
{{ item.name }}·{{ item.unit }}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{item.data.proportion}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:item.data.completed>0?'#30B590':'#FF9423'}" >{{item.data.diffValue}}</span></span>
</div>
<operatingSingleBar :detailData="item.data"></operatingSingleBar>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
// 接收父组件传递的 月度+累计 组合数据(原有配置保留,仅优化注释)
relatedData: {
type: Array,
default: () => []
},
dateData: {
type: Object,
default: () => {}
},
factory: {
type: [String, Number],
default: ''
},
// 可选:动态标题(原有配置保留)
title: {
type: String,
default: ''
}
},
data() {
return {
chart: null
// 注释无效的activeData保持数据简洁样式不变
// activeData: this.relatedData || []
}
},
computed: {
indicatorDefs() {
return [
{ key: 'coatingLiquid', name: '镀膜液', unit: '万元'},
{ key: 'ink', name: '油墨', unit: '万元'},
{ key: 'glaze', name: '釉料', unit: '万元'},
]
},
indicators() {
const fallback = { targetValue: 0, value: 0, completed: 0, diffValue: 0 }
const list = (Array.isArray(this.relatedData) ? this.relatedData : [])
return this.indicatorDefs.map(def => {
const data = list.find(item => item && item.name.includes(def.name)) || fallback
return {
...def,
data,
sortValue: Number((data && data.value) ?? 0)
}
})
},
sortedIndicators() {
const unitOrder = ['万元']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx
}
return this.indicators.slice().sort((a, b) => {
const ur = unitRank(a.unit) - unitRank(b.unit)
if (ur !== 0) return ur
const vr = (b.sortValue ?? -Infinity) - (a.sortValue ?? -Infinity)
if (vr !== 0) return vr
return String(a.key).localeCompare(String(b.key))
})
}
},
// 保留原有watch注释若需恢复可直接启用样式不变
// watch: {
// relatedData: {
// handler(newVal) {
// this.activeData = newVal.relatedMon || [];
// },
// immediate: true,
// deep: true
// }
// },
mounted() {
// 优化打印日志,输出有效数据(样式不变)
console.log('组件挂载时的相关数据:', this.relatedData);
},
methods: {
handleDashboardClick(name) {
this.$router.push({
path: 'singleProcAuxMatCost',
query: {
name,
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
dateData: this.dateData
}
})
}
}
}
</script>
<style lang='scss' scoped>
/* 样式100%保留不变,无任何增删改 */
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
.scroll-container {
/* 1. 固定容器高度根据页面布局调整示例300px超出则滚动 */
max-height: 210px;
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
overflow-y: auto;
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
overflow-x: hidden;
/* 4. 内边距:与标题栏和容器边缘对齐 */
padding: 10px 0;
/* 5. 隐藏滚动条(兼容主流浏览器) */
/* Chrome/Safari */
&::-webkit-scrollbar {
display: none;
}
/* Firefox */
scrollbar-width: none;
/* IE/Edge */
-ms-overflow-style: none;
}
.dashboard {
width: 250px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
font-style: normal;
letter-spacing: 2px;
}
.number {
display: flex;
align-items: center;
gap: 6px;
// width: 190px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
white-space: nowrap;
}
.mom {
width: 120px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
font-style: normal;
z-index: 1000;
}
}
// .line {
// width: 500px;
// height: 205px;
// background: #F9FCFF;
// }
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</style>
<!-- <style>
/* 全局 tooltip 样式(不使用 scoped确保生效 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style> -->

View File

@@ -7,7 +7,7 @@
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="handleDashboardClick(item.name)"
@click="item.route && handleDashboardClick(item.name,item.route)"
>
<div class="title">
{{ item.name }}·{{ item.unit }}
@@ -62,9 +62,9 @@ export default {
computed: {
indicatorDefs() {
return [
{ key: 'mx', name: '芒硝', unit: '元/㎡'},
{ key: 'xsn', name: '硝酸钠', unit: '元/㎡'},
{ key: 'jtsn', name: '焦锑酸钠', unit: '元/㎡'},
{ key: 'mx', name: '芒硝', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'xsn', name: '硝酸钠', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'jtsn', name: '焦锑酸钠', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
]
},
indicators() {
@@ -112,16 +112,17 @@ export default {
console.log('组件挂载时的相关数据:', this.relatedData);
},
methods: {
handleDashboardClick(name) {
handleDashboardClick(material, path) {
this.$router.push({
path: 'singleProcMfgOverheadCost',
path: path,
query: {
name,
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
name: material,
month: this.month,
factory: this.$route.query.factory ? this.$route.query.factory :5,
dateData: this.dateData
}
})
}
});
},
}
}
</script>

View File

@@ -134,7 +134,7 @@ export default {
return; // 实例未初始化则返回
}
const { allPlaceNames, series } = this.chartData || {};
const { allPlaceNames,unit, series } = this.chartData || {};
const xData = allPlaceNames || [];
const chartSeries = series || [];
@@ -152,7 +152,7 @@ export default {
top: 30,
bottom: 5,
right: 20,
left: 35,
left: 55,
containLabel: true
},
xAxis: [
@@ -181,7 +181,7 @@ export default {
yAxis: [
{
type: 'value',
name: '元/m²',
name: unit,
nameTextStyle: {
color: 'rgba(0, 0, 0, 0.45)',
fontSize: 12,

View File

@@ -67,10 +67,10 @@ export default {
computed: {
indicatorDefs() {
return [
{ key: 'naturalGas', name: '天然气', unit: '元/',route:'singleCombustible'},
{ key: 'lng', name: 'LNG液化天然气', unit: '元/',route:'singleCombustible'},
{ key: 'heavyOil', name: '重油', unit: '元/',route:'singleCombustible'},
{ key: 'water', name: '水', unit: '元/',route:'singleCombustible'}
{ key: 'naturalGas', name: '天然气', unit: '元/',route:'singleCombustible'},
{ key: 'lng', name: 'LNG液化天然气', unit: '元/',route:'singleCombustible'},
{ key: 'heavyOil', name: '重油', unit: '元/',route:'singleCombustible'},
{ key: 'water', name: '水', unit: '元/',route:'singleCombustible'}
]
},
indicators() {
@@ -87,7 +87,7 @@ export default {
})
},
sortedIndicators() {
const unitOrder = ['元/']
const unitOrder = ['元/㎡', '元/m³']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx

View File

@@ -76,10 +76,10 @@ export default {
computed: {
indicatorDefs() {
return [
{ key: 'packaging', name: '包材', unit: '元/'},
{ key: 'spareParts', name: '备件、机物料', unit: '元/'},
{ key: 'depreciation', name: '折旧', unit: '元/'},
{ key: 'other', name: '其他', unit: '元/'},
{ key: 'packaging', name: '包材', unit: '元/'},
{ key: 'spareParts', name: '备件、机物料', unit: '元/'},
{ key: 'depreciation', name: '折旧', unit: '元/'},
{ key: 'other', name: '其他', unit: '元/'},
]
},
indicators() {
@@ -96,7 +96,7 @@ export default {
})
},
sortedIndicators() {
const unitOrder = ['元/']
const unitOrder = ['元/']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx

View File

@@ -0,0 +1,230 @@
<template>
<div style="flex: 1">
<Container :isShowTab="true" :name="title" icon="cockpitItemIcon" size="opLargeBg" topSize="large"
@tabChange="handleChange">
<div class="kpi-content" style="padding: 14px 16px; display: flex; width: 100%;">
<div class="topItem-container" style="display: flex; gap: 8px">
<div
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="item.route && handleDashboardClick(item.name,item.route)"
>
<div class="title">
{{ item.name }}·{{ item.unit }}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{item.data.proportion}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:item.data.completed>0?'#30B590':'#FF9423'}" >{{item.data.diffValue}}</span></span>
</div>
<operatingSingleBar :detailData="item.data"></operatingSingleBar>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from '../components/container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
export default {
name: 'ProductionStatus', // 如需区分可重命名为MaterialProductionStatus
components: { Container, operatingSingleBar },
props: {
// 对齐第二个组件改为Object类型支持月度/累计物料数据,格式统一
relatedData: {
type: Object,
default: () => ({
relatedMon: [], // 物料月度数据(数组格式,存储各物料项)
relatedTotal: [] // 物料累计数据(数组格式,存储各物料项)
})
},
dateData: {
type: Object,
default: () => ({})
},
title: {
type: String,
default: ''
},
month: {
type: String,
default: ''
},
},
data() {
return {
chart: null,
// 核心:当前激活的物料数据集(默认月度数据,与第二个组件逻辑一致)
activeData: this.relatedData.relatedMon || [],
}
},
computed: {
indicatorDefs() {
return [
{ key: 'coatingLiquid', name: '镀膜液', unit: '元/㎡',route:'singleProcAuxMatCost'},
{ key: 'ink', name: '油墨', unit: '元/㎡',route:'singleProcAuxMatCost'},
{ key: 'glaze', name: '釉料', unit: '元/㎡',route:'singleProcAuxMatCost'},
{ key: 'wsyc', name: '无水乙醇', unit: '元/㎡',route:'singleProcAuxMatCost'},
{ key: 'ybc', name: '异丙醇', unit: '元/㎡',route:'singleProcAuxMatCost'},
]
},
indicators() {
const fallback = { targetValue: 0, value: 0, completed: 0, diffValue: 0 }
const list = (Array.isArray(this.activeData) ? this.activeData : [])
return this.indicatorDefs.map(def => {
const data = list.find(item => item && item.name === def.name+'成本') || fallback
return {
...def,
data,
sortValue: Number((data && data.value) ?? 0)
}
})
},
sortedIndicators() {
const unitOrder = ['元/㎡']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx
}
return this.indicators.slice().sort((a, b) => {
const ur = unitRank(a.unit) - unitRank(b.unit)
if (ur !== 0) return ur
const vr = (b.sortValue ?? -Infinity) - (a.sortValue ?? -Infinity)
if (vr !== 0) return vr
return String(a.key).localeCompare(String(b.key))
})
}
},
watch: {
// 对齐第二个组件:监听物料数据变化,同步更新激活数据集
relatedData: {
handler(newVal) {
this.activeData = newVal.relatedMon || [];
},
immediate: true, // 组件挂载时立即执行
deep: true // 深度监听对象内部变化
}
},
mounted() {
console.log('物料组件挂载时的激活数据:', this.activeData);
},
methods: {
handleChange(value) {
console.log('Tab 切换值:', value);
// 根据 Tab 值更新当前激活的数据集
if (value === 'month') {
// 切换为月度数据
this.activeData = this.relatedData.relatedMon || [];
} else {
// 切换为累计数据(非 month 均视为累计,可根据实际需求调整判断条件)
this.activeData = this.relatedData.relatedTotal || [];
}
console.log('当前激活数据集:', this.activeData);
},
// 对齐第二个组件:优化点击事件,接收物料名和路由路径参数
handleDashboardClick(material, path) {
// 2. 优化路由跳转month放入query中修复原代码参数错误与第二个组件一致
this.$router.push({
path: path,
query: {
name: material,
month: this.month,
factory: this.$route.query.factory ? this.$route.query.factory :5,
dateData: this.dateData
}
});
},
// 保留原有的可选弹窗方法(如需使用可保留)
openMaterialDetail(material) {
alert(`查看【${material}】的详细成本分析`);
}
}
}
</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;
}
.dashboard {
width: 310px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
flex-shrink: 0; // 固定宽度,不被挤压(与第二个组件一致)
cursor: pointer; // 鼠标悬浮手型(保留原点击反馈)
transition: all 0.2s ease; // 过渡动画(保留原样式)
// 悬浮样式(保留原交互效果)
// &:hover {
// background: #F0F8FF;
// box-shadow: 0 2px 8px rgba(11, 88, 255, 0.1);
// }
// // 选中状态样式(可选,与第二个组件风格统一)
// &.active {
// background: #E8F4FF;
// border: 1px solid #0B58FF;
// }
.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;
font-style: normal;
}
.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;
font-style: normal;
}
// .line {
// width: 280px; // 适配310px宽度避免图表溢出与第二个组件一致
// height: 150px; // 图表高度与第二个组件统一
// }
}
/* 隐藏横向滚动条,与第二个组件样式完全统一 */
.topItem-container::-webkit-scrollbar {
display: none;
}
.topItem-container {
scrollbar-width: none;
-ms-overflow-style: none;
}
</style>

View File

@@ -65,14 +65,13 @@ export default {
indicatorDefs() {
return [
{ key: 'silicaSand', name: '硅砂', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
// { key: 'seaSand', name: '海砂', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'sodaAsh', name: '纯碱', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'dolomite', name: '白云石', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'limestone', name: '石灰石', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'compoundClarifyingAgent', name: '复合澄清剂', unit: '元/㎡', route: 'compositeClarifyingAgentCostAnalysis'},
{ key: 'ATH', name: '氢氧化铝', unit: '元/㎡', route: null},
{ key: 'cosolvent', name: '助剂', unit: '元/㎡', route: null},
{ key: 'brokenGlass', name: '碎玻璃', unit: '元/㎡', route: null},
{ key: 'ATH', name: '氢氧化铝', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'cosolvent', name: '助剂', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
{ key: 'brokenGlass', name: '碎玻璃', unit: '元/㎡', route: 'SIMFRMCostAnalysis'},
]
},
indicators() {

View File

@@ -50,6 +50,10 @@ export default {
type: String,
default: '' // 默认空字符串,保持原有配置
},
fuelName: { // 接收父组件传递的设备数据数组
type: String,
default: '' // 默认空字符串,保持原有配置
}
},
data() {
return {
@@ -77,13 +81,23 @@ export default {
},
computed: {
indicatorDefs() {
if (this.fuelName === '重油') {
return [
{ key: 'unitPrice', name: '采购单价', unit: '元/吨'},
{ key: 'product', name: '产量', unit: '㎡'},
{ key: 'unitHao', name: '单耗', unit: '千克/㎡'},
{ key: 'haoNum', name: '消耗量', unit: '吨'},
{ key: 'heatConsumption', name: '热耗', unit: '千卡/千克'},
]
}else{
return [
{ key: 'unitPrice', name: '采购单价', unit: '元/m³'},
{ key: 'product', name: '产量', unit: ''},
{ key: 'unitHao', name: '单耗', unit: '吨/m³'},
{ key: 'product', name: '产量', unit: ''},
{ key: 'unitHao', name: '单耗', unit: 'm³/㎡'},
{ key: 'haoNum', name: '消耗量', unit: 'm³'},
{ key: 'heatConsumption', name: '热耗', unit: 'kcal/kg'},
{ key: 'heatConsumption', name: '热耗', unit: '千卡/千克'},
]
}
},
indicators() {
const fallback = { targetValue: 0, value: 0, completed: 0, diffValue: 0 }
@@ -99,7 +113,7 @@ export default {
})
},
sortedIndicators() {
const unitOrder = ['元/','','吨/m³','m³','kcal/kg']
const unitOrder = ['元/','','m³/㎡','m³','千卡/千克']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx

View File

@@ -53,7 +53,7 @@ export default {
month: {
type: String,
default: ''
},
}
},
data() {
return {
@@ -66,8 +66,8 @@ export default {
indicatorDefs() {
return [
{ key: 'rawMaterialCost', name: '采购单价', unit: '元/吨'},
{ key: 'fuelCost', name: '产量', unit: ''},
{ key: 'electricityCost', name: '单耗', unit: ''},
{ key: 'fuelCost', name: '产量', unit: ''},
{ key: 'electricityCost', name: '单耗', unit: '千克/㎡'},
{ key: 'laborCost', name: '消耗量', unit: '吨'},
{ key: 'laborCostDay', name: '日均消耗量', unit: '吨'}
]
@@ -86,7 +86,7 @@ export default {
})
},
sortedIndicators() {
const unitOrder = ['元/吨', '吨']
const unitOrder = ['元/吨','㎡','千克/㎡', '吨']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx

View File

@@ -8,7 +8,7 @@
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard">
<div class="title">
采购单价·/
采购单价·{{unitList[0]}}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{unitPriceData.proportion}}%</span></span>
@@ -20,7 +20,7 @@
</div>
<div class="dashboard">
<div class="title">
产量·
产量.{{unitList[1]}}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{productData.proportion}}%</span></span>
@@ -32,7 +32,7 @@
</div>
<div class="dashboard">
<div class="title">
单耗·/
单耗·{{unitList[2]}}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{unitHaoData.proportion}}%</span></span>
@@ -44,7 +44,7 @@
</div>
<div class="dashboard">
<div class="title">
消耗量·
消耗量·{{unitList[3]}}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{haoNumData.proportion}}%</span></span>
@@ -90,6 +90,10 @@ export default {
type: String,
default: ''
},
fuelName:{
type: String,
default: ''
},
},
data() {
return {
@@ -101,6 +105,13 @@ export default {
},
// 对齐第二个组件:添加计算属性,精准筛选各物料数据
computed: {
unitList() {
if (this.fuelName === '电') {
return ['元/度','㎡','度/㎡','度']
}else{
return ['元/m³','㎡','m³/㎡','m³']
}
},
// 1. 硅砂数据:从激活数据集中筛选,兜底值与第二个组件统一
unitPriceData() {
return this.activeData.find(item => (item.name || '').includes('采购单价')) || {

View File

@@ -65,10 +65,10 @@ export default {
computed: {
indicatorDefs() {
return [
{ key: 'unitPrice', name: '采购单价', unit: 'kg/㎡'},
{ key: 'product', name: '产量', unit: '㎡'},
{ key: 'unitHao', name: '单耗', unit: 'kg/m²'},
{ key: 'haoNum', name: '消耗量', unit: 'kg'},
{ key: 'unitPrice', name: '采购单价', unit: '元/千克'},
{ key: 'product', name: '产量', unit: '㎡'},
{ key: 'unitHao', name: '单耗', unit: '千克/㎡'},
{ key: 'haoNum', name: '消耗量', unit: '千克'},
]
},
indicators() {

View File

@@ -7,7 +7,7 @@
<div class="topItem-container" style="display: flex; gap: 8px;">
<div class="dashboard left" @click="handleDashboardClick('电')" :detailData="dianData">
<div class="title">
·
·/
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{dianData.proportion}}%</span></span>

View File

@@ -1,240 +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">
<div
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="handleDashboardClick(item.name)"
>
<div class="title">
{{ item.name }}·{{ item.unit }}
</div>
<div style='font-size: 14px;text-align: right;padding-right: 5px;'>
<span>完成率:<span style='color: #0B58FF;'>{{item.data.proportion}}%</span></span>
<span style='display: inline-block;margin-left: 10px;'>差值:<span :style="{color:item.data.completed>0?'#30B590':'#FF9423'}" >{{item.data.diffValue}}</span></span>
</div>
<operatingSingleBar :detailData="item.data"></operatingSingleBar>
</div>
</div>
</div>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import operatingSingleBar from './operatingSingleBar.vue'
// import * as echarts from 'echarts'
// import rawItem from './raw-Item.vue'
export default {
name: 'ProductionStatus',
components: { Container, operatingSingleBar },
props: {
// 接收父组件传递的 月度+累计 组合数据(原有配置保留,仅优化注释)
relatedData: {
type: Array,
default: () => []
},
dateData: {
type: Object,
default: () => {}
},
factory: {
type: [String, Number],
default: ''
},
// 可选:动态标题(原有配置保留)
title: {
type: String,
default: ''
}
},
data() {
return {
chart: null
// 注释无效的activeData保持数据简洁样式不变
// activeData: this.relatedData || []
}
},
computed: {
indicatorDefs() {
return [
{ key: 'coatingLiquid', name: '镀膜液', unit: '万元'},
{ key: 'ink', name: '油墨', unit: '万元'},
{ key: 'glaze', name: '釉料', unit: '万元'},
]
},
indicators() {
const fallback = { targetValue: 0, value: 0, completed: 0, diffValue: 0 }
const list = (Array.isArray(this.relatedData) ? this.relatedData : [])
return this.indicatorDefs.map(def => {
const data = list.find(item => item && item.name.includes(def.name)) || fallback
return {
...def,
data,
sortValue: Number((data && data.value) ?? 0)
}
})
},
sortedIndicators() {
const unitOrder = ['万元']
const unitRank = (u) => {
const idx = unitOrder.indexOf(u)
return idx === -1 ? 999 : idx
}
return this.indicators.slice().sort((a, b) => {
const ur = unitRank(a.unit) - unitRank(b.unit)
if (ur !== 0) return ur
const vr = (b.sortValue ?? -Infinity) - (a.sortValue ?? -Infinity)
if (vr !== 0) return vr
return String(a.key).localeCompare(String(b.key))
})
}
},
// 保留原有watch注释若需恢复可直接启用样式不变
// watch: {
// relatedData: {
// handler(newVal) {
// this.activeData = newVal.relatedMon || [];
// },
// immediate: true,
// deep: true
// }
// },
mounted() {
// 优化打印日志,输出有效数据(样式不变)
console.log('组件挂载时的相关数据:', this.relatedData);
},
methods: {
handleDashboardClick(name) {
this.$router.push({
path: 'singleProcAuxMatCost',
query: {
name,
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
dateData: this.dateData
}
})
}
}
}
</script>
<style lang='scss' scoped>
/* 样式100%保留不变,无任何增删改 */
/* 3. 核心:滚动容器样式(固定高度+溢出滚动+隐藏滚动条) */
.scroll-container {
/* 1. 固定容器高度根据页面布局调整示例300px超出则滚动 */
max-height: 210px;
/* 2. 溢出滚动:内容超出高度时显示滚动功能 */
overflow-y: auto;
/* 3. 隐藏横向滚动条(防止设备名过长导致横向滚动) */
overflow-x: hidden;
/* 4. 内边距:与标题栏和容器边缘对齐 */
padding: 10px 0;
/* 5. 隐藏滚动条(兼容主流浏览器) */
/* Chrome/Safari */
&::-webkit-scrollbar {
display: none;
}
/* Firefox */
scrollbar-width: none;
/* IE/Edge */
-ms-overflow-style: none;
}
.dashboard {
width: 250px;
height: 205px;
background: #F9FCFF;
padding: 16px 0 0 10px;
.title {
// width: 190px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
font-style: normal;
letter-spacing: 2px;
}
.number {
display: flex;
align-items: center;
gap: 6px;
// width: 190px;
height: 32px;
font-family: YouSheBiaoTiHei;
font-size: 32px;
color: #0B58FF;
line-height: 32px;
letter-spacing: 2px;
text-align: left;
font-style: normal;
white-space: nowrap;
}
.mom {
width: 120px;
height: 18px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 18px;
color: #000000;
line-height: 18px;
letter-spacing: 1px;
text-align: left;
font-style: normal;
z-index: 1000;
}
}
// .line {
// width: 500px;
// height: 205px;
// background: #F9FCFF;
// }
// .leftTitle {
// .item {
// width: 67px;
// height: 180px;
// padding: 37px 23px;
// background: #F9FCFF;
// font-family: PingFangSC, PingFang SC;
// font-weight: 400;
// font-size: 18px;
// color: #000000;
// line-height: 25px;
// letter-spacing: 1px;
// // text-align: left;
// font-style: normal;
// }
// }
</style>
<!-- <style>
/* 全局 tooltip 样式(不使用 scoped确保生效 */
.production-status-chart-tooltip {
background: #0a2b4f77 !important;
border: none !important;
backdrop-filter: blur(12px);
}
.production-status-chart-tooltip * {
color: #fff !important;
}
</style> -->

View File

@@ -7,7 +7,7 @@
v-for="item in sortedIndicators"
:key="item.key"
class="dashboard"
@click="handleDashboardClick(item.name)"
@click="item.route && handleDashboardClick(item.name,item.route)"
>
<div class="title">
{{ item.name }}·{{ item.unit }}
@@ -112,16 +112,17 @@ export default {
console.log('组件挂载时的相关数据:', this.relatedData);
},
methods: {
handleDashboardClick(name) {
handleDashboardClick(material, path) {
this.$router.push({
path: 'singleProcMfgOverheadCost',
path: path,
query: {
name,
factory: this.$route.query.factory ? this.$route.query.factory : this.factory,
name: material,
month: this.month,
factory: this.$route.query.factory ? this.$route.query.factory :5,
dateData: this.dateData
}
})
}
});
},
}
}
</script>

View File

@@ -181,7 +181,7 @@ export default {
// index: this.index,
// sort: 1,
paramName: '产销率',
paramList: ['产量(深加工)', '销量'],
paramList: ['加工产量', '销量'],
baseId: this.factory,
// baseId: Number(this.factory),
};

View File

@@ -67,7 +67,7 @@ export default {
profitOptions:[
{name:'产销率',unit:'%'},
{name:'销量',unit:'万㎡'},
{name:'产量(深加工)',unit:'㎡'},
{name:'加工产量',unit:'㎡'},
],
unit:'%',
};